diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..295898e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +/dist +/docs +/node_modules +/.env +/.eslintrc +/.git +/.gitignore +/demo.html +/devServer.js +/Dockerfile +/Procfile +/README.md + +# jetbrains folder: +/.idea + +# npm log: +/npm-debug.log* \ No newline at end of file diff --git a/.gitignore b/.gitignore index b637658..9bec688 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,6 @@ # dependencies: node_modules/ -# compiled files: -dist/ - # environment variables: .env @@ -12,3 +9,9 @@ dist/ # npm log: npm-debug.log* + +# vscode folder +.vscode/ + +test.html + diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..913dcda --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "cSpell.words": [ + "CERTBOT", + "idoco", + "Loadmill", + "preact", + "Sergey", + "unactive" + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fc57bb8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM node:alpine + +ENV HOME=/opt/intergram +RUN mkdir -p $HOME +WORKDIR $HOME + +COPY . . + +RUN npm ci +RUN npm run build +CMD npm run start +EXPOSE 3000 \ No newline at end of file diff --git a/README.md b/README.md index 0347994..bccd92f 100644 --- a/README.md +++ b/README.md @@ -1,83 +1,187 @@ -# Intergram - [Demo](https://www.intergram.xyz/) +# Telegram Chat Widget - [Demo](https://kintoyyy.github.io/Telegram-Chat-Widget/) -A **Free** live chat widget that you can easily add to your website. It will let you chat with your website visitors using your Telegram messenger. +The **Telegram Chat Widget** is a free and customizable tool forked from idoco/intergram that lets website owners add a chat feature to their sites. Visitors can use it to talk to the website owners through Telegram. It's an easy way for websites to have real-time conversations with their users. -#### :tada: Main Contributors :tada: -- [aslauris](https://github.com/aslauris) - Who redesgined the new UI! Check out his website - [wedofe.com](https://www.wedofe.com/) -![](https://user-images.githubusercontent.com/5776439/40442974-c107cb4a-5e79-11e8-8af1-4d2c8be14f48.gif) +### Adding Telegram Chat Widget in your website with these 2 simple steps -##### How? -You initiate a chat with my Telegram bot and add 2 lines of script to your website to embed the widget. Visitors' messages are sent from the chat widget to my bot server, which sends them to your Telegram messenger where you can answer them. You can also self-host the bot server to get more control of this process. +1. Open [Telegram](https://web.telegram.org/), search for **`@MikrotikHsSupportBot`** and hit `/start` to get your unique chat ID. -##### Why use a Telegram bot to implement a chat widget? -By using a Telegram bot, I delegate all the message routing work and chat state management to Telegram. I don't have to build fancy mobile and desktop apps for several platforms because Telegram already has a great multiplatform chat client. And, I can easily handle a huge amount of load, because my app runs completely stateless (No database) and just serves as a pipe between Telegram and the chat widget users. +

-### Embed Intergram in your website with these 2 simple steps +1. Paste this code snippet right before the closing body tag of every page where you want the chat to appear +(Don't forget to add your actual chat ID). -1. Open [Telegram messenger](https://web.telegram.org/), search for `@Intergram` and hit `/start` to get your unique chat ID. ([direct link](https://web.telegram.org/#/im?p=@IntergramBot)) +```html + + +``` -

+*Note: `replying to a specific message should be used to respond to that specific visitor`. Feel free to send a standard message they won't be send to any dialog. You can talk with your colleague if the bot attached to a telegram group. Use `/all [any_text]` command to broadcast to all connected chat clients -2. Paste this code snippet right before the closing body tag of every page where you want the chat to appear -(Don't forget to add your actual chat ID). + + +### Bot commands +- `/start` - Link between the embedded chat and this telegram chat +- `/all [any_text]` - Send message to all online users +- `/who` - Get users list who are online now +- `/online` - Set online status for the chat. Widget is shown for users +- `/offline` - Set offline status for the chat. Widget is hidden for users +- `/ban [name]` - Ban user +- `/unban [name]` - Unban user +- `/user [name]` - To view the users data +- `/info` - more information about the bot +- `/help` - instructions on how to setup + +## Passing custom values and access it using `/user [name]` + +```js +let number = 300; +let timeLeft = '3hrs'; + +window.CustomData = { + 'username': 'test', + 'location': 'PH', + 'Time left': timeLeft, + 'Number': number +}; + +window.CustomData.text = 'Hello World!'; +window.CustomData.pet = '🐈'; + +window.intergramId = "Your unique chat id"; +//rest of the code ...... +``` + +

+ + +## Use Case in mikrotik hotspot portal +
+ View mikrotik code sample + +# Mikrotik installation + +### 1.) Adding the bot to Walled Ip's + +Goto **ip** > **hotspot** > **Walled Garden Ip List** + +add a new entry to **accept** Dst. Host as `https://telegram-chat-widget-t3ez.onrender.com` +### 2.) Add your chat Id in window.intergramId + +`window.intergramId = "Your unique chat id";` + + + +### 2.) Add the scripts ```html - - + + ``` + for more mikrotik variables please refer to the [Mikrotik hs portal documentation's](https://help.mikrotik.com/docs/display/ROS/Hotspot+customisation) + +### 3.) Done! + +## Examples in mikrotik +*login.html* +

+ +*admins telegram* +

