[tbb-commits] [tor-browser/tor-browser-91.4.0esr-11.0-1] Bug 27476: Implement about:torconnect captive portal within Tor Browser

sysrqb at torproject.org sysrqb at torproject.org
Thu Dec 2 23:12:16 UTC 2021


commit bf9abfee292a439712f90288d9e5479fd841b479
Author: Richard Pospesel <richard at torproject.org>
Date:   Wed Apr 28 23:09:34 2021 -0500

    Bug 27476: Implement about:torconnect captive portal within Tor Browser
    
    - implements new about:torconnect page as tor-launcher replacement
    - adds tor connection status to url bar and tweaks UX when not online
    - adds new torconnect component to browser
    - tor process management functionality remains implemented in tor-launcher through the TorProtocolService module
    - the onion pattern from about:tor migrated to an .inc.xhtml file now used by both about:tor and about:torconnect
    - various design tweaks and resusability fixes to onion pattern
    - adds warning/error box to about:preferences#tor when not connected to tor
    - explicitly allows about:torconnect URIs to ignore Resist Fingerprinting (RFP)
    - various tweaks to info-pages.inc.css for about:torconnect (also affects other firefox info pages)
---
 browser/actors/NetErrorParent.jsm                  |   8 +
 browser/base/content/browser-siteIdentity.js       |   2 +-
 browser/base/content/browser.js                    |  66 ++-
 browser/base/content/browser.xhtml                 |   2 +
 browser/base/content/certerror/aboutNetError.js    |  12 +-
 browser/base/content/navigator-toolbox.inc.xhtml   |   1 +
 browser/base/content/utilityOverlay.js             |   8 +
 browser/branding/alpha/content/jar.mn              |   2 +
 browser/branding/alpha/content/tor-styles.css      |  13 +
 browser/branding/nightly/content/jar.mn            |   1 +
 browser/branding/nightly/content/tor-styles.css    |  13 +
 browser/branding/official/content/jar.mn           |   1 +
 browser/branding/official/content/tor-styles.css   |  14 +
 browser/branding/tor-styles.inc.css                |  87 ++++
 browser/components/BrowserGlue.jsm                 |  37 +-
 browser/components/about/AboutRedirector.cpp       |   4 +
 browser/components/about/components.conf           |   1 +
 browser/components/moz.build                       |   1 +
 .../onionservices/HttpsEverywhereControl.jsm       |  17 +-
 browser/components/sessionstore/SessionStore.jsm   |   4 +
 browser/components/torconnect/TorConnectChild.jsm  |   9 +
 browser/components/torconnect/TorConnectParent.jsm | 144 ++++++
 .../torconnect/content/aboutTorConnect.css         | 159 +++++++
 .../torconnect/content/aboutTorConnect.js          | 302 ++++++++++++
 .../torconnect/content/aboutTorConnect.xhtml       |  45 ++
 .../components/torconnect/content/onion-slash.svg  |  13 +
 browser/components/torconnect/content/onion.svg    |   8 +
 .../torconnect/content/torBootstrapUrlbar.js       |  93 ++++
 .../torconnect/content/torconnect-urlbar.css       |  57 +++
 .../torconnect/content/torconnect-urlbar.inc.xhtml |  10 +
 browser/components/torconnect/jar.mn               |   7 +
 browser/components/torconnect/moz.build            |   6 +
 .../components/torpreferences/content/torPane.js   | 123 +++++
 .../torpreferences/content/torPane.xhtml           |  34 ++
 .../torpreferences/content/torPreferences.css      | 123 +++++
 browser/components/urlbar/UrlbarInput.jsm          |  32 ++
 browser/modules/TorConnect.jsm                     | 506 +++++++++++++++++++++
 browser/modules/TorProcessService.jsm              |  12 +
 browser/modules/TorProtocolService.jsm             |  58 ++-
 browser/modules/TorStrings.jsm                     |  80 ++++
 browser/modules/moz.build                          |   2 +
 .../shared/identity-block/identity-block.inc.css   |   7 +-
 browser/themes/shared/jar.inc.mn                   |   2 +
 browser/themes/shared/onionPattern.css             |  31 ++
 browser/themes/shared/onionPattern.inc.xhtml       |  12 +
 browser/themes/shared/onionPattern.svg             |  22 +
 browser/themes/shared/urlbar-searchbar.inc.css     |   2 +
 dom/base/Document.cpp                              |  51 ++-
 dom/base/nsGlobalWindowOuter.cpp                   |   2 +
 .../processsingleton/MainProcessSingleton.jsm      |   5 +
 toolkit/modules/AsyncPrefs.jsm                     |   1 +
 toolkit/modules/RemotePageAccessManager.jsm        |  16 +
 toolkit/mozapps/update/UpdateService.jsm           |  68 ++-
 .../lib/environments/browser-window.js             |   4 +
 54 files changed, 2298 insertions(+), 42 deletions(-)

diff --git a/browser/actors/NetErrorParent.jsm b/browser/actors/NetErrorParent.jsm
index dfae068fd5c0..ba35962c943e 100644
--- a/browser/actors/NetErrorParent.jsm
+++ b/browser/actors/NetErrorParent.jsm
@@ -21,6 +21,10 @@ const { TelemetryController } = ChromeUtils.import(
   "resource://gre/modules/TelemetryController.jsm"
 );
 
+const { TorConnect } = ChromeUtils.import(
+  "resource:///modules/TorConnect.jsm"
+);
+
 const PREF_SSL_IMPACT_ROOTS = [
   "security.tls.version.",
   "security.ssl3.",
@@ -350,6 +354,10 @@ class NetErrorParent extends JSWindowActorParent {
             break;
           }
         }
+        break;
+      case "ShouldShowTorConnect":
+        return TorConnect.shouldShowTorConnect;
     }
+    return undefined;
   }
 }
