New Upstream Release - flask

Ready changes

Summary

Merged new upstream version: 2.3.2 (was: 2.2.2).

Diff

diff --git a/CHANGES.rst b/CHANGES.rst
index e33abe8..1ba0f34 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,92 @@
+Version 2.3.2
+-------------
+
+Released 2022-05-01
+
+-   Set ``Vary: Cookie`` header when the session is accessed, modified, or refreshed.
+-   Update Werkzeug requirement to >=2.3.3 to apply recent bug fixes.
+
+
+Version 2.3.1
+-------------
+
+Released 2023-04-25
+
+-   Restore deprecated ``from flask import Markup``. :issue:`5084`
+
+
+Version 2.3.0
+-------------
+
+Released 2023-04-25
+
+-   Drop support for Python 3.7. :pr:`5072`
+-   Update minimum requirements to the latest versions: Werkzeug>=2.3.0, Jinja2>3.1.2,
+    itsdangerous>=2.1.2, click>=8.1.3.
+-   Remove previously deprecated code. :pr:`4995`
+
+    -   The ``push`` and ``pop`` methods of the deprecated ``_app_ctx_stack`` and
+        ``_request_ctx_stack`` objects are removed. ``top`` still exists to give
+        extensions more time to update, but it will be removed.
+    -   The ``FLASK_ENV`` environment variable, ``ENV`` config key, and ``app.env``
+        property are removed.
+    -   The ``session_cookie_name``, ``send_file_max_age_default``, ``use_x_sendfile``,
+        ``propagate_exceptions``, and ``templates_auto_reload`` properties on ``app``
+        are removed.
+    -   The ``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_MIMETYPE``, and
+        ``JSONIFY_PRETTYPRINT_REGULAR`` config keys are removed.
+    -   The ``app.before_first_request`` and ``bp.before_app_first_request`` decorators
+        are removed.
+    -   ``json_encoder`` and ``json_decoder`` attributes on app and blueprint, and the
+        corresponding ``json.JSONEncoder`` and ``JSONDecoder`` classes, are removed.
+    -   The ``json.htmlsafe_dumps`` and ``htmlsafe_dump`` functions are removed.
+    -   Calling setup methods on blueprints after registration is an error instead of a
+        warning. :pr:`4997`
+
+-   Importing ``escape`` and ``Markup`` from ``flask`` is deprecated. Import them
+    directly from ``markupsafe`` instead. :pr:`4996`
+-   The ``app.got_first_request`` property is deprecated. :pr:`4997`
+-   The ``locked_cached_property`` decorator is deprecated. Use a lock inside the
+    decorated function if locking is needed. :issue:`4993`
+-   Signals are always available. ``blinker>=1.6.2`` is a required dependency. The
+    ``signals_available`` attribute is deprecated. :issue:`5056`
+-   Signals support ``async`` subscriber functions. :pr:`5049`
+-   Remove uses of locks that could cause requests to block each other very briefly.
+    :issue:`4993`
+-   Use modern packaging metadata with ``pyproject.toml`` instead of ``setup.cfg``.
+    :pr:`4947`
+-   Ensure subdomains are applied with nested blueprints. :issue:`4834`
+-   ``config.from_file`` can use ``text=False`` to indicate that the parser wants a
+    binary file instead. :issue:`4989`
+-   If a blueprint is created with an empty name it raises a ``ValueError``.
+    :issue:`5010`
+-   ``SESSION_COOKIE_DOMAIN`` does not fall back to ``SERVER_NAME``. The default is not
+    to set the domain, which modern browsers interpret as an exact match rather than
+    a subdomain match. Warnings about ``localhost`` and IP addresses are also removed.
+    :issue:`5051`
+-   The ``routes`` command shows each rule's ``subdomain`` or ``host`` when domain
+    matching is in use. :issue:`5004`
+-   Use postponed evaluation of annotations. :pr:`5071`
+
+
+Version 2.2.4
+-------------
+
+Released 2023-04-25
+
+-   Update for compatibility with Werkzeug 2.3.
+
+
+Version 2.2.3
+-------------
+
+Released 2023-02-15
+
+-   Autoescape is enabled by default for ``.svg`` template files. :issue:`4831`
+-   Fix the type of ``template_folder`` to accept ``pathlib.Path``. :issue:`4892`
+-   Add ``--debug`` option to the ``flask run`` command. :issue:`4777`
+
+
 Version 2.2.2
 -------------
 
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index d5e3a3f..24daa72 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -67,9 +67,29 @@ Include the following in your patch:
 .. _pre-commit: https://pre-commit.com
 
 
-First time setup
-~~~~~~~~~~~~~~~~
+First time setup using GitHub Codespaces
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+`GitHub Codespaces`_ creates a development environment that is already set up for the
+project. By default it opens in Visual Studio Code for the Web, but this can
+be changed in your GitHub profile settings to use Visual Studio Code or JetBrains
+PyCharm on your local computer.
+
+-   Make sure you have a `GitHub account`_.
+-   From the project's repository page, click the green "Code" button and then "Create
+    codespace on main".
+-   The codespace will be set up, then Visual Studio Code will open. However, you'll
+    need to wait a bit longer for the Python extension to be installed. You'll know it's
+    ready when the terminal at the bottom shows that the virtualenv was activated.
+-   Check out a branch and `start coding`_.
+
+.. _GitHub Codespaces: https://docs.github.com/en/codespaces
+.. _devcontainer: https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers
+
+First time setup in your local environment
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+-   Make sure you have a `GitHub account`_.
 -   Download and install the `latest version of git`_.
 -   Configure git with your `username`_ and `email`_.
 
@@ -78,102 +98,90 @@ First time setup
         $ git config --global user.name 'your name'
         $ git config --global user.email 'your email'
 
--   Make sure you have a `GitHub account`_.
 -   Fork Flask to your GitHub account by clicking the `Fork`_ button.
--   `Clone`_ the main repository locally.
+-   `Clone`_ your fork locally, replacing ``your-username`` in the command below with
+    your actual username.
 
     .. code-block:: text
 
-        $ git clone https://github.com/pallets/flask
+        $ git clone https://github.com/your-username/flask
         $ cd flask
 
--   Add your fork as a remote to push your work to. Replace
-    ``{username}`` with your username. This names the remote "fork", the
-    default Pallets remote is "origin".
-
-    .. code-block:: text
-
-        $ git remote add fork https://github.com/{username}/flask
-
--   Create a virtualenv.
-
+-   Create a virtualenv. Use the latest version of Python.
 
     - Linux/macOS
 
       .. code-block:: text
 
-         $ python3 -m venv env
-         $ . env/bin/activate
+         $ python3 -m venv .venv
+         $ . .venv/bin/activate
 
     - Windows
 
       .. code-block:: text
 
-         > py -3 -m venv env
-         > env\Scripts\activate
-
--   Upgrade pip and setuptools.
-
-    .. code-block:: text
-
-        $ python -m pip install --upgrade pip setuptools
+         > py -3 -m venv .venv
+         > .venv\Scripts\activate
 
--   Install the development dependencies, then install Flask in editable
-    mode.
+-   Install the development dependencies, then install Flask in editable mode.
 
     .. code-block:: text
 
+        $ python -m pip install -U pip setuptools wheel
         $ pip install -r requirements/dev.txt && pip install -e .
 
 -   Install the pre-commit hooks.
 
     .. code-block:: text
 
-        $ pre-commit install
+        $ pre-commit install --install-hooks
 
+.. _GitHub account: https://github.com/join
 .. _latest version of git: https://git-scm.com/downloads
 .. _username: https://docs.github.com/en/github/using-git/setting-your-username-in-git
 .. _email: https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/setting-your-commit-email-address
-.. _GitHub account: https://github.com/join
 .. _Fork: https://github.com/pallets/flask/fork
 .. _Clone: https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#step-2-create-a-local-clone-of-your-fork
 
+.. _start coding:
 
 Start coding
 ~~~~~~~~~~~~
 
--   Create a branch to identify the issue you would like to work on. If
-    you're submitting a bug or documentation fix, branch off of the
-    latest ".x" branch.
+-   Create a branch to identify the issue you would like to work on. If you're
+    submitting a bug or documentation fix, branch off of the latest ".x" branch.
 
     .. code-block:: text
 
         $ git fetch origin
         $ git checkout -b your-branch-name origin/2.0.x
 
-    If you're submitting a feature addition or change, branch off of the
-    "main" branch.
+    If you're submitting a feature addition or change, branch off of the "main" branch.
 
     .. code-block:: text
 
         $ git fetch origin
         $ git checkout -b your-branch-name origin/main
 
--   Using your favorite editor, make your changes,
-    `committing as you go`_.
--   Include tests that cover any code changes you make. Make sure the
-    test fails without your patch. Run the tests as described below.
--   Push your commits to your fork on GitHub and
-    `create a pull request`_. Link to the issue being addressed with
-    ``fixes #123`` in the pull request.
+-   Using your favorite editor, make your changes, `committing as you go`_.
+
+    -   If you are in a codespace, you will be prompted to `create a fork`_ the first
+        time you make a commit. Enter ``Y`` to continue.
+
+-   Include tests that cover any code changes you make. Make sure the test fails without
+    your patch. Run the tests as described below.
+-   Push your commits to your fork on GitHub and `create a pull request`_. Link to the
+    issue being addressed with ``fixes #123`` in the pull request description.
 
     .. code-block:: text
 
-        $ git push --set-upstream fork your-branch-name
+        $ git push --set-upstream origin your-branch-name
 
-.. _committing as you go: https://dont-be-afraid-to-commit.readthedocs.io/en/latest/git/commandlinegit.html#commit-your-changes
+.. _committing as you go: https://afraid-to-commit.readthedocs.io/en/latest/git/commandlinegit.html#commit-your-changes
+.. _create a fork: https://docs.github.com/en/codespaces/developing-in-codespaces/using-source-control-in-your-codespace#about-automatic-forking
 .. _create a pull request: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request
 
+.. _Running the tests:
 
 Running the tests
 ~~~~~~~~~~~~~~~~~
@@ -201,6 +209,9 @@ Generating a report of lines that do not have test coverage can indicate
 where to start contributing. Run ``pytest`` using ``coverage`` and
 generate a report.
 
+If you are using GitHub Codespaces, ``coverage`` is already installed
+so you can skip the installation command.
+
 .. code-block:: text
 
     $ pip install coverage
diff --git a/PKG-INFO b/PKG-INFO
index 5a0f292..fc7f4fd 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,19 +1,15 @@
 Metadata-Version: 2.1
 Name: Flask
-Version: 2.2.2
+Version: 2.3.2
 Summary: A simple framework for building complex web applications.
-Home-page: https://palletsprojects.com/p/flask
-Author: Armin Ronacher
-Author-email: armin.ronacher@active-4.com
-Maintainer: Pallets
-Maintainer-email: contact@palletsprojects.com
+Author-email: Armin Ronacher <armin.ronacher@active-4.com>
+Maintainer-email: Pallets <contact@palletsprojects.com>
 License: BSD-3-Clause
 Project-URL: Donate, https://palletsprojects.com/donate
 Project-URL: Documentation, https://flask.palletsprojects.com/
 Project-URL: Changes, https://flask.palletsprojects.com/changes/
 Project-URL: Source Code, https://github.com/pallets/flask/
 Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/
-Project-URL: Twitter, https://twitter.com/PalletsTeam
 Project-URL: Chat, https://discord.gg/pallets
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
@@ -26,7 +22,7 @@ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
 Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
-Requires-Python: >=3.7
+Requires-Python: >=3.8
 Description-Content-Type: text/x-rst
 Provides-Extra: async
 Provides-Extra: dotenv
@@ -111,6 +107,4 @@ Links
 -   PyPI Releases: https://pypi.org/project/Flask/
 -   Source Code: https://github.com/pallets/flask/
 -   Issue Tracker: https://github.com/pallets/flask/issues/
--   Website: https://palletsprojects.com/p/flask/
--   Twitter: https://twitter.com/PalletsTeam
 -   Chat: https://discord.gg/pallets
diff --git a/README.rst b/README.rst
index 3d1c388..4b7ff42 100644
--- a/README.rst
+++ b/README.rst
@@ -77,6 +77,4 @@ Links
 -   PyPI Releases: https://pypi.org/project/Flask/
 -   Source Code: https://github.com/pallets/flask/
 -   Issue Tracker: https://github.com/pallets/flask/issues/
--   Website: https://palletsprojects.com/p/flask/
--   Twitter: https://twitter.com/PalletsTeam
 -   Chat: https://discord.gg/pallets
diff --git a/artwork/LICENSE.rst b/artwork/LICENSE.rst
deleted file mode 100644
index 99c58a2..0000000
--- a/artwork/LICENSE.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright 2010 Pallets
-
-This logo or a modified version may be used by anyone to refer to the
-Flask project, but does not indicate endorsement by the project.
-
-Redistribution and use in source (SVG) and binary (renders in PNG, etc.)
-forms, with or without modification, are permitted provided that the
-following conditions are met:
-
-1.  Redistributions of source code must retain the above copyright
-    notice and this list of conditions.
-
-2.  Neither the name of the copyright holder nor the names of its
-    contributors may be used to endorse or promote products derived from
-    this software without specific prior written permission.
-
-We would appreciate that you make the image a link to
-https://palletsprojects.com/p/flask/ if you use it in a medium that
-supports links.
diff --git a/artwork/logo-full.svg b/artwork/logo-full.svg
deleted file mode 100644
index 8c0748a..0000000
--- a/artwork/logo-full.svg
+++ /dev/null
@@ -1,290 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="460"
-   height="180"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.47 r22583"
-   sodipodi:docname="logo.svg">
-  <defs
-     id="defs4">
-    <inkscape:perspective
-       sodipodi:type="inkscape:persp3d"
-       inkscape:vp_x="0 : 526.18109 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_z="744.09448 : 526.18109 : 1"
-       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
-       id="perspective10" />
-    <inkscape:perspective
-       id="perspective2824"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2840"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2878"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2894"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2910"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2926"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2976"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective3020"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective3036"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective3052"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective3866"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="0.98994949"
-     inkscape:cx="240.32415"
-     inkscape:cy="-37.836532"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     inkscape:window-width="1680"
-     inkscape:window-height="998"
-     inkscape:window-x="-8"
-     inkscape:window-y="-8"
-     inkscape:window-maximized="1" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(-27.820801,-24.714976)">
-    <path
-       style="fill:#000000"
-       d="M 96.944917,182.03377 C 89.662681,176.30608 81.894549,170.81448 76.586317,163.08166 65.416842,149.44499 56.816875,133.6567 50.937585,117.06515 47.383955,106.27654 46.166898,94.709824 41.585799,84.338096 c -4.792287,-7.533044 0.821224,-15.767897 9.072722,-18.16242 3.673742,-0.705104 10.133327,-4.170258 2.335951,-1.693539 -6.990592,5.128871 -7.667129,-4.655603 -0.498823,-5.27517 4.892026,-0.650249 6.692895,-4.655044 5.019966,-8.260251 -5.251326,-3.424464 12.733737,-7.18801 3.684373,-12.297799 -9.426987,-10.170666 13.186339,-12.128546 7.607283,-0.577786 -1.335447,8.882061 15.801226,-1.627907 11.825117,8.628945 4.041283,4.925694 15.133562,1.1211 14.85838,8.031392 5.887092,0.404678 7.907562,5.358061 13.433992,5.738347 5.72759,2.586557 16.1108,4.624792 18.0598,11.079149 -5.68242,4.498756 -18.84089,-9.292674 -19.47305,3.160397 1.71659,18.396078 1.27926,37.346439 8.00986,54.864989 3.18353,10.60759 10.9012,18.95779 17.87109,27.21946 6.66875,8.09126 15.70186,13.78715 24.90885,18.58338 8.07647,3.80901 16.78383,6.33528 25.58583,7.92044 3.5701,-2.7307 9.87303,-12.8828 15.44238,-8.60188 0.26423,4.81007 -11.0541,10.05512 -0.53248,9.5235 6.17819,-1.86378 10.46336,4.77803 15.55099,-1.21289 4.68719,5.55206 19.48197,-3.54734 16.14693,7.80115 -4.50972,2.90955 -11.08689,1.15142 -15.60404,5.15397 -7.44757,-3.71979 -13.37691,3.32843 -21.6219,2.43707 -9.15641,1.64002 -18.4716,2.30204 -27.75473,2.31642 -15.22952,-1.20328 -30.78158,-1.71049 -45.26969,-7.01291 -8.16166,-2.37161 -16.12649,-7.01887 -23.299683,-11.66829 z m 12.862043,5.5729 c 7.9696,3.44651 15.76243,7.07889 24.49656,8.17457 13.85682,1.92727 28.16653,4.89163 42.07301,2.18757 -6.2939,-2.84199 -12.80077,1.10719 -19.07096,-2.0322 -7.52033,1.61821 -15.59049,-0.41223 -23.23574,-1.41189 -8.69395,-3.87259 -18.0762,-6.53549 -26.21772,-11.56219 -10.173155,-3.71578 5.26142,4.76524 8.00873,5.45214 6.35952,3.60969 -6.99343,-1.85044 -8.87589,-3.35101 -5.32648,-2.9879 -6.00529,-2.36357 -0.52745,0.67085 1.10332,0.64577 2.19359,1.32226 3.34946,1.87216 z M 94.642259,176.88976 c 7.722781,2.86052 -0.03406,-5.43082 -3.572941,-4.94904 -1.567906,-2.72015 -5.9903,-4.43854 -2.870721,-5.89973 -5.611524,1.9481 -5.878319,-7.40814 -8.516004,-6.07139 -5.936516,-1.87454 -2.310496,-8.51501 -9.381929,-12.59292 -0.645488,-4.29697 -7.02577,-8.02393 -9.060801,-14.50525 -0.898786,-3.31843 -7.208336,-12.84783 -3.332369,-3.97927 3.300194,8.53747 9.106618,15.84879 13.93868,23.15175 3.752083,6.95328 8.182497,14.22026 15.015767,18.55788 2.303436,2.20963 4.527452,5.59533 7.780318,6.28797 z M 72.39456,152.46355 c 0.26956,-1.16626 1.412424,2.52422 0,0 z m 31.49641,27.85526 c 1.71013,-0.76577 -2.45912,-0.96476 0,0 z m 4.19228,1.52924 c -0.43419,-2.1116 -1.91376,1.18074 0,0 z m 5.24749,2.18891 c 2.49828,-2.37871 -3.85009,-1.49983 0,0 z m 8.99389,5.01274 c 1.51811,-2.2439 -4.85872,-0.84682 0,0 z m -17.2707,-12.03933 c 3.88031,-2.51023 -5.01186,-0.0347 0,0 z m 3.9366,1.96293 c -0.11004,-1.32709 -1.40297,0.59432 0,0 z m 19.67473,12.28006 c 3.16281,1.99601 18.46961,4.3749 8.88477,0.81847 -1.60377,0.33811 -17.77263,-4.57336 -8.88477,-0.81847 z M 97.430958,166.92721 c -0.307503,-1.33094 -4.909341,-1.4694 0,0 z m 9.159302,5.33813 c 2.38371,-1.66255 -4.94757,-1.28235 0,0 z m 7.70426,4.72382 c 3.42065,-1.28963 -5.54907,-1.29571 0,0 z M 93.703927,162.86805 c 3.711374,2.84621 14.967683,0.36473 5.683776,-1.69906 -4.225516,-2.2524 -13.74889,-3.79415 -7.25757,1.35821 l 1.573785,0.34088 9e-6,-3e-5 z m 25.808723,15.75216 c 1.54595,-2.63388 -6.48298,-1.50411 0,0 z m -7.84249,-6.23284 c 9.0752,2.56719 -7.63142,-5.739 -2.23911,-0.94466 l 1.19513,0.54082 1.04399,0.4039 -1e-5,-6e-5 z m 15.72354,9.0878 c 8.59474,0.082 -7.76304,-1.18486 0,1e-5 l 0,-1e-5 z M 90.396984,157.89545 c -0.335695,-1.60094 -2.120962,0.13419 0,0 z m 51.535396,31.73502 c 0.2292,-2.89141 -2.80486,2.15157 0,0 z m -36.86817,-22.75299 c -0.51986,-1.52251 -2.68548,-0.0622 0,0 z m -13.852128,-9.98649 c 4.934237,-0.29629 -6.755322,-2.17418 0,0 z M 74.802387,146.28394 c -0.614146,-2.36536 -5.369213,-4.2519 0,0 z m 43.079323,27.33941 c -0.90373,-1.0307 -0.4251,0.22546 0,0 z m 26.81408,16.45475 c -0.086,-1.57503 -1.46039,0.59616 0,0 z m -29.18712,-18.90528 c 0.48266,-2.02932 -4.20741,-0.61442 0,0 z M 95.532612,158.51286 c 3.670785,-0.39305 -5.880434,-2.48161 0,0 z M 129.32396,179.51 c 5.72042,-2.26627 -5.57541,-1.10635 0,0 z m -17.57682,-11.93145 c 6.59278,0.85002 -7.84442,-4.48425 -1.44651,-0.4773 l 1.4465,0.47734 1e-5,-4e-5 z m 22.91296,14.0886 c 6.15514,-3.67975 4.12588,8.61677 10.44254,1.0388 6.23086,-4.54942 -5.38086,5.62451 2.29838,0.81116 5.55359,-3.71438 13.75643,1.76075 18.93848,3.5472 3.72659,-0.18307 7.34938,3.22236 11.16973,1.15059 7.3542,-1.98082 -14.38097,-2.93789 -8.68344,-6.4523 -6.72914,1.95848 -11.70093,-2.33483 -15.01213,-6.64508 -7.54812,-1.74298 -16.27548,-5.602 -20.04257,-12.28184 -1.5359,-2.50802 2.21884,0.35333 -1.32586,-3.74638 -4.54834,-4.04546 -6.81948,-8.63766 -9.87278,-13.5552 -3.64755,-1.94587 -4.07249,-7.67345 -4.44123,-0.19201 0.0289,-4.72164 -4.40393,-7.89964 -5.48589,-6.57859 -0.0194,-4.54721 4.74396,-2.26787 1.40945,-5.63228 -0.71771,-4.71302 -3.08085,-9.6241 -3.79115,-14.9453 -1.1036,-2.56502 -0.15541,-8.05863 -3.76662,-2.25204 -1.31566,6.13669 -0.43668,-7.54129 1.6093,-3.03083 2.68543,-4.60251 -0.9641,-4.0612 -1.11361,-3.42211 1.74931,-3.88333 1.10719,-9.39159 -0.45644,-7.29023 0.93213,-4.11586 1.47259,-15.147529 -1.3951,-13.192579 1.73833,-4.303958 3.29668,-19.694077 -4.24961,-13.826325 -3.058358,0.04294 -8.354541,1.110195 -10.858032,2.355243 7.849502,4.326857 -0.789543,1.562577 -3.984808,0.874879 -0.416343,4.003642 -3.58119,2.272086 -7.535123,2.311339 6.315273,0.781339 -3.075253,6.458962 -6.698132,4.253506 -4.705102,2.248756 4.060621,7.862038 0.0944,9.597586 0.487433,2.616581 -7.208227,-0.944906 -6.603832,5.097711 -4.56774,-1.92155 -0.628961,7.16796 1.656273,4.09382 7.768882,2.10261 5.469108,6.89631 5.666947,11.44992 -1.265833,2.6534 -6.249495,-6.23691 -1.109939,-5.82517 -4.054715,-6.58674 -4.485232,-2.38081 -7.854566,0.67911 -0.783857,0.22222 8.5944,4.35376 2.709059,6.3967 5.177884,0.79894 5.325199,5.33008 6.379284,8.19735 3.11219,3.24152 2.475226,-3.57931 6.199071,0.31623 -2.356488,-3.4705 -12.48183,-9.77839 -4.329567,-7.7553 -0.04358,-3.49291 -1.474412,-6.30951 1.02322,-6.24118 2.473367,-4.47926 -2.590385,11.044 2.984725,5.35124 1.543285,-0.67388 1.92554,-4.48494 4.699544,0.35989 4.029096,3.96363 1.45533,6.83577 -4.228162,3.20648 1.016828,3.44946 7.603062,4.68217 6.365348,10.07646 1.3121,4.7444 3.147844,2.99695 4.747999,2.72266 1.25523,4.60973 1.968016,1.2201 2.027559,-0.97355 5.747357,1.23033 4.401142,4.62773 6.199456,7.00134 3.960416,1.78761 -5.668696,-12.11713 1.130659,-4.18106 7.153577,6.4586 2.682797,9.15464 -3.736856,8.11995 4.063129,-0.32824 5.373423,5.49305 10.455693,5.28853 4.63456,2.20477 7.77237,10.67291 -0.21613,7.1478 -2.77074,-2.49821 -12.575734,-5.5801 -4.56731,-0.82823 7.39657,3.42523 13.27117,5.47432 20.40487,9.77384 5.10535,3.64464 7.31104,7.81908 9.24607,8.64541 -4.29084,2.04946 -12.93089,-1.63655 -6.51514,-2.76618 -4.00168,-0.72894 -8.50258,-2.75259 -4.66961,2.2333 3.25926,2.72127 11.54708,2.43298 13.0328,2.74132 -1.25934,2.77488 -3.4207,2.99556 0.0516,3.21078 -3.87375,2.06438 1.24216,2.38403 1.60114,3.56362 z m -7.9215,-22.36993 c -2.35682,-2.46475 -2.9662,-7.08134 -0.41852,-3.06426 1.30648,0.52466 4.18523,7.54428 0.41857,3.06426 l -5e-5,0 z m 25.79733,16.38693 c 1.47004,-0.0952 0.0427,1.11681 0,0 z m -29.51867,-22.43039 c -0.0904,-3.72637 0.8525,2.87419 0,0 z m -2.56392,-3.44965 c -2.96446,-5.72787 3.73721,1.62212 0,0 z M 89.382646,128.35916 c 1.7416,-0.46446 0.856841,2.97864 0,0 z m 24.728294,13.40357 c 1.06957,-4.01654 1.25692,3.37014 0,0 z M 96.64115,129.61525 c -1.231543,-2.21638 2.576009,2.07865 0,0 z m 14.99279,4.80618 c -2.80851,-6.29223 1.98836,-3.43699 0.62135,1.03124 l -0.62135,-1.03124 0,0 z M 85.778757,117.17864 c -1.255624,-2.06432 -3.332663,-8.12135 -2.663982,-9.97042 0.604935,3.0114 6.403914,12.95956 2.844571,4.12096 -3.933386,-7.40908 4.701805,2.40491 5.590052,4.2529 0.413624,1.83837 -2.426789,-0.50225 -0.502192,3.80828 -3.509809,-4.90766 -2.071967,2.71088 -5.268449,-2.21172 z m -7.990701,-5.50612 c 0.328938,-4.79981 1.829262,3.29132 0,0 z m 3.594293,1.23728 c 1.715175,-3.62282 2.908243,5.05052 0,0 z m -8.64616,-6.68847 c -2.974956,-2.95622 -5.127809,-5.68132 0.139193,-1.83474 2.029482,0.0792 -4.509002,-6.19705 0.488751,-1.99305 5.25531,0.95822 2.5951,8.61674 -0.627944,3.82779 z m 4.541717,-0.11873 c 1.727646,-1.71203 0.917172,1.6853 0,0 z m 2.794587,0.8959 c -2.619181,-4.9094 3.178801,2.05822 0,0 z m -5.55546,-5.30909 c -8.64844,-7.696511 10.867309,4.02451 1.4129,1.4269 l -1.412955,-1.42683 5.5e-5,-7e-5 z m 24.77908,14.39717 c -3.742506,-2.24398 -0.991777,-15.79747 0.284503,-6.52785 3.638294,-1.17695 -0.200879,4.78728 2.512784,4.73208 -0.42767,3.76305 -1.64169,5.11594 -2.797287,1.79577 z m 9.165207,5.41684 c 0.36705,-4.08462 0.77249,2.79262 0,0 z m -1.59198,-1.57295 c 0.41206,-1.74497 0.0426,2.05487 0,0 z M 76.213566,99.16032 c -5.556046,-7.665657 16.147323,7.75413 3.558556,1.9443 -1.315432,-0.34404 -2.898208,-0.46688 -3.558556,-1.9443 z m 17.649112,9.35749 c -0.525779,-6.45461 1.174169,1.06991 -1.92e-4,-2e-5 l 1.92e-4,2e-5 z m 13.399762,8.59585 c 1.03698,-3.67668 0.0773,2.43221 0,0 z M 77.064685,96.23472 c 3.302172,-0.706291 13.684695,5.79939 4.150224,1.85832 -1.059396,-1.17279 -3.317802,-0.63994 -4.150224,-1.85832 z m 28.356745,14.13312 c 0.35296,-6.60002 1.97138,-3.94233 0.0122,0.94474 l -0.0121,-0.94473 -5e-5,-1e-5 z M 79.52277,93.938099 c 1.345456,-1.97361 -3.571631,-8.923063 0.708795,-2.492797 1.849543,1.469605 5.355103,2.461959 2.260017,3.080216 4.867744,4.294162 -1.187244,1.163612 -2.968812,-0.587419 z m 24.49612,14.368161 c 0.92952,-7.51843 0.81971,4.40485 0,0 z M 76.712755,86.993902 c 1.027706,-0.439207 0.542746,1.369335 0,0 z m 6.389622,3.803092 c 1.644416,-3.450522 3.03351,3.848297 0,0 z m 18.023553,10.026276 c -0.0174,-1.3252 0.34003,1.92765 0,0 z m -1.04404,-2.31139 c -2.501612,-6.171646 2.32693,3.26759 0,0 z m -1.536003,-4.046372 c -0.419906,-2.550188 1.427129,3.203862 -7.3e-5,-9e-6 l 7.3e-5,9e-6 z m 2.499773,-4.063514 c -1.71663,-3.025123 2.16777,-13.331073 2.60122,-6.939418 -1.81185,4.980256 -0.52268,7.766309 0.74129,1.086388 2.33417,-5.257159 -0.50421,10.374054 -3.34255,5.853057 l 4e-5,-2.7e-5 z m 2.56889,-15.326649 c 0.74833,-0.918921 0.16609,1.107082 0,0 z m -4.290016,84.534235 c -1.017552,-0.88802 0.127775,0.56506 0,0 z m 8.837726,4.47065 c 4.91599,1.26135 4.89086,-0.76487 0.44782,-1.36683 -2.3898,-2.22316 -9.930475,-4.58124 -3.18119,-0.27586 0.44699,1.13227 1.85944,1.10589 2.73337,1.64269 z M 90.708067,152.48725 c 2.708244,2.01956 10.201213,5.72375 3.858186,0.76868 2.138588,-2.48467 -4.093336,-3.80722 -2.026067,-5.46927 -5.258175,-3.21755 -4.147962,-2.93133 -0.464111,-2.8301 -6.319385,-2.82462 0.912163,-2.61333 0.571661,-4.06067 -2.436706,-0.48126 -12.103074,-4.29664 -6.416395,0.31341 -5.780887,-2.94751 -1.377603,1.09799 -3.12488,0.67029 -5.911336,-1.61178 5.264392,4.50224 -0.938845,2.98448 3.391327,2.6875 9.128301,6.88393 1.433786,2.84407 -1.013816,1.45934 5.506273,3.67136 7.106665,4.77911 z m 9.243194,5.31013 c 11.238769,3.62163 -5.510018,-4.4246 0,0 z m 47.316399,28.66432 c 0.14496,-2.22965 -1.53604,1.90201 0,0 z m 4.86324,2.04679 c 2.59297,-2.51255 0.106,4.00222 4.29655,-0.61509 0.0453,-3.30544 -0.12904,-5.25783 -4.81563,-1.24252 -1.29194,0.71648 -1.86871,3.76288 0.51908,1.85761 z M 74.932378,140.02637 c -0.796355,-3.1304 -5.581949,-3.11418 0,0 z m 5.193029,3.40294 c -1.928397,-3.19739 -6.880525,-2.89469 0,0 z m 29.543373,17.81697 c 2.8844,2.56199 13.24761,1.87984 3.50331,0.31527 -1.44321,-2.13386 -9.16415,-1.6203 -3.50331,-0.31527 z m 40.61236,25.08153 c 4.43933,-3.72512 -4.30122,1.66183 0,0 z m 9.2328,6.34473 c 0.0277,-1.19543 -1.91352,0.52338 0,0 z m 0.0142,-1.6736 c 4.91602,-5.20866 -4.76346,0.30807 -4e-5,0 l 4e-5,0 z M 62.15981,129.33339 c -4.189944,-5.97826 -2.604586,-8.66544 -6.645136,-13.54677 -0.764913,-3.73279 -6.931672,-12.20326 -3.189579,-3.22947 3.42754,5.24836 4.446054,13.37434 9.834715,16.77624 z m 95.82635,60.00977 c 9.04429,-5.84575 -3.7125,-2.54641 0,0 z m 6.9041,2.70461 c 4.52911,-3.88867 -2.86491,-0.81334 0,0 z M 73.393094,133.41838 c 1.296204,-1.92838 -3.347642,-0.24666 0,0 z m 90.055596,56.78275 c 4.38526,-2.82746 -1.01036,-2.39335 -0.79483,0.26003 l 0.79484,-0.26003 -1e-5,0 z m -59.51386,-37.51178 c -0.15075,-1.90924 -2.31574,0.16206 0,0 z m 3.67794,2.11629 c -1.16888,-2.36318 -1.79716,0.37121 0,0 z m 62.8725,37.30625 c 5.61806,-4.05283 -3.4056,-0.77594 -1.17927,0.76785 l 1.17927,-0.76785 0,0 z m -2.15131,-1.03979 c 4.57663,-3.83506 -4.83183,1.69954 0,0 z m 10.99163,7.31983 c 3.0728,-2.05816 -3.73316,-0.66575 0,0 z M 76.211249,132.02781 c 4.118965,0.92286 16.460394,10.1439 9.179466,0.63772 -3.728991,-1.10384 -1.492605,-10.21906 -5.29621,-8.60579 2.552972,4.2649 2.100461,6.08018 -3.259642,3.3914 -6.736808,-3.28853 -3.785888,1.6297 -2.469293,2.98518 -1.794185,0.40772 2.373226,1.5572 1.845679,1.59149 z m -18.76588,-14.82026 c 0.737407,-3.04991 -6.789814,-16.77881 -3.554464,-6.87916 1.167861,2.07373 1.049123,6.00387 3.554464,6.87916 z m 34.443451,21.23513 c -2.120989,-1.77378 -0.100792,-0.25103 0,0 z m 5.222997,1.21548 c -0.0027,-3.23079 -5.77326,-1.31196 0,0 z m 45.261473,28.53321 c -0.86326,-2.20739 -3.41229,-0.0512 8e-5,4e-5 l -8e-5,-4e-5 z m 2.17351,1.58769 c -0.32087,-1.23546 -1.25399,0.23848 0,0 z m 17.94015,11.3001 c 1.72546,-1.27472 -2.15318,-0.1628 0,0 z M 66.819057,119.6006 c 4.935243,-1.91072 -5.28775,-1.36248 0,0 z m 71.569733,45.08937 c -0.0549,-3.19499 -3.14622,0.79264 0,0 z M 64.869152,115.05675 c 3.170167,-1.07084 -2.932663,-0.70531 0,0 z m 9.201532,4.45726 c -0.0575,-1.05014 -0.973336,0.39747 0,0 z m 112.231406,68.82181 c 4.0765,-0.8265 13.36606,2.07489 14.86752,-1.08086 -4.95044,-0.12019 -17.12734,-3.49263 -17.70346,0.80479 l 1.08368,0.17072 1.75226,0.10534 0,1e-5 z M 76.995161,120.25099 c 0.07087,-3.23755 -2.524669,-0.12092 0,0 z M 52.801998,103.4687 c -1.098703,-6.16843 -4.178791,-0.93357 0,0 z m 5.769195,1.45013 c 0.07087,-1.9807 -5.280562,-1.78224 0,0 z m 3.296917,1.61923 c -0.953019,-0.77196 -0.745959,0.97521 0,0 z m 20.744719,13.30775 c 0.976615,-0.89718 -2.312116,-0.66455 0,0 z M 59.672204,102.88617 c -0.557624,-4.65897 -6.681999,-0.69805 0,0 z M 47.844441,95.21166 c -0.168219,-2.150189 -1.152625,0.81111 0,0 z m 1.759336,-1.328672 c -0.28703,-2.549584 -1.510515,0.324387 0,0 z m 9.720792,5.802442 c 4.110486,-1.61465 -7.487254,-3.33984 -0.839893,-0.30506 l 0.839893,0.30506 z m 130.097601,80.35913 c 2.63092,-2.4121 -3.34373,-0.74577 0,0 z m 15.71669,8.14691 c 1.05433,-3.1186 -2.65452,0.41058 0,0 z M 60.318012,94.590436 c 0.433018,-3.016773 -3.258762,0.59902 0,0 z M 46.487687,85.324242 c -0.742965,-4.25911 -0.64134,-11.735065 6.465133,-9.208583 -9.485962,1.883339 6.56534,11.790095 4.538357,3.968363 3.988626,0.195294 7.802669,-2.357284 5.709487,1.516403 7.85876,-0.867958 13.307129,-7.682612 20.898169,-6.72768 5.913058,-0.782493 12.378182,-1.375955 18.750257,-3.756157 5.23905,-0.37743 10.28235,-6.018062 7.41068,-9.361383 -7.14456,-0.604513 -14.62339,0.289393 -22.520112,1.858993 -8.750559,1.819117 -16.699014,5.275307 -25.528125,6.758866 -8.605891,1.15604 1.730998,3.185165 -0.734074,3.637227 -4.490681,1.558136 5.355488,2.608852 -0.582182,4.251428 C 57.228283,77.56448 53.411411,76.304535 54.977788,72.440196 46.7341,73.50992 39.490264,76.931325 46.003276,85.320342 l 0.484402,0.0037 9e-6,-2.56e-4 z m 19.864291,-10.1168 c 1.932856,-7.120464 10.355229,5.859274 3.168052,0.945776 -0.858453,-0.642457 -2.2703,-1.166588 -3.168052,-0.945776 z m 0.376038,-3.452197 c 2.789661,-2.078257 1.482964,1.16516 0,0 z m 3.542213,0.05622 c 0.251833,-3.27648 8.108752,1.73455 1.295517,1.179499 l -1.295517,-1.179499 0,0 z m 4.84543,-1.948193 c 1.769481,-2.067535 0.50862,1.83906 0,0 z m 1.239563,-0.83005 c 2.946379,-3.540216 16.68561,-2.259413 6.628966,-0.34519 -2.695543,-2.029363 -4.761797,1.196575 -6.628966,0.34519 z m 17.930017,-2.763886 c -0.448199,-9.670222 8.907771,3.436477 0,0 z m 5.087539,-0.02784 c 1.860022,-4.873906 7.218072,-1.955774 0.860732,-0.979407 0.13805,0.518656 -0.18642,2.516173 -0.860732,0.979407 z M 58.311842,92.088739 c 5.55753,-3.403212 -5.899945,-2.952541 0,0 l 0,0 z m 4.109214,1.141866 c 1.948513,-2.071884 -4.233857,-0.840369 0,0 z M 50.313395,84.63767 c 3.175569,-2.439416 -3.757842,-0.927473 0,0 z M 214.41578,187.30012 c 0.0918,-2.83019 -2.42718,1.27537 0,0 z m -16.67487,-11.37935 c 0.47417,-3.25845 -2.14286,0.28408 0,0 z m 21.26022,12.47672 c 4.43994,0.015 13.45265,-1.37884 3.79217,-1.37442 -1.51594,0.23641 -8.83311,0.18571 -3.79216,1.37439 l -1e-5,3e-5 z M 66.035603,91.23339 c 3.593258,-0.246807 5.621861,-3.963629 -0.694932,-3.749977 -9.789949,-1.013541 8.637508,3.352129 -1.255898,2.10383 -1.329368,0.880346 1.873606,1.889721 1.95083,1.646147 z m 3.164618,1.601748 c -0.375177,-2.307063 -1.111156,1.225591 0,0 z m 3.753896,-10.009901 c 1.559281,-1.934055 -2.157697,-0.517053 0,0 z M 61.003998,62.84999 c 6.412879,-2.181631 15.182392,-4.633087 18.210335,1.074184 -3.081589,-3.70893 -1.24361,-7.360157 1.666959,-1.937407 4.115576,5.486669 6.175915,-2.495489 3.499086,-4.335821 3.050468,3.790246 6.520044,5.581281 2.042429,0.239564 4.865693,-5.852929 -9.742712,0.766433 -13.063105,0.699775 -1.597564,0.717062 -16.493576,3.79889 -12.355704,4.259705 z m 3.75831,-7.197834 c 3.657324,-2.760416 12.648968,1.641989 6.879078,-2.743367 -0.564117,-0.498292 -12.636077,3.325475 -6.879078,2.743367 z m 13.333489,0.550473 c 4.280389,0.109225 -1.84632,-5.750287 3.254304,-3.095159 -0.837696,-2.736627 -5.938558,-3.248956 -8.432316,-4.342312 -1.410474,2.502054 2.870977,7.471102 5.178012,7.437471 z M 67.100291,44.099162 c 1.480803,-2.007406 -2.59521,1.017699 0,0 z m 5.449586,1.304353 c 6.897867,-0.914901 -1.758292,-2.970542 -1.389954,-0.07352 l 1.389954,0.07352 0,-9e-6 z M 62.374386,37.441437 c -4.856866,-6.340205 9.133987,1.065769 4.199411,-5.572646 -4.153254,-3.307245 -8.144297,3.721775 -4.199411,5.572646 z m 62.330124,33.572802 c 2.22762,-3.948988 -9.19697,-5.323011 -1.5009,-1.399578 0.70858,0.236781 0.54821,1.6727 1.5009,1.399578 z"
-       id="path2900" />
-    <g
-       style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
-       id="text3850">
-      <path
-         d="m 229.85182,43.77803 c -0.79695,3.140714 -1.28913,8.414146 -1.47656,15.820313 -7e-5,1.453199 -0.65632,2.179761 -1.96875,2.179687 -1.31257,7.4e-5 -2.22663,-0.632738 -2.74219,-1.898437 -1.40631,-3.421796 -2.74225,-5.812419 -4.00781,-7.171875 -1.50006,-1.593666 -3.49225,-2.554603 -5.97656,-2.882813 -2.67193,-0.421789 -9.32818,-0.632726 -19.96875,-0.632812 -2.43754,8.6e-5 -4.03129,0.257898 -4.78125,0.773437 -0.46879,0.32821 -0.70316,1.031335 -0.70313,2.109375 l 0,31.851563 c -3e-5,1.078175 0.67966,1.5938 2.03906,1.546875 4.17184,-0.04682 10.21871,-0.328075 18.14063,-0.84375 1.54682,-0.187449 2.58979,-0.691355 3.12891,-1.511719 0.539,-0.820259 1.06634,-2.941351 1.58203,-6.363281 0.32806,-1.87494 1.42963,-2.601502 3.30468,-2.179688 1.59369,0.328186 2.27338,1.054747 2.03907,2.179688 -1.31256,6.375052 -1.73444,14.671919 -1.26563,24.890627 0.0468,1.21878 -0.72662,1.87503 -2.32031,1.96875 -1.31256,0.14065 -2.13287,-0.56247 -2.46094,-2.10938 -1.2188,-5.859333 -3.48052,-8.988236 -6.78515,-9.386716 -3.30474,-0.398394 -8.68364,-0.597612 -16.13672,-0.597656 -0.84379,4.4e-5 -1.26566,0.304731 -1.26563,0.914062 l 0,31.64063 c -3e-5,2.34375 0.86716,3.9375 2.60156,4.78125 1.35934,0.70313 4.28903,1.33594 8.78907,1.89843 2.29683,0.23438 3.30464,1.24219 3.02343,3.02344 -0.28129,1.54688 -2.34379,2.15625 -6.1875,1.82813 -11.1094,-0.89063 -20.27345,-0.84375 -27.49218,0.14062 -2.01564,0.28125 -3.02345,-0.53906 -3.02344,-2.46094 -1e-5,-1.21874 1.0078,-1.92187 3.02344,-2.10937 4.59373,-0.51562 6.8906,-4.54687 6.89062,-12.09375 l 0,-60.187502 c -2e-5,-3.093671 -0.5508,-5.472575 -1.65234,-7.136719 -1.10158,-1.663977 -3.15236,-3.175695 -6.15235,-4.535156 -1.87501,-0.843661 -2.57813,-1.992098 -2.10937,-3.445313 0.23436,-0.890532 0.60936,-1.382719 1.125,-1.476562 0.46874,-0.140532 1.71092,-0.04678 3.72656,0.28125 2.95311,0.468842 9.91404,0.703217 20.88281,0.703125 12.93746,9.2e-5 24.11713,-0.281158 33.53907,-0.84375 3.14055,-0.187407 4.71086,0.07041 4.71093,0.773437 -7e-5,0.187592 -0.0235,0.375092 -0.0703,0.5625 z"
-         style="font-size:144px;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2830" />
-      <path
-         d="m 275.55495,133.14522 c -4e-5,1.875 -1.05473,2.69531 -3.16407,2.46094 -6.46877,-0.60938 -14.48439,-0.51563 -24.04687,0.28125 -1.92189,0.18749 -3.10548,0.14062 -3.55078,-0.14063 -0.44532,-0.28125 -0.66798,-1.05469 -0.66797,-2.32031 -1e-5,-1.125 1.27733,-2.07422 3.83203,-2.84766 2.55467,-0.77343 3.83202,-3.08202 3.83203,-6.92578 l 0,-63.632812 c -1e-5,-3.796796 -0.55079,-6.585856 -1.65234,-8.367188 -1.10158,-1.781164 -3.03517,-3.163975 -5.80078,-4.148437 -1.45313,-0.515537 -2.1797,-1.242099 -2.17969,-2.179688 -1e-5,-1.406158 1.05468,-2.460845 3.16406,-3.164062 3.18749,-1.031156 6.49217,-2.624904 9.91406,-4.78125 2.81248,-1.687401 4.59373,-2.53115 5.34375,-2.53125 1.73435,1e-4 2.60154,1.195412 2.60157,3.585937 -3e-5,-0.187403 -0.0938,2.156345 -0.28125,7.03125 -0.14065,4.64071 -0.18753,9.211018 -0.14063,13.710938 l 0.28125,62.789062 c -2e-5,2.85939 0.7031,4.9336 2.10938,6.22266 1.40622,1.28906 3.82028,2.14453 7.24218,2.5664 2.10934,0.23438 3.16403,1.03126 3.16407,2.39063 z"
-         style="font-size:144px;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2832" />
-      <path
-         d="m 339.67995,128.43428 c -7e-5,0.98438 -1.79303,2.47266 -5.37891,4.46484 -3.58599,1.99219 -6.45708,2.98828 -8.61328,2.98829 -1.82817,-10e-6 -3.44536,-0.89063 -4.85156,-2.67188 -1.40629,-1.78125 -2.39067,-2.67187 -2.95313,-2.67187 -0.42191,0 -2.64847,0.96094 -6.67969,2.88281 -4.03128,1.92187 -8.08596,2.88281 -12.16406,2.88281 -3.84377,0 -7.0547,-1.125 -9.63281,-3.375 -2.81251,-2.48437 -4.21876,-5.85937 -4.21875,-10.125 -1e-5,-8.10935 9.28123,-13.92185 27.84375,-17.4375 3.18746,-0.60934 4.80465,-1.89841 4.85156,-3.86719 l 0.14063,-4.499997 c 0.28121,-7.687454 -3.11723,-11.5312 -10.19532,-11.53125 -2.01565,5e-5 -3.9258,1.804735 -5.73046,5.414062 -1.80471,3.609416 -4.39456,5.554727 -7.76954,5.835938 -3.84376,0.375038 -5.76563,-1.242148 -5.76562,-4.851563 -1e-5,-2.249954 2.85936,-4.874951 8.57812,-7.875 5.99998,-3.14057 11.7656,-4.710881 17.29688,-4.710937 9.51558,5.6e-5 14.22651,4.523489 14.13281,13.570312 l -0.28125,28.968755 c -0.0469,3.04688 1.24214,4.57032 3.86719,4.57031 0.51557,1e-5 1.49994,-0.11718 2.95312,-0.35156 1.45307,-0.23437 2.29682,-0.35156 2.53125,-0.35157 1.35932,1e-5 2.039,0.91407 2.03907,2.74219 z M 318.0237,112.40303 c 0.0468,-1.17185 -0.2227,-1.94529 -0.8086,-2.32031 -0.58597,-0.37498 -1.51175,-0.44529 -2.77734,-0.21094 -11.2969,2.01565 -16.94533,5.69533 -16.94531,11.03906 -2e-5,5.39064 2.92966,8.08595 8.78906,8.08594 2.34372,1e-5 4.75778,-0.44531 7.24219,-1.33594 2.90621,-1.03124 4.35933,-2.27342 4.35937,-3.72656 z"
-         style="font-size:144px;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2834" />
-      <path
-         d="m 392.13307,120.06709 c -5e-5,4.96876 -1.9102,8.91798 -5.73047,11.84766 -3.82035,2.92969 -9.03519,4.39453 -15.64453,4.39453 -4.40627,0 -8.81252,-0.46875 -13.21875,-1.40625 -3.79688,-0.84375 -6.00001,-1.61719 -6.60937,-2.32031 -0.37501,-0.65625 -0.56251,-3.86718 -0.5625,-9.63281 -1e-5,-2.48436 0.56249,-3.77343 1.6875,-3.86719 1.12499,-0.14061 2.08592,0.46876 2.88281,1.82812 3.51561,6.14064 9.18748,9.21095 17.01562,9.21094 6.60934,1e-5 9.91403,-2.29687 9.91407,-6.89062 -4e-5,-2.01562 -0.75004,-3.70311 -2.25,-5.0625 -1.64066,-1.54686 -4.82816,-3.35155 -9.5625,-5.41407 -6.84377,-3.04685 -11.41408,-5.71872 -13.71094,-8.01562 -2.48439,-2.43747 -3.72657,-5.718716 -3.72656,-9.843752 -1e-5,-5.062455 1.9453,-8.999951 5.83593,-11.8125 3.60936,-2.718695 8.43748,-4.078069 14.48438,-4.078125 3.79684,5.6e-5 7.26559,0.304743 10.40625,0.914062 3.37496,0.60943 5.13277,1.359429 5.27344,2.25 0.37495,2.625051 1.14839,6.421922 2.32031,11.390625 0.14058,0.609416 -0.51567,1.101603 -1.96875,1.476563 -1.54692,0.328165 -2.57817,0.07035 -3.09375,-0.773438 -3.70317,-6.046828 -8.39066,-9.070262 -14.0625,-9.070312 -6.4219,5e-5 -9.63283,2.062548 -9.63281,6.1875 -2e-5,2.296916 0.86716,4.12504 2.60156,5.484375 1.54685,1.171912 5.17966,3.000035 10.89844,5.484372 5.99996,2.57816 10.07808,4.89847 12.23437,6.96094 2.81245,2.6719 4.2187,6.25783 4.21875,10.75781 z"
-         style="font-size:144px;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2836" />
-      <path
-         d="m 473.69557,132.72334 c -7e-5,1.64063 -1.10163,2.50782 -3.30469,2.60157 -3.28131,0.0469 -7.57037,0.28124 -12.86718,0.70312 -2.62506,0.51562 -4.50006,0.1875 -5.625,-0.98437 -7.4063,-7.96875 -13.68754,-16.31249 -18.84375,-25.03125 -0.42191,-0.74998 -0.96097,-1.12498 -1.61719,-1.125 -0.79691,2e-5 -2.17972,0.70315 -4.14844,2.10937 -2.20315,1.21877 -3.30471,2.95315 -3.30469,5.20313 -2e-5,1.59376 0.0469,3.89064 0.14063,6.89062 0.0937,3.00001 0.84372,4.96876 2.25,5.90625 0.98435,0.65626 3.25778,1.17188 6.82031,1.54688 2.20309,0.28125 3.30465,1.10156 3.30469,2.46093 -4e-5,1.07813 -0.17582,1.7461 -0.52734,2.00391 -0.3516,0.25781 -1.27738,0.31641 -2.77735,0.17578 -4.68753,-0.42187 -12.60939,-0.1875 -23.76562,0.70313 -2.81251,0.23437 -4.33595,-0.11719 -4.57032,-1.05469 -0.0937,-0.32813 -0.14063,-0.79688 -0.14062,-1.40625 -1e-5,-1.45312 1.42968,-2.55469 4.28906,-3.30469 2.57811,-0.65624 3.86718,-3.67968 3.86719,-9.07031 l 0,-61.453127 c -1e-5,-3.843671 -0.37501,-6.515543 -1.125,-8.015625 -1.03126,-1.92179 -3.18751,-3.421788 -6.46875,-4.5 -1.54688,-0.515536 -2.32032,-1.242098 -2.32031,-2.179688 -1e-5,-1.359283 1.10155,-2.413969 3.30468,-3.164062 3.51562,-1.17178 6.86718,-2.788966 10.05469,-4.851563 2.57811,-1.6874 4.17186,-2.531149 4.78125,-2.53125 1.92185,1.01e-4 2.88279,1.21885 2.88281,3.65625 -2e-5,-0.328027 -0.0235,1.992283 -0.0703,6.960938 -0.0469,3.421962 -0.0703,8.015707 -0.0703,13.78125 l 0.14062,44.015627 c -2e-5,1.21878 0.3281,1.82815 0.98438,1.82812 0.7031,3e-5 1.78122,-0.60934 3.23437,-1.82812 3.8906,-3.046842 8.67184,-7.031213 14.34375,-11.953127 1.12496,-1.17183 1.68746,-2.109329 1.6875,-2.8125 -4e-5,-1.265577 -1.89848,-2.156201 -5.69531,-2.671875 -1.64066,-0.18745 -2.4141,-1.101512 -2.32031,-2.742188 0.14059,-1.64057 0.9609,-2.343695 2.46094,-2.109375 3.37495,0.468805 8.29682,0.726617 14.76562,0.773438 4.49994,0.04693 8.9765,0.07037 13.42969,0.07031 1.45306,0.04693 2.17962,0.914116 2.17969,2.601563 -7e-5,1.5938 -1.14851,2.460986 -3.44532,2.601562 -3.60943,0.140674 -7.00787,0.960986 -10.19531,2.460938 -4.45317,2.015669 -9.21098,5.554728 -14.27344,10.617187 -0.37504,0.281286 -0.56254,0.632845 -0.5625,1.054685 -4e-5,0.65629 0.79684,2.2266 2.39063,4.71094 5.85933,8.90627 11.39057,15.63283 16.59375,20.17969 3.32806,2.85938 6.44525,4.28907 9.35156,4.28906 2.15618,1e-5 3.49212,0.15235 4.00781,0.45703 0.51556,0.30469 0.77337,1.11329 0.77344,2.42578 z"
-         style="font-size:144px;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2838" />
-    </g>
-    <g
-       style="font-size:40px;font-style:normal;font-weight:normal;line-height:89.99999762%;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
-       id="text2870"
-       transform="translate(0,4)">
-      <path
-         d="m 285.18048,153.2296 c -2e-5,0.16407 -0.31447,0.33269 -0.94336,0.50586 -0.41017,0.10938 -0.75653,0.48308 -1.03906,1.12109 -1.64064,3.64584 -3.10809,6.58529 -4.40234,8.81836 -0.0274,0.0547 -0.0957,0.0775 -0.20508,0.0684 -0.10028,-0.009 -0.16864,-0.041 -0.20508,-0.0957 -0.21876,-0.32813 -0.77931,-1.47201 -1.68164,-3.43164 -0.82944,-1.80469 -1.27605,-2.70703 -1.33985,-2.70703 -0.10027,0 -0.5879,0.87044 -1.46289,2.61132 -0.99349,1.94141 -1.58138,3.04883 -1.76367,3.32227 -0.0274,0.0638 -0.0912,0.0911 -0.1914,0.082 -0.10027,0 -0.17775,-0.0365 -0.23243,-0.10937 -0.66537,-1.03907 -2.19662,-3.88281 -4.59375,-8.53125 -0.27344,-0.54687 -0.57877,-0.89322 -0.91601,-1.03907 -0.58334,-0.2552 -0.875,-0.44204 -0.875,-0.56054 0,-0.35546 0.19596,-0.51497 0.58789,-0.47852 1.75911,0.1823 3.29947,0.14584 4.62109,-0.10937 0.30078,-0.0547 0.45117,0.0456 0.45117,0.30078 0,0.35548 -0.20508,0.58334 -0.61523,0.68359 -0.53777,0.12761 -0.80665,0.32814 -0.80664,0.60156 -1e-5,0.17319 0.0638,0.4284 0.19141,0.76563 0.32812,0.84766 0.8203,1.92318 1.47656,3.22656 0.65624,1.30339 1.03905,1.95508 1.14844,1.95508 0.0729,0 0.46027,-0.67903 1.16211,-2.03711 0.70181,-1.35807 1.05272,-2.11002 1.05273,-2.25586 -1e-5,-0.35546 -0.1185,-0.73827 -0.35547,-1.14844 -0.28256,-0.60155 -0.61069,-0.96158 -0.98437,-1.08008 -0.60157,-0.20962 -0.90236,-0.4147 -0.90235,-0.61523 -1e-5,-0.36457 0.20507,-0.51496 0.61524,-0.45117 1.40363,0.19142 2.88931,0.15496 4.45703,-0.10938 0.30077,-0.0547 0.45116,0.0365 0.45117,0.27344 -1e-5,0.34637 -0.22332,0.56056 -0.66992,0.64258 -0.52866,0.10027 -0.79298,0.36915 -0.79297,0.80664 -10e-6,0.20964 0.0592,0.45574 0.17773,0.73828 1.40364,3.4362 2.21483,5.1543 2.4336,5.1543 0.082,0 0.4466,-0.66992 1.09375,-2.00977 0.69269,-1.43098 1.17576,-2.58397 1.44922,-3.45898 0.0182,-0.0456 0.0273,-0.0957 0.0273,-0.15039 -2e-5,-0.39192 -0.30536,-0.69726 -0.91602,-0.91602 -0.47397,-0.15494 -0.71095,-0.32811 -0.71093,-0.51953 -2e-5,-0.319 0.15037,-0.44205 0.45117,-0.36914 0.41014,0.0912 1.14842,0.15952 2.21484,0.20508 1.00259,0.0365 1.66339,0.0319 1.98242,-0.0137 0.37368,-0.0456 0.56053,0.0592 0.56055,0.31445 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2877" />
-      <path
-         d="m 297.29376,160.98155 c -1e-5,0.39193 -0.52865,0.87956 -1.58594,1.46289 -1.20313,0.64714 -2.43815,0.97071 -3.70507,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47266,-1.35352 -1.00261,-0.95703 -1.50391,-2.23307 -1.50391,-3.82812 0,-1.80469 0.56055,-3.2539 1.68164,-4.34766 1.04818,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90234,1e-5 1.74544,0.28712 2.5293,0.86133 0.70181,0.51042 1.16666,1.09376 1.39453,1.75 0.082,0.22787 0.20507,0.35547 0.36914,0.38281 0.23697,0.0456 0.35546,0.20509 0.35547,0.47852 -10e-6,0.3737 -0.44662,0.69271 -1.33984,0.95703 l -6.30274,1.87304 c 0.41016,2.16928 1.52213,3.25391 3.33594,3.25391 1.04817,0 2.24218,-0.38281 3.58203,-1.14844 0.20051,-0.11848 0.39648,-0.17773 0.58789,-0.17773 0.14582,0 0.21874,0.13216 0.21875,0.39648 z m -3.24023,-5.48242 c -1e-5,-0.50129 -0.20281,-0.94107 -0.6084,-1.31934 -0.40561,-0.37824 -0.90463,-0.56737 -1.49707,-0.56738 -1.67709,10e-6 -2.51563,1.20769 -2.51563,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60156,-0.17317 0.90234,-0.48762 0.90235,-0.94336 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2879" />
-      <path
-         d="m 311.11603,157.13976 c -2e-5,1.80469 -0.62892,3.31771 -1.88672,4.53906 -1.20314,1.17578 -2.63412,1.76367 -4.29297,1.76367 -0.93881,0 -1.91862,-0.15495 -2.93945,-0.46484 -0.98438,-0.30078 -1.48112,-0.57422 -1.49024,-0.82032 -0.009,-0.20963 -0.005,-0.94335 0.0137,-2.20117 0.0273,-1.54036 0.041,-2.66145 0.041,-3.36328 0,-1.05728 -0.009,-2.59081 -0.0273,-4.60059 -0.0182,-2.00975 -0.0274,-3.34275 -0.0274,-3.99902 0,-0.65623 -0.10026,-1.1074 -0.30078,-1.35351 -0.18229,-0.21874 -0.57878,-0.39191 -1.18945,-0.51954 -0.23698,-0.082 -0.35547,-0.2324 -0.35547,-0.45117 0,-0.19139 0.17773,-0.35089 0.5332,-0.47851 0.51042,-0.18228 1.14388,-0.48762 1.90039,-0.91602 0.60156,-0.33722 0.97526,-0.50584 1.1211,-0.50586 0.28254,2e-5 0.42382,0.23244 0.42383,0.69727 -1e-5,0.0365 -0.0137,0.51043 -0.041,1.42187 -0.0182,0.85679 -0.0228,1.72723 -0.0137,2.61133 l 0.0273,4.73047 c 0,0.44662 0.15494,0.57423 0.46485,0.38281 1.09374,-0.62889 2.23306,-0.94335 3.41796,-0.94336 1.36718,1e-5 2.47916,0.41245 3.33594,1.23731 0.85676,0.82488 1.28514,1.90267 1.28516,3.2334 z m -2.1875,1.21679 c -1e-5,-1.24869 -0.34637,-2.27408 -1.03906,-3.07617 -0.65626,-0.7565 -1.45379,-1.13476 -2.39258,-1.13477 -0.64714,1e-5 -1.28517,0.16863 -1.91407,0.50586 -0.62891,0.33725 -0.94336,0.69272 -0.94335,1.06641 l 0,3.66406 c -1e-5,1.75912 0.97069,2.63868 2.9121,2.63867 1.01172,1e-5 1.82747,-0.32584 2.44727,-0.97753 0.61978,-0.65169 0.92968,-1.5472 0.92969,-2.68653 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2881" />
-      <path
-         d="m 333.23712,161.93858 c 0.0365,0.082 0.0547,0.14128 0.0547,0.17774 -2e-5,0.1914 -0.46486,0.50586 -1.39453,0.94336 -1.07553,0.5013 -1.709,0.75195 -1.90039,0.75195 -0.16408,0 -0.3532,-0.27572 -0.56739,-0.82715 -0.2142,-0.55143 -0.36231,-0.82715 -0.44433,-0.82715 0.009,0 -0.50815,0.23926 -1.55176,0.71778 -1.04363,0.47851 -1.83432,0.71777 -2.37207,0.71777 -1.20313,0 -2.24675,-0.43294 -3.13086,-1.29883 -1.06641,-1.02994 -1.59961,-2.4746 -1.59961,-4.33398 0,-1.55859 0.62891,-2.90299 1.88672,-4.03321 1.11198,-0.99347 2.24674,-1.49022 3.4043,-1.49023 0.8203,1e-5 1.80012,0.10483 2.93945,0.31445 0.35546,0.0638 0.53319,-0.041 0.5332,-0.31445 l 0,-4.67578 c -1e-5,-0.52863 -0.12761,-0.90233 -0.38281,-1.12109 -0.16407,-0.14582 -0.55144,-0.319 -1.16211,-0.51954 -0.29167,-0.10935 -0.43751,-0.28709 -0.4375,-0.5332 -10e-6,-0.22785 0.19596,-0.39647 0.58789,-0.50586 0.53775,-0.16404 1.18033,-0.43748 1.92774,-0.82031 0.61978,-0.32811 1.00259,-0.49217 1.14843,-0.49219 0.33723,2e-5 0.50585,0.23244 0.50586,0.69727 -1e-5,-0.009 -0.0137,0.44435 -0.041,1.36035 -0.0274,0.91603 -0.041,1.80698 -0.041,2.67285 l 0,12.42773 c -10e-6,0.51954 0.23241,0.7793 0.69727,0.7793 0.17316,0 0.40558,-0.0273 0.69726,-0.082 0.319,-0.0547 0.53319,0.0501 0.64258,0.31445 z m -4.22461,-1.35351 0,-4.89453 c -1e-5,-0.40104 -0.35548,-0.81575 -1.0664,-1.24414 -0.75652,-0.45572 -1.57683,-0.68359 -2.46094,-0.6836 -1.90495,1e-5 -2.85743,1.17579 -2.85742,3.52735 -10e-6,1.25781 0.37825,2.33333 1.13476,3.22656 0.75651,0.89323 1.64518,1.33984 2.66602,1.33984 0.46483,0 1.01171,-0.15494 1.64062,-0.46484 0.6289,-0.3099 0.94335,-0.57878 0.94336,-0.80664 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2883" />
-      <path
-         d="m 345.09064,160.98155 c -1e-5,0.39193 -0.52866,0.87956 -1.58594,1.46289 -1.20313,0.64714 -2.43816,0.97071 -3.70508,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47265,-1.35352 -1.00261,-0.95703 -1.50391,-2.23307 -1.50391,-3.82812 0,-1.80469 0.56054,-3.2539 1.68164,-4.34766 1.04817,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90233,1e-5 1.74543,0.28712 2.5293,0.86133 0.70181,0.51042 1.16665,1.09376 1.39453,1.75 0.082,0.22787 0.20506,0.35547 0.36914,0.38281 0.23697,0.0456 0.35545,0.20509 0.35547,0.47852 -2e-5,0.3737 -0.44663,0.69271 -1.33985,0.95703 l -6.30273,1.87304 c 0.41015,2.16928 1.52213,3.25391 3.33594,3.25391 1.04816,0 2.24217,-0.38281 3.58203,-1.14844 0.20051,-0.11848 0.39647,-0.17773 0.58789,-0.17773 0.14582,0 0.21874,0.13216 0.21875,0.39648 z m -3.24024,-5.48242 c -10e-6,-0.50129 -0.2028,-0.94107 -0.6084,-1.31934 -0.4056,-0.37824 -0.90462,-0.56737 -1.49707,-0.56738 -1.67708,10e-6 -2.51562,1.20769 -2.51562,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60155,-0.17317 0.90233,-0.48762 0.90234,-0.94336 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2885" />
-      <path
-         d="m 360.08868,153.20226 c -1e-5,0.17318 -0.28712,0.35092 -0.86132,0.5332 -0.37371,0.10938 -0.69272,0.48308 -0.95704,1.12109 -0.44662,1.07553 -1.22592,2.65235 -2.33789,4.73047 -0.93881,1.75912 -1.66342,3.04427 -2.17382,3.85547 -0.0274,0.0638 -0.0866,0.0911 -0.17774,0.082 -0.0912,-0.009 -0.15951,-0.0456 -0.20508,-0.10937 -1.93229,-2.77995 -3.58659,-5.6237 -4.96289,-8.53125 -0.29167,-0.61979 -0.57878,-0.96614 -0.86133,-1.03907 -0.52864,-0.20962 -0.79296,-0.39647 -0.79296,-0.56054 0,-0.36458 0.17773,-0.52408 0.5332,-0.47852 0.55599,0.0729 1.38086,0.0912 2.47461,0.0547 1.05729,-0.0273 1.80012,-0.082 2.22851,-0.16406 0.26432,-0.0547 0.39648,0.0456 0.39649,0.30078 -10e-6,0.31902 -0.18686,0.5241 -0.56055,0.61523 -0.71094,0.19142 -1.06641,0.43751 -1.0664,0.73828 -1e-5,0.13673 0.0456,0.33269 0.13671,0.58789 0.30989,0.76564 0.86588,1.87078 1.66797,3.31543 0.80208,1.44467 1.25781,2.167 1.36719,2.167 0.082,0 0.46939,-0.69271 1.16211,-2.07813 0.72916,-1.46744 1.25324,-2.65234 1.57227,-3.55469 0.0456,-0.10025 0.0683,-0.20051 0.0684,-0.30078 -1e-5,-0.35546 -0.21876,-0.64712 -0.65625,-0.875 -0.42839,-0.17317 -0.64258,-0.33723 -0.64257,-0.49219 -10e-6,-0.319 0.14126,-0.45116 0.42382,-0.39648 0.36458,0.0638 1.00716,0.1185 1.92774,0.16406 0.92056,0.0456 1.51756,0.0547 1.79101,0.0274 0.33723,-0.0638 0.50585,0.0319 0.50586,0.28711 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2887" />
-      <path
-         d="m 372.21564,160.98155 c -1e-5,0.39193 -0.52866,0.87956 -1.58594,1.46289 -1.20313,0.64714 -2.43816,0.97071 -3.70508,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47265,-1.35352 -1.00261,-0.95703 -1.50391,-2.23307 -1.50391,-3.82812 0,-1.80469 0.56054,-3.2539 1.68164,-4.34766 1.04817,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90233,1e-5 1.74543,0.28712 2.5293,0.86133 0.70181,0.51042 1.16665,1.09376 1.39453,1.75 0.082,0.22787 0.20506,0.35547 0.36914,0.38281 0.23697,0.0456 0.35545,0.20509 0.35547,0.47852 -2e-5,0.3737 -0.44663,0.69271 -1.33985,0.95703 l -6.30273,1.87304 c 0.41015,2.16928 1.52213,3.25391 3.33594,3.25391 1.04816,0 2.24217,-0.38281 3.58203,-1.14844 0.20051,-0.11848 0.39647,-0.17773 0.58789,-0.17773 0.14582,0 0.21874,0.13216 0.21875,0.39648 z m -3.24024,-5.48242 c -10e-6,-0.50129 -0.2028,-0.94107 -0.6084,-1.31934 -0.4056,-0.37824 -0.90462,-0.56737 -1.49707,-0.56738 -1.67708,10e-6 -2.51562,1.20769 -2.51562,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60155,-0.17317 0.90233,-0.48762 0.90234,-0.94336 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2889" />
-      <path
-         d="m 380.66486,162.88194 c -10e-6,0.36459 -0.20509,0.52409 -0.61524,0.47852 -1.25782,-0.11849 -2.81641,-0.10026 -4.67578,0.0547 -0.3737,0.0365 -0.60384,0.0273 -0.69043,-0.0274 -0.0866,-0.0547 -0.12988,-0.20508 -0.12988,-0.45117 0,-0.21875 0.24837,-0.40332 0.74512,-0.55371 0.49674,-0.15039 0.74511,-0.59928 0.74511,-1.34668 l 0,-12.37305 c 0,-0.73826 -0.1071,-1.28058 -0.32129,-1.62695 -0.21419,-0.34634 -0.59017,-0.61522 -1.12793,-0.80664 -0.28255,-0.10024 -0.42383,-0.24152 -0.42382,-0.42383 -1e-5,-0.27342 0.20507,-0.4785 0.61523,-0.61523 0.61979,-0.20051 1.26237,-0.5104 1.92773,-0.92969 0.54687,-0.32811 0.89323,-0.49217 1.03907,-0.49219 0.33723,2e-5 0.50585,0.23244 0.50586,0.69727 -10e-6,-0.0364 -0.0182,0.41929 -0.0547,1.36718 -0.0273,0.90236 -0.0365,1.79104 -0.0273,2.66602 l 0.0547,12.20898 c 0,0.556 0.13672,0.95932 0.41016,1.20997 0.27343,0.25065 0.74283,0.41699 1.4082,0.49902 0.41015,0.0456 0.61523,0.20052 0.61524,0.46484 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2891" />
-      <path
-         d="m 393.9129,157.76866 c -10e-6,1.58594 -0.5879,2.92806 -1.76367,4.02637 -1.17579,1.09831 -2.63412,1.64746 -4.375,1.64746 -1.74089,0 -3.11719,-0.48307 -4.12891,-1.44922 -0.97526,-0.94791 -1.46289,-2.20117 -1.46289,-3.75976 0,-1.61328 0.61524,-2.97135 1.84571,-4.07422 1.194,-1.0664 2.62043,-1.5996 4.27929,-1.59961 1.77734,1e-5 3.16275,0.47852 4.15625,1.43554 0.96614,0.9297 1.44921,2.18751 1.44922,3.77344 z m -2.40625,0.83399 c -10e-6,-1.43099 -0.34636,-2.5931 -1.03906,-3.48633 -0.67449,-0.86588 -1.53126,-1.29882 -2.57031,-1.29883 -0.96615,1e-5 -1.75456,0.33953 -2.36524,1.01855 -0.61068,0.67905 -0.91602,1.5062 -0.91601,2.48145 -1e-5,1.56771 0.35546,2.78907 1.0664,3.66406 0.65625,0.80209 1.51302,1.20313 2.57032,1.20313 1.00259,0 1.79556,-0.33268 2.3789,-0.99805 0.58333,-0.66536 0.87499,-1.52669 0.875,-2.58398 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2893" />
-      <path
-         d="m 408.20001,157.39952 c -1e-5,1.70443 -0.57195,3.15137 -1.71582,4.34082 -1.14389,1.18945 -2.4769,1.78418 -3.99902,1.78418 -0.92058,0 -1.80925,-0.16406 -2.66602,-0.49219 -0.0911,-0.0365 -0.13672,0.18685 -0.13672,0.66993 l 0,4.11523 c 0,0.66536 0.54232,1.09374 1.62696,1.28516 0.50129,0.0911 0.81802,0.17545 0.95019,0.25293 0.13216,0.0775 0.19824,0.20735 0.19824,0.38964 0,0.30989 -0.20508,0.45117 -0.61523,0.42383 -1.91407,-0.12761 -3.59571,-0.082 -5.04492,0.13672 -0.30078,0.0456 -0.48763,0.0456 -0.56055,0 -0.0729,-0.0456 -0.10937,-0.16863 -0.10937,-0.36914 0,-0.15495 0.25976,-0.3418 0.77929,-0.56055 0.44662,-0.18229 0.66992,-0.61979 0.66993,-1.3125 l 0,-12.05859 c -1e-5,-0.97525 -0.26889,-1.58593 -0.80665,-1.83203 -0.63802,-0.28254 -0.95703,-0.51497 -0.95703,-0.69727 0,-0.20051 0.18685,-0.3509 0.56055,-0.45117 0.48307,-0.11848 0.97982,-0.32811 1.49023,-0.62891 0.42839,-0.24608 0.68815,-0.36912 0.7793,-0.36914 0.27344,2e-5 0.49674,0.25067 0.66992,0.75196 0.25521,0.72917 0.42383,1.09376 0.50586,1.09375 0.0182,1e-5 0.41015,-0.21419 1.17578,-0.64258 0.83854,-0.46483 1.65885,-0.69726 2.46094,-0.69727 1.30338,1e-5 2.3789,0.36915 3.22656,1.10743 1.01171,0.86589 1.51757,2.11914 1.51758,3.75976 z m -2.32422,1.09375 c -10e-6,-1.48567 -0.36915,-2.64322 -1.10742,-3.47266 -0.60157,-0.69269 -1.30795,-1.03905 -2.11914,-1.03906 -0.62891,1e-5 -1.23047,0.21876 -1.80469,0.65625 -0.75651,0.56511 -1.13477,1.35353 -1.13476,2.36524 l 0,3.86914 c -1e-5,0.24609 0.34635,0.50586 1.03906,0.77929 0.74739,0.30079 1.55859,0.45118 2.43359,0.45118 1.79557,0 2.69335,-1.20313 2.69336,-3.60938 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2895" />
-      <path
-         d="m 432.90509,162.90929 c -2e-5,0.30078 -0.1413,0.44205 -0.42383,0.42382 -1.85939,-0.13671 -3.59116,-0.13671 -5.19531,0 -0.35549,0.0273 -0.53322,-0.13216 -0.5332,-0.47851 -2e-5,-0.27344 0.24152,-0.41927 0.72461,-0.4375 0.61065,-0.0365 0.91599,-0.5332 0.91601,-1.49024 l 0,-3.63671 c -2e-5,-2.04166 -0.82944,-3.0625 -2.48828,-3.0625 -0.87502,0 -1.6771,0.15951 -2.40625,0.47851 -0.66538,0.28256 -1.00262,0.56511 -1.01172,0.84766 l -0.0547,5.42773 c -10e-6,0.56511 0.0684,0.93881 0.20508,1.1211 0.10025,0.1276 0.32356,0.21875 0.66992,0.27343 0.80207,0.13672 1.20311,0.30534 1.20313,0.50586 -2e-5,0.17318 -0.0228,0.29167 -0.0684,0.35547 -0.0547,0.0911 -0.21877,0.13216 -0.49219,0.12305 -1.03907,-0.0365 -2.55209,-0.0182 -4.53906,0.0547 -0.28256,0.009 -0.46485,-0.0137 -0.54688,-0.0684 -0.082,-0.0547 -0.12305,-0.1823 -0.12304,-0.38282 -10e-6,-0.23697 0.25975,-0.38281 0.77929,-0.4375 0.5651,-0.0638 0.84765,-0.57877 0.84766,-1.54492 l 0,-3.71875 c -10e-6,-1.0026 -0.22787,-1.77733 -0.68359,-2.32422 -0.40105,-0.49218 -0.91147,-0.73827 -1.53125,-0.73828 -0.91147,1e-5 -1.73178,0.18458 -2.46094,0.55371 -0.72917,0.36915 -1.09376,0.76336 -1.09375,1.18262 l 0,4.99023 c -1e-5,0.56511 0.14583,0.95704 0.4375,1.17579 0.26432,0.20052 0.74283,0.32356 1.43555,0.36914 0.37369,0.0182 0.56054,0.14583 0.56054,0.38281 0,0.26432 -0.15039,0.39648 -0.45117,0.39648 -2.27865,0 -3.92839,0.0638 -4.94922,0.19141 -0.34635,0.0456 -0.56966,0.0456 -0.66992,0 -0.082,-0.0456 -0.12305,-0.15495 -0.12305,-0.32813 0,-0.22786 0.33724,-0.42382 1.01172,-0.58789 0.41016,-0.10937 0.61523,-0.64257 0.61524,-1.59961 l 0,-4.19726 c -1e-5,-1.0664 -0.28711,-1.68163 -0.86133,-1.8457 -0.48308,-0.13671 -0.77702,-0.23697 -0.88184,-0.30079 -0.10482,-0.0638 -0.15722,-0.16861 -0.15722,-0.31445 0,-0.16405 0.47395,-0.51041 1.42187,-1.03906 1.0026,-0.56509 1.61784,-0.84765 1.8457,-0.84766 0.19141,1e-5 0.35319,0.27573 0.48536,0.82715 0.13215,0.55144 0.23469,0.82716 0.30761,0.82715 0.10026,10e-6 0.37825,-0.14127 0.83399,-0.42383 0.5651,-0.35546 1.07551,-0.61978 1.53125,-0.79297 0.74739,-0.29165 1.51757,-0.43749 2.31054,-0.4375 0.63802,1e-5 1.194,0.13673 1.66797,0.41016 0.32812,0.1823 0.62434,0.43295 0.88868,0.75195 0.21873,0.27345 0.32811,0.41017 0.32812,0.41016 -10e-6,1e-5 0.23697,-0.13671 0.71094,-0.41016 0.55597,-0.319 1.09829,-0.56965 1.62695,-0.75195 0.77472,-0.27343 1.52668,-0.41015 2.25586,-0.41016 2.49738,1e-5 3.74607,1.37176 3.74609,4.11524 l 0,4.42968 c -2e-5,0.51954 0.12303,0.88868 0.36914,1.10743 0.21873,0.18229 0.64712,0.33724 1.28516,0.46484 0.48305,0.0911 0.72459,0.22787 0.72461,0.41016 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2897" />
-      <path
-         d="m 444.45782,160.98155 c -1e-5,0.39193 -0.52865,0.87956 -1.58593,1.46289 -1.20314,0.64714 -2.43816,0.97071 -3.70508,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47266,-1.35352 -1.0026,-0.95703 -1.5039,-2.23307 -1.5039,-3.82812 0,-1.80469 0.56054,-3.2539 1.68164,-4.34766 1.04817,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90233,1e-5 1.74543,0.28712 2.52929,0.86133 0.70182,0.51042 1.16666,1.09376 1.39453,1.75 0.082,0.22787 0.20507,0.35547 0.36914,0.38281 0.23697,0.0456 0.35546,0.20509 0.35547,0.47852 -10e-6,0.3737 -0.44662,0.69271 -1.33984,0.95703 l -6.30273,1.87304 c 0.41015,2.16928 1.52213,3.25391 3.33593,3.25391 1.04817,0 2.24218,-0.38281 3.58203,-1.14844 0.20051,-0.11848 0.39648,-0.17773 0.58789,-0.17773 0.14583,0 0.21874,0.13216 0.21875,0.39648 z m -3.24023,-5.48242 c -10e-6,-0.50129 -0.20281,-0.94107 -0.6084,-1.31934 -0.4056,-0.37824 -0.90463,-0.56737 -1.49707,-0.56738 -1.67709,10e-6 -2.51563,1.20769 -2.51562,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60155,-0.17317 0.90233,-0.48762 0.90234,-0.94336 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2899" />
-      <path
-         d="m 460.86407,163.03233 c -1e-5,0.28256 -0.20509,0.41016 -0.61523,0.38282 -1.95965,-0.10026 -3.44532,-0.11849 -4.45703,-0.0547 -0.51954,0.0365 -0.80209,-0.0273 -0.84766,-0.19141 -0.0182,-0.0547 -0.0273,-0.13216 -0.0273,-0.23242 -1e-5,-0.18229 0.26431,-0.34635 0.79297,-0.49219 0.48306,-0.13672 0.7246,-0.60612 0.72461,-1.4082 l 0,-3.63672 c -1e-5,-2.08723 -0.90691,-3.13085 -2.72071,-3.13086 -0.8112,1e-5 -1.56315,0.19142 -2.25586,0.57422 -0.65625,0.37371 -0.98438,0.76108 -0.98437,1.16211 l 0,5.00391 c -1e-5,0.85677 0.56054,1.33528 1.68164,1.43554 0.42838,0.0365 0.64257,0.17318 0.64258,0.41016 -10e-6,0.22786 -0.0592,0.36458 -0.17774,0.41016 -0.0547,0.0182 -0.20964,0.0228 -0.46484,0.0137 -1.43099,-0.0547 -2.90756,0.0182 -4.42969,0.21875 -0.32812,0.0456 -0.5332,0.0592 -0.61523,0.041 -0.18229,-0.0365 -0.27344,-0.17773 -0.27344,-0.42383 0,-0.21874 0.25976,-0.40559 0.7793,-0.56054 0.48307,-0.14583 0.7246,-0.75195 0.72461,-1.81836 l 0,-4.14258 c -1e-5,-0.70182 -0.0912,-1.14843 -0.27344,-1.33984 -0.12761,-0.13671 -0.55143,-0.32812 -1.27148,-0.57422 -0.1823,-0.0638 -0.27344,-0.1914 -0.27344,-0.38281 0,-0.18229 0.18685,-0.34179 0.56054,-0.47852 0.51042,-0.1914 1.07097,-0.49674 1.68165,-0.91602 0.51041,-0.34634 0.83853,-0.51952 0.98437,-0.51953 0.24609,1e-5 0.41015,0.28029 0.49219,0.84082 0.082,0.56056 0.19596,0.84083 0.34179,0.84082 -0.0729,1e-5 0.39193,-0.26659 1.39454,-0.7998 1.00259,-0.53319 1.99152,-0.7998 2.96679,-0.79981 2.36067,1e-5 3.55012,1.34441 3.56836,4.03321 l 0.0273,4.40234 c -2e-5,0.56511 0.14582,0.97071 0.4375,1.2168 0.21873,0.18229 0.64256,0.33724 1.27148,0.46484 0.41014,0.082 0.61522,0.23242 0.61523,0.45117 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2901" />
-      <path
-         d="m 469.64142,161.99327 c -10e-6,0.4375 -0.40105,0.8112 -1.20313,1.12109 -0.71094,0.27344 -1.43099,0.41016 -2.16015,0.41016 -1.97787,0 -2.9668,-1.05273 -2.9668,-3.1582 l 0,-4.94922 c 0,-0.6289 -0.0501,-1.01399 -0.15039,-1.15527 -0.10026,-0.14127 -0.43294,-0.28027 -0.99805,-0.417 -0.14583,-0.0365 -0.21875,-0.17772 -0.21875,-0.42382 0,-0.26432 0.0547,-0.42382 0.16407,-0.47852 0.98437,-0.48306 1.84114,-1.34895 2.57031,-2.59766 0.10025,-0.17316 0.29622,-0.22785 0.58789,-0.16406 0.20051,0.0638 0.30533,0.18231 0.31445,0.35547 l 0.0547,1.70898 c -10e-6,0.12762 0.0228,0.21876 0.0684,0.27344 0.0638,0.082 0.20963,0.12306 0.4375,0.12305 l 3.04883,0 c 0.17317,1e-5 0.17317,0.2142 0,0.64258 -0.20965,0.51954 -0.51954,0.7793 -0.92969,0.77929 l -2.06445,0 c -0.35548,1e-5 -0.57423,0.0593 -0.65625,0.17774 -0.0638,0.082 -0.0957,0.30535 -0.0957,0.66992 l 0,4.4707 c 0,1.13021 0.10026,1.84571 0.30078,2.14649 0.26432,0.38281 0.87956,0.57422 1.84571,0.57422 0.319,0 0.70637,-0.0524 1.16211,-0.15723 0.45572,-0.10482 0.69725,-0.15723 0.72461,-0.15723 0.10936,0 0.16405,0.0684 0.16406,0.20508 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2903" />
-      <path
-         d="m 475.84845,162.60851 c -1e-5,2.36979 -0.99805,4.15168 -2.99414,5.3457 -0.18229,0.10937 -0.31446,0.16406 -0.39649,0.16406 -0.1276,0 -0.24609,-0.14584 -0.35546,-0.4375 -0.0365,-0.10026 -0.0547,-0.1823 -0.0547,-0.24609 0,-0.15495 0.25976,-0.41016 0.7793,-0.76563 0.89322,-0.61979 1.33984,-1.32161 1.33984,-2.10547 0,-0.40104 -0.25977,-0.76562 -0.7793,-1.09375 -0.72917,-0.45573 -1.09375,-1.01627 -1.09375,-1.68164 0,-0.44661 0.15039,-0.81803 0.45117,-1.11426 0.30078,-0.29622 0.70182,-0.44433 1.20313,-0.44433 0.60156,0 1.06868,0.21875 1.40137,0.65625 0.33267,0.4375 0.49901,1.01172 0.49902,1.72266 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2905" />
-      <path
-         d="m 278.95978,182.96866 c -1e-5,1.58594 -0.58791,2.92806 -1.76367,4.02637 -1.1758,1.09831 -2.63413,1.64746 -4.375,1.64746 -1.74089,0 -3.1172,-0.48307 -4.12891,-1.44922 -0.97526,-0.94791 -1.46289,-2.20117 -1.46289,-3.75977 0,-1.61327 0.61523,-2.97134 1.8457,-4.07421 1.19401,-1.0664 2.62044,-1.5996 4.2793,-1.59961 1.77733,1e-5 3.16275,0.47852 4.15625,1.43554 0.96613,0.9297 1.44921,2.18751 1.44922,3.77344 z m -2.40625,0.83399 c -1e-5,-1.43099 -0.34637,-2.5931 -1.03906,-3.48633 -0.67449,-0.86588 -1.53126,-1.29882 -2.57032,-1.29883 -0.96615,1e-5 -1.75456,0.33953 -2.36523,1.01855 -0.61068,0.67905 -0.91602,1.5062 -0.91602,2.48145 0,1.56771 0.35547,2.78906 1.06641,3.66406 0.65624,0.80209 1.51301,1.20313 2.57031,1.20313 1.0026,0 1.79557,-0.33268 2.37891,-0.99805 0.58332,-0.66536 0.87499,-1.52669 0.875,-2.58398 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2907" />
-      <path
-         d="m 295.59845,188.23233 c -2e-5,0.28255 -0.20509,0.41016 -0.61523,0.38282 -1.95965,-0.10027 -3.44533,-0.11849 -4.45704,-0.0547 -0.51954,0.0365 -0.80209,-0.0274 -0.84765,-0.19141 -0.0182,-0.0547 -0.0274,-0.13216 -0.0274,-0.23242 -1e-5,-0.18229 0.26432,-0.34635 0.79297,-0.49219 0.48306,-0.13672 0.7246,-0.60612 0.72461,-1.4082 l 0,-3.63672 c -10e-6,-2.08723 -0.90691,-3.13085 -2.7207,-3.13086 -0.81121,1e-5 -1.56316,0.19142 -2.25586,0.57422 -0.65626,0.37371 -0.98438,0.76107 -0.98438,1.16211 l 0,5.00391 c 0,0.85677 0.56055,1.33528 1.68165,1.43554 0.42837,0.0365 0.64257,0.17318 0.64257,0.41016 0,0.22786 -0.0593,0.36458 -0.17773,0.41015 -0.0547,0.0182 -0.20964,0.0228 -0.46484,0.0137 -1.431,-0.0547 -2.90756,0.0182 -4.42969,0.21875 -0.32813,0.0456 -0.53321,0.0592 -0.61524,0.041 -0.18229,-0.0365 -0.27344,-0.17773 -0.27343,-0.42383 -10e-6,-0.21875 0.25976,-0.40559 0.77929,-0.56054 0.48307,-0.14584 0.72461,-0.75195 0.72461,-1.81836 l 0,-4.14258 c 0,-0.70182 -0.0912,-1.14843 -0.27344,-1.33984 -0.1276,-0.13671 -0.55143,-0.32812 -1.27148,-0.57422 -0.18229,-0.0638 -0.27344,-0.1914 -0.27344,-0.38282 0,-0.18228 0.18685,-0.34178 0.56055,-0.47851 0.51041,-0.1914 1.07096,-0.49674 1.68164,-0.91602 0.51041,-0.34634 0.83854,-0.51952 0.98438,-0.51953 0.24608,1e-5 0.41015,0.28029 0.49218,0.84082 0.082,0.56056 0.19596,0.84083 0.3418,0.84082 -0.0729,1e-5 0.39192,-0.26659 1.39453,-0.7998 1.0026,-0.53319 1.99153,-0.7998 2.9668,-0.79981 2.36066,1e-5 3.55011,1.34441 3.56836,4.03321 l 0.0273,4.40234 c -10e-6,0.56511 0.14582,0.9707 0.4375,1.2168 0.21874,0.18229 0.64256,0.33724 1.27149,0.46484 0.41014,0.082 0.61521,0.23242 0.61523,0.45117 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2909" />
-      <path
-         d="m 307.02814,186.18155 c -1e-5,0.39193 -0.52866,0.87956 -1.58594,1.46289 -1.20313,0.64714 -2.43816,0.97071 -3.70508,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47265,-1.35352 -1.00261,-0.95703 -1.50391,-2.23307 -1.50391,-3.82813 0,-1.80468 0.56054,-3.25389 1.68164,-4.34765 1.04817,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90233,1e-5 1.74543,0.28712 2.5293,0.86133 0.70181,0.51042 1.16665,1.09376 1.39453,1.75 0.082,0.22787 0.20506,0.35547 0.36914,0.38281 0.23697,0.0456 0.35545,0.20508 0.35547,0.47851 -2e-5,0.37371 -0.44663,0.69272 -1.33985,0.95704 l -6.30273,1.87304 c 0.41015,2.16928 1.52213,3.25391 3.33594,3.25391 1.04816,0 2.24217,-0.38281 3.58203,-1.14844 0.20051,-0.11849 0.39647,-0.17773 0.58789,-0.17773 0.14582,0 0.21874,0.13216 0.21875,0.39648 z m -3.24024,-5.48242 c -10e-6,-0.50129 -0.2028,-0.94107 -0.6084,-1.31934 -0.4056,-0.37824 -0.90462,-0.56737 -1.49707,-0.56738 -1.67708,1e-5 -2.51562,1.20769 -2.51562,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60155,-0.17317 0.90233,-0.48762 0.90234,-0.94336 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2911" />
-      <path
-         d="m 328.91681,187.13858 c 0.0364,0.082 0.0547,0.14128 0.0547,0.17774 -2e-5,0.1914 -0.46486,0.50586 -1.39453,0.94336 -1.07554,0.5013 -1.709,0.75195 -1.9004,0.75195 -0.16407,0 -0.3532,-0.27572 -0.56738,-0.82715 -0.2142,-0.55143 -0.36231,-0.82715 -0.44433,-0.82715 0.009,0 -0.50815,0.23926 -1.55176,0.71778 -1.04363,0.47851 -1.83432,0.71777 -2.37207,0.71777 -1.20313,0 -2.24675,-0.43294 -3.13086,-1.29883 -1.06641,-1.02994 -1.59961,-2.4746 -1.59961,-4.33398 0,-1.55859 0.6289,-2.90299 1.88672,-4.03321 1.11197,-0.99348 2.24674,-1.49022 3.40429,-1.49023 0.82031,1e-5 1.80013,0.10483 2.93946,0.31445 0.35546,0.0638 0.53319,-0.041 0.5332,-0.31445 l 0,-4.67578 c -1e-5,-0.52863 -0.12761,-0.90233 -0.38281,-1.1211 -0.16407,-0.14581 -0.55144,-0.31899 -1.16211,-0.51953 -0.29168,-0.10935 -0.43751,-0.28709 -0.4375,-0.5332 -10e-6,-0.22785 0.19595,-0.39647 0.58789,-0.50586 0.53775,-0.16404 1.18033,-0.43748 1.92773,-0.82031 0.61978,-0.32811 1.0026,-0.49217 1.14844,-0.49219 0.33723,2e-5 0.50585,0.23244 0.50586,0.69727 -1e-5,-0.009 -0.0137,0.44435 -0.041,1.36035 -0.0274,0.91603 -0.041,1.80698 -0.041,2.67285 l 0,12.42773 c -1e-5,0.51954 0.23241,0.7793 0.69727,0.7793 0.17316,0 0.40558,-0.0273 0.69726,-0.082 0.319,-0.0547 0.53319,0.0501 0.64258,0.31445 z m -4.22461,-1.35351 0,-4.89453 c -1e-5,-0.40104 -0.35548,-0.81575 -1.06641,-1.24414 -0.75651,-0.45572 -1.57683,-0.68359 -2.46093,-0.6836 -1.90496,1e-5 -2.85743,1.17579 -2.85743,3.52735 0,1.25781 0.37826,2.33333 1.13477,3.22656 0.7565,0.89323 1.64518,1.33984 2.66602,1.33984 0.46483,0 1.01171,-0.15494 1.64062,-0.46484 0.6289,-0.3099 0.94335,-0.57878 0.94336,-0.80664 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2913" />
-      <path
-         d="m 339.23907,178.78507 c -1e-5,1.10287 -0.31902,1.6543 -0.95703,1.65429 -0.10027,1e-5 -0.43295,-0.0638 -0.99804,-0.1914 -0.56511,-0.1276 -0.97983,-0.1914 -1.24414,-0.19141 -1.02084,1e-5 -1.53126,0.68816 -1.53125,2.06445 l 0,4.03321 c -10e-6,0.47396 0.16405,0.8112 0.49218,1.01172 0.1914,0.11849 0.66992,0.27799 1.43555,0.47851 0.41015,0.10026 0.61523,0.25521 0.61523,0.46485 0,0.24609 -0.0342,0.3942 -0.10253,0.44433 -0.0684,0.0501 -0.23927,0.0615 -0.5127,0.0342 -2.00521,-0.20963 -3.59115,-0.21875 -4.75781,-0.0273 -0.36459,0.0638 -0.59701,0.0638 -0.69727,0 -0.082,-0.0547 -0.12305,-0.21419 -0.12304,-0.47852 -1e-5,-0.24609 0.25292,-0.44205 0.75878,-0.58789 0.50586,-0.14583 0.75879,-0.58333 0.75879,-1.3125 l 0,-4.38867 c 0,-0.57421 -0.0752,-0.96158 -0.22558,-1.16211 -0.1504,-0.20051 -0.56283,-0.46939 -1.23731,-0.80664 l -0.082,-0.041 c -0.18229,-0.0911 -0.27344,-0.21874 -0.27344,-0.38281 0,-0.18228 0.18685,-0.33723 0.56055,-0.46484 0.6289,-0.21874 1.20312,-0.52864 1.72266,-0.92969 0.42838,-0.33723 0.70182,-0.50585 0.82031,-0.50586 0.32812,1e-5 0.52408,0.28256 0.58789,0.84766 0.082,0.72006 0.17773,1.08008 0.28711,1.08008 0.009,0 0.51953,-0.34179 1.53125,-1.0254 0.66536,-0.4466 1.39452,-0.66991 2.1875,-0.66992 0.65624,1e-5 0.98436,0.35092 0.98437,1.05274 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2915" />
-      <path
-         d="m 352.7879,182.96866 c -10e-6,1.58594 -0.5879,2.92806 -1.76367,4.02637 -1.17579,1.09831 -2.63412,1.64746 -4.375,1.64746 -1.74089,0 -3.11719,-0.48307 -4.12891,-1.44922 -0.97526,-0.94791 -1.46289,-2.20117 -1.46289,-3.75977 0,-1.61327 0.61524,-2.97134 1.84571,-4.07421 1.194,-1.0664 2.62043,-1.5996 4.27929,-1.59961 1.77734,1e-5 3.16275,0.47852 4.15625,1.43554 0.96614,0.9297 1.44921,2.18751 1.44922,3.77344 z m -2.40625,0.83399 c -10e-6,-1.43099 -0.34636,-2.5931 -1.03906,-3.48633 -0.67449,-0.86588 -1.53126,-1.29882 -2.57031,-1.29883 -0.96615,1e-5 -1.75456,0.33953 -2.36524,1.01855 -0.61068,0.67905 -0.91602,1.5062 -0.91601,2.48145 -1e-5,1.56771 0.35546,2.78906 1.0664,3.66406 0.65625,0.80209 1.51302,1.20313 2.57032,1.20313 1.00259,0 1.79556,-0.33268 2.3789,-0.99805 0.58333,-0.66536 0.87499,-1.52669 0.875,-2.58398 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2917" />
-      <path
-         d="m 367.07501,182.59952 c -1e-5,1.70443 -0.57195,3.15137 -1.71582,4.34082 -1.14389,1.18945 -2.4769,1.78418 -3.99902,1.78418 -0.92058,0 -1.80925,-0.16406 -2.66602,-0.49219 -0.0911,-0.0365 -0.13672,0.18685 -0.13672,0.66992 l 0,4.11524 c 0,0.66536 0.54232,1.09374 1.62696,1.28516 0.50129,0.0911 0.81802,0.17544 0.95019,0.25292 0.13216,0.0775 0.19824,0.20736 0.19824,0.38965 0,0.30989 -0.20508,0.45117 -0.61523,0.42383 -1.91407,-0.12761 -3.59571,-0.082 -5.04492,0.13672 -0.30078,0.0456 -0.48763,0.0456 -0.56055,0 -0.0729,-0.0456 -0.10937,-0.16863 -0.10937,-0.36914 0,-0.15495 0.25976,-0.3418 0.77929,-0.56055 0.44662,-0.18229 0.66992,-0.61979 0.66993,-1.3125 l 0,-12.05859 c -1e-5,-0.97525 -0.26889,-1.58593 -0.80665,-1.83203 -0.63802,-0.28254 -0.95703,-0.51497 -0.95703,-0.69727 0,-0.20051 0.18685,-0.3509 0.56055,-0.45117 0.48307,-0.11848 0.97982,-0.32811 1.49023,-0.62891 0.42839,-0.24608 0.68815,-0.36913 0.7793,-0.36914 0.27344,1e-5 0.49674,0.25067 0.66992,0.75196 0.25521,0.72917 0.42383,1.09376 0.50586,1.09375 0.0182,1e-5 0.41015,-0.21419 1.17578,-0.64258 0.83854,-0.46483 1.65885,-0.69726 2.46094,-0.69727 1.30338,1e-5 2.3789,0.36915 3.22656,1.10742 1.01171,0.8659 1.51757,2.11915 1.51758,3.75977 z m -2.32422,1.09375 c -10e-6,-1.48567 -0.36915,-2.64322 -1.10742,-3.47266 -0.60157,-0.6927 -1.30795,-1.03905 -2.11914,-1.03906 -0.62891,1e-5 -1.23047,0.21876 -1.80469,0.65625 -0.75651,0.56511 -1.13477,1.35352 -1.13476,2.36524 l 0,3.86914 c -1e-5,0.24609 0.34635,0.50586 1.03906,0.77929 0.74739,0.30079 1.55859,0.45118 2.43359,0.45118 1.79557,0 2.69335,-1.20313 2.69336,-3.60938 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2919" />
-      <path
-         d="m 387.33673,187.16593 c -10e-6,0.1914 -0.34864,0.48079 -1.0459,0.86816 -0.69727,0.38737 -1.25554,0.58106 -1.6748,0.58106 -0.35548,0 -0.66993,-0.17318 -0.94336,-0.51954 -0.27345,-0.34635 -0.46485,-0.51953 -0.57422,-0.51953 -0.082,0 -0.51498,0.18685 -1.29883,0.56055 -0.78386,0.3737 -1.57227,0.56055 -2.36523,0.56055 -0.7474,0 -1.37175,-0.21875 -1.87305,-0.65625 -0.54688,-0.48308 -0.82031,-1.13932 -0.82031,-1.96875 0,-1.57682 1.80468,-2.70703 5.41406,-3.39063 0.61978,-0.11848 0.93424,-0.36913 0.94336,-0.75195 l 0.0273,-0.875 c 0.0547,-1.49478 -0.60612,-2.24218 -1.98242,-2.24219 -0.39193,1e-5 -0.76335,0.35092 -1.11426,1.05274 -0.35091,0.70183 -0.85449,1.08008 -1.51074,1.13476 -0.7474,0.0729 -1.12109,-0.24153 -1.12109,-0.94336 0,-0.43749 0.55598,-0.94791 1.66797,-1.53125 1.16666,-0.61067 2.28775,-0.916 3.36328,-0.91601 1.85025,1e-5 2.76626,0.87956 2.74804,2.63867 l -0.0547,5.63281 c -0.009,0.59245 0.24152,0.88867 0.75195,0.88867 0.10025,0 0.29166,-0.0228 0.57422,-0.0684 0.28254,-0.0456 0.4466,-0.0683 0.49219,-0.0684 0.26431,1e-5 0.39647,0.17774 0.39648,0.53321 z m -4.21094,-3.11719 c 0.009,-0.22786 -0.0433,-0.37825 -0.15722,-0.45117 -0.11394,-0.0729 -0.29396,-0.0866 -0.54004,-0.041 -2.19662,0.39193 -3.29493,1.10743 -3.29492,2.14649 -10e-6,1.04817 0.56965,1.57226 1.70898,1.57226 0.45572,0 0.92512,-0.0866 1.4082,-0.25976 0.5651,-0.20052 0.84765,-0.44206 0.84766,-0.72461 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2921" />
-      <path
-         d="m 396.52423,187.19327 c -10e-6,0.4375 -0.40105,0.8112 -1.20312,1.12109 -0.71095,0.27344 -1.431,0.41016 -2.16016,0.41016 -1.97787,0 -2.9668,-1.05273 -2.9668,-3.1582 l 0,-4.94922 c 0,-0.6289 -0.0501,-1.01399 -0.15039,-1.15528 -0.10026,-0.14126 -0.43294,-0.28026 -0.99804,-0.41699 -0.14584,-0.0365 -0.21876,-0.17772 -0.21875,-0.42383 -1e-5,-0.26431 0.0547,-0.42381 0.16406,-0.47851 0.98437,-0.48306 1.84114,-1.34895 2.57031,-2.59766 0.10026,-0.17316 0.29622,-0.22785 0.58789,-0.16406 0.20052,0.0638 0.30533,0.1823 0.31445,0.35547 l 0.0547,1.70898 c 0,0.12762 0.0228,0.21876 0.0684,0.27344 0.0638,0.082 0.20963,0.12306 0.4375,0.12305 l 3.04883,0 c 0.17317,1e-5 0.17317,0.2142 0,0.64258 -0.20964,0.51954 -0.51954,0.7793 -0.92969,0.77929 l -2.06445,0 c -0.35548,1e-5 -0.57422,0.0593 -0.65625,0.17774 -0.0638,0.082 -0.0957,0.30534 -0.0957,0.66992 l 0,4.4707 c -1e-5,1.13021 0.10025,1.84571 0.30078,2.14649 0.26431,0.38281 0.87955,0.57422 1.8457,0.57422 0.319,0 0.70637,-0.0524 1.16211,-0.15723 0.45572,-0.10482 0.69726,-0.15723 0.72461,-0.15723 0.10936,0 0.16405,0.0684 0.16406,0.20508 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2923" />
-      <path
-         d="m 416.32111,187.16593 c -2e-5,0.1914 -0.34865,0.48079 -1.0459,0.86816 -0.69728,0.38737 -1.25555,0.58106 -1.67481,0.58106 -0.35547,0 -0.66993,-0.17318 -0.94336,-0.51954 -0.27344,-0.34635 -0.46485,-0.51953 -0.57422,-0.51953 -0.082,0 -0.51498,0.18685 -1.29882,0.56055 -0.78386,0.3737 -1.57227,0.56055 -2.36524,0.56055 -0.7474,0 -1.37175,-0.21875 -1.87304,-0.65625 -0.54688,-0.48308 -0.82032,-1.13932 -0.82032,-1.96875 0,-1.57682 1.80469,-2.70703 5.41407,-3.39063 0.61978,-0.11848 0.93423,-0.36913 0.94335,-0.75195 l 0.0273,-0.875 c 0.0547,-1.49478 -0.60613,-2.24218 -1.98242,-2.24219 -0.39194,1e-5 -0.76335,0.35092 -1.11426,1.05274 -0.35092,0.70183 -0.8545,1.08008 -1.51074,1.13476 -0.7474,0.0729 -1.1211,-0.24153 -1.1211,-0.94336 0,-0.43749 0.55599,-0.94791 1.66797,-1.53125 1.16666,-0.61067 2.28776,-0.916 3.36328,-0.91601 1.85025,1e-5 2.76627,0.87956 2.74805,2.63867 l -0.0547,5.63281 c -0.009,0.59245 0.24153,0.88867 0.75196,0.88867 0.10025,0 0.29165,-0.0228 0.57421,-0.0684 0.28254,-0.0456 0.44661,-0.0683 0.49219,-0.0684 0.26431,1e-5 0.39647,0.17774 0.39649,0.53321 z m -4.21094,-3.11719 c 0.009,-0.22786 -0.0433,-0.37825 -0.15723,-0.45117 -0.11394,-0.0729 -0.29395,-0.0866 -0.54004,-0.041 -2.19662,0.39193 -3.29492,1.10743 -3.29492,2.14649 0,1.04817 0.56966,1.57226 1.70899,1.57226 0.45572,0 0.92512,-0.0866 1.4082,-0.25976 0.5651,-0.20052 0.84765,-0.44206 0.84765,-0.72461 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2925" />
-      <path
-         d="m 432.45392,187.19327 c -10e-6,0.4375 -0.40105,0.8112 -1.20313,1.12109 -0.71094,0.27344 -1.43099,0.41016 -2.16015,0.41016 -1.97787,0 -2.9668,-1.05273 -2.9668,-3.1582 l 0,-4.94922 c 0,-0.6289 -0.0501,-1.01399 -0.15039,-1.15528 -0.10026,-0.14126 -0.43294,-0.28026 -0.99805,-0.41699 -0.14583,-0.0365 -0.21875,-0.17772 -0.21875,-0.42383 0,-0.26431 0.0547,-0.42381 0.16407,-0.47851 0.98437,-0.48306 1.84114,-1.34895 2.57031,-2.59766 0.10025,-0.17316 0.29622,-0.22785 0.58789,-0.16406 0.20051,0.0638 0.30533,0.1823 0.31445,0.35547 l 0.0547,1.70898 c -10e-6,0.12762 0.0228,0.21876 0.0684,0.27344 0.0638,0.082 0.20963,0.12306 0.4375,0.12305 l 3.04883,0 c 0.17317,1e-5 0.17317,0.2142 0,0.64258 -0.20965,0.51954 -0.51954,0.7793 -0.92969,0.77929 l -2.06445,0 c -0.35548,1e-5 -0.57423,0.0593 -0.65625,0.17774 -0.0638,0.082 -0.0957,0.30534 -0.0957,0.66992 l 0,4.4707 c 0,1.13021 0.10026,1.84571 0.30078,2.14649 0.26432,0.38281 0.87956,0.57422 1.84571,0.57422 0.319,0 0.70637,-0.0524 1.16211,-0.15723 0.45572,-0.10482 0.69725,-0.15723 0.72461,-0.15723 0.10936,0 0.16405,0.0684 0.16406,0.20508 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2927" />
-      <path
-         d="m 439.30353,172.66007 c -1e-5,0.41928 -0.15723,0.80893 -0.47168,1.16894 -0.31446,0.36004 -0.65854,0.54006 -1.03223,0.54004 -0.42839,2e-5 -0.7793,-0.12759 -1.05273,-0.38281 -0.27344,-0.25519 -0.41016,-0.58788 -0.41016,-0.99805 0,-0.40102 0.16634,-0.77472 0.49902,-1.12109 0.33268,-0.34634 0.69043,-0.51952 1.07325,-0.51953 0.92968,1e-5 1.39452,0.43751 1.39453,1.3125 z m 1.51758,15.47656 c -0.0456,0.26432 -0.15496,0.41471 -0.32813,0.45117 -0.0456,0.009 -0.26433,0 -0.65625,-0.0273 -1.35808,-0.0911 -2.69336,-0.0729 -4.00586,0.0547 -0.35547,0.0365 -0.57878,0.0228 -0.66992,-0.041 -0.0912,-0.0638 -0.13672,-0.20964 -0.13672,-0.4375 0,-0.20964 0.24154,-0.38281 0.72461,-0.51953 0.52864,-0.15495 0.79297,-0.66081 0.79297,-1.51758 l 0,-3.58203 c 0,-0.72005 -0.0729,-1.22591 -0.21875,-1.51758 -0.20052,-0.40103 -0.61524,-0.70637 -1.24414,-0.91601 -0.28256,-0.10026 -0.42383,-0.25065 -0.42383,-0.45118 0,-0.26431 0.20508,-0.46027 0.61523,-0.58789 0.76563,-0.23697 1.44466,-0.55142 2.03711,-0.94336 0.47396,-0.32811 0.76562,-0.49217 0.875,-0.49218 0.36458,1e-5 0.54232,0.23699 0.53321,0.71093 -0.0365,2.38803 -0.0547,4.82162 -0.0547,7.30078 -10e-6,0.59245 0.0866,1.01628 0.25977,1.27149 0.1914,0.28255 0.55598,0.48307 1.09375,0.60156 0.59244,0.13672 0.86132,0.35091 0.80664,0.64258 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2929" />
-      <path
-         d="m 464.89728,188.10929 c -3e-5,0.30078 -0.1413,0.44205 -0.42383,0.42382 -1.8594,-0.13671 -3.59117,-0.13671 -5.19531,0 -0.35549,0.0274 -0.53322,-0.13216 -0.53321,-0.47851 -1e-5,-0.27344 0.24152,-0.41927 0.72461,-0.4375 0.61066,-0.0365 0.916,-0.5332 0.91602,-1.49024 l 0,-3.63671 c -2e-5,-2.04166 -0.82945,-3.0625 -2.48828,-3.0625 -0.87502,0 -1.6771,0.15951 -2.40625,0.47851 -0.66538,0.28256 -1.00262,0.56511 -1.01172,0.84766 l -0.0547,5.42773 c -10e-6,0.56511 0.0684,0.9388 0.20508,1.1211 0.10025,0.1276 0.32355,0.21875 0.66992,0.27343 0.80207,0.13672 1.20311,0.30534 1.20313,0.50586 -2e-5,0.17318 -0.0228,0.29167 -0.0684,0.35547 -0.0547,0.0911 -0.21877,0.13216 -0.49219,0.12305 -1.03908,-0.0365 -2.5521,-0.0182 -4.53906,0.0547 -0.28256,0.009 -0.46486,-0.0137 -0.54688,-0.0684 -0.082,-0.0547 -0.12305,-0.1823 -0.12304,-0.38282 -10e-6,-0.23698 0.25975,-0.38281 0.77929,-0.4375 0.5651,-0.0638 0.84765,-0.57877 0.84766,-1.54492 l 0,-3.71875 c -10e-6,-1.0026 -0.22788,-1.77733 -0.6836,-2.32422 -0.40105,-0.49218 -0.91146,-0.73827 -1.53125,-0.73828 -0.91146,1e-5 -1.73177,0.18458 -2.46093,0.55371 -0.72917,0.36915 -1.09376,0.76336 -1.09375,1.18262 l 0,4.99023 c -1e-5,0.56511 0.14583,0.95704 0.4375,1.17579 0.26431,0.20052 0.74283,0.32356 1.43554,0.36914 0.37369,0.0182 0.56054,0.14583 0.56055,0.38281 -10e-6,0.26432 -0.1504,0.39648 -0.45117,0.39648 -2.27865,0 -3.92839,0.0638 -4.94922,0.19141 -0.34636,0.0456 -0.56966,0.0456 -0.66992,0 -0.082,-0.0456 -0.12305,-0.15495 -0.12305,-0.32813 0,-0.22786 0.33724,-0.42382 1.01172,-0.58789 0.41015,-0.10937 0.61523,-0.64257 0.61523,-1.59961 l 0,-4.19726 c 0,-1.0664 -0.28711,-1.68163 -0.86132,-1.8457 -0.48308,-0.13672 -0.77702,-0.23698 -0.88184,-0.30079 -0.10482,-0.0638 -0.15723,-0.16861 -0.15723,-0.31445 0,-0.16405 0.47396,-0.51041 1.42188,-1.03906 1.0026,-0.5651 1.61783,-0.84765 1.8457,-0.84766 0.1914,1e-5 0.35319,0.27573 0.48535,0.82715 0.13216,0.55144 0.2347,0.82716 0.30762,0.82715 0.10026,1e-5 0.37825,-0.14127 0.83399,-0.42383 0.56509,-0.35546 1.07551,-0.61978 1.53125,-0.79297 0.74738,-0.29165 1.51756,-0.43749 2.31054,-0.4375 0.63801,1e-5 1.194,0.13673 1.66797,0.41016 0.32811,0.1823 0.62434,0.43295 0.88867,0.75195 0.21874,0.27345 0.32812,0.41017 0.32813,0.41016 -10e-6,1e-5 0.23696,-0.13671 0.71094,-0.41016 0.55597,-0.319 1.09829,-0.56965 1.62695,-0.75195 0.77472,-0.27343 1.52667,-0.41015 2.25586,-0.41016 2.49737,1e-5 3.74607,1.37176 3.74609,4.11524 l 0,4.42968 c -2e-5,0.51954 0.12303,0.88868 0.36914,1.10743 0.21873,0.18229 0.64712,0.33724 1.28516,0.46484 0.48305,0.0911 0.72458,0.22786 0.72461,0.41016 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2931" />
-      <path
-         d="m 476.45001,186.18155 c -1e-5,0.39193 -0.52865,0.87956 -1.58594,1.46289 -1.20313,0.64714 -2.43815,0.97071 -3.70507,0.97071 -1.37631,0 -2.53386,-0.45118 -3.47266,-1.35352 -1.00261,-0.95703 -1.50391,-2.23307 -1.50391,-3.82813 0,-1.80468 0.56055,-3.25389 1.68164,-4.34765 1.04818,-1.02082 2.33333,-1.53124 3.85547,-1.53125 0.90234,1e-5 1.74544,0.28712 2.5293,0.86133 0.70181,0.51042 1.16666,1.09376 1.39453,1.75 0.082,0.22787 0.20507,0.35547 0.36914,0.38281 0.23697,0.0456 0.35546,0.20508 0.35547,0.47851 -10e-6,0.37371 -0.44662,0.69272 -1.33984,0.95704 l -6.30274,1.87304 c 0.41016,2.16928 1.52213,3.25391 3.33594,3.25391 1.04817,0 2.24218,-0.38281 3.58203,-1.14844 0.20051,-0.11849 0.39648,-0.17773 0.58789,-0.17773 0.14582,0 0.21874,0.13216 0.21875,0.39648 z m -3.24023,-5.48242 c -1e-5,-0.50129 -0.20281,-0.94107 -0.6084,-1.31934 -0.40561,-0.37824 -0.90463,-0.56737 -1.49707,-0.56738 -1.67709,1e-5 -2.51563,1.20769 -2.51563,3.62305 l 0,0.32812 3.71875,-1.12109 c 0.60156,-0.17317 0.90234,-0.48762 0.90235,-0.94336 z"
-         style="font-size:28px;font-variant:normal;font-stretch:normal;text-align:end;line-height:89.99999762%;writing-mode:lr-tb;text-anchor:end;font-family:High Tower Text;-inkscape-font-specification:High Tower Text"
-         id="path2933" />
-    </g>
-  </g>
-</svg>
diff --git a/artwork/logo-lineart.svg b/artwork/logo-lineart.svg
deleted file mode 100644
index 615260d..0000000
--- a/artwork/logo-lineart.svg
+++ /dev/null
@@ -1,165 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="211.15901"
-   height="190.52811"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.48.5 r10040"
-   sodipodi:docname="logo-lineart.svg">
-  <defs
-     id="defs4">
-    <inkscape:perspective
-       sodipodi:type="inkscape:persp3d"
-       inkscape:vp_x="0 : 526.18109 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_z="744.09448 : 526.18109 : 1"
-       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
-       id="perspective10" />
-    <inkscape:perspective
-       id="perspective2824"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2840"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2878"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2894"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2910"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2926"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective2976"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective3020"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective3036"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective3052"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-    <inkscape:perspective
-       id="perspective3866"
-       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
-       inkscape:vp_z="1 : 0.5 : 1"
-       inkscape:vp_y="0 : 1000 : 0"
-       inkscape:vp_x="0 : 0.5 : 1"
-       sodipodi:type="inkscape:persp3d" />
-  </defs>
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="2.1723341"
-     inkscape:cx="242.05817"
-     inkscape:cy="92.686293"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     inkscape:window-width="1676"
-     inkscape:window-height="1005"
-     inkscape:window-x="4"
-     inkscape:window-y="0"
-     inkscape:window-maximized="1"
-     fit-margin-top="10"
-     fit-margin-left="10"
-     fit-margin-right="10"
-     fit-margin-bottom="10" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(-29.820801,-20.186869)">
-    <path
-       style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="M 9.7525867,55.788422 C 40.421293,45.982204 33.821969,46.567748 69.327984,40.346648 c 8.493721,2.411576 22.910914,5.687215 22.240236,12.296506 -6.241933,2.320572 -15.351869,-6.455434 -20.254712,-1.539882 0.01014,18.421641 5.965221,38.200493 13.480678,55.747588 7.515457,17.5471 18.880714,32.86245 34.290034,42.35708 20.42595,12.66826 41.92048,14.9356 63.64846,15.65546 6.66858,0.23786 17.30912,-1.47838 20.01846,0 -4.9124,8.703 -19.28006,12.8118 -34.21844,14.71154 -14.93837,1.89974 -30.44747,1.59043 -37.64272,1.45723 -15.88921,-0.50065 -29.5942,-2.65111 -42.06658,-7.29048 C 56.640409,160.78176 38.428746,134.71246 24.668078,106.25832 16.765019,89.693325 11.290118,72.259923 9.7525867,55.788422 z"
-       id="path3826"
-       inkscape:connector-curvature="0"
-       transform="translate(29.820801,20.186869)"
-       sodipodi:nodetypes="ccccscccscccc" />
-    <path
-       style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 54.066806,32.351647 c -0.427165,0.87404 -0.384822,1.998232 -0.02834,2.90275 0.781834,1.983761 2.799883,3.252081 4.397491,4.681241 0.728446,0.651642 2.26934,0.803097 2.364296,1.769134 0.215279,2.190161 -2.700769,3.566537 -4.456242,4.921486 -1.316317,1.015991 -3.845581,0.776849 -4.451985,2.314219 -0.417515,1.058499 0.837317,2.10047 1.1679,3.188615 0.465799,1.533243 1.642442,3.150334 1.145997,4.674061 -0.597449,1.833868 -2.700081,2.84663 -4.420626,3.754433 -1.893115,0.998854 -4.450538,0.497797 -6.207667,1.715064 -1.674125,1.159765 -3.485979,2.907099 -3.554321,4.925579 -0.03097,0.915115 -0.384582,2.676814 -0.233936,3.114037 12.863193,-4.155671 20.195138,-6.507915 28.694286,-8.598094 8.499136,-2.090222 16.108852,-3.399531 29.579722,-5.689662 -0.06867,-0.457321 -1.197061,-1.855664 -1.647827,-2.652661 -0.994254,-1.75795 -3.408869,-2.469029 -5.429591,-2.722885 -2.120906,-0.26644 -4.15652,1.360749 -6.296964,1.350851 -1.945327,-0.009 -4.277958,0.06569 -5.655921,-1.283841 -1.144955,-1.121286 -0.849755,-3.099246 -1.145997,-4.674061 -0.210243,-1.117649 0.420309,-2.621884 -0.439473,-3.367212 -1.248754,-1.082519 -3.380554,0.299434 -5.017542,0.0075 -2.183125,-0.389274 -5.405114,-0.260713 -6.227327,-2.302063 -0.362663,-0.900401 0.93342,-1.747432 1.277831,-2.662119 0.75535,-2.006065 1.957858,-4.064009 1.733419,-6.184432 -0.102333,-0.966833 -0.5848,-1.983113 -1.367813,-2.56044 -1.68203,-1.191313 -4.366912,-1.323763 -7.531636,-0.525881 -3.164723,0.797885 -5.342193,2.137743 -6.247739,3.90434 z"
-       id="path3832"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="csssssscssscccssscssssssccc" />
-    <path
-       style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
-       d="m 71.836704,50.617573 c 0,0 -24.55635,5.277975 -35.918352,8.551988 C 27.8621,61.491007 12.143824,67.37947 12.143824,67.37947"
-       id="path3847"
-       inkscape:connector-curvature="0"
-       transform="translate(29.820801,20.186869)"
-       sodipodi:nodetypes="csc" />
-  </g>
-</svg>
diff --git a/debian/changelog b/debian/changelog
index 0d89c78..93da147 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+flask (2.3.2-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+  * Drop patch
+    CVE-2023-30861_set_Vary_Cookie_header_consistently_for_session.patch,
+    present upstream.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Mon, 19 Jun 2023 19:10:42 -0000
+
 flask (2.2.2-3) unstable; urgency=high
 
   * CVE-2023-30861: Flask vulnerable to possible disclosure of permanent
diff --git a/debian/patches/0001-Don-t-require-sphinxcontrib.log_cabinet-extension.patch b/debian/patches/0001-Don-t-require-sphinxcontrib.log_cabinet-extension.patch
index 3bbf7f6..59e253f 100644
--- a/debian/patches/0001-Don-t-require-sphinxcontrib.log_cabinet-extension.patch
+++ b/debian/patches/0001-Don-t-require-sphinxcontrib.log_cabinet-extension.patch
@@ -7,10 +7,10 @@ Subject: Don't require sphinxcontrib.log_cabinet extension
  docs/conf.py | 1 -
  1 file changed, 1 deletion(-)
 
-Index: flask/docs/conf.py
+Index: flask.git/docs/conf.py
 ===================================================================
---- flask.orig/docs/conf.py
-+++ flask/docs/conf.py
+--- flask.git.orig/docs/conf.py
++++ flask.git/docs/conf.py
 @@ -15,7 +15,6 @@ master_doc = "index"
  extensions = [
      "sphinx.ext.autodoc",
diff --git a/debian/patches/0002-Make-the-documentation-build-reproducibly.patch b/debian/patches/0002-Make-the-documentation-build-reproducibly.patch
index b9e3754..8e64042 100644
--- a/debian/patches/0002-Make-the-documentation-build-reproducibly.patch
+++ b/debian/patches/0002-Make-the-documentation-build-reproducibly.patch
@@ -21,10 +21,10 @@ This commit skips this (inherited) member from the documentation.
  docs/api.rst | 1 +
  1 file changed, 1 insertion(+)
 
-Index: flask/docs/api.rst
+Index: flask.git/docs/api.rst
 ===================================================================
---- flask.orig/docs/api.rst
-+++ flask/docs/api.rst
+--- flask.git.orig/docs/api.rst
++++ flask.git/docs/api.rst
 @@ -22,6 +22,7 @@ Blueprint Objects
  .. autoclass:: Blueprint
     :members:
diff --git a/debian/patches/CVE-2023-30861_set_Vary_Cookie_header_consistently_for_session.patch b/debian/patches/CVE-2023-30861_set_Vary_Cookie_header_consistently_for_session.patch
deleted file mode 100644
index 87d7d37..0000000
--- a/debian/patches/CVE-2023-30861_set_Vary_Cookie_header_consistently_for_session.patch
+++ /dev/null
@@ -1,87 +0,0 @@
-Subject: CVE-2023-30861 set `Vary: Cookie` header consistently for session
-Author: David Lord <davidism@gmail.com>
-Date: Mon, 1 May 2023 08:01:32 -0700
-Origin: upstream, https://github.com/pallets/flask/commit/8646edca6f47e2cd57464081b3911218d4734f8d
-Last-Update: 2022-05-11
-
-Index: flask/src/flask/sessions.py
-===================================================================
---- flask.orig/src/flask/sessions.py
-+++ flask/src/flask/sessions.py
-@@ -383,6 +383,10 @@ class SecureCookieSessionInterface(Sessi
-         samesite = self.get_cookie_samesite(app)
-         httponly = self.get_cookie_httponly(app)
- 
-+        # Add a "Vary: Cookie" header if the session was accessed at all.
-+        if session.accessed:
-+            response.vary.add("Cookie")
-+
-         # If the session is modified to be empty, remove the cookie.
-         # If the session is empty, return without setting the cookie.
-         if not session:
-@@ -395,13 +399,10 @@ class SecureCookieSessionInterface(Sessi
-                     samesite=samesite,
-                     httponly=httponly,
-                 )
-+                response.vary.add("Cookie")
- 
-             return
- 
--        # Add a "Vary: Cookie" header if the session was accessed at all.
--        if session.accessed:
--            response.vary.add("Cookie")
--
-         if not self.should_set_cookie(app, session):
-             return
- 
-@@ -417,3 +418,4 @@ class SecureCookieSessionInterface(Sessi
-             secure=secure,
-             samesite=samesite,
-         )
-+        response.vary.add("Cookie")
-Index: flask/tests/test_basic.py
-===================================================================
---- flask.orig/tests/test_basic.py
-+++ flask/tests/test_basic.py
-@@ -556,6 +556,11 @@ def test_session_vary_cookie(app, client
-     def setdefault():
-         return flask.session.setdefault("test", "default")
- 
-+    @app.route("/clear")
-+    def clear():
-+        flask.session.clear()
-+        return ""
-+
-     @app.route("/vary-cookie-header-set")
-     def vary_cookie_header_set():
-         response = flask.Response()
-@@ -588,11 +593,29 @@ def test_session_vary_cookie(app, client
-     expect("/get")
-     expect("/getitem")
-     expect("/setdefault")
-+    expect("/clear")
-     expect("/vary-cookie-header-set")
-     expect("/vary-header-set", "Accept-Encoding, Accept-Language, Cookie")
-     expect("/no-vary-header", None)
- 
- 
-+def test_session_refresh_vary(app, client):
-+    @app.get("/login")
-+    def login():
-+        flask.session["user_id"] = 1
-+        flask.session.permanent = True
-+        return ""
-+
-+    @app.get("/ignored")
-+    def ignored():
-+        return ""
-+
-+    rv = client.get("/login")
-+    assert rv.headers["Vary"] == "Cookie"
-+    rv = client.get("/ignored")
-+    assert rv.headers["Vary"] == "Cookie"
-+
-+
- def test_flashes(app, req_ctx):
-     assert not flask.session.modified
-     flask.flash("Zap")
diff --git a/debian/patches/remove-eticalads-in-doc.patch b/debian/patches/remove-eticalads-in-doc.patch
index cba9065..0051628 100644
--- a/debian/patches/remove-eticalads-in-doc.patch
+++ b/debian/patches/remove-eticalads-in-doc.patch
@@ -4,9 +4,11 @@ Author: Thomas Goirand <zigo@debian.org>
 Forwarded: no
 Last-Update: 2021-09-19
 
---- flask-2.0.1.orig/docs/conf.py
-+++ flask-2.0.1/docs/conf.py
-@@ -47,10 +47,10 @@ html_context = {
+Index: flask.git/docs/conf.py
+===================================================================
+--- flask.git.orig/docs/conf.py
++++ flask.git/docs/conf.py
+@@ -45,10 +45,10 @@ html_context = {
      ]
  }
  html_sidebars = {
@@ -18,5 +20,5 @@ Last-Update: 2021-09-19
 -singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
 +singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]}
  html_static_path = ["_static"]
- html_favicon = "_static/flask-icon.png"
- html_logo = "_static/flask-icon.png"
+ html_favicon = "_static/shortcut-icon.png"
+ html_logo = "_static/flask-vertical.png"
diff --git a/debian/patches/remove-using-intersphinx.patch b/debian/patches/remove-using-intersphinx.patch
index 9a1b772..973aa1e 100644
--- a/debian/patches/remove-using-intersphinx.patch
+++ b/debian/patches/remove-using-intersphinx.patch
@@ -3,8 +3,10 @@ Author: Thomas Goirand <zigo@debian.org>
 Forwarded: not-needed
 Last-Update: 2021-09-19
 
---- flask-2.0.1.orig/docs/conf.py
-+++ flask-2.0.1/docs/conf.py
+Index: flask.git/docs/conf.py
+===================================================================
+--- flask.git.orig/docs/conf.py
++++ flask.git/docs/conf.py
 @@ -14,7 +14,6 @@ release, version = get_version("Flask")
  master_doc = "index"
  extensions = [
diff --git a/debian/patches/series b/debian/patches/series
index 9d5587a..2f2e6b6 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -2,4 +2,3 @@
 0002-Make-the-documentation-build-reproducibly.patch
 remove-using-intersphinx.patch
 remove-eticalads-in-doc.patch
-CVE-2023-30861_set_Vary_Cookie_header_consistently_for_session.patch
diff --git a/docs/_static/flask-horizontal.png b/docs/_static/flask-horizontal.png
new file mode 100644
index 0000000..a0df2c6
Binary files /dev/null and b/docs/_static/flask-horizontal.png differ
diff --git a/docs/_static/flask-icon.png b/docs/_static/flask-icon.png
deleted file mode 100644
index 55cb847..0000000
Binary files a/docs/_static/flask-icon.png and /dev/null differ
diff --git a/docs/_static/flask-logo.png b/docs/_static/flask-logo.png
deleted file mode 100644
index ce23606..0000000
Binary files a/docs/_static/flask-logo.png and /dev/null differ
diff --git a/docs/_static/flask-vertical.png b/docs/_static/flask-vertical.png
new file mode 100644
index 0000000..d1fd149
Binary files /dev/null and b/docs/_static/flask-vertical.png differ
diff --git a/docs/_static/no.png b/docs/_static/no.png
deleted file mode 100644
index 644c3f7..0000000
Binary files a/docs/_static/no.png and /dev/null differ
diff --git a/docs/_static/pycharm-run-config.png b/docs/_static/pycharm-run-config.png
index 3f78915..ad02554 100644
Binary files a/docs/_static/pycharm-run-config.png and b/docs/_static/pycharm-run-config.png differ
diff --git a/docs/_static/shortcut-icon.png b/docs/_static/shortcut-icon.png
new file mode 100644
index 0000000..4d3e6c3
Binary files /dev/null and b/docs/_static/shortcut-icon.png differ
diff --git a/docs/_static/yes.png b/docs/_static/yes.png
deleted file mode 100644
index 56917ab..0000000
Binary files a/docs/_static/yes.png and /dev/null differ
diff --git a/docs/api.rst b/docs/api.rst
index 880720b..043beb0 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -3,7 +3,7 @@ API
 
 .. module:: flask
 
-This part of the documentation covers all the interfaces of Flask.  For
+This part of the documentation covers all the interfaces of Flask. For
 parts where Flask depends on external libraries, we document the most
 important right here and provide links to the canonical documentation.
 
@@ -34,12 +34,12 @@ Incoming Request Data
 .. attribute:: request
 
    To access incoming request data, you can use the global `request`
-   object.  Flask parses incoming request data for you and gives you
-   access to it through that global object.  Internally Flask makes
+   object. Flask parses incoming request data for you and gives you
+   access to it through that global object. Internally Flask makes
    sure that you always get the correct data for the active thread if you
    are in a multithreaded environment.
 
-   This is a proxy.  See :ref:`notes-on-proxies` for more information.
+   This is a proxy. See :ref:`notes-on-proxies` for more information.
 
    The request object is an instance of a :class:`~flask.Request`.
 
@@ -69,7 +69,7 @@ To access the current session you can use the :class:`session` object:
    The session object works pretty much like an ordinary dict, with the
    difference that it keeps track of modifications.
 
-   This is a proxy.  See :ref:`notes-on-proxies` for more information.
+   This is a proxy. See :ref:`notes-on-proxies` for more information.
 
    The following attributes are interesting:
 
@@ -79,10 +79,10 @@ To access the current session you can use the :class:`session` object:
 
    .. attribute:: modified
 
-      ``True`` if the session object detected a modification.  Be advised
+      ``True`` if the session object detected a modification. Be advised
       that modifications on mutable structures are not picked up
       automatically, in that situation you have to explicitly set the
-      attribute to ``True`` yourself.  Here an example::
+      attribute to ``True`` yourself. Here an example::
 
           # this change is not picked up because a mutable object (here
           # a list) is changed.
@@ -93,8 +93,8 @@ To access the current session you can use the :class:`session` object:
    .. attribute:: permanent
 
       If set to ``True`` the session lives for
-      :attr:`~flask.Flask.permanent_session_lifetime` seconds.  The
-      default is 31 days.  If set to ``False`` (which is the default) the
+      :attr:`~flask.Flask.permanent_session_lifetime` seconds. The
+      default is 31 days. If set to ``False`` (which is the default) the
       session will be deleted when the user closes the browser.
 
 
@@ -155,9 +155,9 @@ Application Globals
 
 To share data that is valid for one request only from one function to
 another, a global variable is not good enough because it would break in
-threaded environments.  Flask provides you with a special object that
+threaded environments. Flask provides you with a special object that
 ensures it is only valid for the active request and that will return
-different values for each request.  In a nutshell: it does the right
+different values for each request. In a nutshell: it does the right
 thing, like it does for :class:`request` and :class:`session`.
 
 .. data:: g
@@ -217,10 +217,6 @@ Useful Functions and Classes
 
 .. autofunction:: send_from_directory
 
-.. autofunction:: escape
-
-.. autoclass:: Markup
-   :members: escape, unescape, striptags
 
 Message Flashing
 ----------------
@@ -248,7 +244,7 @@ HTML ``<script>`` tags.
 .. sourcecode:: html+jinja
 
     <script>
-        const names = {{ names|tosjon }};
+        const names = {{ names|tojson }};
         renderChart(names, {{ axis_data|tojson }});
     </script>
 
@@ -270,12 +266,6 @@ HTML ``<script>`` tags.
     :members:
     :member-order: bysource
 
-.. autoclass:: JSONEncoder
-   :members:
-
-.. autoclass:: JSONDecoder
-   :members:
-
 .. automodule:: flask.json.tag
 
 
@@ -343,18 +333,13 @@ Useful Internals
 Signals
 -------
 
-.. versionadded:: 0.6
-
-.. data:: signals.signals_available
-
-   ``True`` if the signaling system is available.  This is the case
-   when `blinker`_ is installed.
+Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introduction.
 
-The following signals exist in Flask:
+.. _blinker: https://blinker.readthedocs.io/
 
 .. data:: template_rendered
 
-   This signal is sent when a template was successfully rendered.  The
+   This signal is sent when a template was successfully rendered. The
    signal is invoked with the instance of the template as `template`
    and the context as dictionary (named `context`).
 
@@ -388,7 +373,7 @@ The following signals exist in Flask:
 .. data:: request_started
 
    This signal is sent when the request context is set up, before
-   any request processing happens.  Because the request context is already
+   any request processing happens. Because the request context is already
    bound, the subscriber can access the request with the standard global
    proxies such as :class:`~flask.request`.
 
@@ -408,7 +393,7 @@ The following signals exist in Flask:
    Example subscriber::
 
         def log_response(sender, response, **extra):
-            sender.logger.debug('Request context is about to close down.  '
+            sender.logger.debug('Request context is about to close down. '
                                 'Response: %s', response)
 
         from flask import request_finished
@@ -445,8 +430,8 @@ The following signals exist in Flask:
 
 .. data:: request_tearing_down
 
-   This signal is sent when the request is tearing down.  This is always
-   called, even if an exception is caused.  Currently functions listening
+   This signal is sent when the request is tearing down. This is always
+   called, even if an exception is caused. Currently functions listening
    to this signal are called after the regular teardown handlers, but this
    is not something you can rely on.
 
@@ -464,8 +449,8 @@ The following signals exist in Flask:
 
 .. data:: appcontext_tearing_down
 
-   This signal is sent when the app context is tearing down.  This is always
-   called, even if an exception is caused.  Currently functions listening
+   This signal is sent when the app context is tearing down. This is always
+   called, even if an exception is caused. Currently functions listening
    to this signal are called after the regular teardown handlers, but this
    is not something you can rely on.
 
@@ -482,9 +467,9 @@ The following signals exist in Flask:
 
 .. data:: appcontext_pushed
 
-   This signal is sent when an application context is pushed.  The sender
-   is the application.  This is usually useful for unittests in order to
-   temporarily hook in information.  For instance it can be used to
+   This signal is sent when an application context is pushed. The sender
+   is the application. This is usually useful for unittests in order to
+   temporarily hook in information. For instance it can be used to
    set a resource early onto the `g` object.
 
    Example usage::
@@ -511,16 +496,15 @@ The following signals exist in Flask:
 
 .. data:: appcontext_popped
 
-   This signal is sent when an application context is popped.  The sender
-   is the application.  This usually falls in line with the
+   This signal is sent when an application context is popped. The sender
+   is the application. This usually falls in line with the
    :data:`appcontext_tearing_down` signal.
 
    .. versionadded:: 0.10
 
-
 .. data:: message_flashed
 
-   This signal is sent when the application is flashing a message.  The
+   This signal is sent when the application is flashing a message. The
    messages is sent as `message` keyword argument and the category as
    `category`.
 
@@ -535,22 +519,10 @@ The following signals exist in Flask:
 
    .. versionadded:: 0.10
 
-.. class:: signals.Namespace
-
-   An alias for :class:`blinker.base.Namespace` if blinker is available,
-   otherwise a dummy class that creates fake signals.  This class is
-   available for Flask extensions that want to provide the same fallback
-   system as Flask itself.
-
-   .. method:: signal(name, doc=None)
-
-      Creates a new signal for this namespace if blinker is available,
-      otherwise returns a fake signal that has a send method that will
-      do nothing but will fail with a :exc:`RuntimeError` for all other
-      operations, including connecting.
-
+.. data:: signals.signals_available
 
-.. _blinker: https://pypi.org/project/blinker/
+    .. deprecated:: 2.3
+        Will be removed in Flask 2.4. Signals are always available
 
 
 Class-Based Views
@@ -579,7 +551,7 @@ Generally there are three ways to define rules for the routing system:
     which is exposed as :attr:`flask.Flask.url_map`.
 
 Variable parts in the route can be specified with angular brackets
-(``/user/<username>``).  By default a variable part in the URL accepts any
+(``/user/<username>``). By default a variable part in the URL accepts any
 string without a slash however a different converter can be specified as
 well by using ``<converter:name>``.
 
@@ -613,7 +585,7 @@ Here are some examples::
         pass
 
 An important detail to keep in mind is how Flask deals with trailing
-slashes.  The idea is to keep each URL unique so the following rules
+slashes. The idea is to keep each URL unique so the following rules
 apply:
 
 1. If a rule ends with a slash and is requested without a slash by the
@@ -622,11 +594,11 @@ apply:
 2. If a rule does not end with a trailing slash and the user requests the
    page with a trailing slash, a 404 not found is raised.
 
-This is consistent with how web servers deal with static files.  This
+This is consistent with how web servers deal with static files. This
 also makes it possible to use relative link targets safely.
 
-You can also define multiple rules for the same function.  They have to be
-unique however.  Defaults can also be specified.  Here for example is a
+You can also define multiple rules for the same function. They have to be
+unique however. Defaults can also be specified. Here for example is a
 definition for a URL that accepts an optional page::
 
     @app.route('/users/', defaults={'page': 1})
@@ -649,33 +621,33 @@ can't preserve form data. ::
       pass
 
 Here are the parameters that :meth:`~flask.Flask.route` and
-:meth:`~flask.Flask.add_url_rule` accept.  The only difference is that
+:meth:`~flask.Flask.add_url_rule` accept. The only difference is that
 with the route parameter the view function is defined with the decorator
 instead of the `view_func` parameter.
 
 =============== ==========================================================
 `rule`          the URL rule as string
-`endpoint`      the endpoint for the registered URL rule.  Flask itself
+`endpoint`      the endpoint for the registered URL rule. Flask itself
                 assumes that the name of the view function is the name
                 of the endpoint if not explicitly stated.
 `view_func`     the function to call when serving a request to the
-                provided endpoint.  If this is not provided one can
+                provided endpoint. If this is not provided one can
                 specify the function later by storing it in the
                 :attr:`~flask.Flask.view_functions` dictionary with the
                 endpoint as key.
-`defaults`      A dictionary with defaults for this rule.  See the
+`defaults`      A dictionary with defaults for this rule. See the
                 example above for how defaults work.
 `subdomain`     specifies the rule for the subdomain in case subdomain
-                matching is in use.  If not specified the default
+                matching is in use. If not specified the default
                 subdomain is assumed.
 `**options`     the options to be forwarded to the underlying
-                :class:`~werkzeug.routing.Rule` object.  A change to
-                Werkzeug is handling of method options.  methods is a list
+                :class:`~werkzeug.routing.Rule` object. A change to
+                Werkzeug is handling of method options. methods is a list
                 of methods this rule should be limited to (``GET``, ``POST``
-                etc.).  By default a rule just listens for ``GET`` (and
-                implicitly ``HEAD``).  Starting with Flask 0.6, ``OPTIONS`` is
+                etc.). By default a rule just listens for ``GET`` (and
+                implicitly ``HEAD``). Starting with Flask 0.6, ``OPTIONS`` is
                 implicitly added and handled by the standard request
-                handling.  They have to be specified as keyword arguments.
+                handling. They have to be specified as keyword arguments.
 =============== ==========================================================
 
 
@@ -687,19 +659,19 @@ customize behavior the view function would normally not have control over.
 The following attributes can be provided optionally to either override
 some defaults to :meth:`~flask.Flask.add_url_rule` or general behavior:
 
--   `__name__`: The name of a function is by default used as endpoint.  If
-    endpoint is provided explicitly this value is used.  Additionally this
+-   `__name__`: The name of a function is by default used as endpoint. If
+    endpoint is provided explicitly this value is used. Additionally this
     will be prefixed with the name of the blueprint by default which
     cannot be customized from the function itself.
 
 -   `methods`: If methods are not provided when the URL rule is added,
     Flask will look on the view function object itself if a `methods`
-    attribute exists.  If it does, it will pull the information for the
+    attribute exists. If it does, it will pull the information for the
     methods from there.
 
 -   `provide_automatic_options`: if this attribute is set Flask will
     either force enable or disable the automatic implementation of the
-    HTTP ``OPTIONS`` response.  This can be useful when working with
+    HTTP ``OPTIONS`` response. This can be useful when working with
     decorators that want to customize the ``OPTIONS`` response on a per-view
     basis.
 
diff --git a/docs/appcontext.rst b/docs/appcontext.rst
index a4ae386..5509a9a 100644
--- a/docs/appcontext.rst
+++ b/docs/appcontext.rst
@@ -140,10 +140,8 @@ Accessing ``db`` will call ``get_db`` internally, in the same way that
 Events and Signals
 ------------------
 
-The application will call functions registered with
-:meth:`~Flask.teardown_appcontext` when the application context is
-popped.
+The application will call functions registered with :meth:`~Flask.teardown_appcontext`
+when the application context is popped.
 
-If :data:`~signals.signals_available` is true, the following signals are
-sent: :data:`appcontext_pushed`, :data:`appcontext_tearing_down`, and
-:data:`appcontext_popped`.
+The following signals are sent: :data:`appcontext_pushed`,
+:data:`appcontext_tearing_down`, and :data:`appcontext_popped`.
diff --git a/docs/blueprints.rst b/docs/blueprints.rst
index af368ba..d5cf3d8 100644
--- a/docs/blueprints.rst
+++ b/docs/blueprints.rst
@@ -140,6 +140,19 @@ name, and child URLs will be prefixed with the parent's URL prefix.
     url_for('parent.child.create')
     /parent/child/create
 
+In addition a child blueprint's will gain their parent's subdomain,
+with their subdomain as prefix if present i.e.
+
+.. code-block:: python
+
+    parent = Blueprint('parent', __name__, subdomain='parent')
+    child = Blueprint('child', __name__, subdomain='child')
+    parent.register_blueprint(child)
+    app.register_blueprint(parent)
+
+    url_for('parent.child.create', _external=True)
+    "child.parent.domain.tld"
+
 Blueprint-specific before request functions, etc. registered with the
 parent will trigger for the child. If a child does not have an error
 handler that can handle a given exception, the parent's will be tried.
diff --git a/docs/cli.rst b/docs/cli.rst
index 22484f1..a72e6d5 100644
--- a/docs/cli.rst
+++ b/docs/cli.rst
@@ -95,7 +95,7 @@ the ``--debug`` option.
 
 .. code-block:: console
 
-     $ flask --app hello --debug run
+     $ flask --app hello run --debug
       * Serving Flask app "hello"
       * Debug mode: on
       * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
@@ -103,6 +103,14 @@ the ``--debug`` option.
       * Debugger is active!
       * Debugger PIN: 223-456-919
 
+The ``--debug`` option can also be passed to the top level ``flask`` command to enable
+debug mode for any command. The following two ``run`` calls are equivalent.
+
+.. code-block:: console
+
+    $ flask --app hello --debug run
+    $ flask --app hello run --debug
+
 
 Watch and Ignore Files with the Reloader
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -280,25 +288,25 @@ script. Activating the virtualenv will set the variables.
 
    .. group-tab:: Bash
 
-      Unix Bash, :file:`venv/bin/activate`::
+      Unix Bash, :file:`.venv/bin/activate`::
 
           $ export FLASK_APP=hello
 
    .. group-tab:: Fish
 
-      Fish, :file:`venv/bin/activate.fish`::
+      Fish, :file:`.venv/bin/activate.fish`::
 
           $ set -x FLASK_APP hello
 
    .. group-tab:: CMD
 
-      Windows CMD, :file:`venv\\Scripts\\activate.bat`::
+      Windows CMD, :file:`.venv\\Scripts\\activate.bat`::
 
           > set FLASK_APP=hello
 
    .. group-tab:: Powershell
 
-      Windows Powershell, :file:`venv\\Scripts\\activate.ps1`::
+      Windows Powershell, :file:`.venv\\Scripts\\activate.ps1`::
 
           > $env:FLASK_APP = "hello"
 
@@ -438,24 +446,16 @@ Plugins
 
 Flask will automatically load commands specified in the ``flask.commands``
 `entry point`_. This is useful for extensions that want to add commands when
-they are installed. Entry points are specified in :file:`setup.py` ::
-
-    from setuptools import setup
+they are installed. Entry points are specified in :file:`pyproject.toml`:
 
-    setup(
-        name='flask-my-extension',
-        ...,
-        entry_points={
-            'flask.commands': [
-                'my-command=flask_my_extension.commands:cli'
-            ],
-        },
-    )
+.. code-block:: toml
 
+    [project.entry-points."flask.commands"]
+    my-command = "my_extension.commands:cli"
 
 .. _entry point: https://packaging.python.org/tutorials/packaging-projects/#entry-points
 
-Inside :file:`flask_my_extension/commands.py` you can then export a Click
+Inside :file:`my_extension/commands.py` you can then export a Click
 object::
 
     import click
@@ -493,19 +493,12 @@ Create an instance of :class:`~cli.FlaskGroup` and pass it the factory::
     def cli():
         """Management script for the Wiki application."""
 
-Define the entry point in :file:`setup.py`::
+Define the entry point in :file:`pyproject.toml`:
 
-    from setuptools import setup
+.. code-block:: toml
 
-    setup(
-        name='flask-my-extension',
-        ...,
-        entry_points={
-            'console_scripts': [
-                'wiki=wiki:cli'
-            ],
-        },
-    )
+    [project.scripts]
+    wiki = "wiki:cli"
 
 Install the application in the virtualenv in editable mode and the custom
 script is available. Note that you don't need to set ``--app``. ::
@@ -550,7 +543,7 @@ a name such as "flask run".
 Click the *Script path* dropdown and change it to *Module name*, then input ``flask``.
 
 The *Parameters* field is set to the CLI command to execute along with any arguments.
-This example uses ``--app hello --debug run``, which will run the development server in
+This example uses ``--app hello run --debug``, which will run the development server in
 debug mode. ``--app hello`` should be the import or file with your Flask app.
 
 If you installed your project as a package in your virtualenv, you may uncheck the
diff --git a/docs/conf.py b/docs/conf.py
index f0be072..771a722 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -43,8 +43,6 @@ html_context = {
         ProjectLink("PyPI Releases", "https://pypi.org/project/Flask/"),
         ProjectLink("Source Code", "https://github.com/pallets/flask/"),
         ProjectLink("Issue Tracker", "https://github.com/pallets/flask/issues/"),
-        ProjectLink("Website", "https://palletsprojects.com/p/flask/"),
-        ProjectLink("Twitter", "https://twitter.com/PalletsTeam"),
         ProjectLink("Chat", "https://discord.gg/pallets"),
     ]
 }
@@ -54,8 +52,8 @@ html_sidebars = {
 }
 singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
 html_static_path = ["_static"]
-html_favicon = "_static/flask-icon.png"
-html_logo = "_static/flask-icon.png"
+html_favicon = "_static/shortcut-icon.png"
+html_logo = "_static/flask-vertical.png"
 html_title = f"Flask Documentation ({version})"
 html_show_sourcelink = False
 
diff --git a/docs/config.rst b/docs/config.rst
index bdcbdcd..3c06b29 100644
--- a/docs/config.rst
+++ b/docs/config.rst
@@ -47,17 +47,17 @@ Debug Mode
 
 The :data:`DEBUG` config value is special because it may behave inconsistently if
 changed after the app has begun setting up. In order to set debug mode reliably, use the
-``--debug`` option on the ``flask`` command.``flask run`` will use the interactive
-debugger and reloader by default in debug mode.
+``--debug`` option on the ``flask`` or ``flask run`` command. ``flask run`` will use the
+interactive debugger and reloader by default in debug mode.
 
 .. code-block:: text
 
-    $ flask --app hello --debug run
+    $ flask --app hello run --debug
 
 Using the option is recommended. While it is possible to set :data:`DEBUG` in your
-config or code, this is strongly discouraged. It can't be read early by the ``flask``
-command, and some systems or extensions may have already configured themselves based on
-a previous value.
+config or code, this is strongly discouraged. It can't be read early by the
+``flask run`` command, and some systems or extensions may have already configured
+themselves based on a previous value.
 
 
 Builtin Configuration Values
@@ -65,18 +65,6 @@ Builtin Configuration Values
 
 The following configuration values are used internally by Flask:
 
-.. py:data:: ENV
-
-    What environment the app is running in. The :attr:`~flask.Flask.env` attribute maps
-    to this config key.
-
-    Default: ``'production'``
-
-    .. deprecated:: 2.2
-        Will be removed in Flask 2.3. Use ``--debug`` instead.
-
-    .. versionadded:: 1.0
-
 .. py:data:: DEBUG
 
     Whether debug mode is enabled. When using ``flask run`` to start the development
@@ -146,12 +134,17 @@ The following configuration values are used internally by Flask:
 
 .. py:data:: SESSION_COOKIE_DOMAIN
 
-    The domain match rule that the session cookie will be valid for. If not
-    set, the cookie will be valid for all subdomains of :data:`SERVER_NAME`.
-    If ``False``, the cookie's domain will not be set.
+    The value of the ``Domain`` parameter on the session cookie. If not set, browsers
+    will only send the cookie to the exact domain it was set from. Otherwise, they
+    will send it to any subdomain of the given value as well.
+
+    Not setting this value is more restricted and secure than setting it.
 
     Default: ``None``
 
+    .. versionchanged:: 2.3
+        Not set by default, does not fall back to ``SERVER_NAME``.
+
 .. py:data:: SESSION_COOKIE_PATH
 
     The path that the session cookie will be valid for. If not set, the cookie
@@ -231,19 +224,14 @@ The following configuration values are used internally by Flask:
     Inform the application what host and port it is bound to. Required
     for subdomain route matching support.
 
-    If set, will be used for the session cookie domain if
-    :data:`SESSION_COOKIE_DOMAIN` is not set. Modern web browsers will
-    not allow setting cookies for domains without a dot. To use a domain
-    locally, add any names that should route to the app to your
-    ``hosts`` file. ::
-
-        127.0.0.1 localhost.dev
-
     If set, ``url_for`` can generate external URLs with only an application
     context instead of a request context.
 
     Default: ``None``
 
+    .. versionchanged:: 2.3
+        Does not affect ``SESSION_COOKIE_DOMAIN``.
+
 .. py:data:: APPLICATION_ROOT
 
     Inform the application what path it is mounted under by the application /
@@ -271,52 +259,6 @@ The following configuration values are used internally by Flask:
 
     Default: ``None``
 
-.. py:data:: JSON_AS_ASCII
-
-    Serialize objects to ASCII-encoded JSON. If this is disabled, the
-    JSON returned from ``jsonify`` will contain Unicode characters. This
-    has security implications when rendering the JSON into JavaScript in
-    templates, and should typically remain enabled.
-
-    Default: ``True``
-
-    .. deprecated:: 2.2
-        Will be removed in Flask 2.3. Set ``app.json.ensure_ascii``
-        instead.
-
-.. py:data:: JSON_SORT_KEYS
-
-    Sort the keys of JSON objects alphabetically. This is useful for caching
-    because it ensures the data is serialized the same way no matter what
-    Python's hash seed is. While not recommended, you can disable this for a
-    possible performance improvement at the cost of caching.
-
-    Default: ``True``
-
-    .. deprecated:: 2.2
-        Will be removed in Flask 2.3. Set ``app.json.sort_keys``
-        instead.
-
-.. py:data:: JSONIFY_PRETTYPRINT_REGULAR
-
-    :func:`~flask.jsonify` responses will be output with newlines,
-    spaces, and indentation for easier reading by humans. Always enabled
-    in debug mode.
-
-    Default: ``False``
-
-    .. deprecated:: 2.2
-        Will be removed in Flask 2.3. Set ``app.json.compact`` instead.
-
-.. py:data:: JSONIFY_MIMETYPE
-
-    The mimetype of ``jsonify`` responses.
-
-    Default: ``'application/json'``
-
-    .. deprecated:: 2.2
-        Will be removed in Flask 2.3. Set ``app.json.mimetype`` instead.
-
 .. py:data:: TEMPLATES_AUTO_RELOAD
 
     Reload templates when they are changed. If not set, it will be enabled in
@@ -381,14 +323,13 @@ The following configuration values are used internally by Flask:
 .. versionchanged:: 2.2
     Removed ``PRESERVE_CONTEXT_ON_EXCEPTION``.
 
-.. versionchanged:: 2.2
-    ``JSON_AS_ASCII``, ``JSON_SORT_KEYS``,
-    ``JSONIFY_MIMETYPE``, and ``JSONIFY_PRETTYPRINT_REGULAR`` will be
-    removed in Flask 2.3. The default ``app.json`` provider has
+.. versionchanged:: 2.3
+    ``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_MIMETYPE``, and
+    ``JSONIFY_PRETTYPRINT_REGULAR`` were removed. The default ``app.json`` provider has
     equivalent attributes instead.
 
-.. versionchanged:: 2.2
-    ``ENV`` will be removed in Flask 2.3. Use ``--debug`` instead.
+.. versionchanged:: 2.3
+    ``ENV`` was removed.
 
 
 Configuring from Python Files
diff --git a/docs/debugging.rst b/docs/debugging.rst
index fb3604b..f6b56ca 100644
--- a/docs/debugging.rst
+++ b/docs/debugging.rst
@@ -43,7 +43,7 @@ The debugger is enabled by default when the development server is run in debug m
 
 .. code-block:: text
 
-    $ flask --app hello --debug run
+    $ flask --app hello run --debug
 
 When running from Python code, passing ``debug=True`` enables debug mode, which is
 mostly equivalent.
@@ -66,13 +66,13 @@ be used to step through code during a request before an error is raised,
 or if no error is raised. Some even have a remote mode so you can debug
 code running on another machine.
 
-When using an external debugger, the app should still be in debug mode,
-but it can be useful to disable the built-in debugger and reloader,
-which can interfere.
+When using an external debugger, the app should still be in debug mode, otherwise Flask
+turns unhandled errors into generic 500 error pages. However, the built-in debugger and
+reloader should be disabled so they don't interfere with the external debugger.
 
 .. code-block:: text
 
-    $ flask --app hello --debug run --no-debugger --no-reload
+    $ flask --app hello run --debug --no-debugger --no-reload
 
 When running from Python:
 
@@ -80,8 +80,20 @@ When running from Python:
 
     app.run(debug=True, use_debugger=False, use_reloader=False)
 
-Disabling these isn't required, an external debugger will continue to
-work with the following caveats. If the built-in debugger is not
-disabled, it will catch unhandled exceptions before the external
-debugger can. If the reloader is not disabled, it could cause an
-unexpected reload if code changes during debugging.
+Disabling these isn't required, an external debugger will continue to work with the
+following caveats.
+
+-   If the built-in debugger is not disabled, it will catch unhandled exceptions before
+    the external debugger can.
+-   If the reloader is not disabled, it could cause an unexpected reload if code changes
+    during a breakpoint.
+-   The development server will still catch unhandled exceptions if the built-in
+    debugger is disabled, otherwise it would crash on any error. If you want that (and
+    usually you don't) pass ``passthrough_errors=True`` to ``app.run``.
+
+    .. code-block:: python
+
+        app.run(
+            debug=True, passthrough_errors=True,
+            use_debugger=False, use_reloader=False
+        )
diff --git a/docs/deploying/eventlet.rst b/docs/deploying/eventlet.rst
index 243be5e..8a718b2 100644
--- a/docs/deploying/eventlet.rst
+++ b/docs/deploying/eventlet.rst
@@ -34,8 +34,8 @@ Create a virtualenv, install your application, then install
 .. code-block:: text
 
     $ cd hello-app
-    $ python -m venv venv
-    $ . venv/bin/activate
+    $ python -m venv .venv
+    $ . .venv/bin/activate
     $ pip install .  # install your application
     $ pip install eventlet
 
@@ -54,7 +54,7 @@ its ``wsgi.server``, as well as your app or app factory.
     from hello import create_app
 
     app = create_app()
-    wsgi.server(eventlet.listen(("127.0.0.1", 8000), app)
+    wsgi.server(eventlet.listen(("127.0.0.1", 8000)), app)
 
 .. code-block:: text
 
diff --git a/docs/deploying/gevent.rst b/docs/deploying/gevent.rst
index aae63e8..448b93e 100644
--- a/docs/deploying/gevent.rst
+++ b/docs/deploying/gevent.rst
@@ -33,8 +33,8 @@ Create a virtualenv, install your application, then install ``gevent``.
 .. code-block:: text
 
     $ cd hello-app
-    $ python -m venv venv
-    $ . venv/bin/activate
+    $ python -m venv .venv
+    $ . .venv/bin/activate
     $ pip install .  # install your application
     $ pip install gevent
 
diff --git a/docs/deploying/gunicorn.rst b/docs/deploying/gunicorn.rst
index 93d11d3..c50edc2 100644
--- a/docs/deploying/gunicorn.rst
+++ b/docs/deploying/gunicorn.rst
@@ -30,8 +30,8 @@ Create a virtualenv, install your application, then install
 .. code-block:: text
 
     $ cd hello-app
-    $ python -m venv venv
-    $ . venv/bin/activate
+    $ python -m venv .venv
+    $ . .venv/bin/activate
     $ pip install .  # install your application
     $ pip install gunicorn
 
diff --git a/docs/deploying/index.rst b/docs/deploying/index.rst
index 2e48682..4135596 100644
--- a/docs/deploying/index.rst
+++ b/docs/deploying/index.rst
@@ -66,7 +66,6 @@ interface. The links below are for some of the most common platforms,
 which have instructions for Flask, WSGI, or Python.
 
 - `PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
-- `Heroku <https://devcenter.heroku.com/articles/getting-started-with-python>`_
 - `Google App Engine <https://cloud.google.com/appengine/docs/standard/python3/building-app>`_
 - `Google Cloud Run <https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-python-service>`_
 - `AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst
index eae973d..23e8227 100644
--- a/docs/deploying/mod_wsgi.rst
+++ b/docs/deploying/mod_wsgi.rst
@@ -33,8 +33,8 @@ Create a virtualenv, install your application, then install
 .. code-block:: text
 
     $ cd hello-app
-    $ python -m venv venv
-    $ . venv/bin/activate
+    $ python -m venv .venv
+    $ . .venv/bin/activate
     $ pip install .  # install your application
     $ pip install mod_wsgi
 
@@ -89,6 +89,6 @@ mod_wsgi to drop to that user after starting.
 
 .. code-block:: text
 
-    $ sudo /home/hello/venv/bin/mod_wsgi-express start-server \
+    $ sudo /home/hello/.venv/bin/mod_wsgi-express start-server \
         /home/hello/wsgi.py \
         --user hello --group hello --port 80 --processes 4
diff --git a/docs/deploying/uwsgi.rst b/docs/deploying/uwsgi.rst
index 2da5efe..1f9d5ec 100644
--- a/docs/deploying/uwsgi.rst
+++ b/docs/deploying/uwsgi.rst
@@ -29,8 +29,8 @@ Create a virtualenv, install your application, then install ``pyuwsgi``.
 .. code-block:: text
 
     $ cd hello-app
-    $ python -m venv venv
-    $ . venv/bin/activate
+    $ python -m venv .venv
+    $ . .venv/bin/activate
     $ pip install .  # install your application
     $ pip install pyuwsgi
 
diff --git a/docs/deploying/waitress.rst b/docs/deploying/waitress.rst
index 9b2fe13..aeafb9f 100644
--- a/docs/deploying/waitress.rst
+++ b/docs/deploying/waitress.rst
@@ -27,8 +27,8 @@ Create a virtualenv, install your application, then install
 .. code-block:: text
 
     $ cd hello-app
-    $ python -m venv venv
-    $ . venv/bin/activate
+    $ python -m venv .venv
+    $ . .venv/bin/activate
     $ pip install .  # install your application
     $ pip install waitress
 
@@ -45,10 +45,10 @@ pattern, use ``--call {module}:{factory}`` instead.
 .. code-block:: text
 
     # equivalent to 'from hello import app'
-    $ waitress-serve hello:app --host 127.0.0.1
+    $ waitress-serve --host 127.0.0.1 hello:app
 
     # equivalent to 'from hello import create_app; create_app()'
-    $ waitress-serve --call hello:create_app --host 127.0.0.1
+    $ waitress-serve --host 127.0.0.1 --call hello:create_app
 
     Serving on http://127.0.0.1:8080
 
diff --git a/docs/errorhandling.rst b/docs/errorhandling.rst
index 808a61a..c281055 100644
--- a/docs/errorhandling.rst
+++ b/docs/errorhandling.rst
@@ -69,7 +69,6 @@ See also:
 -   Sentry also supports catching errors from a worker queue
     (RQ, Celery, etc.) in a similar fashion. See the `Python SDK docs
     <https://docs.sentry.io/platforms/python/>`__ for more information.
--   `Getting started with Sentry <https://docs.sentry.io/quickstart/?platform=python>`__
 -   `Flask-specific documentation <https://docs.sentry.io/platforms/python/guides/flask/>`__
 
 
diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst
index 9574511..c9dee5f 100644
--- a/docs/extensiondev.rst
+++ b/docs/extensiondev.rst
@@ -184,7 +184,7 @@ context is active when a request context is, or when a CLI command is
 run. If you're storing something that should be closed, use
 :meth:`~flask.Flask.teardown_appcontext` to ensure that it gets closed
 when the application context ends. If it should only be valid during a
-request, or would not be used in the CLI outside a reqeust, use
+request, or would not be used in the CLI outside a request, use
 :meth:`~flask.Flask.teardown_request`.
 
 
@@ -212,7 +212,7 @@ class's :meth:`~views.View.as_view` method.
         def __init__(self, model):
             self.model = model
 
-        def get(id):
+        def get(self, id):
             post = self.model.query.get(id)
             return jsonify(post.to_json())
 
@@ -293,9 +293,8 @@ ecosystem remain consistent and compatible.
     any particular version scheme, but should use lower bounds to
     indicate minimum compatibility support. For example,
     ``sqlalchemy>=1.4``.
-9.  Indicate the versions of Python supported using
-    ``python_requires=">=version"``. Flask itself supports Python >=3.7
-    as of December 2021, but this will update over time.
+9.  Indicate the versions of Python supported using ``python_requires=">=version"``.
+    Flask itself supports Python >=3.8 as of April 2023, but this will update over time.
 
 .. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask
 .. _Discord Chat: https://discord.gg/pallets
diff --git a/docs/index.rst b/docs/index.rst
index 983f612..c447bb0 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -3,10 +3,8 @@
 Welcome to Flask
 ================
 
-.. image:: _static/flask-logo.png
-    :alt: Flask: web development, one drop at a time
+.. image:: _static/flask-horizontal.png
     :align: center
-    :target: https://palletsprojects.com/p/flask/
 
 Welcome to Flask's documentation. Get started with :doc:`installation`
 and then get an overview with the :doc:`quickstart`. There is also a
@@ -16,14 +14,13 @@ complete application with Flask. Common patterns are described in the
 component of Flask in detail, with a full reference in the :doc:`api`
 section.
 
-Flask depends on the `Jinja`_ template engine and the `Werkzeug`_ WSGI
-toolkit. The documentation for these libraries can be found at:
+Flask depends on the `Werkzeug`_ WSGI toolkit, the `Jinja`_ template engine, and the
+`Click`_ CLI toolkit. Be sure to check their documentation as well as Flask's when
+looking for information.
 
-- `Jinja documentation <https://jinja.palletsprojects.com/>`_
-- `Werkzeug documentation <https://werkzeug.palletsprojects.com/>`_
-
-.. _Jinja: https://www.palletsprojects.com/p/jinja/
-.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/
+.. _Werkzeug: https://werkzeug.palletsprojects.com
+.. _Jinja: https://jinja.palletsprojects.com
+.. _Click: https://click.palletsprojects.com
 
 
 User's Guide
@@ -48,6 +45,7 @@ community-maintained extensions to add even more functionality.
    config
    signals
    views
+   lifecycle
    appcontext
    reqcontext
    blueprints
diff --git a/docs/installation.rst b/docs/installation.rst
index 8a338e1..aeb00ce 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -5,8 +5,7 @@ Installation
 Python Version
 --------------
 
-We recommend using the latest version of Python. Flask supports Python
-3.7 and newer.
+We recommend using the latest version of Python. Flask supports Python 3.8 and newer.
 
 
 Dependencies
@@ -24,12 +23,14 @@ These distributions will be installed automatically when installing Flask.
   to protect Flask's session cookie.
 * `Click`_ is a framework for writing command line applications. It provides
   the ``flask`` command and allows adding custom management commands.
+* `Blinker`_ provides support for :doc:`signals`.
 
 .. _Werkzeug: https://palletsprojects.com/p/werkzeug/
 .. _Jinja: https://palletsprojects.com/p/jinja/
 .. _MarkupSafe: https://palletsprojects.com/p/markupsafe/
 .. _ItsDangerous: https://palletsprojects.com/p/itsdangerous/
 .. _Click: https://palletsprojects.com/p/click/
+.. _Blinker: https://blinker.readthedocs.io/
 
 
 Optional dependencies
@@ -38,13 +39,11 @@ Optional dependencies
 These distributions will not be installed automatically. Flask will detect and
 use them if you install them.
 
-* `Blinker`_ provides support for :doc:`signals`.
 * `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask``
   commands.
 * `Watchdog`_ provides a faster, more efficient reloader for the development
   server.
 
-.. _Blinker: https://pythonhosted.org/blinker/
 .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
 .. _watchdog: https://pythonhosted.org/watchdog/
 
@@ -85,7 +84,7 @@ environments.
 Create an environment
 ~~~~~~~~~~~~~~~~~~~~~
 
-Create a project folder and a :file:`venv` folder within:
+Create a project folder and a :file:`.venv` folder within:
 
 .. tabs::
 
@@ -95,7 +94,7 @@ Create a project folder and a :file:`venv` folder within:
 
          $ mkdir myproject
          $ cd myproject
-         $ python3 -m venv venv
+         $ python3 -m venv .venv
 
    .. group-tab:: Windows
 
@@ -103,7 +102,7 @@ Create a project folder and a :file:`venv` folder within:
 
          > mkdir myproject
          > cd myproject
-         > py -3 -m venv venv
+         > py -3 -m venv .venv
 
 
 .. _install-activate-env:
@@ -119,13 +118,13 @@ Before you work on your project, activate the corresponding environment:
 
       .. code-block:: text
 
-         $ . venv/bin/activate
+         $ . .venv/bin/activate
 
    .. group-tab:: Windows
 
       .. code-block:: text
 
-         > venv\Scripts\activate
+         > .venv\Scripts\activate
 
 Your shell prompt will change to show the name of the activated
 environment.
diff --git a/docs/license.rst b/docs/license.rst
index f3f6482..a53a98c 100644
--- a/docs/license.rst
+++ b/docs/license.rst
@@ -1,19 +1,4 @@
-License
-=======
-
-BSD-3-Clause Source License
----------------------------
-
-The BSD-3-Clause license applies to all files in the Flask repository
-and source distribution. This includes Flask's source code, the
-examples, and tests, as well as the documentation.
+BSD-3-Clause License
+====================
 
 .. include:: ../LICENSE.rst
-
-
-Artwork License
----------------
-
-This license applies to Flask's logo.
-
-.. include:: ../artwork/LICENSE.rst
diff --git a/docs/lifecycle.rst b/docs/lifecycle.rst
new file mode 100644
index 0000000..2344d98
--- /dev/null
+++ b/docs/lifecycle.rst
@@ -0,0 +1,168 @@
+Application Structure and Lifecycle
+===================================
+
+Flask makes it pretty easy to write a web application. But there are quite a few
+different parts to an application and to each request it handles. Knowing what happens
+during application setup, serving, and handling requests will help you know what's
+possible in Flask and how to structure your application.
+
+
+Application Setup
+-----------------
+
+The first step in creating a Flask application is creating the application object. Each
+Flask application is an instance of the :class:`.Flask` class, which collects all
+configuration, extensions, and views.
+
+.. code-block:: python
+
+    from flask import Flask
+
+    app = Flask(__name__)
+    app.config.from_mapping(
+        SECRET_KEY="dev",
+    )
+    app.config.from_prefixed_env()
+
+    @app.route("/")
+    def index():
+        return "Hello, World!"
+
+This is known as the "application setup phase", it's the code you write that's outside
+any view functions or other handlers. It can be split up between different modules and
+sub-packages, but all code that you want to be part of your application must be imported
+in order for it to be registered.
+
+All application setup must be completed before you start serving your application and
+handling requests. This is because WSGI servers divide work between multiple workers, or
+can be distributed across multiple machines. If the configuration changed in one worker,
+there's no way for Flask to ensure consistency between other workers.
+
+Flask tries to help developers catch some of these setup ordering issues by showing an
+error if setup-related methods are called after requests are handled. In that case
+you'll see this error:
+
+    The setup method 'route' can no longer be called on the application. It has already
+    handled its first request, any changes will not be applied consistently.
+    Make sure all imports, decorators, functions, etc. needed to set up the application
+    are done before running it.
+
+However, it is not possible for Flask to detect all cases of out-of-order setup. In
+general, don't do anything to modify the ``Flask`` app object and ``Blueprint`` objects
+from within view functions that run during requests. This includes:
+
+-   Adding routes, view functions, and other request handlers with ``@app.route``,
+    ``@app.errorhandler``, ``@app.before_request``, etc.
+-   Registering blueprints.
+-   Loading configuration with ``app.config``.
+-   Setting up the Jinja template environment with ``app.jinja_env``.
+-   Setting a session interface, instead of the default itsdangerous cookie.
+-   Setting a JSON provider with ``app.json``, instead of the default provider.
+-   Creating and initializing Flask extensions.
+
+
+Serving the Application
+-----------------------
+
+Flask is a WSGI application framework. The other half of WSGI is the WSGI server. During
+development, Flask, through Werkzeug, provides a development WSGI server with the
+``flask run`` CLI command. When you are done with development, use a production server
+to serve your application, see :doc:`deploying/index`.
+
+Regardless of what server you're using, it will follow the :pep:`3333` WSGI spec. The
+WSGI server will be told how to access your Flask application object, which is the WSGI
+application. Then it will start listening for HTTP requests, translate the request data
+into a WSGI environ, and call the WSGI application with that data. The WSGI application
+will return data that is translated into an HTTP response.
+
+#.  Browser or other client makes HTTP request.
+#.  WSGI server receives request.
+#.  WSGI server converts HTTP data to WSGI ``environ`` dict.
+#.  WSGI server calls WSGI application with the ``environ``.
+#.  Flask, the WSGI application, does all its internal processing to route the request
+    to a view function, handle errors, etc.
+#.  Flask translates View function return into WSGI response data, passes it to WSGI
+    server.
+#.  WSGI server creates and send an HTTP response.
+#.  Client receives the HTTP response.
+
+
+Middleware
+~~~~~~~~~~
+
+The WSGI application above is a callable that behaves in a certain way. Middleware
+is a WSGI application that wraps another WSGI application. It's a similar concept to
+Python decorators. The outermost middleware will be called by the server. It can modify
+the data passed to it, then call the WSGI application (or further middleware) that it
+wraps, and so on. And it can take the return value of that call and modify it further.
+
+From the WSGI server's perspective, there is one WSGI application, the one it calls
+directly. Typically, Flask is the "real" application at the end of the chain of
+middleware. But even Flask can call further WSGI applications, although that's an
+advanced, uncommon use case.
+
+A common middleware you'll see used with Flask is Werkzeug's
+:class:`~werkzeug.middleware.proxy_fix.ProxyFix`, which modifies the request to look
+like it came directly from a client even if it passed through HTTP proxies on the way.
+There are other middleware that can handle serving static files, authentication, etc.
+
+
+How a Request is Handled
+------------------------
+
+For us, the interesting part of the steps above is when Flask gets called by the WSGI
+server (or middleware). At that point, it will do quite a lot to handle the request and
+generate the response. At the most basic, it will match the URL to a view function, call
+the view function, and pass the return value back to the server. But there are many more
+parts that you can use to customize its behavior.
+
+#.  WSGI server calls the Flask object, which calls :meth:`.Flask.wsgi_app`.
+#.  A :class:`.RequestContext` object is created. This converts the WSGI ``environ``
+    dict into a :class:`.Request` object. It also creates an :class:`AppContext` object.
+#.  The :doc:`app context <appcontext>` is pushed, which makes :data:`.current_app` and
+    :data:`.g` available.
+#.  The :data:`.appcontext_pushed` signal is sent.
+#.  The :doc:`request context <reqcontext>` is pushed, which makes :attr:`.request` and
+    :class:`.session` available.
+#.  The session is opened, loading any existing session data using the app's
+    :attr:`~.Flask.session_interface`, an instance of :class:`.SessionInterface`.
+#.  The URL is matched against the URL rules registered with the :meth:`~.Flask.route`
+    decorator during application setup. If there is no match, the error - usually a 404,
+    405, or redirect - is stored to be handled later.
+#.  The :data:`.request_started` signal is sent.
+#.  Any :meth:`~.Flask.url_value_preprocessor` decorated functions are called.
+#.  Any :meth:`~.Flask.before_request` decorated functions are called. If any of
+    these function returns a value it is treated as the response immediately.
+#.  If the URL didn't match a route a few steps ago, that error is raised now.
+#.  The :meth:`~.Flask.route` decorated view function associated with the matched URL
+    is called and returns a value to be used as the response.
+#.  If any step so far raised an exception, and there is an :meth:`~.Flask.errorhandler`
+    decorated function that matches the exception class or HTTP error code, it is
+    called to handle the error and return a response.
+#.  Whatever returned a response value - a before request function, the view, or an
+    error handler, that value is converted to a :class:`.Response` object.
+#.  Any :func:`~.after_this_request` decorated functions are called, then cleared.
+#.  Any :meth:`~.Flask.after_request` decorated functions are called, which can modify
+    the response object.
+#.  The session is saved, persisting any modified session data using the app's
+    :attr:`~.Flask.session_interface`.
+#.  The :data:`.request_finished` signal is sent.
+#.  If any step so far raised an exception, and it was not handled by an error handler
+    function, it is handled now. HTTP exceptions are treated as responses with their
+    corresponding status code, other exceptions are converted to a generic 500 response.
+    The :data:`.got_request_exception` signal is sent.
+#.  The response object's status, headers, and body are returned to the WSGI server.
+#.  Any :meth:`~.Flask.teardown_request` decorated functions are called.
+#.  The :data:`.request_tearing_down` signal is sent.
+#.  The request context is popped, :attr:`.request` and :class:`.session` are no longer
+    available.
+#.  Any :meth:`~.Flask.teardown_appcontext` decorated functions are called.
+#.  The :data:`.appcontext_tearing_down` signal is sent.
+#.  The app context is popped, :data:`.current_app` and :data:`.g` are no longer
+    available.
+#.  The :data:`.appcontext_popped` signal is sent.
+
+There are even more decorators and customization points than this, but that aren't part
+of every request lifecycle. They're more specific to certain things you might use during
+a request, such as templates, building URLs, or handling JSON data. See the rest of this
+documentation, as well as the :doc:`api` to explore further.
diff --git a/docs/patterns/appdispatch.rst b/docs/patterns/appdispatch.rst
index 0c5e846..efa470a 100644
--- a/docs/patterns/appdispatch.rst
+++ b/docs/patterns/appdispatch.rst
@@ -93,7 +93,7 @@ exist yet, it is dynamically created and remembered::
 
     from threading import Lock
 
-    class SubdomainDispatcher(object):
+    class SubdomainDispatcher:
 
         def __init__(self, domain, create_app):
             self.domain = domain
@@ -148,7 +148,7 @@ request path up to the first slash::
     from threading import Lock
     from werkzeug.wsgi import pop_path_info, peek_path_info
 
-    class PathDispatcher(object):
+    class PathDispatcher:
 
         def __init__(self, default_app, create_app):
             self.default_app = default_app
diff --git a/docs/patterns/appfactories.rst b/docs/patterns/appfactories.rst
index 415c10f..32fd062 100644
--- a/docs/patterns/appfactories.rst
+++ b/docs/patterns/appfactories.rst
@@ -91,7 +91,7 @@ To run such an application, you can use the :command:`flask` command:
 
 .. code-block:: text
 
-    $ flask run --app hello run
+    $ flask --app hello run
 
 Flask will automatically detect the factory if it is named
 ``create_app`` or ``make_app`` in ``hello``. You can also pass arguments
@@ -99,7 +99,7 @@ to the factory like this:
 
 .. code-block:: text
 
-    $ flask run --app hello:create_app(local_auth=True)``
+    $ flask --app hello:create_app(local_auth=True) run
 
 Then the ``create_app`` factory in ``myapp`` is called with the keyword
 argument ``local_auth=True``. See :doc:`/cli` for more detail.
diff --git a/docs/patterns/celery.rst b/docs/patterns/celery.rst
index 228a04a..2e9a43a 100644
--- a/docs/patterns/celery.rst
+++ b/docs/patterns/celery.rst
@@ -1,105 +1,242 @@
-Celery Background Tasks
-=======================
+Background Tasks with Celery
+============================
 
-If your application has a long running task, such as processing some uploaded
-data or sending email, you don't want to wait for it to finish during a
-request. Instead, use a task queue to send the necessary data to another
-process that will run the task in the background while the request returns
-immediately.
+If your application has a long running task, such as processing some uploaded data or
+sending email, you don't want to wait for it to finish during a request. Instead, use a
+task queue to send the necessary data to another process that will run the task in the
+background while the request returns immediately.
+
+`Celery`_ is a powerful task queue that can be used for simple background tasks as well
+as complex multi-stage programs and schedules. This guide will show you how to configure
+Celery using Flask. Read Celery's `First Steps with Celery`_ guide to learn how to use
+Celery itself.
+
+.. _Celery: https://celery.readthedocs.io
+.. _First Steps with Celery: https://celery.readthedocs.io/en/latest/getting-started/first-steps-with-celery.html
+
+The Flask repository contains `an example <https://github.com/pallets/flask/tree/main/examples/celery>`_
+based on the information on this page, which also shows how to use JavaScript to submit
+tasks and poll for progress and results.
 
-Celery is a powerful task queue that can be used for simple background tasks
-as well as complex multi-stage programs and schedules. This guide will show you
-how to configure Celery using Flask, but assumes you've already read the
-`First Steps with Celery <https://celery.readthedocs.io/en/latest/getting-started/first-steps-with-celery.html>`_
-guide in the Celery documentation.
 
 Install
 -------
 
-Celery is a separate Python package. Install it from PyPI using pip::
+Install Celery from PyPI, for example using pip:
+
+.. code-block:: text
 
     $ pip install celery
 
-Configure
----------
 
-The first thing you need is a Celery instance, this is called the celery
-application.  It serves the same purpose as the :class:`~flask.Flask`
-object in Flask, just for Celery.  Since this instance is used as the
-entry-point for everything you want to do in Celery, like creating tasks
-and managing workers, it must be possible for other modules to import it.
+Integrate Celery with Flask
+---------------------------
 
-For instance you can place this in a ``tasks`` module.  While you can use
-Celery without any reconfiguration with Flask, it becomes a bit nicer by
-subclassing tasks and adding support for Flask's application contexts and
-hooking it up with the Flask configuration.
+You can use Celery without any integration with Flask, but it's convenient to configure
+it through Flask's config, and to let tasks access the Flask application.
 
-This is all that is necessary to integrate Celery with Flask:
+Celery uses similar ideas to Flask, with a ``Celery`` app object that has configuration
+and registers tasks. While creating a Flask app, use the following code to create and
+configure a Celery app as well.
 
 .. code-block:: python
 
-    from celery import Celery
+    from celery import Celery, Task
 
-    def make_celery(app):
-        celery = Celery(app.import_name)
-        celery.conf.update(app.config["CELERY_CONFIG"])
-
-        class ContextTask(celery.Task):
-            def __call__(self, *args, **kwargs):
+    def celery_init_app(app: Flask) -> Celery:
+        class FlaskTask(Task):
+            def __call__(self, *args: object, **kwargs: object) -> object:
                 with app.app_context():
                     return self.run(*args, **kwargs)
 
-        celery.Task = ContextTask
-        return celery
-
-The function creates a new Celery object, configures it with the broker
-from the application config, updates the rest of the Celery config from
-the Flask config and then creates a subclass of the task that wraps the
-task execution in an application context.
+        celery_app = Celery(app.name, task_cls=FlaskTask)
+        celery_app.config_from_object(app.config["CELERY"])
+        celery_app.set_default()
+        app.extensions["celery"] = celery_app
+        return celery_app
 
-.. note::
-    Celery 5.x deprecated uppercase configuration keys, and 6.x will
-    remove them. See their official `migration guide`_.
+This creates and returns a ``Celery`` app object. Celery `configuration`_ is taken from
+the ``CELERY`` key in the Flask configuration. The Celery app is set as the default, so
+that it is seen during each request. The ``Task`` subclass automatically runs task
+functions with a Flask app context active, so that services like your database
+connections are available.
 
-.. _migration guide: https://docs.celeryproject.org/en/stable/userguide/configuration.html#conf-old-settings-map.
+.. _configuration: https://celery.readthedocs.io/en/stable/userguide/configuration.html
 
-An example task
----------------
+Here's a basic ``example.py`` that configures Celery to use Redis for communication. We
+enable a result backend, but ignore results by default. This allows us to store results
+only for tasks where we care about the result.
 
-Let's write a task that adds two numbers together and returns the result. We
-configure Celery's broker and backend to use Redis, create a ``celery``
-application using the factory from above, and then use it to define the task. ::
+.. code-block:: python
 
     from flask import Flask
 
-    flask_app = Flask(__name__)
-    flask_app.config.update(CELERY_CONFIG={
-        'broker_url': 'redis://localhost:6379',
-        'result_backend': 'redis://localhost:6379',
-    })
-    celery = make_celery(flask_app)
+    app = Flask(__name__)
+    app.config.from_mapping(
+        CELERY=dict(
+            broker_url="redis://localhost",
+            result_backend="redis://localhost",
+            task_ignore_result=True,
+        ),
+    )
+    celery_app = celery_init_app(app)
+
+Point the ``celery worker`` command at this and it will find the ``celery_app`` object.
+
+.. code-block:: text
+
+    $ celery -A example worker --loglevel INFO
+
+You can also run the ``celery beat`` command to run tasks on a schedule. See Celery's
+docs for more information about defining schedules.
+
+.. code-block:: text
+
+    $ celery -A example beat --loglevel INFO
+
+
+Application Factory
+-------------------
+
+When using the Flask application factory pattern, call the ``celery_init_app`` function
+inside the factory. It sets ``app.extensions["celery"]`` to the Celery app object, which
+can be used to get the Celery app from the Flask app returned by the factory.
+
+.. code-block:: python
+
+    def create_app() -> Flask:
+        app = Flask(__name__)
+        app.config.from_mapping(
+            CELERY=dict(
+                broker_url="redis://localhost",
+                result_backend="redis://localhost",
+                task_ignore_result=True,
+            ),
+        )
+        app.config.from_prefixed_env()
+        celery_init_app(app)
+        return app
+
+To use ``celery`` commands, Celery needs an app object, but that's no longer directly
+available. Create a ``make_celery.py`` file that calls the Flask app factory and gets
+the Celery app from the returned Flask app.
+
+.. code-block:: python
+
+    from example import create_app
+
+    flask_app = create_app()
+    celery_app = flask_app.extensions["celery"]
+
+Point the ``celery`` command to this file.
+
+.. code-block:: text
+
+    $ celery -A make_celery worker --loglevel INFO
+    $ celery -A make_celery beat --loglevel INFO
+
 
-    @celery.task()
-    def add_together(a, b):
+Defining Tasks
+--------------
+
+Using ``@celery_app.task`` to decorate task functions requires access to the
+``celery_app`` object, which won't be available when using the factory pattern. It also
+means that the decorated tasks are tied to the specific Flask and Celery app instances,
+which could be an issue during testing if you change configuration for a test.
+
+Instead, use Celery's ``@shared_task`` decorator. This creates task objects that will
+access whatever the "current app" is, which is a similar concept to Flask's blueprints
+and app context. This is why we called ``celery_app.set_default()`` above.
+
+Here's an example task that adds two numbers together and returns the result.
+
+.. code-block:: python
+
+    from celery import shared_task
+
+    @shared_task(ignore_result=False)
+    def add_together(a: int, b: int) -> int:
         return a + b
 
-This task can now be called in the background::
+Earlier, we configured Celery to ignore task results by default. Since we want to know
+the return value of this task, we set ``ignore_result=False``. On the other hand, a task
+that didn't need a result, such as sending an email, wouldn't set this.
+
+
+Calling Tasks
+-------------
+
+The decorated function becomes a task object with methods to call it in the background.
+The simplest way is to use the ``delay(*args, **kwargs)`` method. See Celery's docs for
+more methods.
+
+A Celery worker must be running to run the task. Starting a worker is shown in the
+previous sections.
+
+.. code-block:: python
+
+    from flask import request
 
-    result = add_together.delay(23, 42)
-    result.wait()  # 65
+    @app.post("/add")
+    def start_add() -> dict[str, object]:
+        a = request.form.get("a", type=int)
+        b = request.form.get("b", type=int)
+        result = add_together.delay(a, b)
+        return {"result_id": result.id}
 
-Run a worker
-------------
+The route doesn't get the task's result immediately. That would defeat the purpose by
+blocking the response. Instead, we return the running task's result id, which we can use
+later to get the result.
 
-If you jumped in and already executed the above code you will be
-disappointed to learn that ``.wait()`` will never actually return.
-That's because you also need to run a Celery worker to receive and execute the
-task. ::
 
-    $ celery -A your_application.celery worker
+Getting Results
+---------------
+
+To fetch the result of the task we started above, we'll add another route that takes the
+result id we returned before. We return whether the task is finished (ready), whether it
+finished successfully, and what the return value (or error) was if it is finished.
+
+.. code-block:: python
+
+    from celery.result import AsyncResult
+
+    @app.get("/result/<id>")
+    def task_result(id: str) -> dict[str, object]:
+        result = AsyncResult(id)
+        return {
+            "ready": result.ready(),
+            "successful": result.successful(),
+            "value": result.result if result.ready() else None,
+        }
+
+Now you can start the task using the first route, then poll for the result using the
+second route. This keeps the Flask request workers from being blocked waiting for tasks
+to finish.
+
+The Flask repository contains `an example <https://github.com/pallets/flask/tree/main/examples/celery>`_
+using JavaScript to submit tasks and poll for progress and results.
+
+
+Passing Data to Tasks
+---------------------
+
+The "add" task above took two integers as arguments. To pass arguments to tasks, Celery
+has to serialize them to a format that it can pass to other processes. Therefore,
+passing complex objects is not recommended. For example, it would be impossible to pass
+a SQLAlchemy model object, since that object is probably not serializable and is tied to
+the session that queried it.
+
+Pass the minimal amount of data necessary to fetch or recreate any complex data within
+the task. Consider a task that will run when the logged in user asks for an archive of
+their data. The Flask request knows the logged in user, and has the user object queried
+from the database. It got that by querying the database for a given id, so the task can
+do the same thing. Pass the user's id rather than the user object.
+
+.. code-block:: python
 
-The ``your_application`` string has to point to your application's package
-or module that creates the ``celery`` object.
+    @shared_task
+    def generate_user_archive(user_id: str) -> None:
+        user = db.session.get(User, user_id)
+        ...
 
-Now that the worker is running, ``wait`` will return the result once the task
-is finished.
+    generate_user_archive.delay(current_user.id)
diff --git a/docs/patterns/javascript.rst b/docs/patterns/javascript.rst
index dd3bcb9..4b1d7e0 100644
--- a/docs/patterns/javascript.rst
+++ b/docs/patterns/javascript.rst
@@ -28,7 +28,7 @@ It is important to understand the difference between templates and
 JavaScript. Templates are rendered on the server, before the response is
 sent to the user's browser. JavaScript runs in the user's browser, after
 the template is rendered and sent. Therefore, it is impossible to use
-JavaScript to affect how the Jinja template is rendered, but is is
+JavaScript to affect how the Jinja template is rendered, but it is
 possible to render data into the JavaScript that will run.
 
 To provide data to JavaScript when rendering the template, use the
diff --git a/docs/patterns/packages.rst b/docs/patterns/packages.rst
index 13f8270..239a3fa 100644
--- a/docs/patterns/packages.rst
+++ b/docs/patterns/packages.rst
@@ -42,19 +42,20 @@ You should then end up with something like that::
 But how do you run your application now?  The naive ``python
 yourapplication/__init__.py`` will not work.  Let's just say that Python
 does not want modules in packages to be the startup file.  But that is not
-a big problem, just add a new file called :file:`setup.py` next to the inner
-:file:`yourapplication` folder with the following contents::
+a big problem, just add a new file called :file:`pyproject.toml` next to the inner
+:file:`yourapplication` folder with the following contents:
 
-    from setuptools import setup
+.. code-block:: toml
 
-    setup(
-        name='yourapplication',
-        packages=['yourapplication'],
-        include_package_data=True,
-        install_requires=[
-            'flask',
-        ],
-    )
+    [project]
+    name = "yourapplication"
+    dependencies = [
+        "flask",
+    ]
+
+    [build-system]
+    requires = ["setuptools"]
+    build-backend = "setuptools.build_meta"
 
 Install your application so it is importable:
 
@@ -66,6 +67,8 @@ To use the ``flask`` command and run your application you need to set
 the ``--app`` option that tells Flask where to find the application
 instance:
 
+.. code-block:: text
+
     $ flask --app yourapplication run
 
 What did we gain from this?  Now we can restructure the application a bit
@@ -98,7 +101,7 @@ And this is what :file:`views.py` would look like::
 You should then end up with something like that::
 
     /yourapplication
-        setup.py
+        pyproject.toml
         /yourapplication
             __init__.py
             views.py
diff --git a/docs/patterns/streaming.rst b/docs/patterns/streaming.rst
index e35ac4a..c9e6ef2 100644
--- a/docs/patterns/streaming.rst
+++ b/docs/patterns/streaming.rst
@@ -20,7 +20,7 @@ data and to then invoke that function and pass it to a response object::
         def generate():
             for row in iter_all_rows():
                 yield f"{','.join(row)}\n"
-        return generate(), {"Content-Type": "text/csv")
+        return generate(), {"Content-Type": "text/csv"}
 
 Each ``yield`` expression is directly sent to the browser.  Note though
 that some WSGI middlewares might break streaming, so be careful there in
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index 22c411e..0d7ad3f 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -41,7 +41,7 @@ itself.
 
 To run the application, use the ``flask`` command or
 ``python -m flask``. You need to tell the Flask where your application
-is with the ``-app`` option.
+is with the ``--app`` option.
 
 .. code-block:: text
 
@@ -108,7 +108,7 @@ To enable debug mode, use the ``--debug`` option.
 
 .. code-block:: text
 
-    $ flask --app hello --debug run
+    $ flask --app hello run --debug
      * Serving Flask app 'hello'
      * Debug mode: on
      * Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
@@ -359,7 +359,7 @@ the application secure.  Because of that Flask configures the `Jinja2
 
 Templates can be used to generate any type of text file. For web applications, you'll
 primarily be generating HTML pages, but you can also generate markdown, plain text for
-emails, any anything else.
+emails, and anything else.
 
 For a reference to HTML, CSS, and other web APIs, use the `MDN Web Docs`_.
 
diff --git a/docs/reqcontext.rst b/docs/reqcontext.rst
index 2b10977..4f1846a 100644
--- a/docs/reqcontext.rst
+++ b/docs/reqcontext.rst
@@ -69,11 +69,12 @@ everything that runs in the block will have access to :data:`request`,
 populated with your test data. ::
 
     def generate_report(year):
-        format = request.args.get('format')
+        format = request.args.get("format")
         ...
 
     with app.test_request_context(
-            '/make_report/2017', data={'format': 'short'}):
+        "/make_report/2017", query_string={"format": "short"}
+    ):
         generate_report()
 
 If you see that error somewhere else in your code not related to
@@ -203,21 +204,16 @@ contexts until the ``with`` block exits.
 Signals
 ~~~~~~~
 
-If :data:`~signals.signals_available` is true, the following signals are
-sent:
+The following signals are sent:
 
-#.  :data:`request_started` is sent before the
-    :meth:`~Flask.before_request` functions are called.
-
-#.  :data:`request_finished` is sent after the
-    :meth:`~Flask.after_request` functions are called.
-
-#.  :data:`got_request_exception` is sent when an exception begins to
-    be handled, but before an :meth:`~Flask.errorhandler` is looked up or
-    called.
-
-#.  :data:`request_tearing_down` is sent after the
-    :meth:`~Flask.teardown_request` functions are called.
+#.  :data:`request_started` is sent before the :meth:`~Flask.before_request` functions
+    are called.
+#.  :data:`request_finished` is sent after the :meth:`~Flask.after_request` functions
+    are called.
+#.  :data:`got_request_exception` is sent when an exception begins to be handled, but
+    before an :meth:`~Flask.errorhandler` is looked up or called.
+#.  :data:`request_tearing_down` is sent after the :meth:`~Flask.teardown_request`
+    functions are called.
 
 
 .. _notes-on-proxies:
diff --git a/docs/security.rst b/docs/security.rst
index 777e511..3992e8d 100644
--- a/docs/security.rst
+++ b/docs/security.rst
@@ -23,7 +23,7 @@ in templates, but there are still other places where you have to be
 careful:
 
 -   generating HTML without the help of Jinja2
--   calling :class:`~flask.Markup` on data submitted by users
+-   calling :class:`~markupsafe.Markup` on data submitted by users
 -   sending out HTML from uploaded files, never do that, use the
     ``Content-Disposition: attachment`` header to prevent that problem.
 -   sending out textfiles from uploaded files.  Some browsers are using
diff --git a/docs/server.rst b/docs/server.rst
index a34dfab..d38aa12 100644
--- a/docs/server.rst
+++ b/docs/server.rst
@@ -24,7 +24,7 @@ debug mode.
 
 .. code-block:: text
 
-    $ flask --app hello --debug run
+    $ flask --app hello run --debug
 
 This enables debug mode, including the interactive debugger and reloader, and then
 starts the server on http://localhost:5000/. Use ``flask run --help`` to see the
diff --git a/docs/signals.rst b/docs/signals.rst
index 27630de..739bb0b 100644
--- a/docs/signals.rst
+++ b/docs/signals.rst
@@ -1,33 +1,28 @@
 Signals
 =======
 
-.. versionadded:: 0.6
-
-Starting with Flask 0.6, there is integrated support for signalling in
-Flask.  This support is provided by the excellent `blinker`_ library and
-will gracefully fall back if it is not available.
-
-What are signals?  Signals help you decouple applications by sending
-notifications when actions occur elsewhere in the core framework or
-another Flask extensions.  In short, signals allow certain senders to
-notify subscribers that something happened.
-
-Flask comes with a couple of signals and other extensions might provide
-more.  Also keep in mind that signals are intended to notify subscribers
-and should not encourage subscribers to modify data.  You will notice that
-there are signals that appear to do the same thing like some of the
-builtin decorators do (eg: :data:`~flask.request_started` is very similar
-to :meth:`~flask.Flask.before_request`).  However, there are differences in
-how they work.  The core :meth:`~flask.Flask.before_request` handler, for
-example, is executed in a specific order and is able to abort the request
-early by returning a response.  In contrast all signal handlers are
-executed in undefined order and do not modify any data.
-
-The big advantage of signals over handlers is that you can safely
-subscribe to them for just a split second.  These temporary
-subscriptions are helpful for unit testing for example.  Say you want to
-know what templates were rendered as part of a request: signals allow you
-to do exactly that.
+Signals are a lightweight way to notify subscribers of certain events during the
+lifecycle of the application and each request. When an event occurs, it emits the
+signal, which calls each subscriber.
+
+Signals are implemented by the `Blinker`_ library. See its documentation for detailed
+information. Flask provides some built-in signals. Extensions may provide their own.
+
+Many signals mirror Flask's decorator-based callbacks with similar names. For example,
+the :data:`.request_started` signal is similar to the :meth:`~.Flask.before_request`
+decorator. The advantage of signals over handlers is that they can be subscribed to
+temporarily, and can't directly affect the application. This is useful for testing,
+metrics, auditing, and more. For example, if you want to know what templates were
+rendered at what parts of what requests, there is a signal that will notify you of that
+information.
+
+
+Core Signals
+------------
+
+See :ref:`core-signals-list` for a list of all built-in signals. The :doc:`lifecycle`
+page also describes the order that signals and decorators execute.
+
 
 Subscribing to Signals
 ----------------------
@@ -99,17 +94,12 @@ The example above would then look like this::
         ...
         template, context = templates[0]
 
-.. admonition:: Blinker API Changes
-
-   The :meth:`~blinker.base.Signal.connected_to` method arrived in Blinker
-   with version 1.1.
-
 Creating Signals
 ----------------
 
 If you want to use signals in your own application, you can use the
 blinker library directly.  The most common use case are named signals in a
-custom :class:`~blinker.base.Namespace`..  This is what is recommended
+custom :class:`~blinker.base.Namespace`.  This is what is recommended
 most of the time::
 
     from blinker import Namespace
@@ -123,12 +113,6 @@ The name for the signal here makes it unique and also simplifies
 debugging.  You can access the name of the signal with the
 :attr:`~blinker.base.NamedSignal.name` attribute.
 
-.. admonition:: For Extension Developers
-
-   If you are writing a Flask extension and you want to gracefully degrade for
-   missing blinker installations, you can do so by using the
-   :class:`flask.signals.Namespace` class.
-
 .. _signals-sending:
 
 Sending Signals
@@ -170,7 +154,7 @@ in :ref:`signals-sending` and the :data:`~flask.request_tearing_down` signal.
 Decorator Based Signal Subscriptions
 ------------------------------------
 
-With Blinker 1.1 you can also easily subscribe to signals by using the new
+You can also easily subscribe to signals by using the
 :meth:`~blinker.base.NamedSignal.connect_via` decorator::
 
     from flask import template_rendered
@@ -179,10 +163,5 @@ With Blinker 1.1 you can also easily subscribe to signals by using the new
     def when_template_rendered(sender, template, context, **extra):
         print(f'Template {template.name} is rendered with {context}')
 
-Core Signals
-------------
-
-Take a look at :ref:`core-signals-list` for a list of all builtin signals.
-
 
 .. _blinker: https://pypi.org/project/blinker/
diff --git a/docs/templating.rst b/docs/templating.rst
index 3cda995..23cfee4 100644
--- a/docs/templating.rst
+++ b/docs/templating.rst
@@ -18,7 +18,7 @@ Jinja Setup
 Unless customized, Jinja2 is configured by Flask as follows:
 
 -   autoescaping is enabled for all templates ending in ``.html``,
-    ``.htm``, ``.xml`` as well as ``.xhtml`` when using
+    ``.htm``, ``.xml``, ``.xhtml``, as well as ``.svg`` when using
     :func:`~flask.templating.render_template`.
 -   autoescaping is enabled for all strings when using
     :func:`~flask.templating.render_template_string`.
@@ -115,7 +115,7 @@ markdown to HTML converter.
 
 There are three ways to accomplish that:
 
--   In the Python code, wrap the HTML string in a :class:`~flask.Markup`
+-   In the Python code, wrap the HTML string in a :class:`~markupsafe.Markup`
     object before passing it to the template.  This is in general the
     recommended way.
 -   Inside the template, use the ``|safe`` filter to explicitly mark a
diff --git a/docs/tutorial/deploy.rst b/docs/tutorial/deploy.rst
index 436ed5e..eb3a53a 100644
--- a/docs/tutorial/deploy.rst
+++ b/docs/tutorial/deploy.rst
@@ -14,22 +14,13 @@ application.
 Build and Install
 -----------------
 
-When you want to deploy your application elsewhere, you build a
-distribution file. The current standard for Python distribution is the
-*wheel* format, with the ``.whl`` extension. Make sure the wheel library
-is installed first:
+When you want to deploy your application elsewhere, you build a *wheel*
+(``.whl``) file. Install and use the ``build`` tool to do this.
 
 .. code-block:: none
 
-    $ pip install wheel
-
-Running ``setup.py`` with Python gives you a command line tool to issue
-build-related commands. The ``bdist_wheel`` command will build a wheel
-distribution file.
-
-.. code-block:: none
-
-    $ python setup.py bdist_wheel
+    $ pip install build
+    $ python -m build --wheel
 
 You can find the file in ``dist/flaskr-1.0.0-py3-none-any.whl``. The
 file name is in the format of {project name}-{version}-{python tag}
@@ -54,7 +45,7 @@ create the database in the instance folder.
 
 When Flask detects that it's installed (not in editable mode), it uses
 a different directory for the instance folder. You can find it at
-``venv/var/flaskr-instance`` instead.
+``.venv/var/flaskr-instance`` instead.
 
 
 Configure the Secret Key
@@ -77,7 +68,7 @@ Create the ``config.py`` file in the instance folder, which the factory
 will read from if it exists. Copy the generated value into it.
 
 .. code-block:: python
-    :caption: ``venv/var/flaskr-instance/config.py``
+    :caption: ``.venv/var/flaskr-instance/config.py``
 
     SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
 
diff --git a/docs/tutorial/factory.rst b/docs/tutorial/factory.rst
index c8e2c5f..39febd1 100644
--- a/docs/tutorial/factory.rst
+++ b/docs/tutorial/factory.rst
@@ -137,7 +137,7 @@ follow the tutorial.
 
 .. code-block:: text
 
-    $ flask --app flaskr --debug run
+    $ flask --app flaskr run --debug
 
 You'll see output similar to this:
 
diff --git a/docs/tutorial/install.rst b/docs/tutorial/install.rst
index 7380b30..9bb1234 100644
--- a/docs/tutorial/install.rst
+++ b/docs/tutorial/install.rst
@@ -1,11 +1,10 @@
 Make the Project Installable
 ============================
 
-Making your project installable means that you can build a
-*distribution* file and install that in another environment, just like
-you installed Flask in your project's environment. This makes deploying
-your project the same as installing any other library, so you're using
-all the standard Python tools to manage everything.
+Making your project installable means that you can build a *wheel* file and install that
+in another environment, just like you installed Flask in your project's environment.
+This makes deploying your project the same as installing any other library, so you're
+using all the standard Python tools to manage everything.
 
 Installing also comes with other benefits that might not be obvious from
 the tutorial or as a new Python user, including:
@@ -28,32 +27,25 @@ the tutorial or as a new Python user, including:
 Describe the Project
 --------------------
 
-The ``setup.py`` file describes your project and the files that belong
-to it.
+The ``pyproject.toml`` file describes your project and how to build it.
 
-.. code-block:: python
-    :caption: ``setup.py``
+.. code-block:: toml
+    :caption: ``pyproject.toml``
 
-    from setuptools import find_packages, setup
+    [project]
+    name = "flaskr"
+    version = "1.0.0"
+    dependencies = [
+        "flask",
+    ]
 
-    setup(
-        name='flaskr',
-        version='1.0.0',
-        packages=find_packages(),
-        include_package_data=True,
-        zip_safe=False,
-        install_requires=[
-            'flask',
-        ],
-    )
+    [build-system]
+    requires = ["setuptools"]
+    build-backend = "setuptools.build_meta"
 
 
-``packages`` tells Python what package directories (and the Python files
-they contain) to include. ``find_packages()`` finds these directories
-automatically so you don't have to type them out. To include other
-files, such as the static and templates directories,
-``include_package_data`` is set. Python needs another file named
-``MANIFEST.in`` to tell what this other data is.
+The setuptools build backend needs another file named ``MANIFEST.in`` to tell it about
+non-Python files to include.
 
 .. code-block:: none
     :caption: ``MANIFEST.in``
@@ -63,9 +55,8 @@ files, such as the static and templates directories,
     graft flaskr/templates
     global-exclude *.pyc
 
-This tells Python to copy everything in the ``static`` and ``templates``
-directories, and the ``schema.sql`` file, but to exclude all bytecode
-files.
+This tells the build to copy everything in the ``static`` and ``templates`` directories,
+and the ``schema.sql`` file, but to exclude all bytecode files.
 
 See the official `Packaging tutorial <packaging tutorial_>`_ and
 `detailed guide <packaging guide_>`_ for more explanation of the files
@@ -84,10 +75,10 @@ Use ``pip`` to install your project in the virtual environment.
 
     $ pip install -e .
 
-This tells pip to find ``setup.py`` in the current directory and install
-it in *editable* or *development* mode. Editable mode means that as you
-make changes to your local code, you'll only need to re-install if you
-change the metadata about the project, such as its dependencies.
+This tells pip to find ``pyproject.toml`` in the current directory and install the
+project in *editable* or *development* mode. Editable mode means that as you make
+changes to your local code, you'll only need to re-install if you change the metadata
+about the project, such as its dependencies.
 
 You can observe that the project is now installed with ``pip list``.
 
diff --git a/docs/tutorial/layout.rst b/docs/tutorial/layout.rst
index b6a09f0..6f8e59f 100644
--- a/docs/tutorial/layout.rst
+++ b/docs/tutorial/layout.rst
@@ -41,7 +41,7 @@ The project directory will contain:
 * ``flaskr/``, a Python package containing your application code and
   files.
 * ``tests/``, a directory containing test modules.
-* ``venv/``, a Python virtual environment where Flask and other
+* ``.venv/``, a Python virtual environment where Flask and other
   dependencies are installed.
 * Installation files telling Python how to install your project.
 * Version control config, such as `git`_. You should make a habit of
@@ -80,8 +80,8 @@ By the end, your project layout will look like this:
     │   ├── test_db.py
     │   ├── test_auth.py
     │   └── test_blog.py
-    ├── venv/
-    ├── setup.py
+    ├── .venv/
+    ├── pyproject.toml
     └── MANIFEST.in
 
 If you're using version control, the following files that are generated
@@ -92,7 +92,7 @@ write. For example, with git:
 .. code-block:: none
     :caption: ``.gitignore``
 
-    venv/
+    .venv/
 
     *.pyc
     __pycache__/
diff --git a/docs/tutorial/tests.rst b/docs/tutorial/tests.rst
index cb60790..f4744cd 100644
--- a/docs/tutorial/tests.rst
+++ b/docs/tutorial/tests.rst
@@ -490,20 +490,18 @@ no longer exist in the database.
 Running the Tests
 -----------------
 
-Some extra configuration, which is not required but makes running
-tests with coverage less verbose, can be added to the project's
-``setup.cfg`` file.
+Some extra configuration, which is not required but makes running tests with coverage
+less verbose, can be added to the project's ``pyproject.toml`` file.
 
-.. code-block:: none
-    :caption: ``setup.cfg``
+.. code-block:: toml
+    :caption: ``pyproject.toml``
 
-    [tool:pytest]
-    testpaths = tests
+    [tool.pytest.ini_options]
+    testpaths = ["tests"]
 
-    [coverage:run]
-    branch = True
-    source =
-        flaskr
+    [tool.coverage.run]
+    branch = true
+    source = ["flaskr"]
 
 To run the tests, use the ``pytest`` command. It will find and run all
 the test functions you've written.
@@ -514,7 +512,7 @@ the test functions you've written.
 
     ========================= test session starts ==========================
     platform linux -- Python 3.6.4, pytest-3.5.0, py-1.5.3, pluggy-0.6.0
-    rootdir: /home/user/Projects/flask-tutorial, inifile: setup.cfg
+    rootdir: /home/user/Projects/flask-tutorial
     collected 23 items
 
     tests/test_auth.py ........                                      [ 34%]
diff --git a/docs/views.rst b/docs/views.rst
index b7c5ba2..f221027 100644
--- a/docs/views.rst
+++ b/docs/views.rst
@@ -116,7 +116,10 @@ function.
             item = self.model.query.get_or_404(id)
             return render_template(self.template, item=item)
 
-    app.add_url_rule("/users/<int:id>", view_func=DetailView.as_view("user_detail"))
+    app.add_url_rule(
+        "/users/<int:id>",
+        view_func=DetailView.as_view("user_detail", User)
+    )
 
 
 View Lifetime and ``self``
@@ -246,14 +249,14 @@ provide get (list) and post (create) methods.
         init_every_request = False
 
         def __init__(self, model):
-            self.model
+            self.model = model
             self.validator = generate_validator(model)
 
         def _get_item(self, id):
             return self.model.query.get_or_404(id)
 
         def get(self, id):
-            user = self._get_item(id)
+            item = self._get_item(id)
             return jsonify(item.to_json())
 
         def patch(self, id):
@@ -294,9 +297,11 @@ provide get (list) and post (create) methods.
             db.session.commit()
             return jsonify(item.to_json())
 
-    def register_api(app, model, url):
-        app.add_url_rule(f"/{name}/<int:id>", view_func=ItemAPI(f"{name}-item", model))
-        app.add_url_rule(f"/{name}/", view_func=GroupAPI(f"{name}-group", model))
+    def register_api(app, model, name):
+        item = ItemAPI.as_view(f"{name}-item", model)
+        group = GroupAPI.as_view(f"{name}-group", model)
+        app.add_url_rule(f"/{name}/<int:id>", view_func=item)
+        app.add_url_rule(f"/{name}/", view_func=group)
 
     register_api(app, User, "users")
     register_api(app, Story, "stories")
diff --git a/examples/celery/README.md b/examples/celery/README.md
new file mode 100644
index 0000000..038eb51
--- /dev/null
+++ b/examples/celery/README.md
@@ -0,0 +1,27 @@
+Background Tasks with Celery
+============================
+
+This example shows how to configure Celery with Flask, how to set up an API for
+submitting tasks and polling results, and how to use that API with JavaScript. See
+[Flask's documentation about Celery](https://flask.palletsprojects.com/patterns/celery/).
+
+From this directory, create a virtualenv and install the application into it. Then run a
+Celery worker.
+
+```shell
+$ python3 -m venv .venv
+$ . ./.venv/bin/activate
+$ pip install -r requirements.txt && pip install -e .
+$ celery -A make_celery worker --loglevel INFO
+```
+
+In a separate terminal, activate the virtualenv and run the Flask development server.
+
+```shell
+$ . ./.venv/bin/activate
+$ flask -A task_app run --debug
+```
+
+Go to http://localhost:5000/ and use the forms to submit tasks. You can see the polling
+requests in the browser dev tools and the Flask logs. You can see the tasks submitting
+and completing in the Celery logs.
diff --git a/examples/celery/make_celery.py b/examples/celery/make_celery.py
new file mode 100644
index 0000000..f7d138e
--- /dev/null
+++ b/examples/celery/make_celery.py
@@ -0,0 +1,4 @@
+from task_app import create_app
+
+flask_app = create_app()
+celery_app = flask_app.extensions["celery"]
diff --git a/examples/celery/pyproject.toml b/examples/celery/pyproject.toml
new file mode 100644
index 0000000..e480aeb
--- /dev/null
+++ b/examples/celery/pyproject.toml
@@ -0,0 +1,11 @@
+[project]
+name = "flask-example-celery"
+version = "1.0.0"
+description = "Example Flask application with Celery background tasks."
+readme = "README.md"
+requires-python = ">=3.8"
+dependencies = ["flask>=2.2.2", "celery[redis]>=5.2.7"]
+
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
diff --git a/examples/celery/requirements.txt b/examples/celery/requirements.txt
new file mode 100644
index 0000000..ce9ae72
--- /dev/null
+++ b/examples/celery/requirements.txt
@@ -0,0 +1,56 @@
+#
+# This file is autogenerated by pip-compile with Python 3.10
+# by the following command:
+#
+#    pip-compile --resolver=backtracking pyproject.toml
+#
+amqp==5.1.1
+    # via kombu
+async-timeout==4.0.2
+    # via redis
+billiard==3.6.4.0
+    # via celery
+celery[redis]==5.2.7
+    # via flask-example-celery (pyproject.toml)
+click==8.1.3
+    # via
+    #   celery
+    #   click-didyoumean
+    #   click-plugins
+    #   click-repl
+    #   flask
+click-didyoumean==0.3.0
+    # via celery
+click-plugins==1.1.1
+    # via celery
+click-repl==0.2.0
+    # via celery
+flask==2.2.3
+    # via flask-example-celery (pyproject.toml)
+itsdangerous==2.1.2
+    # via flask
+jinja2==3.1.2
+    # via flask
+kombu==5.2.4
+    # via celery
+markupsafe==2.1.2
+    # via
+    #   jinja2
+    #   werkzeug
+prompt-toolkit==3.0.37
+    # via click-repl
+pytz==2022.7.1
+    # via celery
+redis==4.5.1
+    # via celery
+six==1.16.0
+    # via click-repl
+vine==5.0.0
+    # via
+    #   amqp
+    #   celery
+    #   kombu
+wcwidth==0.2.6
+    # via prompt-toolkit
+werkzeug==2.2.3
+    # via flask
diff --git a/examples/celery/src/task_app/__init__.py b/examples/celery/src/task_app/__init__.py
new file mode 100644
index 0000000..dafff8a
--- /dev/null
+++ b/examples/celery/src/task_app/__init__.py
@@ -0,0 +1,39 @@
+from celery import Celery
+from celery import Task
+from flask import Flask
+from flask import render_template
+
+
+def create_app() -> Flask:
+    app = Flask(__name__)
+    app.config.from_mapping(
+        CELERY=dict(
+            broker_url="redis://localhost",
+            result_backend="redis://localhost",
+            task_ignore_result=True,
+        ),
+    )
+    app.config.from_prefixed_env()
+    celery_init_app(app)
+
+    @app.route("/")
+    def index() -> str:
+        return render_template("index.html")
+
+    from . import views
+
+    app.register_blueprint(views.bp)
+    return app
+
+
+def celery_init_app(app: Flask) -> Celery:
+    class FlaskTask(Task):
+        def __call__(self, *args: object, **kwargs: object) -> object:
+            with app.app_context():
+                return self.run(*args, **kwargs)
+
+    celery_app = Celery(app.name, task_cls=FlaskTask)
+    celery_app.config_from_object(app.config["CELERY"])
+    celery_app.set_default()
+    app.extensions["celery"] = celery_app
+    return celery_app
diff --git a/examples/celery/src/task_app/tasks.py b/examples/celery/src/task_app/tasks.py
new file mode 100644
index 0000000..b6b3595
--- /dev/null
+++ b/examples/celery/src/task_app/tasks.py
@@ -0,0 +1,23 @@
+import time
+
+from celery import shared_task
+from celery import Task
+
+
+@shared_task(ignore_result=False)
+def add(a: int, b: int) -> int:
+    return a + b
+
+
+@shared_task()
+def block() -> None:
+    time.sleep(5)
+
+
+@shared_task(bind=True, ignore_result=False)
+def process(self: Task, total: int) -> object:
+    for i in range(total):
+        self.update_state(state="PROGRESS", meta={"current": i + 1, "total": total})
+        time.sleep(1)
+
+    return {"current": total, "total": total}
diff --git a/examples/celery/src/task_app/templates/index.html b/examples/celery/src/task_app/templates/index.html
new file mode 100644
index 0000000..4e1145c
--- /dev/null
+++ b/examples/celery/src/task_app/templates/index.html
@@ -0,0 +1,108 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset=UTF-8>
+  <title>Celery Example</title>
+</head>
+<body>
+<h2>Celery Example</h2>
+Execute background tasks with Celery. Submits tasks and shows results using JavaScript.
+
+<hr>
+<h4>Add</h4>
+<p>Start a task to add two numbers, then poll for the result.
+<form id=add method=post action="{{ url_for("tasks.add") }}">
+  <label>A <input type=number name=a value=4></label><br>
+  <label>B <input type=number name=b value=2></label><br>
+  <input type=submit>
+</form>
+<p>Result: <span id=add-result></span></p>
+
+<hr>
+<h4>Block</h4>
+<p>Start a task that takes 5 seconds. However, the response will return immediately.
+<form id=block method=post action="{{ url_for("tasks.block") }}">
+  <input type=submit>
+</form>
+<p id=block-result></p>
+
+<hr>
+<h4>Process</h4>
+<p>Start a task that counts, waiting one second each time, showing progress.
+<form id=process method=post action="{{ url_for("tasks.process") }}">
+  <label>Total <input type=number name=total value="10"></label><br>
+  <input type=submit>
+</form>
+<p id=process-result></p>
+
+<script>
+  const taskForm = (formName, doPoll, report) => {
+    document.forms[formName].addEventListener("submit", (event) => {
+      event.preventDefault()
+      fetch(event.target.action, {
+        method: "POST",
+        body: new FormData(event.target)
+      })
+        .then(response => response.json())
+        .then(data => {
+          report(null)
+
+          const poll = () => {
+            fetch(`/tasks/result/${data["result_id"]}`)
+              .then(response => response.json())
+              .then(data => {
+                report(data)
+
+                if (!data["ready"]) {
+                  setTimeout(poll, 500)
+                } else if (!data["successful"]) {
+                  console.error(formName, data)
+                }
+              })
+          }
+
+          if (doPoll) {
+            poll()
+          }
+        })
+    })
+  }
+
+  taskForm("add", true, data => {
+    const el = document.getElementById("add-result")
+
+    if (data === null) {
+      el.innerText = "submitted"
+    } else if (!data["ready"]) {
+      el.innerText = "waiting"
+    } else if (!data["successful"]) {
+      el.innerText = "error, check console"
+    } else {
+      el.innerText = data["value"]
+    }
+  })
+
+  taskForm("block", false, data => {
+    document.getElementById("block-result").innerText = (
+      "request finished, check celery log to see task finish in 5 seconds"
+    )
+  })
+
+  taskForm("process", true, data => {
+    const el = document.getElementById("process-result")
+
+    if (data === null) {
+      el.innerText = "submitted"
+    } else if (!data["ready"]) {
+      el.innerText = `${data["value"]["current"]} / ${data["value"]["total"]}`
+    } else if (!data["successful"]) {
+      el.innerText = "error, check console"
+    } else {
+      el.innerText = "✅ done"
+    }
+    console.log(data)
+  })
+
+</script>
+</body>
+</html>
diff --git a/examples/celery/src/task_app/views.py b/examples/celery/src/task_app/views.py
new file mode 100644
index 0000000..99cf92d
--- /dev/null
+++ b/examples/celery/src/task_app/views.py
@@ -0,0 +1,38 @@
+from celery.result import AsyncResult
+from flask import Blueprint
+from flask import request
+
+from . import tasks
+
+bp = Blueprint("tasks", __name__, url_prefix="/tasks")
+
+
+@bp.get("/result/<id>")
+def result(id: str) -> dict[str, object]:
+    result = AsyncResult(id)
+    ready = result.ready()
+    return {
+        "ready": ready,
+        "successful": result.successful() if ready else None,
+        "value": result.get() if ready else result.result,
+    }
+
+
+@bp.post("/add")
+def add() -> dict[str, object]:
+    a = request.form.get("a", type=int)
+    b = request.form.get("b", type=int)
+    result = tasks.add.delay(a, b)
+    return {"result_id": result.id}
+
+
+@bp.post("/block")
+def block() -> dict[str, object]:
+    result = tasks.block.delay()
+    return {"result_id": result.id}
+
+
+@bp.post("/process")
+def process() -> dict[str, object]:
+    result = tasks.process.delay(total=request.form.get("total", type=int))
+    return {"result_id": result.id}
diff --git a/examples/javascript/.gitignore b/examples/javascript/.gitignore
index 85a3584..a306afb 100644
--- a/examples/javascript/.gitignore
+++ b/examples/javascript/.gitignore
@@ -1,4 +1,4 @@
-venv/
+.venv/
 *.pyc
 __pycache__/
 instance/
diff --git a/examples/javascript/README.rst b/examples/javascript/README.rst
index 23c7ce4..697bb21 100644
--- a/examples/javascript/README.rst
+++ b/examples/javascript/README.rst
@@ -23,8 +23,8 @@ Install
 
 .. code-block:: text
 
-    $ python3 -m venv venv
-    $ . venv/bin/activate
+    $ python3 -m venv .venv
+    $ . .venv/bin/activate
     $ pip install -e .
 
 
diff --git a/examples/javascript/js_example/__init__.py b/examples/javascript/js_example/__init__.py
index 068b2d9..0ec3ca2 100644
--- a/examples/javascript/js_example/__init__.py
+++ b/examples/javascript/js_example/__init__.py
@@ -2,4 +2,4 @@ from flask import Flask
 
 app = Flask(__name__)
 
-from js_example import views  # noqa: F401
+from js_example import views  # noqa: E402, F401
diff --git a/examples/javascript/pyproject.toml b/examples/javascript/pyproject.toml
new file mode 100644
index 0000000..e74415b
--- /dev/null
+++ b/examples/javascript/pyproject.toml
@@ -0,0 +1,26 @@
+[project]
+name = "js_example"
+version = "1.1.0"
+description = "Demonstrates making AJAX requests to Flask."
+readme = "README.rst"
+license = {text = "BSD-3-Clause"}
+maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
+dependencies = ["flask"]
+
+[project.urls]
+Documentation = "https://flask.palletsprojects.com/patterns/jquery/"
+
+[project.optional-dependencies]
+test = ["pytest"]
+
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+filterwarnings = ["error"]
+
+[tool.coverage.run]
+branch = true
+source = ["js_example", "tests"]
diff --git a/examples/javascript/setup.cfg b/examples/javascript/setup.cfg
deleted file mode 100644
index f509ddf..0000000
--- a/examples/javascript/setup.cfg
+++ /dev/null
@@ -1,29 +0,0 @@
-[metadata]
-name = js_example
-version = 1.1.0
-url = https://flask.palletsprojects.com/patterns/jquery/
-license = BSD-3-Clause
-maintainer = Pallets
-maintainer_email = contact@palletsprojects.com
-description = Demonstrates making AJAX requests to Flask.
-long_description = file: README.rst
-long_description_content_type = text/x-rst
-
-[options]
-packages = find:
-include_package_data = true
-install_requires =
-    Flask
-
-[options.extras_require]
-test =
-    pytest
-    blinker
-
-[tool:pytest]
-testpaths = tests
-
-[coverage:run]
-branch = True
-source =
-    js_example
diff --git a/examples/javascript/setup.py b/examples/javascript/setup.py
deleted file mode 100644
index 6068493..0000000
--- a/examples/javascript/setup.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from setuptools import setup
-
-setup()
diff --git a/examples/tutorial/.gitignore b/examples/tutorial/.gitignore
index 85a3584..a306afb 100644
--- a/examples/tutorial/.gitignore
+++ b/examples/tutorial/.gitignore
@@ -1,4 +1,4 @@
-venv/
+.venv/
 *.pyc
 __pycache__/
 instance/
diff --git a/examples/tutorial/README.rst b/examples/tutorial/README.rst
index a7e12ca..653c216 100644
--- a/examples/tutorial/README.rst
+++ b/examples/tutorial/README.rst
@@ -23,13 +23,13 @@ default Git version is the main branch. ::
 
 Create a virtualenv and activate it::
 
-    $ python3 -m venv venv
-    $ . venv/bin/activate
+    $ python3 -m venv .venv
+    $ . .venv/bin/activate
 
 Or on Windows cmd::
 
-    $ py -3 -m venv venv
-    $ venv\Scripts\activate.bat
+    $ py -3 -m venv .venv
+    $ .venv\Scripts\activate.bat
 
 Install Flaskr::
 
@@ -48,7 +48,7 @@ Run
 .. code-block:: text
 
     $ flask --app flaskr init-db
-    $ flask --app flaskr --debug run
+    $ flask --app flaskr run --debug
 
 Open http://127.0.0.1:5000 in a browser.
 
diff --git a/examples/tutorial/pyproject.toml b/examples/tutorial/pyproject.toml
new file mode 100644
index 0000000..c86eb61
--- /dev/null
+++ b/examples/tutorial/pyproject.toml
@@ -0,0 +1,28 @@
+[project]
+name = "flaskr"
+version = "1.0.0"
+description = "The basic blog app built in the Flask tutorial."
+readme = "README.rst"
+license = {text = "BSD-3-Clause"}
+maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
+dependencies = [
+    "flask",
+]
+
+[project.urls]
+Documentation = "https://flask.palletsprojects.com/tutorial/"
+
+[project.optional-dependencies]
+test = ["pytest"]
+
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+filterwarnings = ["error"]
+
+[tool.coverage.run]
+branch = true
+source = ["flaskr", "tests"]
diff --git a/examples/tutorial/setup.cfg b/examples/tutorial/setup.cfg
deleted file mode 100644
index d001093..0000000
--- a/examples/tutorial/setup.cfg
+++ /dev/null
@@ -1,28 +0,0 @@
-[metadata]
-name = flaskr
-version = 1.0.0
-url = https://flask.palletsprojects.com/tutorial/
-license = BSD-3-Clause
-maintainer = Pallets
-maintainer_email = contact@palletsprojects.com
-description = The basic blog app built in the Flask tutorial.
-long_description = file: README.rst
-long_description_content_type = text/x-rst
-
-[options]
-packages = find:
-include_package_data = true
-install_requires =
-    Flask
-
-[options.extras_require]
-test =
-    pytest
-
-[tool:pytest]
-testpaths = tests
-
-[coverage:run]
-branch = True
-source =
-    flaskr
diff --git a/examples/tutorial/setup.py b/examples/tutorial/setup.py
deleted file mode 100644
index 6068493..0000000
--- a/examples/tutorial/setup.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from setuptools import setup
-
-setup()
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..400ed59
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,97 @@
+[project]
+name = "Flask"
+description = "A simple framework for building complex web applications."
+readme = "README.rst"
+license = {text = "BSD-3-Clause"}
+maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}]
+authors = [{name = "Armin Ronacher", email = "armin.ronacher@active-4.com"}]
+classifiers = [
+    "Development Status :: 5 - Production/Stable",
+    "Environment :: Web Environment",
+    "Framework :: Flask",
+    "Intended Audience :: Developers",
+    "License :: OSI Approved :: BSD License",
+    "Operating System :: OS Independent",
+    "Programming Language :: Python",
+    "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
+    "Topic :: Internet :: WWW/HTTP :: WSGI",
+    "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+    "Topic :: Software Development :: Libraries :: Application Frameworks",
+]
+requires-python = ">=3.8"
+dependencies = [
+    "Werkzeug>=2.3.3",
+    "Jinja2>=3.1.2",
+    "itsdangerous>=2.1.2",
+    "click>=8.1.3",
+    "blinker>=1.6.2",
+    "importlib-metadata>=3.6.0; python_version < '3.10'",
+]
+dynamic = ["version"]
+
+[project.urls]
+Donate = "https://palletsprojects.com/donate"
+Documentation = "https://flask.palletsprojects.com/"
+Changes = "https://flask.palletsprojects.com/changes/"
+"Source Code" = "https://github.com/pallets/flask/"
+"Issue Tracker" = "https://github.com/pallets/flask/issues/"
+Chat = "https://discord.gg/pallets"
+
+[project.optional-dependencies]
+async = ["asgiref>=3.2"]
+dotenv = ["python-dotenv"]
+
+[project.scripts]
+flask = "flask.cli:main"
+
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools.dynamic]
+version = {attr = "flask.__version__"}
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
+filterwarnings = [
+    "error",
+    # change in Python 3.12 alpha causes warning from inside pytest
+    "ignore:onerror argument:DeprecationWarning",
+]
+
+[tool.coverage.run]
+branch = true
+source = ["flask", "tests"]
+
+[tool.coverage.paths]
+source = ["src", "*/site-packages"]
+
+[tool.mypy]
+python_version = "3.8"
+files = ["src/flask"]
+show_error_codes = true
+pretty = true
+#strict = true
+allow_redefinition = true
+disallow_subclassing_any = true
+#disallow_untyped_calls = true
+#disallow_untyped_defs = true
+#disallow_incomplete_defs = true
+no_implicit_optional = true
+local_partial_types = true
+#no_implicit_reexport = true
+strict_equality = true
+warn_redundant_casts = true
+warn_unused_configs = true
+warn_unused_ignores = true
+#warn_return_any = true
+#warn_unreachable = true
+
+[[tool.mypy.overrides]]
+module = [
+    "asgiref.*",
+    "dotenv.*",
+    "cryptography.*",
+    "importlib_metadata",
+]
+ignore_missing_imports = true
diff --git a/requirements/build.txt b/requirements/build.txt
new file mode 100644
index 0000000..196545d
--- /dev/null
+++ b/requirements/build.txt
@@ -0,0 +1,13 @@
+# SHA1:80754af91bfb6d1073585b046fe0a474ce868509
+#
+# This file is autogenerated by pip-compile-multi
+# To update, run:
+#
+#    pip-compile-multi
+#
+build==0.10.0
+    # via -r requirements/build.in
+packaging==23.1
+    # via build
+pyproject-hooks==1.0.0
+    # via build
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 03d224c..f9732cc 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -8,53 +8,55 @@
 -r docs.txt
 -r tests.txt
 -r typing.txt
-build==0.8.0
+build==0.10.0
     # via pip-tools
+cachetools==5.3.0
+    # via tox
 cfgv==3.3.1
     # via pre-commit
+chardet==5.1.0
+    # via tox
 click==8.1.3
     # via
     #   pip-compile-multi
     #   pip-tools
-distlib==0.3.5
+colorama==0.4.6
+    # via tox
+distlib==0.3.6
     # via virtualenv
-filelock==3.7.1
+filelock==3.12.0
     # via
     #   tox
     #   virtualenv
-greenlet==1.1.2 ; python_version < "3.11"
-    # via -r requirements/tests.in
-identify==2.5.3
+identify==2.5.22
     # via pre-commit
 nodeenv==1.7.0
     # via pre-commit
-pep517==0.13.0
-    # via build
-pip-compile-multi==2.4.6
+pip-compile-multi==2.6.2
     # via -r requirements/dev.in
-pip-tools==6.8.0
+pip-tools==6.13.0
     # via pip-compile-multi
-platformdirs==2.5.2
-    # via virtualenv
-pre-commit==2.20.0
+platformdirs==3.3.0
+    # via
+    #   tox
+    #   virtualenv
+pre-commit==3.2.2
     # via -r requirements/dev.in
+pyproject-api==1.5.1
+    # via tox
+pyproject-hooks==1.0.0
+    # via build
 pyyaml==6.0
     # via pre-commit
-six==1.16.0
-    # via tox
-toml==0.10.2
-    # via
-    #   pre-commit
-    #   tox
-toposort==1.7
+toposort==1.10
     # via pip-compile-multi
-tox==3.25.1
+tox==4.5.0
     # via -r requirements/dev.in
-virtualenv==20.16.2
+virtualenv==20.22.0
     # via
     #   pre-commit
     #   tox
-wheel==0.37.1
+wheel==0.40.0
     # via pip-tools
 
 # The following packages are considered to be unsafe in a requirements file:
diff --git a/requirements/docs.txt b/requirements/docs.txt
index 2ac5f54..7ee48f6 100644
--- a/requirements/docs.txt
+++ b/requirements/docs.txt
@@ -5,41 +5,37 @@
 #
 #    pip-compile-multi
 #
-alabaster==0.7.12
+alabaster==0.7.13
     # via sphinx
-babel==2.10.3
+babel==2.12.1
     # via sphinx
-certifi==2022.6.15
+certifi==2022.12.7
     # via requests
-charset-normalizer==2.1.0
+charset-normalizer==3.1.0
     # via requests
 docutils==0.17.1
     # via
     #   sphinx
     #   sphinx-tabs
-idna==3.3
+idna==3.4
     # via requests
 imagesize==1.4.1
     # via sphinx
 jinja2==3.1.2
     # via sphinx
-markupsafe==2.1.1
+markupsafe==2.1.2
     # via jinja2
-packaging==21.3
+packaging==23.1
     # via
     #   pallets-sphinx-themes
     #   sphinx
-pallets-sphinx-themes==2.0.2
+pallets-sphinx-themes==2.1.0
     # via -r requirements/docs.in
-pygments==2.12.0
+pygments==2.15.1
     # via
     #   sphinx
     #   sphinx-tabs
-pyparsing==3.0.9
-    # via packaging
-pytz==2022.1
-    # via babel
-requests==2.28.1
+requests==2.28.2
     # via sphinx
 snowballstemmer==2.2.0
     # via sphinx
@@ -54,11 +50,11 @@ sphinx-issues==3.0.1
     # via -r requirements/docs.in
 sphinx-tabs==3.3.1
     # via -r requirements/docs.in
-sphinxcontrib-applehelp==1.0.2
+sphinxcontrib-applehelp==1.0.4
     # via sphinx
 sphinxcontrib-devhelp==1.0.2
     # via sphinx
-sphinxcontrib-htmlhelp==2.0.0
+sphinxcontrib-htmlhelp==2.0.1
     # via sphinx
 sphinxcontrib-jsmath==1.0.1
     # via sphinx
@@ -68,5 +64,5 @@ sphinxcontrib-qthelp==1.0.3
     # via sphinx
 sphinxcontrib-serializinghtml==1.1.5
     # via sphinx
-urllib3==1.26.11
+urllib3==1.26.15
     # via requests
diff --git a/requirements/tests-pallets-dev.txt b/requirements/tests-pallets-dev.txt
deleted file mode 100644
index a74f556..0000000
--- a/requirements/tests-pallets-dev.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# SHA1:692b640e7f835e536628f76de0afff1296524122
-#
-# This file is autogenerated by pip-compile-multi
-# To update, run:
-#
-#    pip-compile-multi
-#
-click @ https://github.com/pallets/click/archive/refs/heads/main.tar.gz
-    # via -r requirements/tests-pallets-dev.in
-itsdangerous @ https://github.com/pallets/itsdangerous/archive/refs/heads/main.tar.gz
-    # via -r requirements/tests-pallets-dev.in
-jinja2 @ https://github.com/pallets/jinja/archive/refs/heads/main.tar.gz
-    # via -r requirements/tests-pallets-dev.in
-markupsafe @ https://github.com/pallets/markupsafe/archive/refs/heads/main.tar.gz
-    # via
-    #   -r requirements/tests-pallets-dev.in
-    #   jinja2
-    #   werkzeug
-werkzeug @ https://github.com/pallets/werkzeug/archive/refs/heads/main.tar.gz
-    # via -r requirements/tests-pallets-dev.in
diff --git a/requirements/tests-pallets-min.txt b/requirements/tests-pallets-min.txt
index 64f0e1c..1a79a37 100644
--- a/requirements/tests-pallets-min.txt
+++ b/requirements/tests-pallets-min.txt
@@ -1,19 +1,22 @@
-# SHA1:4de7d9e6254a945fd97ec10880dd23b6cd43b70d
+# SHA1:0b58503b99aabc227b7f39357216d676d9987a12
 #
 # This file is autogenerated by pip-compile-multi
 # To update, run:
 #
 #    pip-compile-multi
 #
-click==8.0.0
+blinker==1.6.2
     # via -r requirements/tests-pallets-min.in
-itsdangerous==2.0.0
+click==8.1.3
     # via -r requirements/tests-pallets-min.in
-jinja2==3.0.0
+itsdangerous==2.1.2
     # via -r requirements/tests-pallets-min.in
-markupsafe==2.0.0
+jinja2==3.1.2
+    # via -r requirements/tests-pallets-min.in
+markupsafe==2.1.1
     # via
     #   -r requirements/tests-pallets-min.in
     #   jinja2
-werkzeug==2.0.0
+    #   werkzeug
+werkzeug==2.3.3
     # via -r requirements/tests-pallets-min.in
diff --git a/requirements/tests.txt b/requirements/tests.txt
index d9b7513..29044fb 100644
--- a/requirements/tests.txt
+++ b/requirements/tests.txt
@@ -1,31 +1,19 @@
-# SHA1:69cf1e101a60350e9933c6f1f3b129bd9ed1ea7c
+# SHA1:42d37aff22e2f1fc447e20d483e13d6d4e066b10
 #
 # This file is autogenerated by pip-compile-multi
 # To update, run:
 #
 #    pip-compile-multi
 #
-asgiref==3.5.2
+asgiref==3.6.0
     # via -r requirements/tests.in
-attrs==22.1.0
+iniconfig==2.0.0
     # via pytest
-blinker==1.5
-    # via -r requirements/tests.in
-greenlet==1.1.2 ; python_version < "3.11"
-    # via -r requirements/tests.in
-iniconfig==1.1.1
-    # via pytest
-packaging==21.3
+packaging==23.1
     # via pytest
 pluggy==1.0.0
     # via pytest
-py==1.11.0
-    # via pytest
-pyparsing==3.0.9
-    # via packaging
-pytest==7.1.2
+pytest==7.3.1
     # via -r requirements/tests.in
-python-dotenv==0.20.0
+python-dotenv==1.0.0
     # via -r requirements/tests.in
-tomli==2.0.1
-    # via pytest
diff --git a/requirements/typing.txt b/requirements/typing.txt
index a842e1c..7b40bec 100644
--- a/requirements/typing.txt
+++ b/requirements/typing.txt
@@ -7,21 +7,19 @@
 #
 cffi==1.15.1
     # via cryptography
-cryptography==37.0.4
+cryptography==40.0.2
     # via -r requirements/typing.in
-mypy==0.971
+mypy==1.2.0
     # via -r requirements/typing.in
-mypy-extensions==0.4.3
+mypy-extensions==1.0.0
     # via mypy
 pycparser==2.21
     # via cffi
-tomli==2.0.1
-    # via mypy
-types-contextvars==2.4.7
+types-contextvars==2.4.7.2
     # via -r requirements/typing.in
 types-dataclasses==0.6.6
     # via -r requirements/typing.in
-types-setuptools==63.2.3
+types-setuptools==67.7.0.0
     # via -r requirements/typing.in
-typing-extensions==4.3.0
+typing-extensions==4.5.0
     # via mypy
diff --git a/setup.cfg b/setup.cfg
index e708d44..8bfd5a1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,105 +1,3 @@
-[metadata]
-name = Flask
-version = attr: flask.__version__
-url = https://palletsprojects.com/p/flask
-project_urls = 
-	Donate = https://palletsprojects.com/donate
-	Documentation = https://flask.palletsprojects.com/
-	Changes = https://flask.palletsprojects.com/changes/
-	Source Code = https://github.com/pallets/flask/
-	Issue Tracker = https://github.com/pallets/flask/issues/
-	Twitter = https://twitter.com/PalletsTeam
-	Chat = https://discord.gg/pallets
-license = BSD-3-Clause
-author = Armin Ronacher
-author_email = armin.ronacher@active-4.com
-maintainer = Pallets
-maintainer_email = contact@palletsprojects.com
-description = A simple framework for building complex web applications.
-long_description = file: README.rst
-long_description_content_type = text/x-rst
-classifiers = 
-	Development Status :: 5 - Production/Stable
-	Environment :: Web Environment
-	Framework :: Flask
-	Intended Audience :: Developers
-	License :: OSI Approved :: BSD License
-	Operating System :: OS Independent
-	Programming Language :: Python
-	Topic :: Internet :: WWW/HTTP :: Dynamic Content
-	Topic :: Internet :: WWW/HTTP :: WSGI
-	Topic :: Internet :: WWW/HTTP :: WSGI :: Application
-	Topic :: Software Development :: Libraries :: Application Frameworks
-
-[options]
-packages = find:
-package_dir = = src
-include_package_data = True
-python_requires = >= 3.7
-
-[options.packages.find]
-where = src
-
-[options.entry_points]
-console_scripts = 
-	flask = flask.cli:main
-
-[tool:pytest]
-testpaths = tests
-filterwarnings = 
-	error
-
-[coverage:run]
-branch = True
-source = 
-	flask
-	tests
-
-[coverage:paths]
-source = 
-	src
-	*/site-packages
-
-[flake8]
-select = B, E, F, W, B9, ISC
-ignore = 
-	E203
-	E402
-	E501
-	E722
-	W503
-max-line-length = 80
-per-file-ignores = 
-	src/flask/__init__.py: F401
-
-[mypy]
-files = src/flask, tests/typing
-python_version = 3.7
-show_error_codes = True
-allow_redefinition = True
-disallow_subclassing_any = True
-no_implicit_optional = True
-local_partial_types = True
-strict_equality = True
-warn_redundant_casts = True
-warn_unused_configs = True
-warn_unused_ignores = True
-
-[mypy-asgiref.*]
-ignore_missing_imports = True
-
-[mypy-blinker.*]
-ignore_missing_imports = True
-
-[mypy-dotenv.*]
-ignore_missing_imports = True
-
-[mypy-cryptography.*]
-ignore_missing_imports = True
-
-[mypy-importlib_metadata]
-ignore_missing_imports = True
-
 [egg_info]
 tag_build = 
 tag_date = 0
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 6717546..0000000
--- a/setup.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from setuptools import setup
-
-# Metadata goes in setup.cfg. These are here for GitHub's dependency graph.
-setup(
-    name="Flask",
-    install_requires=[
-        "Werkzeug >= 2.2.2",
-        "Jinja2 >= 3.0",
-        "itsdangerous >= 2.0",
-        "click >= 8.0",
-        "importlib-metadata >= 3.6.0; python_version < '3.10'",
-    ],
-    extras_require={
-        "async": ["asgiref >= 3.2"],
-        "dotenv": ["python-dotenv"],
-    },
-)
diff --git a/src/Flask.egg-info/PKG-INFO b/src/Flask.egg-info/PKG-INFO
index 5a0f292..fc7f4fd 100644
--- a/src/Flask.egg-info/PKG-INFO
+++ b/src/Flask.egg-info/PKG-INFO
@@ -1,19 +1,15 @@
 Metadata-Version: 2.1
 Name: Flask
-Version: 2.2.2
+Version: 2.3.2
 Summary: A simple framework for building complex web applications.
-Home-page: https://palletsprojects.com/p/flask
-Author: Armin Ronacher
-Author-email: armin.ronacher@active-4.com
-Maintainer: Pallets
-Maintainer-email: contact@palletsprojects.com
+Author-email: Armin Ronacher <armin.ronacher@active-4.com>
+Maintainer-email: Pallets <contact@palletsprojects.com>
 License: BSD-3-Clause
 Project-URL: Donate, https://palletsprojects.com/donate
 Project-URL: Documentation, https://flask.palletsprojects.com/
 Project-URL: Changes, https://flask.palletsprojects.com/changes/
 Project-URL: Source Code, https://github.com/pallets/flask/
 Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/
-Project-URL: Twitter, https://twitter.com/PalletsTeam
 Project-URL: Chat, https://discord.gg/pallets
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
@@ -26,7 +22,7 @@ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
 Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
-Requires-Python: >=3.7
+Requires-Python: >=3.8
 Description-Content-Type: text/x-rst
 Provides-Extra: async
 Provides-Extra: dotenv
@@ -111,6 +107,4 @@ Links
 -   PyPI Releases: https://pypi.org/project/Flask/
 -   Source Code: https://github.com/pallets/flask/
 -   Issue Tracker: https://github.com/pallets/flask/issues/
--   Website: https://palletsprojects.com/p/flask/
--   Twitter: https://twitter.com/PalletsTeam
 -   Chat: https://discord.gg/pallets
diff --git a/src/Flask.egg-info/SOURCES.txt b/src/Flask.egg-info/SOURCES.txt
index 0c86eef..169d7d9 100644
--- a/src/Flask.egg-info/SOURCES.txt
+++ b/src/Flask.egg-info/SOURCES.txt
@@ -3,12 +3,8 @@ CONTRIBUTING.rst
 LICENSE.rst
 MANIFEST.in
 README.rst
-setup.cfg
-setup.py
+pyproject.toml
 tox.ini
-artwork/LICENSE.rst
-artwork/logo-full.svg
-artwork/logo-lineart.svg
 docs/Makefile
 docs/api.rst
 docs/appcontext.rst
@@ -27,6 +23,7 @@ docs/extensions.rst
 docs/index.rst
 docs/installation.rst
 docs/license.rst
+docs/lifecycle.rst
 docs/logging.rst
 docs/make.bat
 docs/quickstart.rst
@@ -39,11 +36,10 @@ docs/templating.rst
 docs/testing.rst
 docs/views.rst
 docs/_static/debugger.png
-docs/_static/flask-icon.png
-docs/_static/flask-logo.png
-docs/_static/no.png
+docs/_static/flask-horizontal.png
+docs/_static/flask-vertical.png
 docs/_static/pycharm-run-config.png
-docs/_static/yes.png
+docs/_static/shortcut-icon.png
 docs/deploying/apache-httpd.rst
 docs/deploying/asgi.rst
 docs/deploying/eventlet.rst
@@ -95,12 +91,19 @@ docs/tutorial/static.rst
 docs/tutorial/templates.rst
 docs/tutorial/tests.rst
 docs/tutorial/views.rst
+examples/celery/README.md
+examples/celery/make_celery.py
+examples/celery/pyproject.toml
+examples/celery/requirements.txt
+examples/celery/src/task_app/__init__.py
+examples/celery/src/task_app/tasks.py
+examples/celery/src/task_app/views.py
+examples/celery/src/task_app/templates/index.html
 examples/javascript/.gitignore
 examples/javascript/LICENSE.rst
 examples/javascript/MANIFEST.in
 examples/javascript/README.rst
-examples/javascript/setup.cfg
-examples/javascript/setup.py
+examples/javascript/pyproject.toml
 examples/javascript/js_example/__init__.py
 examples/javascript/js_example/views.py
 examples/javascript/js_example/templates/base.html
@@ -113,8 +116,7 @@ examples/tutorial/.gitignore
 examples/tutorial/LICENSE.rst
 examples/tutorial/MANIFEST.in
 examples/tutorial/README.rst
-examples/tutorial/setup.cfg
-examples/tutorial/setup.py
+examples/tutorial/pyproject.toml
 examples/tutorial/flaskr/__init__.py
 examples/tutorial/flaskr/auth.py
 examples/tutorial/flaskr/blog.py
@@ -133,9 +135,9 @@ examples/tutorial/tests/test_auth.py
 examples/tutorial/tests/test_blog.py
 examples/tutorial/tests/test_db.py
 examples/tutorial/tests/test_factory.py
+requirements/build.txt
 requirements/dev.txt
 requirements/docs.txt
-requirements/tests-pallets-dev.txt
 requirements/tests-pallets-min.txt
 requirements/tests.txt
 requirements/typing.txt
@@ -191,6 +193,7 @@ tests/test_testing.py
 tests/test_user_error_handler.py
 tests/test_views.py
 tests/static/config.json
+tests/static/config.toml
 tests/static/index.html
 tests/templates/_macro.html
 tests/templates/context_template.html
diff --git a/src/Flask.egg-info/requires.txt b/src/Flask.egg-info/requires.txt
index 7b98aea..1e94c88 100644
--- a/src/Flask.egg-info/requires.txt
+++ b/src/Flask.egg-info/requires.txt
@@ -1,7 +1,8 @@
-Werkzeug>=2.2.2
-Jinja2>=3.0
-itsdangerous>=2.0
-click>=8.0
+Werkzeug>=2.3.3
+Jinja2>=3.1.2
+itsdangerous>=2.1.2
+click>=8.1.3
+blinker>=1.6.2
 
 [:python_version < "3.10"]
 importlib-metadata>=3.6.0
diff --git a/src/flask/__init__.py b/src/flask/__init__.py
index e02531c..0bef221 100644
--- a/src/flask/__init__.py
+++ b/src/flask/__init__.py
@@ -1,6 +1,3 @@
-from markupsafe import escape
-from markupsafe import Markup
-
 from . import json as json
 from .app import Flask as Flask
 from .app import Request as Request
@@ -35,14 +32,13 @@ from .signals import message_flashed as message_flashed
 from .signals import request_finished as request_finished
 from .signals import request_started as request_started
 from .signals import request_tearing_down as request_tearing_down
-from .signals import signals_available as signals_available
 from .signals import template_rendered as template_rendered
 from .templating import render_template as render_template
 from .templating import render_template_string as render_template_string
 from .templating import stream_template as stream_template
 from .templating import stream_template_string as stream_template_string
 
-__version__ = "2.2.2"
+__version__ = "2.3.2"
 
 
 def __getattr__(name):
@@ -51,7 +47,7 @@ def __getattr__(name):
         from .globals import __app_ctx_stack
 
         warnings.warn(
-            "'_app_ctx_stack' is deprecated and will be removed in Flask 2.3.",
+            "'_app_ctx_stack' is deprecated and will be removed in Flask 2.4.",
             DeprecationWarning,
             stacklevel=2,
         )
@@ -62,10 +58,45 @@ def __getattr__(name):
         from .globals import __request_ctx_stack
 
         warnings.warn(
-            "'_request_ctx_stack' is deprecated and will be removed in Flask 2.3.",
+            "'_request_ctx_stack' is deprecated and will be removed in Flask 2.4.",
             DeprecationWarning,
             stacklevel=2,
         )
         return __request_ctx_stack
 
+    if name == "escape":
+        import warnings
+        from markupsafe import escape
+
+        warnings.warn(
+            "'flask.escape' is deprecated and will be removed in Flask 2.4. Import"
+            " 'markupsafe.escape' instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return escape
+
+    if name == "Markup":
+        import warnings
+        from markupsafe import Markup
+
+        warnings.warn(
+            "'flask.Markup' is deprecated and will be removed in Flask 2.4. Import"
+            " 'markupsafe.Markup' instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return Markup
+
+    if name == "signals_available":
+        import warnings
+
+        warnings.warn(
+            "'signals_available' is deprecated and will be removed in Flask 2.4."
+            " Signals are always available",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return True
+
     raise AttributeError(name)
diff --git a/src/flask/app.py b/src/flask/app.py
index db442c9..3b6b38d 100644
--- a/src/flask/app.py
+++ b/src/flask/app.py
@@ -1,6 +1,5 @@
-import functools
-import inspect
-import json
+from __future__ import annotations
+
 import logging
 import os
 import sys
@@ -8,9 +7,10 @@ import typing as t
 import weakref
 from collections.abc import Iterator as _abc_Iterator
 from datetime import timedelta
+from inspect import iscoroutinefunction
 from itertools import chain
-from threading import Lock
 from types import TracebackType
+from urllib.parse import quote as _url_quote
 
 import click
 from werkzeug.datastructures import Headers
@@ -27,7 +27,7 @@ from werkzeug.routing import RequestRedirect
 from werkzeug.routing import RoutingException
 from werkzeug.routing import Rule
 from werkzeug.serving import is_running_from_reloader
-from werkzeug.urls import url_quote
+from werkzeug.utils import cached_property
 from werkzeug.utils import redirect as _wz_redirect
 from werkzeug.wrappers import Response as BaseResponse
 
@@ -48,7 +48,6 @@ from .helpers import _split_blueprint_path
 from .helpers import get_debug_flag
 from .helpers import get_flashed_messages
 from .helpers import get_load_dotenv
-from .helpers import locked_cached_property
 from .json.provider import DefaultJSONProvider
 from .json.provider import JSONProvider
 from .logging import create_logger
@@ -70,14 +69,10 @@ from .wrappers import Request
 from .wrappers import Response
 
 if t.TYPE_CHECKING:  # pragma: no cover
-    import typing_extensions as te
     from .blueprints import Blueprint
     from .testing import FlaskClient
     from .testing import FlaskCliRunner
 
-T_before_first_request = t.TypeVar(
-    "T_before_first_request", bound=ft.BeforeFirstRequestCallable
-)
 T_shell_context_processor = t.TypeVar(
     "T_shell_context_processor", bound=ft.ShellContextProcessorCallable
 )
@@ -86,21 +81,8 @@ T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallab
 T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable)
 T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable)
 
-if sys.version_info >= (3, 8):
-    iscoroutinefunction = inspect.iscoroutinefunction
-else:
-
-    def iscoroutinefunction(func: t.Any) -> bool:
-        while inspect.ismethod(func):
-            func = func.__func__
-
-        while isinstance(func, functools.partial):
-            func = func.func
-
-        return inspect.iscoroutinefunction(func)
-
 
-def _make_timedelta(value: t.Union[timedelta, int, None]) -> t.Optional[timedelta]:
+def _make_timedelta(value: timedelta | int | None) -> timedelta | None:
     if value is None or isinstance(value, timedelta):
         return value
 
@@ -274,36 +256,6 @@ class Flask(Scaffold):
     #: :data:`SECRET_KEY` configuration key. Defaults to ``None``.
     secret_key = ConfigAttribute("SECRET_KEY")
 
-    @property
-    def session_cookie_name(self) -> str:
-        """The name of the cookie set by the session interface.
-
-        .. deprecated:: 2.2
-            Will be removed in Flask 2.3. Use ``app.config["SESSION_COOKIE_NAME"]``
-            instead.
-        """
-        import warnings
-
-        warnings.warn(
-            "'session_cookie_name' is deprecated and will be removed in Flask 2.3. Use"
-            " 'SESSION_COOKIE_NAME' in 'app.config' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        return self.config["SESSION_COOKIE_NAME"]
-
-    @session_cookie_name.setter
-    def session_cookie_name(self, value: str) -> None:
-        import warnings
-
-        warnings.warn(
-            "'session_cookie_name' is deprecated and will be removed in Flask 2.3. Use"
-            " 'SESSION_COOKIE_NAME' in 'app.config' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        self.config["SESSION_COOKIE_NAME"] = value
-
     #: A :class:`~datetime.timedelta` which is used to set the expiration
     #: date of a permanent session.  The default is 31 days which makes a
     #: permanent session survive for roughly one month.
@@ -315,153 +267,7 @@ class Flask(Scaffold):
         "PERMANENT_SESSION_LIFETIME", get_converter=_make_timedelta
     )
 
-    @property
-    def send_file_max_age_default(self) -> t.Optional[timedelta]:
-        """The default value for ``max_age`` for :func:`~flask.send_file`. The default
-        is ``None``, which tells the browser to use conditional requests instead of a
-        timed cache.
-
-        .. deprecated:: 2.2
-            Will be removed in Flask 2.3. Use
-            ``app.config["SEND_FILE_MAX_AGE_DEFAULT"]`` instead.
-
-        .. versionchanged:: 2.0
-            Defaults to ``None`` instead of 12 hours.
-        """
-        import warnings
-
-        warnings.warn(
-            "'send_file_max_age_default' is deprecated and will be removed in Flask"
-            " 2.3. Use 'SEND_FILE_MAX_AGE_DEFAULT' in 'app.config' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        return _make_timedelta(self.config["SEND_FILE_MAX_AGE_DEFAULT"])
-
-    @send_file_max_age_default.setter
-    def send_file_max_age_default(self, value: t.Union[int, timedelta, None]) -> None:
-        import warnings
-
-        warnings.warn(
-            "'send_file_max_age_default' is deprecated and will be removed in Flask"
-            " 2.3. Use 'SEND_FILE_MAX_AGE_DEFAULT' in 'app.config' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        self.config["SEND_FILE_MAX_AGE_DEFAULT"] = _make_timedelta(value)
-
-    @property
-    def use_x_sendfile(self) -> bool:
-        """Enable this to use the ``X-Sendfile`` feature, assuming the server supports
-        it, from :func:`~flask.send_file`.
-
-        .. deprecated:: 2.2
-            Will be removed in Flask 2.3. Use ``app.config["USE_X_SENDFILE"]`` instead.
-        """
-        import warnings
-
-        warnings.warn(
-            "'use_x_sendfile' is deprecated and will be removed in Flask 2.3. Use"
-            " 'USE_X_SENDFILE' in 'app.config' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        return self.config["USE_X_SENDFILE"]
-
-    @use_x_sendfile.setter
-    def use_x_sendfile(self, value: bool) -> None:
-        import warnings
-
-        warnings.warn(
-            "'use_x_sendfile' is deprecated and will be removed in Flask 2.3. Use"
-            " 'USE_X_SENDFILE' in 'app.config' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        self.config["USE_X_SENDFILE"] = value
-
-    _json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None
-    _json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None
-
-    @property  # type: ignore[override]
-    def json_encoder(self) -> t.Type[json.JSONEncoder]:  # type: ignore[override]
-        """The JSON encoder class to use. Defaults to
-        :class:`~flask.json.JSONEncoder`.
-
-        .. deprecated:: 2.2
-             Will be removed in Flask 2.3. Customize
-             :attr:`json_provider_class` instead.
-
-        .. versionadded:: 0.10
-        """
-        import warnings
-
-        warnings.warn(
-            "'app.json_encoder' is deprecated and will be removed in Flask 2.3."
-            " Customize 'app.json_provider_class' or 'app.json' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-
-        if self._json_encoder is None:
-            from . import json
-
-            return json.JSONEncoder
-
-        return self._json_encoder
-
-    @json_encoder.setter
-    def json_encoder(self, value: t.Type[json.JSONEncoder]) -> None:
-        import warnings
-
-        warnings.warn(
-            "'app.json_encoder' is deprecated and will be removed in Flask 2.3."
-            " Customize 'app.json_provider_class' or 'app.json' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        self._json_encoder = value
-
-    @property  # type: ignore[override]
-    def json_decoder(self) -> t.Type[json.JSONDecoder]:  # type: ignore[override]
-        """The JSON decoder class to use. Defaults to
-        :class:`~flask.json.JSONDecoder`.
-
-        .. deprecated:: 2.2
-             Will be removed in Flask 2.3. Customize
-             :attr:`json_provider_class` instead.
-
-        .. versionadded:: 0.10
-        """
-        import warnings
-
-        warnings.warn(
-            "'app.json_decoder' is deprecated and will be removed in Flask 2.3."
-            " Customize 'app.json_provider_class' or 'app.json' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-
-        if self._json_decoder is None:
-            from . import json
-
-            return json.JSONDecoder
-
-        return self._json_decoder
-
-    @json_decoder.setter
-    def json_decoder(self, value: t.Type[json.JSONDecoder]) -> None:
-        import warnings
-
-        warnings.warn(
-            "'app.json_decoder' is deprecated and will be removed in Flask 2.3."
-            " Customize 'app.json_provider_class' or 'app.json' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        self._json_decoder = value
-
-    json_provider_class: t.Type[JSONProvider] = DefaultJSONProvider
+    json_provider_class: type[JSONProvider] = DefaultJSONProvider
     """A subclass of :class:`~flask.json.provider.JSONProvider`. An
     instance is created and assigned to :attr:`app.json` when creating
     the app.
@@ -487,7 +293,6 @@ class Flask(Scaffold):
     #: Default configuration parameters.
     default_config = ImmutableDict(
         {
-            "ENV": None,
             "DEBUG": None,
             "TESTING": False,
             "PROPAGATE_EXCEPTIONS": None,
@@ -509,10 +314,6 @@ class Flask(Scaffold):
             "TRAP_HTTP_EXCEPTIONS": False,
             "EXPLAIN_TEMPLATE_LOADING": False,
             "PREFERRED_URL_SCHEME": "http",
-            "JSON_AS_ASCII": None,
-            "JSON_SORT_KEYS": None,
-            "JSONIFY_PRETTYPRINT_REGULAR": None,
-            "JSONIFY_MIMETYPE": None,
             "TEMPLATES_AUTO_RELOAD": None,
             "MAX_COOKIE_SIZE": 4093,
         }
@@ -534,7 +335,7 @@ class Flask(Scaffold):
     #: client class. Defaults to :class:`~flask.testing.FlaskClient`.
     #:
     #: .. versionadded:: 0.7
-    test_client_class: t.Optional[t.Type["FlaskClient"]] = None
+    test_client_class: type[FlaskClient] | None = None
 
     #: The :class:`~click.testing.CliRunner` subclass, by default
     #: :class:`~flask.testing.FlaskCliRunner` that is used by
@@ -542,7 +343,7 @@ class Flask(Scaffold):
     #: Flask app object as the first argument.
     #:
     #: .. versionadded:: 1.0
-    test_cli_runner_class: t.Optional[t.Type["FlaskCliRunner"]] = None
+    test_cli_runner_class: type[FlaskCliRunner] | None = None
 
     #: the session interface to use.  By default an instance of
     #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.
@@ -553,15 +354,15 @@ class Flask(Scaffold):
     def __init__(
         self,
         import_name: str,
-        static_url_path: t.Optional[str] = None,
-        static_folder: t.Optional[t.Union[str, os.PathLike]] = "static",
-        static_host: t.Optional[str] = None,
+        static_url_path: str | None = None,
+        static_folder: str | os.PathLike | None = "static",
+        static_host: str | None = None,
         host_matching: bool = False,
         subdomain_matching: bool = False,
-        template_folder: t.Optional[str] = "templates",
-        instance_path: t.Optional[str] = None,
+        template_folder: str | os.PathLike | None = "templates",
+        instance_path: str | None = None,
         instance_relative_config: bool = False,
-        root_path: t.Optional[str] = None,
+        root_path: str | None = None,
     ):
         super().__init__(
             import_name=import_name,
@@ -621,34 +422,23 @@ class Flask(Scaffold):
         #: Otherwise, its return value is returned by ``url_for``.
         #:
         #: .. versionadded:: 0.9
-        self.url_build_error_handlers: t.List[
-            t.Callable[[Exception, str, t.Dict[str, t.Any]], str]
+        self.url_build_error_handlers: list[
+            t.Callable[[Exception, str, dict[str, t.Any]], str]
         ] = []
 
-        #: A list of functions that will be called at the beginning of the
-        #: first request to this instance. To register a function, use the
-        #: :meth:`before_first_request` decorator.
-        #:
-        #: .. deprecated:: 2.2
-        #:     Will be removed in Flask 2.3. Run setup code when
-        #:     creating the application instead.
-        #:
-        #: .. versionadded:: 0.8
-        self.before_first_request_funcs: t.List[ft.BeforeFirstRequestCallable] = []
-
         #: A list of functions that are called when the application context
         #: is destroyed.  Since the application context is also torn down
         #: if the request ends this is the place to store code that disconnects
         #: from databases.
         #:
         #: .. versionadded:: 0.9
-        self.teardown_appcontext_funcs: t.List[ft.TeardownCallable] = []
+        self.teardown_appcontext_funcs: list[ft.TeardownCallable] = []
 
         #: A list of shell context processor functions that should be run
         #: when a shell context is created.
         #:
         #: .. versionadded:: 0.11
-        self.shell_context_processors: t.List[ft.ShellContextProcessorCallable] = []
+        self.shell_context_processors: list[ft.ShellContextProcessorCallable] = []
 
         #: Maps registered blueprint names to blueprint objects. The
         #: dict retains the order the blueprints were registered in.
@@ -656,7 +446,7 @@ class Flask(Scaffold):
         #: not track how often they were attached.
         #:
         #: .. versionadded:: 0.7
-        self.blueprints: t.Dict[str, "Blueprint"] = {}
+        self.blueprints: dict[str, Blueprint] = {}
 
         #: a place where extensions can store application specific state.  For
         #: example this is where an extension could store database engines and
@@ -692,7 +482,6 @@ class Flask(Scaffold):
         # tracks internally if the application already handled at least one
         # request.
         self._got_first_request = False
-        self._before_request_lock = Lock()
 
         # Add a static route using the provided static_url_path, static_host,
         # and static_folder if there is a configured static_folder.
@@ -729,7 +518,7 @@ class Flask(Scaffold):
                 " running it."
             )
 
-    @locked_cached_property
+    @cached_property
     def name(self) -> str:  # type: ignore
         """The name of the application.  This is usually the import name
         with the difference that it's guessed from the run file if the
@@ -746,29 +535,7 @@ class Flask(Scaffold):
             return os.path.splitext(os.path.basename(fn))[0]
         return self.import_name
 
-    @property
-    def propagate_exceptions(self) -> bool:
-        """Returns the value of the ``PROPAGATE_EXCEPTIONS`` configuration
-        value in case it's set, otherwise a sensible default is returned.
-
-        .. deprecated:: 2.2
-            Will be removed in Flask 2.3.
-
-        .. versionadded:: 0.7
-        """
-        import warnings
-
-        warnings.warn(
-            "'propagate_exceptions' is deprecated and will be removed in Flask 2.3.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        rv = self.config["PROPAGATE_EXCEPTIONS"]
-        if rv is not None:
-            return rv
-        return self.testing or self.debug
-
-    @locked_cached_property
+    @cached_property
     def logger(self) -> logging.Logger:
         """A standard Python :class:`~logging.Logger` for the app, with
         the same name as :attr:`name`.
@@ -795,7 +562,7 @@ class Flask(Scaffold):
         """
         return create_logger(self)
 
-    @locked_cached_property
+    @cached_property
     def jinja_env(self) -> Environment:
         """The Jinja environment used to load templates.
 
@@ -810,8 +577,18 @@ class Flask(Scaffold):
         """This attribute is set to ``True`` if the application started
         handling the first request.
 
+        .. deprecated:: 2.3
+            Will be removed in Flask 2.4.
+
         .. versionadded:: 0.8
         """
+        import warnings
+
+        warnings.warn(
+            "'got_first_request' is deprecated and will be removed in Flask 2.4.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
         return self._got_first_request
 
     def make_config(self, instance_relative: bool = False) -> Config:
@@ -827,7 +604,6 @@ class Flask(Scaffold):
         if instance_relative:
             root_path = self.instance_path
         defaults = dict(self.default_config)
-        defaults["ENV"] = os.environ.get("FLASK_ENV") or "production"
         defaults["DEBUG"] = get_debug_flag()
         return self.config_class(root_path, defaults)
 
@@ -868,42 +644,6 @@ class Flask(Scaffold):
         """
         return open(os.path.join(self.instance_path, resource), mode)
 
-    @property
-    def templates_auto_reload(self) -> bool:
-        """Reload templates when they are changed. Used by
-        :meth:`create_jinja_environment`. It is enabled by default in debug mode.
-
-        .. deprecated:: 2.2
-            Will be removed in Flask 2.3. Use ``app.config["TEMPLATES_AUTO_RELOAD"]``
-            instead.
-
-        .. versionadded:: 1.0
-            This property was added but the underlying config and behavior
-            already existed.
-        """
-        import warnings
-
-        warnings.warn(
-            "'templates_auto_reload' is deprecated and will be removed in Flask 2.3."
-            " Use 'TEMPLATES_AUTO_RELOAD' in 'app.config' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        rv = self.config["TEMPLATES_AUTO_RELOAD"]
-        return rv if rv is not None else self.debug
-
-    @templates_auto_reload.setter
-    def templates_auto_reload(self, value: bool) -> None:
-        import warnings
-
-        warnings.warn(
-            "'templates_auto_reload' is deprecated and will be removed in Flask 2.3."
-            " Use 'TEMPLATES_AUTO_RELOAD' in 'app.config' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        self.config["TEMPLATES_AUTO_RELOAD"] = value
-
     def create_jinja_environment(self) -> Environment:
         """Create the Jinja environment based on :attr:`jinja_options`
         and the various Jinja-related methods of the app. Changing
@@ -961,11 +701,14 @@ class Flask(Scaffold):
         """Returns ``True`` if autoescaping should be active for the given
         template name. If no template name is given, returns `True`.
 
+        .. versionchanged:: 2.2
+            Autoescaping is now enabled by default for ``.svg`` files.
+
         .. versionadded:: 0.5
         """
         if filename is None:
             return True
-        return filename.endswith((".html", ".htm", ".xml", ".xhtml"))
+        return filename.endswith((".html", ".htm", ".xml", ".xhtml", ".svg"))
 
     def update_template_context(self, context: dict) -> None:
         """Update the template context with some commonly used variables.
@@ -978,7 +721,7 @@ class Flask(Scaffold):
         :param context: the context as a dictionary that is updated in place
                         to add extra variables.
         """
-        names: t.Iterable[t.Optional[str]] = (None,)
+        names: t.Iterable[str | None] = (None,)
 
         # A template may be rendered outside a request context.
         if request:
@@ -1007,40 +750,6 @@ class Flask(Scaffold):
             rv.update(processor())
         return rv
 
-    @property
-    def env(self) -> str:
-        """What environment the app is running in. This maps to the :data:`ENV` config
-        key.
-
-        **Do not enable development when deploying in production.**
-
-        Default: ``'production'``
-
-        .. deprecated:: 2.2
-            Will be removed in Flask 2.3.
-        """
-        import warnings
-
-        warnings.warn(
-            "'app.env' is deprecated and will be removed in Flask 2.3."
-            " Use 'app.debug' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        return self.config["ENV"]
-
-    @env.setter
-    def env(self, value: str) -> None:
-        import warnings
-
-        warnings.warn(
-            "'app.env' is deprecated and will be removed in Flask 2.3."
-            " Use 'app.debug' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        self.config["ENV"] = value
-
     @property
     def debug(self) -> bool:
         """Whether debug mode is enabled. When using ``flask run`` to start the
@@ -1063,9 +772,9 @@ class Flask(Scaffold):
 
     def run(
         self,
-        host: t.Optional[str] = None,
-        port: t.Optional[int] = None,
-        debug: t.Optional[bool] = None,
+        host: str | None = None,
+        port: int | None = None,
+        debug: bool | None = None,
         load_dotenv: bool = True,
         **options: t.Any,
     ) -> None:
@@ -1141,16 +850,8 @@ class Flask(Scaffold):
         if get_load_dotenv(load_dotenv):
             cli.load_dotenv()
 
-            # if set, let env vars override previous values
-            if "FLASK_ENV" in os.environ:
-                print(
-                    "'FLASK_ENV' is deprecated and will not be used in"
-                    " Flask 2.3. Use 'FLASK_DEBUG' instead.",
-                    file=sys.stderr,
-                )
-                self.config["ENV"] = os.environ.get("FLASK_ENV") or "production"
-                self.debug = get_debug_flag()
-            elif "FLASK_DEBUG" in os.environ:
+            # if set, env var overrides existing value
+            if "FLASK_DEBUG" in os.environ:
                 self.debug = get_debug_flag()
 
         # debug passed to method overrides all other sources
@@ -1192,7 +893,7 @@ class Flask(Scaffold):
             # without reloader and that stuff from an interactive shell.
             self._got_first_request = False
 
-    def test_client(self, use_cookies: bool = True, **kwargs: t.Any) -> "FlaskClient":
+    def test_client(self, use_cookies: bool = True, **kwargs: t.Any) -> FlaskClient:
         """Creates a test client for this application.  For information
         about unit testing head over to :doc:`/testing`.
 
@@ -1245,12 +946,12 @@ class Flask(Scaffold):
         """
         cls = self.test_client_class
         if cls is None:
-            from .testing import FlaskClient as cls  # type: ignore
+            from .testing import FlaskClient as cls
         return cls(  # type: ignore
             self, self.response_class, use_cookies=use_cookies, **kwargs
         )
 
-    def test_cli_runner(self, **kwargs: t.Any) -> "FlaskCliRunner":
+    def test_cli_runner(self, **kwargs: t.Any) -> FlaskCliRunner:
         """Create a CLI runner for testing CLI commands.
         See :ref:`testing-cli`.
 
@@ -1263,12 +964,12 @@ class Flask(Scaffold):
         cls = self.test_cli_runner_class
 
         if cls is None:
-            from .testing import FlaskCliRunner as cls  # type: ignore
+            from .testing import FlaskCliRunner as cls
 
         return cls(self, **kwargs)  # type: ignore
 
     @setupmethod
-    def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None:
+    def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None:
         """Register a :class:`~flask.Blueprint` on the application. Keyword
         arguments passed to this method will override the defaults set on the
         blueprint.
@@ -1295,7 +996,7 @@ class Flask(Scaffold):
         """
         blueprint.register(self, options)
 
-    def iter_blueprints(self) -> t.ValuesView["Blueprint"]:
+    def iter_blueprints(self) -> t.ValuesView[Blueprint]:
         """Iterates over all blueprints by the order they were registered.
 
         .. versionadded:: 0.11
@@ -1306,9 +1007,9 @@ class Flask(Scaffold):
     def add_url_rule(
         self,
         rule: str,
-        endpoint: t.Optional[str] = None,
-        view_func: t.Optional[ft.RouteCallable] = None,
-        provide_automatic_options: t.Optional[bool] = None,
+        endpoint: str | None = None,
+        view_func: ft.RouteCallable | None = None,
+        provide_automatic_options: bool | None = None,
         **options: t.Any,
     ) -> None:
         if endpoint is None:
@@ -1363,7 +1064,7 @@ class Flask(Scaffold):
 
     @setupmethod
     def template_filter(
-        self, name: t.Optional[str] = None
+        self, name: str | None = None
     ) -> t.Callable[[T_template_filter], T_template_filter]:
         """A decorator that is used to register custom template filter.
         You can specify a name for the filter, otherwise the function
@@ -1385,7 +1086,7 @@ class Flask(Scaffold):
 
     @setupmethod
     def add_template_filter(
-        self, f: ft.TemplateFilterCallable, name: t.Optional[str] = None
+        self, f: ft.TemplateFilterCallable, name: str | None = None
     ) -> None:
         """Register a custom template filter.  Works exactly like the
         :meth:`template_filter` decorator.
@@ -1397,7 +1098,7 @@ class Flask(Scaffold):
 
     @setupmethod
     def template_test(
-        self, name: t.Optional[str] = None
+        self, name: str | None = None
     ) -> t.Callable[[T_template_test], T_template_test]:
         """A decorator that is used to register custom template test.
         You can specify a name for the test, otherwise the function
@@ -1426,7 +1127,7 @@ class Flask(Scaffold):
 
     @setupmethod
     def add_template_test(
-        self, f: ft.TemplateTestCallable, name: t.Optional[str] = None
+        self, f: ft.TemplateTestCallable, name: str | None = None
     ) -> None:
         """Register a custom template test.  Works exactly like the
         :meth:`template_test` decorator.
@@ -1440,7 +1141,7 @@ class Flask(Scaffold):
 
     @setupmethod
     def template_global(
-        self, name: t.Optional[str] = None
+        self, name: str | None = None
     ) -> t.Callable[[T_template_global], T_template_global]:
         """A decorator that is used to register a custom template global function.
         You can specify a name for the global function, otherwise the function
@@ -1464,7 +1165,7 @@ class Flask(Scaffold):
 
     @setupmethod
     def add_template_global(
-        self, f: ft.TemplateGlobalCallable, name: t.Optional[str] = None
+        self, f: ft.TemplateGlobalCallable, name: str | None = None
     ) -> None:
         """Register a custom template global function. Works exactly like the
         :meth:`template_global` decorator.
@@ -1476,32 +1177,6 @@ class Flask(Scaffold):
         """
         self.jinja_env.globals[name or f.__name__] = f
 
-    @setupmethod
-    def before_first_request(self, f: T_before_first_request) -> T_before_first_request:
-        """Registers a function to be run before the first request to this
-        instance of the application.
-
-        The function will be called without any arguments and its return
-        value is ignored.
-
-        .. deprecated:: 2.2
-            Will be removed in Flask 2.3. Run setup code when creating
-            the application instead.
-
-        .. versionadded:: 0.8
-        """
-        import warnings
-
-        warnings.warn(
-            "'before_first_request' is deprecated and will be removed"
-            " in Flask 2.3. Run setup code while creating the"
-            " application instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        self.before_first_request_funcs.append(f)
-        return f
-
     @setupmethod
     def teardown_appcontext(self, f: T_teardown) -> T_teardown:
         """Registers a function to be called when the application
@@ -1547,7 +1222,7 @@ class Flask(Scaffold):
         self.shell_context_processors.append(f)
         return f
 
-    def _find_error_handler(self, e: Exception) -> t.Optional[ft.ErrorHandlerCallable]:
+    def _find_error_handler(self, e: Exception) -> ft.ErrorHandlerCallable | None:
         """Return a registered error handler for an exception in this order:
         blueprint handler for a specific code, app handler for a specific code,
         blueprint handler for an exception class, app handler for an exception
@@ -1572,7 +1247,7 @@ class Flask(Scaffold):
 
     def handle_http_exception(
         self, e: HTTPException
-    ) -> t.Union[HTTPException, ft.ResponseReturnValue]:
+    ) -> HTTPException | ft.ResponseReturnValue:
         """Handles an HTTP exception.  By default this will invoke the
         registered error handlers and fall back to returning the
         exception as response.
@@ -1642,7 +1317,7 @@ class Flask(Scaffold):
 
     def handle_user_exception(
         self, e: Exception
-    ) -> t.Union[HTTPException, ft.ResponseReturnValue]:
+    ) -> HTTPException | ft.ResponseReturnValue:
         """This method is called whenever an exception occurs that
         should be handled. A special case is :class:`~werkzeug
         .exceptions.HTTPException` which is forwarded to the
@@ -1679,7 +1354,7 @@ class Flask(Scaffold):
 
         Always sends the :data:`got_request_exception` signal.
 
-        If :attr:`propagate_exceptions` is ``True``, such as in debug
+        If :data:`PROPAGATE_EXCEPTIONS` is ``True``, such as in debug
         mode, the error will be re-raised so that the debugger can
         display it. Otherwise, the original exception is logged, and
         an :exc:`~werkzeug.exceptions.InternalServerError` is returned.
@@ -1701,7 +1376,7 @@ class Flask(Scaffold):
         .. versionadded:: 0.3
         """
         exc_info = sys.exc_info()
-        got_request_exception.send(self, exception=e)
+        got_request_exception.send(self, _async_wrapper=self.ensure_sync, exception=e)
         propagate = self.config["PROPAGATE_EXCEPTIONS"]
 
         if propagate is None:
@@ -1716,7 +1391,7 @@ class Flask(Scaffold):
             raise e
 
         self.log_exception(exc_info)
-        server_error: t.Union[InternalServerError, ft.ResponseReturnValue]
+        server_error: InternalServerError | ft.ResponseReturnValue
         server_error = InternalServerError(original_exception=e)
         handler = self._find_error_handler(server_error)
 
@@ -1727,9 +1402,7 @@ class Flask(Scaffold):
 
     def log_exception(
         self,
-        exc_info: t.Union[
-            t.Tuple[type, BaseException, TracebackType], t.Tuple[None, None, None]
-        ],
+        exc_info: (tuple[type, BaseException, TracebackType] | tuple[None, None, None]),
     ) -> None:
         """Logs an exception.  This is called by :meth:`handle_exception`
         if debugging is disabled and right before the handler is called.
@@ -1742,7 +1415,7 @@ class Flask(Scaffold):
             f"Exception on {request.path} [{request.method}]", exc_info=exc_info
         )
 
-    def raise_routing_exception(self, request: Request) -> "te.NoReturn":
+    def raise_routing_exception(self, request: Request) -> t.NoReturn:
         """Intercept routing exceptions and possibly do something else.
 
         In debug mode, intercept a routing redirect and replace it with
@@ -1792,7 +1465,7 @@ class Flask(Scaffold):
         ):
             return self.make_default_options_response()
         # otherwise dispatch to the handler for that endpoint
-        view_args: t.Dict[str, t.Any] = req.view_args  # type: ignore[assignment]
+        view_args: dict[str, t.Any] = req.view_args  # type: ignore[assignment]
         return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
 
     def full_dispatch_request(self) -> Response:
@@ -1802,19 +1475,10 @@ class Flask(Scaffold):
 
         .. versionadded:: 0.7
         """
-        # Run before_first_request functions if this is the thread's first request.
-        # Inlined to avoid a method call on subsequent requests.
-        # This is deprecated, will be removed in Flask 2.3.
-        if not self._got_first_request:
-            with self._before_request_lock:
-                if not self._got_first_request:
-                    for func in self.before_first_request_funcs:
-                        self.ensure_sync(func)()
-
-                    self._got_first_request = True
+        self._got_first_request = True
 
         try:
-            request_started.send(self)
+            request_started.send(self, _async_wrapper=self.ensure_sync)
             rv = self.preprocess_request()
             if rv is None:
                 rv = self.dispatch_request()
@@ -1824,7 +1488,7 @@ class Flask(Scaffold):
 
     def finalize_request(
         self,
-        rv: t.Union[ft.ResponseReturnValue, HTTPException],
+        rv: ft.ResponseReturnValue | HTTPException,
         from_error_handler: bool = False,
     ) -> Response:
         """Given the return value from a view function this finalizes
@@ -1842,7 +1506,9 @@ class Flask(Scaffold):
         response = self.make_response(rv)
         try:
             response = self.process_response(response)
-            request_finished.send(self, response=response)
+            request_finished.send(
+                self, _async_wrapper=self.ensure_sync, response=response
+            )
         except Exception:
             if not from_error_handler:
                 raise
@@ -1864,7 +1530,7 @@ class Flask(Scaffold):
         rv.allow.update(methods)
         return rv
 
-    def should_ignore_error(self, error: t.Optional[BaseException]) -> bool:
+    def should_ignore_error(self, error: BaseException | None) -> bool:
         """This is called to figure out if an error should be ignored
         or not as far as the teardown system is concerned.  If this
         function returns ``True`` then the teardown handlers will not be
@@ -1915,10 +1581,10 @@ class Flask(Scaffold):
         self,
         endpoint: str,
         *,
-        _anchor: t.Optional[str] = None,
-        _method: t.Optional[str] = None,
-        _scheme: t.Optional[str] = None,
-        _external: t.Optional[bool] = None,
+        _anchor: str | None = None,
+        _method: str | None = None,
+        _scheme: str | None = None,
+        _external: bool | None = None,
         **values: t.Any,
     ) -> str:
         """Generate a URL to the given endpoint with the given values.
@@ -2031,7 +1697,8 @@ class Flask(Scaffold):
             return self.handle_url_build_error(error, endpoint, values)
 
         if _anchor is not None:
-            rv = f"{rv}#{url_quote(_anchor)}"
+            _anchor = _url_quote(_anchor, safe="%!#$&'()*+,/:;=?@")
+            rv = f"{rv}#{_anchor}"
 
         return rv
 
@@ -2189,9 +1856,7 @@ class Flask(Scaffold):
 
         return rv
 
-    def create_url_adapter(
-        self, request: t.Optional[Request]
-    ) -> t.Optional[MapAdapter]:
+    def create_url_adapter(self, request: Request | None) -> MapAdapter | None:
         """Creates a URL adapter for the given request. The URL adapter
         is created at a point where the request context is not yet set
         up so the request is passed explicitly.
@@ -2238,7 +1903,7 @@ class Flask(Scaffold):
 
         .. versionadded:: 0.7
         """
-        names: t.Iterable[t.Optional[str]] = (None,)
+        names: t.Iterable[str | None] = (None,)
 
         # url_for may be called outside a request context, parse the
         # passed endpoint instead of using request.blueprints.
@@ -2253,7 +1918,7 @@ class Flask(Scaffold):
                     func(endpoint, values)
 
     def handle_url_build_error(
-        self, error: BuildError, endpoint: str, values: t.Dict[str, t.Any]
+        self, error: BuildError, endpoint: str, values: dict[str, t.Any]
     ) -> str:
         """Called by :meth:`.url_for` if a
         :exc:`~werkzeug.routing.BuildError` was raised. If this returns
@@ -2286,7 +1951,7 @@ class Flask(Scaffold):
 
         raise error
 
-    def preprocess_request(self) -> t.Optional[ft.ResponseReturnValue]:
+    def preprocess_request(self) -> ft.ResponseReturnValue | None:
         """Called before the request is dispatched. Calls
         :attr:`url_value_preprocessors` registered with the app and the
         current blueprint (if any). Then calls :attr:`before_request_funcs`
@@ -2342,7 +2007,7 @@ class Flask(Scaffold):
         return response
 
     def do_teardown_request(
-        self, exc: t.Optional[BaseException] = _sentinel  # type: ignore
+        self, exc: BaseException | None = _sentinel  # type: ignore
     ) -> None:
         """Called after the request is dispatched and the response is
         returned, right before the request context is popped.
@@ -2372,10 +2037,10 @@ class Flask(Scaffold):
                 for func in reversed(self.teardown_request_funcs[name]):
                     self.ensure_sync(func)(exc)
 
-        request_tearing_down.send(self, exc=exc)
+        request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
 
     def do_teardown_appcontext(
-        self, exc: t.Optional[BaseException] = _sentinel  # type: ignore
+        self, exc: BaseException | None = _sentinel  # type: ignore
     ) -> None:
         """Called right before the application context is popped.
 
@@ -2397,7 +2062,7 @@ class Flask(Scaffold):
         for func in reversed(self.teardown_appcontext_funcs):
             self.ensure_sync(func)(exc)
 
-        appcontext_tearing_down.send(self, exc=exc)
+        appcontext_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
 
     def app_context(self) -> AppContext:
         """Create an :class:`~flask.ctx.AppContext`. Use as a ``with``
@@ -2448,7 +2113,7 @@ class Flask(Scaffold):
         :data:`request` point at the request for the created
         environment. ::
 
-            with test_request_context(...):
+            with app.test_request_context(...):
                 generate_report()
 
         When using the shell, it may be easier to push and pop the
@@ -2518,7 +2183,7 @@ class Flask(Scaffold):
             start the response.
         """
         ctx = self.request_context(environ)
-        error: t.Optional[BaseException] = None
+        error: BaseException | None = None
         try:
             try:
                 ctx.push()
diff --git a/src/flask/blueprints.py b/src/flask/blueprints.py
index 104f8ac..0407f86 100644
--- a/src/flask/blueprints.py
+++ b/src/flask/blueprints.py
@@ -1,4 +1,5 @@
-import json
+from __future__ import annotations
+
 import os
 import typing as t
 from collections import defaultdict
@@ -15,9 +16,6 @@ if t.TYPE_CHECKING:  # pragma: no cover
 
 DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable]
 T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable)
-T_before_first_request = t.TypeVar(
-    "T_before_first_request", bound=ft.BeforeFirstRequestCallable
-)
 T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable)
 T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable)
 T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable)
@@ -42,8 +40,8 @@ class BlueprintSetupState:
 
     def __init__(
         self,
-        blueprint: "Blueprint",
-        app: "Flask",
+        blueprint: Blueprint,
+        app: Flask,
         options: t.Any,
         first_registration: bool,
     ) -> None:
@@ -89,8 +87,8 @@ class BlueprintSetupState:
     def add_url_rule(
         self,
         rule: str,
-        endpoint: t.Optional[str] = None,
-        view_func: t.Optional[t.Callable] = None,
+        endpoint: str | None = None,
+        view_func: t.Callable | None = None,
         **options: t.Any,
     ) -> None:
         """A helper method to register a rule (and optionally a view function)
@@ -173,89 +171,18 @@ class Blueprint(Scaffold):
 
     _got_registered_once = False
 
-    _json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None
-    _json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None
-
-    @property  # type: ignore[override]
-    def json_encoder(  # type: ignore[override]
-        self,
-    ) -> t.Union[t.Type[json.JSONEncoder], None]:
-        """Blueprint-local JSON encoder class to use. Set to ``None`` to use the app's.
-
-        .. deprecated:: 2.2
-             Will be removed in Flask 2.3. Customize
-             :attr:`json_provider_class` instead.
-
-        .. versionadded:: 0.10
-        """
-        import warnings
-
-        warnings.warn(
-            "'bp.json_encoder' is deprecated and will be removed in Flask 2.3."
-            " Customize 'app.json_provider_class' or 'app.json' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        return self._json_encoder
-
-    @json_encoder.setter
-    def json_encoder(self, value: t.Union[t.Type[json.JSONEncoder], None]) -> None:
-        import warnings
-
-        warnings.warn(
-            "'bp.json_encoder' is deprecated and will be removed in Flask 2.3."
-            " Customize 'app.json_provider_class' or 'app.json' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        self._json_encoder = value
-
-    @property  # type: ignore[override]
-    def json_decoder(  # type: ignore[override]
-        self,
-    ) -> t.Union[t.Type[json.JSONDecoder], None]:
-        """Blueprint-local JSON decoder class to use. Set to ``None`` to use the app's.
-
-        .. deprecated:: 2.2
-             Will be removed in Flask 2.3. Customize
-             :attr:`json_provider_class` instead.
-
-        .. versionadded:: 0.10
-        """
-        import warnings
-
-        warnings.warn(
-            "'bp.json_decoder' is deprecated and will be removed in Flask 2.3."
-            " Customize 'app.json_provider_class' or 'app.json' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        return self._json_decoder
-
-    @json_decoder.setter
-    def json_decoder(self, value: t.Union[t.Type[json.JSONDecoder], None]) -> None:
-        import warnings
-
-        warnings.warn(
-            "'bp.json_decoder' is deprecated and will be removed in Flask 2.3."
-            " Customize 'app.json_provider_class' or 'app.json' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        self._json_decoder = value
-
     def __init__(
         self,
         name: str,
         import_name: str,
-        static_folder: t.Optional[t.Union[str, os.PathLike]] = None,
-        static_url_path: t.Optional[str] = None,
-        template_folder: t.Optional[str] = None,
-        url_prefix: t.Optional[str] = None,
-        subdomain: t.Optional[str] = None,
-        url_defaults: t.Optional[dict] = None,
-        root_path: t.Optional[str] = None,
-        cli_group: t.Optional[str] = _sentinel,  # type: ignore
+        static_folder: str | os.PathLike | None = None,
+        static_url_path: str | None = None,
+        template_folder: str | os.PathLike | None = None,
+        url_prefix: str | None = None,
+        subdomain: str | None = None,
+        url_defaults: dict | None = None,
+        root_path: str | None = None,
+        cli_group: str | None = _sentinel,  # type: ignore
     ):
         super().__init__(
             import_name=import_name,
@@ -265,36 +192,32 @@ class Blueprint(Scaffold):
             root_path=root_path,
         )
 
+        if not name:
+            raise ValueError("'name' may not be empty.")
+
         if "." in name:
             raise ValueError("'name' may not contain a dot '.' character.")
 
         self.name = name
         self.url_prefix = url_prefix
         self.subdomain = subdomain
-        self.deferred_functions: t.List[DeferredSetupFunction] = []
+        self.deferred_functions: list[DeferredSetupFunction] = []
 
         if url_defaults is None:
             url_defaults = {}
 
         self.url_values_defaults = url_defaults
         self.cli_group = cli_group
-        self._blueprints: t.List[t.Tuple["Blueprint", dict]] = []
+        self._blueprints: list[tuple[Blueprint, dict]] = []
 
     def _check_setup_finished(self, f_name: str) -> None:
         if self._got_registered_once:
-            import warnings
-
-            warnings.warn(
-                f"The setup method '{f_name}' can no longer be called on"
-                f" the blueprint '{self.name}'. It has already been"
-                " registered at least once, any changes will not be"
-                " applied consistently.\n"
-                "Make sure all imports, decorators, functions, etc."
-                " needed to set up the blueprint are done before"
-                " registering it.\n"
-                "This warning will become an exception in Flask 2.3.",
-                UserWarning,
-                stacklevel=3,
+            raise AssertionError(
+                f"The setup method '{f_name}' can no longer be called on the blueprint"
+                f" '{self.name}'. It has already been registered at least once, any"
+                " changes will not be applied consistently.\n"
+                "Make sure all imports, decorators, functions, etc. needed to set up"
+                " the blueprint are done before registering it."
             )
 
     @setupmethod
@@ -321,7 +244,7 @@ class Blueprint(Scaffold):
         self.record(update_wrapper(wrapper, func))
 
     def make_setup_state(
-        self, app: "Flask", options: dict, first_registration: bool = False
+        self, app: Flask, options: dict, first_registration: bool = False
     ) -> BlueprintSetupState:
         """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
         object that is later passed to the register callback functions.
@@ -330,7 +253,7 @@ class Blueprint(Scaffold):
         return BlueprintSetupState(self, app, options, first_registration)
 
     @setupmethod
-    def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None:
+    def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None:
         """Register a :class:`~flask.Blueprint` on this blueprint. Keyword
         arguments passed to this method will override the defaults set
         on the blueprint.
@@ -347,7 +270,7 @@ class Blueprint(Scaffold):
             raise ValueError("Cannot register a blueprint on itself")
         self._blueprints.append((blueprint, options))
 
-    def register(self, app: "Flask", options: dict) -> None:
+    def register(self, app: Flask, options: dict) -> None:
         """Called by :meth:`Flask.register_blueprint` to register all
         views and callbacks registered on the blueprint with the
         application. Creates a :class:`.BlueprintSetupState` and calls
@@ -358,6 +281,13 @@ class Blueprint(Scaffold):
         :param options: Keyword arguments forwarded from
             :meth:`~Flask.register_blueprint`.
 
+        .. versionchanged:: 2.3
+            Nested blueprints now correctly apply subdomains.
+
+        .. versionchanged:: 2.1
+            Registering the same blueprint with the same name multiple
+            times is an error.
+
         .. versionchanged:: 2.0.1
             Nested blueprints are registered with their dotted name.
             This allows different blueprints with the same name to be
@@ -368,10 +298,6 @@ class Blueprint(Scaffold):
             name the blueprint is registered with. This allows the same
             blueprint to be registered multiple times with unique names
             for ``url_for``.
-
-        .. versionchanged:: 2.0.1
-            Registering the same blueprint with the same name multiple
-            times is deprecated and will become an error in Flask 2.1.
         """
         name_prefix = options.get("name_prefix", "")
         self_name = options.get("name", self.name)
@@ -453,6 +379,17 @@ class Blueprint(Scaffold):
         for blueprint, bp_options in self._blueprints:
             bp_options = bp_options.copy()
             bp_url_prefix = bp_options.get("url_prefix")
+            bp_subdomain = bp_options.get("subdomain")
+
+            if bp_subdomain is None:
+                bp_subdomain = blueprint.subdomain
+
+            if state.subdomain is not None and bp_subdomain is not None:
+                bp_options["subdomain"] = bp_subdomain + "." + state.subdomain
+            elif bp_subdomain is not None:
+                bp_options["subdomain"] = bp_subdomain
+            elif state.subdomain is not None:
+                bp_options["subdomain"] = state.subdomain
 
             if bp_url_prefix is None:
                 bp_url_prefix = blueprint.url_prefix
@@ -473,13 +410,16 @@ class Blueprint(Scaffold):
     def add_url_rule(
         self,
         rule: str,
-        endpoint: t.Optional[str] = None,
-        view_func: t.Optional[ft.RouteCallable] = None,
-        provide_automatic_options: t.Optional[bool] = None,
+        endpoint: str | None = None,
+        view_func: ft.RouteCallable | None = None,
+        provide_automatic_options: bool | None = None,
         **options: t.Any,
     ) -> None:
-        """Like :meth:`Flask.add_url_rule` but for a blueprint.  The endpoint for
-        the :func:`url_for` function is prefixed with the name of the blueprint.
+        """Register a URL rule with the blueprint. See :meth:`.Flask.add_url_rule` for
+        full documentation.
+
+        The URL rule is prefixed with the blueprint's URL prefix. The endpoint name,
+        used with :func:`url_for`, is prefixed with the blueprint's name.
         """
         if endpoint and "." in endpoint:
             raise ValueError("'endpoint' may not contain a dot '.' character.")
@@ -499,10 +439,10 @@ class Blueprint(Scaffold):
 
     @setupmethod
     def app_template_filter(
-        self, name: t.Optional[str] = None
+        self, name: str | None = None
     ) -> t.Callable[[T_template_filter], T_template_filter]:
-        """Register a custom template filter, available application wide.  Like
-        :meth:`Flask.template_filter` but for a blueprint.
+        """Register a template filter, available in any template rendered by the
+        application. Equivalent to :meth:`.Flask.template_filter`.
 
         :param name: the optional name of the filter, otherwise the
                      function name will be used.
@@ -516,11 +456,11 @@ class Blueprint(Scaffold):
 
     @setupmethod
     def add_app_template_filter(
-        self, f: ft.TemplateFilterCallable, name: t.Optional[str] = None
+        self, f: ft.TemplateFilterCallable, name: str | None = None
     ) -> None:
-        """Register a custom template filter, available application wide.  Like
-        :meth:`Flask.add_template_filter` but for a blueprint.  Works exactly
-        like the :meth:`app_template_filter` decorator.
+        """Register a template filter, available in any template rendered by the
+        application. Works like the :meth:`app_template_filter` decorator. Equivalent to
+        :meth:`.Flask.add_template_filter`.
 
         :param name: the optional name of the filter, otherwise the
                      function name will be used.
@@ -533,10 +473,10 @@ class Blueprint(Scaffold):
 
     @setupmethod
     def app_template_test(
-        self, name: t.Optional[str] = None
+        self, name: str | None = None
     ) -> t.Callable[[T_template_test], T_template_test]:
-        """Register a custom template test, available application wide.  Like
-        :meth:`Flask.template_test` but for a blueprint.
+        """Register a template test, available in any template rendered by the
+        application. Equivalent to :meth:`.Flask.template_test`.
 
         .. versionadded:: 0.10
 
@@ -552,11 +492,11 @@ class Blueprint(Scaffold):
 
     @setupmethod
     def add_app_template_test(
-        self, f: ft.TemplateTestCallable, name: t.Optional[str] = None
+        self, f: ft.TemplateTestCallable, name: str | None = None
     ) -> None:
-        """Register a custom template test, available application wide.  Like
-        :meth:`Flask.add_template_test` but for a blueprint.  Works exactly
-        like the :meth:`app_template_test` decorator.
+        """Register a template test, available in any template rendered by the
+        application. Works like the :meth:`app_template_test` decorator. Equivalent to
+        :meth:`.Flask.add_template_test`.
 
         .. versionadded:: 0.10
 
@@ -571,10 +511,10 @@ class Blueprint(Scaffold):
 
     @setupmethod
     def app_template_global(
-        self, name: t.Optional[str] = None
+        self, name: str | None = None
     ) -> t.Callable[[T_template_global], T_template_global]:
-        """Register a custom template global, available application wide.  Like
-        :meth:`Flask.template_global` but for a blueprint.
+        """Register a template global, available in any template rendered by the
+        application. Equivalent to :meth:`.Flask.template_global`.
 
         .. versionadded:: 0.10
 
@@ -590,11 +530,11 @@ class Blueprint(Scaffold):
 
     @setupmethod
     def add_app_template_global(
-        self, f: ft.TemplateGlobalCallable, name: t.Optional[str] = None
+        self, f: ft.TemplateGlobalCallable, name: str | None = None
     ) -> None:
-        """Register a custom template global, available application wide.  Like
-        :meth:`Flask.add_template_global` but for a blueprint.  Works exactly
-        like the :meth:`app_template_global` decorator.
+        """Register a template global, available in any template rendered by the
+        application. Works like the :meth:`app_template_global` decorator. Equivalent to
+        :meth:`.Flask.add_template_global`.
 
         .. versionadded:: 0.10
 
@@ -609,41 +549,18 @@ class Blueprint(Scaffold):
 
     @setupmethod
     def before_app_request(self, f: T_before_request) -> T_before_request:
-        """Like :meth:`Flask.before_request`.  Such a function is executed
-        before each request, even if outside of a blueprint.
+        """Like :meth:`before_request`, but before every request, not only those handled
+        by the blueprint. Equivalent to :meth:`.Flask.before_request`.
         """
         self.record_once(
             lambda s: s.app.before_request_funcs.setdefault(None, []).append(f)
         )
         return f
 
-    @setupmethod
-    def before_app_first_request(
-        self, f: T_before_first_request
-    ) -> T_before_first_request:
-        """Like :meth:`Flask.before_first_request`.  Such a function is
-        executed before the first request to the application.
-
-        .. deprecated:: 2.2
-            Will be removed in Flask 2.3. Run setup code when creating
-            the application instead.
-        """
-        import warnings
-
-        warnings.warn(
-            "'before_app_first_request' is deprecated and will be"
-            " removed in Flask 2.3. Use 'record_once' instead to run"
-            " setup code when registering the blueprint.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
-        return f
-
     @setupmethod
     def after_app_request(self, f: T_after_request) -> T_after_request:
-        """Like :meth:`Flask.after_request` but for a blueprint.  Such a function
-        is executed after each request, even if outside of the blueprint.
+        """Like :meth:`after_request`, but after every request, not only those handled
+        by the blueprint. Equivalent to :meth:`.Flask.after_request`.
         """
         self.record_once(
             lambda s: s.app.after_request_funcs.setdefault(None, []).append(f)
@@ -652,9 +569,8 @@ class Blueprint(Scaffold):
 
     @setupmethod
     def teardown_app_request(self, f: T_teardown) -> T_teardown:
-        """Like :meth:`Flask.teardown_request` but for a blueprint.  Such a
-        function is executed when tearing down each request, even if outside of
-        the blueprint.
+        """Like :meth:`teardown_request`, but after every request, not only those
+        handled by the blueprint. Equivalent to :meth:`.Flask.teardown_request`.
         """
         self.record_once(
             lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f)
@@ -665,8 +581,8 @@ class Blueprint(Scaffold):
     def app_context_processor(
         self, f: T_template_context_processor
     ) -> T_template_context_processor:
-        """Like :meth:`Flask.context_processor` but for a blueprint.  Such a
-        function is executed each request, even if outside of the blueprint.
+        """Like :meth:`context_processor`, but for templates rendered by every view, not
+        only by the blueprint. Equivalent to :meth:`.Flask.context_processor`.
         """
         self.record_once(
             lambda s: s.app.template_context_processors.setdefault(None, []).append(f)
@@ -675,10 +591,10 @@ class Blueprint(Scaffold):
 
     @setupmethod
     def app_errorhandler(
-        self, code: t.Union[t.Type[Exception], int]
+        self, code: type[Exception] | int
     ) -> t.Callable[[T_error_handler], T_error_handler]:
-        """Like :meth:`Flask.errorhandler` but for a blueprint.  This
-        handler is used for all requests, even if outside of the blueprint.
+        """Like :meth:`errorhandler`, but for every request, not only those handled by
+        the blueprint. Equivalent to :meth:`.Flask.errorhandler`.
         """
 
         def decorator(f: T_error_handler) -> T_error_handler:
@@ -691,7 +607,9 @@ class Blueprint(Scaffold):
     def app_url_value_preprocessor(
         self, f: T_url_value_preprocessor
     ) -> T_url_value_preprocessor:
-        """Same as :meth:`url_value_preprocessor` but application wide."""
+        """Like :meth:`url_value_preprocessor`, but for every request, not only those
+        handled by the blueprint. Equivalent to :meth:`.Flask.url_value_preprocessor`.
+        """
         self.record_once(
             lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f)
         )
@@ -699,7 +617,9 @@ class Blueprint(Scaffold):
 
     @setupmethod
     def app_url_defaults(self, f: T_url_defaults) -> T_url_defaults:
-        """Same as :meth:`url_defaults` but application wide."""
+        """Like :meth:`url_defaults`, but for every request, not only those handled by
+        the blueprint. Equivalent to :meth:`.Flask.url_defaults`.
+        """
         self.record_once(
             lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
         )
diff --git a/src/flask/cli.py b/src/flask/cli.py
index 82fe819..f7e1f29 100644
--- a/src/flask/cli.py
+++ b/src/flask/cli.py
@@ -9,7 +9,7 @@ import sys
 import traceback
 import typing as t
 from functools import update_wrapper
-from operator import attrgetter
+from operator import itemgetter
 
 import click
 from click.core import ParameterSource
@@ -285,7 +285,7 @@ class ScriptInfo:
         self.create_app = create_app
         #: A dictionary with arbitrary data that can be associated with
         #: this script info.
-        self.data: t.Dict[t.Any, t.Any] = {}
+        self.data: dict[t.Any, t.Any] = {}
         self.set_debug_flag = set_debug_flag
         self._loaded_app: Flask | None = None
 
@@ -933,6 +933,9 @@ def run_command(
     )
 
 
+run_command.params.insert(0, _debug_option)
+
+
 @click.command("shell", short_help="Run a shell in the app context.")
 @with_appcontext
 def shell_command() -> None:
@@ -986,49 +989,62 @@ def shell_command() -> None:
 @click.option(
     "--sort",
     "-s",
-    type=click.Choice(("endpoint", "methods", "rule", "match")),
+    type=click.Choice(("endpoint", "methods", "domain", "rule", "match")),
     default="endpoint",
     help=(
-        'Method to sort routes by. "match" is the order that Flask will match '
-        "routes when dispatching a request."
+        "Method to sort routes by. 'match' is the order that Flask will match routes"
+        " when dispatching a request."
     ),
 )
 @click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.")
 @with_appcontext
 def routes_command(sort: str, all_methods: bool) -> None:
     """Show all registered routes with endpoints and methods."""
-
     rules = list(current_app.url_map.iter_rules())
+
     if not rules:
         click.echo("No routes were registered.")
         return
 
-    ignored_methods = set(() if all_methods else ("HEAD", "OPTIONS"))
+    ignored_methods = set() if all_methods else {"HEAD", "OPTIONS"}
+    host_matching = current_app.url_map.host_matching
+    has_domain = any(rule.host if host_matching else rule.subdomain for rule in rules)
+    rows = []
 
-    if sort in ("endpoint", "rule"):
-        rules = sorted(rules, key=attrgetter(sort))
-    elif sort == "methods":
-        rules = sorted(rules, key=lambda rule: sorted(rule.methods))  # type: ignore
+    for rule in rules:
+        row = [
+            rule.endpoint,
+            ", ".join(sorted((rule.methods or set()) - ignored_methods)),
+        ]
 
-    rule_methods = [
-        ", ".join(sorted(rule.methods - ignored_methods))  # type: ignore
-        for rule in rules
-    ]
+        if has_domain:
+            row.append((rule.host if host_matching else rule.subdomain) or "")
 
-    headers = ("Endpoint", "Methods", "Rule")
-    widths = (
-        max(len(rule.endpoint) for rule in rules),
-        max(len(methods) for methods in rule_methods),
-        max(len(rule.rule) for rule in rules),
-    )
-    widths = [max(len(h), w) for h, w in zip(headers, widths)]
-    row = "{{0:<{0}}}  {{1:<{1}}}  {{2:<{2}}}".format(*widths)
+        row.append(rule.rule)
+        rows.append(row)
+
+    headers = ["Endpoint", "Methods"]
+    sorts = ["endpoint", "methods"]
+
+    if has_domain:
+        headers.append("Host" if host_matching else "Subdomain")
+        sorts.append("domain")
+
+    headers.append("Rule")
+    sorts.append("rule")
+
+    try:
+        rows.sort(key=itemgetter(sorts.index(sort)))
+    except ValueError:
+        pass
 
-    click.echo(row.format(*headers).strip())
-    click.echo(row.format(*("-" * width for width in widths)))
+    rows.insert(0, headers)
+    widths = [max(len(row[i]) for row in rows) for i in range(len(headers))]
+    rows.insert(1, ["-" * w for w in widths])
+    template = "  ".join(f"{{{i}:<{w}}}" for i, w in enumerate(widths))
 
-    for rule, methods in zip(rules, rule_methods):
-        click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip())
+    for row in rows:
+        click.echo(template.format(*row))
 
 
 cli = FlaskGroup(
diff --git a/src/flask/config.py b/src/flask/config.py
index 7b6a137..a73dd78 100644
--- a/src/flask/config.py
+++ b/src/flask/config.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import errno
 import json
 import os
@@ -10,7 +12,7 @@ from werkzeug.utils import import_string
 class ConfigAttribute:
     """Makes an attribute forward to the config"""
 
-    def __init__(self, name: str, get_converter: t.Optional[t.Callable] = None) -> None:
+    def __init__(self, name: str, get_converter: t.Callable | None = None) -> None:
         self.__name__ = name
         self.get_converter = get_converter
 
@@ -70,7 +72,7 @@ class Config(dict):
     :param defaults: an optional dictionary of default values
     """
 
-    def __init__(self, root_path: str, defaults: t.Optional[dict] = None) -> None:
+    def __init__(self, root_path: str, defaults: dict | None = None) -> None:
         super().__init__(defaults or {})
         self.root_path = root_path
 
@@ -191,7 +193,7 @@ class Config(dict):
         self.from_object(d)
         return True
 
-    def from_object(self, obj: t.Union[object, str]) -> None:
+    def from_object(self, obj: object | str) -> None:
         """Updates the values from the given object.  An object can be of one
         of the following two types:
 
@@ -234,6 +236,7 @@ class Config(dict):
         filename: str,
         load: t.Callable[[t.IO[t.Any]], t.Mapping],
         silent: bool = False,
+        text: bool = True,
     ) -> bool:
         """Update the values in the config from a file that is loaded
         using the ``load`` parameter. The loaded data is passed to the
@@ -244,8 +247,8 @@ class Config(dict):
             import json
             app.config.from_file("config.json", load=json.load)
 
-            import toml
-            app.config.from_file("config.toml", load=toml.load)
+            import tomllib
+            app.config.from_file("config.toml", load=tomllib.load, text=False)
 
         :param filename: The path to the data file. This can be an
             absolute path or relative to the config root path.
@@ -254,14 +257,18 @@ class Config(dict):
         :type load: ``Callable[[Reader], Mapping]`` where ``Reader``
             implements a ``read`` method.
         :param silent: Ignore the file if it doesn't exist.
+        :param text: Open the file in text or binary mode.
         :return: ``True`` if the file was loaded successfully.
 
+        .. versionchanged:: 2.3
+            The ``text`` parameter was added.
+
         .. versionadded:: 2.0
         """
         filename = os.path.join(self.root_path, filename)
 
         try:
-            with open(filename) as f:
+            with open(filename, "r" if text else "rb") as f:
                 obj = load(f)
         except OSError as e:
             if silent and e.errno in (errno.ENOENT, errno.EISDIR):
@@ -273,15 +280,16 @@ class Config(dict):
         return self.from_mapping(obj)
 
     def from_mapping(
-        self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any
+        self, mapping: t.Mapping[str, t.Any] | None = None, **kwargs: t.Any
     ) -> bool:
-        """Updates the config like :meth:`update` ignoring items with non-upper
-        keys.
+        """Updates the config like :meth:`update` ignoring items with
+        non-upper keys.
+
         :return: Always returns ``True``.
 
         .. versionadded:: 0.11
         """
-        mappings: t.Dict[str, t.Any] = {}
+        mappings: dict[str, t.Any] = {}
         if mapping is not None:
             mappings.update(mapping)
         mappings.update(kwargs)
@@ -292,7 +300,7 @@ class Config(dict):
 
     def get_namespace(
         self, namespace: str, lowercase: bool = True, trim_namespace: bool = True
-    ) -> t.Dict[str, t.Any]:
+    ) -> dict[str, t.Any]:
         """Returns a dictionary containing a subset of configuration options
         that match the specified namespace/prefix. Example usage::
 
diff --git a/src/flask/ctx.py b/src/flask/ctx.py
index ca28449..b37e4e0 100644
--- a/src/flask/ctx.py
+++ b/src/flask/ctx.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import contextvars
 import sys
 import typing as t
@@ -60,7 +62,7 @@ class _AppCtxGlobals:
         except KeyError:
             raise AttributeError(name) from None
 
-    def get(self, name: str, default: t.Optional[t.Any] = None) -> t.Any:
+    def get(self, name: str, default: t.Any | None = None) -> t.Any:
         """Get an attribute by name, or a default value. Like
         :meth:`dict.get`.
 
@@ -233,18 +235,18 @@ class AppContext:
     running CLI commands.
     """
 
-    def __init__(self, app: "Flask") -> None:
+    def __init__(self, app: Flask) -> None:
         self.app = app
         self.url_adapter = app.create_url_adapter(None)
         self.g: _AppCtxGlobals = app.app_ctx_globals_class()
-        self._cv_tokens: t.List[contextvars.Token] = []
+        self._cv_tokens: list[contextvars.Token] = []
 
     def push(self) -> None:
         """Binds the app context to the current context."""
         self._cv_tokens.append(_cv_app.set(self))
-        appcontext_pushed.send(self.app)
+        appcontext_pushed.send(self.app, _async_wrapper=self.app.ensure_sync)
 
-    def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None:  # type: ignore
+    def pop(self, exc: BaseException | None = _sentinel) -> None:  # type: ignore
         """Pops the app context."""
         try:
             if len(self._cv_tokens) == 1:
@@ -260,17 +262,17 @@ class AppContext:
                 f"Popped wrong app context. ({ctx!r} instead of {self!r})"
             )
 
-        appcontext_popped.send(self.app)
+        appcontext_popped.send(self.app, _async_wrapper=self.app.ensure_sync)
 
-    def __enter__(self) -> "AppContext":
+    def __enter__(self) -> AppContext:
         self.push()
         return self
 
     def __exit__(
         self,
-        exc_type: t.Optional[type],
-        exc_value: t.Optional[BaseException],
-        tb: t.Optional[TracebackType],
+        exc_type: type | None,
+        exc_value: BaseException | None,
+        tb: TracebackType | None,
     ) -> None:
         self.pop(exc_value)
 
@@ -299,31 +301,31 @@ class RequestContext:
 
     def __init__(
         self,
-        app: "Flask",
+        app: Flask,
         environ: dict,
-        request: t.Optional["Request"] = None,
-        session: t.Optional["SessionMixin"] = None,
+        request: Request | None = None,
+        session: SessionMixin | None = None,
     ) -> None:
         self.app = app
         if request is None:
             request = app.request_class(environ)
-            request.json_module = app.json  # type: ignore[misc]
+            request.json_module = app.json
         self.request: Request = request
         self.url_adapter = None
         try:
             self.url_adapter = app.create_url_adapter(self.request)
         except HTTPException as e:
             self.request.routing_exception = e
-        self.flashes: t.Optional[t.List[t.Tuple[str, str]]] = None
-        self.session: t.Optional["SessionMixin"] = session
+        self.flashes: list[tuple[str, str]] | None = None
+        self.session: SessionMixin | None = session
         # Functions that should be executed after the request on the response
         # object.  These will be called before the regular "after_request"
         # functions.
-        self._after_request_functions: t.List[ft.AfterRequestCallable] = []
+        self._after_request_functions: list[ft.AfterRequestCallable] = []
 
-        self._cv_tokens: t.List[t.Tuple[contextvars.Token, t.Optional[AppContext]]] = []
+        self._cv_tokens: list[tuple[contextvars.Token, AppContext | None]] = []
 
-    def copy(self) -> "RequestContext":
+    def copy(self) -> RequestContext:
         """Creates a copy of this request context with the same request object.
         This can be used to move a request context to a different greenlet.
         Because the actual request object is the same this cannot be used to
@@ -382,7 +384,7 @@ class RequestContext:
         if self.url_adapter is not None:
             self.match_request()
 
-    def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None:  # type: ignore
+    def pop(self, exc: BaseException | None = _sentinel) -> None:  # type: ignore
         """Pops the request context and unbinds it by doing that.  This will
         also trigger the execution of functions registered by the
         :meth:`~flask.Flask.teardown_request` decorator.
@@ -419,15 +421,15 @@ class RequestContext:
                     f"Popped wrong request context. ({ctx!r} instead of {self!r})"
                 )
 
-    def __enter__(self) -> "RequestContext":
+    def __enter__(self) -> RequestContext:
         self.push()
         return self
 
     def __exit__(
         self,
-        exc_type: t.Optional[type],
-        exc_value: t.Optional[BaseException],
-        tb: t.Optional[TracebackType],
+        exc_type: type | None,
+        exc_value: BaseException | None,
+        tb: TracebackType | None,
     ) -> None:
         self.pop(exc_value)
 
diff --git a/src/flask/debughelpers.py b/src/flask/debughelpers.py
index b063989..6061441 100644
--- a/src/flask/debughelpers.py
+++ b/src/flask/debughelpers.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import typing as t
 
 from .app import Flask
diff --git a/src/flask/globals.py b/src/flask/globals.py
index b230ef7..e9cd4ac 100644
--- a/src/flask/globals.py
+++ b/src/flask/globals.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import typing as t
 from contextvars import ContextVar
 
@@ -17,30 +19,17 @@ class _FakeStack:
         self.name = name
         self.cv = cv
 
-    def _warn(self):
+    @property
+    def top(self) -> t.Any | None:
         import warnings
 
         warnings.warn(
-            f"'_{self.name}_ctx_stack' is deprecated and will be"
-            " removed in Flask 2.3. Use 'g' to store data, or"
-            f" '{self.name}_ctx' to access the current context.",
+            f"'_{self.name}_ctx_stack' is deprecated and will be removed in Flask 2.4."
+            f" Use 'g' to store data, or '{self.name}_ctx' to access the current"
+            " context.",
             DeprecationWarning,
-            stacklevel=3,
+            stacklevel=2,
         )
-
-    def push(self, obj: t.Any) -> None:
-        self._warn()
-        self.cv.set(obj)
-
-    def pop(self) -> t.Any:
-        self._warn()
-        ctx = self.cv.get(None)
-        self.cv.set(None)
-        return ctx
-
-    @property
-    def top(self) -> t.Optional[t.Any]:
-        self._warn()
         return self.cv.get(None)
 
 
@@ -51,15 +40,15 @@ This typically means that you attempted to use functionality that needed
 the current application. To solve this, set up an application context
 with app.app_context(). See the documentation for more information.\
 """
-_cv_app: ContextVar["AppContext"] = ContextVar("flask.app_ctx")
+_cv_app: ContextVar[AppContext] = ContextVar("flask.app_ctx")
 __app_ctx_stack = _FakeStack("app", _cv_app)
-app_ctx: "AppContext" = LocalProxy(  # type: ignore[assignment]
+app_ctx: AppContext = LocalProxy(  # type: ignore[assignment]
     _cv_app, unbound_message=_no_app_msg
 )
-current_app: "Flask" = LocalProxy(  # type: ignore[assignment]
+current_app: Flask = LocalProxy(  # type: ignore[assignment]
     _cv_app, "app", unbound_message=_no_app_msg
 )
-g: "_AppCtxGlobals" = LocalProxy(  # type: ignore[assignment]
+g: _AppCtxGlobals = LocalProxy(  # type: ignore[assignment]
     _cv_app, "g", unbound_message=_no_app_msg
 )
 
@@ -70,15 +59,15 @@ This typically means that you attempted to use functionality that needed
 an active HTTP request. Consult the documentation on testing for
 information about how to avoid this problem.\
 """
-_cv_request: ContextVar["RequestContext"] = ContextVar("flask.request_ctx")
+_cv_request: ContextVar[RequestContext] = ContextVar("flask.request_ctx")
 __request_ctx_stack = _FakeStack("request", _cv_request)
-request_ctx: "RequestContext" = LocalProxy(  # type: ignore[assignment]
+request_ctx: RequestContext = LocalProxy(  # type: ignore[assignment]
     _cv_request, unbound_message=_no_req_msg
 )
-request: "Request" = LocalProxy(  # type: ignore[assignment]
+request: Request = LocalProxy(  # type: ignore[assignment]
     _cv_request, "request", unbound_message=_no_req_msg
 )
-session: "SessionMixin" = LocalProxy(  # type: ignore[assignment]
+session: SessionMixin = LocalProxy(  # type: ignore[assignment]
     _cv_request, "session", unbound_message=_no_req_msg
 )
 
@@ -88,7 +77,7 @@ def __getattr__(name: str) -> t.Any:
         import warnings
 
         warnings.warn(
-            "'_app_ctx_stack' is deprecated and will be remoevd in Flask 2.3.",
+            "'_app_ctx_stack' is deprecated and will be removed in Flask 2.4.",
             DeprecationWarning,
             stacklevel=2,
         )
@@ -98,7 +87,7 @@ def __getattr__(name: str) -> t.Any:
         import warnings
 
         warnings.warn(
-            "'_request_ctx_stack' is deprecated and will be remoevd in Flask 2.3.",
+            "'_request_ctx_stack' is deprecated and will be removed in Flask 2.4.",
             DeprecationWarning,
             stacklevel=2,
         )
diff --git a/src/flask/helpers.py b/src/flask/helpers.py
index 15990d0..61a0f81 100644
--- a/src/flask/helpers.py
+++ b/src/flask/helpers.py
@@ -1,8 +1,11 @@
+from __future__ import annotations
+
 import os
 import pkgutil
 import socket
 import sys
 import typing as t
+import warnings
 from datetime import datetime
 from functools import lru_cache
 from functools import update_wrapper
@@ -22,26 +25,6 @@ from .signals import message_flashed
 if t.TYPE_CHECKING:  # pragma: no cover
     from werkzeug.wrappers import Response as BaseResponse
     from .wrappers import Response
-    import typing_extensions as te
-
-
-def get_env() -> str:
-    """Get the environment the app is running in, indicated by the
-    :envvar:`FLASK_ENV` environment variable. The default is
-    ``'production'``.
-
-    .. deprecated:: 2.2
-        Will be removed in Flask 2.3.
-    """
-    import warnings
-
-    warnings.warn(
-        "'FLASK_ENV' and 'get_env' are deprecated and will be removed"
-        " in Flask 2.3. Use 'FLASK_DEBUG' instead.",
-        DeprecationWarning,
-        stacklevel=2,
-    )
-    return os.environ.get("FLASK_ENV") or "production"
 
 
 def get_debug_flag() -> bool:
@@ -49,21 +32,7 @@ def get_debug_flag() -> bool:
     :envvar:`FLASK_DEBUG` environment variable. The default is ``False``.
     """
     val = os.environ.get("FLASK_DEBUG")
-
-    if not val:
-        env = os.environ.get("FLASK_ENV")
-
-        if env is not None:
-            print(
-                "'FLASK_ENV' is deprecated and will not be used in"
-                " Flask 2.3. Use 'FLASK_DEBUG' instead.",
-                file=sys.stderr,
-            )
-            return env == "development"
-
-        return False
-
-    return val.lower() not in {"0", "false", "no"}
+    return bool(val and val.lower() not in {"0", "false", "no"})
 
 
 def get_load_dotenv(default: bool = True) -> bool:
@@ -82,9 +51,9 @@ def get_load_dotenv(default: bool = True) -> bool:
 
 
 def stream_with_context(
-    generator_or_function: t.Union[
-        t.Iterator[t.AnyStr], t.Callable[..., t.Iterator[t.AnyStr]]
-    ]
+    generator_or_function: (
+        t.Iterator[t.AnyStr] | t.Callable[..., t.Iterator[t.AnyStr]]
+    )
 ) -> t.Iterator[t.AnyStr]:
     """Request contexts disappear when the response is started on the server.
     This is done for efficiency reasons and to make it less likely to encounter
@@ -149,7 +118,7 @@ def stream_with_context(
                 yield from gen
             finally:
                 if hasattr(gen, "close"):
-                    gen.close()  # type: ignore
+                    gen.close()
 
     # The trick is to start the generator.  Then the code execution runs until
     # the first dummy None is yielded at which point the context was already
@@ -160,7 +129,7 @@ def stream_with_context(
     return wrapped_g
 
 
-def make_response(*args: t.Any) -> "Response":
+def make_response(*args: t.Any) -> Response:
     """Sometimes it is necessary to set additional headers in a view.  Because
     views do not have to return response objects but can return a value that
     is converted into a response object by Flask itself, it becomes tricky to
@@ -212,10 +181,10 @@ def make_response(*args: t.Any) -> "Response":
 def url_for(
     endpoint: str,
     *,
-    _anchor: t.Optional[str] = None,
-    _method: t.Optional[str] = None,
-    _scheme: t.Optional[str] = None,
-    _external: t.Optional[bool] = None,
+    _anchor: str | None = None,
+    _method: str | None = None,
+    _scheme: str | None = None,
+    _external: bool | None = None,
     **values: t.Any,
 ) -> str:
     """Generate a URL to the given endpoint with the given values.
@@ -264,8 +233,8 @@ def url_for(
 
 
 def redirect(
-    location: str, code: int = 302, Response: t.Optional[t.Type["BaseResponse"]] = None
-) -> "BaseResponse":
+    location: str, code: int = 302, Response: type[BaseResponse] | None = None
+) -> BaseResponse:
     """Create a redirect response object.
 
     If :data:`~flask.current_app` is available, it will use its
@@ -287,9 +256,7 @@ def redirect(
     return _wz_redirect(location, code=code, Response=Response)
 
 
-def abort(  # type: ignore[misc]
-    code: t.Union[int, "BaseResponse"], *args: t.Any, **kwargs: t.Any
-) -> "te.NoReturn":
+def abort(code: int | BaseResponse, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
     """Raise an :exc:`~werkzeug.exceptions.HTTPException` for the given
     status code.
 
@@ -359,8 +326,10 @@ def flash(message: str, category: str = "message") -> None:
     flashes = session.get("_flashes", [])
     flashes.append((category, message))
     session["_flashes"] = flashes
+    app = current_app._get_current_object()  # type: ignore
     message_flashed.send(
-        current_app._get_current_object(),  # type: ignore
+        app,
+        _async_wrapper=app.ensure_sync,
         message=message,
         category=category,
     )
@@ -368,7 +337,7 @@ def flash(message: str, category: str = "message") -> None:
 
 def get_flashed_messages(
     with_categories: bool = False, category_filter: t.Iterable[str] = ()
-) -> t.Union[t.List[str], t.List[t.Tuple[str, str]]]:
+) -> list[str] | list[tuple[str, str]]:
     """Pulls all flashed messages from the session and returns them.
     Further calls in the same request to the function will return
     the same messages.  By default just the messages are returned,
@@ -408,7 +377,7 @@ def get_flashed_messages(
     return flashes
 
 
-def _prepare_send_file_kwargs(**kwargs: t.Any) -> t.Dict[str, t.Any]:
+def _prepare_send_file_kwargs(**kwargs: t.Any) -> dict[str, t.Any]:
     if kwargs.get("max_age") is None:
         kwargs["max_age"] = current_app.get_send_file_max_age
 
@@ -422,17 +391,15 @@ def _prepare_send_file_kwargs(**kwargs: t.Any) -> t.Dict[str, t.Any]:
 
 
 def send_file(
-    path_or_file: t.Union[os.PathLike, str, t.BinaryIO],
-    mimetype: t.Optional[str] = None,
+    path_or_file: os.PathLike | str | t.BinaryIO,
+    mimetype: str | None = None,
     as_attachment: bool = False,
-    download_name: t.Optional[str] = None,
+    download_name: str | None = None,
     conditional: bool = True,
-    etag: t.Union[bool, str] = True,
-    last_modified: t.Optional[t.Union[datetime, int, float]] = None,
-    max_age: t.Optional[
-        t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]]
-    ] = None,
-) -> "Response":
+    etag: bool | str = True,
+    last_modified: datetime | int | float | None = None,
+    max_age: None | (int | t.Callable[[str | None], int | None]) = None,
+) -> Response:
     """Send the contents of a file to the client.
 
     The first argument can be a file path or a file-like object. Paths
@@ -550,10 +517,10 @@ def send_file(
 
 
 def send_from_directory(
-    directory: t.Union[os.PathLike, str],
-    path: t.Union[os.PathLike, str],
+    directory: os.PathLike | str,
+    path: os.PathLike | str,
     **kwargs: t.Any,
-) -> "Response":
+) -> Response:
     """Send a file from within a directory using :func:`send_file`.
 
     .. code-block:: python
@@ -617,7 +584,7 @@ def get_root_path(import_name: str) -> str:
         return os.getcwd()
 
     if hasattr(loader, "get_filename"):
-        filepath = loader.get_filename(import_name)  # type: ignore
+        filepath = loader.get_filename(import_name)
     else:
         # Fall back to imports.
         __import__(import_name)
@@ -646,6 +613,10 @@ class locked_cached_property(werkzeug.utils.cached_property):
     :class:`werkzeug.utils.cached_property` except access uses a lock
     for thread safety.
 
+    .. deprecated:: 2.3
+        Will be removed in Flask 2.4. Use a lock inside the decorated function if
+        locking is needed.
+
     .. versionchanged:: 2.0
         Inherits from Werkzeug's ``cached_property`` (and ``property``).
     """
@@ -653,9 +624,17 @@ class locked_cached_property(werkzeug.utils.cached_property):
     def __init__(
         self,
         fget: t.Callable[[t.Any], t.Any],
-        name: t.Optional[str] = None,
-        doc: t.Optional[str] = None,
+        name: str | None = None,
+        doc: str | None = None,
     ) -> None:
+        import warnings
+
+        warnings.warn(
+            "'locked_cached_property' is deprecated and will be removed in Flask 2.4."
+            " Use a lock inside the decorated function if locking is needed.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
         super().__init__(fget, name=name, doc=doc)
         self.lock = RLock()
 
@@ -683,7 +662,16 @@ def is_ip(value: str) -> bool:
 
     :return: True if string is an IP address
     :rtype: bool
+
+    .. deprecated:: 2.3
+        Will be removed in Flask 2.4.
     """
+    warnings.warn(
+        "The 'is_ip' function is deprecated and will be removed in Flask 2.4.",
+        DeprecationWarning,
+        stacklevel=2,
+    )
+
     for family in (socket.AF_INET, socket.AF_INET6):
         try:
             socket.inet_pton(family, value)
@@ -696,8 +684,8 @@ def is_ip(value: str) -> bool:
 
 
 @lru_cache(maxsize=None)
-def _split_blueprint_path(name: str) -> t.List[str]:
-    out: t.List[str] = [name]
+def _split_blueprint_path(name: str) -> list[str]:
+    out: list[str] = [name]
 
     if "." in name:
         out.extend(_split_blueprint_path(name.rpartition(".")[0]))
diff --git a/src/flask/json/__init__.py b/src/flask/json/__init__.py
index 65d8829..f15296f 100644
--- a/src/flask/json/__init__.py
+++ b/src/flask/json/__init__.py
@@ -3,85 +3,14 @@ from __future__ import annotations
 import json as _json
 import typing as t
 
-from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps
-
 from ..globals import current_app
 from .provider import _default
 
 if t.TYPE_CHECKING:  # pragma: no cover
-    from ..app import Flask
     from ..wrappers import Response
 
 
-class JSONEncoder(_json.JSONEncoder):
-    """The default JSON encoder. Handles extra types compared to the
-    built-in :class:`json.JSONEncoder`.
-
-    -   :class:`datetime.datetime` and :class:`datetime.date` are
-        serialized to :rfc:`822` strings. This is the same as the HTTP
-        date format.
-    -   :class:`decimal.Decimal` is serialized to a string.
-    -   :class:`uuid.UUID` is serialized to a string.
-    -   :class:`dataclasses.dataclass` is passed to
-        :func:`dataclasses.asdict`.
-    -   :class:`~markupsafe.Markup` (or any object with a ``__html__``
-        method) will call the ``__html__`` method to get a string.
-
-    Assign a subclass of this to :attr:`flask.Flask.json_encoder` or
-    :attr:`flask.Blueprint.json_encoder` to override the default.
-
-    .. deprecated:: 2.2
-        Will be removed in Flask 2.3. Use ``app.json`` instead.
-    """
-
-    def __init__(self, **kwargs) -> None:
-        import warnings
-
-        warnings.warn(
-            "'JSONEncoder' is deprecated and will be removed in"
-            " Flask 2.3. Use 'Flask.json' to provide an alternate"
-            " JSON implementation instead.",
-            DeprecationWarning,
-            stacklevel=3,
-        )
-        super().__init__(**kwargs)
-
-    def default(self, o: t.Any) -> t.Any:
-        """Convert ``o`` to a JSON serializable type. See
-        :meth:`json.JSONEncoder.default`. Python does not support
-        overriding how basic types like ``str`` or ``list`` are
-        serialized, they are handled before this method.
-        """
-        return _default(o)
-
-
-class JSONDecoder(_json.JSONDecoder):
-    """The default JSON decoder.
-
-    This does not change any behavior from the built-in
-    :class:`json.JSONDecoder`.
-
-    Assign a subclass of this to :attr:`flask.Flask.json_decoder` or
-    :attr:`flask.Blueprint.json_decoder` to override the default.
-
-    .. deprecated:: 2.2
-        Will be removed in Flask 2.3. Use ``app.json`` instead.
-    """
-
-    def __init__(self, **kwargs) -> None:
-        import warnings
-
-        warnings.warn(
-            "'JSONDecoder' is deprecated and will be removed in"
-            " Flask 2.3. Use 'Flask.json' to provide an alternate"
-            " JSON implementation instead.",
-            DeprecationWarning,
-            stacklevel=3,
-        )
-        super().__init__(**kwargs)
-
-
-def dumps(obj: t.Any, *, app: Flask | None = None, **kwargs: t.Any) -> str:
+def dumps(obj: t.Any, **kwargs: t.Any) -> str:
     """Serialize data as JSON.
 
     If :data:`~flask.current_app` is available, it will use its
@@ -91,13 +20,13 @@ def dumps(obj: t.Any, *, app: Flask | None = None, **kwargs: t.Any) -> str:
     :param obj: The data to serialize.
     :param kwargs: Arguments passed to the ``dumps`` implementation.
 
+    .. versionchanged:: 2.3
+        The ``app`` parameter was removed.
+
     .. versionchanged:: 2.2
         Calls ``current_app.json.dumps``, allowing an app to override
         the behavior.
 
-    .. versionchanged:: 2.2
-        The ``app`` parameter will be removed in Flask 2.3.
-
     .. versionchanged:: 2.0.2
         :class:`decimal.Decimal` is supported by converting to a string.
 
@@ -108,28 +37,14 @@ def dumps(obj: t.Any, *, app: Flask | None = None, **kwargs: t.Any) -> str:
         ``app`` can be passed directly, rather than requiring an app
         context for configuration.
     """
-    if app is not None:
-        import warnings
-
-        warnings.warn(
-            "The 'app' parameter is deprecated and will be removed in"
-            " Flask 2.3. Call 'app.json.dumps' directly instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-    else:
-        app = current_app
-
-    if app:
-        return app.json.dumps(obj, **kwargs)
+    if current_app:
+        return current_app.json.dumps(obj, **kwargs)
 
     kwargs.setdefault("default", _default)
     return _json.dumps(obj, **kwargs)
 
 
-def dump(
-    obj: t.Any, fp: t.IO[str], *, app: Flask | None = None, **kwargs: t.Any
-) -> None:
+def dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None:
     """Serialize data as JSON and write to a file.
 
     If :data:`~flask.current_app` is available, it will use its
@@ -141,37 +56,25 @@ def dump(
         encoding to be valid JSON.
     :param kwargs: Arguments passed to the ``dump`` implementation.
 
+    .. versionchanged:: 2.3
+        The ``app`` parameter was removed.
+
     .. versionchanged:: 2.2
         Calls ``current_app.json.dump``, allowing an app to override
         the behavior.
 
-    .. versionchanged:: 2.2
-        The ``app`` parameter will be removed in Flask 2.3.
-
     .. versionchanged:: 2.0
         Writing to a binary file, and the ``encoding`` argument, will be
         removed in Flask 2.1.
     """
-    if app is not None:
-        import warnings
-
-        warnings.warn(
-            "The 'app' parameter is deprecated and will be removed in"
-            " Flask 2.3. Call 'app.json.dump' directly instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-    else:
-        app = current_app
-
-    if app:
-        app.json.dump(obj, fp, **kwargs)
+    if current_app:
+        current_app.json.dump(obj, fp, **kwargs)
     else:
         kwargs.setdefault("default", _default)
         _json.dump(obj, fp, **kwargs)
 
 
-def loads(s: str | bytes, *, app: Flask | None = None, **kwargs: t.Any) -> t.Any:
+def loads(s: str | bytes, **kwargs: t.Any) -> t.Any:
     """Deserialize data as JSON.
 
     If :data:`~flask.current_app` is available, it will use its
@@ -181,13 +84,13 @@ def loads(s: str | bytes, *, app: Flask | None = None, **kwargs: t.Any) -> t.Any
     :param s: Text or UTF-8 bytes.
     :param kwargs: Arguments passed to the ``loads`` implementation.
 
+    .. versionchanged:: 2.3
+        The ``app`` parameter was removed.
+
     .. versionchanged:: 2.2
         Calls ``current_app.json.loads``, allowing an app to override
         the behavior.
 
-    .. versionchanged:: 2.2
-        The ``app`` parameter will be removed in Flask 2.3.
-
     .. versionchanged:: 2.0
         ``encoding`` will be removed in Flask 2.1. The data must be a
         string or UTF-8 bytes.
@@ -196,25 +99,13 @@ def loads(s: str | bytes, *, app: Flask | None = None, **kwargs: t.Any) -> t.Any
         ``app`` can be passed directly, rather than requiring an app
         context for configuration.
     """
-    if app is not None:
-        import warnings
-
-        warnings.warn(
-            "The 'app' parameter is deprecated and will be removed in"
-            " Flask 2.3. Call 'app.json.loads' directly instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-    else:
-        app = current_app
-
-    if app:
-        return app.json.loads(s, **kwargs)
+    if current_app:
+        return current_app.json.loads(s, **kwargs)
 
     return _json.loads(s, **kwargs)
 
 
-def load(fp: t.IO[t.AnyStr], *, app: Flask | None = None, **kwargs: t.Any) -> t.Any:
+def load(fp: t.IO[t.AnyStr], **kwargs: t.Any) -> t.Any:
     """Deserialize data as JSON read from a file.
 
     If :data:`~flask.current_app` is available, it will use its
@@ -224,6 +115,9 @@ def load(fp: t.IO[t.AnyStr], *, app: Flask | None = None, **kwargs: t.Any) -> t.
     :param fp: A file opened for reading text or UTF-8 bytes.
     :param kwargs: Arguments passed to the ``load`` implementation.
 
+    .. versionchanged:: 2.3
+        The ``app`` parameter was removed.
+
     .. versionchanged:: 2.2
         Calls ``current_app.json.load``, allowing an app to override
         the behavior.
@@ -235,78 +129,12 @@ def load(fp: t.IO[t.AnyStr], *, app: Flask | None = None, **kwargs: t.Any) -> t.
         ``encoding`` will be removed in Flask 2.1. The file must be text
         mode, or binary mode with UTF-8 bytes.
     """
-    if app is not None:
-        import warnings
-
-        warnings.warn(
-            "The 'app' parameter is deprecated and will be removed in"
-            " Flask 2.3. Call 'app.json.load' directly instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-    else:
-        app = current_app
-
-    if app:
-        return app.json.load(fp, **kwargs)
+    if current_app:
+        return current_app.json.load(fp, **kwargs)
 
     return _json.load(fp, **kwargs)
 
 
-def htmlsafe_dumps(obj: t.Any, **kwargs: t.Any) -> str:
-    """Serialize an object to a string of JSON with :func:`dumps`, then
-    replace HTML-unsafe characters with Unicode escapes and mark the
-    result safe with :class:`~markupsafe.Markup`.
-
-    This is available in templates as the ``|tojson`` filter.
-
-    The returned string is safe to render in HTML documents and
-    ``<script>`` tags. The exception is in HTML attributes that are
-    double quoted; either use single quotes or the ``|forceescape``
-    filter.
-
-    .. deprecated:: 2.2
-        Will be removed in Flask 2.3. This is built-in to Jinja now.
-
-    .. versionchanged:: 2.0
-        Uses :func:`jinja2.utils.htmlsafe_json_dumps`. The returned
-        value is marked safe by wrapping in :class:`~markupsafe.Markup`.
-
-    .. versionchanged:: 0.10
-        Single quotes are escaped, making this safe to use in HTML,
-        ``<script>`` tags, and single-quoted attributes without further
-        escaping.
-    """
-    import warnings
-
-    warnings.warn(
-        "'htmlsafe_dumps' is deprecated and will be removed in Flask"
-        " 2.3. Use 'jinja2.utils.htmlsafe_json_dumps' instead.",
-        DeprecationWarning,
-        stacklevel=2,
-    )
-    return _jinja_htmlsafe_dumps(obj, dumps=dumps, **kwargs)
-
-
-def htmlsafe_dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None:
-    """Serialize an object to JSON written to a file object, replacing
-    HTML-unsafe characters with Unicode escapes. See
-    :func:`htmlsafe_dumps` and :func:`dumps`.
-
-    .. deprecated:: 2.2
-        Will be removed in Flask 2.3.
-    """
-    import warnings
-
-    warnings.warn(
-        "'htmlsafe_dump' is deprecated and will be removed in Flask"
-        " 2.3. Use 'jinja2.utils.htmlsafe_json_dumps' instead.",
-        DeprecationWarning,
-        stacklevel=2,
-    )
-    fp.write(htmlsafe_dumps(obj, **kwargs))
-
-
 def jsonify(*args: t.Any, **kwargs: t.Any) -> Response:
     """Serialize the given arguments as JSON, and return a
     :class:`~flask.Response` object with the ``application/json``
diff --git a/src/flask/json/provider.py b/src/flask/json/provider.py
index cb6aae8..0edd3d5 100644
--- a/src/flask/json/provider.py
+++ b/src/flask/json/provider.py
@@ -10,8 +10,6 @@ from datetime import date
 
 from werkzeug.http import http_date
 
-from ..globals import request
-
 if t.TYPE_CHECKING:  # pragma: no cover
     from ..app import Flask
     from ..wrappers import Response
@@ -74,7 +72,7 @@ class JSONProvider:
         return self.loads(fp.read(), **kwargs)
 
     def _prepare_response_obj(
-        self, args: t.Tuple[t.Any, ...], kwargs: t.Dict[str, t.Any]
+        self, args: tuple[t.Any, ...], kwargs: dict[str, t.Any]
     ) -> t.Any:
         if args and kwargs:
             raise TypeError("app.json.response() takes either args or kwargs, not both")
@@ -176,57 +174,9 @@ class DefaultJSONProvider(JSONProvider):
         :param obj: The data to serialize.
         :param kwargs: Passed to :func:`json.dumps`.
         """
-        cls = self._app._json_encoder
-        bp = self._app.blueprints.get(request.blueprint) if request else None
-
-        if bp is not None and bp._json_encoder is not None:
-            cls = bp._json_encoder
-
-        if cls is not None:
-            import warnings
-
-            warnings.warn(
-                "Setting 'json_encoder' on the app or a blueprint is"
-                " deprecated and will be removed in Flask 2.3."
-                " Customize 'app.json' instead.",
-                DeprecationWarning,
-            )
-            kwargs.setdefault("cls", cls)
-
-            if "default" not in cls.__dict__:
-                kwargs.setdefault("default", self.default)
-        else:
-            kwargs.setdefault("default", self.default)
-
-        ensure_ascii = self._app.config["JSON_AS_ASCII"]
-        sort_keys = self._app.config["JSON_SORT_KEYS"]
-
-        if ensure_ascii is not None:
-            import warnings
-
-            warnings.warn(
-                "The 'JSON_AS_ASCII' config key is deprecated and will"
-                " be removed in Flask 2.3. Set 'app.json.ensure_ascii'"
-                " instead.",
-                DeprecationWarning,
-            )
-        else:
-            ensure_ascii = self.ensure_ascii
-
-        if sort_keys is not None:
-            import warnings
-
-            warnings.warn(
-                "The 'JSON_SORT_KEYS' config key is deprecated and will"
-                " be removed in Flask 2.3. Set 'app.json.sort_keys'"
-                " instead.",
-                DeprecationWarning,
-            )
-        else:
-            sort_keys = self.sort_keys
-
-        kwargs.setdefault("ensure_ascii", ensure_ascii)
-        kwargs.setdefault("sort_keys", sort_keys)
+        kwargs.setdefault("default", self.default)
+        kwargs.setdefault("ensure_ascii", self.ensure_ascii)
+        kwargs.setdefault("sort_keys", self.sort_keys)
         return json.dumps(obj, **kwargs)
 
     def loads(self, s: str | bytes, **kwargs: t.Any) -> t.Any:
@@ -235,23 +185,6 @@ class DefaultJSONProvider(JSONProvider):
         :param s: Text or UTF-8 bytes.
         :param kwargs: Passed to :func:`json.loads`.
         """
-        cls = self._app._json_decoder
-        bp = self._app.blueprints.get(request.blueprint) if request else None
-
-        if bp is not None and bp._json_decoder is not None:
-            cls = bp._json_decoder
-
-        if cls is not None:
-            import warnings
-
-            warnings.warn(
-                "Setting 'json_decoder' on the app or a blueprint is"
-                " deprecated and will be removed in Flask 2.3."
-                " Customize 'app.json' instead.",
-                DeprecationWarning,
-            )
-            kwargs.setdefault("cls", cls)
-
         return json.loads(s, **kwargs)
 
     def response(self, *args: t.Any, **kwargs: t.Any) -> Response:
@@ -271,40 +204,13 @@ class DefaultJSONProvider(JSONProvider):
         :param kwargs: Treat as a dict to serialize.
         """
         obj = self._prepare_response_obj(args, kwargs)
-        dump_args: t.Dict[str, t.Any] = {}
-        pretty = self._app.config["JSONIFY_PRETTYPRINT_REGULAR"]
-        mimetype = self._app.config["JSONIFY_MIMETYPE"]
-
-        if pretty is not None:
-            import warnings
-
-            warnings.warn(
-                "The 'JSONIFY_PRETTYPRINT_REGULAR' config key is"
-                " deprecated and will be removed in Flask 2.3. Set"
-                " 'app.json.compact' instead.",
-                DeprecationWarning,
-            )
-            compact: bool | None = not pretty
-        else:
-            compact = self.compact
+        dump_args: dict[str, t.Any] = {}
 
-        if (compact is None and self._app.debug) or compact is False:
+        if (self.compact is None and self._app.debug) or self.compact is False:
             dump_args.setdefault("indent", 2)
         else:
             dump_args.setdefault("separators", (",", ":"))
 
-        if mimetype is not None:
-            import warnings
-
-            warnings.warn(
-                "The 'JSONIFY_MIMETYPE' config key is deprecated and"
-                " will be removed in Flask 2.3. Set 'app.json.mimetype'"
-                " instead.",
-                DeprecationWarning,
-            )
-        else:
-            mimetype = self.mimetype
-
         return self._app.response_class(
-            f"{self.dumps(obj, **dump_args)}\n", mimetype=mimetype
+            f"{self.dumps(obj, **dump_args)}\n", mimetype=self.mimetype
         )
diff --git a/src/flask/json/tag.py b/src/flask/json/tag.py
index 97f365a..91cc441 100644
--- a/src/flask/json/tag.py
+++ b/src/flask/json/tag.py
@@ -40,6 +40,8 @@ be processed before ``dict``.
 
     app.session_interface.serializer.register(TagOrderedDict, index=0)
 """
+from __future__ import annotations
+
 import typing as t
 from base64 import b64decode
 from base64 import b64encode
@@ -61,9 +63,9 @@ class JSONTag:
 
     #: The tag to mark the serialized object with. If ``None``, this tag is
     #: only used as an intermediate step during tagging.
-    key: t.Optional[str] = None
+    key: str | None = None
 
-    def __init__(self, serializer: "TaggedJSONSerializer") -> None:
+    def __init__(self, serializer: TaggedJSONSerializer) -> None:
         """Create a tagger for the given serializer."""
         self.serializer = serializer
 
@@ -244,17 +246,17 @@ class TaggedJSONSerializer:
     ]
 
     def __init__(self) -> None:
-        self.tags: t.Dict[str, JSONTag] = {}
-        self.order: t.List[JSONTag] = []
+        self.tags: dict[str, JSONTag] = {}
+        self.order: list[JSONTag] = []
 
         for cls in self.default_tags:
             self.register(cls)
 
     def register(
         self,
-        tag_class: t.Type[JSONTag],
+        tag_class: type[JSONTag],
         force: bool = False,
-        index: t.Optional[int] = None,
+        index: int | None = None,
     ) -> None:
         """Register a new tag with this serializer.
 
@@ -283,7 +285,7 @@ class TaggedJSONSerializer:
         else:
             self.order.insert(index, tag)
 
-    def tag(self, value: t.Any) -> t.Dict[str, t.Any]:
+    def tag(self, value: t.Any) -> dict[str, t.Any]:
         """Convert a value to a tagged representation if necessary."""
         for tag in self.order:
             if tag.check(value):
@@ -291,7 +293,7 @@ class TaggedJSONSerializer:
 
         return value
 
-    def untag(self, value: t.Dict[str, t.Any]) -> t.Any:
+    def untag(self, value: dict[str, t.Any]) -> t.Any:
         """Convert a tagged representation back to the original type."""
         if len(value) != 1:
             return value
diff --git a/src/flask/logging.py b/src/flask/logging.py
index 8981b82..99f6be8 100644
--- a/src/flask/logging.py
+++ b/src/flask/logging.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import logging
 import sys
 import typing as t
@@ -50,7 +52,7 @@ default_handler.setFormatter(
 )
 
 
-def create_logger(app: "Flask") -> logging.Logger:
+def create_logger(app: Flask) -> logging.Logger:
     """Get the Flask app's logger and configure it if needed.
 
     The logger name will be the same as
diff --git a/src/flask/scaffold.py b/src/flask/scaffold.py
index 1530a11..6af6906 100644
--- a/src/flask/scaffold.py
+++ b/src/flask/scaffold.py
@@ -1,5 +1,6 @@
+from __future__ import annotations
+
 import importlib.util
-import json
 import os
 import pathlib
 import pkgutil
@@ -12,12 +13,12 @@ from functools import update_wrapper
 from jinja2 import FileSystemLoader
 from werkzeug.exceptions import default_exceptions
 from werkzeug.exceptions import HTTPException
+from werkzeug.utils import cached_property
 
 from . import typing as ft
 from .cli import AppGroup
 from .globals import current_app
 from .helpers import get_root_path
-from .helpers import locked_cached_property
 from .helpers import send_from_directory
 from .templating import _default_template_ctx_processor
 
@@ -71,30 +72,16 @@ class Scaffold:
     """
 
     name: str
-    _static_folder: t.Optional[str] = None
-    _static_url_path: t.Optional[str] = None
-
-    #: JSON encoder class used by :func:`flask.json.dumps`. If a
-    #: blueprint sets this, it will be used instead of the app's value.
-    #:
-    #: .. deprecated:: 2.2
-    #:      Will be removed in Flask 2.3.
-    json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None
-
-    #: JSON decoder class used by :func:`flask.json.loads`. If a
-    #: blueprint sets this, it will be used instead of the app's value.
-    #:
-    #: .. deprecated:: 2.2
-    #:      Will be removed in Flask 2.3.
-    json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None
+    _static_folder: str | None = None
+    _static_url_path: str | None = None
 
     def __init__(
         self,
         import_name: str,
-        static_folder: t.Optional[t.Union[str, os.PathLike]] = None,
-        static_url_path: t.Optional[str] = None,
-        template_folder: t.Optional[str] = None,
-        root_path: t.Optional[str] = None,
+        static_folder: str | os.PathLike | None = None,
+        static_url_path: str | None = None,
+        template_folder: str | os.PathLike | None = None,
+        root_path: str | None = None,
     ):
         #: The name of the package or module that this object belongs
         #: to. Do not change this once it is set by the constructor.
@@ -127,7 +114,7 @@ class Scaffold:
         #:
         #: This data structure is internal. It should not be modified
         #: directly and its format may change at any time.
-        self.view_functions: t.Dict[str, t.Callable] = {}
+        self.view_functions: dict[str, t.Callable] = {}
 
         #: A data structure of registered error handlers, in the format
         #: ``{scope: {code: {class: handler}}}``. The ``scope`` key is
@@ -142,9 +129,9 @@ class Scaffold:
         #:
         #: This data structure is internal. It should not be modified
         #: directly and its format may change at any time.
-        self.error_handler_spec: t.Dict[
+        self.error_handler_spec: dict[
             ft.AppOrBlueprintKey,
-            t.Dict[t.Optional[int], t.Dict[t.Type[Exception], ft.ErrorHandlerCallable]],
+            dict[int | None, dict[type[Exception], ft.ErrorHandlerCallable]],
         ] = defaultdict(lambda: defaultdict(dict))
 
         #: A data structure of functions to call at the beginning of
@@ -157,8 +144,8 @@ class Scaffold:
         #:
         #: This data structure is internal. It should not be modified
         #: directly and its format may change at any time.
-        self.before_request_funcs: t.Dict[
-            ft.AppOrBlueprintKey, t.List[ft.BeforeRequestCallable]
+        self.before_request_funcs: dict[
+            ft.AppOrBlueprintKey, list[ft.BeforeRequestCallable]
         ] = defaultdict(list)
 
         #: A data structure of functions to call at the end of each
@@ -171,8 +158,8 @@ class Scaffold:
         #:
         #: This data structure is internal. It should not be modified
         #: directly and its format may change at any time.
-        self.after_request_funcs: t.Dict[
-            ft.AppOrBlueprintKey, t.List[ft.AfterRequestCallable]
+        self.after_request_funcs: dict[
+            ft.AppOrBlueprintKey, list[ft.AfterRequestCallable]
         ] = defaultdict(list)
 
         #: A data structure of functions to call at the end of each
@@ -186,8 +173,8 @@ class Scaffold:
         #:
         #: This data structure is internal. It should not be modified
         #: directly and its format may change at any time.
-        self.teardown_request_funcs: t.Dict[
-            ft.AppOrBlueprintKey, t.List[ft.TeardownCallable]
+        self.teardown_request_funcs: dict[
+            ft.AppOrBlueprintKey, list[ft.TeardownCallable]
         ] = defaultdict(list)
 
         #: A data structure of functions to call to pass extra context
@@ -201,8 +188,8 @@ class Scaffold:
         #:
         #: This data structure is internal. It should not be modified
         #: directly and its format may change at any time.
-        self.template_context_processors: t.Dict[
-            ft.AppOrBlueprintKey, t.List[ft.TemplateContextProcessorCallable]
+        self.template_context_processors: dict[
+            ft.AppOrBlueprintKey, list[ft.TemplateContextProcessorCallable]
         ] = defaultdict(list, {None: [_default_template_ctx_processor]})
 
         #: A data structure of functions to call to modify the keyword
@@ -216,9 +203,9 @@ class Scaffold:
         #:
         #: This data structure is internal. It should not be modified
         #: directly and its format may change at any time.
-        self.url_value_preprocessors: t.Dict[
+        self.url_value_preprocessors: dict[
             ft.AppOrBlueprintKey,
-            t.List[ft.URLValuePreprocessorCallable],
+            list[ft.URLValuePreprocessorCallable],
         ] = defaultdict(list)
 
         #: A data structure of functions to call to modify the keyword
@@ -232,8 +219,8 @@ class Scaffold:
         #:
         #: This data structure is internal. It should not be modified
         #: directly and its format may change at any time.
-        self.url_default_functions: t.Dict[
-            ft.AppOrBlueprintKey, t.List[ft.URLDefaultCallable]
+        self.url_default_functions: dict[
+            ft.AppOrBlueprintKey, list[ft.URLDefaultCallable]
         ] = defaultdict(list)
 
     def __repr__(self) -> str:
@@ -243,7 +230,7 @@ class Scaffold:
         raise NotImplementedError
 
     @property
-    def static_folder(self) -> t.Optional[str]:
+    def static_folder(self) -> str | None:
         """The absolute path to the configured static folder. ``None``
         if no static folder is set.
         """
@@ -253,7 +240,7 @@ class Scaffold:
             return None
 
     @static_folder.setter
-    def static_folder(self, value: t.Optional[t.Union[str, os.PathLike]]) -> None:
+    def static_folder(self, value: str | os.PathLike | None) -> None:
         if value is not None:
             value = os.fspath(value).rstrip(r"\/")
 
@@ -268,7 +255,7 @@ class Scaffold:
         return self.static_folder is not None
 
     @property
-    def static_url_path(self) -> t.Optional[str]:
+    def static_url_path(self) -> str | None:
         """The URL prefix that the static route will be accessible from.
 
         If it was not configured during init, it is derived from
@@ -284,13 +271,13 @@ class Scaffold:
         return None
 
     @static_url_path.setter
-    def static_url_path(self, value: t.Optional[str]) -> None:
+    def static_url_path(self, value: str | None) -> None:
         if value is not None:
             value = value.rstrip("/")
 
         self._static_url_path = value
 
-    def get_send_file_max_age(self, filename: t.Optional[str]) -> t.Optional[int]:
+    def get_send_file_max_age(self, filename: str | None) -> int | None:
         """Used by :func:`send_file` to determine the ``max_age`` cache
         value for a given file path if it wasn't passed.
 
@@ -314,7 +301,7 @@ class Scaffold:
 
         return value
 
-    def send_static_file(self, filename: str) -> "Response":
+    def send_static_file(self, filename: str) -> Response:
         """The view function used to serve files from
         :attr:`static_folder`. A route is automatically registered for
         this view at :attr:`static_url_path` if :attr:`static_folder` is
@@ -332,8 +319,8 @@ class Scaffold:
             t.cast(str, self.static_folder), filename, max_age=max_age
         )
 
-    @locked_cached_property
-    def jinja_loader(self) -> t.Optional[FileSystemLoader]:
+    @cached_property
+    def jinja_loader(self) -> FileSystemLoader | None:
         """The Jinja loader for this object's templates. By default this
         is a class :class:`jinja2.loaders.FileSystemLoader` to
         :attr:`template_folder` if it is set.
@@ -455,9 +442,9 @@ class Scaffold:
     def add_url_rule(
         self,
         rule: str,
-        endpoint: t.Optional[str] = None,
-        view_func: t.Optional[ft.RouteCallable] = None,
-        provide_automatic_options: t.Optional[bool] = None,
+        endpoint: str | None = None,
+        view_func: ft.RouteCallable | None = None,
+        provide_automatic_options: bool | None = None,
         **options: t.Any,
     ) -> None:
         """Register a rule for routing incoming requests and building
@@ -561,6 +548,11 @@ class Scaffold:
         a non-``None`` value, the value is handled as if it was the
         return value from the view, and further request handling is
         stopped.
+
+        This is available on both app and blueprint objects. When used on an app, this
+        executes before every request. When used on a blueprint, this executes before
+        every request that the blueprint handles. To register with a blueprint and
+        execute before every request, use :meth:`.Blueprint.before_app_request`.
         """
         self.before_request_funcs.setdefault(None, []).append(f)
         return f
@@ -577,6 +569,11 @@ class Scaffold:
         ``after_request`` functions will not be called. Therefore, this
         should not be used for actions that must execute, such as to
         close resources. Use :meth:`teardown_request` for that.
+
+        This is available on both app and blueprint objects. When used on an app, this
+        executes after every request. When used on a blueprint, this executes after
+        every request that the blueprint handles. To register with a blueprint and
+        execute after every request, use :meth:`.Blueprint.after_app_request`.
         """
         self.after_request_funcs.setdefault(None, []).append(f)
         return f
@@ -606,6 +603,11 @@ class Scaffold:
         ``try``/``except`` block and log any errors.
 
         The return values of teardown functions are ignored.
+
+        This is available on both app and blueprint objects. When used on an app, this
+        executes after every request. When used on a blueprint, this executes after
+        every request that the blueprint handles. To register with a blueprint and
+        execute after every request, use :meth:`.Blueprint.teardown_app_request`.
         """
         self.teardown_request_funcs.setdefault(None, []).append(f)
         return f
@@ -615,7 +617,15 @@ class Scaffold:
         self,
         f: T_template_context_processor,
     ) -> T_template_context_processor:
-        """Registers a template context processor function."""
+        """Registers a template context processor function. These functions run before
+        rendering a template. The keys of the returned dict are added as variables
+        available in the template.
+
+        This is available on both app and blueprint objects. When used on an app, this
+        is called for every rendered template. When used on a blueprint, this is called
+        for templates rendered from the blueprint's views. To register with a blueprint
+        and affect every template, use :meth:`.Blueprint.app_context_processor`.
+        """
         self.template_context_processors[None].append(f)
         return f
 
@@ -635,6 +645,11 @@ class Scaffold:
 
         The function is passed the endpoint name and values dict. The return
         value is ignored.
+
+        This is available on both app and blueprint objects. When used on an app, this
+        is called for every request. When used on a blueprint, this is called for
+        requests that the blueprint handles. To register with a blueprint and affect
+        every request, use :meth:`.Blueprint.app_url_value_preprocessor`.
         """
         self.url_value_preprocessors[None].append(f)
         return f
@@ -644,13 +659,18 @@ class Scaffold:
         """Callback function for URL defaults for all view functions of the
         application.  It's called with the endpoint and values and should
         update the values passed in place.
+
+        This is available on both app and blueprint objects. When used on an app, this
+        is called for every request. When used on a blueprint, this is called for
+        requests that the blueprint handles. To register with a blueprint and affect
+        every request, use :meth:`.Blueprint.app_url_defaults`.
         """
         self.url_default_functions[None].append(f)
         return f
 
     @setupmethod
     def errorhandler(
-        self, code_or_exception: t.Union[t.Type[Exception], int]
+        self, code_or_exception: type[Exception] | int
     ) -> t.Callable[[T_error_handler], T_error_handler]:
         """Register a function to handle errors by code or exception class.
 
@@ -667,6 +687,11 @@ class Scaffold:
             def special_exception_handler(error):
                 return 'Database connection failed', 500
 
+        This is available on both app and blueprint objects. When used on an app, this
+        can handle errors from every request. When used on a blueprint, this can handle
+        errors from requests that the blueprint handles. To register with a blueprint
+        and affect every request, use :meth:`.Blueprint.app_errorhandler`.
+
         .. versionadded:: 0.7
             Use :meth:`register_error_handler` instead of modifying
             :attr:`error_handler_spec` directly, for application wide error
@@ -690,7 +715,7 @@ class Scaffold:
     @setupmethod
     def register_error_handler(
         self,
-        code_or_exception: t.Union[t.Type[Exception], int],
+        code_or_exception: type[Exception] | int,
         f: ft.ErrorHandlerCallable,
     ) -> None:
         """Alternative error attach function to the :meth:`errorhandler`
@@ -704,8 +729,8 @@ class Scaffold:
 
     @staticmethod
     def _get_exc_class_and_code(
-        exc_class_or_code: t.Union[t.Type[Exception], int]
-    ) -> t.Tuple[t.Type[Exception], t.Optional[int]]:
+        exc_class_or_code: type[Exception] | int,
+    ) -> tuple[type[Exception], int | None]:
         """Get the exception class being handled. For HTTP status codes
         or ``HTTPException`` subclasses, return both the exception and
         status code.
@@ -713,7 +738,7 @@ class Scaffold:
         :param exc_class_or_code: Any exception class, or an HTTP status
             code as an integer.
         """
-        exc_class: t.Type[Exception]
+        exc_class: type[Exception]
 
         if isinstance(exc_class_or_code, int):
             try:
diff --git a/src/flask/sessions.py b/src/flask/sessions.py
index 02b8cf7..e5650d6 100644
--- a/src/flask/sessions.py
+++ b/src/flask/sessions.py
@@ -1,6 +1,7 @@
+from __future__ import annotations
+
 import hashlib
 import typing as t
-import warnings
 from collections.abc import MutableMapping
 from datetime import datetime
 from datetime import timezone
@@ -9,11 +10,9 @@ from itsdangerous import BadSignature
 from itsdangerous import URLSafeTimedSerializer
 from werkzeug.datastructures import CallbackDict
 
-from .helpers import is_ip
 from .json.tag import TaggedJSONSerializer
 
 if t.TYPE_CHECKING:  # pragma: no cover
-    import typing_extensions as te
     from .app import Flask
     from .wrappers import Request, Response
 
@@ -94,7 +93,7 @@ class NullSession(SecureCookieSession):
     but fail on setting.
     """
 
-    def _fail(self, *args: t.Any, **kwargs: t.Any) -> "te.NoReturn":
+    def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
         raise RuntimeError(
             "The session is unavailable because no secret "
             "key was set.  Set the secret_key on the "
@@ -155,7 +154,7 @@ class SessionInterface:
     #: .. versionadded:: 0.10
     pickle_based = False
 
-    def make_null_session(self, app: "Flask") -> NullSession:
+    def make_null_session(self, app: Flask) -> NullSession:
         """Creates a null session which acts as a replacement object if the
         real session support could not be loaded due to a configuration
         error.  This mainly aids the user experience because the job of the
@@ -176,69 +175,24 @@ class SessionInterface:
         """
         return isinstance(obj, self.null_session_class)
 
-    def get_cookie_name(self, app: "Flask") -> str:
+    def get_cookie_name(self, app: Flask) -> str:
         """The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``."""
         return app.config["SESSION_COOKIE_NAME"]
 
-    def get_cookie_domain(self, app: "Flask") -> t.Optional[str]:
-        """Returns the domain that should be set for the session cookie.
+    def get_cookie_domain(self, app: Flask) -> str | None:
+        """The value of the ``Domain`` parameter on the session cookie. If not set,
+        browsers will only send the cookie to the exact domain it was set from.
+        Otherwise, they will send it to any subdomain of the given value as well.
 
-        Uses ``SESSION_COOKIE_DOMAIN`` if it is configured, otherwise
-        falls back to detecting the domain based on ``SERVER_NAME``.
+        Uses the :data:`SESSION_COOKIE_DOMAIN` config.
 
-        Once detected (or if not set at all), ``SESSION_COOKIE_DOMAIN`` is
-        updated to avoid re-running the logic.
+        .. versionchanged:: 2.3
+            Not set by default, does not fall back to ``SERVER_NAME``.
         """
-
         rv = app.config["SESSION_COOKIE_DOMAIN"]
+        return rv if rv else None
 
-        # set explicitly, or cached from SERVER_NAME detection
-        # if False, return None
-        if rv is not None:
-            return rv if rv else None
-
-        rv = app.config["SERVER_NAME"]
-
-        # server name not set, cache False to return none next time
-        if not rv:
-            app.config["SESSION_COOKIE_DOMAIN"] = False
-            return None
-
-        # chop off the port which is usually not supported by browsers
-        # remove any leading '.' since we'll add that later
-        rv = rv.rsplit(":", 1)[0].lstrip(".")
-
-        if "." not in rv:
-            # Chrome doesn't allow names without a '.'. This should only
-            # come up with localhost. Hack around this by not setting
-            # the name, and show a warning.
-            warnings.warn(
-                f"{rv!r} is not a valid cookie domain, it must contain"
-                " a '.'. Add an entry to your hosts file, for example"
-                f" '{rv}.localdomain', and use that instead."
-            )
-            app.config["SESSION_COOKIE_DOMAIN"] = False
-            return None
-
-        ip = is_ip(rv)
-
-        if ip:
-            warnings.warn(
-                "The session cookie domain is an IP address. This may not work"
-                " as intended in some browsers. Add an entry to your hosts"
-                ' file, for example "localhost.localdomain", and use that'
-                " instead."
-            )
-
-        # if this is not an ip and app is mounted at the root, allow subdomain
-        # matching by adding a '.' prefix
-        if self.get_cookie_path(app) == "/" and not ip:
-            rv = f".{rv}"
-
-        app.config["SESSION_COOKIE_DOMAIN"] = rv
-        return rv
-
-    def get_cookie_path(self, app: "Flask") -> str:
+    def get_cookie_path(self, app: Flask) -> str:
         """Returns the path for which the cookie should be valid.  The
         default implementation uses the value from the ``SESSION_COOKIE_PATH``
         config var if it's set, and falls back to ``APPLICATION_ROOT`` or
@@ -246,29 +200,27 @@ class SessionInterface:
         """
         return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"]
 
-    def get_cookie_httponly(self, app: "Flask") -> bool:
+    def get_cookie_httponly(self, app: Flask) -> bool:
         """Returns True if the session cookie should be httponly.  This
         currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
         config var.
         """
         return app.config["SESSION_COOKIE_HTTPONLY"]
 
-    def get_cookie_secure(self, app: "Flask") -> bool:
+    def get_cookie_secure(self, app: Flask) -> bool:
         """Returns True if the cookie should be secure.  This currently
         just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
         """
         return app.config["SESSION_COOKIE_SECURE"]
 
-    def get_cookie_samesite(self, app: "Flask") -> str:
+    def get_cookie_samesite(self, app: Flask) -> str:
         """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the
         ``SameSite`` attribute. This currently just returns the value of
         the :data:`SESSION_COOKIE_SAMESITE` setting.
         """
         return app.config["SESSION_COOKIE_SAMESITE"]
 
-    def get_expiration_time(
-        self, app: "Flask", session: SessionMixin
-    ) -> t.Optional[datetime]:
+    def get_expiration_time(self, app: Flask, session: SessionMixin) -> datetime | None:
         """A helper method that returns an expiration date for the session
         or ``None`` if the session is linked to the browser session.  The
         default implementation returns now + the permanent session
@@ -278,7 +230,7 @@ class SessionInterface:
             return datetime.now(timezone.utc) + app.permanent_session_lifetime
         return None
 
-    def should_set_cookie(self, app: "Flask", session: SessionMixin) -> bool:
+    def should_set_cookie(self, app: Flask, session: SessionMixin) -> bool:
         """Used by session backends to determine if a ``Set-Cookie`` header
         should be set for this session cookie for this response. If the session
         has been modified, the cookie is set. If the session is permanent and
@@ -294,9 +246,7 @@ class SessionInterface:
             session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"]
         )
 
-    def open_session(
-        self, app: "Flask", request: "Request"
-    ) -> t.Optional[SessionMixin]:
+    def open_session(self, app: Flask, request: Request) -> SessionMixin | None:
         """This is called at the beginning of each request, after
         pushing the request context, before matching the URL.
 
@@ -311,7 +261,7 @@ class SessionInterface:
         raise NotImplementedError()
 
     def save_session(
-        self, app: "Flask", session: SessionMixin, response: "Response"
+        self, app: Flask, session: SessionMixin, response: Response
     ) -> None:
         """This is called at the end of each request, after generating
         a response, before removing the request context. It is skipped
@@ -342,9 +292,7 @@ class SecureCookieSessionInterface(SessionInterface):
     serializer = session_json_serializer
     session_class = SecureCookieSession
 
-    def get_signing_serializer(
-        self, app: "Flask"
-    ) -> t.Optional[URLSafeTimedSerializer]:
+    def get_signing_serializer(self, app: Flask) -> URLSafeTimedSerializer | None:
         if not app.secret_key:
             return None
         signer_kwargs = dict(
@@ -357,9 +305,7 @@ class SecureCookieSessionInterface(SessionInterface):
             signer_kwargs=signer_kwargs,
         )
 
-    def open_session(
-        self, app: "Flask", request: "Request"
-    ) -> t.Optional[SecureCookieSession]:
+    def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None:
         s = self.get_signing_serializer(app)
         if s is None:
             return None
@@ -374,7 +320,7 @@ class SecureCookieSessionInterface(SessionInterface):
             return self.session_class()
 
     def save_session(
-        self, app: "Flask", session: SessionMixin, response: "Response"
+        self, app: Flask, session: SessionMixin, response: Response
     ) -> None:
         name = self.get_cookie_name(app)
         domain = self.get_cookie_domain(app)
@@ -383,6 +329,10 @@ class SecureCookieSessionInterface(SessionInterface):
         samesite = self.get_cookie_samesite(app)
         httponly = self.get_cookie_httponly(app)
 
+        # Add a "Vary: Cookie" header if the session was accessed at all.
+        if session.accessed:
+            response.vary.add("Cookie")
+
         # If the session is modified to be empty, remove the cookie.
         # If the session is empty, return without setting the cookie.
         if not session:
@@ -395,13 +345,10 @@ class SecureCookieSessionInterface(SessionInterface):
                     samesite=samesite,
                     httponly=httponly,
                 )
+                response.vary.add("Cookie")
 
             return
 
-        # Add a "Vary: Cookie" header if the session was accessed at all.
-        if session.accessed:
-            response.vary.add("Cookie")
-
         if not self.should_set_cookie(app, session):
             return
 
@@ -417,3 +364,4 @@ class SecureCookieSessionInterface(SessionInterface):
             secure=secure,
             samesite=samesite,
         )
+        response.vary.add("Cookie")
diff --git a/src/flask/signals.py b/src/flask/signals.py
index 2c6d646..d79f21f 100644
--- a/src/flask/signals.py
+++ b/src/flask/signals.py
@@ -1,49 +1,13 @@
-import typing as t
-
-try:
-    from blinker import Namespace
-
-    signals_available = True
-except ImportError:
-    signals_available = False
-
-    class Namespace:  # type: ignore
-        def signal(self, name: str, doc: t.Optional[str] = None) -> "_FakeSignal":
-            return _FakeSignal(name, doc)
-
-    class _FakeSignal:
-        """If blinker is unavailable, create a fake class with the same
-        interface that allows sending of signals but will fail with an
-        error on anything else.  Instead of doing anything on send, it
-        will just ignore the arguments and do nothing instead.
-        """
-
-        def __init__(self, name: str, doc: t.Optional[str] = None) -> None:
-            self.name = name
-            self.__doc__ = doc
-
-        def send(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
-            pass
+from __future__ import annotations
 
-        def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
-            raise RuntimeError(
-                "Signalling support is unavailable because the blinker"
-                " library is not installed."
-            ) from None
-
-        connect = connect_via = connected_to = temporarily_connected_to = _fail
-        disconnect = _fail
-        has_receivers_for = receivers_for = _fail
-        del _fail
+import typing as t
+import warnings
 
+from blinker import Namespace
 
-# The namespace for code signals.  If you are not Flask code, do
-# not put signals in here.  Create your own namespace instead.
+# This namespace is only for signals provided by Flask itself.
 _signals = Namespace()
 
-
-# Core signals.  For usage examples grep the source code or consult
-# the API documentation in docs/api.rst as well as docs/signals.rst
 template_rendered = _signals.signal("template-rendered")
 before_render_template = _signals.signal("before-render-template")
 request_started = _signals.signal("request-started")
@@ -54,3 +18,16 @@ appcontext_tearing_down = _signals.signal("appcontext-tearing-down")
 appcontext_pushed = _signals.signal("appcontext-pushed")
 appcontext_popped = _signals.signal("appcontext-popped")
 message_flashed = _signals.signal("message-flashed")
+
+
+def __getattr__(name: str) -> t.Any:
+    if name == "signals_available":
+        warnings.warn(
+            "The 'signals_available' attribute is deprecated and will be removed in"
+            " Flask 2.4. Signals are always available.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return True
+
+    raise AttributeError(name)
diff --git a/src/flask/templating.py b/src/flask/templating.py
index 25cc3f9..769108f 100644
--- a/src/flask/templating.py
+++ b/src/flask/templating.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import typing as t
 
 from jinja2 import BaseLoader
@@ -18,13 +20,13 @@ if t.TYPE_CHECKING:  # pragma: no cover
     from .scaffold import Scaffold
 
 
-def _default_template_ctx_processor() -> t.Dict[str, t.Any]:
+def _default_template_ctx_processor() -> dict[str, t.Any]:
     """Default template context processor.  Injects `request`,
     `session` and `g`.
     """
     appctx = _cv_app.get(None)
     reqctx = _cv_request.get(None)
-    rv: t.Dict[str, t.Any] = {}
+    rv: dict[str, t.Any] = {}
     if appctx is not None:
         rv["g"] = appctx.g
     if reqctx is not None:
@@ -39,7 +41,7 @@ class Environment(BaseEnvironment):
     name of the blueprint to referenced templates if necessary.
     """
 
-    def __init__(self, app: "Flask", **options: t.Any) -> None:
+    def __init__(self, app: Flask, **options: t.Any) -> None:
         if "loader" not in options:
             options["loader"] = app.create_global_jinja_loader()
         BaseEnvironment.__init__(self, **options)
@@ -51,24 +53,22 @@ class DispatchingJinjaLoader(BaseLoader):
     the blueprint folders.
     """
 
-    def __init__(self, app: "Flask") -> None:
+    def __init__(self, app: Flask) -> None:
         self.app = app
 
     def get_source(  # type: ignore
         self, environment: Environment, template: str
-    ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
+    ) -> tuple[str, str | None, t.Callable | None]:
         if self.app.config["EXPLAIN_TEMPLATE_LOADING"]:
             return self._get_source_explained(environment, template)
         return self._get_source_fast(environment, template)
 
     def _get_source_explained(
         self, environment: Environment, template: str
-    ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
+    ) -> tuple[str, str | None, t.Callable | None]:
         attempts = []
-        rv: t.Optional[t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]]
-        trv: t.Optional[
-            t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]
-        ] = None
+        rv: tuple[str, str | None, t.Callable[[], bool] | None] | None
+        trv: None | (tuple[str, str | None, t.Callable[[], bool] | None]) = None
 
         for srcobj, loader in self._iter_loaders(template):
             try:
@@ -89,7 +89,7 @@ class DispatchingJinjaLoader(BaseLoader):
 
     def _get_source_fast(
         self, environment: Environment, template: str
-    ) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
+    ) -> tuple[str, str | None, t.Callable | None]:
         for _srcobj, loader in self._iter_loaders(template):
             try:
                 return loader.get_source(environment, template)
@@ -99,7 +99,7 @@ class DispatchingJinjaLoader(BaseLoader):
 
     def _iter_loaders(
         self, template: str
-    ) -> t.Generator[t.Tuple["Scaffold", BaseLoader], None, None]:
+    ) -> t.Generator[tuple[Scaffold, BaseLoader], None, None]:
         loader = self.app.jinja_loader
         if loader is not None:
             yield self.app, loader
@@ -109,7 +109,7 @@ class DispatchingJinjaLoader(BaseLoader):
             if loader is not None:
                 yield blueprint, loader
 
-    def list_templates(self) -> t.List[str]:
+    def list_templates(self) -> list[str]:
         result = set()
         loader = self.app.jinja_loader
         if loader is not None:
@@ -124,17 +124,21 @@ class DispatchingJinjaLoader(BaseLoader):
         return list(result)
 
 
-def _render(app: "Flask", template: Template, context: t.Dict[str, t.Any]) -> str:
+def _render(app: Flask, template: Template, context: dict[str, t.Any]) -> str:
     app.update_template_context(context)
-    before_render_template.send(app, template=template, context=context)
+    before_render_template.send(
+        app, _async_wrapper=app.ensure_sync, template=template, context=context
+    )
     rv = template.render(context)
-    template_rendered.send(app, template=template, context=context)
+    template_rendered.send(
+        app, _async_wrapper=app.ensure_sync, template=template, context=context
+    )
     return rv
 
 
 def render_template(
-    template_name_or_list: t.Union[str, Template, t.List[t.Union[str, Template]]],
-    **context: t.Any
+    template_name_or_list: str | Template | list[str | Template],
+    **context: t.Any,
 ) -> str:
     """Render a template by name with the given context.
 
@@ -160,14 +164,18 @@ def render_template_string(source: str, **context: t.Any) -> str:
 
 
 def _stream(
-    app: "Flask", template: Template, context: t.Dict[str, t.Any]
+    app: Flask, template: Template, context: dict[str, t.Any]
 ) -> t.Iterator[str]:
     app.update_template_context(context)
-    before_render_template.send(app, template=template, context=context)
+    before_render_template.send(
+        app, _async_wrapper=app.ensure_sync, template=template, context=context
+    )
 
     def generate() -> t.Iterator[str]:
         yield from template.generate(context)
-        template_rendered.send(app, template=template, context=context)
+        template_rendered.send(
+            app, _async_wrapper=app.ensure_sync, template=template, context=context
+        )
 
     rv = generate()
 
@@ -179,8 +187,8 @@ def _stream(
 
 
 def stream_template(
-    template_name_or_list: t.Union[str, Template, t.List[t.Union[str, Template]]],
-    **context: t.Any
+    template_name_or_list: str | Template | list[str | Template],
+    **context: t.Any,
 ) -> t.Iterator[str]:
     """Render a template by name with the given context as a stream.
     This returns an iterator of strings, which can be used as a
diff --git a/src/flask/testing.py b/src/flask/testing.py
index ec9ebb9..773f152 100644
--- a/src/flask/testing.py
+++ b/src/flask/testing.py
@@ -1,17 +1,18 @@
+from __future__ import annotations
+
 import typing as t
 from contextlib import contextmanager
 from contextlib import ExitStack
 from copy import copy
 from types import TracebackType
+from urllib.parse import urlsplit
 
 import werkzeug.test
 from click.testing import CliRunner
 from werkzeug.test import Client
-from werkzeug.urls import url_parse
 from werkzeug.wrappers import Request as BaseRequest
 
 from .cli import ScriptInfo
-from .globals import _cv_request
 from .sessions import SessionMixin
 
 if t.TYPE_CHECKING:  # pragma: no cover
@@ -44,11 +45,11 @@ class EnvironBuilder(werkzeug.test.EnvironBuilder):
 
     def __init__(
         self,
-        app: "Flask",
+        app: Flask,
         path: str = "/",
-        base_url: t.Optional[str] = None,
-        subdomain: t.Optional[str] = None,
-        url_scheme: t.Optional[str] = None,
+        base_url: str | None = None,
+        subdomain: str | None = None,
+        url_scheme: str | None = None,
         *args: t.Any,
         **kwargs: t.Any,
     ) -> None:
@@ -68,7 +69,7 @@ class EnvironBuilder(werkzeug.test.EnvironBuilder):
             if url_scheme is None:
                 url_scheme = app.config["PREFERRED_URL_SCHEME"]
 
-            url = url_parse(path)
+            url = urlsplit(path)
             base_url = (
                 f"{url.scheme or url_scheme}://{url.netloc or http_host}"
                 f"/{app_root.lstrip('/')}"
@@ -105,12 +106,12 @@ class FlaskClient(Client):
     Basic usage is outlined in the :doc:`/testing` chapter.
     """
 
-    application: "Flask"
+    application: Flask
 
     def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
         super().__init__(*args, **kwargs)
         self.preserve_context = False
-        self._new_contexts: t.List[t.ContextManager[t.Any]] = []
+        self._new_contexts: list[t.ContextManager[t.Any]] = []
         self._context_stack = ExitStack()
         self.environ_base = {
             "REMOTE_ADDR": "127.0.0.1",
@@ -137,40 +138,35 @@ class FlaskClient(Client):
         :meth:`~flask.Flask.test_request_context` which are directly
         passed through.
         """
-        if self.cookie_jar is None:
-            raise RuntimeError(
-                "Session transactions only make sense with cookies enabled."
+        if self._cookies is None:
+            raise TypeError(
+                "Cookies are disabled. Create a client with 'use_cookies=True'."
             )
+
         app = self.application
-        environ_overrides = kwargs.setdefault("environ_overrides", {})
-        self.cookie_jar.inject_wsgi(environ_overrides)
-        outer_reqctx = _cv_request.get(None)
-        with app.test_request_context(*args, **kwargs) as c:
-            session_interface = app.session_interface
-            sess = session_interface.open_session(app, c.request)
-            if sess is None:
-                raise RuntimeError(
-                    "Session backend did not open a session. Check the configuration"
-                )
-
-            # Since we have to open a new request context for the session
-            # handling we want to make sure that we hide out own context
-            # from the caller.  By pushing the original request context
-            # (or None) on top of this and popping it we get exactly that
-            # behavior.  It's important to not use the push and pop
-            # methods of the actual request context object since that would
-            # mean that cleanup handlers are called
-            token = _cv_request.set(outer_reqctx)  # type: ignore[arg-type]
-            try:
-                yield sess
-            finally:
-                _cv_request.reset(token)
-
-            resp = app.response_class()
-            if not session_interface.is_null_session(sess):
-                session_interface.save_session(app, sess, resp)
-            headers = resp.get_wsgi_headers(c.request.environ)
-            self.cookie_jar.extract_wsgi(c.request.environ, headers)
+        ctx = app.test_request_context(*args, **kwargs)
+        self._add_cookies_to_wsgi(ctx.request.environ)
+
+        with ctx:
+            sess = app.session_interface.open_session(app, ctx.request)
+
+        if sess is None:
+            raise RuntimeError("Session backend did not open a session.")
+
+        yield sess
+        resp = app.response_class()
+
+        if app.session_interface.is_null_session(sess):
+            return
+
+        with ctx:
+            app.session_interface.save_session(app, sess, resp)
+
+        self._update_cookies_from_response(
+            ctx.request.host.partition(":")[0],
+            ctx.request.path,
+            resp.headers.getlist("Set-Cookie"),
+        )
 
     def _copy_environ(self, other):
         out = {**self.environ_base, **other}
@@ -195,7 +191,7 @@ class FlaskClient(Client):
         buffered: bool = False,
         follow_redirects: bool = False,
         **kwargs: t.Any,
-    ) -> "TestResponse":
+    ) -> TestResponse:
         if args and isinstance(
             args[0], (werkzeug.test.EnvironBuilder, dict, BaseRequest)
         ):
@@ -225,7 +221,7 @@ class FlaskClient(Client):
             buffered=buffered,
             follow_redirects=follow_redirects,
         )
-        response.json_module = self.application.json  # type: ignore[misc]
+        response.json_module = self.application.json  # type: ignore[assignment]
 
         # Re-push contexts that were preserved during the request.
         while self._new_contexts:
@@ -234,7 +230,7 @@ class FlaskClient(Client):
 
         return response
 
-    def __enter__(self) -> "FlaskClient":
+    def __enter__(self) -> FlaskClient:
         if self.preserve_context:
             raise RuntimeError("Cannot nest client invocations")
         self.preserve_context = True
@@ -242,9 +238,9 @@ class FlaskClient(Client):
 
     def __exit__(
         self,
-        exc_type: t.Optional[type],
-        exc_value: t.Optional[BaseException],
-        tb: t.Optional[TracebackType],
+        exc_type: type | None,
+        exc_value: BaseException | None,
+        tb: TracebackType | None,
     ) -> None:
         self.preserve_context = False
         self._context_stack.close()
@@ -256,7 +252,7 @@ class FlaskCliRunner(CliRunner):
     :meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`.
     """
 
-    def __init__(self, app: "Flask", **kwargs: t.Any) -> None:
+    def __init__(self, app: Flask, **kwargs: t.Any) -> None:
         self.app = app
         super().__init__(**kwargs)
 
diff --git a/src/flask/typing.py b/src/flask/typing.py
index 8857598..50aef7f 100644
--- a/src/flask/typing.py
+++ b/src/flask/typing.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import typing as t
 
 if t.TYPE_CHECKING:  # pragma: no cover
diff --git a/src/flask/views.py b/src/flask/views.py
index a82f191..c7a2b62 100644
--- a/src/flask/views.py
+++ b/src/flask/views.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import typing as t
 
 from . import typing as ft
@@ -45,12 +47,12 @@ class View:
     #: The methods this view is registered for. Uses the same default
     #: (``["GET", "HEAD", "OPTIONS"]``) as ``route`` and
     #: ``add_url_rule`` by default.
-    methods: t.ClassVar[t.Optional[t.Collection[str]]] = None
+    methods: t.ClassVar[t.Collection[str] | None] = None
 
     #: Control whether the ``OPTIONS`` method is handled automatically.
     #: Uses the same default (``True``) as ``route`` and
     #: ``add_url_rule`` by default.
-    provide_automatic_options: t.ClassVar[t.Optional[bool]] = None
+    provide_automatic_options: t.ClassVar[bool | None] = None
 
     #: A list of decorators to apply, in order, to the generated view
     #: function. Remember that ``@decorator`` syntax is applied bottom
@@ -58,7 +60,7 @@ class View:
     #: decorator.
     #:
     #: .. versionadded:: 0.8
-    decorators: t.ClassVar[t.List[t.Callable]] = []
+    decorators: t.ClassVar[list[t.Callable]] = []
 
     #: Create a new instance of this view class for every request by
     #: default. If a view subclass sets this to ``False``, the same
@@ -92,8 +94,8 @@ class View:
         :attr:`init_every_request` to ``False``, the same instance will
         be used for every request.
 
-        The arguments passed to this method are forwarded to the view
-        class ``__init__`` method.
+        Except for ``name``, all other arguments passed to this method
+        are forwarded to the view class ``__init__`` method.
 
         .. versionchanged:: 2.2
             Added the ``init_every_request`` class attribute.
diff --git a/src/flask/wrappers.py b/src/flask/wrappers.py
index 4b855bf..ef7aa38 100644
--- a/src/flask/wrappers.py
+++ b/src/flask/wrappers.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import typing as t
 
 from werkzeug.exceptions import BadRequest
@@ -25,7 +27,7 @@ class Request(RequestBase):
     specific ones.
     """
 
-    json_module = json
+    json_module: t.Any = json
 
     #: The internal URL rule that matched the request.  This can be
     #: useful to inspect which methods are allowed for the URL from
@@ -37,20 +39,20 @@ class Request(RequestBase):
     #: because the request was never internally bound.
     #:
     #: .. versionadded:: 0.6
-    url_rule: t.Optional["Rule"] = None
+    url_rule: Rule | None = None
 
     #: A dict of view arguments that matched the request.  If an exception
     #: happened when matching, this will be ``None``.
-    view_args: t.Optional[t.Dict[str, t.Any]] = None
+    view_args: dict[str, t.Any] | None = None
 
     #: If matching the URL failed, this is the exception that will be
     #: raised / was raised as part of the request handling.  This is
     #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
     #: something similar.
-    routing_exception: t.Optional[Exception] = None
+    routing_exception: Exception | None = None
 
     @property
-    def max_content_length(self) -> t.Optional[int]:  # type: ignore
+    def max_content_length(self) -> int | None:  # type: ignore
         """Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""
         if current_app:
             return current_app.config["MAX_CONTENT_LENGTH"]
@@ -58,7 +60,7 @@ class Request(RequestBase):
             return None
 
     @property
-    def endpoint(self) -> t.Optional[str]:
+    def endpoint(self) -> str | None:
         """The endpoint that matched the request URL.
 
         This will be ``None`` if matching failed or has not been
@@ -73,7 +75,7 @@ class Request(RequestBase):
         return None
 
     @property
-    def blueprint(self) -> t.Optional[str]:
+    def blueprint(self) -> str | None:
         """The registered name of the current blueprint.
 
         This will be ``None`` if the endpoint is not part of a
@@ -92,7 +94,7 @@ class Request(RequestBase):
         return None
 
     @property
-    def blueprints(self) -> t.List[str]:
+    def blueprints(self) -> list[str]:
         """The registered names of the current blueprint upwards through
         parent blueprints.
 
@@ -123,7 +125,7 @@ class Request(RequestBase):
 
             attach_enctype_error_multidict(self)
 
-    def on_json_loading_failed(self, e: t.Optional[ValueError]) -> t.Any:
+    def on_json_loading_failed(self, e: ValueError | None) -> t.Any:
         try:
             return super().on_json_loading_failed(e)
         except BadRequest as e:
@@ -151,7 +153,7 @@ class Response(ResponseBase):
         Added :attr:`max_cookie_size`.
     """
 
-    default_mimetype = "text/html"
+    default_mimetype: str | None = "text/html"
 
     json_module = json
 
diff --git a/tests/conftest.py b/tests/conftest.py
index 670acc8..a4168f2 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -20,7 +20,6 @@ def _standard_os_environ():
     out = (
         (os.environ, "FLASK_ENV_FILE", monkeypatch.notset),
         (os.environ, "FLASK_APP", monkeypatch.notset),
-        (os.environ, "FLASK_ENV", monkeypatch.notset),
         (os.environ, "FLASK_DEBUG", monkeypatch.notset),
         (os.environ, "FLASK_RUN_FROM_CLI", monkeypatch.notset),
         (os.environ, "WERKZEUG_RUN_MAIN", monkeypatch.notset),
diff --git a/tests/static/config.toml b/tests/static/config.toml
new file mode 100644
index 0000000..64acdbd
--- /dev/null
+++ b/tests/static/config.toml
@@ -0,0 +1,2 @@
+TEST_KEY="foo"
+SECRET_KEY="config"
diff --git a/tests/test_appctx.py b/tests/test_appctx.py
index f5ca0bd..aa3a8b4 100644
--- a/tests/test_appctx.py
+++ b/tests/test_appctx.py
@@ -120,14 +120,14 @@ def test_app_tearing_down_with_unhandled_exception(app, client):
 
     @app.route("/")
     def index():
-        raise Exception("dummy")
+        raise ValueError("dummy")
 
-    with pytest.raises(Exception, match="dummy"):
+    with pytest.raises(ValueError, match="dummy"):
         with app.app_context():
             client.get("/")
 
     assert len(cleanup_stuff) == 1
-    assert isinstance(cleanup_stuff[0], Exception)
+    assert isinstance(cleanup_stuff[0], ValueError)
     assert str(cleanup_stuff[0]) == "dummy"
 
 
diff --git a/tests/test_apps/blueprintapp/__init__.py b/tests/test_apps/blueprintapp/__init__.py
index 4b05798..ad594cf 100644
--- a/tests/test_apps/blueprintapp/__init__.py
+++ b/tests/test_apps/blueprintapp/__init__.py
@@ -2,8 +2,8 @@ from flask import Flask
 
 app = Flask(__name__)
 app.config["DEBUG"] = True
-from blueprintapp.apps.admin import admin
-from blueprintapp.apps.frontend import frontend
+from blueprintapp.apps.admin import admin  # noqa: E402
+from blueprintapp.apps.frontend import frontend  # noqa: E402
 
 app.register_blueprint(admin)
 app.register_blueprint(frontend)
diff --git a/tests/test_async.py b/tests/test_async.py
index 38be27c..f52b049 100644
--- a/tests/test_async.py
+++ b/tests/test_async.py
@@ -95,7 +95,6 @@ def test_async_error_handler(path, async_app):
 
 
 def test_async_before_after_request():
-    app_first_called = False
     app_before_called = False
     app_after_called = False
     bp_before_called = False
@@ -107,13 +106,6 @@ def test_async_before_after_request():
     def index():
         return ""
 
-    with pytest.deprecated_call():
-
-        @app.before_first_request
-        async def before_first():
-            nonlocal app_first_called
-            app_first_called = True
-
     @app.before_request
     async def before():
         nonlocal app_before_called
@@ -146,7 +138,6 @@ def test_async_before_after_request():
 
     test_client = app.test_client()
     test_client.get("/")
-    assert app_first_called
     assert app_before_called
     assert app_after_called
     test_client.get("/bp/")
diff --git a/tests/test_basic.py b/tests/test_basic.py
index d547012..ca373dc 100644
--- a/tests/test_basic.py
+++ b/tests/test_basic.py
@@ -1,16 +1,15 @@
 import gc
 import re
-import time
 import uuid
 import warnings
 import weakref
 from datetime import datetime
 from datetime import timezone
 from platform import python_implementation
-from threading import Thread
 
 import pytest
 import werkzeug.serving
+from markupsafe import Markup
 from werkzeug.exceptions import BadRequest
 from werkzeug.exceptions import Forbidden
 from werkzeug.exceptions import NotFound
@@ -252,34 +251,8 @@ def test_session(app, client):
     assert client.get("/get").data == b"42"
 
 
-def test_session_using_server_name(app, client):
-    app.config.update(SERVER_NAME="example.com")
-
-    @app.route("/")
-    def index():
-        flask.session["testing"] = 42
-        return "Hello World"
-
-    rv = client.get("/", "http://example.com/")
-    assert "domain=.example.com" in rv.headers["set-cookie"].lower()
-    assert "httponly" in rv.headers["set-cookie"].lower()
-
-
-def test_session_using_server_name_and_port(app, client):
-    app.config.update(SERVER_NAME="example.com:8080")
-
-    @app.route("/")
-    def index():
-        flask.session["testing"] = 42
-        return "Hello World"
-
-    rv = client.get("/", "http://example.com:8080/")
-    assert "domain=.example.com" in rv.headers["set-cookie"].lower()
-    assert "httponly" in rv.headers["set-cookie"].lower()
-
-
-def test_session_using_server_name_port_and_path(app, client):
-    app.config.update(SERVER_NAME="example.com:8080", APPLICATION_ROOT="/foo")
+def test_session_path(app, client):
+    app.config.update(APPLICATION_ROOT="/foo")
 
     @app.route("/")
     def index():
@@ -287,9 +260,7 @@ def test_session_using_server_name_port_and_path(app, client):
         return "Hello World"
 
     rv = client.get("/", "http://example.com:8080/foo")
-    assert "domain=example.com" in rv.headers["set-cookie"].lower()
     assert "path=/foo" in rv.headers["set-cookie"].lower()
-    assert "httponly" in rv.headers["set-cookie"].lower()
 
 
 def test_session_using_application_root(app, client):
@@ -337,7 +308,8 @@ def test_session_using_session_settings(app, client):
 
     rv = client.get("/", "http://www.example.com:8080/test/")
     cookie = rv.headers["set-cookie"].lower()
-    assert "domain=.example.com" in cookie
+    # or condition for Werkzeug < 2.3
+    assert "domain=example.com" in cookie or "domain=.example.com" in cookie
     assert "path=/" in cookie
     assert "secure" in cookie
     assert "httponly" not in cookie
@@ -346,7 +318,8 @@ def test_session_using_session_settings(app, client):
     rv = client.get("/clear", "http://www.example.com:8080/test/")
     cookie = rv.headers["set-cookie"].lower()
     assert "session=;" in cookie
-    assert "domain=.example.com" in cookie
+    # or condition for Werkzeug < 2.3
+    assert "domain=example.com" in cookie or "domain=.example.com" in cookie
     assert "path=/" in cookie
     assert "secure" in cookie
     assert "samesite" in cookie
@@ -379,34 +352,6 @@ def test_session_using_samesite_attribute(app, client):
     assert "samesite=lax" in cookie
 
 
-def test_session_localhost_warning(recwarn, app, client):
-    app.config.update(SERVER_NAME="localhost:5000")
-
-    @app.route("/")
-    def index():
-        flask.session["testing"] = 42
-        return "testing"
-
-    rv = client.get("/", "http://localhost:5000/")
-    assert "domain" not in rv.headers["set-cookie"].lower()
-    w = recwarn.pop(UserWarning)
-    assert "'localhost' is not a valid cookie domain" in str(w.message)
-
-
-def test_session_ip_warning(recwarn, app, client):
-    app.config.update(SERVER_NAME="127.0.0.1:5000")
-
-    @app.route("/")
-    def index():
-        flask.session["testing"] = 42
-        return "testing"
-
-    rv = client.get("/", "http://127.0.0.1:5000/")
-    assert "domain=127.0.0.1" in rv.headers["set-cookie"].lower()
-    w = recwarn.pop(UserWarning)
-    assert "cookie domain is an IP" in str(w.message)
-
-
 def test_missing_session(app):
     app.secret_key = None
 
@@ -474,7 +419,7 @@ def test_session_special_types(app, client):
     def dump_session_contents():
         flask.session["t"] = (1, 2, 3)
         flask.session["b"] = b"\xff"
-        flask.session["m"] = flask.Markup("<html>")
+        flask.session["m"] = Markup("<html>")
         flask.session["u"] = the_uuid
         flask.session["d"] = now
         flask.session["t_tag"] = {" t": "not-a-tuple"}
@@ -488,8 +433,8 @@ def test_session_special_types(app, client):
         assert s["t"] == (1, 2, 3)
         assert type(s["b"]) == bytes
         assert s["b"] == b"\xff"
-        assert type(s["m"]) == flask.Markup
-        assert s["m"] == flask.Markup("<html>")
+        assert type(s["m"]) == Markup
+        assert s["m"] == Markup("<html>")
         assert s["u"] == the_uuid
         assert s["d"] == now
         assert s["t_tag"] == {" t": "not-a-tuple"}
@@ -556,6 +501,11 @@ def test_session_vary_cookie(app, client):
     def setdefault():
         return flask.session.setdefault("test", "default")
 
+    @app.route("/clear")
+    def clear():
+        flask.session.clear()
+        return ""
+
     @app.route("/vary-cookie-header-set")
     def vary_cookie_header_set():
         response = flask.Response()
@@ -588,11 +538,29 @@ def test_session_vary_cookie(app, client):
     expect("/get")
     expect("/getitem")
     expect("/setdefault")
+    expect("/clear")
     expect("/vary-cookie-header-set")
     expect("/vary-header-set", "Accept-Encoding, Accept-Language, Cookie")
     expect("/no-vary-header", None)
 
 
+def test_session_refresh_vary(app, client):
+    @app.get("/login")
+    def login():
+        flask.session["user_id"] = 1
+        flask.session.permanent = True
+        return ""
+
+    @app.get("/ignored")
+    def ignored():
+        return ""
+
+    rv = client.get("/login")
+    assert rv.headers["Vary"] == "Cookie"
+    rv = client.get("/ignored")
+    assert rv.headers["Vary"] == "Cookie"
+
+
 def test_flashes(app, req_ctx):
     assert not flask.session.modified
     flask.flash("Zap")
@@ -613,7 +581,7 @@ def test_extended_flashing(app):
     def index():
         flask.flash("Hello World")
         flask.flash("Hello World", "error")
-        flask.flash(flask.Markup("<em>Testing</em>"), "warning")
+        flask.flash(Markup("<em>Testing</em>"), "warning")
         return ""
 
     @app.route("/test/")
@@ -622,7 +590,7 @@ def test_extended_flashing(app):
         assert list(messages) == [
             "Hello World",
             "Hello World",
-            flask.Markup("<em>Testing</em>"),
+            Markup("<em>Testing</em>"),
         ]
         return ""
 
@@ -633,7 +601,7 @@ def test_extended_flashing(app):
         assert list(messages) == [
             ("message", "Hello World"),
             ("error", "Hello World"),
-            ("warning", flask.Markup("<em>Testing</em>")),
+            ("warning", Markup("<em>Testing</em>")),
         ]
         return ""
 
@@ -652,7 +620,7 @@ def test_extended_flashing(app):
         )
         assert list(messages) == [
             ("message", "Hello World"),
-            ("warning", flask.Markup("<em>Testing</em>")),
+            ("warning", Markup("<em>Testing</em>")),
         ]
         return ""
 
@@ -661,7 +629,7 @@ def test_extended_flashing(app):
         messages = flask.get_flashed_messages(category_filter=["message", "warning"])
         assert len(messages) == 2
         assert messages[0] == "Hello World"
-        assert messages[1] == flask.Markup("<em>Testing</em>")
+        assert messages[1] == Markup("<em>Testing</em>")
         return ""
 
     # Create new test client on each test to clean flashed messages.
@@ -1472,11 +1440,11 @@ def test_static_route_with_host_matching():
         rv = flask.url_for("static", filename="index.html", _external=True)
         assert rv == "http://example.com/static/index.html"
     # Providing static_host without host_matching=True should error.
-    with pytest.raises(Exception):
+    with pytest.raises(AssertionError):
         flask.Flask(__name__, static_host="example.com")
     # Providing host_matching=True with static_folder
     # but without static_host should error.
-    with pytest.raises(Exception):
+    with pytest.raises(AssertionError):
         flask.Flask(__name__, host_matching=True)
     # Providing host_matching=True without static_host
     # but with static_folder=None should not error.
@@ -1658,7 +1626,6 @@ def test_no_setup_after_first_request(app, client):
     def index():
         return "Awesome"
 
-    assert not app.got_first_request
     assert client.get("/").data == b"Awesome"
 
     with pytest.raises(AssertionError) as exc_info:
@@ -1667,43 +1634,6 @@ def test_no_setup_after_first_request(app, client):
     assert "setup method 'add_url_rule'" in str(exc_info.value)
 
 
-def test_before_first_request_functions(app, client):
-    got = []
-
-    with pytest.deprecated_call():
-
-        @app.before_first_request
-        def foo():
-            got.append(42)
-
-    client.get("/")
-    assert got == [42]
-    client.get("/")
-    assert got == [42]
-    assert app.got_first_request
-
-
-def test_before_first_request_functions_concurrent(app, client):
-    got = []
-
-    with pytest.deprecated_call():
-
-        @app.before_first_request
-        def foo():
-            time.sleep(0.2)
-            got.append(42)
-
-    def get_and_assert():
-        client.get("/")
-        assert got == [42]
-
-    t = Thread(target=get_and_assert)
-    t.start()
-    get_and_assert()
-    t.join()
-    assert app.got_first_request
-
-
 def test_routing_redirect_debugging(monkeypatch, app, client):
     app.config["DEBUG"] = True
 
diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py
index 1bf130f..76cee66 100644
--- a/tests/test_blueprints.py
+++ b/tests/test_blueprints.py
@@ -256,6 +256,11 @@ def test_dotted_name_not_allowed(app, client):
         flask.Blueprint("app.ui", __name__)
 
 
+def test_empty_name_not_allowed(app, client):
+    with pytest.raises(ValueError):
+        flask.Blueprint("", __name__)
+
+
 def test_dotted_names_from_app(app, client):
     test = flask.Blueprint("test", __name__)
 
@@ -722,12 +727,6 @@ def test_app_request_processing(app, client):
     bp = flask.Blueprint("bp", __name__)
     evts = []
 
-    with pytest.deprecated_call():
-
-        @bp.before_app_first_request
-        def before_first_request():
-            evts.append("first")
-
     @bp.before_app_request
     def before_app():
         evts.append("before")
@@ -755,12 +754,12 @@ def test_app_request_processing(app, client):
     # first request
     resp = client.get("/").data
     assert resp == b"request|after"
-    assert evts == ["first", "before", "after", "teardown"]
+    assert evts == ["before", "after", "teardown"]
 
     # second request
     resp = client.get("/").data
     assert resp == b"request|after"
-    assert evts == ["first"] + ["before", "after", "teardown"] * 2
+    assert evts == ["before", "after", "teardown"] * 2
 
 
 def test_app_url_processors(app, client):
@@ -950,6 +949,55 @@ def test_nesting_url_prefixes(
     assert response.status_code == 200
 
 
+def test_nesting_subdomains(app, client) -> None:
+    subdomain = "api"
+    parent = flask.Blueprint("parent", __name__)
+    child = flask.Blueprint("child", __name__)
+
+    @child.route("/child/")
+    def index():
+        return "child"
+
+    parent.register_blueprint(child)
+    app.register_blueprint(parent, subdomain=subdomain)
+
+    client.allow_subdomain_redirects = True
+
+    domain_name = "domain.tld"
+    app.config["SERVER_NAME"] = domain_name
+    response = client.get("/child/", base_url="http://api." + domain_name)
+
+    assert response.status_code == 200
+
+
+def test_child_and_parent_subdomain(app, client) -> None:
+    child_subdomain = "api"
+    parent_subdomain = "parent"
+    parent = flask.Blueprint("parent", __name__)
+    child = flask.Blueprint("child", __name__, subdomain=child_subdomain)
+
+    @child.route("/")
+    def index():
+        return "child"
+
+    parent.register_blueprint(child)
+    app.register_blueprint(parent, subdomain=parent_subdomain)
+
+    client.allow_subdomain_redirects = True
+
+    domain_name = "domain.tld"
+    app.config["SERVER_NAME"] = domain_name
+    response = client.get(
+        "/", base_url=f"http://{child_subdomain}.{parent_subdomain}.{domain_name}"
+    )
+
+    assert response.status_code == 200
+
+    response = client.get("/", base_url=f"http://{parent_subdomain}.{domain_name}")
+
+    assert response.status_code == 404
+
+
 def test_unique_blueprint_names(app, client) -> None:
     bp = flask.Blueprint("bp", __name__)
     bp2 = flask.Blueprint("bp", __name__)
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 0d9625b..4b6995f 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -433,16 +433,12 @@ class TestRoutes:
     @pytest.fixture
     def app(self):
         app = Flask(__name__)
-        app.testing = True
-
-        @app.route("/get_post/<int:x>/<int:y>", methods=["GET", "POST"])
-        def yyy_get_post(x, y):
-            pass
-
-        @app.route("/zzz_post", methods=["POST"])
-        def aaa_post():
-            pass
-
+        app.add_url_rule(
+            "/get_post/<int:x>/<int:y>",
+            methods=["GET", "POST"],
+            endpoint="yyy_get_post",
+        )
+        app.add_url_rule("/zzz_post", methods=["POST"], endpoint="aaa_post")
         return app
 
     @pytest.fixture
@@ -450,17 +446,6 @@ class TestRoutes:
         cli = FlaskGroup(create_app=lambda: app)
         return partial(runner.invoke, cli)
 
-    @pytest.fixture
-    def invoke_no_routes(self, runner):
-        def create_app():
-            app = Flask(__name__, static_folder=None)
-            app.testing = True
-
-            return app
-
-        cli = FlaskGroup(create_app=create_app)
-        return partial(runner.invoke, cli)
-
     def expect_order(self, order, output):
         # skip the header and match the start of each row
         for expect, line in zip(order, output.splitlines()[2:]):
@@ -493,11 +478,31 @@ class TestRoutes:
         output = invoke(["routes", "--all-methods"]).output
         assert "GET, HEAD, OPTIONS, POST" in output
 
-    def test_no_routes(self, invoke_no_routes):
-        result = invoke_no_routes(["routes"])
+    def test_no_routes(self, runner):
+        app = Flask(__name__, static_folder=None)
+        cli = FlaskGroup(create_app=lambda: app)
+        result = runner.invoke(cli, ["routes"])
         assert result.exit_code == 0
         assert "No routes were registered." in result.output
 
+    def test_subdomain(self, runner):
+        app = Flask(__name__, static_folder=None)
+        app.add_url_rule("/a", subdomain="a", endpoint="a")
+        app.add_url_rule("/b", subdomain="b", endpoint="b")
+        cli = FlaskGroup(create_app=lambda: app)
+        result = runner.invoke(cli, ["routes"])
+        assert result.exit_code == 0
+        assert "Subdomain" in result.output
+
+    def test_host(self, runner):
+        app = Flask(__name__, static_folder=None, host_matching=True)
+        app.add_url_rule("/a", host="a", endpoint="a")
+        app.add_url_rule("/b", host="b", endpoint="b")
+        cli = FlaskGroup(create_app=lambda: app)
+        result = runner.invoke(cli, ["routes"])
+        assert result.exit_code == 0
+        assert "Host" in result.output
+
 
 def dotenv_not_available():
     try:
diff --git a/tests/test_config.py b/tests/test_config.py
index 76c5d27..580ae86 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -6,7 +6,6 @@ import pytest
 
 import flask
 
-
 # config keys used for the TestConfig
 TEST_KEY = "foo"
 SECRET_KEY = "config"
@@ -30,13 +29,23 @@ def test_config_from_object():
     common_object_test(app)
 
 
-def test_config_from_file():
+def test_config_from_file_json():
     app = flask.Flask(__name__)
     current_dir = os.path.dirname(os.path.abspath(__file__))
     app.config.from_file(os.path.join(current_dir, "static", "config.json"), json.load)
     common_object_test(app)
 
 
+def test_config_from_file_toml():
+    tomllib = pytest.importorskip("tomllib", reason="tomllib added in 3.11")
+    app = flask.Flask(__name__)
+    current_dir = os.path.dirname(os.path.abspath(__file__))
+    app.config.from_file(
+        os.path.join(current_dir, "static", "config.toml"), tomllib.load, text=False
+    )
+    common_object_test(app)
+
+
 def test_from_prefixed_env(monkeypatch):
     monkeypatch.setenv("FLASK_STRING", "value")
     monkeypatch.setenv("FLASK_BOOL", "true")
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 0e6a885..7e742b1 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -302,24 +302,18 @@ class TestStreaming:
 
 class TestHelpers:
     @pytest.mark.parametrize(
-        "debug, expected_flag, expected_default_flag",
+        ("debug", "expect"),
         [
-            ("", False, False),
-            ("0", False, False),
-            ("False", False, False),
-            ("No", False, False),
-            ("True", True, True),
+            ("", False),
+            ("0", False),
+            ("False", False),
+            ("No", False),
+            ("True", True),
         ],
     )
-    def test_get_debug_flag(
-        self, monkeypatch, debug, expected_flag, expected_default_flag
-    ):
+    def test_get_debug_flag(self, monkeypatch, debug, expect):
         monkeypatch.setenv("FLASK_DEBUG", debug)
-        if expected_flag is None:
-            assert get_debug_flag() is None
-        else:
-            assert get_debug_flag() == expected_flag
-        assert get_debug_flag() == expected_default_flag
+        assert get_debug_flag() == expect
 
     def test_make_response(self):
         app = flask.Flask(__name__)
diff --git a/tests/test_json.py b/tests/test_json.py
index ba9f38d..500eb64 100644
--- a/tests/test_json.py
+++ b/tests/test_json.py
@@ -267,25 +267,6 @@ def _has_encoding(name):
         return False
 
 
-@pytest.mark.skipif(
-    not _has_encoding("euc-kr"), reason="The euc-kr encoding is required."
-)
-def test_modified_url_encoding(app, client):
-    class ModifiedRequest(flask.Request):
-        url_charset = "euc-kr"
-
-    app.request_class = ModifiedRequest
-    app.url_map.charset = "euc-kr"
-
-    @app.route("/")
-    def index():
-        return flask.request.args["foo"]
-
-    rv = client.get("/", query_string={"foo": "정상처리"}, charset="euc-kr")
-    assert rv.status_code == 200
-    assert rv.get_data(as_text=True) == "정상처리"
-
-
 def test_json_key_sorting(app, client):
     app.debug = True
     assert app.json.sort_keys
diff --git a/tests/test_json_tag.py b/tests/test_json_tag.py
index 7d11b96..677160a 100644
--- a/tests/test_json_tag.py
+++ b/tests/test_json_tag.py
@@ -3,8 +3,8 @@ from datetime import timezone
 from uuid import uuid4
 
 import pytest
+from markupsafe import Markup
 
-from flask import Markup
 from flask.json.tag import JSONTag
 from flask.json.tag import TaggedJSONSerializer
 
diff --git a/tests/test_reqctx.py b/tests/test_reqctx.py
index abfacb9..6c38b66 100644
--- a/tests/test_reqctx.py
+++ b/tests/test_reqctx.py
@@ -227,7 +227,6 @@ def test_session_error_pops_context():
 
 
 def test_session_dynamic_cookie_name():
-
     # This session interface will use a cookie with a different name if the
     # requested url ends with the string "dynamic_cookie"
     class PathAwareSessionInterface(SecureCookieSessionInterface):
diff --git a/tests/test_signals.py b/tests/test_signals.py
index 8aa6983..6174fe8 100644
--- a/tests/test_signals.py
+++ b/tests/test_signals.py
@@ -1,16 +1,5 @@
-import pytest
-
-try:
-    import blinker
-except ImportError:
-    blinker = None
-
 import flask
 
-pytestmark = pytest.mark.skipif(
-    blinker is None, reason="Signals require the blinker library."
-)
-
 
 def test_template_rendered(app, client):
     @app.route("/")
diff --git a/tests/test_templating.py b/tests/test_templating.py
index 863417c..c9fb375 100644
--- a/tests/test_templating.py
+++ b/tests/test_templating.py
@@ -3,6 +3,7 @@ import logging
 import pytest
 import werkzeug.serving
 from jinja2 import TemplateNotFound
+from markupsafe import Markup
 
 import flask
 
@@ -73,7 +74,7 @@ def test_escaping(app, client):
     @app.route("/")
     def index():
         return flask.render_template(
-            "escaping_template.html", text=text, html=flask.Markup(text)
+            "escaping_template.html", text=text, html=Markup(text)
         )
 
     lines = client.get("/").data.splitlines()
@@ -93,7 +94,7 @@ def test_no_escaping(app, client):
     @app.route("/")
     def index():
         return flask.render_template(
-            "non_escaping_template.txt", text=text, html=flask.Markup(text)
+            "non_escaping_template.txt", text=text, html=Markup(text)
         )
 
     lines = client.get("/").data.splitlines()
diff --git a/tests/test_testing.py b/tests/test_testing.py
index 40f6fe1..8703e04 100644
--- a/tests/test_testing.py
+++ b/tests/test_testing.py
@@ -10,11 +10,6 @@ from flask.json import jsonify
 from flask.testing import EnvironBuilder
 from flask.testing import FlaskCliRunner
 
-try:
-    import blinker
-except ImportError:
-    blinker = None
-
 
 def test_environ_defaults_from_config(app, client):
     app.config["SERVER_NAME"] = "example.com:1234"
@@ -206,10 +201,10 @@ def test_session_transactions_keep_context(app, client, req_ctx):
 
 def test_session_transaction_needs_cookies(app):
     c = app.test_client(use_cookies=False)
-    with pytest.raises(RuntimeError) as e:
+
+    with pytest.raises(TypeError, match="Cookies are disabled."):
         with c.session_transaction():
             pass
-    assert "cookies" in str(e.value)
 
 
 def test_test_client_context_binding(app, client):
@@ -285,7 +280,6 @@ def test_json_request_and_response(app, client):
         assert rv.get_json() == json_data
 
 
-@pytest.mark.skipif(blinker is None, reason="blinker is not installed")
 def test_client_json_no_app_context(app, client):
     @app.route("/hello", methods=["POST"])
     def hello():
diff --git a/tests/typing/typing_app_decorators.py b/tests/typing/typing_app_decorators.py
index 3df3e71..6b2188a 100644
--- a/tests/typing/typing_app_decorators.py
+++ b/tests/typing/typing_app_decorators.py
@@ -10,12 +10,12 @@ app = Flask(__name__)
 
 @app.after_request
 def after_sync(response: Response) -> Response:
-    ...
+    return Response()
 
 
 @app.after_request
 async def after_async(response: Response) -> Response:
-    ...
+    return Response()
 
 
 @app.before_request
diff --git a/tests/typing/typing_route.py b/tests/typing/typing_route.py
index 566280c..bbd044a 100644
--- a/tests/typing/typing_route.py
+++ b/tests/typing/typing_route.py
@@ -3,8 +3,6 @@ from __future__ import annotations
 import typing as t
 from http import HTTPStatus
 
-import typing_extensions as te
-
 from flask import Flask
 from flask import jsonify
 from flask import stream_template
@@ -40,7 +38,7 @@ def hello_json_list() -> t.List[t.Any]:
     return [{"message": "Hello"}, {"message": "World"}]
 
 
-class StatusJSON(te.TypedDict):
+class StatusJSON(t.TypedDict):
     status: str
 
 
diff --git a/tox.ini b/tox.ini
index ee4d40f..6ade371 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,20 +1,28 @@
 [tox]
 envlist =
-    py3{11,10,9,8,7},pypy3{8,7}
-    py310-min
-    py37-dev
+    py3{12,11,10,9,8}
+    pypy39
+    py311-min
+    py38-dev
     style
     typing
     docs
 skip_missing_interpreters = true
 
 [testenv]
+package = wheel
+wheel_build_env = .pkg
 envtmpdir = {toxworkdir}/tmp/{envname}
+constrain_package_deps = true
+use_frozen_constraints = true
 deps =
     -r requirements/tests.txt
     min: -r requirements/tests-pallets-min.txt
-    dev: -r requirements/tests-pallets-dev.txt
-
+    dev: https://github.com/pallets/werkzeug/archive/refs/heads/main.tar.gz
+    dev: https://github.com/pallets/jinja/archive/refs/heads/main.tar.gz
+    dev: https://github.com/pallets/markupsafe/archive/refs/heads/main.tar.gz
+    dev: https://github.com/pallets/itsdangerous/archive/refs/heads/main.tar.gz
+    dev: https://github.com/pallets/click/archive/refs/heads/main.tar.gz
 #    examples/tutorial[test]
 #    examples/javascript[test]
 # commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs:tests examples}
@@ -23,12 +31,20 @@ commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs:tests}
 [testenv:style]
 deps = pre-commit
 skip_install = true
-commands = pre-commit run --all-files --show-diff-on-failure
+commands = pre-commit run --all-files
 
 [testenv:typing]
+package = wheel
+wheel_build_env = .pkg
+constrain_package_deps = true
+use_frozen_constraints = true
 deps = -r requirements/typing.txt
 commands = mypy
 
 [testenv:docs]
+package = wheel
+wheel_build_env = .pkg
+constrain_package_deps = true
+use_frozen_constraints = true
 deps = -r requirements/docs.txt
 commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html

More details

Full run details

Historical runs