-*Note: replying to a specific message should be used to respond to that specific visitor. Sending a standard message will broadcast to all connected chat clients (You might find it easier to use if you have only one active conversation)* +> the admin can see the users details +
-**Important Notice:** I plan to keep the hosted chat service 100% free (no ads also!), but there are some hosting expenses (servers, domain, cdn, etc), so I will soon start bundeling a [Loadmill](https://www.loadmill.com) component with the Intergram script. Loadmill is a new concept and still in Beta - **If you don't feel comfortable with this, please use your own self hosted version.** +--- -### Customization - [Try It](https://jsfiddle.net/z9ffzr9n/6/) -Currently you can customize all visible texts and the main widget color by setting an optional `intergramCustomizations` object in the injection script tag. (All its properties are also optional and will fallback to their original values) +## Using an external button +```html + +
+ +
+ + + +``` + + +### Customization +Currently you can customize all visible texts and the main widget color by setting an optional `intergramCustomizations` object in the injection script tag. (**All its properties are also optional and will fallback to their original values**) ```html - + ``` -

- -### Initial Footprint - - Using [Preact](https://github.com/developit/preact) helped creating a pretty minimal `js` bundle. - - The widget injection script is about 5KB gziped and executes only after the host page finished loading ('onload' event). - - The chat iframe will only be loaded if the user interacts with the chat widget (currently about 40KB gziped). - -![](docs/footprint.png) - -### Deploy your own Intergram instance (Self Hosting) -1. Talk to Telegram [@BotFather](https://telegram.me/botfather), create a new bot and get its API Token. - -2. Deploy this repo to your own chat server. - - Clone it locally and install or if you use Heroku, fork this repository and point the new app to it. - - Set an .env variable named `TELEGRAM_TOKEN` with the value you got from @BotFather - -3. Point the bot webhook to your bot server by making a `GET` request to the following url - `https://api.telegram.org/bot/setWebhook?url=/hook` - (Don't forget to replace with your token and server url) - -4. Open a chat with your bot and hit `/start` to get your unique chat ID - -5. Embed this code snippet in your website - ```html - - - ``` -6. :tada: +

+ + +> # Donations would be appreciated +> **Gcash/Paymaya**: Kent R. `09760009422`
+> **Paypal** [paypalme/Kintoyyyy](https://www.paypal.com/paypalme/Kintoyyyy) + +--- + +## Deployment an additional information available at [idoco/intergram](https://github.com/idoco/intergram) and [yamaha252/intergram](https://github.com/yamaha252/intergram) diff --git a/chat.html b/chat.html deleted file mode 100644 index 6790cc2..0000000 --- a/chat.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - Intergram! - - - - -
- - - - - diff --git a/css/chat.css b/css/chat.css deleted file mode 100644 index e80d50e..0000000 --- a/css/chat.css +++ /dev/null @@ -1,158 +0,0 @@ - -/* An adaptation of this https://codepen.io/Varo/pen/gbZzgr */ - -html, body { - background: #e5e5e5; - font-family: 'Lato', sans-serif; - margin: 0 auto; -} - -/* M E S S A G E S */ - -.chat { - list-style: none; - background: none; - padding: 0 0 50px 0; - margin: 0 0 10px; -} - -.chat li { - padding: 8px; - padding: 0.5rem; - font-size: 14.4px; - font-size: 0.9rem; - overflow: hidden; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; -} - -.visitor { - -webkit-box-pack: end; - -webkit-justify-content: flex-end; - -ms-flex-pack: end; - justify-content: flex-end; - -webkit-box-align: end; - -webkit-align-items: flex-end; - -ms-flex-align: end; - -ms-grid-row-align: flex-end; - align-items: flex-end; -} - -.visitor .msg { - -webkit-box-ordinal-group: 2; - -webkit-order: 1; - -ms-flex-order: 1; - order: 1; - border-bottom-right-radius: 0; - box-shadow: 1px 2px 0 #D4D4D4; -} - -.admin .msg { - -webkit-box-ordinal-group: 2; - -webkit-order: 1; - -ms-flex-order: 1; - order: 1; - border-top-left-radius: 0; - box-shadow: -1px 2px 0 #D4D4D4; -} - -.msg { - word-wrap: break-word; - background: white; - min-width: 50px; - max-width: 80%; - padding: 10px; - border-radius: 2px; - box-shadow: 0 2px 0 rgba(0, 0, 0, 0.07); -} - -.msg p { - margin: 0 0 3.2px 0; - margin: 0 0 0.2rem 0; -} - -.msg .time { - font-size: 11.2px; - font-size: 0.7rem; - color: #ccc; - margin-top: 3px; - float: right; - cursor: default; - -webkit-touch-callout: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; -} - -::-webkit-scrollbar { - min-width: 12px; - width: 12px; - max-width: 12px; - min-height: 12px; - height: 12px; - max-height: 12px; - background: #e5e5e5; - box-shadow: inset 0 -14px 0 #e5e5e5, inset 0 -46px 0 #fafafa; -} - -::-webkit-scrollbar-thumb { - background: #bbb; - border-radius: 100px; - border: solid 3px #e5e5e5; - box-shadow: inset 0 0 3px #999; -} - -::-webkit-scrollbar-thumb:hover { - background: #b0b0b0; - box-shadow: inset 0 0 3px #888; -} - -::-webkit-scrollbar-thumb:active { - background: #aaa; - box-shadow: inset 0 0 3px #7f7f7f; -} - -::-webkit-scrollbar-button { - display: block; - height: 24px; -} - -/* I N P U T */ - -input.textarea { - position: fixed; - bottom: 14px; - left: 0; - right: 0; - width: 95%; - height: 30px; - z-index: 99; - background: #fafafa; - border: none; - outline: none; - padding-left: 15px; - padding-right: 15px; - color: #666; - font-weight: 400; -} - - -a.banner { - position: fixed; - bottom: 0; - left: 0; - right: 0; - width: 100%; - height: 12px; - z-index: 99; - border-top: 2px solid #e5e5e5; - background: #e5e5e5; - outline: none; - color: #777; - font-size: 10px; - text-align: right; - font-weight: 200; - text-decoration: none -} diff --git a/demo.html b/demo.html deleted file mode 100644 index 0bd6d7e..0000000 --- a/demo.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - Intergram Demo Page - - - - - - - - - - -
-

Intergram Demo Page

-

Free live chat widget linked to your Telegram messenger.

- -

More information...

-
- - - - - - diff --git a/docs/BotProfile.png b/docs/BotProfile.png new file mode 100644 index 0000000..043a3ee Binary files /dev/null and b/docs/BotProfile.png differ diff --git a/docs/CustomDataExample.png b/docs/CustomDataExample.png new file mode 100644 index 0000000..90eede0 Binary files /dev/null and b/docs/CustomDataExample.png differ diff --git a/docs/HsDataExample.png b/docs/HsDataExample.png new file mode 100644 index 0000000..b94bb89 Binary files /dev/null and b/docs/HsDataExample.png differ diff --git a/docs/HsDataExample2.png b/docs/HsDataExample2.png new file mode 100644 index 0000000..ce7ac8a Binary files /dev/null and b/docs/HsDataExample2.png differ diff --git a/docs/cat-call-center.gif b/docs/cat-call-center.gif new file mode 100644 index 0000000..0181c83 Binary files /dev/null and b/docs/cat-call-center.gif differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..92f5457 --- /dev/null +++ b/index.html @@ -0,0 +1,259 @@ + + + + + + + + Telegram Chat Widget + + + + + + + +
+ + +
+
+
+
+ +
+
+
+
+

intergram / Telegram Chat Widget

+

+ The Telegram Chat Widget is a free and customizable tool made by idoco/intergram that + lets website owners add a chat feature to their sites. Visitors can use it to talk to + the website owners through Telegram. It's an easy way for websites to have real-time + conversations with their users. +

+
+ Github repository + + Telegram Bot + + + +
+
+
+
+ illustration +
+ Hero +
+
+
+
+ +
+
+
+
+
+
+
+  Feature 01 illustration +
+  Feature 01 +
+
+

Features

+

+

    +
  • Works on any MikroTik Hotspot Portals
  • +
  • Easy access for customer support
  • +
  • Real-time chats
  • +
  • Instant support and troubleshooting +
  • Commands!
  • +
+

+
+
+ +
+
+
+
+
+ +
+ + + +
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/media/demo_avatar.jpg b/media/demo_avatar.jpg deleted file mode 100644 index be81534..0000000 Binary files a/media/demo_avatar.jpg and /dev/null differ diff --git a/package.json b/package.json index 8717c10..43a60ac 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,21 @@ { - "name": "intergram", - "version": "0.0.1", + "name": "telegram_chat_widget", + "version": "0.1.2", "description": "A live chat widget linked to your telegram messenger", - "engines": { - "node": "6.9.1", - "npm": "3.10.8" - }, "scripts": { "start": "node server.js", - "postinstall": "npm run build", - "dev": "start http://localhost:3000/demo.html & node devServer.js", - "build": "shx mkdir -p dist && shx cp *.html dist && shx cp -r ./media dist && shx cp -r ./css dist && webpack -p", + "dev": "start http://localhost:3000 & node devServer.js", + "build": "webpack -p", "test": "npm run -s lint", "lint": "eslint src" }, "authors": [ "Ido Cohen (http://github.com/idoco)" ], + "contributors": [ + "Sergey https://github.com/yamaha252)", + "Kintoyyy https://github.com/kintoyyy" + ], "devDependencies": { "webpack-bundle-analyzer": "^2.2.1", "webpack-dev-server": "^1.16.2" @@ -33,13 +32,15 @@ "cors": "^2.8.1", "dateformat": "^2.0.0", "eslint": "^3.2.2", - "express": "4.10.2", + "express": "^4.16.4", + "human-readable-ids": "^1.0.4", "preact": "^7.1.0", "request": "^2.79.0", "shx": "^0.2.2", - "socket.io": "^1.7.3", - "socket.io-client": "^1.7.3", + "socket.io": "^2.2.0", + "socket.io-client": "^2.2.0", "store": "^1.3.20", - "webpack": "^1.14.0" + "webpack": "^1.14.0", + "whatwg-fetch": "^3.0.0" } } diff --git a/page/css/style.css b/page/css/style.css new file mode 100644 index 0000000..0b40184 --- /dev/null +++ b/page/css/style.css @@ -0,0 +1,3 @@ +html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:0.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace, monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace, monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}template{display:none}[hidden]{display:none}html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}body{background:#fff;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}hr{border:0;display:block;height:1px;background:#E4E8EE;margin-top:24px;margin-bottom:24px}ul,ol{margin-top:0;margin-bottom:24px;padding-left:24px}ul{list-style:disc}ol{list-style:decimal}li>ul,li>ol{margin-bottom:0}dl{margin-top:0;margin-bottom:24px}dt{font-weight:600}dd{margin-left:24px;margin-bottom:24px}img{height:auto;max-width:100%;vertical-align:middle}figure{margin:24px 0}figcaption{font-size:16px;line-height:24px;padding:8px 0}img,svg{display:block}table{border-collapse:collapse;margin-bottom:24px;width:100%}tr{border-bottom:1px solid #E4E8EE}th{text-align:left}th,td{padding:10px 16px}th:first-child,td:first-child{padding-left:0}th:last-child,td:last-child{padding-right:0}html{font-size:20px;line-height:30px}body{color:#6B7A90;font-size:1rem}body,button,input,select,textarea{font-family:"Heebo", sans-serif}a{color:#4234F8;text-decoration:none}a:hover,a:active{outline:0;text-decoration:underline}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{clear:both;color:#161E2A;font-family:"Oxygen", sans-serif;font-weight:600}h1,.h1{font-size:38px;line-height:48px;letter-spacing:0px}@media (min-width: 641px){h1,.h1{font-size:44px;line-height:54px;letter-spacing:0px}}h2,.h2{font-size:32px;line-height:42px;letter-spacing:0px}@media (min-width: 641px){h2,.h2{font-size:38px;line-height:48px;letter-spacing:0px}}h3,.h3,blockquote{font-size:28px;line-height:34px;letter-spacing:0px}@media (min-width: 641px){h3,.h3,blockquote{font-size:32px;line-height:42px;letter-spacing:0px}}h4,.h4{font-size:28px;line-height:34px;letter-spacing:0px}h5,.h5,h6,.h6{font-size:20px;line-height:30px;letter-spacing:-0.1px}@media (max-width: 640px){.h1-mobile{font-size:38px;line-height:48px;letter-spacing:0px}.h2-mobile{font-size:32px;line-height:42px;letter-spacing:0px}.h3-mobile{font-size:28px;line-height:34px;letter-spacing:0px}.h4-mobile{font-size:28px;line-height:34px;letter-spacing:0px}.h5-mobile,.h6-mobile{font-size:20px;line-height:30px;letter-spacing:-0.1px}}.text-light{color:#606483}.text-light a{color:#606483}.text-light h1,.text-light h2,.text-light h3,.text-light h4,.text-light h5,.text-light h6,.text-light .h1,.text-light .h2,.text-light .h3,.text-light .h4,.text-light .h5,.text-light .h6{color:#fff !important}.text-sm{font-size:18px;line-height:27px;letter-spacing:-0.1px}.text-xs{font-size:16px;line-height:24px;letter-spacing:-0.1px}h1,h2,.h1,.h2{margin-top:48px;margin-bottom:16px}h3,.h3{margin-top:36px;margin-bottom:12px}h4,h5,h6,.h4,.h5,.h6{margin-top:24px;margin-bottom:4px}p{margin-top:0;margin-bottom:24px}dfn,cite,em,i{font-style:italic}blockquote{color:#6B7A90;font-style:italic;margin-top:24px;margin-bottom:24px;margin-left:24px}blockquote::before{content:"\201C"}blockquote::after{content:"\201D"}blockquote p{display:inline}address{color:#6B7A90;border-width:1px 0;border-style:solid;border-color:#E4E8EE;padding:24px 0;margin:0 0 24px}pre,pre h1,pre h2,pre h3,pre h4,pre h5,pre h6,pre .h1,pre .h2,pre .h3,pre .h4,pre .h5,pre .h6{font-family:"Courier 10 Pitch", Courier, monospace}pre,code,kbd,tt,var{background:#F3F4FF}pre{font-size:16px;line-height:24px;margin-bottom:1.6em;max-width:100%;overflow:auto;padding:24px;margin-top:24px;margin-bottom:24px}code,kbd,tt,var{font-family:Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace;font-size:16px;padding:2px 4px}abbr,acronym{cursor:help}mark,ins{text-decoration:none}small{font-size:18px;line-height:27px;letter-spacing:-0.1px}b,strong{font-weight:600}button,input,select,textarea,label{font-size:20px;line-height:30px}.container,.container-sm{width:100%;margin:0 auto;padding-left:16px;padding-right:16px}@media (min-width: 481px){.container,.container-sm{padding-left:24px;padding-right:24px}}.container{max-width:1128px}.container-sm{max-width:848px}.container .container-sm{max-width:800px;padding-left:0;padding-right:0}.screen-reader-text{clip:rect(1px, 1px, 1px, 1px);position:absolute !important;height:1px;width:1px;overflow:hidden;word-wrap:normal !important}.screen-reader-text:focus{border-radius:2px;box-shadow:0 0 2px 2px rgba(0,0,0,0.6);clip:auto !important;display:block;font-size:14px;letter-spacing:0px;font-weight:600;line-height:16px;text-decoration:none;text-transform:uppercase;background-color:#fff;color:#4234F8 !important;border:none;height:auto;left:8px;padding:16px 40px;top:8px;width:auto;z-index:100000}.list-reset{list-style:none;padding:0}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-primary{color:#4234F8}.text-secondary{color:#FF6C50}.has-top-divider{position:relative}.has-top-divider::before{content:'';position:absolute;top:0;left:0;width:100%;display:block;height:1px;background:#E4E8EE}.has-bottom-divider{position:relative}.has-bottom-divider::after{content:'';position:absolute;bottom:0;left:0;width:100%;display:block;height:1px;background:#E4E8EE}.m-0{margin:0}.mt-0{margin-top:0}.mr-0{margin-right:0}.mb-0{margin-bottom:0}.ml-0{margin-left:0}.m-8{margin:8px}.mt-8{margin-top:8px}.mr-8{margin-right:8px}.mb-8{margin-bottom:8px}.ml-8{margin-left:8px}.m-16{margin:16px}.mt-16{margin-top:16px}.mr-16{margin-right:16px}.mb-16{margin-bottom:16px}.ml-16{margin-left:16px}.m-24{margin:24px}.mt-24{margin-top:24px}.mr-24{margin-right:24px}.mb-24{margin-bottom:24px}.ml-24{margin-left:24px}.m-32{margin:32px}.mt-32{margin-top:32px}.mr-32{margin-right:32px}.mb-32{margin-bottom:32px}.ml-32{margin-left:32px}.m-40{margin:40px}.mt-40{margin-top:40px}.mr-40{margin-right:40px}.mb-40{margin-bottom:40px}.ml-40{margin-left:40px}.m-48{margin:48px}.mt-48{margin-top:48px}.mr-48{margin-right:48px}.mb-48{margin-bottom:48px}.ml-48{margin-left:48px}.m-56{margin:56px}.mt-56{margin-top:56px}.mr-56{margin-right:56px}.mb-56{margin-bottom:56px}.ml-56{margin-left:56px}.m-64{margin:64px}.mt-64{margin-top:64px}.mr-64{margin-right:64px}.mb-64{margin-bottom:64px}.ml-64{margin-left:64px}.p-0{padding:0}.pt-0{padding-top:0}.pr-0{padding-right:0}.pb-0{padding-bottom:0}.pl-0{padding-left:0}.p-8{padding:8px}.pt-8{padding-top:8px}.pr-8{padding-right:8px}.pb-8{padding-bottom:8px}.pl-8{padding-left:8px}.p-16{padding:16px}.pt-16{padding-top:16px}.pr-16{padding-right:16px}.pb-16{padding-bottom:16px}.pl-16{padding-left:16px}.p-24{padding:24px}.pt-24{padding-top:24px}.pr-24{padding-right:24px}.pb-24{padding-bottom:24px}.pl-24{padding-left:24px}.p-32{padding:32px}.pt-32{padding-top:32px}.pr-32{padding-right:32px}.pb-32{padding-bottom:32px}.pl-32{padding-left:32px}.p-40{padding:40px}.pt-40{padding-top:40px}.pr-40{padding-right:40px}.pb-40{padding-bottom:40px}.pl-40{padding-left:40px}.p-48{padding:48px}.pt-48{padding-top:48px}.pr-48{padding-right:48px}.pb-48{padding-bottom:48px}.pl-48{padding-left:48px}.p-56{padding:56px}.pt-56{padding-top:56px}.pr-56{padding-right:56px}.pb-56{padding-bottom:56px}.pl-56{padding-left:56px}.p-64{padding:64px}.pt-64{padding-top:64px}.pr-64{padding-right:64px}.pb-64{padding-bottom:64px}.pl-64{padding-left:64px}.sr .has-animations .is-revealing{visibility:hidden}.button{display:inline-flex;font-size:14px;letter-spacing:0px;font-weight:700;line-height:16px;text-decoration:none !important;text-transform:uppercase;background-color:#fff;background:#fff;color:#4234F8 !important;border:none;border-radius:2px;cursor:pointer;justify-content:center;padding:16px 40px;height:48px;text-align:center;white-space:nowrap}.button:active{outline:0}.button::before{border-radius:2px}.button-shadow{position:relative}.button-shadow::before{content:'';position:absolute;top:0;right:0;bottom:0;left:0;box-shadow:0 8px 16px rgba(22,30,42,0.12);mix-blend-mode:multiply;transition:box-shadow .15s ease}.button-shadow:hover::before{box-shadow:0 8px 16px rgba(22,30,42,0.16)}.button-sm{padding:8px 24px;height:32px}.button-sm.button-shadow::before{box-shadow:0 4px 16px rgba(22,30,42,0.12)}.button-sm.button-shadow:hover::before{box-shadow:0 4px 16px rgba(22,30,42,0.16)}.button-primary,.button-secondary{color:#fff !important;transition:background .15s ease}.button-primary{background:#4234F8}.button-primary:hover{background:#4b3ef8}.button-primary.button-shadow::before{box-shadow:0 8px 16px rgba(66,52,248,0.24)}.button-primary.button-shadow:hover::before{box-shadow:0 8px 16px rgba(66,52,248,0.32)}.button-primary .button-sm.button-shadow::before{box-shadow:0 4px 16px rgba(66,52,248,0.24)}.button-primary .button-sm.button-shadow:hover::before{box-shadow:0 4px 16px rgba(66,52,248,0.32)}.button-secondary{background:#FF6C50}.button-secondary:hover{background:#ff795f}.button-secondary.button-shadow::before{box-shadow:0 8px 16px rgba(255,108,80,0.24)}.button-secondary.button-shadow:hover::before{box-shadow:0 8px 16px rgba(255,108,80,0.32)}.button-secondary .button-sm.button-shadow::before{box-shadow:0 4px 16px rgba(255,108,80,0.24)}.button-secondary .button-sm.button-shadow:hover::before{box-shadow:0 4px 16px rgba(255,108,80,0.32)}.button-block{display:flex;width:100%}@media (max-width: 640px){.button-wide-mobile{width:100%;max-width:280px}}.site-header{padding:24px 0;position:absolute;top:0;left:0;width:100%;z-index:1}.site-header-inner{position:relative;display:flex;justify-content:space-between;align-items:center}.header-links{display:inline-flex}.header-links li{display:inline-flex}.header-links a:not(.button){font-size:16px;line-height:24px;letter-spacing:-0.1px;font-weight:600;color:#606483;text-transform:uppercase;text-decoration:none;line-height:16px;padding:8px 24px}.header-links a:not(.button):hover,.header-links a:not(.button):active{color:#fff}.hero{position:relative;padding-top:128px;padding-bottom:88px;z-index:0}.hero .hero-bg{position:absolute;top:0;bottom:42%;left:0;right:0;background:#0B0D19;z-index:-2}.hero .hero-particles-container{position:absolute;top:0;bottom:42%;left:0;right:0}.hero::before,.hero::after{content:'';position:absolute;left:calc(50% - 360px);width:720px;background-repeat:no-repeat}.hero::before{top:0;height:159px;background-image:url("../media/hero-bg-top.svg");background-size:720px 159px}.hero::after{bottom:42%;height:173px;background-image:url("../media/hero-bg-bottom.svg");background-size:720px 173px}.hero-inner{position:relative;z-index:1}.hero-copy{position:relative;margin-bottom:48px}@media (min-width: 641px){.hero{padding-top:160px;padding-bottom:128px}.hero::before,.hero::after{left:calc(50% - 720px);width:1440px}.hero::before{height:318px;background-size:1440px 318px}.hero::after{height:347px;background-size:1440px 347px}.hero-copy{margin-bottom:88px}.hero-paragraph{padding-left:72px;padding-right:72px}}.has-animations .hero .hero-bg,.has-animations .hero::before,.has-animations .hero::after,.has-animations .hero-particles-container,.has-animations .site-header,.has-animations .hero-title,.has-animations .hero-paragraph,.has-animations .hero-cta,.has-animations .hero .mockup-bg,.has-animations .hero .device-mockup{opacity:0}.has-animations.is-loaded .hero .hero-bg{animation:heroBg 0.6s cubic-bezier(0.215, 0.61, 0.355, 1) forwards}.has-animations.is-loaded .hero::before,.has-animations.is-loaded .hero::after{animation:heroFadeIn .6s ease forwards .45s}.has-animations.is-loaded .site-header,.has-animations.is-loaded .hero-particles-container,.has-animations.is-loaded .hero .mockup-bg{animation:heroFadeIn .6s ease forwards .45s}.has-animations.is-loaded .hero-title{animation:heroContent 0.6s cubic-bezier(0.215, 0.61, 0.355, 1) forwards 0.15s}.has-animations.is-loaded .hero-paragraph{animation:heroContent 0.6s cubic-bezier(0.215, 0.61, 0.355, 1) forwards 0.3s}.has-animations.is-loaded .hero-cta{animation:heroContent 0.6s cubic-bezier(0.215, 0.61, 0.355, 1) forwards 0.45s}.has-animations.is-loaded .hero .device-mockup{animation:heroMockup .6s ease forwards .6s}@keyframes heroBg{from{transform:scaleY(0) scaleX(1.2) skewY(30deg);opacity:1}to{transform:scaleY(1) scaleX(1) skewY(0);opacity:1}}@keyframes heroContent{from{transform:translateY(40px) skewY(2deg);opacity:0}to{transform:translateY(0) skewY(0);opacity:1}}@keyframes heroMockup{from{transform:translateY(80px);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes heroFadeIn{from{opacity:0}to{opacity:1}}.mockup-container{position:relative}.mockup-bg{pointer-events:none}.mockup-bg img,.mockup-bg svg{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:auto !important;height:auto;max-width:300% !important}.device-mockup{position:relative;width:350px;height:auto;margin:0 auto;z-index:1}.has-animations .features-extended{opacity:0}.has-animations.is-loaded .features-extended{opacity:1}.features-extended-header{margin-bottom:32px}.features-extended-wrap{display:flex;flex-wrap:wrap;margin-top:-24px}.features-extended-wrap:last-of-type{margin-bottom:-24px}.features-extended-wrap:not(:last-of-type){margin-bottom:24px}.feature-extended{padding:24px 0}.feature-extended-image{position:relative;margin-bottom:32px}.feature-extended-image img,.feature-extended-image svg{width:100%;max-width:296px;height:auto;margin-left:auto;margin-right:auto;overflow:visible}.feature-extended-body{text-align:center}@media (min-width: 641px){.features-extended .container{max-width:912px}.features-extended .section-inner{padding-bottom:128px}.features-extended .section-paragraph{padding-left:72px;padding-right:72px;margin-bottom:0}.features-extended-header{margin-bottom:80px}.features-extended-wrap{margin-top:-64px}.features-extended-wrap:last-of-type{margin-bottom:-64px}.features-extended-wrap:not(:last-of-type){margin-bottom:64px}.feature-extended{display:flex;flex-wrap:nowrap;align-items:stretch;justify-content:flex-end;padding:64px 0}.feature-extended .feature-extended-image{width:440px;margin-right:96px;margin-bottom:0}.feature-extended .feature-extended-image img,.feature-extended .feature-extended-image svg{width:auto}.feature-extended .feature-extended-image img.device-mockup,.feature-extended .feature-extended-image svg.device-mockup{max-width:296px}.feature-extended:nth-child(even){justify-content:flex-start}.feature-extended:nth-child(even) .feature-extended-image{order:1;margin-left:96px;margin-right:0}.feature-extended-body{display:flex;flex-direction:column;justify-content:center;flex-shrink:0;width:360px;text-align:left}}@media (min-width: 1025px){.features-extended .container{max-width:944px}.feature-extended .feature-extended-image{margin-right:64px}.feature-extended:nth-child(even) .feature-extended-image{margin-left:64px}.feature-extended-body{width:392px}}.cta .section-paragraph{margin-bottom:32px}@media (min-width: 641px){.cta .section-paragraph{margin-bottom:32px;padding-left:72px;padding-right:72px}}.is-boxed{background:#fff}.body-wrap{background:#fff;overflow:hidden;display:flex;flex-direction:column;min-height:100vh}.boxed-container{max-width:1440px;margin:0 auto;box-shadow:0 20px 48px rgba(22,30,42,0.16)}main{flex:1 0 auto}.section-inner{position:relative;padding-top:48px;padding-bottom:48px}@media (min-width: 641px){.section-inner{padding-top:88px;padding-bottom:88px}}.site-footer{position:relative;background:#0B0D19}.site-footer::before{content:'';position:absolute;top:-76px;left:calc(50% - 360px);width:720px;height:291px;background-image:url("../media/footer-bg.svg");background-size:720px 291px;background-repeat:no-repeat}.site-footer .footer-particles-container{position:absolute;top:0;bottom:0;left:0;right:0}.site-footer-bottom{font-size:14px;line-height:22px;letter-spacing:0px;z-index:1}.site-footer-bottom a{color:#606483;text-decoration:none}.site-footer-bottom a:hover,.site-footer-bottom a:active{text-decoration:underline}.site-footer-inner{position:relative;display:flex;flex-wrap:wrap;padding-top:48px;padding-bottom:48px;position:relative}.site-footer-inner::before{content:'';position:absolute;top:0;left:0;width:100%;display:block;height:1px;background:#1E233D}.footer-brand,.footer-links,.footer-social-links,.footer-copyright{flex:none;width:100%;display:inline-flex;justify-content:center}.footer-brand,.footer-links,.footer-social-links{margin-bottom:24px}.footer-links li+li,.footer-social-links li+li{margin-left:16px}.footer-social-links li{display:inline-flex}.footer-social-links li a{padding:8px}@media (min-width: 641px){.site-footer::before{top:-152px;left:calc(50% - 720px);width:1440px;height:582px;background-size:1440px 582px}.site-footer-inner{justify-content:space-between;padding-top:64px;padding-bottom:64px}.footer-brand,.footer-links,.footer-social-links,.footer-copyright{flex:50%}.footer-brand,.footer-copyright{justify-content:flex-start}.footer-links,.footer-social-links{justify-content:flex-end}.footer-links{order:1;margin-bottom:0}} + +/*# sourceMappingURL=data:application/json;base64,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LGlCQUFpQixDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsa0JBQWtCLENBQUMsbUVBQW1FLFNBQVMsQ0FBQyxVQUFVLENBQUMsbUJBQW1CLENBQUMsc0JBQXNCLENBQUMsaURBQWlELGtCQUFrQixDQUFDLCtDQUErQyxnQkFBZ0IsQ0FBQyx3QkFBd0IsbUJBQW1CLENBQUMsMEJBQTBCLFdBQVcsQ0FBQywwQkFBMEIscUJBQXFCLFVBQVUsQ0FBQyxzQkFBc0IsQ0FBQyxZQUFZLENBQUMsWUFBWSxDQUFDLDRCQUE0QixDQUFDLG1CQUFtQiw2QkFBNkIsQ0FBQyxnQkFBZ0IsQ0FBQyxtQkFBbUIsQ0FBQyxtRUFBbUUsUUFBUSxDQUFDLGdDQUFnQywwQkFBMEIsQ0FBQyxtQ0FBbUMsd0JBQXdCLENBQUMsY0FBYyxPQUFPLENBQUMsZUFBZSxDQUFDIiwiZmlsZSI6InN0eWxlLmNzcyIsInNvdXJjZXNDb250ZW50IjpbImh0bWx7bGluZS1oZWlnaHQ6MS4xNTstbXMtdGV4dC1zaXplLWFkanVzdDoxMDAlOy13ZWJraXQtdGV4dC1zaXplLWFkanVzdDoxMDAlfWJvZHl7bWFyZ2luOjB9YXJ0aWNsZSxhc2lkZSxmb290ZXIsaGVhZGVyLG5hdixzZWN0aW9ue2Rpc3BsYXk6YmxvY2t9aDF7Zm9udC1zaXplOjJlbTttYXJnaW46MC42N2VtIDB9ZmlnY2FwdGlvbixmaWd1cmUsbWFpbntkaXNwbGF5OmJsb2NrfWZpZ3VyZXttYXJnaW46MWVtIDQwcHh9aHJ7Ym94LXNpemluZzpjb250ZW50LWJveDtoZWlnaHQ6MDtvdmVyZmxvdzp2aXNpYmxlfXByZXtmb250LWZhbWlseTptb25vc3BhY2UsIG1vbm9zcGFjZTtmb250LXNpemU6MWVtfWF7YmFja2dyb3VuZC1jb2xvcjp0cmFuc3BhcmVudDstd2Via2l0LXRleHQtZGVjb3JhdGlvbi1za2lwOm9iamVjdHN9YWJiclt0aXRsZV17Ym9yZGVyLWJvdHRvbTpub25lO3RleHQtZGVjb3JhdGlvbjp1bmRlcmxpbmU7dGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZSBkb3R0ZWR9YixzdHJvbmd7Zm9udC13ZWlnaHQ6aW5oZXJpdH1iLHN0cm9uZ3tmb250LXdlaWdodDpib2xkZXJ9Y29kZSxrYmQsc2FtcHtmb250LWZhbWlseTptb25vc3BhY2UsIG1vbm9zcGFjZTtmb250LXNpemU6MWVtfWRmbntmb250LXN0eWxlOml0YWxpY31tYXJre2JhY2tncm91bmQtY29sb3I6I2ZmMDtjb2xvcjojMDAwfXNtYWxse2ZvbnQtc2l6ZTo4MCV9c3ViLHN1cHtmb250LXNpemU6NzUlO2xpbmUtaGVpZ2h0OjA7cG9zaXRpb246cmVsYXRpdmU7dmVydGljYWwtYWxpZ246YmFzZWxpbmV9c3Vie2JvdHRvbTotMC4yNWVtfXN1cHt0b3A6LTAuNWVtfWF1ZGlvLHZpZGVve2Rpc3BsYXk6aW5saW5lLWJsb2NrfWF1ZGlvOm5vdChbY29udHJvbHNdKXtkaXNwbGF5Om5vbmU7aGVpZ2h0OjB9aW1ne2JvcmRlci1zdHlsZTpub25lfXN2Zzpub3QoOnJvb3Qpe292ZXJmbG93OmhpZGRlbn1idXR0b24saW5wdXQsb3B0Z3JvdXAsc2VsZWN0LHRleHRhcmVhe2ZvbnQtZmFtaWx5OnNhbnMtc2VyaWY7Zm9udC1zaXplOjEwMCU7bGluZS1oZWlnaHQ6MS4xNTttYXJnaW46MH1idXR0b24saW5wdXR7b3ZlcmZsb3c6dmlzaWJsZX1idXR0b24sc2VsZWN0e3RleHQtdHJhbnNmb3JtOm5vbmV9YnV0dG9uLGh0bWwgW3R5cGU9XCJidXR0b25cIl0sW3R5cGU9XCJyZXNldFwiXSxbdHlwZT1cInN1Ym1pdFwiXXstd2Via2l0LWFwcGVhcmFuY2U6YnV0dG9ufWJ1dHRvbjo6LW1vei1mb2N1cy1pbm5lcixbdHlwZT1cImJ1dHRvblwiXTo6LW1vei1mb2N1cy1pbm5lcixbdHlwZT1cInJlc2V0XCJdOjotbW96LWZvY3VzLWlubmVyLFt0eXBlPVwic3VibWl0XCJdOjotbW96LWZvY3VzLWlubmVye2JvcmRlci1zdHlsZTpub25lO3BhZGRpbmc6MH1idXR0b246LW1vei1mb2N1c3JpbmcsW3R5cGU9XCJidXR0b25cIl06LW1vei1mb2N1c3JpbmcsW3R5cGU9XCJyZXNldFwiXTotbW96LWZvY3VzcmluZyxbdHlwZT1cInN1Ym1pdFwiXTotbW96LWZvY3VzcmluZ3tvdXRsaW5lOjFweCBkb3R0ZWQgQnV0dG9uVGV4dH1maWVsZHNldHtwYWRkaW5nOjAuMzVlbSAwLjc1ZW0gMC42MjVlbX1sZWdlbmR7Ym94LXNpemluZzpib3JkZXItYm94O2NvbG9yOmluaGVyaXQ7ZGlzcGxheTp0YWJsZTttYXgtd2lkdGg6MTAwJTtwYWRkaW5nOjA7d2hpdGUtc3BhY2U6bm9ybWFsfXByb2dyZXNze2Rpc3BsYXk6aW5saW5lLWJsb2NrO3ZlcnRpY2FsLWFsaWduOmJhc2VsaW5lfXRleHRhcmVhe292ZXJmbG93OmF1dG99W3R5cGU9XCJjaGVja2JveFwiXSxbdHlwZT1cInJhZGlvXCJde2JveC1zaXppbmc6Ym9yZGVyLWJveDtwYWRkaW5nOjB9W3R5cGU9XCJudW1iZXJcIl06Oi13ZWJraXQtaW5uZXItc3Bpbi1idXR0b24sW3R5cGU9XCJudW1iZXJcIl06Oi13ZWJraXQtb3V0ZXItc3Bpbi1idXR0b257aGVpZ2h0OmF1dG99W3R5cGU9XCJzZWFyY2hcIl17LXdlYmtpdC1hcHBlYXJhbmNlOnRleHRmaWVsZDtvdXRsaW5lLW9mZnNldDotMnB4fVt0eXBlPVwic2VhcmNoXCJdOjotd2Via2l0LXNlYXJjaC1jYW5jZWwtYnV0dG9uLFt0eXBlPVwic2VhcmNoXCJdOjotd2Via2l0LXNlYXJjaC1kZWNvcmF0aW9uey13ZWJraXQtYXBwZWFyYW5jZTpub25lfTo6LXdlYmtpdC1maWxlLXVwbG9hZC1idXR0b257LXdlYmtpdC1hcHBlYXJhbmNlOmJ1dHRvbjtmb250OmluaGVyaXR9ZGV0YWlscyxtZW51e2Rpc3BsYXk6YmxvY2t9c3VtbWFyeXtkaXNwbGF5Omxpc3QtaXRlbX1jYW52YXN7ZGlzcGxheTppbmxpbmUtYmxvY2t9dGVtcGxhdGV7ZGlzcGxheTpub25lfVtoaWRkZW5de2Rpc3BsYXk6bm9uZX1odG1se2JveC1zaXppbmc6Ym9yZGVyLWJveH0qLCo6YmVmb3JlLCo6YWZ0ZXJ7Ym94LXNpemluZzppbmhlcml0fWJvZHl7YmFja2dyb3VuZDojZmZmOy1tb3otb3N4LWZvbnQtc21vb3RoaW5nOmdyYXlzY2FsZTstd2Via2l0LWZvbnQtc21vb3RoaW5nOmFudGlhbGlhc2VkfWhye2JvcmRlcjowO2Rpc3BsYXk6YmxvY2s7aGVpZ2h0OjFweDtiYWNrZ3JvdW5kOiNFNEU4RUU7bWFyZ2luLXRvcDoyNHB4O21hcmdpbi1ib3R0b206MjRweH11bCxvbHttYXJnaW4tdG9wOjA7bWFyZ2luLWJvdHRvbToyNHB4O3BhZGRpbmctbGVmdDoyNHB4fXVse2xpc3Qtc3R5bGU6ZGlzY31vbHtsaXN0LXN0eWxlOmRlY2ltYWx9bGk+dWwsbGk+b2x7bWFyZ2luLWJvdHRvbTowfWRse21hcmdpbi10b3A6MDttYXJnaW4tYm90dG9tOjI0cHh9ZHR7Zm9udC13ZWlnaHQ6NjAwfWRke21hcmdpbi1sZWZ0OjI0cHg7bWFyZ2luLWJvdHRvbToyNHB4fWltZ3toZWlnaHQ6YXV0bzttYXgtd2lkdGg6MTAwJTt2ZXJ0aWNhbC1hbGlnbjptaWRkbGV9ZmlndXJle21hcmdpbjoyNHB4IDB9ZmlnY2FwdGlvbntmb250LXNpemU6MTZweDtsaW5lLWhlaWdodDoyNHB4O3BhZGRpbmc6OHB4IDB9aW1nLHN2Z3tkaXNwbGF5OmJsb2NrfXRhYmxle2JvcmRlci1jb2xsYXBzZTpjb2xsYXBzZTttYXJnaW4tYm90dG9tOjI0cHg7d2lkdGg6MTAwJX10cntib3JkZXItYm90dG9tOjFweCBzb2xpZCAjRTRFOEVFfXRoe3RleHQtYWxpZ246bGVmdH10aCx0ZHtwYWRkaW5nOjEwcHggMTZweH10aDpmaXJzdC1jaGlsZCx0ZDpmaXJzdC1jaGlsZHtwYWRkaW5nLWxlZnQ6MH10aDpsYXN0LWNoaWxkLHRkOmxhc3QtY2hpbGR7cGFkZGluZy1yaWdodDowfWh0bWx7Zm9udC1zaXplOjIwcHg7bGluZS1oZWlnaHQ6MzBweH1ib2R5e2NvbG9yOiM2QjdBOTA7Zm9udC1zaXplOjFyZW19Ym9keSxidXR0b24saW5wdXQsc2VsZWN0LHRleHRhcmVhe2ZvbnQtZmFtaWx5OlwiSGVlYm9cIiwgc2Fucy1zZXJpZn1he2NvbG9yOiM0MjM0Rjg7dGV4dC1kZWNvcmF0aW9uOm5vbmV9YTpob3ZlcixhOmFjdGl2ZXtvdXRsaW5lOjA7dGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZX1oMSxoMixoMyxoNCxoNSxoNiwuaDEsLmgyLC5oMywuaDQsLmg1LC5oNntjbGVhcjpib3RoO2NvbG9yOiMxNjFFMkE7Zm9udC1mYW1pbHk6XCJPeHlnZW5cIiwgc2Fucy1zZXJpZjtmb250LXdlaWdodDo2MDB9aDEsLmgxe2ZvbnQtc2l6ZTozOHB4O2xpbmUtaGVpZ2h0OjQ4cHg7bGV0dGVyLXNwYWNpbmc6MHB4fUBtZWRpYSAobWluLXdpZHRoOiA2NDFweCl7aDEsLmgxe2ZvbnQtc2l6ZTo0NHB4O2xpbmUtaGVpZ2h0OjU0cHg7bGV0dGVyLXNwYWNpbmc6MHB4fX1oMiwuaDJ7Zm9udC1zaXplOjMycHg7bGluZS1oZWlnaHQ6NDJweDtsZXR0ZXItc3BhY2luZzowcHh9QG1lZGlhIChtaW4td2lkdGg6IDY0MXB4KXtoMiwuaDJ7Zm9udC1zaXplOjM4cHg7bGluZS1oZWlnaHQ6NDhweDtsZXR0ZXItc3BhY2luZzowcHh9fWgzLC5oMyxibG9ja3F1b3Rle2ZvbnQtc2l6ZToyOHB4O2xpbmUtaGVpZ2h0OjM0cHg7bGV0dGVyLXNwYWNpbmc6MHB4fUBtZWRpYSAobWluLXdpZHRoOiA2NDFweCl7aDMsLmgzLGJsb2NrcXVvdGV7Zm9udC1zaXplOjMycHg7bGluZS1oZWlnaHQ6NDJweDtsZXR0ZXItc3BhY2luZzowcHh9fWg0LC5oNHtmb250LXNpemU6MjhweDtsaW5lLWhlaWdodDozNHB4O2xldHRlci1zcGFjaW5nOjBweH1oNSwuaDUsaDYsLmg2e2ZvbnQtc2l6ZToyMHB4O2xpbmUtaGVpZ2h0OjMwcHg7bGV0dGVyLXNwYWNpbmc6LTAuMXB4fUBtZWRpYSAobWF4LXdpZHRoOiA2NDBweCl7LmgxLW1vYmlsZXtmb250LXNpemU6MzhweDtsaW5lLWhlaWdodDo0OHB4O2xldHRlci1zcGFjaW5nOjBweH0uaDItbW9iaWxle2ZvbnQtc2l6ZTozMnB4O2xpbmUtaGVpZ2h0OjQycHg7bGV0dGVyLXNwYWNpbmc6MHB4fS5oMy1tb2JpbGV7Zm9udC1zaXplOjI4cHg7bGluZS1oZWlnaHQ6MzRweDtsZXR0ZXItc3BhY2luZzowcHh9Lmg0LW1vYmlsZXtmb250LXNpemU6MjhweDtsaW5lLWhlaWdodDozNHB4O2xldHRlci1zcGFjaW5nOjBweH0uaDUtbW9iaWxlLC5oNi1tb2JpbGV7Zm9udC1zaXplOjIwcHg7bGluZS1oZWlnaHQ6MzBweDtsZXR0ZXItc3BhY2luZzotMC4xcHh9fS50ZXh0LWxpZ2h0e2NvbG9yOiM2MDY0ODN9LnRleHQtbGlnaHQgYXtjb2xvcjojNjA2NDgzfS50ZXh0LWxpZ2h0IGgxLC50ZXh0LWxpZ2h0IGgyLC50ZXh0LWxpZ2h0IGgzLC50ZXh0LWxpZ2h0IGg0LC50ZXh0LWxpZ2h0IGg1LC50ZXh0LWxpZ2h0IGg2LC50ZXh0LWxpZ2h0IC5oMSwudGV4dC1saWdodCAuaDIsLnRleHQtbGlnaHQgLmgzLC50ZXh0LWxpZ2h0IC5oNCwudGV4dC1saWdodCAuaDUsLnRleHQtbGlnaHQgLmg2e2NvbG9yOiNmZmYgIWltcG9ydGFudH0udGV4dC1zbXtmb250LXNpemU6MThweDtsaW5lLWhlaWdodDoyN3B4O2xldHRlci1zcGFjaW5nOi0wLjFweH0udGV4dC14c3tmb250LXNpemU6MTZweDtsaW5lLWhlaWdodDoyNHB4O2xldHRlci1zcGFjaW5nOi0wLjFweH1oMSxoMiwuaDEsLmgye21hcmdpbi10b3A6NDhweDttYXJnaW4tYm90dG9tOjE2cHh9aDMsLmgze21hcmdpbi10b3A6MzZweDttYXJnaW4tYm90dG9tOjEycHh9aDQsaDUsaDYsLmg0LC5oNSwuaDZ7bWFyZ2luLXRvcDoyNHB4O21hcmdpbi1ib3R0b206NHB4fXB7bWFyZ2luLXRvcDowO21hcmdpbi1ib3R0b206MjRweH1kZm4sY2l0ZSxlbSxpe2ZvbnQtc3R5bGU6aXRhbGljfWJsb2NrcXVvdGV7Y29sb3I6IzZCN0E5MDtmb250LXN0eWxlOml0YWxpYzttYXJnaW4tdG9wOjI0cHg7bWFyZ2luLWJvdHRvbToyNHB4O21hcmdpbi1sZWZ0OjI0cHh9YmxvY2txdW90ZTo6YmVmb3Jle2NvbnRlbnQ6XCJcXDIwMUNcIn1ibG9ja3F1b3RlOjphZnRlcntjb250ZW50OlwiXFwyMDFEXCJ9YmxvY2txdW90ZSBwe2Rpc3BsYXk6aW5saW5lfWFkZHJlc3N7Y29sb3I6IzZCN0E5MDtib3JkZXItd2lkdGg6MXB4IDA7Ym9yZGVyLXN0eWxlOnNvbGlkO2JvcmRlci1jb2xvcjojRTRFOEVFO3BhZGRpbmc6MjRweCAwO21hcmdpbjowIDAgMjRweH1wcmUscHJlIGgxLHByZSBoMixwcmUgaDMscHJlIGg0LHByZSBoNSxwcmUgaDYscHJlIC5oMSxwcmUgLmgyLHByZSAuaDMscHJlIC5oNCxwcmUgLmg1LHByZSAuaDZ7Zm9udC1mYW1pbHk6XCJDb3VyaWVyIDEwIFBpdGNoXCIsIENvdXJpZXIsIG1vbm9zcGFjZX1wcmUsY29kZSxrYmQsdHQsdmFye2JhY2tncm91bmQ6I0YzRjRGRn1wcmV7Zm9udC1zaXplOjE2cHg7bGluZS1oZWlnaHQ6MjRweDttYXJnaW4tYm90dG9tOjEuNmVtO21heC13aWR0aDoxMDAlO292ZXJmbG93OmF1dG87cGFkZGluZzoyNHB4O21hcmdpbi10b3A6MjRweDttYXJnaW4tYm90dG9tOjI0cHh9Y29kZSxrYmQsdHQsdmFye2ZvbnQtZmFtaWx5Ok1vbmFjbywgQ29uc29sYXMsIFwiQW5kYWxlIE1vbm9cIiwgXCJEZWphVnUgU2FucyBNb25vXCIsIG1vbm9zcGFjZTtmb250LXNpemU6MTZweDtwYWRkaW5nOjJweCA0cHh9YWJicixhY3Jvbnlte2N1cnNvcjpoZWxwfW1hcmssaW5ze3RleHQtZGVjb3JhdGlvbjpub25lfXNtYWxse2ZvbnQtc2l6ZToxOHB4O2xpbmUtaGVpZ2h0OjI3cHg7bGV0dGVyLXNwYWNpbmc6LTAuMXB4fWIsc3Ryb25ne2ZvbnQtd2VpZ2h0OjYwMH1idXR0b24saW5wdXQsc2VsZWN0LHRleHRhcmVhLGxhYmVse2ZvbnQtc2l6ZToyMHB4O2xpbmUtaGVpZ2h0OjMwcHh9LmNvbnRhaW5lciwuY29udGFpbmVyLXNte3dpZHRoOjEwMCU7bWFyZ2luOjAgYXV0bztwYWRkaW5nLWxlZnQ6MTZweDtwYWRkaW5nLXJpZ2h0OjE2cHh9QG1lZGlhIChtaW4td2lkdGg6IDQ4MXB4KXsuY29udGFpbmVyLC5jb250YWluZXItc217cGFkZGluZy1sZWZ0OjI0cHg7cGFkZGluZy1yaWdodDoyNHB4fX0uY29udGFpbmVye21heC13aWR0aDoxMTI4cHh9LmNvbnRhaW5lci1zbXttYXgtd2lkdGg6ODQ4cHh9LmNvbnRhaW5lciAuY29udGFpbmVyLXNte21heC13aWR0aDo4MDBweDtwYWRkaW5nLWxlZnQ6MDtwYWRkaW5nLXJpZ2h0OjB9LnNjcmVlbi1yZWFkZXItdGV4dHtjbGlwOnJlY3QoMXB4LCAxcHgsIDFweCwgMXB4KTtwb3NpdGlvbjphYnNvbHV0ZSAhaW1wb3J0YW50O2hlaWdodDoxcHg7d2lkdGg6MXB4O292ZXJmbG93OmhpZGRlbjt3b3JkLXdyYXA6bm9ybWFsICFpbXBvcnRhbnR9LnNjcmVlbi1yZWFkZXItdGV4dDpmb2N1c3tib3JkZXItcmFkaXVzOjJweDtib3gtc2hhZG93OjAgMCAycHggMnB4IHJnYmEoMCwwLDAsMC42KTtjbGlwOmF1dG8gIWltcG9ydGFudDtkaXNwbGF5OmJsb2NrO2ZvbnQtc2l6ZToxNHB4O2xldHRlci1zcGFjaW5nOjBweDtmb250LXdlaWdodDo2MDA7bGluZS1oZWlnaHQ6MTZweDt0ZXh0LWRlY29yYXRpb246bm9uZTt0ZXh0LXRyYW5zZm9ybTp1cHBlcmNhc2U7YmFja2dyb3VuZC1jb2xvcjojZmZmO2NvbG9yOiM0MjM0RjggIWltcG9ydGFudDtib3JkZXI6bm9uZTtoZWlnaHQ6YXV0bztsZWZ0OjhweDtwYWRkaW5nOjE2cHggNDBweDt0b3A6OHB4O3dpZHRoOmF1dG87ei1pbmRleDoxMDAwMDB9Lmxpc3QtcmVzZXR7bGlzdC1zdHlsZTpub25lO3BhZGRpbmc6MH0udGV4dC1sZWZ0e3RleHQtYWxpZ246bGVmdH0udGV4dC1jZW50ZXJ7dGV4dC1hbGlnbjpjZW50ZXJ9LnRleHQtcmlnaHR7dGV4dC1hbGlnbjpyaWdodH0udGV4dC1wcmltYXJ5e2NvbG9yOiM0MjM0Rjh9LnRleHQtc2Vjb25kYXJ5e2NvbG9yOiNGRjZDNTB9Lmhhcy10b3AtZGl2aWRlcntwb3NpdGlvbjpyZWxhdGl2ZX0uaGFzLXRvcC1kaXZpZGVyOjpiZWZvcmV7Y29udGVudDonJztwb3NpdGlvbjphYnNvbHV0ZTt0b3A6MDtsZWZ0OjA7d2lkdGg6MTAwJTtkaXNwbGF5OmJsb2NrO2hlaWdodDoxcHg7YmFja2dyb3VuZDojRTRFOEVFfS5oYXMtYm90dG9tLWRpdmlkZXJ7cG9zaXRpb246cmVsYXRpdmV9Lmhhcy1ib3R0b20tZGl2aWRlcjo6YWZ0ZXJ7Y29udGVudDonJztwb3NpdGlvbjphYnNvbHV0ZTtib3R0b206MDtsZWZ0OjA7d2lkdGg6MTAwJTtkaXNwbGF5OmJsb2NrO2hlaWdodDoxcHg7YmFja2dyb3VuZDojRTRFOEVFfS5tLTB7bWFyZ2luOjB9Lm10LTB7bWFyZ2luLXRvcDowfS5tci0we21hcmdpbi1yaWdodDowfS5tYi0we21hcmdpbi1ib3R0b206MH0ubWwtMHttYXJnaW4tbGVmdDowfS5tLTh7bWFyZ2luOjhweH0ubXQtOHttYXJnaW4tdG9wOjhweH0ubXItOHttYXJnaW4tcmlnaHQ6OHB4fS5tYi04e21hcmdpbi1ib3R0b206OHB4fS5tbC04e21hcmdpbi1sZWZ0OjhweH0ubS0xNnttYXJnaW46MTZweH0ubXQtMTZ7bWFyZ2luLXRvcDoxNnB4fS5tci0xNnttYXJnaW4tcmlnaHQ6MTZweH0ubWItMTZ7bWFyZ2luLWJvdHRvbToxNnB4fS5tbC0xNnttYXJnaW4tbGVmdDoxNnB4fS5tLTI0e21hcmdpbjoyNHB4fS5tdC0yNHttYXJnaW4tdG9wOjI0cHh9Lm1yLTI0e21hcmdpbi1yaWdodDoyNHB4fS5tYi0yNHttYXJnaW4tYm90dG9tOjI0cHh9Lm1sLTI0e21hcmdpbi1sZWZ0OjI0cHh9Lm0tMzJ7bWFyZ2luOjMycHh9Lm10LTMye21hcmdpbi10b3A6MzJweH0ubXItMzJ7bWFyZ2luLXJpZ2h0OjMycHh9Lm1iLTMye21hcmdpbi1ib3R0b206MzJweH0ubWwtMzJ7bWFyZ2luLWxlZnQ6MzJweH0ubS00MHttYXJnaW46NDBweH0ubXQtNDB7bWFyZ2luLXRvcDo0MHB4fS5tci00MHttYXJnaW4tcmlnaHQ6NDBweH0ubWItNDB7bWFyZ2luLWJvdHRvbTo0MHB4fS5tbC00MHttYXJnaW4tbGVmdDo0MHB4fS5tLTQ4e21hcmdpbjo0OHB4fS5tdC00OHttYXJnaW4tdG9wOjQ4cHh9Lm1yLTQ4e21hcmdpbi1yaWdodDo0OHB4fS5tYi00OHttYXJnaW4tYm90dG9tOjQ4cHh9Lm1sLTQ4e21hcmdpbi1sZWZ0OjQ4cHh9Lm0tNTZ7bWFyZ2luOjU2cHh9Lm10LTU2e21hcmdpbi10b3A6NTZweH0ubXItNTZ7bWFyZ2luLXJpZ2h0OjU2cHh9Lm1iLTU2e21hcmdpbi1ib3R0b206NTZweH0ubWwtNTZ7bWFyZ2luLWxlZnQ6NTZweH0ubS02NHttYXJnaW46NjRweH0ubXQtNjR7bWFyZ2luLXRvcDo2NHB4fS5tci02NHttYXJnaW4tcmlnaHQ6NjRweH0ubWItNjR7bWFyZ2luLWJvdHRvbTo2NHB4fS5tbC02NHttYXJnaW4tbGVmdDo2NHB4fS5wLTB7cGFkZGluZzowfS5wdC0we3BhZGRpbmctdG9wOjB9LnByLTB7cGFkZGluZy1yaWdodDowfS5wYi0we3BhZGRpbmctYm90dG9tOjB9LnBsLTB7cGFkZGluZy1sZWZ0OjB9LnAtOHtwYWRkaW5nOjhweH0ucHQtOHtwYWRkaW5nLXRvcDo4cHh9LnByLTh7cGFkZGluZy1yaWdodDo4cHh9LnBiLTh7cGFkZGluZy1ib3R0b206OHB4fS5wbC04e3BhZGRpbmctbGVmdDo4cHh9LnAtMTZ7cGFkZGluZzoxNnB4fS5wdC0xNntwYWRkaW5nLXRvcDoxNnB4fS5wci0xNntwYWRkaW5nLXJpZ2h0OjE2cHh9LnBiLTE2e3BhZGRpbmctYm90dG9tOjE2cHh9LnBsLTE2e3BhZGRpbmctbGVmdDoxNnB4fS5wLTI0e3BhZGRpbmc6MjRweH0ucHQtMjR7cGFkZGluZy10b3A6MjRweH0ucHItMjR7cGFkZGluZy1yaWdodDoyNHB4fS5wYi0yNHtwYWRkaW5nLWJvdHRvbToyNHB4fS5wbC0yNHtwYWRkaW5nLWxlZnQ6MjRweH0ucC0zMntwYWRkaW5nOjMycHh9LnB0LTMye3BhZGRpbmctdG9wOjMycHh9LnByLTMye3BhZGRpbmctcmlnaHQ6MzJweH0ucGItMzJ7cGFkZGluZy1ib3R0b206MzJweH0ucGwtMzJ7cGFkZGluZy1sZWZ0OjMycHh9LnAtNDB7cGFkZGluZzo0MHB4fS5wdC00MHtwYWRkaW5nLXRvcDo0MHB4fS5wci00MHtwYWRkaW5nLXJpZ2h0OjQwcHh9LnBiLTQwe3BhZGRpbmctYm90dG9tOjQwcHh9LnBsLTQwe3BhZGRpbmctbGVmdDo0MHB4fS5wLTQ4e3BhZGRpbmc6NDhweH0ucHQtNDh7cGFkZGluZy10b3A6NDhweH0ucHItNDh7cGFkZGluZy1yaWdodDo0OHB4fS5wYi00OHtwYWRkaW5nLWJvdHRvbTo0OHB4fS5wbC00OHtwYWRkaW5nLWxlZnQ6NDhweH0ucC01NntwYWRkaW5nOjU2cHh9LnB0LTU2e3BhZGRpbmctdG9wOjU2cHh9LnByLTU2e3BhZGRpbmctcmlnaHQ6NTZweH0ucGItNTZ7cGFkZGluZy1ib3R0b206NTZweH0ucGwtNTZ7cGFkZGluZy1sZWZ0OjU2cHh9LnAtNjR7cGFkZGluZzo2NHB4fS5wdC02NHtwYWRkaW5nLXRvcDo2NHB4fS5wci02NHtwYWRkaW5nLXJpZ2h0OjY0cHh9LnBiLTY0e3BhZGRpbmctYm90dG9tOjY0cHh9LnBsLTY0e3BhZGRpbmctbGVmdDo2NHB4fS5zciAuaGFzLWFuaW1hdGlvbnMgLmlzLXJldmVhbGluZ3t2aXNpYmlsaXR5OmhpZGRlbn0uYnV0dG9ue2Rpc3BsYXk6aW5saW5lLWZsZXg7Zm9udC1zaXplOjE0cHg7bGV0dGVyLXNwYWNpbmc6MHB4O2ZvbnQtd2VpZ2h0OjcwMDtsaW5lLWhlaWdodDoxNnB4O3RleHQtZGVjb3JhdGlvbjpub25lICFpbXBvcnRhbnQ7dGV4dC10cmFuc2Zvcm06dXBwZXJjYXNlO2JhY2tncm91bmQtY29sb3I6I2ZmZjtiYWNrZ3JvdW5kOiNmZmY7Y29sb3I6IzQyMzRGOCAhaW1wb3J0YW50O2JvcmRlcjpub25lO2JvcmRlci1yYWRpdXM6MnB4O2N1cnNvcjpwb2ludGVyO2p1c3RpZnktY29udGVudDpjZW50ZXI7cGFkZGluZzoxNnB4IDQwcHg7aGVpZ2h0OjQ4cHg7dGV4dC1hbGlnbjpjZW50ZXI7d2hpdGUtc3BhY2U6bm93cmFwfS5idXR0b246YWN0aXZle291dGxpbmU6MH0uYnV0dG9uOjpiZWZvcmV7Ym9yZGVyLXJhZGl1czoycHh9LmJ1dHRvbi1zaGFkb3d7cG9zaXRpb246cmVsYXRpdmV9LmJ1dHRvbi1zaGFkb3c6OmJlZm9yZXtjb250ZW50OicnO3Bvc2l0aW9uOmFic29sdXRlO3RvcDowO3JpZ2h0OjA7Ym90dG9tOjA7bGVmdDowO2JveC1zaGFkb3c6MCA4cHggMTZweCByZ2JhKDIyLDMwLDQyLDAuMTIpO21peC1ibGVuZC1tb2RlOm11bHRpcGx5O3RyYW5zaXRpb246Ym94LXNoYWRvdyAuMTVzIGVhc2V9LmJ1dHRvbi1zaGFkb3c6aG92ZXI6OmJlZm9yZXtib3gtc2hhZG93OjAgOHB4IDE2cHggcmdiYSgyMiwzMCw0MiwwLjE2KX0uYnV0dG9uLXNte3BhZGRpbmc6OHB4IDI0cHg7aGVpZ2h0OjMycHh9LmJ1dHRvbi1zbS5idXR0b24tc2hhZG93OjpiZWZvcmV7Ym94LXNoYWRvdzowIDRweCAxNnB4IHJnYmEoMjIsMzAsNDIsMC4xMil9LmJ1dHRvbi1zbS5idXR0b24tc2hhZG93OmhvdmVyOjpiZWZvcmV7Ym94LXNoYWRvdzowIDRweCAxNnB4IHJnYmEoMjIsMzAsNDIsMC4xNil9LmJ1dHRvbi1wcmltYXJ5LC5idXR0b24tc2Vjb25kYXJ5e2NvbG9yOiNmZmYgIWltcG9ydGFudDt0cmFuc2l0aW9uOmJhY2tncm91bmQgLjE1cyBlYXNlfS5idXR0b24tcHJpbWFyeXtiYWNrZ3JvdW5kOiM0MjM0Rjh9LmJ1dHRvbi1wcmltYXJ5OmhvdmVye2JhY2tncm91bmQ6IzRiM2VmOH0uYnV0dG9uLXByaW1hcnkuYnV0dG9uLXNoYWRvdzo6YmVmb3Jle2JveC1zaGFkb3c6MCA4cHggMTZweCByZ2JhKDY2LDUyLDI0OCwwLjI0KX0uYnV0dG9uLXByaW1hcnkuYnV0dG9uLXNoYWRvdzpob3Zlcjo6YmVmb3Jle2JveC1zaGFkb3c6MCA4cHggMTZweCByZ2JhKDY2LDUyLDI0OCwwLjMyKX0uYnV0dG9uLXByaW1hcnkgLmJ1dHRvbi1zbS5idXR0b24tc2hhZG93OjpiZWZvcmV7Ym94LXNoYWRvdzowIDRweCAxNnB4IHJnYmEoNjYsNTIsMjQ4LDAuMjQpfS5idXR0b24tcHJpbWFyeSAuYnV0dG9uLXNtLmJ1dHRvbi1zaGFkb3c6aG92ZXI6OmJlZm9yZXtib3gtc2hhZG93OjAgNHB4IDE2cHggcmdiYSg2Niw1MiwyNDgsMC4zMil9LmJ1dHRvbi1zZWNvbmRhcnl7YmFja2dyb3VuZDojRkY2QzUwfS5idXR0b24tc2Vjb25kYXJ5OmhvdmVye2JhY2tncm91bmQ6I2ZmNzk1Zn0uYnV0dG9uLXNlY29uZGFyeS5idXR0b24tc2hhZG93OjpiZWZvcmV7Ym94LXNoYWRvdzowIDhweCAxNnB4IHJnYmEoMjU1LDEwOCw4MCwwLjI0KX0uYnV0dG9uLXNlY29uZGFyeS5idXR0b24tc2hhZG93OmhvdmVyOjpiZWZvcmV7Ym94LXNoYWRvdzowIDhweCAxNnB4IHJnYmEoMjU1LDEwOCw4MCwwLjMyKX0uYnV0dG9uLXNlY29uZGFyeSAuYnV0dG9uLXNtLmJ1dHRvbi1zaGFkb3c6OmJlZm9yZXtib3gtc2hhZG93OjAgNHB4IDE2cHggcmdiYSgyNTUsMTA4LDgwLDAuMjQpfS5idXR0b24tc2Vjb25kYXJ5IC5idXR0b24tc20uYnV0dG9uLXNoYWRvdzpob3Zlcjo6YmVmb3Jle2JveC1zaGFkb3c6MCA0cHggMTZweCByZ2JhKDI1NSwxMDgsODAsMC4zMil9LmJ1dHRvbi1ibG9ja3tkaXNwbGF5OmZsZXg7d2lkdGg6MTAwJX1AbWVkaWEgKG1heC13aWR0aDogNjQwcHgpey5idXR0b24td2lkZS1tb2JpbGV7d2lkdGg6MTAwJTttYXgtd2lkdGg6MjgwcHh9fS5zaXRlLWhlYWRlcntwYWRkaW5nOjI0cHggMDtwb3NpdGlvbjphYnNvbHV0ZTt0b3A6MDtsZWZ0OjA7d2lkdGg6MTAwJTt6LWluZGV4OjF9LnNpdGUtaGVhZGVyLWlubmVye3Bvc2l0aW9uOnJlbGF0aXZlO2Rpc3BsYXk6ZmxleDtqdXN0aWZ5LWNvbnRlbnQ6c3BhY2UtYmV0d2VlbjthbGlnbi1pdGVtczpjZW50ZXJ9LmhlYWRlci1saW5rc3tkaXNwbGF5OmlubGluZS1mbGV4fS5oZWFkZXItbGlua3MgbGl7ZGlzcGxheTppbmxpbmUtZmxleH0uaGVhZGVyLWxpbmtzIGE6bm90KC5idXR0b24pe2ZvbnQtc2l6ZToxNnB4O2xpbmUtaGVpZ2h0OjI0cHg7bGV0dGVyLXNwYWNpbmc6LTAuMXB4O2ZvbnQtd2VpZ2h0OjYwMDtjb2xvcjojNjA2NDgzO3RleHQtdHJhbnNmb3JtOnVwcGVyY2FzZTt0ZXh0LWRlY29yYXRpb246bm9uZTtsaW5lLWhlaWdodDoxNnB4O3BhZGRpbmc6OHB4IDI0cHh9LmhlYWRlci1saW5rcyBhOm5vdCguYnV0dG9uKTpob3ZlciwuaGVhZGVyLWxpbmtzIGE6bm90KC5idXR0b24pOmFjdGl2ZXtjb2xvcjojZmZmfS5oZXJve3Bvc2l0aW9uOnJlbGF0aXZlO3BhZGRpbmctdG9wOjEyOHB4O3BhZGRpbmctYm90dG9tOjg4cHg7ei1pbmRleDowfS5oZXJvIC5oZXJvLWJne3Bvc2l0aW9uOmFic29sdXRlO3RvcDowO2JvdHRvbTo0MiU7bGVmdDowO3JpZ2h0OjA7YmFja2dyb3VuZDojMEIwRDE5O3otaW5kZXg6LTJ9Lmhlcm8gLmhlcm8tcGFydGljbGVzLWNvbnRhaW5lcntwb3NpdGlvbjphYnNvbHV0ZTt0b3A6MDtib3R0b206NDIlO2xlZnQ6MDtyaWdodDowfS5oZXJvOjpiZWZvcmUsLmhlcm86OmFmdGVye2NvbnRlbnQ6Jyc7cG9zaXRpb246YWJzb2x1dGU7bGVmdDpjYWxjKDUwJSAtIDM2MHB4KTt3aWR0aDo3MjBweDtiYWNrZ3JvdW5kLXJlcGVhdDpuby1yZXBlYXR9Lmhlcm86OmJlZm9yZXt0b3A6MDtoZWlnaHQ6MTU5cHg7YmFja2dyb3VuZC1pbWFnZTp1cmwoXCIuLi9pbWFnZXMvaGVyby1iZy10b3Auc3ZnXCIpO2JhY2tncm91bmQtc2l6ZTo3MjBweCAxNTlweH0uaGVybzo6YWZ0ZXJ7Ym90dG9tOjQyJTtoZWlnaHQ6MTczcHg7YmFja2dyb3VuZC1pbWFnZTp1cmwoXCIuLi9pbWFnZXMvaGVyby1iZy1ib3R0b20uc3ZnXCIpO2JhY2tncm91bmQtc2l6ZTo3MjBweCAxNzNweH0uaGVyby1pbm5lcntwb3NpdGlvbjpyZWxhdGl2ZTt6LWluZGV4OjF9Lmhlcm8tY29weXtwb3NpdGlvbjpyZWxhdGl2ZTttYXJnaW4tYm90dG9tOjQ4cHh9QG1lZGlhIChtaW4td2lkdGg6IDY0MXB4KXsuaGVyb3twYWRkaW5nLXRvcDoxNjBweDtwYWRkaW5nLWJvdHRvbToxMjhweH0uaGVybzo6YmVmb3JlLC5oZXJvOjphZnRlcntsZWZ0OmNhbGMoNTAlIC0gNzIwcHgpO3dpZHRoOjE0NDBweH0uaGVybzo6YmVmb3Jle2hlaWdodDozMThweDtiYWNrZ3JvdW5kLXNpemU6MTQ0MHB4IDMxOHB4fS5oZXJvOjphZnRlcntoZWlnaHQ6MzQ3cHg7YmFja2dyb3VuZC1zaXplOjE0NDBweCAzNDdweH0uaGVyby1jb3B5e21hcmdpbi1ib3R0b206ODhweH0uaGVyby1wYXJhZ3JhcGh7cGFkZGluZy1sZWZ0OjcycHg7cGFkZGluZy1yaWdodDo3MnB4fX0uaGFzLWFuaW1hdGlvbnMgLmhlcm8gLmhlcm8tYmcsLmhhcy1hbmltYXRpb25zIC5oZXJvOjpiZWZvcmUsLmhhcy1hbmltYXRpb25zIC5oZXJvOjphZnRlciwuaGFzLWFuaW1hdGlvbnMgLmhlcm8tcGFydGljbGVzLWNvbnRhaW5lciwuaGFzLWFuaW1hdGlvbnMgLnNpdGUtaGVhZGVyLC5oYXMtYW5pbWF0aW9ucyAuaGVyby10aXRsZSwuaGFzLWFuaW1hdGlvbnMgLmhlcm8tcGFyYWdyYXBoLC5oYXMtYW5pbWF0aW9ucyAuaGVyby1jdGEsLmhhcy1hbmltYXRpb25zIC5oZXJvIC5tb2NrdXAtYmcsLmhhcy1hbmltYXRpb25zIC5oZXJvIC5kZXZpY2UtbW9ja3Vwe29wYWNpdHk6MH0uaGFzLWFuaW1hdGlvbnMuaXMtbG9hZGVkIC5oZXJvIC5oZXJvLWJne2FuaW1hdGlvbjpoZXJvQmcgMC42cyBjdWJpYy1iZXppZXIoMC4yMTUsIDAuNjEsIDAuMzU1LCAxKSBmb3J3YXJkc30uaGFzLWFuaW1hdGlvbnMuaXMtbG9hZGVkIC5oZXJvOjpiZWZvcmUsLmhhcy1hbmltYXRpb25zLmlzLWxvYWRlZCAuaGVybzo6YWZ0ZXJ7YW5pbWF0aW9uOmhlcm9GYWRlSW4gLjZzIGVhc2UgZm9yd2FyZHMgLjQ1c30uaGFzLWFuaW1hdGlvbnMuaXMtbG9hZGVkIC5zaXRlLWhlYWRlciwuaGFzLWFuaW1hdGlvbnMuaXMtbG9hZGVkIC5oZXJvLXBhcnRpY2xlcy1jb250YWluZXIsLmhhcy1hbmltYXRpb25zLmlzLWxvYWRlZCAuaGVybyAubW9ja3VwLWJne2FuaW1hdGlvbjpoZXJvRmFkZUluIC42cyBlYXNlIGZvcndhcmRzIC40NXN9Lmhhcy1hbmltYXRpb25zLmlzLWxvYWRlZCAuaGVyby10aXRsZXthbmltYXRpb246aGVyb0NvbnRlbnQgMC42cyBjdWJpYy1iZXppZXIoMC4yMTUsIDAuNjEsIDAuMzU1LCAxKSBmb3J3YXJkcyAwLjE1c30uaGFzLWFuaW1hdGlvbnMuaXMtbG9hZGVkIC5oZXJvLXBhcmFncmFwaHthbmltYXRpb246aGVyb0NvbnRlbnQgMC42cyBjdWJpYy1iZXppZXIoMC4yMTUsIDAuNjEsIDAuMzU1LCAxKSBmb3J3YXJkcyAwLjNzfS5oYXMtYW5pbWF0aW9ucy5pcy1sb2FkZWQgLmhlcm8tY3Rhe2FuaW1hdGlvbjpoZXJvQ29udGVudCAwLjZzIGN1YmljLWJlemllcigwLjIxNSwgMC42MSwgMC4zNTUsIDEpIGZvcndhcmRzIDAuNDVzfS5oYXMtYW5pbWF0aW9ucy5pcy1sb2FkZWQgLmhlcm8gLmRldmljZS1tb2NrdXB7YW5pbWF0aW9uOmhlcm9Nb2NrdXAgLjZzIGVhc2UgZm9yd2FyZHMgLjZzfUBrZXlmcmFtZXMgaGVyb0Jne2Zyb217dHJhbnNmb3JtOnNjYWxlWSgwKSBzY2FsZVgoMS4yKSBza2V3WSgzMGRlZyk7b3BhY2l0eToxfXRve3RyYW5zZm9ybTpzY2FsZVkoMSkgc2NhbGVYKDEpIHNrZXdZKDApO29wYWNpdHk6MX19QGtleWZyYW1lcyBoZXJvQ29udGVudHtmcm9te3RyYW5zZm9ybTp0cmFuc2xhdGVZKDQwcHgpIHNrZXdZKDJkZWcpO29wYWNpdHk6MH10b3t0cmFuc2Zvcm06dHJhbnNsYXRlWSgwKSBza2V3WSgwKTtvcGFjaXR5OjF9fUBrZXlmcmFtZXMgaGVyb01vY2t1cHtmcm9te3RyYW5zZm9ybTp0cmFuc2xhdGVZKDgwcHgpO29wYWNpdHk6MH10b3t0cmFuc2Zvcm06dHJhbnNsYXRlWSgwKTtvcGFjaXR5OjF9fUBrZXlmcmFtZXMgaGVyb0ZhZGVJbntmcm9te29wYWNpdHk6MH10b3tvcGFjaXR5OjF9fS5tb2NrdXAtY29udGFpbmVye3Bvc2l0aW9uOnJlbGF0aXZlfS5tb2NrdXAtYmd7cG9pbnRlci1ldmVudHM6bm9uZX0ubW9ja3VwLWJnIGltZywubW9ja3VwLWJnIHN2Z3twb3NpdGlvbjphYnNvbHV0ZTt0b3A6NTAlO2xlZnQ6NTAlO3RyYW5zZm9ybTp0cmFuc2xhdGUoLTUwJSwgLTUwJSk7d2lkdGg6YXV0byAhaW1wb3J0YW50O2hlaWdodDphdXRvO21heC13aWR0aDozMDAlICFpbXBvcnRhbnR9LmRldmljZS1tb2NrdXB7cG9zaXRpb246cmVsYXRpdmU7d2lkdGg6MzUwcHg7aGVpZ2h0OmF1dG87bWFyZ2luOjAgYXV0bzt6LWluZGV4OjF9Lmhhcy1hbmltYXRpb25zIC5mZWF0dXJlcy1leHRlbmRlZHtvcGFjaXR5OjB9Lmhhcy1hbmltYXRpb25zLmlzLWxvYWRlZCAuZmVhdHVyZXMtZXh0ZW5kZWR7b3BhY2l0eToxfS5mZWF0dXJlcy1leHRlbmRlZC1oZWFkZXJ7bWFyZ2luLWJvdHRvbTozMnB4fS5mZWF0dXJlcy1leHRlbmRlZC13cmFwe2Rpc3BsYXk6ZmxleDtmbGV4LXdyYXA6d3JhcDttYXJnaW4tdG9wOi0yNHB4fS5mZWF0dXJlcy1leHRlbmRlZC13cmFwOmxhc3Qtb2YtdHlwZXttYXJnaW4tYm90dG9tOi0yNHB4fS5mZWF0dXJlcy1leHRlbmRlZC13cmFwOm5vdCg6bGFzdC1vZi10eXBlKXttYXJnaW4tYm90dG9tOjI0cHh9LmZlYXR1cmUtZXh0ZW5kZWR7cGFkZGluZzoyNHB4IDB9LmZlYXR1cmUtZXh0ZW5kZWQtaW1hZ2V7cG9zaXRpb246cmVsYXRpdmU7bWFyZ2luLWJvdHRvbTozMnB4fS5mZWF0dXJlLWV4dGVuZGVkLWltYWdlIGltZywuZmVhdHVyZS1leHRlbmRlZC1pbWFnZSBzdmd7d2lkdGg6MTAwJTttYXgtd2lkdGg6Mjk2cHg7aGVpZ2h0OmF1dG87bWFyZ2luLWxlZnQ6YXV0bzttYXJnaW4tcmlnaHQ6YXV0bztvdmVyZmxvdzp2aXNpYmxlfS5mZWF0dXJlLWV4dGVuZGVkLWJvZHl7dGV4dC1hbGlnbjpjZW50ZXJ9QG1lZGlhIChtaW4td2lkdGg6IDY0MXB4KXsuZmVhdHVyZXMtZXh0ZW5kZWQgLmNvbnRhaW5lcnttYXgtd2lkdGg6OTEycHh9LmZlYXR1cmVzLWV4dGVuZGVkIC5zZWN0aW9uLWlubmVye3BhZGRpbmctYm90dG9tOjEyOHB4fS5mZWF0dXJlcy1leHRlbmRlZCAuc2VjdGlvbi1wYXJhZ3JhcGh7cGFkZGluZy1sZWZ0OjcycHg7cGFkZGluZy1yaWdodDo3MnB4O21hcmdpbi1ib3R0b206MH0uZmVhdHVyZXMtZXh0ZW5kZWQtaGVhZGVye21hcmdpbi1ib3R0b206ODBweH0uZmVhdHVyZXMtZXh0ZW5kZWQtd3JhcHttYXJnaW4tdG9wOi02NHB4fS5mZWF0dXJlcy1leHRlbmRlZC13cmFwOmxhc3Qtb2YtdHlwZXttYXJnaW4tYm90dG9tOi02NHB4fS5mZWF0dXJlcy1leHRlbmRlZC13cmFwOm5vdCg6bGFzdC1vZi10eXBlKXttYXJnaW4tYm90dG9tOjY0cHh9LmZlYXR1cmUtZXh0ZW5kZWR7ZGlzcGxheTpmbGV4O2ZsZXgtd3JhcDpub3dyYXA7YWxpZ24taXRlbXM6c3RyZXRjaDtqdXN0aWZ5LWNvbnRlbnQ6ZmxleC1lbmQ7cGFkZGluZzo2NHB4IDB9LmZlYXR1cmUtZXh0ZW5kZWQgLmZlYXR1cmUtZXh0ZW5kZWQtaW1hZ2V7d2lkdGg6NDQwcHg7bWFyZ2luLXJpZ2h0Ojk2cHg7bWFyZ2luLWJvdHRvbTowfS5mZWF0dXJlLWV4dGVuZGVkIC5mZWF0dXJlLWV4dGVuZGVkLWltYWdlIGltZywuZmVhdHVyZS1leHRlbmRlZCAuZmVhdHVyZS1leHRlbmRlZC1pbWFnZSBzdmd7d2lkdGg6YXV0b30uZmVhdHVyZS1leHRlbmRlZCAuZmVhdHVyZS1leHRlbmRlZC1pbWFnZSBpbWcuZGV2aWNlLW1vY2t1cCwuZmVhdHVyZS1leHRlbmRlZCAuZmVhdHVyZS1leHRlbmRlZC1pbWFnZSBzdmcuZGV2aWNlLW1vY2t1cHttYXgtd2lkdGg6Mjk2cHh9LmZlYXR1cmUtZXh0ZW5kZWQ6bnRoLWNoaWxkKGV2ZW4pe2p1c3RpZnktY29udGVudDpmbGV4LXN0YXJ0fS5mZWF0dXJlLWV4dGVuZGVkOm50aC1jaGlsZChldmVuKSAuZmVhdHVyZS1leHRlbmRlZC1pbWFnZXtvcmRlcjoxO21hcmdpbi1sZWZ0Ojk2cHg7bWFyZ2luLXJpZ2h0OjB9LmZlYXR1cmUtZXh0ZW5kZWQtYm9keXtkaXNwbGF5OmZsZXg7ZmxleC1kaXJlY3Rpb246Y29sdW1uO2p1c3RpZnktY29udGVudDpjZW50ZXI7ZmxleC1zaHJpbms6MDt3aWR0aDozNjBweDt0ZXh0LWFsaWduOmxlZnR9fUBtZWRpYSAobWluLXdpZHRoOiAxMDI1cHgpey5mZWF0dXJlcy1leHRlbmRlZCAuY29udGFpbmVye21heC13aWR0aDo5NDRweH0uZmVhdHVyZS1leHRlbmRlZCAuZmVhdHVyZS1leHRlbmRlZC1pbWFnZXttYXJnaW4tcmlnaHQ6NjRweH0uZmVhdHVyZS1leHRlbmRlZDpudGgtY2hpbGQoZXZlbikgLmZlYXR1cmUtZXh0ZW5kZWQtaW1hZ2V7bWFyZ2luLWxlZnQ6NjRweH0uZmVhdHVyZS1leHRlbmRlZC1ib2R5e3dpZHRoOjM5MnB4fX0uY3RhIC5zZWN0aW9uLXBhcmFncmFwaHttYXJnaW4tYm90dG9tOjMycHh9QG1lZGlhIChtaW4td2lkdGg6IDY0MXB4KXsuY3RhIC5zZWN0aW9uLXBhcmFncmFwaHttYXJnaW4tYm90dG9tOjMycHg7cGFkZGluZy1sZWZ0OjcycHg7cGFkZGluZy1yaWdodDo3MnB4fX0uaXMtYm94ZWR7YmFja2dyb3VuZDojZmZmfS5ib2R5LXdyYXB7YmFja2dyb3VuZDojZmZmO292ZXJmbG93OmhpZGRlbjtkaXNwbGF5OmZsZXg7ZmxleC1kaXJlY3Rpb246Y29sdW1uO21pbi1oZWlnaHQ6MTAwdmh9LmJveGVkLWNvbnRhaW5lcnttYXgtd2lkdGg6MTQ0MHB4O21hcmdpbjowIGF1dG87Ym94LXNoYWRvdzowIDIwcHggNDhweCByZ2JhKDIyLDMwLDQyLDAuMTYpfW1haW57ZmxleDoxIDAgYXV0b30uc2VjdGlvbi1pbm5lcntwb3NpdGlvbjpyZWxhdGl2ZTtwYWRkaW5nLXRvcDo0OHB4O3BhZGRpbmctYm90dG9tOjQ4cHh9QG1lZGlhIChtaW4td2lkdGg6IDY0MXB4KXsuc2VjdGlvbi1pbm5lcntwYWRkaW5nLXRvcDo4OHB4O3BhZGRpbmctYm90dG9tOjg4cHh9fS5zaXRlLWZvb3Rlcntwb3NpdGlvbjpyZWxhdGl2ZTtiYWNrZ3JvdW5kOiMwQjBEMTl9LnNpdGUtZm9vdGVyOjpiZWZvcmV7Y29udGVudDonJztwb3NpdGlvbjphYnNvbHV0ZTt0b3A6LTc2cHg7bGVmdDpjYWxjKDUwJSAtIDM2MHB4KTt3aWR0aDo3MjBweDtoZWlnaHQ6MjkxcHg7YmFja2dyb3VuZC1pbWFnZTp1cmwoXCIuLi9pbWFnZXMvZm9vdGVyLWJnLnN2Z1wiKTtiYWNrZ3JvdW5kLXNpemU6NzIwcHggMjkxcHg7YmFja2dyb3VuZC1yZXBlYXQ6bm8tcmVwZWF0fS5zaXRlLWZvb3RlciAuZm9vdGVyLXBhcnRpY2xlcy1jb250YWluZXJ7cG9zaXRpb246YWJzb2x1dGU7dG9wOjA7Ym90dG9tOjA7bGVmdDowO3JpZ2h0OjB9LnNpdGUtZm9vdGVyLWJvdHRvbXtmb250LXNpemU6MTRweDtsaW5lLWhlaWdodDoyMnB4O2xldHRlci1zcGFjaW5nOjBweDt6LWluZGV4OjF9LnNpdGUtZm9vdGVyLWJvdHRvbSBhe2NvbG9yOiM2MDY0ODM7dGV4dC1kZWNvcmF0aW9uOm5vbmV9LnNpdGUtZm9vdGVyLWJvdHRvbSBhOmhvdmVyLC5zaXRlLWZvb3Rlci1ib3R0b20gYTphY3RpdmV7dGV4dC1kZWNvcmF0aW9uOnVuZGVybGluZX0uc2l0ZS1mb290ZXItaW5uZXJ7cG9zaXRpb246cmVsYXRpdmU7ZGlzcGxheTpmbGV4O2ZsZXgtd3JhcDp3cmFwO3BhZGRpbmctdG9wOjQ4cHg7cGFkZGluZy1ib3R0b206NDhweDtwb3NpdGlvbjpyZWxhdGl2ZX0uc2l0ZS1mb290ZXItaW5uZXI6OmJlZm9yZXtjb250ZW50OicnO3Bvc2l0aW9uOmFic29sdXRlO3RvcDowO2xlZnQ6MDt3aWR0aDoxMDAlO2Rpc3BsYXk6YmxvY2s7aGVpZ2h0OjFweDtiYWNrZ3JvdW5kOiMxRTIzM0R9LmZvb3Rlci1icmFuZCwuZm9vdGVyLWxpbmtzLC5mb290ZXItc29jaWFsLWxpbmtzLC5mb290ZXItY29weXJpZ2h0e2ZsZXg6bm9uZTt3aWR0aDoxMDAlO2Rpc3BsYXk6aW5saW5lLWZsZXg7anVzdGlmeS1jb250ZW50OmNlbnRlcn0uZm9vdGVyLWJyYW5kLC5mb290ZXItbGlua3MsLmZvb3Rlci1zb2NpYWwtbGlua3N7bWFyZ2luLWJvdHRvbToyNHB4fS5mb290ZXItbGlua3MgbGkrbGksLmZvb3Rlci1zb2NpYWwtbGlua3MgbGkrbGl7bWFyZ2luLWxlZnQ6MTZweH0uZm9vdGVyLXNvY2lhbC1saW5rcyBsaXtkaXNwbGF5OmlubGluZS1mbGV4fS5mb290ZXItc29jaWFsLWxpbmtzIGxpIGF7cGFkZGluZzo4cHh9QG1lZGlhIChtaW4td2lkdGg6IDY0MXB4KXsuc2l0ZS1mb290ZXI6OmJlZm9yZXt0b3A6LTE1MnB4O2xlZnQ6Y2FsYyg1MCUgLSA3MjBweCk7d2lkdGg6MTQ0MHB4O2hlaWdodDo1ODJweDtiYWNrZ3JvdW5kLXNpemU6MTQ0MHB4IDU4MnB4fS5zaXRlLWZvb3Rlci1pbm5lcntqdXN0aWZ5LWNvbnRlbnQ6c3BhY2UtYmV0d2VlbjtwYWRkaW5nLXRvcDo2NHB4O3BhZGRpbmctYm90dG9tOjY0cHh9LmZvb3Rlci1icmFuZCwuZm9vdGVyLWxpbmtzLC5mb290ZXItc29jaWFsLWxpbmtzLC5mb290ZXItY29weXJpZ2h0e2ZsZXg6NTAlfS5mb290ZXItYnJhbmQsLmZvb3Rlci1jb3B5cmlnaHR7anVzdGlmeS1jb250ZW50OmZsZXgtc3RhcnR9LmZvb3Rlci1saW5rcywuZm9vdGVyLXNvY2lhbC1saW5rc3tqdXN0aWZ5LWNvbnRlbnQ6ZmxleC1lbmR9LmZvb3Rlci1saW5rc3tvcmRlcjoxO21hcmdpbi1ib3R0b206MH19XG4iXX0= */ \ No newline at end of file diff --git a/page/js/main.min.js b/page/js/main.min.js new file mode 100644 index 0000000..3cf3176 --- /dev/null +++ b/page/js/main.min.js @@ -0,0 +1 @@ +!function(){const t=document,e=t.documentElement,i=t.body,n=window.sr=ScrollReveal({mobile:!1});e.classList.remove("no-js"),e.classList.add("js"),window.addEventListener("load",function(){i.classList.add("is-loaded")}),i.classList.contains("has-animations")&&window.addEventListener("load",function(){n.reveal(".feature-extended .device-mockup",{duration:600,distance:"100px",easing:"cubic-bezier(0.215, 0.61, 0.355, 1)",origin:"bottom",viewFactor:.6}),n.reveal(".feature-extended .feature-extended-body",{duration:600,distance:"40px",easing:"cubic-bezier(0.215, 0.61, 0.355, 1)",origin:"top",viewFactor:.6})});let s=function(t){let e=this;e.parentNode=t,e.getCanvasSize(),window.addEventListener("resize",function(){e.getCanvasSize()}),e.mouseX=0,e.mouseY=0,window.addEventListener("mousemove",function(t){e.mouseX=t.clientX,e.mouseY=t.clientY}),e.randomise()};s.prototype.getCanvasSize=function(){this.canvasWidth=this.parentNode.clientWidth,this.canvasHeight=this.parentNode.clientHeight},s.prototype.generateDecimalBetween=function(t,e){return(Math.random()*(t-e)+e).toFixed(2)},s.prototype.update=function(){let t=this;t.translateX=t.translateX-t.movementX,t.translateY=t.translateY-t.movementY,t.posX+=(t.mouseX/(t.staticity/t.magnetism)-t.posX)/t.smoothFactor,t.posY+=(t.mouseY/(t.staticity/t.magnetism)-t.posY)/t.smoothFactor,(t.translateY+t.posY<0||t.translateX+t.posX<0||t.translateX+t.posX>t.canvasWidth)&&(t.randomise(),t.translateY=t.canvasHeight)},s.prototype.randomise=function(){this.colors=["85,107,139","38,141,247","66,52,248","255,108,80","243, 244, 255","96, 100, 131"],this.velocity=30,this.smoothFactor=50,this.staticity=30,this.magnetism=.1+4*Math.random(),this.color=this.colors[Math.floor(Math.random()*this.colors.length)],this.alpha=this.generateDecimalBetween(5,10)/10,this.size=this.generateDecimalBetween(1,4),this.posX=0,this.posY=0,this.movementX=this.generateDecimalBetween(-2,2)/this.velocity,this.movementY=this.generateDecimalBetween(1,20)/this.velocity,this.translateX=this.generateDecimalBetween(0,this.canvasWidth),this.translateY=this.generateDecimalBetween(0,this.canvasHeight)};let a=function(t){this.canvas=document.getElementById(t),this.ctx=this.canvas.getContext("2d"),this.dpr=window.devicePixelRatio};a.prototype.start=function(){let t=this;t.canvasSize(),window.addEventListener("resize",function(){t.canvasSize()}),t.bubblesList=[],t.generateBubbles(),t.animate()},a.prototype.canvasSize=function(){this.container=this.canvas.parentNode,this.w=this.container.offsetWidth,this.h=this.container.offsetHeight,this.wdpi=this.w*this.dpr,this.hdpi=this.h*this.dpr,this.canvas.width=this.wdpi,this.canvas.height=this.hdpi,this.canvas.style.width=this.w+"px",this.canvas.style.height=this.h+"px",this.ctx.scale(this.dpr,this.dpr)},a.prototype.animate=function(){let t=this;t.ctx.clearRect(0,0,t.canvas.clientWidth,t.canvas.clientHeight),t.bubblesList.forEach(function(e){e.update(),t.ctx.translate(e.translateX,e.translateY),t.ctx.beginPath(),t.ctx.arc(e.posX,e.posY,e.size,0,2*Math.PI),t.ctx.fillStyle="rgba("+e.color+","+e.alpha+")",t.ctx.fill(),t.ctx.setTransform(t.dpr,0,0,t.dpr,0,0)}),requestAnimationFrame(this.animate.bind(this))},a.prototype.addBubble=function(t){return this.bubblesList.push(t)},a.prototype.generateBubbles=function(){let t=this;for(let e=0;e \ No newline at end of file diff --git a/page/media/hero-bg-bottom.svg b/page/media/hero-bg-bottom.svg new file mode 100644 index 0000000..7b905e2 --- /dev/null +++ b/page/media/hero-bg-bottom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/page/media/hero-bg-top.svg b/page/media/hero-bg-top.svg new file mode 100644 index 0000000..0065311 --- /dev/null +++ b/page/media/hero-bg-top.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/page/media/iphone-feature-bg-01.svg b/page/media/iphone-feature-bg-01.svg new file mode 100644 index 0000000..f074eea --- /dev/null +++ b/page/media/iphone-feature-bg-01.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/page/media/iphone-hero-bg.svg b/page/media/iphone-hero-bg.svg new file mode 100644 index 0000000..a913ebf --- /dev/null +++ b/page/media/iphone-hero-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/page/media/logo.svg b/page/media/logo.svg new file mode 100644 index 0000000..fe9cd59 --- /dev/null +++ b/page/media/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/page/media/phone-hero.png b/page/media/phone-hero.png new file mode 100644 index 0000000..3c8d914 Binary files /dev/null and b/page/media/phone-hero.png differ diff --git a/server.js b/server.js index 28facee..f10898d 100644 --- a/server.js +++ b/server.js @@ -6,92 +6,444 @@ const bodyParser = require('body-parser'); const app = express(); const http = require('http').Server(app); const io = require('socket.io')(http); +const serverLink = process.env.SERVER_URL; -app.use(express.static('dist', {index: 'demo.html', maxage: '4h'})); +app.use(express.static(__dirname + '/static')); app.use(bodyParser.json()); +app.use(cors()); + +const users = []; +const chats = []; + +const defaultOnlineState = true; // handle admin Telegram messages -app.post('/hook', function(req, res){ - try { - const message = req.body.message || req.body.channel_post; - const chatId = message.chat.id; - const name = message.chat.first_name || message.chat.title || "admin"; - const text = message.text || ""; - const reply = message.reply_to_message; - - if (text.startsWith("/start")) { - console.log("/start chatId " + chatId); - sendTelegramMessage(chatId, - "*Welcome to Intergram* \n" + - "Your unique chat id is `" + chatId + "`\n" + - "Use it to link between the embedded chat and this telegram chat", - "Markdown"); - } else if (reply) { - let replyText = reply.text || ""; - let userId = replyText.split(':')[0]; - io.emit(chatId + "-" + userId, {name, text, from: 'admin'}); - } else if (text){ - io.emit(chatId, {name, text, from: 'admin'}); - } - - } catch (e) { - console.error("hook error", e, req.body); - } - res.statusCode = 200; - res.end(); +app.post('/hook', function (req, res) { + try { + + if (!req.body.callback_query) { + const message = req.body.message || req.body.channel_post; + const chatId = message.chat.id; + const name = message.from.first_name || message.chat.title || 'admin'; + const text = message.text || ''; + const reply = message.reply_to_message; + + console.log('< ' + text) + + if (text.startsWith('/start')) { + console.log('/start chatId ' + chatId); + sendTelegramMessage(chatId, + '*Welcome to Telegram Chat Widget Bot* 🔥\n\n' + + 'Your unique chat id is `' + chatId + '`\n' + + 'Use it to link between the embedded chat and this telegram chat\n\n' + + + '🔹 Works on any MikroTik Hotspot Portals\n' + + '🔹 Easy access for customer support\n' + + '🔹 Real-time chats\n' + + '🔹 Instant support and troubleshooting\n' + + '🔹 Personalized interaction with your chat ID\n\n' + + + '*Available Commands:*\n' + + '`/start` - Info about @MikrotikHsSupportBot \n' + + '`/all [any_text]` - Send message to all online users\n' + + '`/who` - Get online users list\n' + + '`/online` - Set chat online (Show Chat Widget)\n' + + '`/offline` - Set chat offline (Hide Chat Widget)\n' + + '`/ban [name]` - Ban user\n' + + '`/unban [name]` - Unban user\n' + + '`/user [name]` - See the user\'s information\n' + + '`/info` - more information about @MikrotikHsSupportBot\n' + + '`/help` - For detailed instructions\n\n' + + + '[Kintoyyy/Telegram-Chat-Widget](https://github.com/Kintoyyy/Telegram-Chat-Widget)Consider giving it a ⭐', + 'Markdown'); + } + + if (text.startsWith('/help')) { + console.log('/help chatId ' + chatId); + sendTelegramMessage(chatId, + '*Telegram Chat Widget Bot instructions* 🔥🤖\n\n' + + 'Your unique chat id is `' + chatId + '`\n\n' + + + '*How to Setup on mikrotik:*\n\n' + + '*1.)* We need to add @MikrotikHsSupportBot to hotspot walled-garden by pasting this following command in the *terminal*\n\n' + + 'goto: *ip* > *hotspot* > *Walled Garden Ip List*\n\n' + + 'then add a new entry\nset to *accept*\nDst. Host `' + serverLink + '`\n\n' + + '2. Add this in your preferred *html file* ex: *login.html*\n\n' + + '```\n\n' + + '\n' + + '```\n' + + '3. *Done*\n\n' + + 'for more details: [Kintoyyy/Telegram-Chat-Widget](https://github.com/Kintoyyy/Telegram-Chat-Widget)\n\n' + + '*Feel free to support this project*\n' + + '*Paypal* - paypal.me/Kintoyyyy\n' + + '*Gcash / Maya - * `09760009422`\n', + 'Markdown'); + } + + if (text.startsWith('/info')) { + console.log('/info chatId ' + chatId); + sendTelegramMessage(chatId, + '*Telegram Chat Widget Bot information* 🔥[🐈](https://media.tenor.com/gTrQ1V5mSxQAAAAC/cat-call-center.gif)\n\n' + + '@MikrotikHsSupportBot / [Kintoyyy/Telegram-Chat-Widget](https://github.com/Kintoyyy/Telegram-Chat-Widget) is a fork of [idoco/intergram](https://github.com/idoco/intergram) and [yamaha252/intergram](https://github.com/yamaha252/intergram) Consider giving the repositories a ⭐ to show some support\n\n' + + 'If you encounter some errors or you want new features\n' + + 'open a pull request in [Kintoyyy/Telegram-Chat-Widget](https://github.com/Kintoyyy/Telegram-Chat-Widget/pulls) 🙂\n\n' + + '*Feel free to support this project*\n' + + '*Paypal* - paypal.me/Kintoyyyy\n' + + '*Gcash / Maya - * `09760009422`\n', + 'Markdown'); + } + + + if (text.startsWith('/who')) { + + console.log('/who'); + const usersOnline = users.filter(user => user.chatId === chatId && user.online); + if (usersOnline.length) { + sendTelegramMessage(chatId, + '*Online users* 🧑‍🦯\n' + + usersOnline.map(user => '- `' + user.userId + '`').join('\n'), + 'Markdown'); + } else { + sendTelegramMessage(chatId, 'No users online 🌵'); + } + + } + + if (text.startsWith('/online')) { + console.log('/online chatId ' + chatId); + const chatIndex = chats.findIndex(chat => chat.chatId === chatId); + if (chats[chatIndex]) { + chats[chatIndex].online = true; + } else { + chats.push({ + chatId: chatId, + online: true + }) + } + sendTelegramMessage(chatId, 'Your chat is *online* 🟢 now and it will be shown for new users', 'Markdown'); + } + + if (text.startsWith('/offline')) { + console.log('/offline chatId ' + chatId); + const chatIndex = chats.findIndex(chat => chat.chatId === chatId); + if (chats[chatIndex]) { + chats[chatIndex].online = false; + } else { + chats.push({ + chatId: chatId, + online: false + }) + } + sendTelegramMessage(chatId, 'Your chat is *offline* 🔴 now and it won\'t be shown for new users', 'Markdown'); + } + + if (text.startsWith('/all')) { + const message = text.replace(/^\/all(@?\w+)? /, ''); + console.log('/all ' + message); + io.emit(chatId, { + name: name, + text: message, + from: 'admin', + }); + } + + if (text.startsWith('/ban')) { + const userId = text.replace(/^\/ban(@?\w+)? /, ''); + + if (userId === '') { + sendTelegramMessage(chatId, 'Please enter a username ex.`/ban cat`', 'Markdown'); + } + + const userIndex = users.findIndex(user => user.userId === userId && user.chatId === chatId); + if (users[userIndex]) { + users[userIndex].banned = true; + sendTelegramMessage(chatId, 'Ok, *' + userId + '* was banned ⛔', 'Markdown'); + } else { + sendTelegramMessage(chatId, 'User not found or banned.', 'Markdown'); + } + } + + if (text.startsWith('/unban')) { + const userId = text.replace(/^\/unban(@?\w+)? /, '').trim(); + const userIndex = users.findIndex(user => user.userId === userId && user.chatId === chatId); + if (userIndex !== -1) { + users[userIndex].banned = false; + sendTelegramMessage(chatId, 'Ok, *' + userId + '* was unbanned 🥳', 'Markdown'); + } else { + sendTelegramMessage(chatId, 'User not found or not banned.', 'Markdown'); + } + } + + + if (text.startsWith('/user')) { + const userId = text.replace(/^\/user(@?\w+)? /, ''); + const user = users.find(user => user.userId === userId && user.chatId === chatId); + if (user) { + const CustomData = user.CustomData || {}; + const username = user.CustomData.username || userId; + const CustomMsg = `\`${username}\`\n\n${Object.entries(CustomData).map(([label, value]) => `${label.trim()} : \`${value.trim()}\``).join('\n')}`; + sendTelegramMessage(chatId, CustomMsg, 'Markdown'); + } else { + sendTelegramMessage(chatId, 'User not found', 'Markdown'); + } + } + + if (text.startsWith('/test')) { + const inlineKeyboard = [ + [ + { text: 'Button 1', callback_data: 'button_1' }, + { text: 'Button 2', callback_data: 'button_2' }, + ], + [ + { text: 'Button 3', callback_data: 'button_3' }, + { text: 'Button 4', callback_data: 'button_4' }, + ], + [ + { text: 'Button 5', callback_data: 'button_5' }, + ], + ]; + sendTelegramMessage( + chatId, + 'What todo with the user?🔥\n\n', + 'Markdown', + false, + inlineKeyboard + ); + } + + if (reply && text) { + const replyText = reply.text || ''; + const userId = replyText.split(':')[0]; + const userIndex = users.findIndex(user => user.userId === userId && user.chatId === chatId); + + console.log(userId) + + if (users[userIndex]) { + if (users[userIndex].online) { + io.emit(chatId + '-' + userId, { name, text, from: 'admin' }); + } else { + users[userIndex].messages.push({ + name: name, + text: text, + time: new Date, + from: 'admin', + }); + } + } + } + + } else { + const callbackQuery = req.body.callback_query; + console.log(callbackQuery) + const chatId = callbackQuery.message.chat.id; + const data = callbackQuery.data; + + switch (data) { + case 'button_1': + sendTelegramMessage(chatId, 'You clicked Button 1!'); + break; + case 'button_2': + sendTelegramMessage(chatId, 'You clicked Button 2!'); + break; + default: + break; + } + + // Respond to the callback query to acknowledge receipt + request.post('https://api.telegram.org/bot' + process.env.TELEGRAM_TOKEN + '/answerCallbackQuery') + .form({ + callback_query_id: callbackQuery.id, + }) + .on('response', function (response) { + console.log('telegram callback response:', response.statusCode); + }); + } + + } catch (e) { + console.error('hook error', e, req.body); + } + res.statusCode = 200; + res.end(); }); // handle chat visitors websocket messages -io.on('connection', function(client){ - - client.on('register', function(registerMsg){ - let userId = registerMsg.userId; - let chatId = registerMsg.chatId; - let messageReceived = false; - console.log("useId " + userId + " connected to chatId " + chatId); - - client.on('message', function(msg) { - messageReceived = true; - io.emit(chatId + "-" + userId, msg); - let visitorName = msg.visitorName ? "[" + msg.visitorName + "]: " : ""; - sendTelegramMessage(chatId, userId + ":" + visitorName + " " + msg.text); - }); - - client.on('disconnect', function(){ - if (messageReceived) { - sendTelegramMessage(chatId, userId + " has left"); - } - }); - }); +io.on('connection', function (client) { + + client.on('register', function (registerMsg) { + + const userId = registerMsg.userId; + const chatId = parseInt(registerMsg.chatId); + const CustomData = registerMsg.CustomData; + + console.log('useId ' + userId + ' connected to chatId ' + chatId); + + const CustomMsg = `\`${userId}\`: *connected to chat* 😶‍🌫️\n\n`; + let CustomMsgData = ''; + + if (CustomData) { + CustomMsgData = `${Object.entries(CustomData).map(([label, value]) => `${label}: ${value}`).join('\n')}`; + } + + sendTelegramMessage(chatId, `${CustomMsg}${CustomMsgData}`, 'Markdown', true); + + + const userIndex = users.findIndex(user => user.userId === userId && user.chatId === chatId); + if (users[userIndex]) { + if (users[userIndex].banned) { + client.disconnect(); + return; + } + + users[userIndex].online = true; + users[userIndex].messages.forEach(message => io.emit(chatId + '-' + userId, message)); + users[userIndex].messages = []; + if (users[userIndex].active) { + sendTelegramMessage(chatId, '`' + userId + '` has come back 👋', 'Markdown', true); + } + } + + client.on('message', function (msg) { + + const userIndex = users.findIndex(user => user.userId === userId && user.chatId === chatId); + if (users[userIndex] && users[userIndex].banned) { + client.disconnect(); + return; + } + io.emit(chatId + '-' + userId, msg); + + console.log('> ' + msg.text) + + if (msg.text === '/help') { + io.emit(chatId + '-' + userId, { + text: registerMsg.helpMsg || 'help is coming😭', + from: 'admin', + }); + return; + } + + + let visitorName = msg.visitorName ? '[' + msg.visitorName + ']: ' : ''; + + sendTelegramMessage(chatId, '`' + userId + '`:' + visitorName + ' ' + msg.text, 'Markdown'); + + + + if (users[userIndex]) { + users[userIndex].active = true; + if (users[userIndex].unactiveTimeout) { + clearTimeout(users[userIndex].unactiveTimeout); + } + } else { + users.push({ + userId: userId, + chatId: chatId, + online: true, + active: true, + banned: false, + messages: [], + CustomData: CustomData || {} + }); + } + }); + + client.on('disconnect', function () { + const userIndex = users.findIndex(user => user.userId === userId && user.chatId === chatId); + if (users[userIndex]) { + users[userIndex].online = false; + if (users[userIndex].active) { + users[userIndex].unactiveTimeout = setTimeout(() => { + users[userIndex].active = false; + }, 60000); + if (!users[userIndex].banned) { + sendTelegramMessage(chatId, '`' + userId + '` has left 🏃💨', 'Markdown', true); + } + } + } + }); + }); }); -function sendTelegramMessage(chatId, text, parseMode) { - request - .post('https://api.telegram.org/bot' + process.env.TELEGRAM_TOKEN + '/sendMessage') - .form({ - "chat_id": chatId, - "text": text, - "parse_mode": parseMode - }); +function sendTelegramMessage(chatId, text, parseMode, disableNotification, inlineKeyboard) { + const options = { + 'chat_id': chatId, + 'text': text, + 'parse_mode': parseMode, + 'disable_notification': !!disableNotification, + }; + + if (inlineKeyboard) { + options.reply_markup = JSON.stringify({ + inline_keyboard: inlineKeyboard, + }); + } + + request + .post('https://api.telegram.org/bot' + process.env.TELEGRAM_TOKEN + '/sendMessage') + .form(options) + .on('response', function (response) { + console.log('telegram status code:', response.statusCode); + }); } -app.post('/usage-start', cors(), function(req, res) { - console.log('usage from', req.query.host); - res.statusCode = 200; - res.end(); +app.post('/usage-start', function (req, res) { + const chatId = parseInt(req.body.chatId); + const host = req.body.host; + + let chat = chats.find(chat => chat.chatId === chatId); + if (!chat) { + chat = { + chatId: chatId, + online: defaultOnlineState + }; + chats.push(chat) + } + + console.log('usage chat ' + chatId + ' (' + (chat.online ? 'online' : 'offline') + ') from ' + host); + res.statusCode = 200; + res.json({ + online: chat.online + }); }); // left here until the cache expires -app.post('/usage-end', cors(), function(req, res) { - res.statusCode = 200; - res.end(); +app.post('/usage-end', function (req, res) { + res.statusCode = 200; + res.end(); +}); + +app.get('/status', function (req, res) { + const currentTime = new Date().toISOString(); + res.statusCode = 200; + res.send({ + status: 'ok', + pingTime: currentTime + }); + console.log({ + status: 'ok', + pingTime: currentTime + }) +}); + +app.get('/', function (req, res) { + res.redirect((process.env.REDIRECT_URL || 'https://kintoyyy.github.io/Telegram-Chat-Widget')) }); -http.listen(process.env.PORT || 3000, function(){ - console.log('listening on port:' + (process.env.PORT || 3000)); +http.listen(process.env.PORT || 3000, function () { + console.log('listening on port:' + (process.env.PORT || 3000)); }); -app.get("/.well-known/acme-challenge/:content", (req, res) => { - res.send(process.env.CERTBOT_RESPONSE); +app.get('/.well-known/acme-challenge/:content', (req, res) => { + res.send(process.env.CERTBOT_RESPONSE); }); diff --git a/src/chat/chat-index.js b/src/chat/chat-index.js index 63868c8..9fe1ec1 100644 --- a/src/chat/chat-index.js +++ b/src/chat/chat-index.js @@ -1,9 +1,21 @@ import { h, render } from 'preact'; import Chat from './chat'; import * as store from 'store' +import { hri } from 'human-readable-ids'; let conf = {}; +let CustomData = {}; const confString = getUrlParameter('conf'); +const CustomDataString = getUrlParameter('CustomData'); + +if (CustomDataString) { + try { + CustomData = JSON.parse(CustomDataString); + } catch (e) { + console.log('Failed to parse conf', CustomDataString, e); + } +} + if (confString) { try { conf = JSON.parse(confString); @@ -16,6 +28,7 @@ render( , @@ -29,14 +42,31 @@ function getUrlParameter(name) { return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' ')); } -function getUserId () { +function generateGuestUsername() { + return 'Guest-' + Math.random().toString(36).substr(2, 6); +} + +function getUserId() { + if (store.enabled) { - return store.get('userId') || store.set('userId', generateRandomId()); + const userId = store.get('userId'); + + if (CustomData && CustomData.username && CustomData.username !== userId) { + store.set('userId', CustomData.username); + return CustomData.username; + } + + if (!userId) { + const generatedUserId = conf.humanReadableIds ? hri.random() : generateGuestUsername(); + store.set('userId', generatedUserId); + return generatedUserId; + } + + return userId; } else { - return generateRandomId(); + return CustomData.username || (conf.humanReadableIds ? hri.random() : generateGuestUsername()); } + + } -function generateRandomId() { - return Math.random().toString(36).substr(2, 6); -} \ No newline at end of file diff --git a/src/chat/chat.js b/src/chat/chat.js index b032ab0..4316f8d 100644 --- a/src/chat/chat.js +++ b/src/chat/chat.js @@ -1,12 +1,11 @@ -import * as store from 'store' -import io from 'socket.io-client' +import * as store from 'store'; +import io from 'socket.io-client'; import { h, Component } from 'preact'; import MessageArea from './message-area'; export default class Chat extends Component { - - autoResponseState = 'pristine'; // pristine, set or canceled + autoResponseState = 'pristine'; // pristine, set, or canceled autoResponseTimer = 0; constructor(props) { @@ -17,55 +16,98 @@ export default class Chat extends Component { } else { this.state.messages = []; } + this.state.isMobile = this.isMobileDevice(); } componentDidMount() { this.socket = io.connect(); this.socket.on('connect', () => { - this.socket.emit('register', {chatId: this.props.chatId, userId: this.props.userId }); + this.socket.emit('register', { chatId: this.props.chatId, userId: this.props.userId, CustomData: this.props.CustomData, helpMsg: this.props.conf.helpMessage }); }); this.socket.on(this.props.chatId, this.incomingMessage); - this.socket.on(this.props.chatId+'-'+this.props.userId, this.incomingMessage); + this.socket.on(this.props.chatId + '-' + this.props.userId, this.incomingMessage); if (!this.state.messages.length) { - this.writeToMessages({text: this.props.conf.introMessage, from: 'admin'}); + this.writeToMessages({ text: this.props.conf.introMessage, from: 'admin' }); } } - render({},state) { - return ( -
- + isMobileDevice() { + return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); + } - { this.input = input }} - onKeyPress={this.handleKeyPress}/> + render({ }, state) { + const inputEvent = state.isMobile ? null : this.handleKeyPress; - + return ( +
+ + {state.isMobile && this.props.conf.displayBanner ? ( + + ) : null} +
+