diff --git a/browser/base/content/browser-siteIdentity.js b/browser/base/content/browser-siteIdentity.js
index 1b2c7bcb22cf..2111e67c5d09 100644
--- a/browser/base/content/browser-siteIdentity.js
+++ b/browser/base/content/browser-siteIdentity.js
@@ -57,7 +57,7 @@ var gIdentityHandler = {
    * RegExp used to decide if an about url should be shown as being part of
    * the browser UI.
    */
-  _secureInternalPages: (AppConstants.TOR_BROWSER_UPDATE ? /^(?:accounts|addons|cache|certificate|config|crashes|downloads|license|logins|preferences|protections|rights|sessionrestore|support|welcomeback|ion|tor|tbupdate)(?:[?#]|$)/i : /^(?:accounts|addons|cache|certificate|config|crashes|downloads|license|logins|preferences|protections|rights|sessionrestore|support|welcomeback|ion|tor)(?:[?#]|$)/i),
+  _secureInternalPages: (AppConstants.TOR_BROWSER_UPDATE ? /^(?:accounts|addons|cache|certificate|config|crashes|downloads|license|logins|preferences|protections|rights|sessionrestore|support|welcomeback|ion|tor|tbupdate)(?:[?#]|$)/i : /^(?:accounts|addons|cache|certificate|config|crashes|downloads|license|logins|preferences|protections|rights|sessionrestore|support|welcomeback|ion|tor|torconnect)(?:[?#]|$)/i),
 
   /**
    * Whether the established HTTPS connection is considered "broken".
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 090de22cb294..cfbbdc7cb9e0 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -81,6 +81,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
   TabModalPrompt: "chrome://global/content/tabprompts.jsm",
   TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
   TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
+  TorConnect: "resource:///modules/TorConnect.jsm",
   Translation: "resource:///modules/translation/TranslationParent.jsm",
   OnionAliasStore: "resource:///modules/OnionAliasStore.jsm",
   UITour: "resource:///modules/UITour.jsm",
@@ -646,6 +647,7 @@ var gPageIcons = {
 
 var gInitialPages = [
   "about:tor",
+  "about:torconnect",
   "about:blank",
   "about:newtab",
   "about:home",
@@ -1866,6 +1868,8 @@ var gBrowserInit = {
     }
 
     this._loadHandled = true;
+
+    TorBootstrapUrlbar.init();
   },
 
   _cancelDelayedStartup() {
@@ -2416,32 +2420,48 @@ var gBrowserInit = {
 
       let defaultArgs = BrowserHandler.defaultArgs;
 
-      // If the given URI is different from the homepage, we want to load it.
-      if (uri != defaultArgs) {
-        AboutNewTab.noteNonDefaultStartup();
+      // figure out which URI to actually load (or a Promise to get the uri)
+      uri = ((uri) => {
+        // If the given URI is different from the homepage, we want to load it.
+        if (uri != defaultArgs) {
+          AboutNewTab.noteNonDefaultStartup();
+
+          if (uri instanceof Ci.nsIArray) {
+            // Transform the nsIArray of nsISupportsString's into a JS Array of
+            // JS strings.
+            return Array.from(
+              uri.enumerate(Ci.nsISupportsString),
+              supportStr => supportStr.data
+            );
+          } else if (uri instanceof Ci.nsISupportsString) {
+            return uri.data;
+          }
+          return uri;
+        }
 
-        if (uri instanceof Ci.nsIArray) {
-          // Transform the nsIArray of nsISupportsString's into a JS Array of
-          // JS strings.
-          return Array.from(
-            uri.enumerate(Ci.nsISupportsString),
-            supportStr => supportStr.data
-          );
-        } else if (uri instanceof Ci.nsISupportsString) {
-          return uri.data;
+        // The URI appears to be the the homepage. We want to load it only if
+        // session restore isn't about to override the homepage.
+        let willOverride = SessionStartup.willOverrideHomepage;
+        if (typeof willOverride == "boolean") {
+          return willOverride ? null : uri;
         }
-        return uri;
-      }
+        return willOverride.then(willOverrideHomepage =>
+          willOverrideHomepage ? null : uri
+        );
+      })(uri);
+
+      // if using TorConnect, convert these uris to redirects
+      if (TorConnect.shouldShowTorConnect) {
+        return Promise.resolve(uri).then((uri) => {
+          if (uri == null) {
+            uri  = [];
+          }
 
-      // The URI appears to be the the homepage. We want to load it only if
-      // session restore isn't about to override the homepage.
-      let willOverride = SessionStartup.willOverrideHomepage;
-      if (typeof willOverride == "boolean") {
-        return willOverride ? null : uri;
+          uri = TorConnect.getURIsToLoad(uri);
+          return uri;
+        });
       }
-      return willOverride.then(willOverrideHomepage =>
-        willOverrideHomepage ? null : uri
-      );
+      return uri;
     })());
   },
 
@@ -2508,6 +2528,8 @@ var gBrowserInit = {
 
     OnionAuthPrompt.uninit();
 
+    TorBootstrapUrlbar.uninit();
+
     gAccessibilityServiceIndicator.uninit();
 
     if (gToolbarKeyNavEnabled) {
diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml
index 65445a099148..394a46414018 100644
--- a/browser/base/content/browser.xhtml
+++ b/browser/base/content/browser.xhtml
@@ -10,6 +10,7 @@
      override rules using selectors with the same specificity. This applies to
      both "content" and "skin" packages, which bug 1385444 will unify later. -->
 <?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+<?xml-stylesheet href="chrome://branding/content/tor-styles.css" type="text/css"?>
 
 <!-- While these stylesheets are defined in Toolkit, they are only used in the
      main browser window, so we can load them here. Bug 1474241 is on file to
@@ -113,6 +114,7 @@
   Services.scriptloader.loadSubScript("chrome://browser/content/search/searchbar.js", this);
   Services.scriptloader.loadSubScript("chrome://torbutton/content/tor-circuit-display.js", this);
   Services.scriptloader.loadSubScript("chrome://torbutton/content/torbutton.js", this);
+  Services.scriptloader.loadSubScript("chrome://browser/content/torconnect/torBootstrapUrlbar.js", this);
 
   window.onload = gBrowserInit.onLoad.bind(gBrowserInit);
   window.onunload = gBrowserInit.onUnload.bind(gBrowserInit);
diff --git a/browser/base/content/certerror/aboutNetError.js b/browser/base/content/certerror/aboutNetError.js
index e5b223025a8b..60f602ba6530 100644
--- a/browser/base/content/certerror/aboutNetError.js
+++ b/browser/base/content/certerror/aboutNetError.js
@@ -240,7 +240,7 @@ function setErrorPageStrings(err) {
   document.l10n.setAttributes(titleElement, title);
 }
 
-function initPage() {
+async function initPage() {
   // We show an offline support page in case of a system-wide error,
   // when a user cannot connect to the internet and access the SUMO website.
   // For example, clock error, which causes certerrors across the web or
@@ -259,6 +259,16 @@ function initPage() {
   }
 
   var err = getErrorCode();
+
+  // proxyConnectFailure because no-tor running daemon would return this error
+  if (
+     (err === "proxyConnectFailure") &&
+     (await RPMSendQuery("ShouldShowTorConnect"))
+  ) {
+     // pass orginal destination as redirect param
+     const encodedRedirect = encodeURIComponent(document.location.href);
+     document.location.replace(`about:torconnect?redirect=${encodedRedirect}`);
+  }
   // List of error pages with an illustration.
   let illustratedErrors = [
     "malformedURI",
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
index 1aad36ab3bfc..b881492864ae 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -331,6 +331,7 @@
                    data-l10n-id="urlbar-go-button"/>
             <hbox id="page-action-buttons" context="pageActionContextMenu">
               <toolbartabstop/>
+#include ../../components/torconnect/content/torconnect-urlbar.inc.xhtml
               <hbox id="contextual-feature-recommendation" role="button" hidden="true">
                 <hbox id="cfr-label-container">
                   <label id="cfr-label"/>
diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js
index 342a92a766a4..3d22093119ca 100644
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -20,6 +20,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
   ExtensionSettingsStore: "resource://gre/modules/ExtensionSettingsStore.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   ShellService: "resource:///modules/ShellService.jsm",
+  TorConnect: "resource:///modules/TorConnect.jsm",
 });
 
 XPCOMUtils.defineLazyGetter(this, "ReferrerInfo", () =>
@@ -336,6 +337,13 @@ function openUILinkIn(
   aPostData,
   aReferrerInfo
 ) {
+
+  // make sure users are not faced with the scary red 'tor isn't working' screen
+  // if they navigate to about:tor before bootstrapped
+  if (url === "about:tor" && TorConnect.shouldShowTorConnect) {
+    url = `about:torconnect?redirect=${encodeURIComponent("about:tor")}`;
+  }
+
   var params;
 
   if (arguments.length == 3 && typeof arguments[2] == "object") {
diff --git a/browser/branding/alpha/content/jar.mn b/browser/branding/alpha/content/jar.mn
index 6db01f74fd20..93ff6ecf736b 100644
--- a/browser/branding/alpha/content/jar.mn
+++ b/browser/branding/alpha/content/jar.mn
@@ -18,4 +18,6 @@ browser.jar:
   content/branding/icon128.png                   (../default128.png)
   content/branding/icon256.png                   (../default256.png)
   content/branding/icon512.png                   (../default512.png)
+  content/branding/identity-icons-brand.svg
   content/branding/aboutDialog.css
+* content/branding/tor-styles.css
diff --git a/browser/branding/alpha/content/tor-styles.css b/browser/branding/alpha/content/tor-styles.css
new file mode 100644
index 000000000000..14c1915ef871
--- /dev/null
+++ b/browser/branding/alpha/content/tor-styles.css
@@ -0,0 +1,13 @@
+%include ../../tor-styles.inc.css
+
+/* default theme*/
+:root,
+/* light theme*/
+:root:-moz-lwtheme-darktext {
+    --tor-branding-color: var(--teal-70);
+}
+
+/* dark theme */
+:root:-moz-lwtheme-brighttext {
+    --tor-branding-color: var(--teal-60);
+}
\ No newline at end of file
diff --git a/browser/branding/nightly/content/jar.mn b/browser/branding/nightly/content/jar.mn
index 713d4c492475..93ff6ecf736b 100644
--- a/browser/branding/nightly/content/jar.mn
+++ b/browser/branding/nightly/content/jar.mn
@@ -20,3 +20,4 @@ browser.jar:
   content/branding/icon512.png                   (../default512.png)
   content/branding/identity-icons-brand.svg
   content/branding/aboutDialog.css
+* content/branding/tor-styles.css
diff --git a/browser/branding/nightly/content/tor-styles.css b/browser/branding/nightly/content/tor-styles.css
new file mode 100644
index 000000000000..52e1761e5459
--- /dev/null
+++ b/browser/branding/nightly/content/tor-styles.css
@@ -0,0 +1,13 @@
+%include ../../tor-styles.inc.css
+
+/* default theme*/
+:root,
+/* light theme*/
+:root:-moz-lwtheme-darktext {
+    --tor-branding-color: var(--blue-60);
+}
+
+/* dark theme */
+:root:-moz-lwtheme-brighttext {
+    --tor-branding-color: var(--blue-40);
+}
\ No newline at end of file
diff --git a/browser/branding/official/content/jar.mn b/browser/branding/official/content/jar.mn
index 713d4c492475..93ff6ecf736b 100644
--- a/browser/branding/official/content/jar.mn
+++ b/browser/branding/official/content/jar.mn
@@ -20,3 +20,4 @@ browser.jar:
   content/branding/icon512.png                   (../default512.png)
   content/branding/identity-icons-brand.svg
   content/branding/aboutDialog.css
+* content/branding/tor-styles.css
diff --git a/browser/branding/official/content/tor-styles.css b/browser/branding/official/content/tor-styles.css
new file mode 100644
index 000000000000..e4ccb5c767a9
--- /dev/null
+++ b/browser/branding/official/content/tor-styles.css
@@ -0,0 +1,14 @@
+%include ../../tor-styles.inc.css
+
+/* default theme*/
+:root,
+/* light theme*/
+:root:-moz-lwtheme-darktext {
+    --tor-branding-color: var(--purple-60);
+}
+
+/* dark theme */
+:root:-moz-lwtheme-brighttext {
+    --tor-branding-color: var(--purple-30);
+}
+
diff --git a/browser/branding/tor-styles.inc.css b/browser/branding/tor-styles.inc.css
new file mode 100644
index 000000000000..55dc9b6238b3
--- /dev/null
+++ b/browser/branding/tor-styles.inc.css
@@ -0,0 +1,87 @@
+:root {
+  /* photon colors, not all of them are available for whatever reason
+     in firefox, so here they are */
+
+  --magenta-50: #ff1ad9;
+  --magenta-60: #ed00b5;
+  --magenta-70: #b5007f;
+  --magenta-80: #7d004f;
+  --magenta-90: #440027;
+
+  --purple-30: #c069ff;
+  --purple-40: #ad3bff;
+  --purple-50: #9400ff;
+  --purple-60: #8000d7;
+  --purple-70: #6200a4;
+  --purple-80: #440071;
+  --purple-90: #25003e;
+
+  --blue-40: #45a1ff;
+  --blue-50: #0a84ff;
+  --blue-50-a30: rgba(10, 132, 255, 0.3);
+  --blue-60: #0060df;
+  --blue-70: #003eaa;
+  --blue-80: #002275;
+  --blue-90: #000f40;
+
+  --teal-50: #00feff;
+  --teal-60: #00c8d7;
+  --teal-70: #008ea4;
+  --teal-80: #005a71;
+  --teal-90: #002d3e;
+
+  --green-50: #30e60b;
+  --green-60: #12bc00;
+  --green-70: #058b00;
+  --green-80: #006504;
+  --green-90: #003706;
+
+  --yellow-50: #ffe900;
+  --yellow-60: #d7b600;
+  --yellow-70: #a47f00;
+  --yellow-80: #715100;
+  --yellow-90: #3e2800;
+
+  --red-50: #ff0039;
+  --red-60: #d70022;
+  --red-70: #a4000f;
+  --red-80: #5a0002;
+  --red-90: #3e0200;
+
+  --orange-50: #ff9400;
+  --orange-60: #d76e00;
+  --orange-70: #a44900;
+  --orange-80: #712b00;
+  --orange-90: #3e1300;
+
+  --grey-10: #f9f9fa;
+  --grey-10-a10: rgba(249, 249, 250, 0.1);
+  --grey-10-a20: rgba(249, 249, 250, 0.2);
+  --grey-10-a40: rgba(249, 249, 250, 0.4);
+  --grey-10-a60: rgba(249, 249, 250, 0.6);
+  --grey-10-a80: rgba(249, 249, 250, 0.8);
+  --grey-20: #ededf0;
+  --grey-30: #d7d7db;
+  --grey-40: #b1b1b3;
+  --grey-50: #737373;
+  --grey-60: #4a4a4f;
+  --grey-70: #38383d;
+  --grey-80: #2a2a2e;
+  --grey-90: #0c0c0d;
+  --grey-90-a05: rgba(12, 12, 13, 0.05);
+  --grey-90-a10: rgba(12, 12, 13, 0.1);
+  --grey-90-a20: rgba(12, 12, 13, 0.2);
+  --grey-90-a30: rgba(12, 12, 13, 0.3);
+  --grey-90-a40: rgba(12, 12, 13, 0.4);
+  --grey-90-a50: rgba(12, 12, 13, 0.5);
+  --grey-90-a60: rgba(12, 12, 13, 0.6);
+  --grey-90-a70: rgba(12, 12, 13, 0.7);
+  --grey-90-a80: rgba(12, 12, 13, 0.8);
+  --grey-90-a90: rgba(12, 12, 13, 0.9);
+
+  --ink-70: #363959;
+  --ink-80: #202340;
+  --ink-90: #0f1126;
+
+  --white-100: #ffffff;
+}
\ No newline at end of file
diff --git a/browser/components/BrowserGlue.jsm b/browser/components/BrowserGlue.jsm
index 65d5ea9eae5b..5588d1463d94 100644
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -725,6 +725,20 @@ let JSWINDOWACTORS = {
     allFrames: true,
   },
 
+  TorConnect: {
+    parent: {
+      moduleURI: "resource:///modules/TorConnectParent.jsm",
+    },
+    child: {
+      moduleURI: "resource:///modules/TorConnectChild.jsm",
+      events: {
+        DOMWindowCreated: {},
+      },
+    },
+
+    matches: ["about:torconnect","about:torconnect?*"],
+  },
+
   Translation: {
     parent: {
       moduleURI: "resource:///modules/translation/TranslationParent.jsm",
@@ -2522,7 +2536,28 @@ BrowserGlue.prototype = {
 
       {
         task: () => {
-          OnionAliasStore.init();
+          const { TorConnect, TorConnectTopics } = ChromeUtils.import(
+            "resource:///modules/TorConnect.jsm"
+          );
+          if (!TorConnect.shouldShowTorConnect) {
+            // we will take this path when the user is using the legacy tor launcher or
+            // when Tor Browser didn't launch its own tor.
+            OnionAliasStore.init();
+          } else {
+            // this path is taken when using about:torconnect, we wait to init
+            // after we are bootstrapped and connected to tor
+            const topic = TorConnectTopics.BootstrapComplete;
+            let bootstrapObserver = {
+              observe(aSubject, aTopic, aData) {
+                if (aTopic === topic) {
+                  OnionAliasStore.init();
+                  // we only need to init once, so remove ourselves as an obvserver
+                  Services.obs.removeObserver(this, topic);
+                }
+              }
+            };
+            Services.obs.addObserver(bootstrapObserver, topic);
+          }
         },
       },
 
diff --git a/browser/components/about/AboutRedirector.cpp b/browser/components/about/AboutRedirector.cpp
index 323c1b6fb653..fd828a630c92 100644
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -128,6 +128,10 @@ static const RedirEntry kRedirMap[] = {
          nsIAboutModule::URI_MUST_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
          nsIAboutModule::HIDE_FROM_ABOUTABOUT},
 #endif
+    {"torconnect", "chrome://browser/content/torconnect/aboutTorConnect.xhtml",
+     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+         nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
+         nsIAboutModule::HIDE_FROM_ABOUTABOUT},
 };
 
 static nsAutoCString GetAboutModuleName(nsIURI* aURI) {
diff --git a/browser/components/about/components.conf b/browser/components/about/components.conf
index 67f178ee23ff..0916bb75e1d5 100644
--- a/browser/components/about/components.conf
+++ b/browser/components/about/components.conf
@@ -26,6 +26,7 @@ pages = [
     'robots',
     'sessionrestore',
     'tabcrashed',
+    'torconnect',
     'welcome',
     'welcomeback',
 ]
diff --git a/browser/components/moz.build b/browser/components/moz.build
index 0ea2969e60b0..c30497374912 100644
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -55,6 +55,7 @@ DIRS += [
     "syncedtabs",
     "uitour",
     "urlbar",
+    "torconnect",
     "torpreferences",
     "translation",
 ]
diff --git a/browser/components/onionservices/HttpsEverywhereControl.jsm b/browser/components/onionservices/HttpsEverywhereControl.jsm
index 525ed5233be7..d673de4cd6e5 100644
--- a/browser/components/onionservices/HttpsEverywhereControl.jsm
+++ b/browser/components/onionservices/HttpsEverywhereControl.jsm
@@ -41,6 +41,7 @@ const SECUREDROP_TOR_ONION_CHANNEL = {
 class HttpsEverywhereControl {
   constructor() {
     this._extensionMessaging = null;
+    this._init();
   }
 
   async _sendMessage(type, object) {
@@ -61,7 +62,6 @@ class HttpsEverywhereControl {
    * Installs the .tor.onion update channel in https-everywhere
    */
   async installTorOnionUpdateChannel(retries = 5) {
-    this._init();
 
     // TODO: https-everywhere store is initialized asynchronously, so sending a message
     // immediately results in a `store.get is undefined` error.
@@ -143,5 +143,20 @@ class HttpsEverywhereControl {
     if (!this._extensionMessaging) {
       this._extensionMessaging = new ExtensionMessaging();
     }
+
+    // update all of the existing https-everywhere channels
+    setTimeout(async () => {
+      let pinnedChannels = await this._sendMessage("get_pinned_update_channels");
+      for(let channel of pinnedChannels.update_channels) {
+        this._sendMessage("update_update_channel", channel);
+      }
+
+      let storedChannels = await this._sendMessage("get_stored_update_channels");
+      for(let channel of storedChannels.update_channels) {
+        this._sendMessage("update_update_channel", channel);
+      }
+    }, 0);
+
+
   }
 }
diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm
index 2150c424d8b8..ddeb92378432 100644
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -186,6 +186,10 @@ ChromeUtils.defineModuleGetter(
   "resource://gre/modules/sessionstore/SessionHistory.jsm"
 );
 
+const { TorProtocolService } = ChromeUtils.import(
+    "resource:///modules/TorProtocolService.jsm"
+);
+
 XPCOMUtils.defineLazyServiceGetters(this, {
   gScreenManager: ["@mozilla.org/gfx/screenmanager;1", "nsIScreenManager"],
 });
diff --git a/browser/components/torconnect/TorConnectChild.jsm b/browser/components/torconnect/TorConnectChild.jsm
new file mode 100644
index 000000000000..bd6dd549f156
--- /dev/null
+++ b/browser/components/torconnect/TorConnectChild.jsm
@@ -0,0 +1,9 @@
+// Copyright (c) 2021, The Tor Project, Inc.
+
+var EXPORTED_SYMBOLS = ["TorConnectChild"];
+
+const { RemotePageChild } = ChromeUtils.import(
+  "resource://gre/actors/RemotePageChild.jsm"
+);
+
+class TorConnectChild extends RemotePageChild {}
diff --git a/browser/components/torconnect/TorConnectParent.jsm b/browser/components/torconnect/TorConnectParent.jsm
new file mode 100644
index 000000000000..526c588a423e
--- /dev/null
+++ b/browser/components/torconnect/TorConnectParent.jsm
@@ -0,0 +1,144 @@
+// Copyright (c) 2021, The Tor Project, Inc.
+
+var EXPORTED_SYMBOLS = ["TorConnectParent"];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
+const { TorConnect, TorConnectTopics, TorConnectState } = ChromeUtils.import(
+  "resource:///modules/TorConnect.jsm"
+);
+
+const TorLauncherPrefs = Object.freeze({
+  quickstart: "extensions.torlauncher.quickstart",
+});
+
+/*
+This object is basically a marshalling interface between the TorConnect module
+and a particular about:torconnect page
+*/
+
+class TorConnectParent extends JSWindowActorParent {
+  constructor(...args) {
+    super(...args);
+
+    const self = this;
+
+    this.state = {
+      State: TorConnect.state,
+      StateChanged: false,
+      ErrorMessage: TorConnect.errorMessage,
+      ErrorDetails: TorConnect.errorDetails,
+      BootstrapProgress: TorConnect.bootstrapProgress,
+      BootstrapStatus: TorConnect.bootstrapStatus,
+      ShowCopyLog: TorConnect.logHasWarningOrError,
+      QuickStartEnabled: Services.prefs.getBoolPref(TorLauncherPrefs.quickstart, false),
+    };
+
+    // JSWindowActiveParent derived objects cannot observe directly, so create a member
+    // object to do our observing for us
+    //
+    // This object converts the various lifecycle events from the TorConnect module, and
+    // maintains a state object which we pass down to our about:torconnect page, which uses
+    // the state object to update its UI
+    this.torConnectObserver = {
+      observe(aSubject, aTopic, aData) {
+        let obj = aSubject?.wrappedJSObject;
+
+        // update our state struct based on received torconnect topics and forward on
+        // to aboutTorConnect.js
+        self.state.StateChanged = false;
+        switch(aTopic) {
+          case TorConnectTopics.StateChange: {
+            self.state.State = obj.state;
+            self.state.StateChanged = true;
+            // clear any previous error information if we are bootstrapping
+            if (self.state.State === TorConnectState.Bootstrapping) {
+              self.state.ErrorMessage = null;
+              self.state.ErrorDetails = null;
+            }
+            break;
+          }
+          case TorConnectTopics.BootstrapProgress: {
+            self.state.BootstrapProgress = obj.progress;
+            self.state.BootstrapStatus = obj.status;
+            self.state.ShowCopyLog = obj.hasWarnings;
+            break;
+          }
+          case TorConnectTopics.BootstrapComplete: {
+            // noop
+            break;
+          }
+          case TorConnectTopics.BootstrapError: {
+            self.state.ErrorMessage = obj.message;
+            self.state.ErrorDetails = obj.details;
+            self.state.ShowCopyLog = true;
+            break;
+          }
+          case TorConnectTopics.FatalError: {
+            // TODO: handle
+            break;
+          }
+          case "nsPref:changed": {
+            if (aData === TorLauncherPrefs.quickstart) {
+              self.state.QuickStartEnabled = Services.prefs.getBoolPref(TorLauncherPrefs.quickstart);
+            }
+            break;
+          }
+          default: {
+            console.log(`TorConnect: unhandled observe topic '${aTopic}'`);
+          }
+        }
+
+        self.sendAsyncMessage("torconnect:state-change", self.state);
+      },
+    };
+
+    // observe all of the torconnect:.* topics
+    for (const key in TorConnectTopics) {
+      const topic = TorConnectTopics[key];
+      Services.obs.addObserver(this.torConnectObserver, topic);
+    }
+    Services.prefs.addObserver(TorLauncherPrefs.quickstart, this.torConnectObserver);
+  }
+
+  willDestroy() {
+    // stop observing all of our torconnect:.* topics
+    for (const key in TorConnectTopics) {
+      const topic = TorConnectTopics[key];
+      Services.obs.removeObserver(this.torConnectObserver, topic);
+    }
+    Services.prefs.removeObserver(TorLauncherPrefs.quickstart, this.torConnectObserver);
+  }
+
+  receiveMessage(message) {
+    switch (message.name) {
+      case "torconnect:set-quickstart":
+        Services.prefs.setBoolPref(TorLauncherPrefs.quickstart, message.data);
+        break;
+      case "torconnect:open-tor-preferences":
+        TorConnect.openTorPreferences();
+        break;
+      case "torconnect:copy-tor-logs":
+        return TorConnect.copyTorLogs();
+      case "torconnect:cancel-bootstrap":
+        TorConnect.cancelBootstrap();
+        break;
+      case "torconnect:begin-bootstrap":
+        TorConnect.beginBootstrap();
+        break;
+      case "torconnect:get-init-args":
+        // called on AboutTorConnect.init(), pass down all state data it needs to init
+
+        // pretend this is a state transition on init
+        // so we always get fresh UI
+        this.state.StateChanged = true;
+        return {
+            TorStrings: TorStrings,
+            TorConnectState: TorConnectState,
+            Direction: Services.locale.isAppLocaleRTL ? "rtl" : "ltr",
+            State: this.state,
+        };
+    }
+    return undefined;
+  }
+}
diff --git a/browser/components/torconnect/content/aboutTorConnect.css b/browser/components/torconnect/content/aboutTorConnect.css
new file mode 100644
index 000000000000..460ecb511fde
--- /dev/null
+++ b/browser/components/torconnect/content/aboutTorConnect.css
@@ -0,0 +1,159 @@
+
+/* Copyright (c) 2021, The Tor Project, Inc. */
+
+ at import url("chrome://browser/skin/error-pages.css");
+ at import url("chrome://branding/content/tor-styles.css");
+
+:root {
+  --onion-opacity: 1;
+  --onion-color: var(--card-outline-color);
+  --onion-radius: 75px;
+}
+
+/* override firefox's default blue focus coloring */
+:focus {
+  outline:  none!important;
+  box-shadow: 0 0 0 3px var(--purple-30) !important;
+  border:  1px var(--purple-80) solid !important;
+}
+
+ at media (prefers-color-scheme: dark)
+{
+  :focus {
+    box-shadow: 0 0 0 3px var(--purple-50)!important;
+  }
+}
+
+/* override firefox's default blue border on hover */
+input[type="checkbox"]:not(:disabled):hover {
+  border-color: var(--purple-70);
+}
+
+#connectButton,
+input[type="checkbox"]:not(:disabled) {
+  background-color: var(--purple-60)!important;
+  color: white;
+  fill: white;
+}
+
+#connectButton:hover,
+input[type="checkbox"]:not(:disabled):hover {
+  background-color: var(--purple-70)!important;
+  color: white;
+  fill: white;
+}
+
+#connectButton:active,
+input[type="checkbox"]:not(:disabled):active {
+  background-color: var(--purple-80)!important;
+  color: white;
+  fill: white;
+}
+
+#progressBackground {
+  position:fixed;
+  padding:0;
+  margin:0;
+  top:0;
+  left:0;
+  width: 0%;
+  height: 7px;
+  background-image: linear-gradient(90deg, rgb(20, 218, 221) 0%, rgb(128, 109, 236) 100%);
+  border-radius: 0;
+}
+
+#connectPageContainer {
+  margin-top: 10vh;
+  width: 50%;
+}
+
+#quickstartCheckbox, #quickstartCheckboxLabel {
+  vertical-align: middle;
+}
+
+#copyLogButton {
+  position: relative;
+}
+
+/* mirrors p element spacing */
+#copyLogContainer {
+  margin:  1em 0;
+  height:  1.2em;
+  min-height:  1.2em;
+}
+
+#copyLogLink {
+  position:  relative;
+  display:  inline-block;
+  color:  var(--in-content-link-color);
+}
+
+/* hidden apparently only works if no display is set; who knew? */
+#copyLogLink[hidden="true"] {
+  display:  none;
+}
+
+#copyLogLink:hover {
+  cursor:pointer;
+}
+
+/* This div:
+   - is centered over its parent
+   - centers its child
+   - has z-index above parent
+   - ignores mouse events from parent
+*/
+#copyLogTooltip {
+  pointer-events: none;
+  visibility: hidden;
+  display:  flex;
+  justify-content: center;
+  white-space: nowrap;
+  width: 0;
+  position:  absolute;
+
+  z-index:  1;
+  left: 50%;
+  bottom:  calc(100% + 0.25em);
+}
+
+/* tooltip content (any content could go here) */
+#copyLogTooltipText {
+  background-color: var(--green-50);
+  color: var(--green-90);
+  border-radius: 2px;
+  padding: 4px;
+  line-height: 13px;
+  font: 11px sans-serif;
+  font-weight: 400;
+}
+
+/* our speech bubble tail */
+#copyLogTooltipText::after {
+  content: "";
+  position: absolute;
+  top: 100%;
+  left: 50%;
+  margin-left: -4px;
+  border-width: 4px;
+  border-style: solid;
+  border-color: var(--green-50) transparent transparent transparent;
+}
+
+body {
+  padding: 0px !important;
+  justify-content: space-between;
+  background-color: var(--in-content-page-background);
+}
+
+.title {
+  background-image: url("chrome://browser/content/torconnect/onion.svg");
+  -moz-context-properties: fill, fill-opacity;
+  fill-opacity: var(--onion-opacity);
+  fill: var(--onion-color);
+}
+
+.title.error {
+  background-image: url("chrome://browser/content/torconnect/onion-slash.svg");
+}
+
diff --git a/browser/components/torconnect/content/aboutTorConnect.js b/browser/components/torconnect/content/aboutTorConnect.js
new file mode 100644
index 000000000000..b53f8b13cb80
--- /dev/null
+++ b/browser/components/torconnect/content/aboutTorConnect.js
@@ -0,0 +1,302 @@
+// Copyright (c) 2021, The Tor Project, Inc.
+
+/* eslint-env mozilla/frame-script */
+
+// populated in AboutTorConnect.init()
+let TorStrings = {};
+let TorConnectState = {};
+
+class AboutTorConnect {
+  selectors = Object.freeze({
+    textContainer: {
+      title: "div.title",
+      titleText: "h1.title-text",
+    },
+    progress: {
+      description: "p#connectShortDescText",
+      meter: "div#progressBackground",
+    },
+    copyLog: {
+      link: "span#copyLogLink",
+      tooltip: "div#copyLogTooltip",
+      tooltipText: "span#copyLogTooltipText",
+    },
+    quickstart: {
+      checkbox: "input#quickstartCheckbox",
+      label: "label#quickstartCheckboxLabel",
+    },
+    buttons: {
+      connect: "button#connectButton",
+      cancel: "button#cancelButton",
+      advanced: "button#advancedButton",
+    },
+  })
+
+  elements = Object.freeze({
+    title: document.querySelector(this.selectors.textContainer.title),
+    titleText: document.querySelector(this.selectors.textContainer.titleText),
+    progressDescription: document.querySelector(this.selectors.progress.description),
+    progressMeter: document.querySelector(this.selectors.progress.meter),
+    copyLogLink: document.querySelector(this.selectors.copyLog.link),
+    copyLogTooltip: document.querySelector(this.selectors.copyLog.tooltip),
+    copyLogTooltipText: document.querySelector(this.selectors.copyLog.tooltipText),
+    quickstartCheckbox: document.querySelector(this.selectors.quickstart.checkbox),
+    quickstartLabel: document.querySelector(this.selectors.quickstart.label),
+    connectButton: document.querySelector(this.selectors.buttons.connect),
+    cancelButton: document.querySelector(this.selectors.buttons.cancel),
+    advancedButton: document.querySelector(this.selectors.buttons.advanced),
+  })
+
+  // a redirect url can be passed as a query parameter for the page to
+  // forward us to once bootstrap completes (otherwise the window will just close)
+  redirect = null
+
+  beginBootstrap() {
+    this.hide(this.elements.connectButton);
+    this.show(this.elements.cancelButton);
+    this.elements.cancelButton.focus();
+    RPMSendAsyncMessage("torconnect:begin-bootstrap");
+  }
+
+  cancelBootstrap() {
+    RPMSendAsyncMessage("torconnect:cancel-bootstrap");
+  }
+
+  /*
+  Element helper methods
+  */
+
+  show(element) {
+    element.removeAttribute("hidden");
+  }
+
+  hide(element) {
+    element.setAttribute("hidden", "true");
+  }
+
+  setTitle(title, error) {
+    this.elements.titleText.textContent = title;
+    document.title = title;
+
+    if (error) {
+      this.elements.title.classList.add("error");
+    } else {
+      this.elements.title.classList.remove("error");
+    }
+  }
+
+  setProgress(description, visible, percent) {
+    this.elements.progressDescription.textContent = description;
+    if (visible) {
+      this.show(this.elements.progressMeter);
+      this.elements.progressMeter.style.width = `${percent}%`;
+    } else {
+      this.hide(this.elements.progressMeter);
+    }
+  }
+
+  /*
+  These methods update the UI based on the current TorConnect state
+  */
+
+  updateUI(state) {
+    console.log(state);
+
+    // calls update_$state()
+    this[`update_${state.State}`](state);
+    this.elements.quickstartCheckbox.checked = state.QuickStartEnabled;
+  }
+
+  /* Per-state updates */
+
+  update_Initial(state) {
+    const hasError = false;
+    const showProgressbar = false;
+
+    this.setTitle(TorStrings.torConnect.torConnect, hasError);
+    this.setProgress(TorStrings.settings.torPreferencesDescription, showProgressbar);
+    this.hide(this.elements.copyLogLink);
+    this.hide(this.elements.connectButton);
+    this.hide(this.elements.advancedButton);
+    this.hide(this.elements.cancelButton);
+  }
+
+  update_Configuring(state) {
+    const hasError = state.ErrorMessage != null;
+    const showProgressbar = false;
+
+    if (hasError) {
+      this.setTitle(state.ErrorMessage, hasError);
+      this.setProgress(state.ErrorDetails, showProgressbar);
+      this.show(this.elements.copyLogLink);
+      this.elements.connectButton.textContent = TorStrings.torConnect.tryAgain;
+    } else {
+      this.setTitle(TorStrings.torConnect.torConnect, hasError);
+      this.setProgress(TorStrings.settings.torPreferencesDescription, showProgressbar);
+      this.hide(this.elements.copyLogLink);
+      this.elements.connectButton.textContent = TorStrings.torConnect.torConnectButton;
+    }
+    this.show(this.elements.connectButton);
+    if (state.StateChanged) {
+      this.elements.connectButton.focus();
+    }
+    this.show(this.elements.advancedButton);
+    this.hide(this.elements.cancelButton);
+  }
+
+  update_AutoConfiguring(state) {
+    // TODO: noop until this state is used
+  }
+
+  update_Bootstrapping(state) {
+    const hasError = false;
+    const showProgressbar = true;
+
+    this.setTitle(state.BootstrapStatus ? state.BootstrapStatus : TorStrings.torConnect.torConnecting, hasError);
+    this.setProgress(TorStrings.settings.torPreferencesDescription, showProgressbar, state.BootstrapProgress);
+    if (state.ShowCopyLog) {
+      this.show(this.elements.copyLogLink);
+    } else {
+      this.hide(this.elements.copyLogLink);
+    }
+    this.hide(this.elements.connectButton);
+    this.hide(this.elements.advancedButton);
+    this.show(this.elements.cancelButton);
+    if (state.StateChanged) {
+      this.elements.cancelButton.focus();
+    }
+  }
+
+  update_Error(state) {
+    const hasError = true;
+    const showProgressbar = false;
+
+    this.setTitle(state.ErrorMessage, hasError);
+    this.setProgress(state.ErrorDetails, showProgressbar);
+    this.show(this.elements.copyLogLink);
+    this.elements.connectButton.textContent = TorStrings.torConnect.tryAgain;
+    this.show(this.elements.connectButton);
+    this.show(this.elements.advancedButton);
+    this.hide(this.elements.cancelButton);
+  }
+
+  update_FatalError(state) {
+    // TODO: noop until this state is used
+  }
+
+  update_Bootstrapped(state) {
+    const hasError = false;
+    const showProgressbar = true;
+
+    this.setTitle(TorStrings.torConnect.torConnected, hasError);
+    this.setProgress(TorStrings.settings.torPreferencesDescription, showProgressbar, 100);
+    this.hide(this.elements.connectButton);
+    this.hide(this.elements.advancedButton);
+    this.hide(this.elements.cancelButton);
+
+    // redirects page to the requested redirect url, removes about:torconnect
+    // from the page stack, so users cannot accidentally go 'back' to the
+    // now unresponsive page
+    window.location.replace(this.redirect);
+  }
+
+  update_Disabled(state) {
+    // TODO: we should probably have some UX here if a user goes to about:torconnect when
+    // it isn't in use (eg using tor-launcher or system tor)
+  }
+
+  async initElements(direction) {
+
+    document.documentElement.setAttribute("dir", direction);
+
+    // sets the text content while keeping the child elements intact
+    this.elements.copyLogLink.childNodes[0].nodeValue =
+      TorStrings.torConnect.copyLog;
+    this.elements.copyLogLink.addEventListener("click", async (event) => {
+      const copiedMessage = await RPMSendQuery("torconnect:copy-tor-logs");
+      this.elements.copyLogTooltipText.textContent = copiedMessage;
+      this.elements.copyLogTooltipText.style.visibility = "visible";
+
+      // clear previous timeout if one already exists
+      if (this.copyLogTimeoutId) {
+        clearTimeout(this.copyLogTimeoutId);
+      }
+
+      // hide tooltip after X ms
+      const TOOLTIP_TIMEOUT = 2000;
+      this.copyLogTimeoutId = setTimeout(() => {
+        this.elements.copyLogTooltipText.style.visibility = "hidden";
+        this.copyLogTimeoutId = 0;
+      }, TOOLTIP_TIMEOUT);
+    });
+
+    this.elements.quickstartCheckbox.addEventListener("change", () => {
+      const quickstart = this.elements.quickstartCheckbox.checked;
+      RPMSendAsyncMessage("torconnect:set-quickstart", quickstart);
+    });
+    this.elements.quickstartLabel.textContent = TorStrings.settings.quickstartCheckbox;
+
+    this.elements.connectButton.textContent =
+      TorStrings.torConnect.torConnectButton;
+    this.elements.connectButton.addEventListener("click", () => {
+      this.beginBootstrap();
+    });
+
+    this.elements.advancedButton.textContent = TorStrings.torConnect.torConfigure;
+    this.elements.advancedButton.addEventListener("click", () => {
+      RPMSendAsyncMessage("torconnect:open-tor-preferences");
+    });
+
+    this.elements.cancelButton.textContent = TorStrings.torConnect.cancel;
+    this.elements.cancelButton.addEventListener("click", () => {
+      this.cancelBootstrap();
+    });
+  }
+
+  initObservers() {
+    // TorConnectParent feeds us state blobs to we use to update our UI
+    RPMAddMessageListener("torconnect:state-change", ({ data }) => {
+      this.updateUI(data);
+    });
+  }
+
+  initKeyboardShortcuts() {
+    document.onkeydown = (evt) => {
+      // unfortunately it looks like we still haven't standardized keycodes to
+      // integers, so we must resort to a string compare here :(
+      // see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code for relevant documentation
+      if (evt.code === "Escape") {
+        this.cancelBootstrap();
+      }
+    };
+  }
+
+  async init() {
+    // see if a user has a final destination after bootstrapping
+    let params = new URLSearchParams(new URL(document.location.href).search);
+    if (params.has("redirect")) {
+      const encodedRedirect = params.get("redirect");
+      this.redirect = decodeURIComponent(encodedRedirect);
+    } else {
+      // if the user gets here manually or via the button in the urlbar
+      // then we will redirect to about:tor
+      this.redirect = "about:tor";
+    }
+
+    let args = await RPMSendQuery("torconnect:get-init-args");
+
+    // various constants
+    TorStrings = Object.freeze(args.TorStrings);
+    TorConnectState = Object.freeze(args.TorConnectState);
+
+    this.initElements(args.Direction);
+    this.initObservers();
+    this.initKeyboardShortcuts();
+
+    // populate UI based on current state
+    this.updateUI(args.State);
+  }
+}
+
+const aboutTorConnect = new AboutTorConnect();
+aboutTorConnect.init();
diff --git a/browser/components/torconnect/content/aboutTorConnect.xhtml b/browser/components/torconnect/content/aboutTorConnect.xhtml
new file mode 100644
index 000000000000..595bbdf9a70a
--- /dev/null
+++ b/browser/components/torconnect/content/aboutTorConnect.xhtml
@@ -0,0 +1,45 @@
+<!-- Copyright (c) 2021, The Tor Project, Inc. -->
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'" />
+    <link rel="stylesheet" href="chrome://browser/skin/onionPattern.css" type="text/css" media="all" />
+    <link rel="stylesheet" href="chrome://browser/content/torconnect/aboutTorConnect.css" type="text/css" media="all" />
+  </head>
+  <body>
+    <div id="progressBackground"></div>
+    <div id="connectPageContainer" class="container">
+      <div id="text-container">
+        <div class="title">
+          <h1 class="title-text"/>
+        </div>
+        <div id="connectLongContent">
+          <div id="connectShortDesc">
+            <p id="connectShortDescText" />
+          </div>
+        </div>
+
+        <div id="copyLogContainer">
+          <span id="copyLogLink" hidden="true">
+            <div id="copyLogTooltip">
+              <span id="copyLogTooltipText"/>
+            </div>
+          </span>
+        </div>
+
+        <div id="quickstartContainer">
+          <input id="quickstartCheckbox" type="checkbox" />
+          <label id="quickstartCheckboxLabel" for="quickstartCheckbox"/>
+        </div>
+
+        <div id="connectButtonContainer" class="button-container">
+          <button id="advancedButton" hidden="true"></button>
+          <button id="cancelButton" hidden="true"></button>
+          <button id="connectButton" class="primary try-again" hidden="true"></button>
+        </div>
+      </div>
+    </div>
+#include ../../../themes/shared/onionPattern.inc.xhtml
+  </body>
+  <script src="chrome://browser/content/torconnect/aboutTorConnect.js"/>
+</html>
diff --git a/browser/components/torconnect/content/onion-slash.svg b/browser/components/torconnect/content/onion-slash.svg
new file mode 100644
index 000000000000..d049bcd39cae
--- /dev/null
+++ b/browser/components/torconnect/content/onion-slash.svg
@@ -0,0 +1,13 @@
+<svg viewBox="0 0 16 16" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
+  <g fill-opacity="context-fill-opacity" fill="context-fill">
+    <path d="m13.3034 13.3032c-1.3572 1.3573-3.2323 2.1968-5.3034 2.1968-4.14214 0-7.5-3.3579-7.5-7.5 0-2.07093.83935-3.94582 2.19643-5.30303l.82867.82861c-1.14502 1.14515-1.85322 2.72708-1.85322 4.47442 0 3.4949 2.83319 6.3281 6.32812 6.3281 1.74752 0 3.3296-.7083 4.4748-1.8536z"/>
+    <path d="m14.1137 12.3453c.8729-1.226 1.3863-2.72567 1.3863-4.3453 0-4.14214-3.3579-7.5-7.5-7.5-1.61963 0-3.11935.51339-4.34531 1.38631l.84258.84258c1.00297-.66783 2.2074-1.05701 3.50273-1.05701 3.4949 0 6.3281 2.83319 6.3281 6.32812 0 1.29533-.3892 2.4998-1.057 3.5027z"/>
+    <path d="m12.4902 10.7218c.4822-.79365.7598-1.7253.7598-2.72181 0-2.89949-2.3505-5.25-5.25001-5.25-.9965 0-1.92816.27764-2.72184.75978l.86063.86062c.558-.28671 1.19071-.44852 1.86121-.44852 2.25231 0 4.07811 1.82584 4.07811 4.07812 0 .67051-.1618 1.30322-.4485 1.86122z"/>
+    <path d="m11.7124 11.7122-.8287-.8286c-.738.738-1.75754 1.1945-2.88371 1.1945-2.25228 0-4.07812-1.8258-4.07812-4.07811 0-1.12605.45639-2.14551 1.19428-2.88349l-.82868-.82861c-.94994.95005-1.53748 2.26246-1.53748 3.7121 0 2.89951 2.35051 5.25001 5.25 5.25001 1.44979 0 2.76231-.5877 3.71241-1.5378z"/>
+    <path d="m5.87853 5.87883c-.5428.54288-.87853 1.29282-.87853 2.12117 0 1.65686 1.34315 3 3 3 .82844 0 1.57845-.3358 2.1213-.8787l-.82863-.8286c-.33083.33081-.78785.53543-1.29267.53543-1.00964 0-1.82812-.81848-1.82812-1.82813 0-.50476.20457-.96175.53533-1.29256z"/>
+    <path d="m9.8272 8.05881c.00062-.01952.00093-.03913.00093-.05881 0-1.00964-.81848-1.82812-1.82813-1.82812-.01968 0-.03928.00031-.05881.00093l-.98589-.9859c.32532-.12086.6773-.18691 1.0447-.18691 1.65686 0 3 1.34315 3 3 0 .3674-.066.71938-.1869 1.04471z"/>
+    <path d="m8 15.5c-4.14214 0-7.5-3.3579-7.5-7.5 0-2.07093.83935-3.94582 2.19643-5.30303l5.30357 5.30316z"/>
+    <path d="m8 6.23161v-5.73161c-1.61963 0-3.11935.51339-4.34531 1.38631z"/>
+  </g>
+  <path d="m14.1161 15.6245c-.0821.0001-.1634-.016-.2393-.0474-.0758-.0314-.1447-.0775-.2027-.1356l-12.749984-12.749c-.109266-.11882-.168406-.27526-.165071-.43666.003335-.16139.068886-.31525.182967-.42946.114078-.11421.267868-.17994.429258-.18345.16139-.00352.3179.05544.43685.16457l12.74998 12.75c.1168.1176.1824.2767.1824.4425s-.0656.3249-.1824.4425c-.058.058-.1269.1039-.2028.1352-.0759.0312-.1571.0471-.2392.0468z" fill="#ff0039"/>
+</svg>
diff --git a/browser/components/torconnect/content/onion.svg b/browser/components/torconnect/content/onion.svg
new file mode 100644
index 000000000000..382a061774aa
--- /dev/null
+++ b/browser/components/torconnect/content/onion.svg
@@ -0,0 +1,8 @@
+<svg fill="context-fill" fill-opacity="context-fill-opacity" viewBox="0 0 16 16" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
+  <g clip-rule="evenodd" fill-rule="evenodd">
+    <path d="m11 8c0 1.65686-1.34314 3-3 3-1.65685 0-3-1.34314-3-3 0-1.65685 1.34315-3 3-3 1.65686 0 3 1.34315 3 3zm-1.17187 0c0 1.00965-.81848 1.82813-1.82813 1.82813-1.00964 0-1.82812-.81848-1.82812-1.82813 0-1.00964.81848-1.82812 1.82812-1.82812 1.00965 0 1.82813.81848 1.82813 1.82812z"/>
+    <path d="m7.99999 13.25c2.89951 0 5.25001-2.3505 5.25001-5.25001 0-2.89949-2.3505-5.25-5.25001-5.25-2.89949 0-5.25 2.35051-5.25 5.25 0 2.89951 2.35051 5.25001 5.25 5.25001zm0-1.1719c2.25231 0 4.07811-1.8258 4.07811-4.07811 0-2.25228-1.8258-4.07812-4.07811-4.07812-2.25228 0-4.07812 1.82584-4.07812 4.07812 0 2.25231 1.82584 4.07811 4.07812 4.07811z"/>
+    <path d="m8 15.5c4.1421 0 7.5-3.3579 7.5-7.5 0-4.14214-3.3579-7.5-7.5-7.5-4.14214 0-7.5 3.35786-7.5 7.5 0 4.1421 3.35786 7.5 7.5 7.5zm0-1.1719c3.4949 0 6.3281-2.8332 6.3281-6.3281 0-3.49493-2.8332-6.32812-6.3281-6.32812-3.49493 0-6.32812 2.83319-6.32812 6.32812 0 3.4949 2.83319 6.3281 6.32812 6.3281z"/>
+  </g>
+  <path d="m.5 8c0 4.1421 3.35786 7.5 7.5 7.5v-15c-4.14214 0-7.5 3.35786-7.5 7.5z"/>
+</svg>
\ No newline at end of file
diff --git a/browser/components/torconnect/content/torBootstrapUrlbar.js b/browser/components/torconnect/content/torBootstrapUrlbar.js
new file mode 100644
index 000000000000..e6a88490f33d
--- /dev/null
+++ b/browser/components/torconnect/content/torBootstrapUrlbar.js
@@ -0,0 +1,93 @@
+// Copyright (c) 2021, The Tor Project, Inc.
+
+"use strict";
+
+const { TorConnect, TorConnectTopics, TorConnectState } = ChromeUtils.import(
+  "resource:///modules/TorConnect.jsm"
+);
+const { TorStrings } = ChromeUtils.import(
+  "resource:///modules/TorStrings.jsm"
+);
+
+var TorBootstrapUrlbar = {
+  selectors: Object.freeze({
+    torConnect: {
+      box: "hbox#torconnect-box",
+      label: "label#torconnect-label",
+    },
+  }),
+
+  elements: null,
+
+  updateTorConnectBox: function(state) {
+    switch(state)
+    {
+      case TorConnectState.Initial:
+      case TorConnectState.Configuring:
+      case TorConnectState.AutoConfiguring:
+      case TorConnectState.Error:
+      case TorConnectState.FatalError: {
+        this.elements.torConnectBox.removeAttribute("hidden");
+        this.elements.torConnectLabel.textContent =
+          TorStrings.torConnect.torNotConnectedConcise;
+        this.elements.inputContainer.setAttribute("torconnect", "offline");
+        break;
+      }
+      case TorConnectState.Bootstrapping: {
+        this.elements.torConnectBox.removeAttribute("hidden");
+        this.elements.torConnectLabel.textContent =
+          TorStrings.torConnect.torConnectingConcise;
+        this.elements.inputContainer.setAttribute("torconnect", "connecting");
+        break;
+      }
+      case TorConnectState.Bootstrapped: {
+        this.elements.torConnectBox.removeAttribute("hidden");
+        this.elements.torConnectLabel.textContent =
+          TorStrings.torConnect.torConnectedConcise;
+        this.elements.inputContainer.setAttribute("torconnect", "connected");
+        // hide torconnect box after 5 seconds
+        setTimeout(() => {
+          this.elements.torConnectBox.setAttribute("hidden", "true");
+        }, 5000);
+        break;
+      }
+      case TorConnectState.Disabled: {
+        this.elements.torConnectBox.setAttribute("hidden", "true");
+        break;
+      }
+      default:
+        break;
+    }
+  },
+
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic === TorConnectTopics.StateChange) {
+      const obj = aSubject?.wrappedJSObject;
+      this.updateTorConnectBox(obj?.state);
+    }
+  },
+
+  init: function() {
+    if (TorConnect.shouldShowTorConnect) {
+      // browser isn't populated until init
+      this.elements = Object.freeze({
+        torConnectBox: browser.ownerGlobal.document.querySelector(this.selectors.torConnect.box),
+        torConnectLabel: browser.ownerGlobal.document.querySelector(this.selectors.torConnect.label),
+        inputContainer: gURLBar._inputContainer,
+      })
+      this.elements.torConnectBox.addEventListener("click", () => {
+        TorConnect.openTorConnect();
+      });
+      Services.obs.addObserver(this, TorConnectTopics.StateChange);
+      this.observing = true;
+      this.updateTorConnectBox(TorConnect.state);
+    }
+  },
+
+  uninit: function() {
+    if (this.observing) {
+      Services.obs.removeObserver(this, TorConnectTopics.StateChange);
+    }
+  },
+};
+
diff --git a/browser/components/torconnect/content/torconnect-urlbar.css b/browser/components/torconnect/content/torconnect-urlbar.css
new file mode 100644
index 000000000000..5aabcffedbd0
--- /dev/null
+++ b/browser/components/torconnect/content/torconnect-urlbar.css
@@ -0,0 +1,57 @@
+/*
+    ensure our torconnect button is always visible (same rule as for the bookmark button)
+*/
+hbox.urlbar-page-action#torconnect-box {
+    display: -moz-inline-box!important;
+    height: 28px;
+}
+
+label#torconnect-label {
+    line-height: 28px;
+    margin: 0;
+    opacity: 0.6;
+    padding: 0 0.5em;
+}
+
+/* set appropriate sizes for the non-standard ui densities */
+:root[uidensity=compact] hbox.urlbar-page-action#torconnect-box {
+    height: 24px;
+}
+:root[uidensity=compact] label#torconnect-label {
+    line-height: 24px;
+}
+
+
+:root[uidensity=touch] hbox.urlbar-page-action#torconnect-box {
+    height: 30px;
+}
+:root[uidensity=touch] label#torconnect-label {
+    line-height: 30px;
+}
+
+
+/* hide when hidden attribute is set */
+hbox.urlbar-page-action#torconnect-box[hidden="true"],
+/* hide when user is typing in URL bar */
+#urlbar[usertyping] > #urlbar-input-container > #page-action-buttons > #torconnect-box {
+    display: none!important;
+}
+
+/* hide urlbar's placeholder text when not connectd to tor */
+hbox#urlbar-input-container[torconnect="offline"] input#urlbar-input::placeholder,
+hbox#urlbar-input-container[torconnect="connecting"] input#urlbar-input::placeholder {
+    opacity: 0;
+}
+
+/* hide search suggestions when not connected to tor */
+hbox#urlbar-input-container[torconnect="offline"] + vbox.urlbarView,
+hbox#urlbar-input-container[torconnect="connecting"] + vbox.urlbarView {
+    display: none!important;
+}
+
+/* hide search icon when we are not connected to tor */
+hbox#urlbar-input-container[torconnect="offline"] > #identity-box[pageproxystate="invalid"] > #identity-icon,
+hbox#urlbar-input-container[torconnect="connecting"] > #identity-box[pageproxystate="invalid"] > #identity-icon
+{
+    display: none!important;
+}
diff --git a/browser/components/torconnect/content/torconnect-urlbar.inc.xhtml b/browser/components/torconnect/content/torconnect-urlbar.inc.xhtml
new file mode 100644
index 000000000000..60e985a72691
--- /dev/null
+++ b/browser/components/torconnect/content/torconnect-urlbar.inc.xhtml
@@ -0,0 +1,10 @@
+# Copyright (c) 2021, The Tor Project, Inc.
+
+<hbox id="torconnect-box"
+      class="urlbar-icon-wrapper urlbar-page-action"
+      role="status"
+      hidden="true">
+    <hbox id="torconnect-container">
+        <label id="torconnect-label"/>
+    </hbox>
+</hbox>
\ No newline at end of file
diff --git a/browser/components/torconnect/jar.mn b/browser/components/torconnect/jar.mn
new file mode 100644
index 000000000000..ed8a4de299b2
--- /dev/null
+++ b/browser/components/torconnect/jar.mn
@@ -0,0 +1,7 @@
+browser.jar:
+    content/browser/torconnect/torBootstrapUrlbar.js               (content/torBootstrapUrlbar.js)
+    content/browser/torconnect/aboutTorConnect.css                 (content/aboutTorConnect.css)
+*   content/browser/torconnect/aboutTorConnect.xhtml               (content/aboutTorConnect.xhtml)
+    content/browser/torconnect/aboutTorConnect.js                  (content/aboutTorConnect.js)
+    content/browser/torconnect/onion.svg                           (content/onion.svg)
+    content/browser/torconnect/onion-slash.svg                     (content/onion-slash.svg)
diff --git a/browser/components/torconnect/moz.build b/browser/components/torconnect/moz.build
new file mode 100644
index 000000000000..eb29c31a4243
--- /dev/null
+++ b/browser/components/torconnect/moz.build
@@ -0,0 +1,6 @@
+JAR_MANIFESTS += ['jar.mn']
+
+EXTRA_JS_MODULES += [
+    'TorConnectChild.jsm',
+    'TorConnectParent.jsm',
+]
diff --git a/browser/components/torpreferences/content/torPane.js b/browser/components/torpreferences/content/torPane.js
index 49054b5dac6a..1f169cbe1a55 100644
--- a/browser/components/torpreferences/content/torPane.js
+++ b/browser/components/torpreferences/content/torPane.js
@@ -1,9 +1,15 @@
 "use strict";
 
+/* global Services */
+
 const { TorProtocolService } = ChromeUtils.import(
   "resource:///modules/TorProtocolService.jsm"
 );
 
+const { TorConnect, TorConnectTopics, TorConnectState } = ChromeUtils.import(
+  "resource:///modules/TorConnect.jsm"
+);
+
 const {
   TorBridgeSource,
   TorBridgeSettings,
@@ -51,6 +57,10 @@ const { parsePort, parseBridgeStrings, parsePortList } = ChromeUtils.import(
   "chrome://browser/content/torpreferences/parseFunctions.jsm"
 );
 
+const TorLauncherPrefs = {
+  quickstart: "extensions.torlauncher.quickstart",
+}
+
 /*
   Tor Pane
 
@@ -62,11 +72,21 @@ const gTorPane = (function() {
     category: {
       title: "label#torPreferences-labelCategory",
     },
+    messageBox: {
+      box: "div#torPreferences-connectMessageBox",
+      message: "td#torPreferences-connectMessageBox-message",
+      button: "button#torPreferences-connectMessageBox-button",
+    },
     torPreferences: {
       header: "h1#torPreferences-header",
       description: "span#torPreferences-description",
       learnMore: "label#torPreferences-learnMore",
     },
+    quickstart: {
+      header: "h2#torPreferences-quickstart-header",
+      description: "span#torPreferences-quickstart-description",
+      enableQuickstartCheckbox: "checkbox#torPreferences-quickstart-toggle",
+    },
     bridges: {
       header: "h2#torPreferences-bridges-header",
       description: "span#torPreferences-bridges-description",
@@ -112,6 +132,10 @@ const gTorPane = (function() {
 
   let retval = {
     // cached frequently accessed DOM elements
+    _messageBox: null,
+    _messageBoxMessage: null,
+    _messageBoxButton: null,
+    _enableQuickstartCheckbox: null,
     _useBridgeCheckbox: null,
     _bridgeSelectionRadiogroup: null,
     _builtinBridgeOption: null,
@@ -161,6 +185,51 @@ const gTorPane = (function() {
 
       let prefpane = document.getElementById("mainPrefPane");
 
+      // 'Connect to Tor' Message Bar
+
+      this._messageBox = prefpane.querySelector(selectors.messageBox.box);
+      this._messageBoxMessage = prefpane.querySelector(selectors.messageBox.message);
+      this._messageBoxButton = prefpane.querySelector(selectors.messageBox.button);
+      // wire up connect button
+      this._messageBoxButton.addEventListener("click", () => {
+        TorConnect.beginBootstrap();
+        TorConnect.openTorConnect();
+      });
+
+      this._populateMessagebox = () => {
+        if (TorConnect.shouldShowTorConnect &&
+            TorConnect.state === TorConnectState.Configuring) {
+          // set messagebox style and text
+          if (TorProtocolService.torBootstrapErrorOccurred()) {
+            this._messageBox.parentNode.style.display = null;
+            this._messageBox.className = "error";
+            this._messageBoxMessage.innerText = TorStrings.torConnect.tryAgainMessage;
+            this._messageBoxButton.innerText = TorStrings.torConnect.tryAgain;
+          } else {
+            this._messageBox.parentNode.style.display = null;
+            this._messageBox.className = "warning";
+            this._messageBoxMessage.innerText = TorStrings.torConnect.connectMessage;
+            this._messageBoxButton.innerText = TorStrings.torConnect.torConnectButton;
+          }
+        } else {
+          // we need to explicitly hide the groupbox, as switching between
+          // the tor pane and other panes will 'unhide' (via the 'hidden'
+          // attribute) the groupbox, offsetting all of the content down
+          // by the groupbox's margin (even if content is 0 height)
+          this._messageBox.parentNode.style.display = "none";
+          this._messageBox.className = "hidden";
+          this._messageBoxMessage.innerText = "";
+          this._messageBoxButton.innerText = "";
+        }
+      }
+      this._populateMessagebox();
+      Services.obs.addObserver(this, TorConnectTopics.StateChange);
+
+      // update the messagebox whenever we come back to the page
+      window.addEventListener("focus", val => {
+        this._populateMessagebox();
+      });
+
       // Heading
       prefpane.querySelector(selectors.torPreferences.header).innerText =
         TorStrings.settings.torPreferencesHeading;
@@ -177,6 +246,26 @@ const gTorPane = (function() {
         );
       }
 
+      // Quickstart
+      prefpane.querySelector(selectors.quickstart.header).innerText =
+        TorStrings.settings.quickstartHeading;
+      prefpane.querySelector(selectors.quickstart.description).textContent =
+        TorStrings.settings.quickstartDescription;
+
+      this._enableQuickstartCheckbox = prefpane.querySelector(
+        selectors.quickstart.enableQuickstartCheckbox
+      );
+      this._enableQuickstartCheckbox.setAttribute(
+        "label",
+        TorStrings.settings.quickstartCheckbox
+      );
+      this._enableQuickstartCheckbox.addEventListener("command", e => {
+        const checked = this._enableQuickstartCheckbox.checked;
+        Services.prefs.setBoolPref(TorLauncherPrefs.quickstart, checked);
+      });
+      this._enableQuickstartCheckbox.checked = Services.prefs.getBoolPref(TorLauncherPrefs.quickstart);
+      Services.prefs.addObserver(TorLauncherPrefs.quickstart, this);
+
       // Bridge setup
       prefpane.querySelector(selectors.bridges.header).innerText =
         TorStrings.settings.bridgesHeading;
@@ -526,6 +615,18 @@ const gTorPane = (function() {
 
     init() {
       this._populateXUL();
+
+      let onUnload = () => {
+        window.removeEventListener("unload", onUnload);
+        gTorPane.uninit();
+      };
+      window.addEventListener("unload", onUnload);
+    },
+
+    uninit() {
+      // unregister our observer topics
+      Services.obs.removeObserver(TorSettingsTopics.SettingChanged, this);
+      Services.obs.removeObserver(TorConnectTopics.StateChange, this);
     },
 
     // whether the page should be present in about:preferences
@@ -537,6 +638,28 @@ const gTorPane = (function() {
     // Callbacks
     //
 
+    observe(subject, topic, data) {
+      switch (topic) {
+       // triggered when a TorSettings param has changed
+        case TorSettingsTopics.SettingChanged: {
+          let obj = subject?.wrappedJSObject;
+          switch(data) {
+            case TorSettingsData.QuickStartEnabled: {
+              this._enableQuickstartCheckbox.checked = obj.value;
+              break;
+            }
+          }
+          break;
+        }
+        // triggered when tor connect state changes and we may
+        // need to update the messagebox
+        case TorConnectTopics.StateChange: {
+          this._populateMessagebox();
+          break;
+        }
+      }
+    },
+
     // callback when using bridges toggled
     onToggleBridge(enabled) {
       this._useBridgeCheckbox.checked = enabled;
diff --git a/browser/components/torpreferences/content/torPane.xhtml b/browser/components/torpreferences/content/torPane.xhtml
index 3c966b2b3726..7c8071f2cf10 100644
--- a/browser/components/torpreferences/content/torPane.xhtml
+++ b/browser/components/torpreferences/content/torPane.xhtml
@@ -3,6 +3,29 @@
 <script type="application/javascript"
         src="chrome://browser/content/torpreferences/torPane.js"/>
 <html:template id="template-paneTor">
+
+<!-- Tor Connect Message Box -->
+<groupbox data-category="paneTor" hidden="true">
+  <html:div id="torPreferences-connectMessageBox"
+            class="subcategory"
+            data-category="paneTor"
+            hidden="true">
+    <html:table >
+      <html:tr>
+        <html:td>
+          <html:div id="torPreferences-connectMessageBox-icon"/>
+        </html:td>
+        <html:td id="torPreferences-connectMessageBox-message">
+        </html:td>
+        <html:td>
+          <html:button id="torPreferences-connectMessageBox-button">
+          </html:button>
+        </html:td>
+      </html:tr>
+    </html:table>
+  </html:div>
+</groupbox>
+
 <hbox id="torPreferencesCategory"
       class="subcategory"
       data-category="paneTor"
@@ -18,6 +41,17 @@
   </description>
 </groupbox>
 
+<!-- Quickstart -->
+<groupbox id="torPreferences-quickstart-group"
+          data-category="paneTor"
+          hidden="true">
+  <html:h2 id="torPreferences-quickstart-header"/>
+  <description flex="1">
+    <html:span id="torPreferences-quickstart-description"/>
+  </description>
+  <checkbox id="torPreferences-quickstart-toggle"/>
+</groupbox>
+
 <!-- Bridges -->
 <groupbox id="torPreferences-bridges-group"
           data-category="paneTor"
diff --git a/browser/components/torpreferences/content/torPreferences.css b/browser/components/torpreferences/content/torPreferences.css
index 4dac2c457823..a977648f0810 100644
--- a/browser/components/torpreferences/content/torPreferences.css
+++ b/browser/components/torpreferences/content/torPreferences.css
@@ -1,7 +1,130 @@
+ at import url("chrome://branding/content/tor-styles.css");
+
 #category-tor > .category-icon {
   list-style-image: url("chrome://browser/content/torpreferences/torPreferencesIcon.svg");
 }
 
+/* Connect Message Box */
+
+#torPreferences-connectMessageBox {
+  display: block;
+  position: relative;
+
+  width: auto;
+  min-height: 32px;
+  border-radius: 4px;
+  padding: 4px;
+}
+
+#torPreferences-connectMessageBox.hidden {
+  display: none;
+}
+
+#torPreferences-connectMessageBox.error {
+  background-color: var(--red-60);
+  color: white;
+}
+
+#torPreferences-connectMessageBox.warning {
+  background-color: var(--purple-50);
+  color: white;
+}
+
+#torPreferences-connectMessageBox table {
+  border-collapse: collapse;
+  width: 100%;
+}
+
+#torPreferences-connectMessageBox td {
+  vertical-align: top;
+  padding: 0px;
+}
+
+#torPreferences-connectMessageBox td:first-child {
+  width: 24px;
+}
+
+#torPreferences-connectMessageBox-icon {
+  display: block;
+  width: 16px;
+  height: 16px;
+  padding: 4px;
+
+  mask-repeat: no-repeat !important;
+  mask-size: 16px !important;
+  mask-position: 4px 4px !important;
+}
+
+#torPreferences-connectMessageBox.error #torPreferences-connectMessageBox-icon
+{
+  mask: url("chrome://browser/skin/onion-slash.svg");
+  background-color: white;
+}
+
+#torPreferences-connectMessageBox.warning #torPreferences-connectMessageBox-icon
+{
+  mask: url("chrome://browser/skin/onion.svg");
+  background-color: white;
+}
+
+#torPreferences-connectMessageBox-message {
+  display: block;
+  line-height: 16px;
+  font-size: 1.0em;
+  margin-right: 8px;
+  padding-left: 4px!important;
+  padding-top: 4px!important;
+}
+
+#torPreferences-connectMessageBox-button {
+  display: block;
+  width: auto;
+  height: 24px;
+  line-height: 24px;
+  min-height: 24px;
+  max-height: 24px;
+  min-width: 0px;
+  margin: 0px;
+
+  border-radius: 4px;
+  border: 0;
+  padding: 0px 1.25em;
+  margin-left: auto;
+  margin-right: 0px;
+
+  font-size: 0.9em;
+  font-weight: 600;
+  white-space: nowrap;
+
+  color: white;
+}
+
+#torPreferences-connectMessageBox.error #torPreferences-connectMessageBox-button {
+  background-color: var(--red-70);
+}
+
+#torPreferences-connectMessageBox.error #torPreferences-connectMessageBox-button:hover {
+  background-color: var(--red-80);
+}
+
+#torPreferences-connectMessageBox.error #torPreferences-connectMessageBox-button:active {
+  background-color: var(--red-90);
+}
+
+#torPreferences-connectMessageBox.warning #torPreferences-connectMessageBox-button {
+  background-color: var(--purple-70);
+}
+
+#torPreferences-connectMessageBox.warning #torPreferences-connectMessageBox-button:hover {
+  background-color: var(--purple-80);
+}
+
+#torPreferences-connectMessageBox.warning #torPreferences-connectMessageBox-button:active {
+  background-color: var(--purple-90);
+}
+
+/* Advanced Settings */
+
 #torPreferences-advanced-grid {
   display: grid;
   grid-template-columns: auto 1fr;
diff --git a/browser/components/urlbar/UrlbarInput.jsm b/browser/components/urlbar/UrlbarInput.jsm
index 27261544127c..e7ab9db0a66c 100644
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -10,6 +10,34 @@ const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 
+const { TorConnect } = ChromeUtils.import(
+  "resource:///modules/TorConnect.jsm"
+);
+
+// in certain scenarios we want user input uris to open in a new tab if they do so from the
+// about:torconnect tab 
+function maybeUpdateOpenLocationForTorConnect(openUILinkWhere, currentURI, destinationURI) {
+  try {
+    // only open in new tab if:
+    if (// user is navigating away from about:torconnect
+        currentURI === "about:torconnect" &&
+        // we are trying to open in same tab
+        openUILinkWhere === "current" &&
+        // only if user still has not bootstrapped
+        TorConnect.shouldShowTorConnect &&
+        // and user is not just navigating to about:torconnect
+        destinationURI !== "about:torconnect") {
+      return "tab";
+    }
+  } catch (e) {
+    // swallow exception and fall through returning original so we don't accidentally break
+    // anything if an exception is thrown
+    console.log(e?.message ? e.message : e);
+  }
+
+  return openUILinkWhere;
+};
+
 XPCOMUtils.defineLazyModuleGetters(this, {
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   BrowserSearchTelemetry: "resource:///modules/BrowserSearchTelemetry.jsm",
@@ -2416,6 +2444,10 @@ class UrlbarInput {
       this.selectionStart = this.selectionEnd = 0;
     }
 
+    openUILinkWhere = maybeUpdateOpenLocationForTorConnect(
+                        openUILinkWhere,
+                        this.window.gBrowser.currentURI.asciiSpec,
+                        url);
     if (openUILinkWhere != "current") {
       this.handleRevert();
     }
diff --git a/browser/modules/TorConnect.jsm b/browser/modules/TorConnect.jsm
new file mode 100644
index 000000000000..19a3f595d490
--- /dev/null
+++ b/browser/modules/TorConnect.jsm
@@ -0,0 +1,506 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["TorConnect", "TorConnectTopics", "TorConnectState"];
+
+const { Services } = ChromeUtils.import(
+    "resource://gre/modules/Services.jsm"
+);
+
+const { BrowserWindowTracker } = ChromeUtils.import(
+    "resource:///modules/BrowserWindowTracker.jsm"
+);
+
+const { TorProtocolService, TorProcessStatus } = ChromeUtils.import(
+    "resource:///modules/TorProtocolService.jsm"
+);
+
+const { TorLauncherUtil } = ChromeUtils.import(
+    "resource://torlauncher/modules/tl-util.jsm"
+);
+
+/* Browser observer topis */
+const BrowserTopics = Object.freeze({
+    ProfileAfterChange: "profile-after-change",
+});
+
+/* tor-launcher observer topics */
+const TorTopics = Object.freeze({
+    ProcessIsReady: "TorProcessIsReady",
+    BootstrapStatus: "TorBootstrapStatus",
+    BootstrapError: "TorBootstrapError",
+    ProcessExited: "TorProcessExited",
+    LogHasWarnOrErr: "TorLogHasWarnOrErr",
+});
+
+/* Relevant prefs used by tor-launcher */
+const TorLauncherPrefs = Object.freeze({
+  quickstart: "extensions.torlauncher.quickstart",
+  prompt_at_startup: "extensions.torlauncher.prompt_at_startup",
+});
+
+const TorConnectState = Object.freeze({
+    /* Our initial state */
+    Initial: "Initial",
+    /* In-between initial boot and bootstrapping, users can change tor network settings during this state */
+    Configuring: "Configuring",
+    /* Geo-location and setting bridges/etc */
+    AutoConfiguring: "AutoConfiguring",
+    /* Tor is bootstrapping */
+    Bootstrapping: "Bootstrapping",
+    /* Passthrough state back to Configuring or Fatal */
+    Error: "Error",
+    /* An unrecoverable error */
+    FatalError: "FatalError",
+    /* Final state, after successful bootstrap */
+    Bootstrapped: "Bootstrapped",
+    /* If we are using System tor or the legacy Tor-Launcher */
+    Disabled: "Disabled",
+});
+
+/*
+
+                                               TorConnect State Transitions
+
+                                              ┌──────────────────────┐
+                                              │       Disabled       │
+                                              └──────────────────────┘
+                                                â–²
+                                                │ legacyOrSystemTor()
+                                                │
+                                              ┌──────────────────────┐
+                      ┌────────────────────── │       Initial        │ ───────────────────────────┐
+                      │                       └──────────────────────┘                            │
+                      │                         │                                                 │
+                      │                         │ beginBootstrap()                                │
+                      │                         ▼                                                 │
+┌────────────────┐    │  bootstrapComplete()  ┌────────────────────────────────────────────────┐  │  beginBootstrap()
+│  Bootstrapped  │ ◀──┼────────────────────── │                 Bootstrapping                  │ ◀┼─────────────────┐
+└────────────────┘    │                       └────────────────────────────────────────────────┘  │                 │
+                      │                         │                       ▲                    │    │                 │
+                      │                         │ cancelBootstrap()     │ beginBootstrap()   └────┼─────────────┐   │
+                      │                         ▼                       │                         │             │   │
+                      │   beginConfigure()    ┌────────────────────────────────────────────────┐  │             │   │
+                      └─────────────────────▶ │                                                │  │             │   │
+                                              │                                                │  │             │   │
+                       beginConfigure()       │                                                │  │             │   │
+                 ┌──────────────────────────▶ │                  Configuring                   │  │             │   │
+                 │                            │                                                │  │             │   │
+                 │                            │                                                │  │             │   │
+                 │    ┌─────────────────────▶ │                                                │  │             │   │
+                 │    │                       └────────────────────────────────────────────────┘  │             │   │
+                 │    │                         │                       │                         │             │   │
+                 │    │ cancelAutoconfigure()   │ autoConfigure()       │                    ┌────┼─────────────┼───┘
+                 │    │                         ▼                       │                    │    │             │
+                 │    │                       ┌──────────────────────┐  │                    │    │             │
+                 │    └────────────────────── │   AutoConfiguring    │ ─┼────────────────────┘    │             │
+                 │                            └──────────────────────┘  │                         │             │
+                 │                              │                       │                         │ onError()   │
+                 │                              │ onError()             │ onError()               │             │
+                 │                              ▼                       ▼                         │             │
+                 │                            ┌────────────────────────────────────────────────┐  │             │
+                 └─────────────────────────── │                     Error                      │ ◀┘             │
+                                              └────────────────────────────────────────────────┘                │
+                                                │                                            ▲   onError()      │
+                                                │ onFatalError()                             └──────────────────┘
+                                                â–¼
+                                              ┌──────────────────────┐
+                                              │      FatalError      │
+                                              └──────────────────────┘
+
+*/
+
+
+/* Maps allowed state transitions
+   TorConnectStateTransitions[state] maps to an array of allowed states to transition to
+*/
+const TorConnectStateTransitions =
+    Object.freeze(new Map([
+        [TorConnectState.Initial,
+            [TorConnectState.Disabled,
+             TorConnectState.Bootstrapping,
+             TorConnectState.Configuring,
+             TorConnectState.Error]],
+        [TorConnectState.Configuring,
+            [TorConnectState.AutoConfiguring,
+             TorConnectState.Bootstrapping,
+             TorConnectState.Error]],
+        [TorConnectState.AutoConfiguring,
+            [TorConnectState.Configuring,
+             TorConnectState.Bootstrapping,
+             TorConnectState.Error]],
+        [TorConnectState.Bootstrapping,
+            [TorConnectState.Configuring,
+             TorConnectState.Bootstrapped,
+             TorConnectState.Error]],
+        [TorConnectState.Error,
+            [TorConnectState.Configuring,
+             TorConnectState.FatalError]],
+        // terminal states
+        [TorConnectState.FatalError, []],
+        [TorConnectState.Bootstrapped, []],
+        [TorConnectState.Disabled, []],
+    ]));
+
+/* Topics Notified by the TorConnect module */
+const TorConnectTopics = Object.freeze({
+    StateChange: "torconnect:state-change",
+    BootstrapProgress: "torconnect:bootstrap-progress",
+    BootstrapComplete: "torconnect:bootstrap-complete",
+    BootstrapError: "torconnect:bootstrap-error",
+    FatalError: "torconnect:fatal-error",
+});
+
+const TorConnect = (() => {
+    let retval = {
+
+        _state: TorConnectState.Initial,
+        _bootstrapProgress: 0,
+        _bootstrapStatus: null,
+        _errorMessage: null,
+        _errorDetails: null,
+        _logHasWarningOrError: false,
+
+        /* These functions are called after transitioning to a new state */
+        _transitionCallbacks: Object.freeze(new Map([
+            /* Initial is never transitioned to */
+            [TorConnectState.Initial, null],
+            /* Configuring */
+            [TorConnectState.Configuring, (self, prevState) => {
+                // TODO move this to the transition function
+                if (prevState === TorConnectState.Bootstrapping) {
+                    TorProtocolService.torStopBootstrap();
+                }
+            }],
+            /* AutoConfiguring */
+            [TorConnectState.AutoConfiguring, (self, prevState) => {
+
+            }],
+            /* Bootstrapping */
+            [TorConnectState.Bootstrapping, (self, prevState) => {
+                let error = TorProtocolService.connect();
+                if (error) {
+                    self.onError(error.message, error.details);
+                } else {
+                    self._errorMessage = self._errorDetails = null;
+                }
+            }],
+            /* Bootstrapped */
+            [TorConnectState.Bootstrapped, (self,prevState) => {
+                // notify observers of bootstrap completion
+                Services.obs.notifyObservers(null, TorConnectTopics.BootstrapComplete);
+            }],
+            /* Error */
+            [TorConnectState.Error, (self, prevState, errorMessage, errorDetails, fatal) => {
+                self._errorMessage = errorMessage;
+                self._errorDetails = errorDetails;
+
+                Services.obs.notifyObservers({message: errorMessage, details: errorDetails}, TorConnectTopics.BootstrapError);
+                if (fatal) {
+                    self.onFatalError();
+                } else {
+                    self.beginConfigure();
+                }
+            }],
+            /* FatalError */
+            [TorConnectState.FatalError, (self, prevState) => {
+                Services.obs.notifyObservers(null, TorConnectTopics.FatalError);
+            }],
+            /* Disabled */
+            [TorConnectState.Disabled, (self, prevState) => {
+
+            }],
+        ])),
+
+        _changeState: function(newState, ...args) {
+            const prevState = this._state;
+
+            // ensure this is a valid state transition
+            if (!TorConnectStateTransitions.get(prevState)?.includes(newState)) {
+                throw Error(`TorConnect: Attempted invalid state transition from ${prevState} to ${newState}`);
+            }
+
+            console.log(`TorConnect: transitioning state from ${prevState} to ${newState}`);
+
+            // set our new state first so that state transitions can themselves trigger
+            // a state transition
+            this._state = newState;
+
+            // call our transition function and forward any args
+            this._transitionCallbacks.get(newState)(this, prevState, ...args);
+
+            Services.obs.notifyObservers({state: newState}, TorConnectTopics.StateChange);
+        },
+
+        // init should be called on app-startup in MainProcessingSingleton.jsm
+        init : function() {
+            console.log("TorConnect: Init");
+
+            // delay remaining init until after profile-after-change
+            Services.obs.addObserver(this, BrowserTopics.ProfileAfterChange);
+        },
+
+        observe: async function(subject, topic, data) {
+            console.log(`TorConnect: observed ${topic}`);
+
+            switch(topic) {
+
+            /* Determine which state to move to from Initial */
+            case BrowserTopics.ProfileAfterChange: {
+                if (TorLauncherUtil.useLegacyLauncher || !TorProtocolService.ownsTorDaemon) {
+                    // Disabled
+                    this.legacyOrSystemTor();
+                } else {
+                    // register the Tor topics we always care about
+                    for (const topicKey in TorTopics) {
+                        const topic = TorTopics[topicKey];
+                        Services.obs.addObserver(this, topic);
+                        console.log(`TorConnect: observing topic '${topic}'`);
+                    }
+
+                    if (TorProtocolService.torProcessStatus == TorProcessStatus.Running) {
+                        if (this.shouldQuickStart) {
+                            // Quickstart
+                            this.beginBootstrap();
+                        } else {
+                            // Configuring
+                            this.beginConfigure();
+                        }
+                    }
+                }
+
+                Services.obs.removeObserver(this, topic);
+                break;
+            }
+            /* Transition out of Initial if Tor daemon wasn't running yet in BrowserTopics.ProfileAfterChange */
+            case TorTopics.ProcessIsReady: {
+                if (this.state === TorConnectState.Initial)
+                {
+                    if (this.shouldQuickStart) {
+                        // Quickstart
+                        this.beginBootstrap();
+                    } else {
+                        // Configuring
+                        this.beginConfigure();
+                    }
+                }
+                break;
+            }
+            /* Updates our bootstrap status */
+            case TorTopics.BootstrapStatus: {
+                if (this._state != TorConnectState.Bootstrapping) {
+                    console.log(`TorConnect: observed ${TorTopics.BootstrapStatus} topic while in state TorConnectState.${this._state}`);
+                    break;
+                }
+
+                const obj = subject?.wrappedJSObject;
+                if (obj) {
+                    this._bootstrapProgress= obj.PROGRESS;
+                    this._bootstrapStatus = TorLauncherUtil.getLocalizedBootstrapStatus(obj, "TAG");
+
+                    console.log(`TorConnect: Bootstrapping ${this._bootstrapProgress}% complete (${this._bootstrapStatus})`);
+                    Services.obs.notifyObservers({
+                        progress: this._bootstrapProgress,
+                        status: this._bootstrapStatus,
+                        hasWarnings: this._logHasWarningOrError
+                    }, TorConnectTopics.BootstrapProgress);
+
+                    if (this._bootstrapProgress === 100) {
+                        this.bootstrapComplete();
+                    }
+                }
+                break;
+            }
+            /* Handle bootstrap error*/
+            case TorTopics.BootstrapError: {
+                const obj = subject?.wrappedJSObject;
+                await TorProtocolService.torStopBootstrap();
+                this.onError(obj.message, obj.details);
+                break;
+            }
+            case TorTopics.LogHasWarnOrErr: {
+                this._logHasWarningOrError = true;
+                break;
+            }
+            default:
+                // ignore
+                break;
+            }
+        },
+
+        /*
+        Various getters
+        */
+
+        get shouldShowTorConnect() {
+                   // TorBrowser must control the daemon
+            return (TorProtocolService.ownsTorDaemon &&
+                   // and we're not using the legacy launcher
+                   !TorLauncherUtil.useLegacyLauncher &&
+                   // if we have succesfully bootstraped, then no need to show TorConnect
+                   this.state != TorConnectState.Bootstrapped);
+        },
+
+        get shouldQuickStart() {
+                   // quickstart must be enabled
+            return Services.prefs.getBoolPref(TorLauncherPrefs.quickstart, false) &&
+                   // and the previous bootstrap attempt must have succeeded
+                   !Services.prefs.getBoolPref(TorLauncherPrefs.prompt_at_startup, true);
+        },
+
+        get state() {
+            return this._state;
+        },
+
+        get bootstrapProgress() {
+            return this._bootstrapProgress;
+        },
+
+        get bootstrapStatus() {
+            return this._bootstrapStatus;
+        },
+
+        get errorMessage() {
+            return this._errorMessage;
+        },
+
+        get errorDetails() {
+            return this._errorDetails;
+        },
+
+        get logHasWarningOrError() {
+            return this._logHasWarningOrError;
+        },
+
+        /*
+        These functions tell TorConnect to transition states
+        */
+
+        legacyOrSystemTor: function() {
+            console.log("TorConnect: legacyOrSystemTor()");
+            this._changeState(TorConnectState.Disabled);
+        },
+
+        beginBootstrap: function() {
+            console.log("TorConnect: beginBootstrap()");
+            this._changeState(TorConnectState.Bootstrapping);
+        },
+
+        beginConfigure: function() {
+            console.log("TorConnect: beginConfigure()");
+            this._changeState(TorConnectState.Configuring);
+        },
+
+        autoConfigure: function() {
+            console.log("TorConnect: autoConfigure()");
+            // TODO: implement
+            throw Error("TorConnect: not implemented");
+        },
+
+        cancelAutoConfigure: function() {
+            console.log("TorConnect: cancelAutoConfigure()");
+            // TODO: implement
+            throw Error("TorConnect: not implemented");
+        },
+
+        cancelBootstrap: function() {
+            console.log("TorConnect: cancelBootstrap()");
+            this._changeState(TorConnectState.Configuring);
+        },
+
+        bootstrapComplete: function() {
+            console.log("TorConnect: bootstrapComplete()");
+            this._changeState(TorConnectState.Bootstrapped);
+        },
+
+        onError: function(message, details) {
+            console.log("TorConnect: onError()");
+            this._changeState(TorConnectState.Error, message, details, false);
+        },
+
+        onFatalError: function() {
+            console.log("TorConnect: onFatalError()");
+            // TODO: implement
+            throw Error("TorConnect: not implemented");
+        },
+
+        /*
+        Further external commands and helper methods
+        */
+        openTorPreferences: function() {
+            const win = BrowserWindowTracker.getTopWindow();
+            win.switchToTabHavingURI("about:preferences#tor", true);
+        },
+
+        openTorConnect: function() {
+            const win = BrowserWindowTracker.getTopWindow();
+            win.switchToTabHavingURI("about:torconnect", true, {ignoreQueryString: true});
+        },
+
+        copyTorLogs: function() {
+            // Copy tor log messages to the system clipboard.
+            const chSvc = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
+              Ci.nsIClipboardHelper
+            );
+            const countObj = { value: 0 };
+            chSvc.copyString(TorProtocolService.getLog(countObj));
+            const count = countObj.value;
+            return TorLauncherUtil.getFormattedLocalizedString(
+              "copiedNLogMessagesShort",
+              [count],
+              1
+            );
+        },
+
+        // called from browser.js on browser startup, passed in either the user's homepage(s)
+        // or uris passed via command-line; we want to replace them with about:torconnect uris
+        // which redirect after bootstrapping
+        getURIsToLoad: function(uriVariant) {
+            // convert the object we get from browser.js
+            let uriStrings = ((v) => {
+                // an interop array
+                if (v instanceof Ci.nsIArray) {
+                    // Transform the nsIArray of nsISupportsString's into a JS Array of
+                    // JS strings.
+                    return Array.from(
+                      v.enumerate(Ci.nsISupportsString),
+                      supportStr => supportStr.data
+                    );
+                // an interop string
+                } else if (v instanceof Ci.nsISupportsString) {
+                    return [v.data];
+                // a js string
+                } else if (typeof v === "string") {
+                    return v.split("|");
+                // a js array of js strings
+                } else if (Array.isArray(v) &&
+                           v.reduce((allStrings, entry) => {return allStrings && (typeof entry === "string");}, true)) {
+                    return v;
+                }
+                // about:tor as safe fallback
+                console.log(`TorConnect: getURIsToLoad() received unknown variant '${JSON.stringify(v)}'`);
+                return ["about:tor"];
+            })(uriVariant);
+
+            // will attempt to convert user-supplied string to a uri, fallback to about:tor if cannot convert
+            // to valid uri object
+            let uriStringToUri = (uriString) => {
+                const fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_NONE;
+                let uri = Services.uriFixup.getFixupURIInfo(uriString, fixupFlags)
+                  .preferredURI;
+                return uri ? uri : Services.io.newURI("about:tor");
+            };
+            let uris = uriStrings.map(uriStringToUri);
+
+            // assume we have a valid uri and generate an about:torconnect redirect uri
+            let uriToRedirectUri = (uri) => {
+                return`about:torconnect?redirect=${encodeURIComponent(uri.spec)}`;
+            };
+            let redirectUris = uris.map(uriToRedirectUri);
+
+            console.log(`TorConnect: will load after bootstrap => [${uris.map((uri) => {return uri.spec;}).join(", ")}]`);
+            return redirectUris;
+        },
+    };
+    retval.init();
+    return retval;
+})(); /* TorConnect */
diff --git a/browser/modules/TorProcessService.jsm b/browser/modules/TorProcessService.jsm
new file mode 100644
index 000000000000..201e331b2806
--- /dev/null
+++ b/browser/modules/TorProcessService.jsm
@@ -0,0 +1,12 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["TorProcessService"];
+
+var TorProcessService = {
+  get isBootstrapDone() {
+    const svc = Cc["@torproject.org/torlauncher-process-service;1"].getService(
+      Ci.nsISupports
+    ).wrappedJSObject;
+    return svc.mIsBootstrapDone;
+  },
+};
diff --git a/browser/modules/TorProtocolService.jsm b/browser/modules/TorProtocolService.jsm
index 409d6be60b83..b8678fbca9aa 100644
--- a/browser/modules/TorProtocolService.jsm
+++ b/browser/modules/TorProtocolService.jsm
@@ -1,21 +1,60 @@
+// Copyright (c) 2021, The Tor Project, Inc.
+
 "use strict";
 
-var EXPORTED_SYMBOLS = ["TorProtocolService"];
+var EXPORTED_SYMBOLS = ["TorProtocolService", "TorProcessStatus"];
 
-const { TorLauncherUtil } = ChromeUtils.import(
-  "resource://torlauncher/modules/tl-util.jsm"
+const { Services } = ChromeUtils.import(
+    "resource://gre/modules/Services.jsm"
 );
 
+// see tl-process.js
+const TorProcessStatus = Object.freeze({
+  Unknown: 0,
+  Starting: 1,
+  Running: 2,
+  Exited: 3,
+});
+
+/* Browser observer topis */
+const BrowserTopics = Object.freeze({
+    ProfileAfterChange: "profile-after-change",
+});
+
 var TorProtocolService = {
-  _tlps: Cc["@torproject.org/torlauncher-protocol-service;1"].getService(
-    Ci.nsISupports
-  ).wrappedJSObject,
+  _TorLauncherUtil: function() {
+      let { TorLauncherUtil } = ChromeUtils.import(
+        "resource://torlauncher/modules/tl-util.jsm"
+      );
+      return TorLauncherUtil;
+    }(),
+  _TorLauncherProtocolService: null,
+  _TorProcessService: null,
 
   // maintain a map of tor settings set by Tor Browser so that we don't
   // repeatedly set the same key/values over and over
   // this map contains string keys to primitive or array values
   _settingsCache: new Map(),
 
+  init() {
+    Services.obs.addObserver(this, BrowserTopics.ProfileAfterChange);
+  },
+
+  observe(subject, topic, data) {
+    if (topic === BrowserTopics.ProfileAfterChange) {
+      // we have to delay init'ing this or else the crypto service inits too early without a profile
+      // which breaks the password manager
+      this._TorLauncherProtocolService = Cc["@torproject.org/torlauncher-protocol-service;1"].getService(
+        Ci.nsISupports
+      ).wrappedJSObject;
+      this._TorProcessService = Cc["@torproject.org/torlauncher-process-service;1"].getService(
+        Ci.nsISupports
+      ).wrappedJSObject,
+
+      Services.obs.removeObserver(this, topic);
+    }
+  },
+
   _typeof(aValue) {
     switch (typeof aValue) {
       case "boolean":
@@ -199,9 +238,9 @@ var TorProtocolService = {
     await this.sendCommand("SAVECONF");
   },
 
-  getLog() {
-    let countObj = { value: 0 };
-    let torLog = this._tlps.TorGetLog(countObj);
+  getLog(countObj) {
+    countObj = countObj || { value: 0 };
+    let torLog = this._TorLauncherProtocolService.TorGetLog(countObj);
     return torLog;
   },
 
@@ -321,3 +360,4 @@ var TorProtocolService = {
     return TorProcessStatus.Unknown;
   },
 };
+TorProtocolService.init();
\ No newline at end of file
diff --git a/browser/modules/TorStrings.jsm b/browser/modules/TorStrings.jsm
index cc4f6b340c5f..73671c08693d 100644
--- a/browser/modules/TorStrings.jsm
+++ b/browser/modules/TorStrings.jsm
@@ -261,6 +261,9 @@ var TorStrings = {
         "Tor Browser routes your traffic over the Tor Network, run by thousands of volunteers around the world."
       ),
       learnMore: getString("torPreferences.learnMore", "Learn More"),
+      quickstartHeading: getString("torPreferences.quickstart", "Quickstart"),
+      quickstartDescription: getString("torPreferences.quickstartDescription", "Quickstart allows Tor Browser to connect automatically."),
+      quickstartCheckbox : getString("torPreferences.quickstartCheckbox", "Always connect automatically"),
       bridgesHeading: getString("torPreferences.bridges", "Bridges"),
       bridgesDescription: getString(
         "torPreferences.bridgesDescription",
@@ -368,6 +371,83 @@ var TorStrings = {
     return retval;
   })() /* Tor Network Settings Strings */,
 
+  torConnect: (() => {
+    const tsbNetwork = new TorDTDStringBundle(
+      ["chrome://torlauncher/locale/network-settings.dtd"],
+      ""
+    );
+    const tsbLauncher = new TorPropertyStringBundle(
+      "chrome://torlauncher/locale/torlauncher.properties",
+      "torlauncher."
+    );
+    const tsbCommon = new TorPropertyStringBundle(
+      "chrome://global/locale/commonDialogs.properties",
+      ""
+    );
+
+    const getStringNet = tsbNetwork.getString.bind(tsbNetwork);
+    const getStringLauncher = tsbLauncher.getString.bind(tsbLauncher);
+    const getStringCommon = tsbCommon.getString.bind(tsbCommon);
+
+    return {
+      torConnect: getStringNet(
+        "torsettings.wizard.title.default",
+        "Connect to Tor"
+      ),
+
+      torConnecting: getStringNet(
+        "torsettings.wizard.title.connecting",
+        "Establishing a Connection"
+      ),
+
+      torNotConnectedConcise: getStringNet(
+        "torConnect.notConnectedConcise",
+        "Not Connected"
+      ),
+
+      torConnectingConcise: getStringNet(
+        "torConnect.connectingConcise",
+        "Connecting…"
+      ),
+
+      torBootstrapFailed: getStringLauncher(
+        "tor_bootstrap_failed",
+        "Tor failed to establish a Tor network connection."
+      ),
+
+      torConfigure: getStringNet(
+        "torsettings.wizard.title.configure",
+        "Tor Network Settings"
+      ),
+
+      copyLog: getStringNet(
+        "torConnect.copyLog",
+        "Copy Tor Logs"
+      ),
+
+      torConnectButton: getStringNet("torSettings.connect", "Connect"),
+
+      cancel: getStringCommon("Cancel", "Cancel"),
+
+      torConnected: getStringLauncher(
+        "torlauncher.bootstrapStatus.done",
+        "Connected to the Tor network"
+      ),
+
+      torConnectedConcise: getStringLauncher(
+        "torConnect.connectedConcise",
+        "Connected"
+      ),
+
+      tryAgain: getStringNet("torConnect.tryAgain", "Try connecting again"),
+      offline: getStringNet("torConnect.offline", "Offline"),
+
+      // tor connect strings for message box in about:preferences#tor
+      connectMessage: getStringNet("torConnect.connectMessage", "Changes to Tor Settings will not take effect until you connect to the Tor Network"),
+      tryAgainMessage: getStringNet("torConnect.tryAgainMessage", "Tor Browser has failed to establish a connection to the Tor Network"),
+    };
+  })(),
+
   /*
     Tor Onion Services Strings, e.g., for the authentication prompt.
   */
diff --git a/browser/modules/moz.build b/browser/modules/moz.build
index 1f7c6bc4c67e..1ea57aba1a93 100644
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -153,6 +153,8 @@ EXTRA_JS_MODULES += [
     "TabsList.jsm",
     "TabUnloader.jsm",
     "ThemeVariableMap.jsm",
+    'TorConnect.jsm',
+    'TorProcessService.jsm',
     "TorProtocolService.jsm",
     "TorStrings.jsm",
     "TransientPrefs.jsm",
diff --git a/browser/themes/shared/identity-block/identity-block.inc.css b/browser/themes/shared/identity-block/identity-block.inc.css
index a863d1d7d20e..145ea2732846 100644
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -53,16 +53,15 @@
   border-radius: var(--urlbar-icon-border-radius);
 }
 
-%ifdef MOZ_OFFICIAL_BRANDING
 #identity-box[pageproxystate="valid"].notSecureText #identity-icon-label,
 #identity-box[pageproxystate="valid"].chromeUI #identity-icon-label {
-  color: #420C5D;
+  color: var(--tor-branding-color);
+  opacity: 1;
 }
 
 toolbar[brighttext] #identity-box[pageproxystate="valid"].chromeUI #identity-icon-label {
   color: #CC80FF;
 }
-%endif
 
 #identity-box[pageproxystate="valid"].chromeUI #identity-icon-label,
 #identity-box[pageproxystate="valid"].extensionPage #identity-icon-label,
@@ -161,6 +160,8 @@ toolbar[brighttext] #identity-box[pageproxystate="valid"].chromeUI #identity-ico
 
 #identity-box[pageproxystate="valid"].chromeUI #identity-icon {
   list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
+  fill: var(--tor-branding-color);
+  fill-opacity: 1;
 }
 
 #identity-box[pageproxystate="valid"].localResource #identity-icon {
diff --git a/browser/themes/shared/jar.inc.mn b/browser/themes/shared/jar.inc.mn
index 3b11a9864cf8..b2e469b90aa8 100644
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -9,6 +9,8 @@
 
   skin/classic/browser/aboutNetError.css                       (../shared/aboutNetError.css)
   skin/classic/browser/offlineSupportPages.css                 (../shared/offlineSupportPages.css)
+  skin/classic/browser/onionPattern.css                        (../shared/onionPattern.css)
+  skin/classic/browser/onionPattern.svg                        (../shared/onionPattern.svg)
   skin/classic/browser/blockedSite.css                         (../shared/blockedSite.css)
   skin/classic/browser/error-pages.css                         (../shared/error-pages.css)
   skin/classic/browser/aboutRestartRequired.css                (../shared/aboutRestartRequired.css)
diff --git a/browser/themes/shared/onionPattern.css b/browser/themes/shared/onionPattern.css
new file mode 100644
index 000000000000..1852350d57f7
--- /dev/null
+++ b/browser/themes/shared/onionPattern.css
@@ -0,0 +1,31 @@
+/* Onion pattern */
+
+.onion-pattern-container {
+
+  flex: auto;           /* grow to consume remaining space on the page */
+  display: flex;
+  margin: 0 auto;
+  width: 100%;
+  /* two onions tall, 4x the radius */
+  height: calc(4 * var(--onion-radius));
+  max-height: calc(4 * var(--onion-radius));
+  min-height: calc(4 * var(--onion-radius));
+  direction: ltr;
+}
+
+.onion-pattern-crop {
+  height: 100%;
+  width: 100%;
+
+  -moz-context-properties: fill;
+  fill: var(--onion-color, currentColor);
+  /* opacity of the entire div, not context-opacity */
+  opacity: var(--onion-opacity, 1);
+
+  background-image: url("chrome://browser/skin/onionPattern.svg");
+  background-repeat: repeat;
+  background-attachment: local;
+  background-position: center;
+  /* svg source is 6 onions wide and 2 onions tall */
+  background-size: calc(6 * 2 * var(--onion-radius)) calc(2 * 2 * var(--onion-radius));;
+}
\ No newline at end of file
diff --git a/browser/themes/shared/onionPattern.inc.xhtml b/browser/themes/shared/onionPattern.inc.xhtml
new file mode 100644
index 000000000000..de57b6ee301a
--- /dev/null
+++ b/browser/themes/shared/onionPattern.inc.xhtml
@@ -0,0 +1,12 @@
+<!--
+  Container div that holds onionPattern.svg
+  It is expected the includer of this xhtml file also includes onionPattern.css
+  and define the following vars:
+    onion-radius : radius of an onion
+    onion-color : the base color of the onion pattern
+    onion-opacity : the opacity of the entire repeating pattern
+-->
+
+<div class="onion-pattern-container">
+  <div class="onion-pattern-crop"/>
+</div>
\ No newline at end of file
diff --git a/browser/themes/shared/onionPattern.svg b/browser/themes/shared/onionPattern.svg
new file mode 100644
index 000000000000..e2937b175341
--- /dev/null
+++ b/browser/themes/shared/onionPattern.svg
@@ -0,0 +1,22 @@
+<svg fill="context-fill"  viewBox="0 0 900 300" width="900" height="300" xmlns="http://www.w3.org/2000/svg">
+    <g>
+        <path d="m825 0c41.421 0 75 33.5786 75 75 0 41.421-33.579 75-75 75z" fill-opacity=".3"/>
+        <path d="m750 0c41.421 0 75 33.5786 75 75 0 41.421-33.579 75-75 75z" fill-opacity=".15"/>
+        <path d="m525 225c0-41.421-33.579-75-75-75s-75 33.579-75 75z" fill-opacity=".3"/>
+        <path d="m525 300c0-41.421-33.579-75-75-75s-75 33.579-75 75z" fill-opacity=".15"/>
+        <path d="m300 0c0 41.4214-33.579 75-75 75s-75-33.5786-75-75z" fill-opacity=".3"/>
+        <path d="m300 75c0 41.421-33.579 75-75 75s-75-33.579-75-75z" fill-opacity=".15"/>
+        <g clip-rule="evenodd" fill-opacity=".3" fill-rule="evenodd">
+            <path d="m525 .25c-.176 0-.351.000606-.527.001817-.966.006671-1.744.795563-1.737 1.762033.006.96648.795 1.74455 1.762 1.73788.167-.00115.334-.00173.502-.00173s.335.00058.502.00173c.967.00667 1.756-.7714 1.762-1.73788.007-.96647-.771-1.755363-1.737-1.762033-.176-.001211-.351-.001817-.527-.001817zm7.849.407251c-.962-.100329-1.822.597609-1.923 1.558879-.1.96128.598 1.82188 1.559 1.92221.333.03473.665.07174.996.11103.96.11381 1.83-.57199 1.944-1.53176s-.572-1.830084-1.532-1.943891c-.347-.041214-.695-.080042-1.044-.116468zm-15.334 3.481099c.961-.10034 1.659-.96094 1.559-1.92221-.101-.96128-.961-1.659216-1.923-1.558886-.349.036426-.697.075254-1.044.116468-.96.113808-1.646.984118-1.532 1.943888.114.95978.984 1.64557 1.944 1.53176.331-.03928.663-.0763.996-.11102zm23.612-2.14381c-.944-.2076-1.877.38933-2.085 1.33327-.207.94394.389 1.87744 1.333 2.08504.326.07165.651.14553.975.22162.941.2209 1.883-.36277 2.104-1.30369.221-.94091-.363-1.88275-1.304-2.10366-.34-.07986-.681-.15739-1.
 023-.23258zm-31.502 3.41832c.944-.2076 1.54-1.14111 1.333-2.08505-.208-.94394-1.141-1.54086-2.085-1.33326-.342.07519-.683.15272-1.023.23257-.941.22091-1.525 1.16276-1.304 2.10367s1.163 1.52459 2.104 1.30368c.324-.07609.649-.14997.975-.22161zm39.576-1.15763c-.914-.31276-1.909.17503-2.222 1.08953-.312.91449.175 1.90938 1.09 2.22214.315.10775.629.21764.942.32966.91.32565 1.912-.14805 2.237-1.05804.326-.90998-.148-1.91166-1.058-2.23731-.328-.11756-.658-.2329-.989-.34598zm-47.27 3.31168c.915-.31277 1.402-1.30766 1.09-2.22215-.313-.91449-1.308-1.40229-2.222-1.08952-.331.11308-.661.22841-.989.34598-.91.32565-1.384 1.32733-1.058 2.23731.325.90999 1.327 1.38369 2.237 1.05804.313-.11202.627-.22191.942-.32966zm55.037-.15521c-.874-.41383-1.917-.04125-2.331.83218-.414.87342-.041 1.91692.832 2.33072.301.1427.601.2875.9.4343.868.426 1.916.0682 2.343-.7993.426-.86751.068-1.91617-.8-2.34226-.313-.15402-.628-.3059-.944-.45564zm-62.437 3.163c.873-.4139 1.246-1.45739.832-2.33082-.414-.87342-1.457-1.246
 -2.331-.83217-.316.14973-.631.30162-.944.45564-.868.42608-1.226 1.47475-.8 2.34225s1.475 1.2254 2.343.7993c.298-.1468.599-.2916.9-.4342zm69.8.8472c-.822-.5092-1.901-.2561-2.41.5653-.509.8215-.256 1.9002.565 2.4095.284.1756.566.3532.846.5327.815.5207 1.897.2827 2.418-.5315.52-.8143.282-1.8965-.532-2.4172-.294-.1883-.59-.3746-.887-.5588zm-76.817 2.9748c.821-.5092 1.074-1.588.565-2.4095-.509-.8214-1.588-1.0745-2.41-.5653-.297.1843-.593.3705-.887.5588-.814.5207-1.052 1.6029-.532 2.4172.521.8142 1.603 1.0522 2.418.5315.28-.1795.562-.3571.846-.5327zm83.683 1.834c-.759-.5978-1.859-.4668-2.457.2927-.598.7594-.467 1.8597.292 2.4575.262.206.523.4139.781.6235.751.6084 1.853.4927 2.462-.2583.608-.7511.492-1.8531-.259-2.4614-.271-.2199-.544-.4379-.819-.654zm-90.229 2.7502c.759-.5978.89-1.6981.292-2.4575-.598-.7595-1.698-.8905-2.457-.2927-.275.2161-.548.4341-.819.654-.751.6083-.867 1.7103-.259 2.4614.609.751 1.711.8667 2.462.2583.258-.2096.519-.4175.781-.6235zm96.516 2.7935c-.688-.6786-1.796-.670
 9-2.475.0173-.678.6881-.67 1.7962.018 2.4748.237.2339.473.4695.706.7067.679.6882 1.787.6959 2.475.0173s.696-1.7867.018-2.4748c-.246-.2488-.493-.4959-.742-.7413zm-102.511 2.4921c.688-.6786.696-1.7866.017-2.4748-.678-.6882-1.786-.6959-2.474-.0173-.249.2454-.496.4925-.742.7413-.678.6881-.67 1.7962.018 2.4748s1.796.6709 2.474-.0173c.234-.2372.47-.4728.707-.7067zm108.142 3.7171c-.608-.751-1.71-.8667-2.461-.2583-.751.6083-.867 1.7103-.258 2.4613.209.2588.417.5191.623.7809.598.7595 1.698.8905 2.458.2927.759-.5978.89-1.698.292-2.4575-.216-.2746-.434-.5476-.654-.8191zm-113.511 2.203c.609-.751.493-1.853-.258-2.4613-.751-.6084-1.853-.4927-2.461.2583-.22.2715-.438.5445-.654.8191-.598.7595-.467 1.8598.292 2.4575.76.5978 1.86.4668 2.458-.2927.206-.2618.414-.5221.623-.7809zm118.415 4.5953c-.52-.8142-1.603-1.0522-2.417-.5315s-1.052 1.6029-.531 2.4171c.179.2807.357.5627.532.8461.51.8214 1.588 1.0745 2.41.5653.821-.5092 1.074-1.588.565-2.4094-.184-.2973-.37-.5931-.559-.8876zm-123.09 1.8856c.521-.8142
 .283-1.8964-.531-2.4171s-1.897-.2827-2.417.5315c-.189.2945-.375.5903-.559.8876-.509.8215-.256 1.9002.565 2.4094.822.5093 1.9.2561 2.41-.5653.175-.2834.353-.5654.532-.8461zm127.204 5.4202c-.426-.8675-1.475-1.2254-2.343-.7993-.867.4261-1.225 1.4748-.799 2.3423.147.2988.292.5988.434.9.414.8734 1.458 1.246 2.331.8322.874-.4139 1.246-1.4574.832-2.3308-.149-.3161-.301-.6309-.455-.9444zm-131.124 1.543c.426-.8675.068-1.9162-.799-2.3423-.868-.4261-1.917-.0682-2.343.7993-.154.3135-.306.6284-.455.9444-.414.8734-.042 1.9169.832 2.3308.873.4138 1.917.0412 2.331-.8322.142-.3012.287-.6012.434-.9zm134.39 6.1791c-.326-.91-1.328-1.3837-2.238-1.0581-.91.3257-1.383 1.3273-1.058 2.2373.112.3131.222.6271.33.9421.313.9145 1.308 1.4023 2.222 1.0896.915-.3128 1.403-1.3077 1.09-2.2222-.113-.3306-.229-.6602-.346-.9887zm-137.502 1.1793c.325-.91-.148-1.9117-1.058-2.2374-.91-.3256-1.912.1481-2.238 1.0581-.117.3285-.233.6581-.346.9887-.313.9145.175 1.9094 1.09 2.2222.914.3127 1.909-.1751 2.222-1.0895.108-.3151.21
 8-.6291.33-.9421zm139.876 6.8607c-.221-.9409-1.163-1.5246-2.104-1.3037s-1.524 1.1628-1.303 2.1037c.076.3241.15.649.221.9748.208.9439 1.141 1.5408 2.085 1.3332s1.541-1.1411 1.333-2.085c-.075-.3419-.152-.6829-.232-1.023zm-142.139.8c.221-.9409-.362-1.8828-1.303-2.1037s-1.883.3628-2.104 1.3037c-.08.3401-.157.6811-.232 1.023-.208.944.389 1.8775 1.333 2.0851s1.877-.3894 2.085-1.3333c.071-.3258.145-.6507.221-.9748zm143.592 7.4568c-.114-.9598-.984-1.6456-1.944-1.5318-.959.1138-1.645.9841-1.531 1.9439.039.3313.076.6634.111.9961.1.9612.961 1.6592 1.922 1.5588.961-.1003 1.659-.9609 1.559-1.9222-.037-.349-.076-.6973-.117-1.0448zm-144.977.4121c.114-.9597-.572-1.8301-1.531-1.9439-.96-.1138-1.83.572-1.944 1.5318-.041.3476-.08.6959-.117 1.0449-.1.9612.598 1.8218 1.559 1.9222.961.1003 1.822-.5976 1.922-1.5589.035-.3327.072-.6647.111-.9961zm145.499 7.9547c-.006-.9665-.795-1.7445-1.762-1.7379-.966.0067-1.744.7956-1.738 1.7621.001.0523.001.1046.001.1569l.001.0826v.2629c0 .1676-.001.3351-.002.5024-.006.
 9665.772 1.7554 1.738 1.762.967.0067 1.756-.7714 1.762-1.7378.001-.1754.002-.3509.002-.5266s-.001-.3512-.002-.5266zm-145.996.0242c.006-.9665-.772-1.7554-1.738-1.762-.967-.0067-1.756.7714-1.762 1.7378-.001.1754-.002.3509-.002.5266s.001.3512.002.5266c.006.9665.795 1.7445 1.762 1.7379.966-.0067 1.744-.7956 1.738-1.7621-.001-.1673-.002-.3348-.002-.5024s.001-.3351.002-.5024zm145.591 8.3509c.1-.9612-.598-1.8218-1.559-1.9222-.961-.1003-1.822.5976-1.922 1.5589-.035.3327-.072.6647-.111.9961-.114.9597.572 1.83 1.531 1.9439.96.1138 1.83-.572 1.944-1.5318.042-.3476.08-.6959.117-1.0449zm-145.205-.3633c-.1-.9612-.961-1.6592-1.922-1.5588-.961.1003-1.659.9609-1.559 1.9222.037.349.076.6973.117 1.0448.114.9598.984 1.6456 1.944 1.5318.959-.1138 1.645-.9841 1.531-1.9439-.039-.3313-.076-.6634-.111-.9961zm143.867 8.642c.208-.944-.389-1.8775-1.333-2.0851s-1.877.3894-2.085 1.3333c-.071.3258-.145.6507-.221.9748-.221.9409.362 1.8828 1.303 2.1037s1.883-.3628 2.104-1.3037c.08-.3401.157-.6811.232-1.023zm-142.59
 2-.7518c-.208-.9439-1.141-1.5408-2.085-1.3332s-1.541 1.1411-1.333 2.085c.075.3419.152.6829.232 1.023.221.9409 1.163 1.5246 2.104 1.3037s1.524-1.1628 1.303-2.1037c-.076-.3241-.149-.649-.221-.9748zm140.332 8.8261c.313-.9145-.175-1.9094-1.09-2.2222-.914-.3127-1.909.1751-2.222 1.0895-.108.3151-.218.6291-.33.9421-.325.91.148 1.9121 1.058 2.2371.91.326 1.912-.148 2.238-1.058.117-.3283.233-.6579.346-.9885zm-138.178-1.1326c-.313-.9145-1.308-1.4023-2.222-1.0896-.915.3128-1.403 1.3077-1.09 2.2222.113.3306.229.6602.346.9885.326.91 1.328 1.384 2.238 1.058.91-.325 1.383-1.327 1.058-2.237-.112-.3131-.222-.6271-.33-.9421zm135.021 8.8991c.414-.874.042-1.917-.832-2.331-.873-.414-1.917-.041-2.331.832-.142.301-.287.601-.434.9-.426.868-.068 1.916.799 2.343.868.426 1.917.068 2.343-.8.154-.313.306-.628.455-.944zm-132.013-1.499c-.414-.873-1.458-1.246-2.331-.832-.874.414-1.246 1.457-.832 2.331.149.316.301.631.455.944.426.868 1.475 1.226 2.343.8.867-.426 1.225-1.475.799-2.343-.147-.298-.292-.599-.434-.9zm12
 8.003 8.862c.509-.822.256-1.901-.565-2.41-.822-.509-1.9-.256-2.41.565-.175.284-.353.566-.532.846-.521.815-.283 1.897.531 2.418.814.52 1.897.282 2.417-.532.189-.294.375-.59.559-.887zm-124.181-1.845c-.51-.821-1.588-1.074-2.41-.565-.821.509-1.074 1.588-.565 2.41.184.297.37.593.559.887.52.814 1.603 1.052 2.417.532.814-.521 1.052-1.603.531-2.418-.179-.28-.357-.562-.532-.846zm119.372 8.711c.598-.759.467-1.859-.292-2.457-.76-.598-1.86-.467-2.458.292-.206.262-.414.523-.623.781-.609.751-.493 1.853.258 2.462.751.608 1.853.492 2.461-.259.22-.271.438-.544.654-.819zm-114.788-2.165c-.598-.759-1.698-.89-2.458-.292-.759.598-.89 1.698-.292 2.457.216.275.434.548.654.819.608.751 1.71.867 2.461.259.751-.609.867-1.711.258-2.462-.209-.258-.417-.519-.623-.781zm109.245 8.452c.678-.688.67-1.796-.018-2.475-.688-.678-1.796-.67-2.474.018-.234.237-.47.473-.707.706-.688.679-.696 1.787-.017 2.475.678.688 1.786.696 2.474.018.249-.246.496-.493.742-.742zm-103.959-2.457c-.679-.688-1.787-.696-2.475-.017-.688.678-.696 
 1.786-.018 2.474.246.249.493.496.742.742.688.678 1.796.67 2.474-.018.679-.688.671-1.796-.017-2.474-.237-.234-.473-.47-.706-.707zm5.92 5.369c-.751-.609-1.853-.493-2.462.258-.608.751-.492 1.853.259 2.461.271.22.544.438.819.654.759.598 1.859.467 2.457-.292.598-.76.467-1.86-.292-2.458-.262-.206-.523-.414-.781-.623zm91.829 2.719c.751-.608.867-1.71.259-2.461-.609-.751-1.711-.867-2.462-.258-.258.209-.519.417-.781.623-.759.598-.89 1.698-.292 2.458.598.759 1.698.89 2.457.292.275-.216.548-.434.819-.654zm-6.798 4.904c.814-.52 1.052-1.603.532-2.417-.521-.814-1.603-1.052-2.418-.531-.28.179-.562.357-.846.532-.821.51-1.074 1.588-.565 2.41.509.821 1.588 1.074 2.41.565.297-.184.593-.37.887-.559zm-78.55-2.948c-.815-.521-1.897-.283-2.418.531-.52.814-.282 1.897.532 2.417.294.189.59.375.887.559.822.509 1.901.256 2.41-.565.509-.822.256-1.9-.565-2.41-.284-.175-.566-.353-.846-.532zm6.963 3.92c-.868-.426-1.916-.068-2.343.799-.426.868-.068 1.917.8 2.343.313.154.628.306.944.455.874.414 1.917.042 2.331-.832.41
 4-.873.041-1.917-.832-2.331-.301-.142-.601-.287-.9-.434zm64.281 3.142c.868-.426 1.226-1.475.8-2.343-.426-.867-1.475-1.225-2.343-.799-.298.147-.599.292-.9.434-.873.414-1.246 1.458-.832 2.331.414.874 1.457 1.246 2.331.832.316-.149.631-.301.944-.455zm-7.722 3.266c.91-.326 1.384-1.328 1.058-2.238-.325-.91-1.327-1.383-2.237-1.058-.313.112-.627.222-.942.33-.915.313-1.402 1.308-1.09 2.222.313.915 1.308 1.403 2.222 1.09.331-.113.661-.229.989-.346zm-49.201-3.296c-.91-.325-1.912.148-2.237 1.058-.326.91.148 1.912 1.058 2.238.328.117.658.233.989.346.914.313 1.909-.175 2.222-1.09.312-.914-.175-1.909-1.09-2.222-.315-.108-.629-.218-.942-.33zm41.161 5.67c.941-.221 1.525-1.163 1.304-2.104s-1.163-1.524-2.104-1.303c-.324.076-.649.15-.975.221-.944.208-1.54 1.141-1.333 2.085.208.944 1.141 1.541 2.085 1.333.342-.075.683-.152 1.023-.232zm-33.5-3.407c-.941-.221-1.883.362-2.104 1.303s.363 1.883 1.304 2.104c.34.08.681.157 1.023.232.944.208 1.877-.389 2.085-1.333.207-.944-.389-1.877-1.333-2.085-.326-.071-.651
 -.145-.975-.221zm7.869 1.385c-.96-.114-1.83.572-1.944 1.531-.114.96.572 1.83 1.532 1.944.347.042.695.08 1.044.117.962.1 1.822-.598 1.923-1.559.1-.961-.598-1.822-1.559-1.922-.333-.035-.665-.072-.996-.111zm17.374 3.475c.96-.114 1.646-.984 1.532-1.944-.114-.959-.984-1.645-1.944-1.531-.331.039-.663.076-.996.111-.961.1-1.659.961-1.559 1.922.101.961.961 1.659 1.923 1.559.349-.037.697-.076 1.044-.117zm-9.395-2.978c-.967-.006-1.756.772-1.762 1.738-.007.967.771 1.756 1.737 1.762.176.001.351.002.527.002s.351-.001.527-.002c.966-.006 1.744-.795 1.737-1.762-.006-.966-.795-1.744-1.762-1.738-.167.001-.334.002-.502.002-.093 0-.185 0-.278-.001-.045 0-.089 0-.134 0-.03 0-.06-.001-.09-.001zm.502-130.998c-.179 0-.357.0008-.536.0024-.966.0084-1.743.7988-1.734 1.7653.008.9664.799 1.743 1.765 1.7345.168-.0015.337-.0022.505-.0022s.337.0007.505.0022c.966.0085 1.757-.7681 1.765-1.7345.009-.9665-.768-1.7569-1.734-1.7653-.179-.0016-.357-.0024-.536-.0024zm-7.515 3.9974c.958-.1279 1.631-1.0081 1.503-1.9661s-1.00
 8-1.631-1.966-1.5031c-.352.047-.703.097-1.053.1501-.956.1449-1.613 1.037-1.468 1.9926s1.037 1.6127 1.993 1.4678c.329-.0499.66-.0971.991-.1413zm15.493-3.4692c-.958-.1279-1.838.5451-1.966 1.5031s.545 1.8382 1.503 1.9661c.331.0442.662.0914.991.1413.956.1449 1.848-.5122 1.993-1.4678s-.512-1.8477-1.468-1.9926c-.35-.0531-.701-.1031-1.053-.1501zm-23.35 5.0976c.929-.2635 1.47-1.231 1.206-2.1609-.264-.9298-1.231-1.47-2.161-1.2064-.342.097-.683.1969-1.023.2998-.925.2803-1.447 1.2573-1.167 2.1823s1.257 1.4476 2.182 1.1674c.32-.0968.641-.1909.963-.2822zm31.699-3.3673c-.93-.2636-1.897.2766-2.161 1.2064-.264.9299.277 1.8974 1.206 2.1609.322.0913.643.1854.963.2822.925.2802 1.902-.2424 2.182-1.1674s-.242-1.902-1.167-2.1823c-.34-.1029-.681-.2028-1.023-.2998zm-39.242 6.1049c.882-.3942 1.278-1.4291.884-2.3116s-1.429-1.2783-2.311-.8842c-.325.1451-.648.293-.97.4436-.875.4099-1.253 1.4518-.843 2.3271.41.8752 1.452 1.2525 2.327.8426.303-.1418.607-.281.913-.4175zm47.257-3.1958c-.882-.3941-1.917.0017-2.311.
 8842s.002 1.9174.884 2.3116c.306.1365.61.2757.913.4175.875.4099 1.917.0326 2.327-.8426.41-.8753.032-1.9172-.843-2.3271-.322-.1506-.645-.2985-.97-.4436zm7.516 4.0254c-.817-.516-1.898-.2718-2.414.5454s-.271 1.898.546 2.4141c.283.1787.564.3599.844.5436.808.5305 1.893.3056 2.423-.5022.531-.8079.306-1.8929-.502-2.4234-.297-.1951-.596-.3876-.897-.5775zm-61.848 2.9595c.817-.5161 1.062-1.5969.546-2.4141s-1.597-1.0614-2.414-.5454c-.301.1899-.6.3824-.897.5775-.808.5305-1.033 1.6155-.502 2.4234.53.8079 1.615 1.0327 2.423.5022.28-.1837.561-.3649.844-.5436zm68.714 2.0935c-.735-.6268-1.84-.5384-2.467.1974-.626.7357-.538 1.8402.198 2.467.255.2171.508.4364.759.658.724.6397 1.83.5709 2.47-.1536.639-.7245.571-1.8304-.154-2.4701-.266-.2353-.535-.4682-.806-.6987zm-75.179 2.6644c.736-.6268.824-1.7313.198-2.467-.627-.7358-1.732-.8242-2.467-.1974-.271.2305-.54.4634-.806.6987-.725.6397-.793 1.7456-.154 2.4701.64.7245 1.746.7933 2.47.1536.251-.2216.504-.4409.759-.658zm81.26 3.3095c-.639-.7245-1.745-.7933-2.
 47-.1536-.724.6397-.793 1.7456-.153 2.4701.221.2509.441.5039.658.7587.626.7358 1.731.8242 2.467.1975.735-.6268.824-1.7313.197-2.467-.231-.2707-.463-.5393-.699-.8057zm-86.987 2.3165c.64-.7245.571-1.8304-.153-2.4701-.725-.6397-1.831-.5709-2.47.1536-.236.2664-.468.535-.699.8057-.627.7357-.538 1.8402.197 2.467.736.6267 1.841.5383 2.467-.1975.217-.2548.437-.5078.658-.7587zm92.162 4.4585c-.531-.8079-1.616-1.0327-2.424-.5022s-1.033 1.6155-.502 2.4234c.184.2796.365.5611.544.8442.516.8172 1.596 1.0614 2.414.5453.817-.516 1.061-1.5968.545-2.414-.19-.3007-.382-.5996-.577-.8967zm-97.034 1.9212c.531-.8079.306-1.8929-.502-2.4234s-1.893-.3057-2.424.5022c-.195.2971-.387.596-.577.8967-.516.8172-.272 1.898.545 2.414.818.5161 1.898.2719 2.414-.5453.179-.2831.36-.5646.544-.8442zm-3.915 7.0062c.41-.8753.033-1.9171-.843-2.3271-.875-.4099-1.917-.0326-2.327.8427-.15.3217-.298.6449-.443.9697-.395.8825.001 1.9174.884 2.3116.882.3941 1.917-.0018 2.311-.8842.137-.3057.276-.61.418-.9127zm105.108-1.4844c-.41-.87
 53-1.452-1.2526-2.327-.8427-.876.41-1.253 1.4518-.843 2.3271.142.3027.281.607.418.9127.394.8824 1.429 1.2783 2.311.8842.883-.3942 1.279-1.4291.884-2.3116-.145-.3248-.293-.648-.443-.9697zm-107.981 8.9773c.28-.925-.242-1.902-1.167-2.1822-.925-.2803-1.902.2424-2.183 1.1674-.103.3396-.203.6805-.3 1.0227-.263.9299.277 1.8973 1.207 2.1609s1.897-.2766 2.161-1.2064c.091-.322.185-.6428.282-.9624zm111.034-1.0148c-.281-.925-1.258-1.4477-2.183-1.1674-.925.2802-1.447 1.2572-1.167 2.1822.097.3196.191.6404.282.9624.264.9298 1.231 1.47 2.161 1.2064s1.47-1.231 1.207-2.1609c-.097-.3422-.197-.6831-.3-1.0227zm-112.803 8.8434c.145-.9556-.513-1.8477-1.468-1.9926-.956-.1449-1.848.5122-1.993 1.4678-.053.3501-.103.7012-.15 1.0532-.128.958.545 1.8383 1.503 1.9661.958.1279 1.839-.5451 1.966-1.5031.045-.3314.092-.6619.142-.9914zm114.683-.5248c-.145-.9556-1.037-1.6127-1.993-1.4678-.955.1449-1.613 1.037-1.468 1.9926.05.3295.097.66.142.9914.127.958 1.008 1.631 1.966 1.5031.958-.1278 1.631-1.0081 1.503-1.9661-.047
 -.352-.097-.7031-.15-1.0532zm-115.32 8.5262c.009-.9665-.768-1.7568-1.734-1.7653-.967-.0085-1.757.7681-1.766 1.7346-.001.1783-.002.3568-.002.5355s.001.3572.002.5355c.009.9665.799 1.7431 1.766 1.7346.966-.0085 1.743-.7989 1.734-1.7653-.001-.1681-.002-.3364-.002-.5048s.001-.3367.002-.5048zm115.996-.0307c-.009-.9665-.799-1.7431-1.766-1.7346-.966.0085-1.743.7989-1.734 1.7653.001.1016.001.2033.002.305v.1998c0 .1684-.001.3367-.002.5048-.009.9665.768 1.7568 1.734 1.7653.967.0085 1.757-.7681 1.766-1.7346.001-.1783.002-.3568.002-.5355s-.001-.3572-.002-.5355zm-115.501 8.0503c-.127-.958-1.008-1.631-1.966-1.5031-.958.1278-1.631 1.0081-1.503 1.9661.047.352.097.7031.15 1.0532.145.9555 1.037 1.6127 1.993 1.4678.955-.1449 1.613-1.037 1.468-1.9926-.05-.3295-.097-.66-.142-.9914zm114.975.463c.128-.958-.545-1.8383-1.503-1.9661-.958-.1279-1.839.5451-1.966 1.5031-.045.3314-.092.6619-.142.9914-.145.9556.513 1.8477 1.468 1.9926.956.1449 1.848-.5122 1.993-1.4678.053-.3501.103-.7012.15-1.0532zm-113.346 7.3946
 c-.264-.9298-1.231-1.47-2.161-1.2064s-1.47 1.231-1.207 2.1609c.097.3422.197.6831.3 1.0227.281.925 1.258 1.4477 2.183 1.1674.925-.2802 1.447-1.2572 1.167-2.1822-.097-.3196-.191-.6404-.282-.9624zm111.616.9545c.263-.9299-.277-1.8973-1.207-2.1609s-1.897.2766-2.161 1.2064c-.091.322-.185.6428-.282.9624-.28.925.242 1.902 1.167 2.1822.925.2803 1.902-.2424 2.183-1.1674.103-.3396.203-.6805.3-1.0227zm-108.879 6.5881c-.394-.8824-1.429-1.2783-2.311-.8842-.883.3942-1.279 1.4291-.884 2.3116.145.3248.293.648.443.9696.41.875 1.452 1.253 2.327.843.876-.41 1.253-1.452.843-2.3273-.142-.3027-.281-.607-.418-.9127zm105.969 1.4274c.395-.8825-.001-1.9174-.884-2.3116-.882-.3941-1.917.0018-2.311.8842-.137.3057-.276.61-.418.9127-.41.8753-.033 1.9173.843 2.3273.875.41 1.917.032 2.327-.843.15-.3216.298-.6448.443-.9696zm-102.18 5.6476c-.516-.817-1.596-1.062-2.414-.546-.817.516-1.061 1.597-.545 2.414.19.301.382.6.577.897.531.808 1.616 1.033 2.424.502.808-.53 1.033-1.615.502-2.423-.184-.28-.365-.561-.544-.844zm98.1
 55 1.868c.516-.817.272-1.898-.545-2.414-.818-.516-1.898-.271-2.414.546-.179.283-.36.564-.544.844-.531.808-.306 1.893.502 2.423.808.531 1.893.306 2.424-.502.195-.297.387-.596.577-.897zm-93.397 4.597c-.626-.736-1.731-.824-2.467-.198-.735.627-.824 1.732-.197 2.467.231.271.463.54.699.806.639.725 1.745.793 2.47.154.724-.64.793-1.746.153-2.47-.221-.251-.441-.504-.658-.759zm88.344 2.269c.627-.735.538-1.84-.197-2.467-.736-.626-1.841-.538-2.467.198-.217.255-.437.508-.658.759-.64.724-.571 1.83.153 2.47.725.639 1.831.571 2.47-.154.236-.266.468-.535.699-.806zm-5.974 6.081c.725-.639.793-1.745.154-2.47-.64-.724-1.746-.793-2.47-.153-.251.221-.504.441-.759.658-.736.626-.824 1.731-.198 2.467.627.735 1.732.824 2.467.197.271-.231.54-.463.806-.699zm-76.744-2.623c-.724-.64-1.83-.571-2.47.153-.639.725-.571 1.831.154 2.47.266.236.535.468.806.699.735.627 1.84.538 2.467-.197.626-.736.538-1.841-.198-2.467-.255-.217-.508-.437-.759-.658zm6.38 4.872c-.808-.531-1.893-.306-2.423.502-.531.808-.306 1.893.502 2.424.
 297.195.596.387.897.577.817.516 1.898.272 2.414-.545.516-.818.271-1.898-.546-2.414-.283-.179-.564-.36-.844-.544zm63.589 2.926c.808-.531 1.033-1.616.502-2.424-.53-.808-1.615-1.033-2.423-.502-.28.184-.561.365-.844.544-.817.516-1.062 1.596-.546 2.414.516.817 1.597 1.061 2.414.545.301-.19.6-.382.897-.577zm-56.583.989c-.875-.41-1.917-.033-2.327.843-.41.875-.032 1.917.843 2.327.322.15.645.298.97.443.882.395 1.917-.001 2.311-.884.394-.882-.002-1.917-.884-2.311-.306-.137-.61-.276-.913-.418zm49.14 3.17c.875-.41 1.253-1.452.843-2.327-.41-.876-1.452-1.253-2.327-.843-.303.142-.607.281-.913.418-.882.394-1.278 1.429-.884 2.311.394.883 1.429 1.279 2.311.884.325-.145.648-.293.97-.443zm-41.647-.297c-.925-.28-1.902.242-2.182 1.167s.242 1.902 1.167 2.183c.34.103.681.203 1.023.3.93.263 1.897-.277 2.161-1.207s-.277-1.897-1.206-2.161c-.322-.091-.643-.185-.963-.282zm33.685 3.35c.925-.281 1.447-1.258 1.167-2.183s-1.257-1.447-2.182-1.167c-.32.097-.641.191-.963.282-.929.264-1.47 1.231-1.206 2.161s1.231 1.47 
 2.161 1.207c.342-.097.683-.197 1.023-.3zm-25.856-1.581c-.956-.145-1.848.513-1.993 1.468-.145.956.512 1.848 1.468 1.993.35.053.701.103 1.053.15.958.128 1.838-.545 1.966-1.503s-.545-1.839-1.503-1.966c-.331-.045-.662-.092-.991-.142zm17.537 3.461c.956-.145 1.613-1.037 1.468-1.993-.145-.955-1.037-1.613-1.993-1.468-.329.05-.66.097-.991.142-.958.127-1.631 1.008-1.503 1.966s1.008 1.631 1.966 1.503c.352-.047.703-.097 1.053-.15zm-9.536-2.824c-.966-.009-1.757.768-1.765 1.734-.009.967.768 1.757 1.734 1.766h.102l.16.001c.091.001.183.001.274.001.112 0 .225 0 .337-.001h.091l.108-.001c.966-.009 1.743-.799 1.734-1.766-.008-.966-.799-1.743-1.765-1.734-.168.001-.337.002-.505.002-.064 0-.128 0-.192 0-.104-.001-.209-.001-.313-.002zm-.022-98.9948c.176-.0021.351-.0032.527-.0032s.351.0011.527.0032c.966.0117 1.74.8046 1.728 1.771-.011.9664-.804 1.7404-1.771 1.7287-.161-.0019-.322-.0029-.484-.0029s-.323.001-.484.0029c-.967.0117-1.76-.7623-1.771-1.7287-.012-.9664.762-1.7593 1.728-1.771zm-5.261 2.1143c.176.950
 4-.452 1.8634-1.402 2.0392-.316.0585-.63.1208-.944.1868-.945.1992-1.874-.4059-2.073-1.3517-.199-.9457.406-1.8739 1.352-2.0731.341-.072.684-.1398 1.028-.2035.951-.1759 1.864.4519 2.039 1.4023zm11.576 0c.175-.9504 1.088-1.5782 2.039-1.4023.344.0637.687.1315 1.028.2035.946.1992 1.551 1.1274 1.352 2.0731-.199.9458-1.128 1.5509-2.073 1.3517-.313-.066-.628-.1283-.944-.1868-.95-.1758-1.578-1.0888-1.402-2.0392zm-19.392 1.9036c.359.8973-.077 1.9158-.974 2.275-.299.1193-.595.2423-.89.3687-.888.3812-1.917-.0298-2.298-.9179s.03-1.9171.918-2.2984c.321-.1377.644-.2717.969-.4018.897-.3591 1.916.0771 2.275.9744zm27.208 0c.359-.8973 1.378-1.3335 2.275-.9744.325.1301.648.2641.969.4018.888.3813 1.299 1.4103.918 2.2984-.381.8882-1.41 1.2991-2.298.9179-.295-.1264-.591-.2494-.89-.3687-.897-.3592-1.333-1.3777-.974-2.275zm-34.499 3.4018c.528.8097.3 1.8939-.51 2.4217-.269.1754-.536.3541-.8.5359-.797.5475-1.886.3456-2.434-.4508-.547-.7965-.346-1.886.451-2.4335.288-.198.579-.3926.872-.5836.809-.5278 1.894-.29
 93 2.421.5103zm41.79 0c.527-.8096 1.612-1.0381 2.421-.5103.293.1911.584.3856.872.5836.797.5475.998 1.637.451 2.4335-.548.7964-1.637.9983-2.434.4508-.264-.1818-.531-.3604-.8-.5359-.81-.5278-1.038-1.612-.51-2.4217zm-48.276 4.7598c.675.6917.662 1.7997-.03 2.4747-.23.2244-.457.4515-.682.6814-.675.6917-1.783.7052-2.474.0302-.692-.675-.705-1.783-.03-2.4747.244-.2502.491-.4975.741-.7417.692-.6751 1.8-.6616 2.475.0301zm54.762 0c.675-.6917 1.783-.7052 2.475-.0301.25.2442.497.4915.741.7417.675.6917.662 1.7997-.03 2.4747-.691.6751-1.799.6615-2.474-.0302-.225-.2299-.452-.457-.682-.6814-.692-.675-.705-1.7829-.03-2.4747zm-60.201 5.9284c.796.5475.998 1.637.451 2.4335-.182.2645-.361.5313-.536.8004-.528.8097-1.612 1.0382-2.422.5104s-1.038-1.6121-.51-2.4217c.191-.2931.385-.5837.583-.8717.548-.7965 1.637-.9984 2.434-.4509zm65.64 0c.797-.5475 1.886-.3456 2.434.4509.198.288.392.5786.583.8717.528.8097.3 1.8939-.51 2.4217s-1.894.2993-2.422-.5103c-.175-.2692-.354-.536-.536-.8005-.547-.7965-.345-1.886.451-2
 .4335zm-69.823 6.873c.888.3812 1.299 1.4102.918 2.2984-.127.2945-.25.5909-.369.8891-.359.8972-1.378 1.3335-2.275.9743s-1.333-1.3777-.974-2.275c.13-.325.264-.648.402-.9689.381-.8882 1.41-1.2991 2.298-.9179zm74.006 0c.888-.3812 1.917.0297 2.298.9179.138.3209.272.6439.402.9689.359.8973-.077 1.9158-.974 2.275s-1.916-.0771-2.275-.9743c-.119-.2982-.242-.5946-.369-.8891-.381-.8882.03-1.9172.918-2.2984zm-76.761 7.559c.946.1992 1.551 1.1274 1.351 2.0732-.066.3132-.128.6278-.186.9438-.176.9504-1.089 1.5782-2.04 1.4023-.95-.1759-1.578-1.0889-1.402-2.0392.064-.3443.132-.6871.204-1.0285.199-.9457 1.127-1.5509 2.073-1.3516zm79.516 0c.946-.1993 1.874.4059 2.073 1.3516.072.3414.14.6842.204 1.0285.176.9503-.452 1.8633-1.402 2.0392-.951.1759-1.864-.4519-2.04-1.4023-.058-.316-.12-.6306-.186-.9438-.2-.9458.405-1.874 1.351-2.0732zm-80.734 7.9517c.967.0116 1.741.8045 1.729 1.771-.002.1611-.003.3225-.003.4842s.001.3231.003.4842c.012.9665-.762 1.7594-1.729 1.771-.966.0117-1.759-.7622-1.771-1.7287-.002-.175
 2-.003-.3507-.003-.5265s.001-.3513.003-.5265c.012-.9665.805-1.7404 1.771-1.7287zm81.952 0c.966-.0117 1.759.7622 1.771 1.7287.001.0705.001.1411.002.2117.001.1048.001.2098.001.3148 0 .1318-.001.2635-.002.395l-.001.1315c-.012.9665-.805 1.7404-1.771 1.7287-.967-.0116-1.741-.8045-1.729-1.771.002-.1611.003-.3225.003-.4842s-.001-.3231-.003-.4842c-.012-.9665.762-1.7594 1.729-1.771zm-81.609 8.0428c.951-.1759 1.864.4519 2.04 1.4023.058.316.12.6306.186.9438.2.9458-.405 1.874-1.351 2.0732-.946.1993-1.874-.4059-2.073-1.3516-.072-.3414-.14-.6842-.204-1.0285-.176-.9503.452-1.8633 1.402-2.0392zm81.265 0c.951.1759 1.579 1.0889 1.403 2.0392-.064.3443-.132.6871-.204 1.0285-.199.9457-1.127 1.5509-2.073 1.3516-.946-.1992-1.551-1.1274-1.351-2.0732.066-.3132.128-.6278.186-.9438.176-.9504 1.089-1.5782 2.039-1.4023zm-79.361 7.8165c.897-.3592 1.916.0771 2.275.9743.119.2982.242.5946.369.8891.381.8882-.03 1.9172-.918 2.2984s-1.917-.0297-2.298-.9179c-.138-.3209-.272-.6439-.402-.9689-.359-.8973.077-1.9158.974-2.
 275zm77.458 0c.897.3592 1.333 1.3777.974 2.275-.13.325-.264.648-.402.9689-.381.8882-1.41 1.2991-2.298.9179s-1.299-1.4102-.918-2.2984c.127-.2945.25-.5909.369-.8891.359-.8972 1.378-1.3335 2.275-.9743zm-74.056 7.2905c.81-.5278 1.894-.2993 2.422.5104.175.2691.354.5359.536.8004.547.7965.345 1.886-.451 2.4335-.797.5471-1.886.3456-2.434-.4509-.198-.288-.392-.5786-.583-.8717-.528-.8097-.3-1.8939.51-2.4217zm70.654 0c.81.5278 1.038 1.6121.51 2.4217-.191.2931-.385.5837-.583.8717-.548.7965-1.637.998-2.434.4509-.796-.5475-.998-1.637-.451-2.4335.182-.2645.361-.5313.536-.8004.528-.8097 1.612-1.0382 2.422-.5104zm-65.894 6.4864c.691-.675 1.799-.662 2.474.03.225.23.452.457.682.682.692.675.705 1.783.03 2.474-.675.692-1.783.705-2.475.03-.25-.244-.497-.491-.741-.741-.675-.692-.662-1.8.03-2.475zm61.134 0c.692.675.705 1.783.03 2.475-.244.25-.491.497-.741.741-.692.675-1.8.662-2.475-.03-.675-.691-.662-1.799.03-2.474.23-.225.457-.452.682-.682.675-.692 1.783-.705 2.474-.03zm-55.206 5.439c.548-.796 1.637-.998 
 2.434-.451.264.182.531.361.8.536.81.528 1.038 1.612.51 2.422-.527.81-1.612 1.038-2.421.51-.293-.191-.584-.385-.872-.583-.797-.548-.998-1.637-.451-2.434zm49.278 0c.547.797.346 1.886-.451 2.434-.288.198-.579.392-.872.583-.809.528-1.894.3-2.421-.51-.528-.81-.3-1.894.51-2.422.269-.175.536-.354.8-.536.797-.547 1.886-.345 2.434.451zm-42.405 4.183c.381-.888 1.41-1.299 2.298-.918.295.127.591.25.89.369.897.359 1.333 1.378.974 2.275s-1.378 1.333-2.275.974c-.325-.13-.648-.264-.969-.402-.888-.381-1.299-1.41-.918-2.298zm35.532 0c.381.888-.03 1.917-.918 2.298-.321.138-.644.272-.969.402-.897.359-1.916-.077-2.275-.974s.077-1.916.974-2.275c.299-.119.595-.242.89-.369.888-.381 1.917.03 2.298.918zm-27.973 2.755c.199-.946 1.128-1.551 2.073-1.351.313.066.628.128.944.186.95.176 1.578 1.089 1.402 2.039-.175.951-1.088 1.579-2.039 1.403-.344-.064-.687-.132-1.028-.204-.946-.199-1.551-1.127-1.352-2.073zm20.414 0c.199.946-.406 1.874-1.352 2.073-.341.072-.684.14-1.028.204-.951.176-1.864-.452-2.039-1.402-.176-.95
 1.452-1.864 1.402-2.04.316-.058.63-.12.944-.186.945-.2 1.874.405 2.073 1.351zm-12.462 1.218c.011-.967.804-1.741 1.771-1.729.161.002.322.003.484.003s.323-.001.484-.003c.967-.012 1.76.762 1.771 1.729.012.966-.762 1.759-1.728 1.771-.044 0-.088.001-.132.001-.132.001-.263.002-.395.002-.105 0-.21 0-.315-.001-.07-.001-.141-.001-.212-.002-.966-.012-1.74-.805-1.728-1.771zm2.255-66.726c-.169 0-.338.0016-.507.0049-.966.0187-1.734.8172-1.716 1.7835.019.9663.818 1.7345 1.784 1.7158.146-.0028.292-.0042.439-.0042s.293.0014.439.0042c.966.0187 1.765-.7495 1.784-1.7158.018-.9663-.75-1.7648-1.716-1.7835-.169-.0033-.338-.0049-.507-.0049zm7.477 1.1027c-.925-.2802-1.902.2424-2.183 1.1674-.28.925.243 1.902 1.168 2.1822.279.0847.556.1746.831.2698.913.3167 1.91-.1669 2.226-1.0801.317-.9131-.167-1.9101-1.08-2.2267-.318-.1103-.639-.2146-.962-.3126zm-13.939 3.3496c.925-.2802 1.448-1.2572 1.168-2.1822-.281-.925-1.258-1.4476-2.183-1.1674-.323.098-.644.2023-.962.3126-.913.3166-1.397 1.3136-1.08 2.2267.316.9132 1.
 313 1.3968 2.226 1.0801.275-.0952.552-.1851.831-.2698zm21.188.1714c-.793-.5533-1.884-.3595-2.437.4329-.553.7925-.359 1.8834.433 2.4367.239.1669.475.3384.707.5143.77.5839 1.868.4329 2.451-.3373.584-.7702.433-1.8679-.337-2.4518-.268-.2034-.541-.4018-.817-.5948zm-27.448 2.8696c.792-.5533.986-1.6442.433-2.4367-.553-.7924-1.644-.9862-2.437-.4329-.276.193-.549.3914-.817.5948-.77.5839-.921 1.6816-.337 2.4518.583.7702 1.681.9212 2.451.3373.232-.1759.468-.3474.707-.5143zm33.253 2.7135c-.583-.7701-1.681-.9211-2.451-.3372s-.922 1.6816-.338 2.4518c.176.232.348.4676.515.7066.553.7924 1.644.9862 2.436.4329.793-.5533.987-1.6442.433-2.4367-.193-.2765-.391-.549-.595-.8174zm-38.273 2.1146c.583-.7702.432-1.8679-.338-2.4518s-1.868-.4329-2.451.3373c-.204.2683-.402.5408-.595.8173-.554.7925-.36 1.8834.433 2.4367.792.5533 1.883.3595 2.436-.4329.167-.239.339-.4746.515-.7066zm42.077 4.9894c-.317-.9132-1.314-1.3968-2.227-1.0801-.913.3166-1.397 1.3136-1.08 2.2267.095.2746.185.5515.27.8308.28.925 1.257 1.4477 2
 .182 1.1674.925-.2802 1.448-1.2572 1.167-2.1822-.098-.3236-.202-.6445-.312-.9626zm-45.363 1.1466c.317-.9131-.167-1.9101-1.08-2.2267-.913-.3167-1.91.1669-2.227 1.0801-.11.3181-.214.639-.312.9626-.281.925.242 1.902 1.167 2.1822.925.2803 1.902-.2424 2.182-1.1674.085-.2793.175-.5562.27-.8308zm46.773 6.7859c-.019-.9664-.817-1.7346-1.783-1.7159-.967.0187-1.735.8172-1.716 1.7835.003.146.004.2924.004.4391s-.001.2931-.004.4391c-.019.9663.749 1.7648 1.716 1.7835.966.0187 1.764-.7495 1.783-1.7159.003-.1685.005-.3374.005-.5067s-.002-.3382-.005-.5067zm-47.991.0676c.019-.9663-.749-1.7648-1.716-1.7835-.966-.0187-1.764.7495-1.783 1.7159-.003.1685-.005.3374-.005.5067s.002.3382.005.5067c.019.9664.817 1.7346 1.783 1.7159.967-.0187 1.735-.8172 1.716-1.7835-.003-.146-.004-.2924-.004-.4391s.001-.2931.004-.4391zm46.893 7.9157c.281-.925-.242-1.902-1.167-2.1822-.925-.2803-1.902.2424-2.182 1.1674-.085.2793-.175.5562-.27.8308-.317.9131.167 1.9101 1.08 2.2267.913.3167 1.91-.1669 2.227-1.0801.11-.3181.214-.639.
 312-.9626zm-45.945-1.0148c-.28-.925-1.257-1.4477-2.182-1.1674-.925.2802-1.448 1.2572-1.167 2.1822.098.3236.202.6445.312.9626.317.9132 1.314 1.3968 2.227 1.0801.913-.3166 1.397-1.3136 1.08-2.2267-.095-.2746-.185-.5515-.27-.8308zm42.424 8.264c.554-.7925.36-1.8834-.433-2.4367-.792-.5534-1.883-.3595-2.436.4329-.167.239-.339.4746-.515.7066-.583.7702-.432 1.8679.338 2.4518s1.868.4329 2.451-.3372c.204-.2684.402-.5409.595-.8174zm-39.383-2.0038c-.553-.7924-1.644-.9862-2.436-.4329-.793.5533-.987 1.6442-.433 2.4367.193.2765.391.549.595.8174.583.7701 1.681.9211 2.451.3372s.922-1.6816.338-2.4518c-.176-.232-.348-.4676-.515-.7066zm33.8 7.8095c.77-.5839.921-1.6816.337-2.4518-.583-.7702-1.681-.9212-2.451-.3373-.232.1759-.468.3474-.707.5143-.792.5533-.986 1.6442-.433 2.4367.553.7924 1.644.9862 2.437.4329.276-.193.549-.3914.817-.5948zm-28.972-2.7891c-.77-.5839-1.868-.4329-2.451.3373-.584.7702-.433 1.8679.337 2.4518.268.2034.541.4018.817.5948.793.5533 1.884.3595 2.437-.4329.553-.7925.359-1.8834-.433-2.
 4367-.239-.1669-.475-.3384-.707-.5143zm6.136 3.2855c-.913-.3167-1.91.1669-2.226 1.0801-.317.9131.167 1.9101 1.08 2.2267.318.1103.639.2146.962.3126.925.2802 1.902-.2424 2.183-1.1674.28-.925-.243-1.902-1.168-2.1822-.279-.0847-.556-.1746-.831-.2698zm15.732 3.3068c.913-.3166 1.397-1.3136 1.08-2.2267-.316-.9132-1.313-1.3968-2.226-1.0801-.275.0952-.552.1851-.831.2698-.925.2802-1.448 1.2572-1.168 2.1822.281.925 1.258 1.4476 2.183 1.1674.323-.098.644-.2023.962-.3126zm-8.878-2.0889c-.966-.0187-1.765.7495-1.784 1.7158-.018.9663.75 1.7644 1.716 1.7834.169.003.338.005.507.005s.338-.002.507-.005c.966-.019 1.734-.8171 1.716-1.7834-.019-.9663-.818-1.7345-1.784-1.7158-.146.0028-.292.0042-.439.0042s-.293-.0014-.439-.0042z"/>
+            <path d="m3.75 75c0-39.3503 31.8997-71.25 71.25-71.25 39.35 0 71.25 31.8997 71.25 71.25 0 39.35-31.9 71.25-71.25 71.25-39.3503 0-71.25-31.9-71.25-71.25zm71.25-74.75c-41.2833 0-74.75 33.4667-74.75 74.75 0 41.283 33.4667 74.75 74.75 74.75 41.283 0 74.75-33.467 74.75-74.75 0-41.2833-33.467-74.75-74.75-74.75zm-55.25 74.75c0-30.5137 24.7363-55.25 55.25-55.25 30.514 0 55.25 24.7363 55.25 55.25 0 30.514-24.736 55.25-55.25 55.25-30.5137 0-55.25-24.736-55.25-55.25zm55.25-58.75c-32.4467 0-58.75 26.3033-58.75 58.75 0 32.447 26.3033 58.75 58.75 58.75 32.447 0 58.75-26.303 58.75-58.75 0-32.4467-26.303-58.75-58.75-58.75zm0 19.5c-21.6772 0-39.25 17.5728-39.25 39.25s17.5728 39.25 39.25 39.25 39.25-17.5728 39.25-39.25-17.5728-39.25-39.25-39.25zm-42.75 39.25c0-23.6102 19.1398-42.75 42.75-42.75s42.75 19.1398 42.75 42.75-19.1398 42.75-42.75 42.75-42.75-19.1398-42.75-42.75zm20.5 0c0-12.2883 9.9617-22.25 22.25-22.25s22.25 9.9617 22.25 22.25-9.9617 22.25-22.25 22.25-22.25-9.9617-22.25-22.25zm2
 2.25-25.75c-14.2213 0-25.75 11.5287-25.75 25.75s11.5287 25.75 25.75 25.75 25.75-11.5287 25.75-25.75-11.5287-25.75-25.75-25.75z"/>
+            <path d="m228.75 225c0-39.35 31.9-71.25 71.25-71.25s71.25 31.9 71.25 71.25-31.9 71.25-71.25 71.25-71.25-31.9-71.25-71.25zm71.25-74.75c-41.283 0-74.75 33.467-74.75 74.75s33.467 74.75 74.75 74.75 74.75-33.467 74.75-74.75-33.467-74.75-74.75-74.75zm-55.25 74.75c0-30.514 24.736-55.25 55.25-55.25s55.25 24.736 55.25 55.25-24.736 55.25-55.25 55.25-55.25-24.736-55.25-55.25zm55.25-58.75c-32.447 0-58.75 26.303-58.75 58.75s26.303 58.75 58.75 58.75 58.75-26.303 58.75-58.75-26.303-58.75-58.75-58.75zm0 19.5c-21.677 0-39.25 17.573-39.25 39.25s17.573 39.25 39.25 39.25 39.25-17.573 39.25-39.25-17.573-39.25-39.25-39.25zm-42.75 39.25c0-23.61 19.14-42.75 42.75-42.75s42.75 19.14 42.75 42.75-19.14 42.75-42.75 42.75-42.75-19.14-42.75-42.75zm20.5 0c0-12.288 9.962-22.25 22.25-22.25s22.25 9.962 22.25 22.25-9.962 22.25-22.25 22.25-22.25-9.962-22.25-22.25zm22.25-25.75c-14.221 0-25.75 11.529-25.75 25.75s11.529 25.75 25.75 25.75 25.75-11.529 25.75-25.75-11.529-25.75-25.75-25.75z"/>
+            <path d="m828.75 225c0-39.35 31.9-71.25 71.25-71.25v-3.5c-41.283 0-74.75 33.467-74.75 74.75s33.467 74.75 74.75 74.75v-3.5c-39.35 0-71.25-31.9-71.25-71.25zm16 0c0-30.514 24.736-55.25 55.25-55.25v-3.5c-32.447 0-58.75 26.303-58.75 58.75s26.303 58.75 58.75 58.75v-3.5c-30.514 0-55.25-24.736-55.25-55.25zm55.25-39.25c-21.677 0-39.25 17.573-39.25 39.25s17.573 39.25 39.25 39.25v3.5c-23.61 0-42.75-19.14-42.75-42.75s19.14-42.75 42.75-42.75zm-22.25 39.25c0-12.288 9.962-22.25 22.25-22.25v-3.5c-14.221 0-25.75 11.529-25.75 25.75s11.529 25.75 25.75 25.75v-3.5c-12.288 0-22.25-9.962-22.25-22.25z"/>
+            <path d="m71.25 225c0-39.35-31.8997-71.25-71.25-71.25v-3.5c41.2833 0 74.75 33.467 74.75 74.75s-33.4667 74.75-74.75 74.75v-3.5c39.3503 0 71.25-31.9 71.25-71.25zm-16 0c0-30.514-24.7363-55.25-55.25-55.25v-3.5c32.4467 0 58.75 26.303 58.75 58.75s-26.3033 58.75-58.75 58.75v-3.5c30.5137 0 55.25-24.736 55.25-55.25zm-55.25-39.25c21.6772 0 39.25 17.573 39.25 39.25s-17.5728 39.25-39.25 39.25v3.5c23.6102 0 42.75-19.14 42.75-42.75s-19.1398-42.75-42.75-42.75zm22.25 39.25c0-12.288-9.9617-22.25-22.25-22.25v-3.5c14.2213 0 25.75 11.529 25.75 25.75s-11.5287 25.75-25.75 25.75v-3.5c12.2883 0 22.25-9.962 22.25-22.25z"/>
+            <path d="m303.75 75c0-39.3503 31.9-71.25 71.25-71.25s71.25 31.8997 71.25 71.25c0 39.35-31.9 71.25-71.25 71.25s-71.25-31.9-71.25-71.25zm71.25-74.75c-41.283 0-74.75 33.4667-74.75 74.75 0 41.283 33.467 74.75 74.75 74.75s74.75-33.467 74.75-74.75c0-41.2833-33.467-74.75-74.75-74.75zm-55.25 74.75c0-30.5137 24.736-55.25 55.25-55.25s55.25 24.7363 55.25 55.25c0 30.514-24.736 55.25-55.25 55.25s-55.25-24.736-55.25-55.25zm55.25-58.75c-32.447 0-58.75 26.3033-58.75 58.75 0 32.447 26.303 58.75 58.75 58.75s58.75-26.303 58.75-58.75c0-32.4467-26.303-58.75-58.75-58.75zm0 19.5c-21.677 0-39.25 17.5728-39.25 39.25s17.573 39.25 39.25 39.25 39.25-17.5728 39.25-39.25-17.573-39.25-39.25-39.25zm-42.75 39.25c0-23.6102 19.14-42.75 42.75-42.75s42.75 19.1398 42.75 42.75-19.14 42.75-42.75 42.75-42.75-19.1398-42.75-42.75zm20.5 0c0-12.2883 9.962-22.25 22.25-22.25s22.25 9.9617 22.25 22.25-9.962 22.25-22.25 22.25-22.25-9.9617-22.25-22.25zm22.25-25.75c-14.221 0-25.75 11.5287-25.75 25.75s11.529 25.75 25.75 25
 .75 25.75-11.5287 25.75-25.75-11.529-25.75-25.75-25.75z"/>
+            <path d="m603.75 75c0-39.3503 31.9-71.25 71.25-71.25s71.25 31.8997 71.25 71.25c0 39.35-31.9 71.25-71.25 71.25s-71.25-31.9-71.25-71.25zm71.25-74.75c-41.283 0-74.75 33.4667-74.75 74.75 0 41.283 33.467 74.75 74.75 74.75s74.75-33.467 74.75-74.75c0-41.2833-33.467-74.75-74.75-74.75zm-55.25 74.75c0-30.5137 24.736-55.25 55.25-55.25s55.25 24.7363 55.25 55.25c0 30.514-24.736 55.25-55.25 55.25s-55.25-24.736-55.25-55.25zm55.25-58.75c-32.447 0-58.75 26.3033-58.75 58.75 0 32.447 26.303 58.75 58.75 58.75s58.75-26.303 58.75-58.75c0-32.4467-26.303-58.75-58.75-58.75zm0 19.5c-21.677 0-39.25 17.5728-39.25 39.25s17.573 39.25 39.25 39.25 39.25-17.5728 39.25-39.25-17.573-39.25-39.25-39.25zm-42.75 39.25c0-23.6102 19.14-42.75 42.75-42.75s42.75 19.1398 42.75 42.75-19.14 42.75-42.75 42.75-42.75-19.1398-42.75-42.75zm20.5 0c0-12.2883 9.962-22.25 22.25-22.25s22.25 9.9617 22.25 22.25-9.962 22.25-22.25 22.25-22.25-9.9617-22.25-22.25zm22.25-25.75c-14.221 0-25.75 11.5287-25.75 25.75s11.529 25.75 25.75 25
 .75 25.75-11.5287 25.75-25.75-11.529-25.75-25.75-25.75z"/>
+            <path d="m150 150.25c-.878 0-1.753.015-2.624.045-.966.034-1.722.844-1.689 1.81.033.965.843 1.721 1.809 1.688.831-.029 1.666-.043 2.504-.043s1.673.014 2.504.043c.966.033 1.776-.723 1.809-1.688.033-.966-.723-1.776-1.689-1.81-.871-.03-1.746-.045-2.624-.045zm-11.449 4.415c.954-.154 1.603-1.053 1.449-2.007s-1.053-1.603-2.007-1.449c-1.735.281-3.45.621-5.143 1.018-.941.221-1.525 1.163-1.304 2.104s1.163 1.524 2.104 1.303c1.613-.378 3.248-.702 4.901-.969zm23.456-3.456c-.954-.154-1.853.495-2.007 1.449s.495 1.853 1.449 2.007c1.653.267 3.288.591 4.901.969.941.221 1.883-.362 2.104-1.303s-.363-1.883-1.304-2.104c-1.693-.397-3.408-.737-5.143-1.018zm-36.956 7.031c.905-.339 1.365-1.347 1.026-2.252-.338-.906-1.347-1.365-2.252-1.027-1.642.615-3.258 1.285-4.843 2.009-.879.401-1.266 1.439-.865 2.319.402.879 1.44 1.266 2.319.865 1.511-.69 3.05-1.329 4.615-1.914zm51.124-3.279c-.905-.338-1.914.121-2.252 1.027-.339.905.121 1.913 1.026 2.252 1.565.585 3.104 1.224 4.615 1.914.879.401 1.917.014 2.31
 9-.865.401-.88.014-1.918-.865-2.319-1.585-.724-3.201-1.394-4.843-2.009zm-63.661 9.436c.821-.51 1.074-1.588.565-2.41-.509-.821-1.588-1.074-2.41-.565-1.487.922-2.94 1.895-4.355 2.916-.784.565-.961 1.659-.395 2.443.565.784 1.659.961 2.443.395 1.349-.973 2.734-1.9 4.152-2.779zm76.817-2.975c-.822-.509-1.901-.256-2.41.565-.509.822-.256 1.9.565 2.41 1.418.879 2.803 1.806 4.152 2.779.784.566 1.878.389 2.443-.395.566-.784.389-1.878-.395-2.443-1.415-1.021-2.868-1.994-4.355-2.916zm-87.915 11.461c.707-.659.745-1.767.086-2.474-.659-.706-1.7663-.745-2.4731-.086-1.2779 1.192-2.5139 2.428-3.7057 3.706-.6591.707-.6205 1.814.0863 2.473s1.8142.621 2.4733-.086c1.1363-1.218 2.3152-2.397 3.5332-3.533zm99.555-2.56c-.707-.659-1.814-.62-2.473.086-.659.707-.621 1.815.086 2.474 1.218 1.136 2.397 2.315 3.533 3.533.659.707 1.767.745 2.474.086.706-.659.745-1.766.086-2.473-1.192-1.278-2.428-2.514-3.706-3.706zm-108.7948 13.039c.5655-.784.3884-1.878-.3954-2.443-.7838-.566-1.8776-.389-2.4431.395-1.021 1.415-1.9938 2
 .868-2.9158 4.355-.5092.822-.2561 1.901.5653 2.41.8215.509 1.9003.256 2.4095-.565.8789-1.418 1.8061-2.803 2.7795-4.152zm118.4858-2.048c-.565-.784-1.659-.961-2.443-.395-.784.565-.961 1.659-.395 2.443.973 1.349 1.9 2.734 2.779 4.152.51.821 1.588 1.074 2.41.565.821-.509 1.074-1.588.565-2.41-.922-1.487-1.895-2.94-2.916-4.355zm-125.5085 14.122c.4015-.879.0143-1.917-.8649-2.319-.8792-.401-1.9173-.014-2.3188.865-.724 1.585-1.3942 3.201-2.0083 4.843-.3385.905.121 1.914 1.0263 2.252.9053.339 1.9135-.121 2.252-1.026.5852-1.565 1.2238-3.104 1.9137-4.615zm132.8765-1.454c-.401-.879-1.439-1.266-2.319-.865-.879.402-1.266 1.44-.865 2.319.69 1.511 1.329 3.05 1.914 4.615.339.905 1.347 1.365 2.252 1.026.906-.338 1.365-1.347 1.027-2.252-.615-1.642-1.285-3.258-2.009-4.843zm-137.3955 14.668c.2209-.941-.3628-1.883-1.3037-2.104s-1.8828.363-2.1037 1.304c-.3975 1.693-.7374 3.408-1.0176 5.143-.1541.954.4944 1.853 1.4486 2.007.9541.154 1.8525-.495 2.0066-1.449.267-1.653.591-3.288.9698-4.901zm142.1385-.8c-.221-
 .941-1.163-1.525-2.104-1.304s-1.524 1.163-1.303 2.104c.378 1.613.702 3.248.969 4.901.154.954 1.053 1.603 2.007 1.449s1.603-1.053 1.449-2.007c-.281-1.735-.621-3.45-1.018-5.143zm-143.9799 14.646c.0334-.966-.7226-1.776-1.6885-1.809-.966-.033-1.776.723-1.8094 1.689-.03.871-.0452 1.746-.0452 2.624s.0152 1.753.0452 2.624c.0334.966.8434 1.722 1.8094 1.689.9659-.033 1.7219-.843 1.6885-1.809-.0286-.831-.0431-1.666-.0431-2.504s.0145-1.673.0431-2.504zm145.9119-.12c-.034-.966-.844-1.722-1.81-1.689-.965.033-1.721.843-1.688 1.809.029.831.043 1.666.043 2.504s-.014 1.673-.043 2.504c-.033.966.723 1.776 1.688 1.809.966.033 1.776-.723 1.81-1.689.03-.871.045-1.746.045-2.624s-.015-1.753-.045-2.624zm-145.0403 14.073c-.1541-.954-1.0525-1.603-2.0066-1.449-.9542.154-1.6027 1.053-1.4486 2.007.2802 1.735.6201 3.45 1.0176 5.143.2209.941 1.1628 1.525 2.1037 1.304s1.5246-1.163 1.3037-2.104c-.3788-1.613-.7028-3.248-.9698-4.901zm144.1263.558c.154-.954-.495-1.853-1.449-2.007s-1.853.495-2.007 1.449c-.267 1.653-.591 
 3.288-.969 4.901-.221.941.362 1.883 1.303 2.104s1.883-.363 2.104-1.304c.397-1.693.737-3.408 1.018-5.143zm-140.5512 12.942c-.3385-.905-1.3467-1.365-2.252-1.026-.9053.338-1.3648 1.347-1.0263 2.252.6141 1.642 1.2843 3.258 2.0083 4.843.4015.879 1.4396 1.266 2.3188.865.8792-.402 1.2664-1.44.8649-2.319-.6899-1.511-1.3285-3.05-1.9137-4.615zm136.7992 1.226c.338-.905-.121-1.914-1.027-2.252-.905-.339-1.913.121-2.252 1.026-.585 1.565-1.224 3.104-1.914 4.615-.401.879-.014 1.917.865 2.319.88.401 1.918.014 2.319-.865.724-1.585 1.394-3.201 2.009-4.843zm-130.6423 11.311c-.5093-.821-1.588-1.074-2.4095-.565-.8214.509-1.0745 1.588-.5653 2.41.922 1.487 1.8948 2.94 2.9158 4.355.5655.784 1.6593.961 2.4431.395.7838-.565.9609-1.659.3954-2.443-.9734-1.349-1.9006-2.734-2.7795-4.152zm124.1813 1.845c.509-.822.256-1.901-.565-2.41-.822-.509-1.9-.256-2.41.565-.879 1.418-1.806 2.803-2.779 4.152-.566.784-.389 1.878.395 2.443.784.566 1.878.389 2.443-.395 1.021-1.415 1.994-2.868 2.916-4.355zm-115.6952 9.253c-.6592-.7
 07-1.7665-.745-2.4733-.086s-.7455 1.766-.0863 2.473c1.1918 1.278 2.4278 2.514 3.7057 3.706.7068.659 1.8141.62 2.4731-.086.659-.707.621-1.815-.086-2.474-1.218-1.136-2.3969-2.315-3.5332-3.533zm106.7942 2.387c.659-.707.62-1.814-.086-2.473-.707-.659-1.815-.621-2.474.086-1.136 1.218-2.315 2.397-3.533 3.533-.707.659-.745 1.767-.086 2.474.659.706 1.766.745 2.473.086 1.278-1.192 2.514-2.428 3.706-3.706zm-96.315 6.853c-.784-.566-1.878-.389-2.443.395-.566.784-.389 1.878.395 2.443 1.415 1.021 2.868 1.994 4.355 2.916.822.509 1.901.256 2.41-.565.509-.822.256-1.9-.565-2.41-1.418-.879-2.803-1.806-4.152-2.779zm85.324 2.838c.784-.565.961-1.659.395-2.443-.565-.784-1.659-.961-2.443-.395-1.349.973-2.734 1.9-4.152 2.779-.821.51-1.074 1.588-.565 2.41.509.821 1.588 1.074 2.41.565 1.487-.922 2.94-1.895 4.355-2.916zm-73.25 4.184c-.879-.401-1.917-.014-2.319.865-.401.88-.014 1.918.865 2.319 1.585.724 3.201 1.394 4.843 2.009.905.338 1.914-.121 2.252-1.027.339-.905-.121-1.913-1.026-2.252-1.565-.585-3.104-1.224-
 4.615-1.914zm60.582 3.184c.879-.401 1.266-1.439.865-2.319-.402-.879-1.44-1.266-2.319-.865-1.511.69-3.05 1.329-4.615 1.914-.905.339-1.365 1.347-1.026 2.252.338.906 1.347 1.365 2.252 1.027 1.642-.615 3.258-1.285 4.843-2.009zm-47.368 1.336c-.941-.221-1.883.362-2.104 1.303s.363 1.883 1.304 2.104c1.693.397 3.408.737 5.143 1.018.954.154 1.853-.495 2.007-1.449s-.495-1.853-1.449-2.007c-1.653-.267-3.288-.591-4.901-.969zm33.5 3.407c.941-.221 1.525-1.163 1.304-2.104s-1.163-1.524-2.104-1.303c-1.613.378-3.248.702-4.901.969-.954.154-1.603 1.053-1.449 2.007s1.053 1.603 2.007 1.449c1.735-.281 3.45-.621 5.143-1.018zm-19.654-1.566c-.966-.033-1.776.723-1.809 1.688-.033.966.723 1.776 1.689 1.81.871.03 1.746.045 2.624.045s1.753-.015 2.624-.045c.966-.034 1.722-.844 1.689-1.81-.033-.965-.843-1.721-1.809-1.688-.831.029-1.666.043-2.504.043s-1.673-.014-2.504-.043zm2.504-130.957c-.802 0-1.601.016-2.396.047-.965.038-1.717.852-1.679 1.818s.852 1.718 1.817 1.679c.749-.029 1.502-.044 2.258-.044s1.509.015 2.258.04
 4c.966.039 1.779-.713 1.817-1.679s-.714-1.78-1.679-1.818c-.795-.031-1.594-.047-2.396-.047zm-10.317 4.444c.95-.176 1.578-1.09 1.402-2.04s-1.089-1.578-2.04-1.402c-1.579.293-3.136.648-4.668 1.062-.933.252-1.485 1.213-1.233 2.146s1.213 1.485 2.146 1.233c1.442-.39 2.907-.724 4.393-.999zm21.272-3.442c-.951-.176-1.864.452-2.04 1.402s.452 1.864 1.402 2.04c1.486.275 2.951.609 4.393.999.933.252 1.894-.3 2.146-1.233s-.3-1.894-1.233-2.146c-1.532-.414-3.089-.769-4.668-1.062zm12.799 3.907c-.887-.385-1.917.022-2.302.909-.384.887.023 1.917.909 2.302 1.383.599 2.737 1.253 4.059 1.958.853.454 1.913.132 2.368-.721.454-.853.132-1.913-.721-2.368-1.405-.749-2.844-1.443-4.313-2.08zm-46.115 3.211c.886-.385 1.293-1.415.909-2.302-.385-.887-1.415-1.294-2.302-.909-1.469.637-2.908 1.331-4.313 2.08-.853.455-1.175 1.515-.721 2.368.455.853 1.515 1.175 2.368.721 1.322-.705 2.676-1.359 4.059-1.958zm-10.921 6.278c.779-.573.946-1.668.373-2.447-.572-.778-1.667-.945-2.446-.373-1.287.946-2.535 1.943-3.741 2.987-.731.633-
 .81 1.738-.177 2.469.632.73 1.738.81 2.468.177 1.136-.983 2.311-1.922 3.523-2.813zm68.637-2.82c-.779-.572-1.874-.405-2.446.373-.573.779-.406 1.874.373 2.447 1.212.891 2.387 1.83 3.523 2.813.73.633 1.836.553 2.468-.177.633-.731.554-1.836-.177-2.469-1.206-1.044-2.454-2.041-3.741-2.987zm-77.894 11.367c.633-.73.553-1.836-.177-2.468-.731-.633-1.836-.554-2.469.177-1.044 1.206-2.041 2.454-2.987 3.741-.572.779-.405 1.874.373 2.446.779.573 1.874.406 2.447-.373.891-1.212 1.83-2.387 2.813-3.523zm87.724-2.291c-.633-.731-1.738-.81-2.469-.177-.73.632-.81 1.738-.177 2.468.983 1.136 1.922 2.311 2.813 3.523.573.779 1.668.946 2.447.373.778-.572.945-1.667.373-2.446-.946-1.287-1.943-2.535-2.987-3.741zm7.576 11.029c-.455-.853-1.515-1.175-2.368-.721-.853.455-1.175 1.515-.721 2.368.705 1.322 1.359 2.676 1.958 4.059.385.886 1.415 1.293 2.302.909.887-.385 1.294-1.415.909-2.302-.637-1.469-1.331-2.908-2.08-4.313zm-102.433 1.647c.454-.853.132-1.913-.7213-2.368-.8529-.454-1.9128-.132-2.3675.721-.7489 1.405-1.44
 33 2.844-2.0805 4.313-.3846.887.0224 1.917.9091 2.302.8867.384 1.9173-.023 2.3019-.909.5996-1.383 1.2532-2.737 1.9583-4.059zm-4.6354 11.71c.2521-.933-.2999-1.894-1.233-2.146-.933-.252-1.8937.3-2.1459 1.233-.4141 1.532-.7688 3.089-1.0614 4.668-.1761.951.4515 1.864 1.4018 2.04s1.8635-.452 2.0396-1.402c.2754-1.486.6092-2.951.9989-4.393zm111.9934-.913c-.252-.933-1.213-1.485-2.146-1.233s-1.485 1.213-1.233 2.146c.39 1.442.724 2.907.999 4.393.176.95 1.09 1.578 2.04 1.402s1.578-1.089 1.402-2.04c-.293-1.579-.648-3.136-1.062-4.668zm-113.8915 13.365c.0381-.966-.7139-1.779-1.6797-1.817-.9657-.038-1.7795.714-1.8176 1.679-.0314.795-.0472 1.594-.0472 2.396s.0158 1.601.0472 2.396c.0381.965.8519 1.717 1.8176 1.679.9658-.038 1.7178-.852 1.6797-1.817-.0296-.749-.0445-1.502-.0445-2.258s.0149-1.509.0445-2.258zm115.9085-.138c-.038-.965-.852-1.717-1.818-1.679s-1.718.852-1.679 1.817c.029.749.044 1.502.044 2.258s-.015 1.509-.044 2.258c-.039.966.713 1.779 1.679 1.817s1.78-.714 1.818-1.679c.031-.795.047-1.594
 .047-2.396s-.016-1.601-.047-2.396zm-115.0093 12.713c-.1761-.95-1.0893-1.578-2.0396-1.402s-1.5779 1.089-1.4018 2.04c.2926 1.579.6473 3.136 1.0614 4.668.2521.933 1.2129 1.485 2.1459 1.233.9331-.252 1.4851-1.213 1.233-2.146-.3897-1.442-.7235-2.907-.9989-4.393zm114.0543.638c.176-.951-.452-1.864-1.402-2.04s-1.864.452-2.04 1.402c-.275 1.486-.609 2.951-.999 4.393-.252.933.3 1.894 1.233 2.146s1.894-.3 2.146-1.233c.414-1.532.769-3.089 1.062-4.668zm-110.3783 11.406c-.3846-.886-1.4152-1.293-2.3019-.909-.8867.385-1.2937 1.415-.9091 2.302.6372 1.469 1.3316 2.908 2.0805 4.313.4547.853 1.5146 1.175 2.3675.721.8533-.455 1.1753-1.515.7213-2.368-.7051-1.322-1.3587-2.676-1.9583-4.059zm106.4713 1.393c.385-.887-.022-1.917-.909-2.302-.887-.384-1.917.023-2.302.909-.599 1.383-1.253 2.737-1.958 4.059-.454.853-.132 1.913.721 2.368.853.454 1.913.132 2.368-.721.749-1.405 1.443-2.844 2.08-4.313zm-100.193 9.528c-.573-.779-1.668-.946-2.447-.373-.778.572-.945 1.667-.373 2.446.946 1.287 1.943 2.535 2.987 3.741.633.
 731 1.738.81 2.469.177.73-.632.81-1.738.177-2.468-.983-1.136-1.922-2.311-2.813-3.523zm93.524 2.073c.572-.779.405-1.874-.373-2.446-.779-.573-1.874-.406-2.447.373-.891 1.212-1.83 2.387-2.813 3.523-.633.73-.553 1.836.177 2.468.731.633 1.836.554 2.469-.177 1.044-1.206 2.041-2.454 2.987-3.741zm-84.977 7.184c-.73-.633-1.836-.553-2.468.177-.633.731-.554 1.836.177 2.469 1.206 1.044 2.454 2.041 3.741 2.987.779.572 1.874.405 2.446-.373.573-.779.406-1.874-.373-2.447-1.212-.891-2.387-1.83-3.523-2.813zm75.901 2.646c.731-.633.81-1.738.177-2.469-.632-.73-1.738-.81-2.468-.177-1.136.983-2.311 1.922-3.523 2.813-.779.573-.946 1.668-.373 2.447.572.778 1.667.945 2.446.373 1.287-.946 2.535-1.943 3.741-2.987zm-65.516 4.487c-.853-.454-1.913-.132-2.368.721-.454.853-.132 1.913.721 2.368 1.405.749 2.844 1.443 4.313 2.08.887.385 1.917-.022 2.302-.909.384-.887-.023-1.917-.909-2.302-1.383-.599-2.737-1.253-4.059-1.958zm54.487 3.089c.853-.455 1.175-1.515.721-2.368-.455-.853-1.515-1.175-2.368-.721-1.322.705-2.676 1
 .359-4.059 1.958-.886.385-1.293 1.415-.909 2.302.385.887 1.415 1.294 2.302.909 1.469-.637 2.908-1.331 4.313-2.08zm-42.777 1.546c-.933-.252-1.894.3-2.146 1.233s.3 1.894 1.233 2.146c1.532.414 3.089.769 4.668 1.062.951.176 1.864-.452 2.04-1.402s-.452-1.864-1.402-2.04c-1.486-.275-2.951-.609-4.393-.999zm30.333 3.379c.933-.252 1.485-1.213 1.233-2.146s-1.213-1.485-2.146-1.233c-1.442.39-2.907.724-4.393.999-.95.176-1.578 1.09-1.402 2.04s1.089 1.578 2.04 1.402c1.579-.293 3.136-.648 4.668-1.062zm-17.881-1.48c-.966-.039-1.779.713-1.817 1.679s.714 1.78 1.679 1.818c.795.031 1.594.047 2.396.047s1.601-.016 2.396-.047c.965-.038 1.717-.852 1.679-1.818s-.852-1.718-1.817-1.679c-.749.029-1.502.044-2.258.044s-1.509-.015-2.258-.044zm-.139-98.89c.794-.044 1.593-.066 2.397-.066s1.603.022 2.397.066c.965.053 1.704.879 1.65 1.844-.053.965-.878 1.704-1.843 1.651-.73-.041-1.464-.061-2.204-.061s-1.474.02-2.204.061c-.965.053-1.79-.686-1.843-1.651-.054-.965.685-1.791 1.65-1.844zm-6.378 2.587c.247.934-.312 1.891-1.2
 46 2.137-1.429.377-2.826.831-4.186 1.36-.901.35-1.915-.097-2.265-.998s.097-1.915.997-2.265c1.483-.576 3.005-1.071 4.563-1.481.934-.246 1.891.312 2.137 1.247zm17.55 0c.246-.935 1.203-1.493 2.137-1.247 1.558.41 3.08.905 4.563 1.481.901.35 1.347 1.364.997 2.265s-1.364 1.348-2.265.998c-1.36-.529-2.757-.983-4.186-1.36-.934-.246-1.493-1.203-1.246-2.137zm-29.509 4.675c.525.812.291 1.895-.521 2.419-1.236.798-2.424 1.663-3.559 2.591-.749.611-1.851.5-2.463-.248-.611-.749-.5-1.851.248-2.463 1.236-1.01 2.53-1.952 3.876-2.82.812-.525 1.895-.291 2.419.521zm41.468 0c.524-.812 1.607-1.046 2.419-.521 1.346.868 2.64 1.81 3.876 2.82.748.612.859 1.714.248 2.463-.612.748-1.714.859-2.463.248-1.135-.928-2.323-1.793-3.559-2.591-.812-.524-1.046-1.607-.521-2.419zm-51.394 8.145c.748.612.859 1.714.248 2.463-.928 1.135-1.793 2.323-2.591 3.559-.524.812-1.607 1.046-2.419.521-.812-.524-1.046-1.607-.521-2.419.868-1.346 1.81-2.64 2.82-3.876.612-.748 1.714-.859 2.463-.248zm61.32 0c.749-.611 1.851-.5 2.463.248 1.01 1.
 236 1.952 2.53 2.82 3.876.525.812.291 1.895-.521 2.419-.812.525-1.895.291-2.419-.521-.798-1.236-1.663-2.424-2.591-3.559-.611-.749-.5-1.851.248-2.463zm-68.258 10.805c.901.35 1.348 1.364.998 2.265-.529 1.36-.983 2.757-1.36 4.186-.246.934-1.203 1.493-2.137 1.246-.935-.246-1.493-1.203-1.247-2.137.41-1.558.905-3.08 1.481-4.563.35-.9 1.364-1.347 2.265-.997zm75.196 0c.901-.35 1.915.097 2.265.997.576 1.483 1.071 3.005 1.481 4.563.246.934-.312 1.891-1.247 2.137-.934.247-1.891-.312-2.137-1.246-.377-1.429-.831-2.826-1.36-4.186-.35-.901.097-1.915.998-2.265zm-78.438 12.425c.965.053 1.704.878 1.651 1.843-.041.73-.061 1.464-.061 2.204s.02 1.474.061 2.204c.053.965-.686 1.79-1.651 1.843-.965.054-1.791-.685-1.844-1.65-.044-.794-.066-1.593-.066-2.397s.022-1.603.066-2.397c.053-.965.879-1.704 1.844-1.65zm81.68 0c.965-.054 1.791.685 1.844 1.65.044.794.066 1.593.066 2.397s-.022 1.603-.066 2.397c-.053.965-.879 1.704-1.844 1.65-.965-.053-1.704-.878-1.651-1.843.041-.73.061-1.464.061-2.204s-.02-1.474-.061-2.2
 04c-.053-.965.686-1.79 1.651-1.843zm-80.937 12.822c.934-.247 1.891.312 2.137 1.246.377 1.429.831 2.826 1.36 4.186.35.901-.097 1.915-.998 2.265s-1.915-.097-2.265-.997c-.576-1.483-1.071-3.005-1.481-4.563-.246-.934.312-1.891 1.247-2.137zm80.194 0c.935.246 1.493 1.203 1.247 2.137-.41 1.558-.905 3.08-1.481 4.563-.35.901-1.364 1.347-2.265.997s-1.348-1.364-.998-2.265c.529-1.36.983-2.757 1.36-4.186.246-.934 1.203-1.493 2.137-1.246zm-75.519 11.959c.812-.525 1.895-.291 2.419.521.798 1.236 1.663 2.424 2.591 3.559.611.749.5 1.851-.248 2.463-.749.611-1.851.5-2.463-.248-1.01-1.236-1.952-2.53-2.82-3.876-.525-.812-.291-1.895.521-2.419zm70.844 0c.812.524 1.046 1.607.521 2.419-.868 1.346-1.81 2.64-2.82 3.876-.612.748-1.714.859-2.463.248-.748-.612-.859-1.714-.248-2.463.928-1.135 1.793-2.323 2.591-3.559.524-.812 1.607-1.046 2.419-.521zm-62.699 9.926c.612-.748 1.714-.859 2.463-.248 1.135.928 2.323 1.793 3.559 2.591.812.524 1.046 1.607.521 2.419-.524.812-1.607 1.046-2.419.521-1.346-.868-2.64-1.81-3.876-2
 .82-.748-.612-.859-1.714-.248-2.463zm54.554 0c.611.749.5 1.851-.248 2.463-1.236 1.01-2.53 1.952-3.876 2.82-.812.525-1.895.291-2.419-.521-.525-.812-.291-1.895.521-2.419 1.236-.798 2.424-1.663 3.559-2.591.749-.611 1.851-.5 2.463.248zm-43.749 6.938c.35-.901 1.364-1.348 2.265-.998 1.36.529 2.757.983 4.186 1.36.934.246 1.493 1.203 1.246 2.137-.246.935-1.203 1.493-2.137 1.247-1.558-.41-3.08-.905-4.563-1.481-.9-.35-1.347-1.364-.997-2.265zm32.944 0c.35.901-.097 1.915-.997 2.265-1.483.576-3.005 1.071-4.563 1.481-.934.246-1.891-.312-2.137-1.247-.247-.934.312-1.891 1.246-2.137 1.429-.377 2.826-.831 4.186-1.36.901-.35 1.915.097 2.265.998zm-20.519 3.242c.053-.965.878-1.704 1.843-1.651.73.041 1.464.061 2.204.061s1.474-.02 2.204-.061c.965-.053 1.79.686 1.843 1.651.054.965-.685 1.791-1.65 1.844-.794.044-1.593.066-2.397.066s-1.603-.022-2.397-.066c-.965-.053-1.704-.879-1.65-1.844zm4.047-66.59c-.81 0-1.612.037-2.403.111-.963.089-1.671.941-1.582 1.904.09.962.942 1.67 1.904 1.581.685-.064 1.379-.096 2.0
 81-.096s1.396.032 2.081.096c.962.089 1.814-.619 1.904-1.581.089-.963-.619-1.815-1.582-1.904-.791-.074-1.593-.111-2.403-.111zm10.74 2.34c-.878-.403-1.917-.018-2.32.86-.404.878-.019 1.917.859 2.321 1.267.582 2.47 1.28 3.596 2.08.788.56 1.88.375 2.44-.412.56-.788.376-1.881-.412-2.441-1.303-.925-2.695-1.733-4.163-2.408zm-20.019 3.181c.878-.404 1.263-1.443.859-2.321-.403-.878-1.442-1.263-2.32-.86-1.468.675-2.86 1.483-4.163 2.408-.788.56-.972 1.653-.412 2.441.56.787 1.652.972 2.44.412 1.126-.8 2.329-1.498 3.596-2.08zm30.281 5.326c-.56-.788-1.653-.972-2.441-.412s-.972 1.652-.412 2.44c.8 1.126 1.498 2.329 2.08 3.596.404.878 1.443 1.263 2.321.859.878-.403 1.263-1.442.86-2.32-.675-1.468-1.483-2.86-2.408-4.163zm-39.151 2.028c.56-.788.375-1.88-.412-2.44-.788-.56-1.881-.376-2.441.412-.925 1.303-1.733 2.695-2.408 4.163-.403.878-.018 1.917.86 2.32.878.404 1.917.019 2.321-.859.582-1.267 1.28-2.47 2.08-3.596zm-4.005 10.794c.089-.962-.619-1.814-1.581-1.904-.963-.089-1.815.619-1.904 1.582-.074.791-.11
 1 1.593-.111 2.403s.037 1.612.111 2.403c.089.963.941 1.671 1.904 1.582.962-.09 1.67-.942 1.581-1.904-.064-.685-.096-1.379-.096-2.081s.032-1.396.096-2.081zm47.793-.322c-.089-.963-.941-1.671-1.904-1.582-.962.09-1.67.942-1.581 1.904.064.685.096 1.379.096 2.081s-.032 1.396-.096 2.081c-.089.962.619 1.814 1.581 1.904.963.089 1.815-.619 1.904-1.582.074-.791.111-1.593.111-2.403s-.037-1.612-.111-2.403zm-2.229 13.143c.403-.878.018-1.917-.86-2.32-.878-.404-1.917-.019-2.321.859-.582 1.267-1.28 2.47-2.08 3.596-.56.788-.376 1.88.412 2.44s1.881.376 2.441-.412c.925-1.303 1.733-2.695 2.408-4.163zm-43.639-1.461c-.404-.878-1.443-1.263-2.321-.859-.878.403-1.263 1.442-.86 2.32.675 1.468 1.483 2.86 2.408 4.163.56.788 1.653.972 2.441.412.787-.56.972-1.652.412-2.44-.8-1.126-1.498-2.329-2.08-3.596zm7.354 8.87c-.788-.56-1.88-.376-2.44.412s-.376 1.881.412 2.441c1.303.925 2.695 1.733 4.163 2.408.878.403 1.917.018 2.32-.86.404-.878.019-1.917-.859-2.321-1.267-.582-2.47-1.28-3.596-2.08zm27.778 2.853c.788-.56.972-
 1.653.412-2.441s-1.652-.972-2.44-.412c-1.126.8-2.329 1.498-3.596 2.08-.878.404-1.263 1.443-.859 2.321.403.878 1.442 1.263 2.32.86 1.468-.675 2.86-1.483 4.163-2.408zm-16.984 1.152c-.962-.089-1.814.619-1.904 1.581-.089.963.619 1.815 1.582 1.904.791.074 1.593.111 2.403.111s1.612-.037 2.403-.111c.963-.089 1.671-.941 1.582-1.904-.09-.962-.942-1.67-1.904-1.581-.685.064-1.379.096-2.081.096s-1.396-.032-2.081-.096z"/>
+            <path d="m750 150.25c-.878 0-1.753.015-2.624.045-.966.034-1.722.844-1.689 1.81.033.965.843 1.721 1.809 1.688.831-.029 1.666-.043 2.504-.043s1.673.014 2.504.043c.966.033 1.776-.723 1.809-1.688.033-.966-.723-1.776-1.689-1.81-.871-.03-1.746-.045-2.624-.045zm-11.449 4.415c.954-.154 1.603-1.053 1.449-2.007s-1.053-1.603-2.007-1.449c-1.735.281-3.45.621-5.143 1.018-.941.221-1.525 1.163-1.304 2.104s1.163 1.524 2.104 1.303c1.613-.378 3.248-.702 4.901-.969zm23.456-3.456c-.954-.154-1.853.495-2.007 1.449s.495 1.853 1.449 2.007c1.653.267 3.288.591 4.901.969.941.221 1.883-.362 2.104-1.303s-.363-1.883-1.304-2.104c-1.693-.397-3.408-.737-5.143-1.018zm-36.956 7.031c.905-.339 1.365-1.347 1.026-2.252-.338-.906-1.347-1.365-2.252-1.027-1.642.615-3.258 1.285-4.843 2.009-.879.401-1.266 1.439-.865 2.319.402.879 1.44 1.266 2.319.865 1.511-.69 3.05-1.329 4.615-1.914zm51.124-3.279c-.905-.338-1.914.121-2.252 1.027-.339.905.121 1.913 1.026 2.252 1.565.585 3.104 1.224 4.615 1.914.879.401 1.917.014 2.31
 9-.865.401-.88.014-1.918-.865-2.319-1.585-.724-3.201-1.394-4.843-2.009zm-63.661 9.436c.821-.51 1.074-1.588.565-2.41-.509-.821-1.588-1.074-2.41-.565-1.487.922-2.94 1.895-4.355 2.916-.784.565-.961 1.659-.395 2.443.565.784 1.659.961 2.443.395 1.349-.973 2.734-1.9 4.152-2.779zm76.817-2.975c-.822-.509-1.901-.256-2.41.565-.509.822-.256 1.9.565 2.41 1.418.879 2.803 1.806 4.152 2.779.784.566 1.878.389 2.443-.395.566-.784.389-1.878-.395-2.443-1.415-1.021-2.868-1.994-4.355-2.916zm-87.915 11.461c.707-.659.745-1.767.086-2.474-.659-.706-1.766-.745-2.473-.086-1.278 1.192-2.514 2.428-3.706 3.706-.659.707-.62 1.814.086 2.473.707.659 1.815.621 2.474-.086 1.136-1.218 2.315-2.397 3.533-3.533zm99.555-2.56c-.707-.659-1.814-.62-2.473.086-.659.707-.621 1.815.086 2.474 1.218 1.136 2.397 2.315 3.533 3.533.659.707 1.767.745 2.474.086.706-.659.745-1.766.086-2.473-1.192-1.278-2.428-2.514-3.706-3.706zm-108.795 13.039c.566-.784.389-1.878-.395-2.443-.784-.566-1.878-.389-2.443.395-1.021 1.415-1.994 2.868-2.916 4.3
 55-.509.822-.256 1.901.565 2.41.822.509 1.9.256 2.41-.565.879-1.418 1.806-2.803 2.779-4.152zm118.486-2.048c-.565-.784-1.659-.961-2.443-.395-.784.565-.961 1.659-.395 2.443.973 1.349 1.9 2.734 2.779 4.152.51.821 1.588 1.074 2.41.565.821-.509 1.074-1.588.565-2.41-.922-1.487-1.895-2.94-2.916-4.355zm-125.508 14.122c.401-.879.014-1.917-.865-2.319-.88-.401-1.918-.014-2.319.865-.724 1.585-1.394 3.201-2.009 4.843-.338.905.121 1.914 1.027 2.252.905.339 1.913-.121 2.252-1.026.585-1.565 1.224-3.104 1.914-4.615zm132.876-1.454c-.401-.879-1.439-1.266-2.319-.865-.879.402-1.266 1.44-.865 2.319.69 1.511 1.329 3.05 1.914 4.615.339.905 1.347 1.365 2.252 1.026.906-.338 1.365-1.347 1.027-2.252-.615-1.642-1.285-3.258-2.009-4.843zm-137.396 14.668c.221-.941-.362-1.883-1.303-2.104s-1.883.363-2.104 1.304c-.397 1.693-.737 3.408-1.018 5.143-.154.954.495 1.853 1.449 2.007s1.853-.495 2.007-1.449c.267-1.653.591-3.288.969-4.901zm142.139-.8c-.221-.941-1.163-1.525-2.104-1.304s-1.524 1.163-1.303 2.104c.378 1.613.702 3
 .248.969 4.901.154.954 1.053 1.603 2.007 1.449s1.603-1.053 1.449-2.007c-.281-1.735-.621-3.45-1.018-5.143zm-143.98 14.646c.033-.966-.723-1.776-1.688-1.809-.966-.033-1.776.723-1.81 1.689-.03.871-.045 1.746-.045 2.624s.015 1.753.045 2.624c.034.966.844 1.722 1.81 1.689.965-.033 1.721-.843 1.688-1.809-.029-.831-.043-1.666-.043-2.504s.014-1.673.043-2.504zm145.912-.12c-.034-.966-.844-1.722-1.81-1.689-.965.033-1.721.843-1.688 1.809.029.831.043 1.666.043 2.504s-.014 1.673-.043 2.504c-.033.966.723 1.776 1.688 1.809.966.033 1.776-.723 1.81-1.689.03-.871.045-1.746.045-2.624s-.015-1.753-.045-2.624zm-145.04 14.073c-.154-.954-1.053-1.603-2.007-1.449s-1.603 1.053-1.449 2.007c.281 1.735.621 3.45 1.018 5.143.221.941 1.163 1.525 2.104 1.304s1.524-1.163 1.303-2.104c-.378-1.613-.702-3.248-.969-4.901zm144.126.558c.154-.954-.495-1.853-1.449-2.007s-1.853.495-2.007 1.449c-.267 1.653-.591 3.288-.969 4.901-.221.941.362 1.883 1.303 2.104s1.883-.363 2.104-1.304c.397-1.693.737-3.408 1.018-5.143zm-140.551 12.942c
 -.339-.905-1.347-1.365-2.252-1.026-.906.338-1.365 1.347-1.027 2.252.615 1.642 1.285 3.258 2.009 4.843.401.879 1.439 1.266 2.319.865.879-.402 1.266-1.44.865-2.319-.69-1.511-1.329-3.05-1.914-4.615zm136.799 1.226c.338-.905-.121-1.914-1.027-2.252-.905-.339-1.913.121-2.252 1.026-.585 1.565-1.224 3.104-1.914 4.615-.401.879-.014 1.917.865 2.319.88.401 1.918.014 2.319-.865.724-1.585 1.394-3.201 2.009-4.843zm-130.642 11.311c-.51-.821-1.588-1.074-2.41-.565-.821.509-1.074 1.588-.565 2.41.922 1.487 1.895 2.94 2.916 4.355.565.784 1.659.961 2.443.395.784-.565.961-1.659.395-2.443-.973-1.349-1.9-2.734-2.779-4.152zm124.181 1.845c.509-.822.256-1.901-.565-2.41-.822-.509-1.9-.256-2.41.565-.879 1.418-1.806 2.803-2.779 4.152-.566.784-.389 1.878.395 2.443.784.566 1.878.389 2.443-.395 1.021-1.415 1.994-2.868 2.916-4.355zm-115.695 9.253c-.659-.707-1.767-.745-2.474-.086-.706.659-.745 1.766-.086 2.473 1.192 1.278 2.428 2.514 3.706 3.706.707.659 1.814.62 2.473-.086.659-.707.621-1.815-.086-2.474-1.218-1.136-2.3
 97-2.315-3.533-3.533zm106.794 2.387c.659-.707.62-1.814-.086-2.473-.707-.659-1.815-.621-2.474.086-1.136 1.218-2.315 2.397-3.533 3.533-.707.659-.745 1.767-.086 2.474.659.706 1.766.745 2.473.086 1.278-1.192 2.514-2.428 3.706-3.706zm-96.315 6.853c-.784-.566-1.878-.389-2.443.395-.566.784-.389 1.878.395 2.443 1.415 1.021 2.868 1.994 4.355 2.916.822.509 1.901.256 2.41-.565.509-.822.256-1.9-.565-2.41-1.418-.879-2.803-1.806-4.152-2.779zm85.324 2.838c.784-.565.961-1.659.395-2.443-.565-.784-1.659-.961-2.443-.395-1.349.973-2.734 1.9-4.152 2.779-.821.51-1.074 1.588-.565 2.41.509.821 1.588 1.074 2.41.565 1.487-.922 2.94-1.895 4.355-2.916zm-73.25 4.184c-.879-.401-1.917-.014-2.319.865-.401.88-.014 1.918.865 2.319 1.585.724 3.201 1.394 4.843 2.009.905.338 1.914-.121 2.252-1.027.339-.905-.121-1.913-1.026-2.252-1.565-.585-3.104-1.224-4.615-1.914zm60.582 3.184c.879-.401 1.266-1.439.865-2.319-.402-.879-1.44-1.266-2.319-.865-1.511.69-3.05 1.329-4.615 1.914-.905.339-1.365 1.347-1.026 2.252.338.906 1.347 1
 .365 2.252 1.027 1.642-.615 3.258-1.285 4.843-2.009zm-47.368 1.336c-.941-.221-1.883.362-2.104 1.303s.363 1.883 1.304 2.104c1.693.397 3.408.737 5.143 1.018.954.154 1.853-.495 2.007-1.449s-.495-1.853-1.449-2.007c-1.653-.267-3.288-.591-4.901-.969zm33.5 3.407c.941-.221 1.525-1.163 1.304-2.104s-1.163-1.524-2.104-1.303c-1.613.378-3.248.702-4.901.969-.954.154-1.603 1.053-1.449 2.007s1.053 1.603 2.007 1.449c1.735-.281 3.45-.621 5.143-1.018zm-19.654-1.566c-.966-.033-1.776.723-1.809 1.688-.033.966.723 1.776 1.689 1.81.871.03 1.746.045 2.624.045s1.753-.015 2.624-.045c.966-.034 1.722-.844 1.689-1.81-.033-.965-.843-1.721-1.809-1.688-.831.029-1.666.043-2.504.043s-1.673-.014-2.504-.043zm2.504-130.957c-.802 0-1.601.016-2.396.047-.965.038-1.717.852-1.679 1.818s.852 1.718 1.817 1.679c.749-.029 1.502-.044 2.258-.044s1.509.015 2.258.044c.966.039 1.779-.713 1.817-1.679s-.714-1.78-1.679-1.818c-.795-.031-1.594-.047-2.396-.047zm-10.317 4.444c.95-.176 1.578-1.09 1.402-2.04s-1.089-1.578-2.04-1.402c-1.579.293
 -3.136.648-4.668 1.062-.933.252-1.485 1.213-1.233 2.146s1.213 1.485 2.146 1.233c1.442-.39 2.907-.724 4.393-.999zm21.272-3.442c-.951-.176-1.864.452-2.04 1.402s.452 1.864 1.402 2.04c1.486.275 2.951.609 4.393.999.933.252 1.894-.3 2.146-1.233s-.3-1.894-1.233-2.146c-1.532-.414-3.089-.769-4.668-1.062zm12.799 3.907c-.887-.385-1.917.022-2.302.909-.384.887.023 1.917.909 2.302 1.383.599 2.737 1.253 4.059 1.958.853.454 1.913.132 2.368-.721.454-.853.132-1.913-.721-2.368-1.405-.749-2.844-1.443-4.313-2.08zm-46.115 3.211c.886-.385 1.293-1.415.909-2.302-.385-.887-1.415-1.294-2.302-.909-1.469.637-2.908 1.331-4.313 2.08-.853.455-1.175 1.515-.721 2.368.455.853 1.515 1.175 2.368.721 1.322-.705 2.676-1.359 4.059-1.958zm-10.921 6.278c.779-.573.946-1.668.373-2.447-.572-.778-1.667-.945-2.446-.373-1.287.946-2.535 1.943-3.741 2.987-.731.633-.81 1.738-.177 2.469.632.73 1.738.81 2.468.177 1.136-.983 2.311-1.922 3.523-2.813zm68.637-2.82c-.779-.572-1.874-.405-2.446.373-.573.779-.406 1.874.373 2.447 1.212.891 2.3
 87 1.83 3.523 2.813.73.633 1.836.553 2.468-.177.633-.731.554-1.836-.177-2.469-1.206-1.044-2.454-2.041-3.741-2.987zm-77.894 11.367c.633-.73.553-1.836-.177-2.468-.731-.633-1.836-.554-2.469.177-1.044 1.206-2.041 2.454-2.987 3.741-.572.779-.405 1.874.373 2.446.779.573 1.874.406 2.447-.373.891-1.212 1.83-2.387 2.813-3.523zm87.724-2.291c-.633-.731-1.738-.81-2.469-.177-.73.632-.81 1.738-.177 2.468.983 1.136 1.922 2.311 2.813 3.523.573.779 1.668.946 2.447.373.778-.572.945-1.667.373-2.446-.946-1.287-1.943-2.535-2.987-3.741zm7.576 11.029c-.455-.853-1.515-1.175-2.368-.721-.853.455-1.175 1.515-.721 2.368.705 1.322 1.359 2.676 1.958 4.059.385.886 1.415 1.293 2.302.909.887-.385 1.294-1.415.909-2.302-.637-1.469-1.331-2.908-2.08-4.313zm-102.433 1.647c.454-.853.132-1.913-.721-2.368-.853-.454-1.913-.132-2.368.721-.749 1.405-1.443 2.844-2.08 4.313-.385.887.022 1.917.909 2.302.887.384 1.917-.023 2.302-.909.599-1.383 1.253-2.737 1.958-4.059zm-4.635 11.71c.252-.933-.3-1.894-1.233-2.146s-1.894.3-2.146 1.2
 33c-.414 1.532-.769 3.089-1.062 4.668-.176.951.452 1.864 1.402 2.04s1.864-.452 2.04-1.402c.275-1.486.609-2.951.999-4.393zm111.993-.913c-.252-.933-1.213-1.485-2.146-1.233s-1.485 1.213-1.233 2.146c.39 1.442.724 2.907.999 4.393.176.95 1.09 1.578 2.04 1.402s1.578-1.089 1.402-2.04c-.293-1.579-.648-3.136-1.062-4.668zm-113.892 13.365c.039-.966-.713-1.779-1.679-1.817s-1.78.714-1.818 1.679c-.031.795-.047 1.594-.047 2.396s.016 1.601.047 2.396c.038.965.852 1.717 1.818 1.679s1.718-.852 1.679-1.817c-.029-.749-.044-1.502-.044-2.258s.015-1.509.044-2.258zm115.909-.138c-.038-.965-.852-1.717-1.818-1.679s-1.718.852-1.679 1.817c.029.749.044 1.502.044 2.258s-.015 1.509-.044 2.258c-.039.966.713 1.779 1.679 1.817s1.78-.714 1.818-1.679c.031-.795.047-1.594.047-2.396s-.016-1.601-.047-2.396zm-115.009 12.713c-.176-.95-1.09-1.578-2.04-1.402s-1.578 1.089-1.402 2.04c.293 1.579.648 3.136 1.062 4.668.252.933 1.213 1.485 2.146 1.233s1.485-1.213 1.233-2.146c-.39-1.442-.724-2.907-.999-4.393zm114.054.638c.176-.951-.452
 -1.864-1.402-2.04s-1.864.452-2.04 1.402c-.275 1.486-.609 2.951-.999 4.393-.252.933.3 1.894 1.233 2.146s1.894-.3 2.146-1.233c.414-1.532.769-3.089 1.062-4.668zm-110.378 11.406c-.385-.886-1.415-1.293-2.302-.909-.887.385-1.294 1.415-.909 2.302.637 1.469 1.331 2.908 2.08 4.313.455.853 1.515 1.175 2.368.721.853-.455 1.175-1.515.721-2.368-.705-1.322-1.359-2.676-1.958-4.059zm106.471 1.393c.385-.887-.022-1.917-.909-2.302-.887-.384-1.917.023-2.302.909-.599 1.383-1.253 2.737-1.958 4.059-.454.853-.132 1.913.721 2.368.853.454 1.913.132 2.368-.721.749-1.405 1.443-2.844 2.08-4.313zm-100.193 9.528c-.573-.779-1.668-.946-2.447-.373-.778.572-.945 1.667-.373 2.446.946 1.287 1.943 2.535 2.987 3.741.633.731 1.738.81 2.469.177.73-.632.81-1.738.177-2.468-.983-1.136-1.922-2.311-2.813-3.523zm93.524 2.073c.572-.779.405-1.874-.373-2.446-.779-.573-1.874-.406-2.447.373-.891 1.212-1.83 2.387-2.813 3.523-.633.73-.553 1.836.177 2.468.731.633 1.836.554 2.469-.177 1.044-1.206 2.041-2.454 2.987-3.741zm-84.977 7.184c-.
 73-.633-1.836-.553-2.468.177-.633.731-.554 1.836.177 2.469 1.206 1.044 2.454 2.041 3.741 2.987.779.572 1.874.405 2.446-.373.573-.779.406-1.874-.373-2.447-1.212-.891-2.387-1.83-3.523-2.813zm75.901 2.646c.731-.633.81-1.738.177-2.469-.632-.73-1.738-.81-2.468-.177-1.136.983-2.311 1.922-3.523 2.813-.779.573-.946 1.668-.373 2.447.572.778 1.667.945 2.446.373 1.287-.946 2.535-1.943 3.741-2.987zm-65.516 4.487c-.853-.454-1.913-.132-2.368.721-.454.853-.132 1.913.721 2.368 1.405.749 2.844 1.443 4.313 2.08.887.385 1.917-.022 2.302-.909.384-.887-.023-1.917-.909-2.302-1.383-.599-2.737-1.253-4.059-1.958zm54.487 3.089c.853-.455 1.175-1.515.721-2.368-.455-.853-1.515-1.175-2.368-.721-1.322.705-2.676 1.359-4.059 1.958-.886.385-1.293 1.415-.909 2.302.385.887 1.415 1.294 2.302.909 1.469-.637 2.908-1.331 4.313-2.08zm-42.777 1.546c-.933-.252-1.894.3-2.146 1.233s.3 1.894 1.233 2.146c1.532.414 3.089.769 4.668 1.062.951.176 1.864-.452 2.04-1.402s-.452-1.864-1.402-2.04c-1.486-.275-2.951-.609-4.393-.999zm30.333
  3.379c.933-.252 1.485-1.213 1.233-2.146s-1.213-1.485-2.146-1.233c-1.442.39-2.907.724-4.393.999-.95.176-1.578 1.09-1.402 2.04s1.089 1.578 2.04 1.402c1.579-.293 3.136-.648 4.668-1.062zm-17.881-1.48c-.966-.039-1.779.713-1.817 1.679s.714 1.78 1.679 1.818c.795.031 1.594.047 2.396.047s1.601-.016 2.396-.047c.965-.038 1.717-.852 1.679-1.818s-.852-1.718-1.817-1.679c-.749.029-1.502.044-2.258.044s-1.509-.015-2.258-.044zm-.139-98.89c.794-.044 1.593-.066 2.397-.066s1.603.022 2.397.066c.965.053 1.704.879 1.65 1.844-.053.965-.878 1.704-1.843 1.651-.73-.041-1.464-.061-2.204-.061s-1.474.02-2.204.061c-.965.053-1.79-.686-1.843-1.651-.054-.965.685-1.791 1.65-1.844zm-6.378 2.587c.247.934-.312 1.891-1.246 2.137-1.429.377-2.826.831-4.186 1.36-.901.35-1.915-.097-2.265-.998s.097-1.915.997-2.265c1.483-.576 3.005-1.071 4.563-1.481.934-.246 1.891.312 2.137 1.247zm17.55 0c.246-.935 1.203-1.493 2.137-1.247 1.558.41 3.08.905 4.563 1.481.901.35 1.347 1.364.997 2.265s-1.364 1.348-2.265.998c-1.36-.529-2.757-.983-4.
 186-1.36-.934-.246-1.493-1.203-1.246-2.137zm-29.509 4.675c.525.812.291 1.895-.521 2.419-1.236.798-2.424 1.663-3.559 2.591-.749.611-1.851.5-2.463-.248-.611-.749-.5-1.851.248-2.463 1.236-1.01 2.53-1.952 3.876-2.82.812-.525 1.895-.291 2.419.521zm41.468 0c.524-.812 1.607-1.046 2.419-.521 1.346.868 2.64 1.81 3.876 2.82.748.612.859 1.714.248 2.463-.612.748-1.714.859-2.463.248-1.135-.928-2.323-1.793-3.559-2.591-.812-.524-1.046-1.607-.521-2.419zm-51.394 8.145c.748.612.859 1.714.248 2.463-.928 1.135-1.793 2.323-2.591 3.559-.524.812-1.607 1.046-2.419.521-.812-.524-1.046-1.607-.521-2.419.868-1.346 1.81-2.64 2.82-3.876.612-.748 1.714-.859 2.463-.248zm61.32 0c.749-.611 1.851-.5 2.463.248 1.01 1.236 1.952 2.53 2.82 3.876.525.812.291 1.895-.521 2.419-.812.525-1.895.291-2.419-.521-.798-1.236-1.663-2.424-2.591-3.559-.611-.749-.5-1.851.248-2.463zm-68.258 10.805c.901.35 1.348 1.364.998 2.265-.529 1.36-.983 2.757-1.36 4.186-.246.934-1.203 1.493-2.137 1.246-.935-.246-1.493-1.203-1.247-2.137.41-1.558.905
 -3.08 1.481-4.563.35-.9 1.364-1.347 2.265-.997zm75.196 0c.901-.35 1.915.097 2.265.997.576 1.483 1.071 3.005 1.481 4.563.246.934-.312 1.891-1.247 2.137-.934.247-1.891-.312-2.137-1.246-.377-1.429-.831-2.826-1.36-4.186-.35-.901.097-1.915.998-2.265zm-78.438 12.425c.965.053 1.704.878 1.651 1.843-.041.73-.061 1.464-.061 2.204s.02 1.474.061 2.204c.053.965-.686 1.79-1.651 1.843-.965.054-1.791-.685-1.844-1.65-.044-.794-.066-1.593-.066-2.397s.022-1.603.066-2.397c.053-.965.879-1.704 1.844-1.65zm81.68 0c.965-.054 1.791.685 1.844 1.65.044.794.066 1.593.066 2.397s-.022 1.603-.066 2.397c-.053.965-.879 1.704-1.844 1.65-.965-.053-1.704-.878-1.651-1.843.041-.73.061-1.464.061-2.204s-.02-1.474-.061-2.204c-.053-.965.686-1.79 1.651-1.843zm-80.937 12.822c.934-.247 1.891.312 2.137 1.246.377 1.429.831 2.826 1.36 4.186.35.901-.097 1.915-.998 2.265s-1.915-.097-2.265-.997c-.576-1.483-1.071-3.005-1.481-4.563-.246-.934.312-1.891 1.247-2.137zm80.194 0c.935.246 1.493 1.203 1.247 2.137-.41 1.558-.905 3.08-1.481 4.5
 63-.35.901-1.364 1.347-2.265.997s-1.348-1.364-.998-2.265c.529-1.36.983-2.757 1.36-4.186.246-.934 1.203-1.493 2.137-1.246zm-75.519 11.959c.812-.525 1.895-.291 2.419.521.798 1.236 1.663 2.424 2.591 3.559.611.749.5 1.851-.248 2.463-.749.611-1.851.5-2.463-.248-1.01-1.236-1.952-2.53-2.82-3.876-.525-.812-.291-1.895.521-2.419zm70.844 0c.812.524 1.046 1.607.521 2.419-.868 1.346-1.81 2.64-2.82 3.876-.612.748-1.714.859-2.463.248-.748-.612-.859-1.714-.248-2.463.928-1.135 1.793-2.323 2.591-3.559.524-.812 1.607-1.046 2.419-.521zm-62.699 9.926c.612-.748 1.714-.859 2.463-.248 1.135.928 2.323 1.793 3.559 2.591.812.524 1.046 1.607.521 2.419-.524.812-1.607 1.046-2.419.521-1.346-.868-2.64-1.81-3.876-2.82-.748-.612-.859-1.714-.248-2.463zm54.554 0c.611.749.5 1.851-.248 2.463-1.236 1.01-2.53 1.952-3.876 2.82-.812.525-1.895.291-2.419-.521-.525-.812-.291-1.895.521-2.419 1.236-.798 2.424-1.663 3.559-2.591.749-.611 1.851-.5 2.463.248zm-43.749 6.938c.35-.901 1.364-1.348 2.265-.998 1.36.529 2.757.983 4.186 1.3
 6.934.246 1.493 1.203 1.246 2.137-.246.935-1.203 1.493-2.137 1.247-1.558-.41-3.08-.905-4.563-1.481-.9-.35-1.347-1.364-.997-2.265zm32.944 0c.35.901-.097 1.915-.997 2.265-1.483.576-3.005 1.071-4.563 1.481-.934.246-1.891-.312-2.137-1.247-.247-.934.312-1.891 1.246-2.137 1.429-.377 2.826-.831 4.186-1.36.901-.35 1.915.097 2.265.998zm-20.519 3.242c.053-.965.878-1.704 1.843-1.651.73.041 1.464.061 2.204.061s1.474-.02 2.204-.061c.965-.053 1.79.686 1.843 1.651.054.965-.685 1.791-1.65 1.844-.794.044-1.593.066-2.397.066s-1.603-.022-2.397-.066c-.965-.053-1.704-.879-1.65-1.844zm4.047-66.59c-.81 0-1.612.037-2.403.111-.963.089-1.671.941-1.582 1.904.09.962.942 1.67 1.904 1.581.685-.064 1.379-.096 2.081-.096s1.396.032 2.081.096c.962.089 1.814-.619 1.904-1.581.089-.963-.619-1.815-1.582-1.904-.791-.074-1.593-.111-2.403-.111zm10.74 2.34c-.878-.403-1.917-.018-2.32.86-.404.878-.019 1.917.859 2.321 1.267.582 2.47 1.28 3.596 2.08.788.56 1.88.375 2.44-.412.56-.788.376-1.881-.412-2.441-1.303-.925-2.695-1.733-4
 .163-2.408zm-20.019 3.181c.878-.404 1.263-1.443.859-2.321-.403-.878-1.442-1.263-2.32-.86-1.468.675-2.86 1.483-4.163 2.408-.788.56-.972 1.653-.412 2.441.56.787 1.652.972 2.44.412 1.126-.8 2.329-1.498 3.596-2.08zm30.281 5.326c-.56-.788-1.653-.972-2.441-.412s-.972 1.652-.412 2.44c.8 1.126 1.498 2.329 2.08 3.596.404.878 1.443 1.263 2.321.859.878-.403 1.263-1.442.86-2.32-.675-1.468-1.483-2.86-2.408-4.163zm-39.151 2.028c.56-.788.375-1.88-.412-2.44-.788-.56-1.881-.376-2.441.412-.925 1.303-1.733 2.695-2.408 4.163-.403.878-.018 1.917.86 2.32.878.404 1.917.019 2.321-.859.582-1.267 1.28-2.47 2.08-3.596zm-4.005 10.794c.089-.962-.619-1.814-1.581-1.904-.963-.089-1.815.619-1.904 1.582-.074.791-.111 1.593-.111 2.403s.037 1.612.111 2.403c.089.963.941 1.671 1.904 1.582.962-.09 1.67-.942 1.581-1.904-.064-.685-.096-1.379-.096-2.081s.032-1.396.096-2.081zm47.793-.322c-.089-.963-.941-1.671-1.904-1.582-.962.09-1.67.942-1.581 1.904.064.685.096 1.379.096 2.081s-.032 1.396-.096 2.081c-.089.962.619 1.814 1.581
  1.904.963.089 1.815-.619 1.904-1.582.074-.791.111-1.593.111-2.403s-.037-1.612-.111-2.403zm-2.229 13.143c.403-.878.018-1.917-.86-2.32-.878-.404-1.917-.019-2.321.859-.582 1.267-1.28 2.47-2.08 3.596-.56.788-.376 1.88.412 2.44s1.881.376 2.441-.412c.925-1.303 1.733-2.695 2.408-4.163zm-43.639-1.461c-.404-.878-1.443-1.263-2.321-.859-.878.403-1.263 1.442-.86 2.32.675 1.468 1.483 2.86 2.408 4.163.56.788 1.653.972 2.441.412.787-.56.972-1.652.412-2.44-.8-1.126-1.498-2.329-2.08-3.596zm7.354 8.87c-.788-.56-1.88-.376-2.44.412s-.376 1.881.412 2.441c1.303.925 2.695 1.733 4.163 2.408.878.403 1.917.018 2.32-.86.404-.878.019-1.917-.859-2.321-1.267-.582-2.47-1.28-3.596-2.08zm27.778 2.853c.788-.56.972-1.653.412-2.441s-1.652-.972-2.44-.412c-1.126.8-2.329 1.498-3.596 2.08-.878.404-1.263 1.443-.859 2.321.403.878 1.442 1.263 2.32.86 1.468-.675 2.86-1.483 4.163-2.408zm-16.984 1.152c-.962-.089-1.814.619-1.904 1.581-.089.963.619 1.815 1.582 1.904.791.074 1.593.111 2.403.111s1.612-.037 2.403-.111c.963-.089 1.6
 71-.941 1.582-1.904-.09-.962-.942-1.67-1.904-1.581-.685.064-1.379.096-2.081.096s-1.396-.032-2.081-.096z"/>
+            <path d="m528.75 225c0-39.35 31.9-71.25 71.25-71.25s71.25 31.9 71.25 71.25-31.9 71.25-71.25 71.25-71.25-31.9-71.25-71.25zm71.25-74.75c-41.283 0-74.75 33.467-74.75 74.75s33.467 74.75 74.75 74.75 74.75-33.467 74.75-74.75-33.467-74.75-74.75-74.75zm-55.25 74.75c0-30.514 24.736-55.25 55.25-55.25s55.25 24.736 55.25 55.25-24.736 55.25-55.25 55.25-55.25-24.736-55.25-55.25zm55.25-58.75c-32.447 0-58.75 26.303-58.75 58.75s26.303 58.75 58.75 58.75 58.75-26.303 58.75-58.75-26.303-58.75-58.75-58.75zm0 19.5c-21.677 0-39.25 17.573-39.25 39.25s17.573 39.25 39.25 39.25 39.25-17.573 39.25-39.25-17.573-39.25-39.25-39.25zm-42.75 39.25c0-23.61 19.14-42.75 42.75-42.75s42.75 19.14 42.75 42.75-19.14 42.75-42.75 42.75-42.75-19.14-42.75-42.75zm20.5 0c0-12.288 9.962-22.25 22.25-22.25s22.25 9.962 22.25 22.25-9.962 22.25-22.25 22.25-22.25-9.962-22.25-22.25zm22.25-25.75c-14.221 0-25.75 11.529-25.75 25.75s11.529 25.75 25.75 25.75 25.75-11.529 25.75-25.75-11.529-25.75-25.75-25.75z"/>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/browser/themes/shared/urlbar-searchbar.inc.css b/browser/themes/shared/urlbar-searchbar.inc.css
index 0158597991ec..d7dc7df17f19 100644
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -747,3 +747,5 @@ moz-input-box > menupopup .context-menu-add-engine > .menu-iconic-left::after {
 }
 
 %include ../../components/onionservices/content/onionlocation-urlbar.css
+%include ../../components/torconnect/content/torconnect-urlbar.css
+
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp
index b00399e2eccb..7e103e2705d1 100644
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -17143,9 +17143,56 @@ void Document::RemoveToplevelLoadingDocument(Document* aDoc) {
 
 StylePrefersColorScheme Document::PrefersColorScheme(
     IgnoreRFP aIgnoreRFP) const {
+
+  // tor-browser#27476
+  // should this document ignore resist finger-printing settings with regards to
+  // setting the color scheme
+  // currently only enabled for about:torconnect but we could expand to other non-
+  // SystemPrincipal pages if we wish
+  const auto documentUsesPreferredColorScheme = [](auto const* constDocument) -> bool {
+    if (auto* document = const_cast<Document*>(constDocument); document != nullptr) {
+      auto uri = document->GetDocBaseURI();
+
+      // try and extract out our prepath and filepath portions of the uri to C-strings
+      nsAutoCString prePathStr, filePathStr;
+      if(NS_FAILED(uri->GetPrePath(prePathStr)) ||
+         NS_FAILED(uri->GetFilePath(filePathStr))) {
+        return false;
+      }
+
+      // stick them in string view for easy comparisons
+      std::string_view prePath(prePathStr.get(), prePathStr.Length()),
+                       filePath(filePathStr.get(), filePathStr.Length());
+
+      // these about URIs will have the user's preferred color scheme exposed to them
+      // we can place other URIs here in the future if we wish
+      // see nsIURI.idl for URI part definitions
+      constexpr struct {
+        std::string_view prePath;
+        std::string_view filePath;
+      } allowedURIs[] = {
+        { "about:", "torconnect" },
+      };
+
+      // check each uri in the allow list against this document's uri
+      // verify the prepath and the file path match
+      for(auto const& uri : allowedURIs) {
+        if (prePath == uri.prePath &&
+            filePath == uri.filePath) {
+          // positive match means we can apply dark-mode to the page
+          return true;
+        }
+      }
+    }
+
+    // do not allow if no match or other error
+    return false;
+  };
+
   if (aIgnoreRFP == IgnoreRFP::No &&
-      nsContentUtils::ShouldResistFingerprinting(this)) {
-    return StylePrefersColorScheme::Light;
+      nsContentUtils::ShouldResistFingerprinting(this) &&
+      !documentUsesPreferredColorScheme(this)) {
+      return StylePrefersColorScheme::Light;
   }
 
   if (auto* bc = GetBrowsingContext()) {
diff --git a/dom/base/nsGlobalWindowOuter.cpp b/dom/base/nsGlobalWindowOuter.cpp
index 4da5365f214d..e981573e9822 100644
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -6213,6 +6213,8 @@ void nsGlobalWindowOuter::CloseOuter(bool aTrustedCaller) {
     NS_ENSURE_SUCCESS_VOID(rv);
 
     if (!StringBeginsWith(url, u"about:neterror"_ns) &&
+        // we want about:torconnect pages to be able to close themselves after bootstrap
+        !StringBeginsWith(url, u"about:torconnect"_ns) &&
         !mBrowsingContext->HadOriginalOpener() && !aTrustedCaller &&
         !IsOnlyTopLevelDocumentInSHistory()) {
       bool allowClose =
diff --git a/toolkit/components/processsingleton/MainProcessSingleton.jsm b/toolkit/components/processsingleton/MainProcessSingleton.jsm
index ecdbf2a01d99..62afa98e1ffc 100644
--- a/toolkit/components/processsingleton/MainProcessSingleton.jsm
+++ b/toolkit/components/processsingleton/MainProcessSingleton.jsm
@@ -24,6 +24,11 @@ MainProcessSingleton.prototype = {
           null
         );
 
+        ChromeUtils.import(
+          "resource:///modules/TorConnect.jsm",
+          null
+        );
+
         Services.ppmm.loadProcessScript(
           "chrome://global/content/process-content.js",
           true
diff --git a/toolkit/modules/AsyncPrefs.jsm b/toolkit/modules/AsyncPrefs.jsm
index 2834c484c919..f7d867e47dc0 100644
--- a/toolkit/modules/AsyncPrefs.jsm
+++ b/toolkit/modules/AsyncPrefs.jsm
@@ -20,6 +20,7 @@ const kAllowedPrefs = new Set([
 
   "browser.contentblocking.report.hide_vpn_banner",
   "browser.contentblocking.report.show_mobile_app",
+  "extensions.torlauncher.quickstart",
 
   "narrate.rate",
   "narrate.voice",
diff --git a/toolkit/modules/RemotePageAccessManager.jsm b/toolkit/modules/RemotePageAccessManager.jsm
index c12e71ac4d42..5125203866b8 100644
--- a/toolkit/modules/RemotePageAccessManager.jsm
+++ b/toolkit/modules/RemotePageAccessManager.jsm
@@ -103,6 +103,7 @@ let RemotePageAccessManager = {
       RPMGetInnerMostURI: ["*"],
       RPMGetHttpResponseHeader: ["*"],
       RPMGetTorStrings: ["*"],
+      RPMSendQuery: ["ShouldShowTorConnect"],
     },
     "about:plugins": {
       RPMSendQuery: ["RequestPlugins"],
@@ -219,6 +220,21 @@ let RemotePageAccessManager = {
         "FetchUpdateData",
       ],
     },
+    "about:torconnect": {
+      RPMAddMessageListener: [
+        "torconnect:state-change",
+      ],
+      RPMSendAsyncMessage: [
+        "torconnect:open-tor-preferences",
+        "torconnect:begin-bootstrap",
+        "torconnect:cancel-bootstrap",
+        "torconnect:set-quickstart",
+      ],
+      RPMSendQuery: [
+        "torconnect:get-init-args",
+        "torconnect:copy-tor-logs",
+      ],
+    },
   },
 
   /**
diff --git a/toolkit/mozapps/update/UpdateService.jsm b/toolkit/mozapps/update/UpdateService.jsm
index f4f925992027..f0a48d021638 100644
--- a/toolkit/mozapps/update/UpdateService.jsm
+++ b/toolkit/mozapps/update/UpdateService.jsm
@@ -12,6 +12,17 @@ const { AppConstants } = ChromeUtils.import(
 const { AUSTLMY } = ChromeUtils.import(
   "resource://gre/modules/UpdateTelemetry.jsm"
 );
+
+const { TorProtocolService } = ChromeUtils.import(
+  "resource:///modules/TorProtocolService.jsm"
+);
+
+function _shouldRegisterBootstrapObserver(errorCode) {
+  return errorCode == PROXY_SERVER_CONNECTION_REFUSED &&
+         !TorProtocolService.isBootstrapDone() &&
+         TorProtocolService.ownsTorDaemon;
+};
+
 const {
   Bits,
   BitsRequest,
@@ -232,6 +243,7 @@ const SERVICE_ERRORS = [
 // Custom update error codes
 const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
 const NETWORK_ERROR_OFFLINE = 111;
+const PROXY_SERVER_CONNECTION_REFUSED = 2152398920;
 
 // Error codes should be < 1000. Errors above 1000 represent http status codes
 const HTTP_ERROR_OFFSET = 1000;
@@ -2676,6 +2688,9 @@ UpdateService.prototype = {
       case "network:offline-status-changed":
         this._offlineStatusChanged(data);
         break;
+      case "torconnect:bootstrap-complete":
+        this._bootstrapComplete();
+        break;
       case "nsPref:changed":
         if (data == PREF_APP_UPDATE_LOG || data == PREF_APP_UPDATE_LOG_FILE) {
           gLogEnabled; // Assigning this before it is lazy-loaded is an error.
@@ -3169,6 +3184,35 @@ UpdateService.prototype = {
     this._attemptResume();
   },
 
+  _registerBootstrapObserver: function AUS__registerBootstrapObserver() {
+    if (this._registeredBootstrapObserver) {
+      LOG(
+        "UpdateService:_registerBootstrapObserver - observer already registered"
+      );
+      return;
+    }
+
+    LOG(
+      "UpdateService:_registerBootstrapObserver - waiting for tor bootstrap to " +
+        "be complete, then forcing another check"
+    );
+
+    Services.obs.addObserver(this, "torconnect:bootstrap-complete");
+    this._registeredBootstrapObserver = true;
+  },
+
+  _bootstrapComplete: function AUS__bootstrapComplete() {
+    Services.obs.removeObserver(this, "torconnect:bootstrap-complete");
+    this._registeredBootstrapObserver = false;
+
+    LOG(
+      "UpdateService:_bootstrapComplete - bootstrapping complete, forcing " +
+        "another background check"
+    );
+
+    this._attemptResume();
+  },
+
   onCheckComplete: function AUS_onCheckComplete(request, updates) {
     this._selectAndInstallUpdate(updates);
   },
@@ -3188,6 +3232,11 @@ UpdateService.prototype = {
         AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_OFFLINE);
       }
       return;
+    } else if (_shouldRegisterBootstrapObserver(update.errorCode)) {
+      // Register boostrap observer to try again, but only when we own the
+      // tor process.
+      this._registerBootstrapObserver();
+      return;
     }
 
     // Send the error code to telemetry
@@ -6011,6 +6060,7 @@ Downloader.prototype = {
     var state = this._patch.state;
     var shouldShowPrompt = false;
     var shouldRegisterOnlineObserver = false;
+    var shouldRegisterBootstrapObserver = false;
     var shouldRetrySoon = false;
     var deleteActiveUpdate = false;
     let migratedToReadyUpdate = false;
@@ -6129,7 +6179,18 @@ Downloader.prototype = {
       );
       shouldRegisterOnlineObserver = true;
       deleteActiveUpdate = false;
-
+    } else if(_shouldRegisterBootstrapObserver(status)) {
+      // Register a bootstrap observer to try again.
+      // The bootstrap observer will continue the incremental download by
+      // calling downloadUpdate on the active update which continues
+      // downloading the file from where it was.
+      LOG("Downloader:onStopRequest - not bootstrapped, register bootstrap observer: true");
+      AUSTLMY.pingDownloadCode(
+        this.isCompleteUpdate,
+        AUSTLMY.DWNLD_RETRY_OFFLINE
+      );
+      shouldRegisterBootstrapObserver = true;
+      deleteActiveUpdate = false;
       // Each of NS_ERROR_NET_TIMEOUT, ERROR_CONNECTION_REFUSED,
       // NS_ERROR_NET_RESET and NS_ERROR_DOCUMENT_NOT_CACHED can be returned
       // when disconnecting the internet while a download of a MAR is in
@@ -6251,7 +6312,7 @@ Downloader.prototype = {
 
     // Only notify listeners about the stopped state if we
     // aren't handling an internal retry.
-    if (!shouldRetrySoon && !shouldRegisterOnlineObserver) {
+    if (!shouldRetrySoon && !shouldRegisterOnlineObserver && !shouldRegisterBootstrapObserver) {
       this.updateService.forEachDownloadListener(listener => {
         listener.onStopRequest(request, status);
       });
@@ -6437,6 +6498,9 @@ Downloader.prototype = {
     if (shouldRegisterOnlineObserver) {
       LOG("Downloader:onStopRequest - Registering online observer");
       this.updateService._registerOnlineObserver();
+    } else if (shouldRegisterBootstrapObserver) {
+      LOG("Downloader:onStopRequest - Registering bootstrap observer");
+      this.updateService._registerBootstrapObserver();
     } else if (shouldRetrySoon) {
       LOG("Downloader:onStopRequest - Retrying soon");
       this.updateService._consecutiveSocketErrors++;
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/browser-window.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/browser-window.js
index 2ff107b553b2..f8fa83574df7 100644
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/browser-window.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/browser-window.js
@@ -70,6 +70,10 @@ function getGlobalScriptIncludes(scriptPath) {
     let match = line.match(globalScriptsRegExp);
     if (match) {
       let sourceFile = match[1]
+        .replace(
+          "chrome://browser/content/torconnect/",
+          "browser/components/torconnect/content/"
+        )
         .replace(
           "chrome://browser/content/search/",
           "browser/components/search/content/"





More information about the tbb-commits mailing list