From fc89b5d558163c099dca50286ced91e98e3ca10b Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 9 Jul 2023 01:23:34 -0500 Subject: [PATCH 01/19] Updates for Python 3 Upgrades code to be Python 3 compatible. Initial port using 2to3, then incremental pass over the code to correct issues discovered with failing unit tests. Also brought all Python requirements up to their latest version. Moved a copy of torndb (no long maintained) into the repository with a few updates for Python 3. Plan is to release this against a Ubutnu 22.04 LTS host and base Docker image for web/worker instances. This update also uses the latest nginx server, version 1.25.1. --- .buildkite/docker-compose.yml | 2 +- .gitignore | 1 + Dockerfile | 20 +- Makefile | 14 +- README.md | 8 +- developerdocs/conf.py | 12 +- handlers/__init__.py | 38 ++-- handlers/account.py | 14 +- handlers/admin.py | 2 +- handlers/api.py | 12 +- handlers/base.py | 2 +- handlers/conversations.py | 2 +- handlers/developers.py | 2 +- handlers/error.py | 2 +- handlers/friends.py | 2 +- handlers/home.py | 6 +- handlers/image.py | 12 +- handlers/incoming.py | 2 +- handlers/misc.py | 2 +- handlers/popular.py | 2 +- handlers/search.py | 2 +- handlers/shake.py | 34 +-- handlers/stripe_hooks.py | 6 +- handlers/tag.py | 2 +- handlers/tools.py | 83 ++++---- handlers/upload.py | 18 +- lib/db/__init__.py | 0 lib/feathers/__init__.py | 20 +- lib/flyingcow/__init__.py | 6 +- lib/flyingcow/model.py | 13 +- lib/uimodules.py | 2 +- lib/utilities.py | 23 ++- main.py | 23 ++- migrate.py | 4 +- mltshpoptions.py | 2 +- models/__init__.py | 68 +++--- models/accesstoken.py | 4 +- models/app.py | 8 +- models/bookmark.py | 27 +-- models/comment.py | 12 +- models/comment_like.py | 2 +- models/conversation.py | 4 +- models/externalservice.py | 2 +- models/favorite.py | 2 +- models/invitation.py | 6 +- models/notification.py | 16 +- models/shake.py | 14 +- models/sharedfile.py | 20 +- models/sourcefile.py | 32 ++- models/subscription.py | 6 +- models/tag.py | 2 +- models/user.py | 45 ++-- models/voucher.py | 4 +- requirements-test.txt | 4 +- requirements.txt | 44 ++-- runner.py | 2 +- scripts/make-zip-of-images.py | 5 +- scripts/migrate-user.py | 4 +- scripts/populate-save-like.py | 4 +- scripts/populate-timelines.py | 2 +- scripts/transcode-sharedfile.py | 8 +- setup/dev/supervisord.conf | 2 +- setup/production/mysql.conf | 18 ++ setup/production/supervisord-web.conf | 2 +- setup/production/supervisord-worker.conf | 6 +- static/developers/example.py.txt | 8 +- tasks/__init__.py | 7 +- tasks/counts.py | 32 +-- tasks/transcode.py | 3 + templates/account/settings-profile.html | 4 +- templates/account/settings.html | 2 +- templates/home/index.html | 2 +- templates/tools/picker-error.html | 2 +- test.py | 4 +- test/AccountTests.py | 201 +++++++----------- test/CommentTests.py | 37 +--- test/ExternalAccountTests.py | 217 ++++++++++---------- test/FileTests.py | 201 +++++++----------- test/SimpleTests.py | 17 +- test/SiteFunctionTests.py | 54 ++--- test/base.py | 53 ++--- test/factories.py | 1 + test/functional/account_settings_tests.py | 2 +- test/functional/account_tests.py | 6 +- test/functional/api_tests.py | 196 +++++------------- test/functional/comment_favor_tests.py | 8 +- test/functional/conversations_tests.py | 69 ++----- test/functional/create_account_tests.py | 48 ++--- test/functional/home_tests.py | 39 ++-- test/functional/image_like_tests.py | 2 +- test/functional/image_save_tests.py | 2 +- test/functional/payments_tests.py | 6 +- test/functional/readonly_tests.py | 8 +- test/functional/request_invitation_tests.py | 4 +- test/functional/shake_crud_tests.py | 8 +- test/functional/tag_tests.py | 4 +- test/functional/verify_email_tests.py | 2 +- test/functional/voucher_tests.py | 48 ++--- test/unit/apihit_tests.py | 2 +- test/unit/base.py | 2 +- test/unit/bookmark_tests.py | 2 +- test/unit/comment_tests.py | 2 +- test/unit/conversation_tests.py | 2 +- test/unit/external_relationship_tests.py | 2 +- test/unit/externalservice_tests.py | 2 +- test/unit/fileview_tests.py | 2 +- test/unit/notification_tests.py | 2 +- test/unit/script_log_tests.py | 2 +- test/unit/shake_tests.py | 2 +- test/unit/sharedfile_tests.py | 2 +- test/unit/sourcefile_tests.py | 2 +- test/unit/task_tests.py | 36 +--- test/unit/user_tests.py | 2 +- 113 files changed, 930 insertions(+), 1201 deletions(-) create mode 100644 lib/db/__init__.py create mode 100644 setup/production/mysql.conf diff --git a/.buildkite/docker-compose.yml b/.buildkite/docker-compose.yml index a7da3cb5..fcb7821b 100644 --- a/.buildkite/docker-compose.yml +++ b/.buildkite/docker-compose.yml @@ -19,7 +19,7 @@ services: aliases: - mltshp.dev mysql: - image: mysql:5 + image: mysql:latest ports: - "3306:3306" environment: diff --git a/.gitignore b/.gitignore index 1934add2..5b8b75f1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ pip-log.txt .DS_Store *.swp env +env3 mounts .env .deploy.env diff --git a/Dockerfile b/Dockerfile index 11c4924b..4440df02 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM ubuntu:22.04 LABEL maintainer "brad@bradchoate.com" ENV PYTHONUNBUFFERED 1 @@ -9,7 +9,7 @@ RUN apt-get -y update && apt-get install -y \ supervisor \ libmysqlclient-dev \ mysql-client \ - python-dev \ + python3-dev \ libjpeg-dev \ libcurl4-openssl-dev \ curl \ @@ -20,20 +20,14 @@ RUN apt-get -y update && apt-get install -y \ libpcre3-dev \ libssl-dev \ libffi-dev \ - python-pip && \ + python3-pip && \ rm -rf /var/lib/apt/lists/* && \ - \ - pip install -U 'pip==20.3.4' 'setuptools==44.0.0' distribute && \ - # fixes a weird issue where distribute complains about setuptools "0.7" - # (incorrectly matching version "20.7.0" which ubuntu 16.04 has preinstalled) - rm -rf /usr/lib/python2.7/dist-packages/setuptools-20.7.0.egg-info && \ - \ # install nginx + upload module mkdir -p /tmp/install && \ cd /tmp/install && \ - wget http://nginx.org/download/nginx-0.8.55.tar.gz && tar zxf nginx-0.8.55.tar.gz && \ - wget https://github.com/fdintino/nginx-upload-module/archive/2.2.0.tar.gz && tar zxf 2.2.0.tar.gz && \ - cd /tmp/install/nginx-0.8.55 && \ + wget http://nginx.org/download/nginx-1.25.1.tar.gz && tar zxf nginx-1.25.1.tar.gz && \ + wget https://github.com/fdintino/nginx-upload-module/archive/2.3.0.tar.gz && tar zxf 2.3.0.tar.gz && \ + cd /tmp/install/nginx-1.25.1 && \ ./configure \ --with-http_ssl_module \ --with-http_stub_status_module \ @@ -43,7 +37,7 @@ RUN apt-get -y update && apt-get install -y \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/srv/mltshp.com/nginx-error.log \ --http-log-path=/srv/mltshp.com/nginx-access.log \ - --add-module=/tmp/install/nginx-upload-module-2.2.0 && \ + --add-module=/tmp/install/nginx-upload-module-2.3.0 && \ make && make install && \ mkdir -p /etc/nginx && \ rm -rf /tmp/install && \ diff --git a/Makefile b/Makefile index f6a5d613..772c4c0b 100644 --- a/Makefile +++ b/Makefile @@ -6,26 +6,26 @@ init-dev: mkdir -p mounts/mysql mounts/logs mounts/fakes3 mounts/uploaded run: - docker-compose --env-file .env up -d + docker compose --env-file .env up -d stop: - docker-compose --env-file .env down + docker compose --env-file .env down build: docker build -t mltshp/mltshp-web:latest . shell: - docker-compose --env-file .env exec mltshp bash + docker compose --env-file .env exec mltshp bash test: - docker-compose --env-file .env exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; python test.py $(TEST)" + docker compose --env-file .env exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; python3 test.py $(TEST)" destroy: - docker-compose down + docker compose down rm -rf mounts migrate: - docker-compose --env-file .env exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; python migrate.py" + docker compose --env-file .env exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; python3 migrate.py" mysql: - docker-compose --env-file .env exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; mysql -u root --host mysql mltshp" + docker compose --env-file .env exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; mysql -u root --host mysql mltshp" diff --git a/README.md b/README.md index 5429c637..bc8b5169 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,12 @@ [![Build status](https://badge.buildkite.com/a86854c6272f21c9b46b8b6aafd3a4fb99bcfabe6e611bc370.svg)](https://buildkite.com/mltshp-inc/mltshp-web-service) [![Coverage Status](https://coveralls.io/repos/github/MLTSHP/mltshp/badge.svg?branch=master)](https://coveralls.io/github/MLTSHP/mltshp?branch=master) +## Project Description + +This project is the codebase for running [mltshp.com](https://mltshp.com). +It's a Python 3 application, utilizing a MySQL database, Amazon S3 for +asset storage, and RabbitMQ for background jobs. + ## Development Environment MLTSHP is a Dockerized application. This greatly simplifies running the @@ -155,6 +161,6 @@ update from the pattern library. ## About -MLTSHP is open-source software, ©2017 the MLTSHP team and released to the public under the terms of the Mozilla Public License. A copy of the MPL can be found in the LICENSE file. +MLTSHP is open-source software, ©2023 the MLTSHP team and released to the public under the terms of the Mozilla Public License. A copy of the MPL can be found in the LICENSE file. [![Fastly logo](/static/images/fastly-logo.png)](https://www.fastly.com) MLTSHP is proudly powered by Fastly. diff --git a/developerdocs/conf.py b/developerdocs/conf.py index cb5fd0f2..784218b1 100644 --- a/developerdocs/conf.py +++ b/developerdocs/conf.py @@ -40,8 +40,8 @@ master_doc = 'docindex' # General information about the project. -project = u'mltshp' -copyright = u'2017 MLTSHP, Inc.' +project = 'mltshp' +copyright = '2023 MLTSHP, Inc.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -180,8 +180,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('docindex', 'mltshp-api.tex', u'mltshp-api Documentation', - u'MLTSHP, LLC', 'manual'), + ('docindex', 'mltshp-api.tex', 'mltshp-api Documentation', + 'MLTSHP, LLC', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -213,6 +213,6 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('docindex', 'mltshp-api', u'mltshp-api Documentation', - [u'MLTSHP, LLC'], 1) + ('docindex', 'mltshp-api', 'mltshp-api Documentation', + ['MLTSHP, LLC'], 1) ] diff --git a/handlers/__init__.py b/handlers/__init__.py index 0f5f0efc..43e743c5 100644 --- a/handlers/__init__.py +++ b/handlers/__init__.py @@ -1,20 +1,20 @@ #handlers -import base -import account -import admin -import image -import home -import misc -import tools -import upload -import incoming -import friends -import api -import developers -import conversations -import error -import popular -import shake -import tag -import stripe_hooks -import search +from . import base +from . import account +from . import admin +from . import image +from . import home +from . import misc +from . import tools +from . import upload +from . import incoming +from . import friends +from . import api +from . import developers +from . import conversations +from . import error +from . import popular +from . import shake +from . import tag +from . import stripe_hooks +from . import search diff --git a/handlers/account.py b/handlers/account.py index 19079d2c..4cb7e348 100644 --- a/handlers/account.py +++ b/handlers/account.py @@ -1,7 +1,7 @@ import datetime import re import json -from urllib import urlencode +from urllib.parse import urlencode import logging import tornado.httpclient @@ -12,7 +12,7 @@ import postmark import requests -from base import BaseHandler, require_membership +from .base import BaseHandler, require_membership from models import User, Invitation, Shake, Notification, Conversation, Invitation,\ App, PaymentLog, Voucher, Promotion, MigrationState from lib.utilities import email_re, base36decode, is_valid_voucher_key,\ @@ -348,9 +348,9 @@ def post(self, app_id=None): user = self.get_current_user_object() app = App.get("id = %s", app_id) if not app: - return {'error' : 'Invalid request.'} + return self.write({'error' : 'Invalid request.'}) app.disconnect_for_user(user) - return {'result' : 'ok'} + return self.write({'result' : 'ok'}) class ForgotPasswordHandler(BaseHandler): @@ -843,7 +843,7 @@ def get(self, page=None): """ current_user_obj = self.get_current_user_object() - if self.get_arguments('agree', None): + if self.get_argument('agree', None): current_user_obj.tou_agreed = True current_user_obj.save() return self.redirect("/") @@ -929,7 +929,7 @@ def post(self): if plan_id == "mltshp-double": quantity = int(float(self.get_argument("quantity"))) if quantity < 24 or quantity > 500: - raise "Invalid request" + raise Exception("Invalid request") customer = None sub = None @@ -946,7 +946,7 @@ def post(self): if customer is None: if token_id is None: # FIXME: handle this more gracefully... - raise "Invalid request" + raise Exception("Invalid request") # create a new customer object for this subscription customer = stripe.Customer.create( diff --git a/handlers/admin.py b/handlers/admin.py index e45661a5..959e4d13 100644 --- a/handlers/admin.py +++ b/handlers/admin.py @@ -4,7 +4,7 @@ import tornado.web import postmark -from base import BaseHandler +from .base import BaseHandler from models import Sharedfile, User, Shake, Shakesharedfile, Invitation, Waitlist, ShakeCategory from lib.utilities import send_slack_notification diff --git a/handlers/api.py b/handlers/api.py index 00162af7..37c13c3f 100644 --- a/handlers/api.py +++ b/handlers/api.py @@ -1,16 +1,16 @@ from datetime import datetime import time -from urllib import urlencode -from urlparse import urlparse, urlunparse, urljoin +from urllib.parse import urlencode +from urllib.parse import urlparse, urlunparse from hashlib import sha1 import hmac import base64 import functools import tornado.web -from tornado.options import define, options +from tornado.options import options -from base import BaseHandler +from .base import BaseHandler from lib.utilities import normalize_string, base36decode from models import Accesstoken, Apihit, Apilog, App, Authorizationcode, \ Favorite, Magicfile, Sharedfile, User, Shake, Comment @@ -102,8 +102,8 @@ def wrapper(self, *args, **kwargs): parsed_url.path, query_array) - digest = hmac.new(access_token.consumer_secret.encode('ascii'), normalized_string, sha1).digest() - signature = base64.encodestring(digest).strip() + digest = hmac.new(access_token.consumer_secret.encode('ascii'), normalized_string.encode('ascii'), sha1).digest() + signature = base64.encodebytes(digest).strip().decode('ascii') if signature == auth_items['signature']: self.oauth2_user_id = access_token.user_id diff --git a/handlers/base.py b/handlers/base.py index c500e62e..a8ec0181 100644 --- a/handlers/base.py +++ b/handlers/base.py @@ -103,7 +103,7 @@ def add_error(self, key, message): self._errors[key] = message def add_errors(self, errors_dict): - for error_key in errors_dict.keys(): + for error_key in list(errors_dict.keys()): self.add_error(error_key, errors_dict[error_key]) def log_user_in(self, user): diff --git a/handlers/conversations.py b/handlers/conversations.py index f5f5abde..ed56d648 100644 --- a/handlers/conversations.py +++ b/handlers/conversations.py @@ -1,6 +1,6 @@ import tornado.web -from base import BaseHandler, require_membership +from .base import BaseHandler, require_membership from models import Conversation, Notification, Comment, Sharedfile class IndexHandler(BaseHandler): diff --git a/handlers/developers.py b/handlers/developers.py index b1e02944..88cc5875 100644 --- a/handlers/developers.py +++ b/handlers/developers.py @@ -1,6 +1,6 @@ import tornado.web -from base import BaseHandler +from .base import BaseHandler from models import App diff --git a/handlers/error.py b/handlers/error.py index a98f2eee..29d22a38 100644 --- a/handlers/error.py +++ b/handlers/error.py @@ -1,5 +1,5 @@ import tornado -import base +from . import base class NotFoundHandler(base.BaseHandler): def check_xsrf_cookie(self): diff --git a/handlers/friends.py b/handlers/friends.py index d7d6d8b7..dc128718 100644 --- a/handlers/friends.py +++ b/handlers/friends.py @@ -1,5 +1,5 @@ import tornado.web -from base import BaseHandler, require_membership +from .base import BaseHandler, require_membership class FriendHandler(BaseHandler): diff --git a/handlers/home.py b/handlers/home.py index 5006f56c..ae7907c4 100644 --- a/handlers/home.py +++ b/handlers/home.py @@ -1,6 +1,4 @@ -import hashlib - -from base import BaseHandler +from .base import BaseHandler import models import lib.utilities @@ -35,7 +33,7 @@ def get(self, before_or_after=None, base36_id=None, returning=None): # We're going to older, so ony use before_id. if before_id: notifications_count = models.Notification.for_user_count(current_user_obj) - sharedfiles = current_user_obj.sharedfiles_from_subscriptions(before_id=before_id,per_page=11) + sharedfiles = current_user_obj.sharedfiles_from_subscriptions(before_id=before_id, per_page=11) # we have nothing on this page, redirect to home page with out params. if len(sharedfiles) == 0: return self.redirect('/') diff --git a/handlers/image.py b/handlers/image.py index 2ccf9165..fe9d5d6c 100644 --- a/handlers/image.py +++ b/handlers/image.py @@ -1,6 +1,6 @@ import tempfile import re -from urlparse import urlparse +from urllib.parse import urlparse import time from datetime import datetime, timedelta import tornado.web @@ -8,7 +8,7 @@ from tornado.escape import json_encode from tornado.options import options -from base import BaseHandler, require_membership +from .base import BaseHandler, require_membership from models import Favorite, User, Sharedfile, Sourcefile, Comment, Shake, Externalservice import models from lib.utilities import s3_authenticated_url, uses_a_banned_phrase @@ -35,7 +35,7 @@ def post(self, share_key): if not current_user: raise tornado.web.HTTPError(403) - json = self.get_arguments('json', False) + json = self.get_argument('json', False) if not sharedfile.can_save(current_user): if json: return self.write({'error' : "Can't save that file."}) @@ -148,7 +148,7 @@ class ShowLikesHandler(BaseHandler): def get(self, share_key): sharedfile = Sharedfile.get_by_share_key(share_key) if not sharedfile: - return {'error': 'Invalid file key.'} + return self.write({'error': 'Invalid file key.'}) response_data = [] for sharedfile in sharedfile.favorites(): @@ -169,7 +169,7 @@ class ShowSavesHandler(BaseHandler): def get(self, share_key): sharedfile = Sharedfile.get_by_share_key(share_key) if not sharedfile: - return {'error': 'Invalid file key.'} + return self.write({'error': 'Invalid file key.'}) response_data = [] for sharedfile in sharedfile.saves(): @@ -353,7 +353,7 @@ def post(self, share_key): raise tornado.web.HTTPError(404) if current_user.id != sharedfile.user_id: raise tornado.web.HTTPError(403) - shakes = self.get_arguments('shakes', []) + shakes = self.get_arguments('shakes') for shake_id in shakes: shake = Shake.get("id = %s", shake_id) if shake.can_update(current_user.id): diff --git a/handlers/incoming.py b/handlers/incoming.py index 58545c1c..7269fafa 100644 --- a/handlers/incoming.py +++ b/handlers/incoming.py @@ -1,7 +1,7 @@ from lib.utilities import base36decode import tornado.web -from base import BaseHandler, require_membership +from .base import BaseHandler, require_membership from models import User, Sharedfile, notification diff --git a/handlers/misc.py b/handlers/misc.py index e483c274..dc617038 100644 --- a/handlers/misc.py +++ b/handlers/misc.py @@ -1,4 +1,4 @@ -from base import BaseHandler +from .base import BaseHandler import postmark diff --git a/handlers/popular.py b/handlers/popular.py index 2f844d8f..bfe9d86a 100644 --- a/handlers/popular.py +++ b/handlers/popular.py @@ -3,7 +3,7 @@ import tornado.web from tornado.options import options -from base import BaseHandler, require_membership +from .base import BaseHandler, require_membership from models import sharedfile, notification, user diff --git a/handlers/search.py b/handlers/search.py index 331540fe..a7906741 100644 --- a/handlers/search.py +++ b/handlers/search.py @@ -3,7 +3,7 @@ import tornado.web from tornado import escape from tornado.options import options -from base import BaseHandler, require_membership +from .base import BaseHandler, require_membership import lib.utilities from models import sharedfile, user diff --git a/handlers/shake.py b/handlers/shake.py index 47087ad1..a426fc6d 100644 --- a/handlers/shake.py +++ b/handlers/shake.py @@ -4,7 +4,7 @@ import torndb from tornado.options import options -from base import BaseHandler, require_membership +from .base import BaseHandler, require_membership from models import Shake, User, Notification, ShakeManager, MigrationState from lib.utilities import base36decode @@ -101,6 +101,10 @@ def get(self, shake_name=None, before_or_after=None, base36_id=None): older_link=older_link,newer_link=newer_link, shake_editor=shake.owner(), managers=managers, is_shake_manager=is_shake_manager, followers=followers[:10], + base36_id=base36_id, + sharedfile_id=sharedfile_id, + since_id=since_id, + max_id=max_id, follower_count=follower_count) @@ -137,7 +141,7 @@ def post(self): new_shake = Shake(name=name, title=title, description=description, user_id=user_object.id, type='group') try: if new_shake.save(): - return self.redirect('/%s' % (new_shake.name)) + return self.redirect('/%s' % new_shake.name) except torndb.IntegrityError: # This is a rare edge case, so we handle it lazily -- IK. pass @@ -187,7 +191,7 @@ def post(self, shake_name): shake_to_update.description = new_description shake_to_update.save() - return self.redirect('/shake/' + shake_to_update.name + '/quick-details') + return self.redirect('/shake/%s/quick-details' % shake_to_update.name) class UpdateShakeHandler(BaseHandler): @@ -234,7 +238,7 @@ def post(self, shake_name=None): if json: return self.write({'ok':'Saved it!'}) else: - return self.redirect("/%s" % (shake_name)) + return self.redirect("/%s" % shake_name) class SubscribeShakeHandler(BaseHandler): @@ -313,13 +317,13 @@ def post(self, shake_name): if is_json: return self.write({'error':'error'}) else: - return self.redirect('/%s' % (shake_name)) + return self.redirect('/%s' % shake_name) Notification.new_invitation(sender, receiver, shake.id) if is_json: return self.write({'invitation_status':True}) else: - return self.redirect('/%s' % (shake_name)) + return self.redirect('/%s' % shake_name) class AcceptInvitationHandler(BaseHandler): @@ -368,7 +372,7 @@ def post(self, shake_name): return self.write({'response' : 'ok', 'count' : remaining_notifications_count}) else: - return self.redirect("/%s", shake_object.name) + return self.redirect("/%s" % shake_object.name) class RequestInvitationHandler(BaseHandler): @@ -389,7 +393,7 @@ def post(self, shake_name=None): if self.get_argument('json', None): return self.write({'status':'ok'}) else: - return self.redirect('/%s' % (shake.name)) + return self.redirect('/%s' % shake.name) else: if self.get_argument('json', None): return self.write({'status':'error', 'message':'not allowed'}) @@ -415,7 +419,7 @@ def post(self, shake_name=None): if self.get_argument('json', None): return self.write({'status':'error'}) else: - return self.redirect('/%s' % (shake.name)) + return self.redirect('/%s' % shake.name) no = Notification.get('sender_id = %s and receiver_id = %s and action_id = %s and deleted = 0', requestor.id, current_user_object.id, shake.id) @@ -423,7 +427,7 @@ def post(self, shake_name=None): if self.get_argument('json', None): return self.write({'status':'error'}) else: - return self.redirect('/%s' % (shake.name)) + return self.redirect('/%s' % shake.name) if shake.add_manager(user_to_add=requestor): no.delete() @@ -432,12 +436,12 @@ def post(self, shake_name=None): if self.get_argument('json', None): return self.write({'status':'ok', 'count' : Notification.count_for_user_by_type(current_user_object.id, 'invitation_request')}) else: - return self.redirect('/%s' % (shake.name)) + return self.redirect('/%s' % shake.name) else: if self.get_argument('json', None): return self.write({'status':'error'}) else: - return self.redirect('/%s' % (shake.name)) + return self.redirect('/%s' % shake.name) class DeclineInvitationHandler(BaseHandler): @@ -458,7 +462,7 @@ def post(self, shake_name=None): if self.get_argument('json', None): return self.write({'status':'error'}) else: - return self.redirect('/%s', shake.name) + return self.redirect('/%s' % shake.name) no = Notification.get('sender_id = %s and receiver_id = %s and action_id = %s and deleted = 0', requestor.id, current_user_object.id, shake.id) @@ -466,14 +470,14 @@ def post(self, shake_name=None): if self.get_argument('json', None): return self.write({'status':'error'}) else: - return self.redirect('/%s' % (shake.name)) + return self.redirect('/%s' % shake.name) no.delete() if self.get_argument('json', None): return self.write({'status':'ok', 'count' : Notification.count_for_user_by_type(current_user_object.id, 'invitation_request')}) else: - return self.redirect('/%s' % (shake.name)) + return self.redirect('/%s' % shake.name) class RSSFeedHandler(BaseHandler): diff --git a/handlers/stripe_hooks.py b/handlers/stripe_hooks.py index 8bd0044b..116b06ea 100644 --- a/handlers/stripe_hooks.py +++ b/handlers/stripe_hooks.py @@ -2,12 +2,13 @@ from tornado.options import options -from base import BaseHandler +from .base import BaseHandler from models import User, PaymentLog import json import postmark import stripe +import stripe.util class StripeWebhook(BaseHandler): @@ -17,7 +18,6 @@ def check_xsrf_cookie(self): def post(self): # type of message is passed through "type" parameter json_response = json.loads(self.request.body) - body_str = json.dumps(json_response).replace("\n","\\n") stripe_customer_id = None period_start = None @@ -30,7 +30,7 @@ def post(self): amount = 0 operation = None - evt = stripe.convert_to_stripe_object(json_response, + evt = stripe.util.convert_to_stripe_object(json_response, options.stripe_secret_key, None) if evt.type == 'invoice.payment_failed': diff --git a/handlers/tag.py b/handlers/tag.py index 578e252d..da74b826 100644 --- a/handlers/tag.py +++ b/handlers/tag.py @@ -2,7 +2,7 @@ from tornado.escape import json_encode from tornado import escape -from base import BaseHandler, require_membership +from .base import BaseHandler, require_membership from models import Tag, TaggedFile from lib.utilities import base36decode diff --git a/handlers/tools.py b/handlers/tools.py index 8d0e1cd7..2d9616e2 100644 --- a/handlers/tools.py +++ b/handlers/tools.py @@ -1,21 +1,17 @@ -from urlparse import urlparse, parse_qs +from urllib.parse import urlparse, parse_qs import os import re import random -import json from tornado.httpclient import HTTPRequest import tornado.auth import tornado.web -from tornado.escape import url_escape, json_decode -from tornado.options import define, options +from tornado.escape import json_decode +from tornado.options import options -from BeautifulSoup import BeautifulSoup - -from models import Externalservice, User, Sourcefile, Sharedfile, Shake, ExternalRelationship, ShakeCategory -from base import BaseHandler, require_membership +from models import Externalservice, User, Sourcefile, Sharedfile, Shake, ShakeCategory +from .base import BaseHandler, require_membership from lib.utilities import base36encode -import lib.feathers class PickerPopupHandler(BaseHandler): @@ -79,9 +75,8 @@ def get(self): can_upload_this_month=can_upload_this_month) @tornado.web.authenticated - @tornado.web.asynchronous @require_membership - def post(self): + async def post(self): """ TODO: better determination of correct file name, if it is indeed a file, plus type. """ @@ -96,17 +91,10 @@ def post(self): http = tornado.httpclient.AsyncHTTPClient() - #this sends a header value for cookie to d/l protected FP files - fp_cookie = None - b = re.compile(r"^http(s?)://(.*?)(.?)filepile\.org") - m = b.match(self.url) - if m: - for char in [' ', '[', ']']: - self.url = self.url.replace(char, url_escape(char)) - - fp_cookie = {'Cookie':'_filepile_session=4c2eff30dd27e679d38fbc030b204488'} - request = HTTPRequest(self.url, headers=fp_cookie, header_callback=self.on_header) - http.fetch(request, self.on_response) + request = HTTPRequest(self.url, header_callback=self.on_header) + fut = http.fetch(request) + response = await fut + self.on_response(response) def on_response(self, response): url_parts = urlparse(response.request.url) @@ -120,13 +108,13 @@ def on_response(self, response): title = None if self.content_type not in self.approved_content_types: - if response.body[0:50].find('JFIF') > -1: + if response.body[0:50].find(b'JFIF') > -1: self.content_type = 'image/jpeg' else: - return self.render("tools/picker-error.html") + return self.render("tools/picker-error.html", error="Invalid file type: %s" % self.content_type) if len(file_name) == 0: - return self.render("tools/picker-error.html") + return self.render("tools/picker-error.html", error="file_name is empty") sha1_file_key = Sourcefile.get_sha1_file_key(file_data=response.body) user = self.get_current_user() @@ -157,12 +145,12 @@ def on_response(self, response): self.render("tools/picker-success.html", sf=sf) def on_header(self, header): - if header.startswith("Content-Length:"): - content_length = re.search("Content-Length: (.*)", header) + if header.lower().startswith("content-length:"): + content_length = re.search("content-length: (.*)", header, re.IGNORECASE) if int(content_length.group(1).rstrip()) > 10000000: #this is not hte correct size to error on raise tornado.web.HTTPError(413) - elif header.startswith("Content-Type:"): - ct = re.search("Content-Type: (.*)", header) + elif header.lower().startswith("content-type:"): + ct = re.search("content-type: (.*)", header, re.IGNORECASE) self.content_type = ct.group(1).rstrip() @@ -188,10 +176,9 @@ def get(self): class ToolsTwitterConnectHandler(BaseHandler, tornado.auth.TwitterMixin): - @tornado.web.asynchronous @tornado.web.authenticated @require_membership - def get(self): + async def get(self): if self.get_argument("oauth_token", None): self.get_authenticated_user(self._on_auth) return @@ -250,10 +237,9 @@ def get(self): class SaveVideoHandler(BaseHandler): - @tornado.web.asynchronous @tornado.web.authenticated @require_membership - def get(self): + async def get(self): url = self.get_argument('url', None) shake_id = self.get_argument('shake_id', "") if not url: @@ -262,30 +248,32 @@ def get(self): url = Sourcefile.make_oembed_url(url.strip()) if url: - self.handle_oembed_url(url) + await self.handle_oembed_url(url) else: self.render("tools/save-video-error.html", message="Invalid URL. We didn't recognize that URL") - def on_oembed_response(self, response): + async def on_oembed_response(self, response): if response.code == 401: self.render("tools/save-video-error.html", message="Embedding disabled by request. The user who uploaded this file has requested it not be embedded on other web sites.") return - self.handle_oembed_data(response.body) + await self.handle_oembed_data(response.body) - def handle_oembed_url(self, url): + async def handle_oembed_url(self, url): """Takes a sanitized URL (as created by models.sourcefile.make_oembed_url) and issues a request for it. If the URL is actually a data URI, strip off the well-known header, and handle the oembed JSON encoded into it instead. """ if url.startswith('data:text/json;charset=utf-8,'): j_oembed = url.replace('data:text/json;charset=utf-8,', '', 1) - self.handle_oembed_data(j_oembed) + await self.handle_oembed_data(j_oembed) else: request = HTTPRequest(url, 'GET') http = tornado.httpclient.AsyncHTTPClient() - http.fetch(request,self.on_oembed_response) + fut = http.fetch(request) + response = await fut + await self.on_oembed_response(response) - def handle_oembed_data(self, oembed): + async def handle_oembed_data(self, oembed): try: j_oembed = json_decode(oembed) except Exception as e: @@ -296,7 +284,7 @@ def handle_oembed_data(self, oembed): self.render("tools/save-video-error.html", message="We could not load the embed code for this file. Please contact support.") return - if j_oembed.has_key('type') and j_oembed['provider_name'] == 'Flickr' and j_oembed['type'] != 'video': + if 'type' in j_oembed and j_oembed['provider_name'] == 'Flickr' and j_oembed['type'] != 'video': self.render("tools/save-video-error.html", message="We could not load the embed code for this file. Please contact support.") return @@ -321,7 +309,9 @@ def handle_oembed_data(self, oembed): self.oembed_doc = j_oembed request = HTTPRequest(self.oembed_doc['thumbnail_url'], 'GET') http = tornado.httpclient.AsyncHTTPClient() - http.fetch(request,self.on_thumbnail_response) + fut = http.fetch(request) + response = await fut + self.on_thumbnail_response(response) else: self.render("tools/save-video.html", url=url, html=j_oembed['html'], shake_id=shake_id) @@ -348,7 +338,7 @@ def on_thumbnail_response(self, response): pass title = '' - if self.oembed_doc.has_key('title'): + if 'title' in self.oembed_doc: title = self.oembed_doc['title'] shared_file = Sharedfile(user_id=current_user.id, name=url, content_type='text/html', source_id=source_file.id, title=title, source_url=url) @@ -361,16 +351,15 @@ def on_thumbnail_response(self, response): user_shake = Shake.get('user_id = %s and type=%s', current_user.id, 'user') shared_file.add_to_shake(self.destination_shake) - if self.oembed_doc.has_key('description'): + if 'description' in self.oembed_doc: shared_file.description = self.oembed_doc['description'] self.write({'path' : "/p/%s" % (share_key)}) self.finish() - @tornado.web.asynchronous @tornado.web.authenticated @require_membership - def post(self): + async def post(self): url = self.get_argument('url', None) if not url: self.render("tools/save-video.html", url = url, title = None, description=None) @@ -388,7 +377,7 @@ def post(self): return self.render("tools/save-video-error.html", message="We couldn't save the video to specified shake. Please contact support.") if current_user.email_confirmed != 1: return self.render("tools/save-video-error.html", message="You must confirm your email address before you can post.") - self.handle_oembed_url(url) + await self.handle_oembed_url(url) else: self.render("tools/save-video-error.html", message="We could not load the embed code. The video server may be down. Please contact support.") diff --git a/handlers/upload.py b/handlers/upload.py index c21cc1b3..b9fffc3e 100644 --- a/handlers/upload.py +++ b/handlers/upload.py @@ -1,10 +1,9 @@ -from base import BaseHandler, require_membership +from .base import BaseHandler, require_membership import tornado.httpclient from tornado.httpclient import HTTPRequest import tornado.web -import postmark -from tornado.options import define, options +from tornado.options import options import tornado.escape from models import Sharedfile, Externalservice @@ -15,14 +14,13 @@ class UploadHandler(BaseHandler): """ def check_xsrf_cookie(self): user = self.get_current_user() - if self.request.headers.has_key('X-Verify-Credentials-Authorization') and not user: + if 'X-Verify-Credentials-Authorization' in self.request.headers and not user: return else: super(UploadHandler, self).check_xsrf_cookie() - @tornado.web.asynchronous @require_membership - def post(self): + async def post(self): """ { "file_name": ["before.png"], @@ -64,7 +62,7 @@ def post(self): raise tornado.web.HTTPError(403) else: return self.redirect("/") - elif self.request.headers.has_key('X-Verify-Credentials-Authorization') and self.request.headers.has_key('X-Auth-Service-Provider'): + elif 'X-Verify-Credentials-Authorization' in self.request.headers and 'X-Auth-Service-Provider' in self.request.headers: #pm = postmark.PMMail(api_key=options.postmark_api_key, # sender="hello@mltshp.com", to="notifications@mltshp.com", # subject="TWITTER REQUEST", @@ -73,14 +71,16 @@ def post(self): if self.request.headers['X-Auth-Service-Provider'].startswith("https://api.twitter.com/1.1/account/verify_credentials.json") or self.request.headers['X-Auth-Service-Provider'].startswith("http://localhost:"): http = tornado.httpclient.AsyncHTTPClient() - http.fetch( + fut = http.fetch( HTTPRequest( url=self.request.headers['X-Auth-Service-Provider'], method='GET', headers={'Authorization':self.request.headers['X-Verify-Credentials-Authorization']}, body=None #"asdf=asdf" -- GET requests can't have a body ), - callback=self.on_response) + ) + response = await fut + self.on_response(response) else: raise tornado.web.HTTPError(403) else: diff --git a/lib/db/__init__.py b/lib/db/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/lib/feathers/__init__.py b/lib/feathers/__init__.py index be23e089..9f9f9dc7 100644 --- a/lib/feathers/__init__.py +++ b/lib/feathers/__init__.py @@ -1,6 +1,6 @@ import sys -import urllib -import urlparse +import urllib.request, urllib.parse, urllib.error +import urllib.parse import hmac import hashlib import binascii @@ -71,10 +71,10 @@ def _build_base_url(self, method): return self.endpoint + self.api_version + method + '.' + self.format def _build_url(self, base_url, params): - query = '' if not params else '?' + urllib.urlencode(params) + query = '' if not params else '?' + urllib.parse.urlencode(params) return base_url + query - def _fetch(self, url, headers={}, callback=None): + async def _fetch(self, url, headers={}, callback=None): """ Make the request. If an IOloop is available make request asynchronous and use the passed in callback if it's provided. @@ -82,7 +82,9 @@ def _fetch(self, url, headers={}, callback=None): request = tornado.httpclient.HTTPRequest(url=url, method="GET", headers=headers) if tornado.ioloop.IOLoop.initialized(): http = tornado.httpclient.AsyncHTTPClient() - http.fetch(request, callback) + fut = http.fetch(request) + response = await fut + callback(response) else: http = tornado.httpclient.HTTPClient() return http.fetch(request) @@ -96,7 +98,7 @@ def _oauth_signature(self, consumer_secret, method, url, parameters={}, token_se See http://oauth.net/core/1.0/#signing_process """ - parts = urlparse.urlparse(url) + parts = urllib.parse.urlparse(url) scheme, netloc, path = parts[:3] normalized_url = scheme.lower() + "://" + netloc.lower() + path @@ -111,7 +113,7 @@ def _oauth_signature(self, consumer_secret, method, url, parameters={}, token_se key_elems.append(token_secret if token_secret else "") key = "&".join(key_elems) - hash = hmac.new(key, base_string, hashlib.sha1) + hash = hmac.new(key.encode("ascii"), base_string.encode("ascii"), hashlib.sha1) return binascii.b2a_base64(hash.digest())[:-1] @classmethod @@ -119,6 +121,6 @@ def _oauth_escape(self, val): """ From tornado.auth """ - if isinstance(val, unicode): + if isinstance(val, str): val = val.encode("utf-8") - return urllib.quote(val, safe="~") + return urllib.parse.quote(val, safe="~") diff --git a/lib/flyingcow/__init__.py b/lib/flyingcow/__init__.py index 61c108c5..603a2136 100644 --- a/lib/flyingcow/__init__.py +++ b/lib/flyingcow/__init__.py @@ -1,3 +1,3 @@ -from db import register_connection, connection -from model import Model -from properties import Property \ No newline at end of file +from .db import register_connection, connection +from .model import Model +from .properties import Property \ No newline at end of file diff --git a/lib/flyingcow/model.py b/lib/flyingcow/model.py index 47c78c8d..c8ac2966 100644 --- a/lib/flyingcow/model.py +++ b/lib/flyingcow/model.py @@ -2,9 +2,9 @@ import logging import time -import error -import db -import properties +from . import error +from . import db +from . import properties class MultipleRows(Exception): def __str__(self): @@ -21,17 +21,16 @@ def __new__(cls, name, bases, attrs): return super(BaseModel, cls).__new__(cls, name, bases, attrs) new_class = type.__new__(cls, name, bases, attrs) - for key, value in attrs.items(): + for key, value in list(attrs.items()): if isinstance(value, properties.Property): value.contribute_to_class(new_class, key) new_class.properties() return new_class -class Model(object): +class Model(object, metaclass=BaseModel): """ The Model class that gets inherited to create table-specific Models. """ - __metaclass__ = BaseModel def __init__(self, *args, **kwargs): self._id = None @@ -225,7 +224,7 @@ def properties(cls): return cls._properties_cache cls._properties_cache = [] - for key in cls.__dict__.keys(): + for key in list(cls.__dict__.keys()): if isinstance(cls.__dict__[key], properties.Property): cls._properties_cache.append(key) return cls._properties_cache diff --git a/lib/uimodules.py b/lib/uimodules.py index b6d62588..dcc91e60 100644 --- a/lib/uimodules.py +++ b/lib/uimodules.py @@ -33,7 +33,7 @@ def render(self, object_count=1, current_page=1, url_format='', per_page=10, \ def chunk_pages(self, num_pages=1, current_page=1, adjacent=2): max_pages_display = (adjacent * 2) + 1 + 4 - mylist = range(1,num_pages+1) + mylist = list(range(1,num_pages+1)) if num_pages < max_pages_display: return mylist diff --git a/lib/utilities.py b/lib/utilities.py index 3c74a08c..b30fb703 100644 --- a/lib/utilities.py +++ b/lib/utilities.py @@ -4,9 +4,9 @@ import time import json import base64 -import urllib +import urllib.request, urllib.parse, urllib.error from datetime import datetime -import urllib +import urllib.request, urllib.parse, urllib.error from PIL import Image from tornado.options import options @@ -58,8 +58,8 @@ def s3_authenticated_url(s3_key, s3_secret, bucket_name=None, file_path=None, seconds = int(time.time()) + seconds to_sign = "GET\n\n\n%s\n/%s/%s" % (seconds, bucket_name, file_path) - digest = hmac.new(s3_secret, to_sign, hashlib.sha1).digest() - signature = urllib.quote(base64.encodestring(digest).strip()) + digest = hmac.new(s3_secret.encode("ascii"), to_sign.encode("ascii"), hashlib.sha1).digest() + signature = urllib.parse.quote(base64.b64encode(digest).strip()) signature = "?AWSAccessKeyId=%s&Expires=%s&Signature=%s" % (s3_key, seconds, signature) if options.aws_host == "s3.amazonaws.com": @@ -93,7 +93,7 @@ def s3_url(file_path): def base36encode(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'): - if not isinstance(number, (int, long)): + if not isinstance(number, int): raise TypeError('number must be an integer') # Special case for zero @@ -120,7 +120,7 @@ def base36decode(number): def generate_digest_from_dictionary(values): h = hashlib.sha1() for value in values: - h.update("%s" % (value)) + h.update(("%s" % value).encode("ascii")) return h.hexdigest() @@ -237,12 +237,14 @@ def payment_notifications(user, action, amount=None): def transform_to_square_thumbnail(file_path, size_constraint, destination): """ Takes a local path with a file, and writes the thumbntail to the destination - which is a cStringIO.StringIO instance. Used by User.set_profile_image() + which is a io.BytesIO instance. Used by User.set_profile_image() """ img = Image.open(file_path) # convert to RGB, probably for proper resizing of gifs. if img.mode != "RGB": - img = img.convert("RGB") + img2 = img.convert("RGB") + img.close() + img = img2 width, height = img.size # if image is below size constraint, we just paste it on @@ -261,8 +263,9 @@ def transform_to_square_thumbnail(file_path, size_constraint, destination): max_dimension = width cropped = img.crop((0,0,max_dimension,max_dimension,)) cropped.load() - cropped.thumbnail((size_constraint,size_constraint), Image.ANTIALIAS) + cropped.thumbnail((size_constraint,size_constraint), Image.Resampling.LANCZOS) cropped.save(destination, format="JPEG", quality=95) + img.close() return True email_re = re.compile( @@ -321,4 +324,4 @@ def clean_search_term(s): if s is None: return None - return s.replace(u"\u201C", '"').replace(u"\u201D", '"') + return s.replace("\u201C", '"').replace("\u201D", '"') diff --git a/main.py b/main.py index e1761b2e..26662f0c 100755 --- a/main.py +++ b/main.py @@ -3,12 +3,10 @@ import os.path import sys -import tornado.httpserver -import tornado.ioloop +import asyncio import tornado.web import tornado.options -import torndb -from tornado.options import define, options +from tornado.options import options from tornado.httpclient import AsyncHTTPClient from lib.flyingcow import register_connection @@ -54,21 +52,24 @@ def __init__(self, *args, **settings): super(MltshpApplication, self).__init__(*args, **settings) -if __name__ == "__main__": + +async def main(): mltshpoptions.parse_dictionary(settings) tornado.options.parse_command_line() + app_settings = MltshpApplication.app_settings() + if options.dump_settings: from pprint import pprint pprint({'options': dict((k, opt.value()) - for k, opt in options.iteritems()), + for k, opt in options.items()), 'app_settings': app_settings}) sys.exit(0) - app_settings = MltshpApplication.app_settings() application = MltshpApplication(routes, autoescape=None, **app_settings) - http_server = tornado.httpserver.HTTPServer(application) + print("starting on port %s" % (options.on_port)) + application.listen(int(options.on_port)) + await asyncio.Event().wait() - print "starting on port %s" % (options.on_port) - http_server.listen(int(options.on_port)) - tornado.ioloop.IOLoop.instance().start() +if __name__ == "__main__": + asyncio.run(main()) diff --git a/migrate.py b/migrate.py index b008cd44..8f5c656e 100644 --- a/migrate.py +++ b/migrate.py @@ -13,6 +13,6 @@ (options.database_user, options.database_password, options.database_host, options.database_name)) migrations = read_migrations("migrations") -print "Applying migrations..." +print("Applying migrations...") backend.apply_migrations(backend.to_apply(migrations)) -print "...complete!" +print("...complete!") diff --git a/mltshpoptions.py b/mltshpoptions.py index ee521322..349cbbe2 100644 --- a/mltshpoptions.py +++ b/mltshpoptions.py @@ -2,7 +2,7 @@ def parse_dictionary(settings): - for key, value in settings.iteritems(): + for key, value in settings.items(): if key in options: setattr(options, key, value) diff --git a/models/__init__.py b/models/__init__.py index 86d05732..fdd36fa8 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1,34 +1,34 @@ -from favorite import Favorite -from subscription import Subscription -from user import User -from invitation import Invitation -from sharedfile import Sharedfile -from sourcefile import Sourcefile -from externalservice import Externalservice -from shake import Shake -from shakesharedfile import Shakesharedfile -from waitlist import Waitlist -from comment import Comment -from post import Post -from notification import Notification -from app import App -from authorizationcode import Authorizationcode -from accesstoken import Accesstoken -from apilog import Apilog -from conversation import Conversation -from external_relationship import ExternalRelationship -from shakemanager import ShakeManager -from payment_log import PaymentLog -from bookmark import Bookmark -from apihit import Apihit -from magicfile import Magicfile -from nsfw_log import NSFWLog -from script_log import ScriptLog -from fileview import Fileview -from shake_category import ShakeCategory -from comment_like import CommentLike -from tag import Tag -from tagged_file import TaggedFile -from voucher import Voucher -from promotion import Promotion -from migration_state import MigrationState \ No newline at end of file +from .favorite import Favorite +from .subscription import Subscription +from .user import User +from .invitation import Invitation +from .sharedfile import Sharedfile +from .sourcefile import Sourcefile +from .externalservice import Externalservice +from .shake import Shake +from .shakesharedfile import Shakesharedfile +from .waitlist import Waitlist +from .comment import Comment +from .post import Post +from .notification import Notification +from .app import App +from .authorizationcode import Authorizationcode +from .accesstoken import Accesstoken +from .apilog import Apilog +from .conversation import Conversation +from .external_relationship import ExternalRelationship +from .shakemanager import ShakeManager +from .payment_log import PaymentLog +from .bookmark import Bookmark +from .apihit import Apihit +from .magicfile import Magicfile +from .nsfw_log import NSFWLog +from .script_log import ScriptLog +from .fileview import Fileview +from .shake_category import ShakeCategory +from .comment_like import CommentLike +from .tag import Tag +from .tagged_file import TaggedFile +from .voucher import Voucher +from .promotion import Promotion +from .migration_state import MigrationState \ No newline at end of file diff --git a/models/accesstoken.py b/models/accesstoken.py index bc8a0d81..5e924a19 100644 --- a/models/accesstoken.py +++ b/models/accesstoken.py @@ -1,7 +1,7 @@ from lib.flyingcow import Model, Property from tornado.options import options from datetime import datetime, timedelta -import authorizationcode +from . import authorizationcode from hashlib import sha224 from lib.utilities import base36encode @@ -49,7 +49,7 @@ def generate(authorization_id): """ auth = authorizationcode.Authorizationcode.get('id=%s', authorization_id) consumer_key = uuid.uuid3(uuid.NAMESPACE_DNS, base36encode(auth.id) + '-' + base36encode(auth.app_id)) - consumer_secret = sha224("%s%s" % (str(uuid.uuid1()), time.time())).hexdigest() + consumer_secret = sha224(("%s%s" % (str(uuid.uuid1()), time.time())).encode("ascii")).hexdigest() if auth.expires_at > datetime.utcnow(): access_token = Accesstoken(user_id=auth.user_id, app_id=auth.app_id, consumer_key=str(consumer_key), consumer_secret=str(consumer_secret)) diff --git a/models/app.py b/models/app.py index 5b5ea1a9..15f39974 100644 --- a/models/app.py +++ b/models/app.py @@ -2,14 +2,14 @@ import time import uuid import hashlib -from urlparse import urlparse +from urllib.parse import urlparse from tornado.options import options from lib.flyingcow import Model, Property from lib.utilities import base36encode, base36decode -import user -import accesstoken +from . import user +from . import accesstoken class App(Model): @@ -72,7 +72,7 @@ def _verify_redirect_url(self): def on_create(self): """Set the secret""" - self.secret = hashlib.sha224("%s%s" % (str(uuid.uuid1()), time.time())).hexdigest() + self.secret = hashlib.sha224(("%s%s" % (str(uuid.uuid1()), time.time())).encode('ascii')).hexdigest() self.save() def key(self): diff --git a/models/bookmark.py b/models/bookmark.py index 74c489d8..eaff0830 100644 --- a/models/bookmark.py +++ b/models/bookmark.py @@ -25,7 +25,7 @@ def save(self, *args, **kwargs): def on_create(self): existing_previous_bookmark = Bookmark.where('user_id=%s and id < %s ORDER BY id desc LIMIT 1', self.user_id, self.id) - if existing_previous_bookmark and existing_previous_bookmark[0].sharedfile_id > 0: + if existing_previous_bookmark and existing_previous_bookmark[0] and (existing_previous_bookmark[0].sharedfile_id or 0) > 0: self.previous_sharedfile_id = existing_previous_bookmark[0].sharedfile_id self.save() @@ -100,18 +100,19 @@ def merge_with_sharedfiles(self, bookmarks, sharedfiles): Sorts a list of bookmarks with a list of sharedfiles based on created_at. Bookmark will come before sharedfile if date is the same.. """ - def compare_created_at(x, y): - if x.created_at > y.created_at: - return -1 - elif x.created_at < y.created_at: - return 1 - else: - x_name = x.__class__.__name__ - y_name = y.__class__.__name__ - if x_name == 'Bookmark' and y_name == 'Sharedfile': - return -1 - return 0 + def compare_created_at_key(x): + return str(x.created_at) + (x.__class__.__name__ == "Bookmark" and "1" or "0") + # if x.created_at > y.created_at: + # return -1 + # elif x.created_at < y.created_at: + # return 1 + # else: + # x_name = x.__class__.__name__ + # y_name = y.__class__.__name__ + # if x_name == 'Bookmark' and y_name == 'Sharedfile': + # return -1 + # return 0 composite_list = sharedfiles + bookmarks - composite_list.sort(compare_created_at) + composite_list.sort(key=compare_created_at_key, reverse=True) return composite_list diff --git a/models/comment.py b/models/comment.py index ec2b150a..982ae4c1 100644 --- a/models/comment.py +++ b/models/comment.py @@ -5,12 +5,12 @@ from tornado.options import options from lib.flyingcow import Model, Property from lib.utilities import pretty_date -from BeautifulSoup import BeautifulSoup +from bs4 import BeautifulSoup -import user -import notification -import sharedfile -import conversation +from . import user +from . import notification +from . import sharedfile +from . import conversation class Comment(Model): @@ -73,7 +73,7 @@ def chopped_body(self): """ Returns a comment that has its HTML removed, shortened to 15 words, and if it doesn't end in a period, add ... """ - new_body = ''.join(BeautifulSoup(self.body).findAll(text=True)) + new_body = ''.join(BeautifulSoup(self.body, features="html.parser").findAll(string=True)) new_body = new_body.replace('\n', '') body_parts = new_body.split(' ') new_body = body_parts[:12] diff --git a/models/comment_like.py b/models/comment_like.py index 107c673e..06dc2733 100644 --- a/models/comment_like.py +++ b/models/comment_like.py @@ -1,7 +1,7 @@ from lib.flyingcow import Model, Property from datetime import datetime from lib.utilities import pretty_date -import user, notification +from . import user, notification from tornado.options import options diff --git a/models/conversation.py b/models/conversation.py index 8b7aa5ef..15a94197 100644 --- a/models/conversation.py +++ b/models/conversation.py @@ -2,8 +2,8 @@ from datetime import datetime from tornado.options import options -import comment -import sharedfile +from . import comment +from . import sharedfile class Conversation(Model): diff --git a/models/externalservice.py b/models/externalservice.py index 5b35c9d8..8118af94 100644 --- a/models/externalservice.py +++ b/models/externalservice.py @@ -2,7 +2,7 @@ from datetime import datetime from tornado.options import options -import user +from . import user class Externalservice(Model): diff --git a/models/favorite.py b/models/favorite.py index 22ac49ab..cf85c680 100644 --- a/models/favorite.py +++ b/models/favorite.py @@ -1,7 +1,7 @@ from lib.flyingcow import Model, Property from datetime import datetime from lib.utilities import pretty_date -import user +from . import user from tornado.options import options diff --git a/models/invitation.py b/models/invitation.py index 92c788a8..bed206ac 100644 --- a/models/invitation.py +++ b/models/invitation.py @@ -5,7 +5,7 @@ import hashlib import time -import user +from . import user from datetime import datetime @@ -52,8 +52,8 @@ def create_for_email(self, email, user_id): Creates an invitation for an email address. """ h = hashlib.sha1() - h.update("%s" % (time.time())) - h.update("%s" % (email)) + h.update(("%s" % (time.time())).encode('ascii')) + h.update(("%s" % (email)).encode('ascii')) invitation_key = h.hexdigest() sending_user = user.User.get('id = %s', user_id) invitation = Invitation(user_id=user_id, invitation_key=invitation_key, email_address=email, claimed_by_user_id=0) diff --git a/models/notification.py b/models/notification.py index 2fcbff31..c1da7001 100644 --- a/models/notification.py +++ b/models/notification.py @@ -3,12 +3,12 @@ from datetime import datetime, timedelta import postmark -import sharedfile -import user -import comment -import shake -import invitation -import subscription +from . import sharedfile +from . import user +from . import comment +from . import shake +from . import invitation +from . import subscription class Notification(Model): @@ -127,7 +127,7 @@ def display_for_user(cls, user): _notification = {'sender' : sender, 'related_object' : related_object, 'id' : notification.id} if notification.type == 'favorite': - if not notifications['like']['items'].has_key(related_object.id): + if related_object.id not in notifications['like']['items']: notifications['like']['items'][related_object.id] = [] notifications['like']['items'][related_object.id].append(_notification) notifications['like']['count'] += 1 @@ -137,7 +137,7 @@ def display_for_user(cls, user): notifications['follow'].append(_notification) elif notification.type == 'save': - if not notifications['save']['items'].has_key(related_object.id): + if related_object.id not in notifications['save']['items']: notifications['save']['items'][related_object.id] = [] notifications['save']['items'][related_object.id].append(_notification) notifications['save']['count'] += 1 diff --git a/models/shake.py b/models/shake.py index 8f96b40f..5328f842 100644 --- a/models/shake.py +++ b/models/shake.py @@ -1,7 +1,7 @@ import re -import cStringIO +import io from datetime import datetime -from urlparse import urljoin +from urllib.parse import urljoin from tornado.options import options from lib.s3 import S3Bucket @@ -13,7 +13,7 @@ from lib.reservedshakenames import reserved_names from lib.utilities import transform_to_square_thumbnail, s3_url -import user, shake, shakesharedfile, sharedfile, subscription, shakemanager +from . import user, shake, shakesharedfile, sharedfile, subscription, shakemanager class Shake(ModelQueryCache, Model): user_id = Property(name='user_id') @@ -195,7 +195,7 @@ def subscribers(self, page=None): WHERE shake_id = %s AND subscription.deleted = 0 ORDER BY subscription.id """ - if page > 0: + if page is not None and page > 0: limit_start = (page-1) * 20 sql = "%s LIMIT %s, %s" % (sql, limit_start, 20) return user.User.object_query(sql, self.id) @@ -317,8 +317,8 @@ def can_edit(self, user): return self.is_owner(user) def set_page_image(self, file_path=None, sha1_value=None): - thumb_cstr = cStringIO.StringIO() - image_cstr = cStringIO.StringIO() + thumb_cstr = io.BytesIO() + image_cstr = io.BytesIO() if not file_path or not sha1_value: return False @@ -340,6 +340,7 @@ def set_page_image(self, file_path=None, sha1_value=None): k.set_metadata('Cache-Control', 'max-age=86400') k.set_contents_from_string(thumb_cstr.getvalue()) k.set_acl('public-read') + k.close(fast=True) #save small k = Key(bucket) @@ -348,6 +349,7 @@ def set_page_image(self, file_path=None, sha1_value=None): k.set_metadata('Cache-Control', 'max-age=86400') k.set_contents_from_string(image_cstr.getvalue()) k.set_acl('public-read') + k.close(fast=True) self.image = 1 self.save() diff --git a/models/sharedfile.py b/models/sharedfile.py index db574879..f32d28cd 100644 --- a/models/sharedfile.py +++ b/models/sharedfile.py @@ -11,15 +11,15 @@ from lib.flyingcow.db import IntegrityError from lib.utilities import base36encode, base36decode, pretty_date, s3_authenticated_url -import user -import sourcefile -import fileview -import favorite -import shakesharedfile -import shake -import comment -import notification -import conversation +from . import user +from . import sourcefile +from . import fileview +from . import favorite +from . import shakesharedfile +from . import shake +from . import comment +from . import notification +from . import conversation import models.post import models.nsfw_log import models.tag @@ -780,7 +780,7 @@ def create_from_file(file_path, file_name, sha1_value, content_type, user_id, ti """ TODO: Must only accept acceptable content-types after consulting a list. """ - if len(sha1_value) <> 40: + if len(sha1_value) != 40: return None if user_id == None: diff --git a/models/sourcefile.py b/models/sourcefile.py index 8aebf314..2d0ef1b8 100644 --- a/models/sourcefile.py +++ b/models/sourcefile.py @@ -1,8 +1,8 @@ import hashlib -import cStringIO +import io from os import path from datetime import datetime -from urlparse import urlparse +from urllib.parse import urlparse import re from tornado.escape import url_escape, json_decode, json_encode @@ -101,14 +101,20 @@ def get_sha1_file_key(file_path=None, file_data=None): except Exception as e: return None else: - h.update(file_data) + # test if file_data is a string + if isinstance(file_data, str): + h.update(file_data.encode("UTF-8")) + elif isinstance(file_data, bytes): + h.update(file_data) + else: + raise Exception("file_data must be a string or bytes") return h.hexdigest() @staticmethod def get_from_file(file_path, sha1_value, type='image', skip_s3=None): existing_source_file = Sourcefile.get("file_key = %s", sha1_value) - thumb_cstr = cStringIO.StringIO() - small_cstr = cStringIO.StringIO() + thumb_cstr = io.BytesIO() + small_cstr = io.BytesIO() if existing_source_file: return existing_source_file try: @@ -120,14 +126,16 @@ def get_from_file(file_path, sha1_value, type='image', skip_s3=None): return None if img.mode != "RGB": - img = img.convert("RGB") + img2 = img.convert("RGB") + img.close() + img = img2 #generate smaller versions thumb = img.copy() small = img.copy() - thumb.thumbnail((100,100), Image.ANTIALIAS) - small.thumbnail((240,184), Image.ANTIALIAS) + thumb.thumbnail((100,100), Image.Resampling.LANCZOS) + small.thumbnail((240,184), Image.Resampling.LANCZOS) thumb.save(thumb_cstr, format="JPEG") small.save(small_cstr, format="JPEG") @@ -140,8 +148,10 @@ def get_from_file(file_path, sha1_value, type='image', skip_s3=None): if type != 'link': if not skip_s3: k = Key(bucket) - k.key = "originals/%s" % (sha1_value) + k.key = "originals/%s" % sha1_value k.set_contents_from_filename(file_path) + k.close(fast=True) + img.close() #save thumbnail thumbnail_file_key = Sourcefile.get_sha1_file_key(file_data=thumb_cstr.getvalue()) @@ -149,6 +159,8 @@ def get_from_file(file_path, sha1_value, type='image', skip_s3=None): k = Key(bucket) k.key = "thumbnails/%s" % thumbnail_file_key k.set_contents_from_string(thumb_cstr.getvalue()) + k.close(fast=True) + thumb.close() #save small small_file_key = Sourcefile.get_sha1_file_key(file_data=small_cstr.getvalue()) @@ -156,6 +168,8 @@ def get_from_file(file_path, sha1_value, type='image', skip_s3=None): k = Key(bucket) k.key = "smalls/%s" % small_file_key k.set_contents_from_string(small_cstr.getvalue()) + k.close(fast=True) + small.close() #save source file sf = Sourcefile(width=original_width, height=original_height, file_key=sha1_value, thumb_key=thumbnail_file_key, small_key=small_file_key, type=type) diff --git a/models/subscription.py b/models/subscription.py index b5284186..e9c83257 100644 --- a/models/subscription.py +++ b/models/subscription.py @@ -3,9 +3,9 @@ from lib.flyingcow import Model, Property from tornado.options import options -import shake -import user -import post +from . import shake +from . import user +from . import post class Subscription(Model): diff --git a/models/tag.py b/models/tag.py index dc0f0ef5..0aaab8c8 100644 --- a/models/tag.py +++ b/models/tag.py @@ -1,7 +1,7 @@ from lib.flyingcow import Model, Property from lib.flyingcow.cache import ModelQueryCache from datetime import datetime -import sharedfile +from . import sharedfile from tornado.options import options diff --git a/models/user.py b/models/user.py index b14a64e8..40fb5a17 100644 --- a/models/user.py +++ b/models/user.py @@ -1,10 +1,10 @@ -import cStringIO +import io import time import hashlib import calendar import random import re -import urlparse +import urllib.parse from datetime import datetime from lib.s3 import S3Bucket @@ -20,14 +20,14 @@ from tasks.migration import migrate_for_user from lib.badpasswords import bad_list -import notification -import subscription -import shake -import invitation -import sharedfile -import externalservice -import invitation_request -import shakemanager +from . import notification +from . import subscription +from . import shake +from . import invitation +from . import sharedfile +from . import externalservice +from . import invitation_request +from . import shakemanager # we use models.favorite due to some weird edge case where the reference # to the module gets lost. To recreate, rename to "import favorite" and # change references from models.favorite to just favorite. You can then @@ -163,8 +163,8 @@ def send_invitation(self, email_address): def invalidate_email(self): self.email_confirmed = 0 h = hashlib.sha1() - h.update("%s" % (time.time())) - h.update("%s" % (random.random())) + h.update(str(time.time()).encode("ascii")) + h.update(str(random.random()).encode("ascii")) self.verify_email_token = h.hexdigest() self.save() if not options.debug: @@ -183,8 +183,8 @@ def create_reset_password_token(self): """ h = hashlib.sha1() - h.update("%s" % (time.time())) - h.update("%s" % (random.random())) + h.update(str(time.time()).encode("ascii")) + h.update(str(random.random()).encode("ascii")) self.reset_password_token = h.hexdigest() self.save() body = """ @@ -233,17 +233,18 @@ def set_profile_image(self, file_path, file_name, content_type): if content_type not in valid_content_types: return False - destination = cStringIO.StringIO() + destination = io.BytesIO() if not transform_to_square_thumbnail(file_path, 100*2, destination): return False bucket = S3Bucket() k = Key(bucket) - k.key = "account/%s/profile.jpg" % (self.id) + k.key = "account/%s/profile.jpg" % self.id k.set_metadata('Content-Type', 'image/jpeg') k.set_metadata('Cache-Control', 'max-age=86400') k.set_contents_from_string(destination.getvalue()) k.set_acl('public-read') + k.close(fast=True) self.profile_image = 1 self.save() return True @@ -441,7 +442,7 @@ def total_file_stats(self): """ counts = sharedfile.Sharedfile.query("SELECT sum(like_count) as likes, sum(save_count) as saves, sum(view_count) as views from sharedfile where user_id = %s AND deleted=0", self.id) counts = counts[0] - for key, value in counts.items(): + for key, value in list(counts.items()): if not value: counts[key] = 0 return counts @@ -642,7 +643,7 @@ def following(self, page=None): AND subscription.deleted = 0 """ % self.id - if page > 0: + if page is not None and page > 0: limit_start = (page-1) * 20 select = "%s LIMIT %s, %s" % (select, limit_start, 20) @@ -925,7 +926,7 @@ def _validate_website(self): self.add_error('website', "The URL is too long.") return False if self.website != '': - parsed = urlparse.urlparse(self.website) + parsed = urllib.parse.urlparse(self.website) if parsed.scheme not in ('http', 'https',): self.add_error('website', "Doesn't look to be a valid URL.") return False @@ -1043,8 +1044,8 @@ def find_unmigrated_user(name, password): def generate_password_digest(password): secret = options.auth_secret h = hashlib.sha1() - h.update(password) - h.update(secret) + h.update(password.encode(encoding="UTF-8")) + h.update(secret.encode(encoding="UTF-8")) return h.hexdigest() -from sharedfile import Sharedfile +from .sharedfile import Sharedfile diff --git a/models/voucher.py b/models/voucher.py index baf7bff8..afb64393 100644 --- a/models/voucher.py +++ b/models/voucher.py @@ -5,10 +5,10 @@ import hashlib import time -import user +from . import user import datetime -import promotion +from . import promotion from lib.utilities import payment_notifications diff --git a/requirements-test.txt b/requirements-test.txt index 9d643313..87c39233 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,2 +1,2 @@ -coverage -coveralls +coverage==7.2.7 +coveralls==3.3.1 diff --git a/requirements.txt b/requirements.txt index 97815b0b..ceeae5f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,26 +1,18 @@ -BeautifulSoup==3.2.0 -MySQL-python==1.2.5 -Pillow==6.2.2 -amqplib==1.0.0 -anyjson==0.3.1 -boto==2.8.0 -celery==4.0.2 -distribute==0.7.3 -kombu==4.0.2 -pycurl==7.43.0 -pyparsing==2.2.0 -python-dateutil==1.5 -python-postmark==0.5.0 -python-slugify==5 -recaptcha-client==1.0.6 -tornado==4.1 -torndb==0.3 -tweepy==2.3 -wsgiref==0.1.2 -mock==1.0.1 -pyOpenSSL==17.5.0 -backports.ssl==0.0.9 -requests==2.20.0 -stripe==1.82.1 -yoyo-migrations==5.0.5 -ffmpy==0.2.2 +beautifulsoup4==4.12.2 +mysqlclient==2.1.1 +Pillow==10.0.0 +amqplib==1.0.2 +boto==2.49.0 +celery==5.3.1 +kombu==5.3.1 +pycurl==7.45.2 +pyparsing==3.1.0 +python-dateutil==2.8.2 +python-postmark==0.6.0 +tornado==6.3.2 +mock==5.0.2 +pyOpenSSL==23.2.0 +requests==2.31.0 +stripe==5.4.0 +yoyo-migrations==8.2.0 +ffmpy==0.3.0 diff --git a/runner.py b/runner.py index d9b73fee..8ead6c7f 100755 --- a/runner.py +++ b/runner.py @@ -39,7 +39,7 @@ def run(script_path): if result: script_log.result = result script_log.success = 1 - except Exception, e: + except Exception as e: script_log.success = 0 script_log.result = json.dumps({'error': str(e)}) sys.exit("Exception: %s" % e) diff --git a/scripts/make-zip-of-images.py b/scripts/make-zip-of-images.py index f19bd514..aeef207b 100644 --- a/scripts/make-zip-of-images.py +++ b/scripts/make-zip-of-images.py @@ -51,7 +51,7 @@ def make_zip_file(for_user=None): sfs = models.Sharedfile.where("user_id = %s and deleted=0 order by id", user.id) if sfs: - print(len(sfs)) + print((len(sfs))) for sf in sfs: source = sf.sourcefile() if source.type == 'link': @@ -71,7 +71,7 @@ def make_zip_file(for_user=None): extension = "png" if extension == "": - print(sf.content_type) + print((sf.content_type)) print("extension blank") sys.exit() @@ -86,6 +86,7 @@ def make_zip_file(for_user=None): k.set_contents_from_filename("/mnt/backups/users/{0}.zip".format(user.name), cb=percent_cb, num_cb=10) happy_url = k.generate_url(expires_in=72000) + k.close(fast=True) #email link to user email 8 hours pm = postmark.PMMail(api_key=options.postmark_api_key, sender="hello@mltshp.com", to=user.email, diff --git a/scripts/migrate-user.py b/scripts/migrate-user.py index 7f891161..8baf89f9 100644 --- a/scripts/migrate-user.py +++ b/scripts/migrate-user.py @@ -16,7 +16,7 @@ def main(): for name in names: user = User.get("name=%s and deleted=2", name) if user is not None: - print "Migrating %s..." % name + print("Migrating %s..." % name) migrate_for_user.delay_or_run(user.id) else: - print "Could not find user named: %s" % name + print("Could not find user named: %s" % name) diff --git a/scripts/populate-save-like.py b/scripts/populate-save-like.py index 1826ff77..426e4d50 100755 --- a/scripts/populate-save-like.py +++ b/scripts/populate-save-like.py @@ -28,5 +28,5 @@ def main(): saves = save_counts[0]['save_count'] if likes > 0 or saves > 0: - print "UPDATE sharedfile SET like_count = %s, save_count = %s WHERE id = %s" % (likes, saves, sf.id) - print db1.execute("UPDATE sharedfile SET like_count = %s, save_count = %s WHERE id = %s", likes, saves, sf.id) + print("UPDATE sharedfile SET like_count = %s, save_count = %s WHERE id = %s" % (likes, saves, sf.id)) + print(db1.execute("UPDATE sharedfile SET like_count = %s, save_count = %s WHERE id = %s", likes, saves, sf.id)) diff --git a/scripts/populate-timelines.py b/scripts/populate-timelines.py index 1bb0005a..245ec173 100755 --- a/scripts/populate-timelines.py +++ b/scripts/populate-timelines.py @@ -12,5 +12,5 @@ def main(): ssfs = db1.query("""SELECT shake_id, sharedfile_id from shakesharedfile order by created_at""") for shakesharedfile in ssfs: sf = db1.get("""SELECT id, source_id, name, deleted, created_at FROM sharedfile WHERE id = %s""", shakesharedfile['sharedfile_id']) - print "%s. Adding posts for sharedfile: %s created at %s." % (sf.id, sf.name, sf.created_at) + print("%s. Adding posts for sharedfile: %s created at %s." % (sf.id, sf.name, sf.created_at)) add_posts(shake_id=shakesharedfile['shake_id'], sharedfile_id=sf['id'], sourcefile_id=sf['source_id'], deleted=sf['deleted'], created_at=sf['created_at']) diff --git a/scripts/transcode-sharedfile.py b/scripts/transcode-sharedfile.py index dab2e91e..7cfbee9f 100644 --- a/scripts/transcode-sharedfile.py +++ b/scripts/transcode-sharedfile.py @@ -13,7 +13,7 @@ def main(): options.use_workers = False if len(keys) == 0: - print "Selecting untranscoded sharedfiles..." + print("Selecting untranscoded sharedfiles...") select = """SELECT share_key FROM sharedfile JOIN sourcefile ON sourcefile.id = sharedfile.source_id @@ -25,12 +25,12 @@ def main(): results = Sharedfile.query(select) for result in results: keys.append(result["share_key"]) - print "Found %d sharedfiles to transcode" % len(keys) + print("Found %d sharedfiles to transcode" % len(keys)) for key in keys: sf = Sharedfile.get("share_key=%s AND content_type='image/gif' AND deleted=0", key) if sf is not None: - print "Transcoding %s..." % sf.share_key + print("Transcoding %s..." % sf.share_key) transcode_sharedfile.delay_or_run(sf.id) else: - print "Could not find sharedfile with key: %s" % key + print("Could not find sharedfile with key: %s" % key) diff --git a/setup/dev/supervisord.conf b/setup/dev/supervisord.conf index ecb6aed4..6fbac4a0 100644 --- a/setup/dev/supervisord.conf +++ b/setup/dev/supervisord.conf @@ -9,7 +9,7 @@ autorestart=true programs=main-8000 [program:main-8000] -command=python main.py --on_port=8000 +command=python3 main.py --on_port=8000 directory=/srv/mltshp.com/mltshp autorestart=true redirect_stderr=true diff --git a/setup/production/mysql.conf b/setup/production/mysql.conf new file mode 100644 index 00000000..da44f0d8 --- /dev/null +++ b/setup/production/mysql.conf @@ -0,0 +1,18 @@ +# /etc/mysql/mysql.conf.d/mltshp.cnf +[mysqld] +skip-name-resolve = 1 +key_buffer_size = 2048M +sort_buffer_size = 16M +table_open_cache = 4000 +thread_cache_size = 8 +query_cache_limit = 1M +query_cache_size = 32M +query_cache_type = 1 +innodb_buffer_pool_size = 8G +innodb_ft_min_token_size = 2 + +# +# Here you can see queries with especially long duration +#log_slow_queries = /var/log/mysql/mysql-slow.log +#long_query_time = 2 +#log-queries-not-using-indexes \ No newline at end of file diff --git a/setup/production/supervisord-web.conf b/setup/production/supervisord-web.conf index 96f9d2a4..eebff2e0 100644 --- a/setup/production/supervisord-web.conf +++ b/setup/production/supervisord-web.conf @@ -7,7 +7,7 @@ autorestart=true [program:main] process_name=mltshp-%(process_num)d -command=python main.py --on_port=80%(process_num)02d +command=python3 main.py --on_port=80%(process_num)02d numprocs=6 numprocs_start=1 directory=/srv/mltshp.com/mltshp diff --git a/setup/production/supervisord-worker.conf b/setup/production/supervisord-worker.conf index dfdbef70..bdfc9bee 100644 --- a/setup/production/supervisord-worker.conf +++ b/setup/production/supervisord-worker.conf @@ -9,7 +9,7 @@ autorestart=true programs=celeryd-celery,celeryd-transcode [program:celeryd-celery] -command=python worker.py -Q celery --loglevel INFO +command=python3 worker.py -Q celery --loglevel INFO directory=/srv/mltshp.com/mltshp autorestart=true redirect_stderr=true @@ -23,7 +23,7 @@ user=ubuntu environment=HOME="/home/ubuntu",USER="ubuntu" [program:celeryd-transcode] -command=python worker.py -Q transcode --loglevel INFO +command=python3 worker.py -Q transcode --loglevel INFO directory=/srv/mltshp.com/mltshp autorestart=true redirect_stderr=true @@ -34,4 +34,4 @@ stdout_capture_maxbytes=1MB stdout_events_enabled=false loglevel=info user=ubuntu -environment=HOME="/home/ubuntu",USER="ubuntu" \ No newline at end of file +environment=HOME="/home/ubuntu",USER="ubuntu" diff --git a/static/developers/example.py.txt b/static/developers/example.py.txt index 06fcf498..c62d6739 100755 --- a/static/developers/example.py.txt +++ b/static/developers/example.py.txt @@ -68,7 +68,7 @@ print "SEE CODE FOR THE STEPS" # on the developer site soon. There is a specific method for encoding this bit.) timestamp = int(time.mktime(datetime.utcnow().timetuple())) -nonce = md5("%s" % random.random()).hexdigest() #you might want to generate better nonces +nonce = md5(("%s" % random.random()).encode('ascii')).hexdigest() #you might want to generate better nonces #start by normalizing the message. the order here is important normalized_string = "%s\n" % (j_response['access_token']) @@ -82,9 +82,9 @@ normalized_string += "/api/sharedfile/GA4\n" #note, we're not including the query string because there isn't one. #now we sign the request -digest = hmac.new(j_response['secret'].encode('ascii'), normalized_string, sha1).digest() -signature = base64.encodestring(digest).strip() #we strip the end off because it gives us a \n at the end -authorization_string = 'MAC token="%s", timestamp="%s", nonce="%s", signature="%s"' % (j_response['access_token'], str(timestamp), nonce, signature) +digest = hmac.new(j_response['secret'].encode('ascii'), normalized_string.encode('ascii'), sha1).digest() +signature = base64.encodebytes(digest).strip() #we strip the end off because it gives us a \n at the end +authorization_string = 'MAC token="%s", timestamp="%s", nonce="%s", signature="%s"' % (j_response['access_token'], str(timestamp), nonce, signature.decode('ascii')) req = urllib2.Request(RESOURCE_URL,headers={ 'Authorization' : authorization_string }) diff --git a/tasks/__init__.py b/tasks/__init__.py index 84332227..38d3a820 100644 --- a/tasks/__init__.py +++ b/tasks/__init__.py @@ -1,6 +1,5 @@ -from celery.task import task -from celery.task.base import Task -import postmark +from celery import shared_task +from celery.app.task import Task from tornado.options import define, options import mltshpoptions @@ -30,4 +29,4 @@ def delay_or_run(self, *args, **kwargs): def mltshp_task(*args, **options): # This is how celery's periodic_task decorator customizes the class, so try it here too. - return task(**dict({"base": MltshpTask}, **options)) + return shared_task(**dict({"base": MltshpTask}, **options)) diff --git a/tasks/counts.py b/tasks/counts.py index f7c01323..03e46ce4 100644 --- a/tasks/counts.py +++ b/tasks/counts.py @@ -1,8 +1,6 @@ from torndb import Connection from tornado.options import options from datetime import datetime -import os -import tweepy #temporary, need to use feathers from tasks import mltshp_task @@ -34,21 +32,23 @@ def tweet_or_magic(db, sharedfile_id, like_count): if like_count == likes_to_magic: created_at = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") db.execute("INSERT IGNORE INTO magicfile (sharedfile_id, created_at) VALUES (%s, %s)", sharedfile_id, created_at) - if like_count == likes_to_tweet and not options.debug: - title = '' - if sf['title'] == '' or sf['title'] == None: - title = sf['name'] - else: - title = sf['title'] - auth = tweepy.OAuthHandler(options.twitter_consumer_key, options.twitter_consumer_secret) - auth.set_access_token(options.twitter_access_key, options.twitter_access_secret) - api = tweepy.API(auth) - via_twitter_account = "" - twitter_account = db.get("SELECT screen_name from externalservice where user_id = %s and deleted=0", sf['user_id']) - if twitter_account: - via_twitter_account = " via @{0}".format(twitter_account['screen_name']) - api.update_status('https://mltshp.com/p/%s "%s"%s' % (sf['share_key'], title[:90], via_twitter_account)) + # The Twitter API is dead. + # if like_count == likes_to_tweet and not options.debug: + # title = '' + # if sf['title'] == '' or sf['title'] == None: + # title = sf['name'] + # else: + # title = sf['title'] + + # auth = tweepy.OAuthHandler(options.twitter_consumer_key, options.twitter_consumer_secret) + # auth.set_access_token(options.twitter_access_key, options.twitter_access_secret) + # api = tweepy.API(auth) + # via_twitter_account = "" + # twitter_account = db.get("SELECT screen_name from externalservice where user_id = %s and deleted=0", sf['user_id']) + # if twitter_account: + # via_twitter_account = " via @{0}".format(twitter_account['screen_name']) + # api.update_status('https://mltshp.com/p/%s "%s"%s' % (sf['share_key'], title[:90], via_twitter_account)) @mltshp_task() diff --git a/tasks/transcode.py b/tasks/transcode.py index 5405858f..e44d68c5 100644 --- a/tasks/transcode.py +++ b/tasks/transcode.py @@ -74,6 +74,7 @@ def gif_to_video(sourcefile_id, file_key, input_file, format): logger.info("uploading transcoded video: %s" % file_key) key.set_contents_from_filename(output_file) + key.close(fast=True) logger.info("-- upload complete") db = db_connect() db.execute( @@ -125,6 +126,7 @@ def transcode_sharedfile(sharedfile_id): key.key = "originals/%s" % sourcefile["file_key"] logger.info("Downloading original GIF from S3 for sourcefile %s..." % sharedfile["source_id"]) key.get_contents_to_filename(input_file) + key.close(fast=True) # Test to see if GIF is animated or not animated = False @@ -134,6 +136,7 @@ def transcode_sharedfile(sharedfile_id): animated = True except EOFError: pass + im.close() if not animated: os.unlink(input_file) diff --git a/templates/account/settings-profile.html b/templates/account/settings-profile.html index 37320b79..59f52286 100644 --- a/templates/account/settings-profile.html +++ b/templates/account/settings-profile.html @@ -36,8 +36,8 @@

Want to change it?

{% if not site_is_readonly and user.email_confirmed == 1 %}
-

Tips for your profile image:

-

Shoot for 200x200 pixels square, it will also be displayed at 48×48 for file pages. (100k maximum size)

+

Tips for your profile image:

+

Shoot for 200×200 pixels square, it will also be displayed at 48×48 for file pages. (100k maximum size)

{% end %} diff --git a/templates/account/settings.html b/templates/account/settings.html index bb1c83b0..f97756a1 100644 --- a/templates/account/settings.html +++ b/templates/account/settings.html @@ -161,7 +161,7 @@

You have canceled your subscription, but it will remain active {% end %}

Your Membership Plan: {{ plan_name }}

- {% if user.stripe_plan_id == "mltshp-double" and user.stripe_plan_rate > 24 %} + {% if user.stripe_plan_id == "mltshp-double" and user.stripe_plan_rate is not None and user.stripe_plan_rate > 24 %}

You subscribe at a custom rate of ${{ user.stripe_plan_rate }}/year. Thank you for your added support.

{% end %} diff --git a/templates/home/index.html b/templates/home/index.html index 0c0d6ff3..86d2bd15 100644 --- a/templates/home/index.html +++ b/templates/home/index.html @@ -127,7 +127,7 @@

Cool Tools! Cool Tools!

- {% if sharedfile.previous_sharedfile_id > 0 %} + {% if sharedfile.previous_sharedfile_id or 0 > 0 %} jump to previous diff --git a/templates/tools/picker-error.html b/templates/tools/picker-error.html index 41274710..6999af34 100644 --- a/templates/tools/picker-error.html +++ b/templates/tools/picker-error.html @@ -2,6 +2,6 @@ {% block main %}
-

We had a problem.

+

We had a problem ({{ error }}).

{% end %} diff --git a/test.py b/test.py index bac48a6c..8f480037 100755 --- a/test.py +++ b/test.py @@ -19,7 +19,7 @@ TEST_MODULES = [ 'test.AccountTests', 'test.CommentTests', - 'test.ExternalAccountTests', + # 'test.ExternalAccountTests', 'test.FileTests', 'test.SimpleTests', 'test.SiteFunctionTests', @@ -75,7 +75,7 @@ def all(): db = Connection(options.database_host, 'mysql', options.database_user, options.database_password) try: db.execute("CREATE database %s" % options.database_name) - except MySQLdb.ProgrammingError, exc: + except MySQLdb.ProgrammingError as exc: if exc.args[0] != 1007: # database already exists raise else: diff --git a/test/AccountTests.py b/test/AccountTests.py index f6206f56..a477e280 100644 --- a/test/AccountTests.py +++ b/test/AccountTests.py @@ -1,21 +1,14 @@ -from tornado.testing import AsyncHTTPTestCase -from torndb import Connection -from tornado.httpclient import HTTPRequest from tornado.escape import json_decode - -import Cookie -import base64 import time -import copy import hashlib import random import os +from .base import BaseAsyncTestCase -from base import BaseAsyncTestCase +from models import User, Sourcefile, Sharedfile, Subscription, Notification, Post -from models import User, Sourcefile, Sharedfile, Shake, Subscription, Notification, Post class AccountIndexTests(BaseAsyncTestCase): def setUp(self): @@ -24,9 +17,9 @@ def setUp(self): self.admin.set_password('asdfasdf') self.admin.save() self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.flake=str(time.time()) - + def test_pagination_returns_correct_counts(self): """ This tests creating 111 shared files for a user and then tests that pagination @@ -36,14 +29,14 @@ def test_pagination_returns_correct_counts(self): user_shake = user.shake() source_file = Sourcefile(width=10, height=10, file_key='mumbles', thumb_key='bumbles') source_file.save() - + missing_ids = [] - + for i in range(111): sf = Sharedfile(source_id = source_file.id, user_id = user.id, name="shgaredfile.png", title='shared file', share_key='asdf', content_type='image/png', deleted=0) sf.save() sf.add_to_shake(user_shake) - + for i in range(12): response = self.fetch('/user/admin/%s' % (i + 1)) self.assertEqual(response.code, 200) @@ -52,10 +45,9 @@ def test_new_user_sees_welcome_page(self): """ This tests that a new user who just signed up will see the welcome page. """ - self.http_client.fetch(self.get_url('/'), self.stop) - response = self.wait() + response = self.fetch_url('/') self.assertEqual(response.code, 200) - self.assertTrue(response.body.find('Getting Started')) + self.assertIn('Getting Started', response.body) class SubscriptionTests(BaseAsyncTestCase): @@ -64,99 +56,83 @@ def setUp(self): self.admin = User(name='admin', email='admin@mltshp.com', email_confirmed=1, is_paid=1) self.admin.set_password('asdfasdf') self.admin.save() - + self.user2 = User(name='user2', email='user2@mltshp.com', email_confirmed=1, is_paid=1) self.user2.set_password('asdfasdf') self.user2.save() - + self.user3 = User(name='user3', email='user3@mltshp.com', email_confirmed=1, is_paid=1) self.user3.set_password('asdfasdf') self.user3.save() - + self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.flake=str(time.time()) - + def test_follow_signed_in(self): - request = HTTPRequest(self.get_url('/user/user3/subscribe?json=1'), 'POST', {'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, "_xsrf=%s" % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - + response = self.fetch("/user/user3/subscribe?json=1", method="POST", headers={'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, body="_xsrf=%s" % self.xsrf) + j = json_decode(response.body) self.assertEqual(j['subscription_status'], True) - + subscription = Subscription.get('user_id=1 and shake_id=3') self.assertTrue(subscription) self.assertFalse(subscription.deleted) - + def test_follow_creates_posts(self): self.sign_in('user3', 'asdfasdf') response = self.upload_test_file() self.sign_in('admin', 'asdfasdf') self.post_url('/user/user3/subscribe?json=1') - + post = Post.where('user_id=%s', self.admin.id) + self.assertTrue(len(post) > 0) self.assertEqual(3, post[0].shake_id) - + def test_cannot_follow_self(self): - request = HTTPRequest(self.get_url('/user/admin/subscribe?json=1'), 'POST', {'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, "_xsrf=%s" % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - + response = self.fetch('/user/admin/subscribe?json=1', method='POST', headers={'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, body="_xsrf=%s" % (self.xsrf)) + j = json_decode(response.body) self.assertTrue('error' in j) - + subscription = Subscription.all() self.assertTrue(len(subscription) == 0) def test_cannot_subscribe_if_not_signed_in(self): - request = HTTPRequest(self.get_url('/user/user3/subscribe?json=1'), 'POST', {'Cookie':'_xsrf=%s' % (self.xsrf)}, "_xsrf=%s" % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/user/user3/subscribe?json=1', method="POST", headers={'Cookie':'_xsrf=%s' % self.xsrf}, body="_xsrf=%s" % self.xsrf) self.assertEqual(response.code, 403) - + def test_unsubscribe_shake(self): - request = HTTPRequest(self.get_url('/user/user3/subscribe?json=1'), 'POST', {'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, "_xsrf=%s" % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - - request = HTTPRequest(self.get_url('/user/user3/unsubscribe?json=1'), 'POST', {'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, "_xsrf=%s" % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - + response = self.fetch('/user/user3/subscribe?json=1', method="POST", headers={'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, body="_xsrf=%s" % self.xsrf) + + response = self.fetch('/user/user3/unsubscribe?json=1', method='POST', headers={'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, body="_xsrf=%s" % self.xsrf) + j = json_decode(response.body) self.assertEqual(j['subscription_status'], False) - + subscription = Subscription.get('user_id=1 and shake_id=3') self.assertTrue(subscription) self.assertTrue(subscription.deleted) - - + def test_subscribe_unsubscribe_is_same_object(self): - request = HTTPRequest(self.get_url('/user/user3/subscribe?json=1'), 'POST', {'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, "_xsrf=%s" % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - + response = self.fetch('/user/user3/subscribe?json=1', method='POST', headers={'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, body="_xsrf=%s" % self.xsrf) + first_subscription = Subscription.get('user_id=1 and shake_id=3') - - request = HTTPRequest(self.get_url('/user/user3/unsubscribe?json=1'), 'POST', {'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, "_xsrf=%s" % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - + + response = self.fetch('/user/user3/unsubscribe?json=1', method='POST', headers={'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, body="_xsrf=%s" % self.xsrf) + j = json_decode(response.body) self.assertEqual(j['subscription_status'], False) - + second_subscription = Subscription.get('user_id=1 and shake_id=3') self.assertEqual(first_subscription.id, second_subscription.id) - + def test_notification_created_when_subscription_created(self): """ User is followed. Notification created. """ - request = HTTPRequest(self.get_url('/user/user3/subscribe?json=1'), 'POST', {'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, "_xsrf=%s" % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - + response = self.fetch('/user/user3/subscribe?json=1', method='POST', headers={'Cookie':'sid=%s;_xsrf=%s' % (self.sid, self.xsrf)}, body="_xsrf=%s" % self.xsrf) + subscriptions = Subscription.all() self.assertEqual(len(subscriptions), 1) notifications = Notification.all() @@ -174,58 +150,52 @@ def setUp(self): self.user = User(name="admin", email="admin@mltshp.com", email_confirmed=1, is_paid=1) self.user.set_password('asdfasdf') self.user.save() - self.xsrf = self.get_xsrf() - + self.xsrf = self.get_xsrf().decode("ascii") + def test_email_verification(self): self.assertEqual(self.user.email_confirmed, 1) self.assertTrue(self.user.verify_email_token == None) - - + self.user.invalidate_email() reload_user = User.get("id=%s", self.user.id) - + self.assertEqual(reload_user.email_confirmed, 0) self.assertTrue(len(reload_user.verify_email_token) == 40) - + def test_lost_password(self): self.assertTrue(self.user.reset_password_token == None) - request=HTTPRequest( - url = self.get_url("/account/forgot-password"), + response = self.fetch( + "/account/forgot-password", method='POST', - headers = {'Cookie':'_xsrf=%s' % (self.xsrf)}, + headers = {'Cookie':'_xsrf=%s' % self.xsrf}, body = "_xsrf=%s&email=%s" % (self.xsrf, self.user.email)) - self.http_client.fetch(request, self.stop) - response = self.wait() self.assertEqual(response.code, 200) - self.assertTrue(response.body.find("We Sent You Instructions!") > 0) + self.assertIn("We Sent You Instructions!", response.body) user = User.get("id=%s", 1) self.assertTrue(len(user.reset_password_token) == 40) def test_lost_password_lookup_failure(self): self.assertTrue(self.user.reset_password_token == None) - request=HTTPRequest( - url = self.get_url("/account/forgot-password"), + response = self.fetch( + "/account/forgot-password", method='POST', headers = {'Cookie':'_xsrf=%s' % self.xsrf}, - body = "_xsrf=%s&email=25235235" % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find("That email address doesn't have an account.") > 0) + body = "_xsrf=%s&email=25235235" % self.xsrf) + self.assertIn("That email address doesn't have an account.", response.body) def test_reset_password_lookup(self): """ Hitting the reset-password url with a valid key will correctly look it up. """ h = hashlib.sha1() - h.update("%s-%s" % (time.time(), random.random())) + h.update(("%s-%s" % (time.time(), random.random())).encode("ascii")) self.user.reset_password_token = h.hexdigest() self.user.save() - self.http_client.fetch(self.get_url("/account/reset-password/%s" % (self.user.reset_password_token)), self.stop) - response = self.wait() - self.assertTrue(response.body.find("Enter a new password for your account.") > -1) + response = self.fetch("/account/reset-password/%s" % self.user.reset_password_token) + self.assertIn("Enter a new password for your account.", response.body) def test_reset_password_finish(self): """ @@ -233,17 +203,14 @@ def test_reset_password_finish(self): """ self.user.create_reset_password_token() - self.http_client.fetch(self.get_url("/account/reset-password/%s" % (self.user.reset_password_token)), self.stop) - response = self.wait() + response = self.fetch("/account/reset-password/%s" % self.user.reset_password_token) - request = HTTPRequest( - url = self.get_url("/account/reset-password/%s" % (self.user.reset_password_token)), + response = self.fetch( + "/account/reset-password/%s" % self.user.reset_password_token, method='POST', - headers={"Cookie":"_xsrf=%s" % (self.xsrf)}, + headers={"Cookie":"_xsrf=%s" % self.xsrf}, body="password=%s&password_again=%s&_xsrf=%s" % ("qwertyqwerty", "qwertyqwerty", self.xsrf) ) - self.http_client.fetch(request, self.stop) - response = self.wait() self.user = User.get("id=%s", 1) self.assertEqual(self.user.reset_password_token, "") @@ -256,15 +223,13 @@ def test_reset_password_throws_error_if_passwords_dont_match(self): self.user.create_reset_password_token() reset_token = self.user.reset_password_token - request = HTTPRequest( - url = self.get_url("/account/reset-password/%s" % (reset_token)), + response = self.fetch( + "/account/reset-password/%s" % reset_token, method='POST', headers={"Cookie":"_xsrf=%s" % (self.xsrf)}, body="password=%s&password_again=%s&_xsrf=%s" % ("qwertyqwerty", "poiupoiu", self.xsrf) ) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find("Those passwords didn't match, or are invalid. Please try again.") > -1) + self.assertIn("Those passwords didn't match, or are invalid. Please try again.", response.body) self.user = User.get("id = %s", 1) self.assertEqual(self.user.hashed_password, User.generate_password_digest('asdfasdf')) self.assertEqual(reset_token, self.user.reset_password_token) @@ -279,8 +244,7 @@ def test_reset_password_throws_404s_on_invalid_tokens(self): "", "029%203208%2032093093%2020923"] for token in invalid_tokens: - self.http_client.fetch(self.get_url("/account/reset-password/%s" % (token)), self.stop) - response = self.wait() + response = self.fetch("/account/reset-password/%s" % token) self.assertEqual(response.code, 404) def test_password_reset_while_signed_in(self): @@ -290,14 +254,12 @@ def test_password_reset_while_signed_in(self): sid = self.sign_in("admin", "asdfasdf") self.user.create_reset_password_token() - request = HTTPRequest( - url = self.get_url("/account/reset-password/%s" % (self.user.reset_password_token)), + response = self.fetch( + "/account/reset-password/%s" % self.user.reset_password_token, method='GET', headers={'Cookie':"sid=%s" % (sid)}, follow_redirects=False ) - self.http_client.fetch(request, self.stop) - response = self.wait() class NotificationTests(BaseAsyncTestCase): def setUp(self): @@ -309,7 +271,7 @@ def setUp(self): self.receiver.set_password('asdfasdf') self.receiver.save() - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.sender_sid = self.sign_in('admin', 'asdfasdf') self.receiver_sid = self.sign_in('user2', 'asdfasdf') @@ -323,9 +285,7 @@ def test_clear_single_notification(self): sharedfile = Sharedfile.get('id=1') n = Notification.new_favorite(self.sender, sharedfile) - request = HTTPRequest(self.get_url('/account/clear-notification'), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.receiver_sid)}, '_xsrf=%s&type=single&id=%s' % (self.xsrf, n.id)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/account/clear-notification', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.receiver_sid)}, body='_xsrf=%s&type=single&id=%s' % (self.xsrf, n.id)) j = json_decode(response.body) self.assertTrue('response' in j) @@ -336,9 +296,7 @@ def test_only_allowed_to_clear_own_notifications(self): sharedfile = Sharedfile.get('id=1') n = Notification.new_favorite(self.sender, sharedfile) - request = HTTPRequest(self.get_url('/account/clear-notification'), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sender_sid)}, '_xsrf=%s&type=single&id=%s' % (self.xsrf, n.id)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/account/clear-notification', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sender_sid)}, body='_xsrf=%s&type=single&id=%s' % (self.xsrf, n.id)) j = json_decode(response.body) self.assertTrue('error' in j) @@ -354,9 +312,7 @@ def test_clears_all_saves(self): n = Notification.new_save(self.sender, sharedfile) - request = HTTPRequest(self.get_url('/account/clear-notification'), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.receiver_sid)}, '_xsrf=%s&type=save' % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/account/clear-notification', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.receiver_sid)}, body='_xsrf=%s&type=save' % (self.xsrf)) j = json_decode(response.body) self.assertEqual(j['response'], "0 new saves") @@ -371,9 +327,7 @@ def test_clears_all_favorites(self): n = Notification.new_favorite(self.sender, sharedfile) n = Notification.new_favorite(self.sender, sharedfile) - request = HTTPRequest(self.get_url('/account/clear-notification'), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.receiver_sid)}, '_xsrf=%s&type=favorite' % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/account/clear-notification', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.receiver_sid)}, body='_xsrf=%s&type=favorite' % self.xsrf) j = json_decode(response.body) self.assertEqual(j['response'], "0 new likes") @@ -394,9 +348,7 @@ def test_clears_all_subscriptions(self): Notification.new_subscriber(self.sender, self.receiver, 1) Notification.new_subscriber(user3, self.receiver, 2) - request = HTTPRequest(self.get_url('/account/clear-notification'), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.receiver_sid)}, '_xsrf=%s&type=subscriber' % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/account/clear-notification', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.receiver_sid)}, body='_xsrf=%s&type=subscriber' % self.xsrf) j = json_decode(response.body) self.assertEqual(j['response'], "You have 0 new followers") @@ -411,7 +363,7 @@ def setUp(self): self.admin.set_password('asdfasdf') self.admin.save() self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.flake=str(time.time()) def test_usernames_with_special_characters_can_be_seen(self): @@ -420,8 +372,5 @@ def test_usernames_with_special_characters_can_be_seen(self): new_user = User(name=name, email='%s@mltshp.com' % (name), email_confirmed=1) new_user.set_password('asdfasdf') new_user.save() - self.http_client.fetch(self.get_url('/user/%s' % name), self.stop) - response = self.wait() + response = self.fetch('/user/%s' % name) self.assertEqual(response.code, 200) - - diff --git a/test/CommentTests.py b/test/CommentTests.py index 1d0e2190..6a2c4cea 100644 --- a/test/CommentTests.py +++ b/test/CommentTests.py @@ -1,11 +1,10 @@ from tornado.testing import AsyncHTTPTestCase from tornado.web import Application -from tornado.httpclient import HTTPRequest from tornado.escape import json_decode, url_escape import time -from base import BaseAsyncTestCase +from .base import BaseAsyncTestCase from models import User, Sharedfile, Sourcefile, Conversation import models @@ -17,7 +16,7 @@ def setUp(self): self.admin.set_password('asdfasdf') self.admin.save() self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.flake=str(time.time()) self.src = Sourcefile(width=1, height=1, file_key='asdf', thumb_key='qwer') self.src.save() @@ -32,9 +31,7 @@ def test_saving_a_comment_is_stored(self): That is all.&_xsrf=asdf """ - request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) comments = self.shf.comments() self.assertEqual(len(comments), 1) @@ -42,9 +39,7 @@ def test_saving_a_comment_is_stored(self): def test_blank_comment_doesnt_save(self): body = "" - request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) comments = self.shf.comments() self.assertEqual(len(comments), 0) @@ -53,9 +48,7 @@ def test_saving_an_empty_comment_not_stored(self): #submit a comment to /share_key/save_comment body = """ """ - request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) comments = self.shf.comments() self.assertEqual(len(comments), 0) @@ -65,9 +58,7 @@ def test_saving_not_signed_in_not_stored(self): body = """ This is a comment. """ - request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s' % (self.xsrf)}, "body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s' % (self.xsrf)}, body="body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) comments = self.shf.comments() self.assertEqual(len(comments), 0) @@ -77,13 +68,9 @@ def test_saving_not_signed_in_not_stored(self): # This is a comment. # """ - # request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) - # self.http_client.fetch(request, self.stop) - # response = self.wait() + # response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) - # request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) - # self.http_client.fetch(request, self.stop) - # response = self.wait() + # response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) # test_user = User.get('id = 1') # self.assertEqual(test_user.restricted, 1) @@ -95,13 +82,9 @@ def test_saving_not_signed_in_not_stored(self): # body = """ # This is a comment. # """ - # request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) - # self.http_client.fetch(request, self.stop) - # response = self.wait() + # response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) - # request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) - # self.http_client.fetch(request, self.stop) - # response = self.wait() + # response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape(body), self.xsrf)) # test_user = User.get('id = 1') # self.assertEqual(test_user.restricted, 0) diff --git a/test/ExternalAccountTests.py b/test/ExternalAccountTests.py index 8450ab95..4a6abd18 100644 --- a/test/ExternalAccountTests.py +++ b/test/ExternalAccountTests.py @@ -1,132 +1,125 @@ -from functools import wraps +### RIP Twitter -from tornado.testing import AsyncHTTPTestCase -from torndb import Connection -from tornado.httpclient import HTTPRequest -from tornado.options import options -import tornado.ioloop -import Cookie -import base64 -import time -import os -import hashlib +# from functools import wraps -from base import BaseAsyncTestCase -from models import User, Sharedfile, Sourcefile, Externalservice +# from tornado.testing import AsyncHTTPTestCase +# from torndb import Connection +# from tornado.options import options +# import tornado.ioloop +# import http.cookies +# import base64 +# import time +# import os +# import hashlib +# from .base import BaseAsyncTestCase +# from models import User, Sharedfile, Sourcefile, Externalservice -def twittertest(fn): - # This would be a "skip" if unittest v1 supported skipping. - @wraps(fn) - def test(self): - if options.twitter_consumer_key: - return fn(self) - return test +# def twittertest(fn): +# # This would be a "skip" if unittest v1 supported skipping. +# @wraps(fn) +# def test(self): +# if options.twitter_consumer_key: +# return fn(self) +# return test -class TwitterTests(BaseAsyncTestCase): - def setUp(self): - super(TwitterTests, self).setUp() - self.user = User(name="admin", email="admin@mltshp.com", email_confirmed=1, is_paid=1) - self.user.set_password('asdfasdf') - self.user.save() - self.sid = self.sign_in("admin", "asdfasdf") + +# class TwitterTests(BaseAsyncTestCase): +# def setUp(self): +# super(TwitterTests, self).setUp() +# self.user = User(name="admin", email="admin@mltshp.com", email_confirmed=1, is_paid=1) +# self.user.set_password('asdfasdf') +# self.user.save() +# self.sid = self.sign_in("admin", "asdfasdf") - self.externalservice = Externalservice(user_id=self.user.id, service_id=555, screen_name='mltshp', type=Externalservice.TWITTER, service_key="blah", service_secret="mah") - self.externalservice.save() +# self.externalservice = Externalservice(user_id=self.user.id, service_id=555, screen_name='mltshp', type=Externalservice.TWITTER, service_key="blah", service_secret="mah") +# self.externalservice.save() - def get_new_ioloop(self): - return tornado.ioloop.IOLoop.instance() +# def get_new_ioloop(self): +# return tornado.ioloop.IOLoop.instance() - @twittertest - def test_twitter_connect(self): - request = HTTPRequest(self.get_url("/tools/twitter/connect"), 'GET', {'Cookie':"sid=%s" % (self.sid)}, follow_redirects=False) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.headers['location'].startswith("https://api.twitter.com/oauth/authorize?oauth")) +# @twittertest +# def test_twitter_connect(self): +# response = self.fetch("/tools/twitter/connect", method='GET', headers={'Cookie':"sid=%s" % (self.sid)}, follow_redirects=False) +# self.assertTrue(response.headers['location'].startswith("https://api.twitter.com/oauth/authorize?oauth")) - def test_post_from_twitter(self): - #provider = "https://api.twitter.com/1.1/account/verify_credentials.json" - provider = self.get_url('/heartbeat') +# def test_post_from_twitter(self): +# #provider = "https://api.twitter.com/1.1/account/verify_credentials.json" +# provider = self.get_url('/heartbeat') - """ - Copies a file to the file-system, then POSTs the location and details to the upload method - for processing - """ - file_path = os.path.abspath("test/files/1.png") - sha1 = Sourcefile.get_sha1_file_key(file_path) - content_type = "image/png" +# """ +# Copies a file to the file-system, then POSTs the location and details to the upload method +# for processing +# """ +# file_path = os.path.abspath("test/files/1.png") +# sha1 = Sourcefile.get_sha1_file_key(file_path) +# content_type = "image/png" - file_name = os.path.basename(file_path) - file_size = os.path.getsize(file_path) - body = "media_name=%s&media_content_type=%s&media_sha1=%s&media_size=%s&media_path=%s" % (file_name, content_type, sha1, file_size, file_path) +# file_name = os.path.basename(file_path) +# file_size = os.path.getsize(file_path) +# body = "media_name=%s&media_content_type=%s&media_sha1=%s&media_size=%s&media_path=%s" % (file_name, content_type, sha1, file_size, file_path) - request = HTTPRequest( - url=self.get_url('/upload'), - method='POST', - headers={'X-Auth-Service-Provider':provider, 'X-Verify-Credentials-Authorization': 'OAuth oauth_timestamp="1290404453", oauth_version="1.0", oauth_consumer_key="IQKbtAYlXLripLGPWd0HUA", oauth_token="37458155-JCG7c8oejM6N4TK4HJbXVC5VGq1gtaSUPt90wxFI", oauth_signature="9QxkJqBAJfZ83sbz6SCJKSaPn9U%3D", oauth_nonce="C7AB0CBC-9193-44EE-AFC1-6FE3BA51F048", oauth_signature_method="HMAC-SHA1"'}, - body=body, - ) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertEqual(response.code, 200) - self.assertEqual(response.body, "https://s.mltshp.com/r/1.png") - sf = Sharedfile.get("id = %s", 1) - self.assertEqual(sf.id, 1) - self.assertEqual(sf.name, '1.png') - self.assertEqual(sf.user_id, self.user.id) +# response = self.fetch( +# '/upload', +# method='POST', +# headers={'X-Auth-Service-Provider':provider, 'X-Verify-Credentials-Authorization': 'OAuth oauth_timestamp="1290404453", oauth_version="1.0", oauth_consumer_key="IQKbtAYlXLripLGPWd0HUA", oauth_token="37458155-JCG7c8oejM6N4TK4HJbXVC5VGq1gtaSUPt90wxFI", oauth_signature="9QxkJqBAJfZ83sbz6SCJKSaPn9U%3D", oauth_nonce="C7AB0CBC-9193-44EE-AFC1-6FE3BA51F048", oauth_signature_method="HMAC-SHA1"'}, +# body=body, +# ) +# self.assertEqual(response.code, 200) +# self.assertEqual(response.body, "https://s.mltshp.com/r/1.png") +# sf = Sharedfile.get("id = %s", 1) +# self.assertEqual(sf.id, 1) +# self.assertEqual(sf.name, '1.png') +# self.assertEqual(sf.user_id, self.user.id) - def test_posting_fails_when_provider_is_not_localhost(self): - provider = "http://notes.torrez.org" - """ - Copies a file to the file-system, then POSTs the location and details to the upload method - for processing - """ - file_path = os.path.abspath("test/files/1.png") - sha1 = Sourcefile.get_sha1_file_key(file_path) - content_type = "image/png" +# def test_posting_fails_when_provider_is_not_localhost(self): +# provider = "http://notes.torrez.org" +# """ +# Copies a file to the file-system, then POSTs the location and details to the upload method +# for processing +# """ +# file_path = os.path.abspath("test/files/1.png") +# sha1 = Sourcefile.get_sha1_file_key(file_path) +# content_type = "image/png" - file_name = os.path.basename(file_path) - file_size = os.path.getsize(file_path) - body = "media_name=%s&media_content_type=%s&media_sha1=%s&media_size=%s&media_path=%s" % (file_name, content_type, sha1, file_size, file_path) +# file_name = os.path.basename(file_path) +# file_size = os.path.getsize(file_path) +# body = "media_name=%s&media_content_type=%s&media_sha1=%s&media_size=%s&media_path=%s" % (file_name, content_type, sha1, file_size, file_path) - request = HTTPRequest( - url=self.get_url('/upload'), - method='POST', - headers={'X-Auth-Service-Provider':provider, 'X-Verify-Credentials-Authorization': 'OAuth oauth_timestamp="1290404453", oauth_version="1.0", oauth_consumer_key="IQKbtAYlXLripLGPWd0HUA", oauth_token="37458155-JCG7c8oejM6N4TK4HJbXVC5VGq1gtaSUPt90wxFI", oauth_signature="9QxkJqBAJfZ83sbz6SCJKSaPn9U%3D", oauth_nonce="C7AB0CBC-9193-44EE-AFC1-6FE3BA51F048", oauth_signature_method="HMAC-SHA1"'}, - body=body, - ) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertEqual(response.code, 403) +# response = self.fetch( +# '/upload', +# method='POST', +# headers={'X-Auth-Service-Provider':provider, 'X-Verify-Credentials-Authorization': 'OAuth oauth_timestamp="1290404453", oauth_version="1.0", oauth_consumer_key="IQKbtAYlXLripLGPWd0HUA", oauth_token="37458155-JCG7c8oejM6N4TK4HJbXVC5VGq1gtaSUPt90wxFI", oauth_signature="9QxkJqBAJfZ83sbz6SCJKSaPn9U%3D", oauth_nonce="C7AB0CBC-9193-44EE-AFC1-6FE3BA51F048", oauth_signature_method="HMAC-SHA1"'}, +# body=body, +# ) +# self.assertEqual(response.code, 403) - def test_post_from_twitter_with_message(self): - provider = self.get_url('/heartbeat') +# def test_post_from_twitter_with_message(self): +# provider = self.get_url('/heartbeat') - """ - Copies a file to the file-system, then POSTs the location and details to the upload method - for processing - """ - file_path = os.path.abspath("test/files/1.png") - sha1 = Sourcefile.get_sha1_file_key(file_path) - content_type = "image/png" +# """ +# Copies a file to the file-system, then POSTs the location and details to the upload method +# for processing +# """ +# file_path = os.path.abspath("test/files/1.png") +# sha1 = Sourcefile.get_sha1_file_key(file_path) +# content_type = "image/png" - file_name = os.path.basename(file_path) - file_size = os.path.getsize(file_path) - message = "hey look\r\n at me!\r\n" - body = "message=%s&media_name=%s&media_content_type=%s&media_sha1=%s&media_size=%s&media_path=%s" % (message, file_name, content_type, sha1, file_size, file_path) +# file_name = os.path.basename(file_path) +# file_size = os.path.getsize(file_path) +# message = "hey look\r\n at me!\r\n" +# body = "message=%s&media_name=%s&media_content_type=%s&media_sha1=%s&media_size=%s&media_path=%s" % (message, file_name, content_type, sha1, file_size, file_path) - request = HTTPRequest( - url=self.get_url('/upload'), - method='POST', - headers={'X-Auth-Service-Provider':provider, 'X-Verify-Credentials-Authorization': 'OAuth oauth_timestamp="1290404453", oauth_version="1.0", oauth_consumer_key="IQKbtAYlXLripLGPWd0HUA", oauth_token="37458155-JCG7c8oejM6N4TK4HJbXVC5VGq1gtaSUPt90wxFI", oauth_signature="9QxkJqBAJfZ83sbz6SCJKSaPn9U%3D", oauth_nonce="C7AB0CBC-9193-44EE-AFC1-6FE3BA51F048", oauth_signature_method="HMAC-SHA1"'}, - body=body, - ) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertEqual(response.code, 200) - self.assertEqual(response.body, "https://s.mltshp.com/r/1.png") - sf = Sharedfile.get("id = %s", 1) - self.assertEqual(sf.id, 1) - self.assertEqual(sf.get_title(), message.replace('\n', '').replace('\r', '')) - self.assertEqual(sf.user_id, self.user.id) +# response = self.fetch( +# '/upload', +# method='POST', +# headers={'X-Auth-Service-Provider':provider, 'X-Verify-Credentials-Authorization': 'OAuth oauth_timestamp="1290404453", oauth_version="1.0", oauth_consumer_key="IQKbtAYlXLripLGPWd0HUA", oauth_token="37458155-JCG7c8oejM6N4TK4HJbXVC5VGq1gtaSUPt90wxFI", oauth_signature="9QxkJqBAJfZ83sbz6SCJKSaPn9U%3D", oauth_nonce="C7AB0CBC-9193-44EE-AFC1-6FE3BA51F048", oauth_signature_method="HMAC-SHA1"'}, +# body=body, +# ) +# self.assertEqual(response.code, 200) +# self.assertEqual(response.body, "https://s.mltshp.com/r/1.png") +# sf = Sharedfile.get("id = %s", 1) +# self.assertEqual(sf.id, 1) +# self.assertEqual(sf.get_title(), message.replace('\n', '').replace('\r', '')) +# self.assertEqual(sf.user_id, self.user.id) diff --git a/test/FileTests.py b/test/FileTests.py index 859c9cdf..54d8ab54 100644 --- a/test/FileTests.py +++ b/test/FileTests.py @@ -1,22 +1,16 @@ -from tornado.testing import AsyncHTTPTestCase -from torndb import Connection -from tornado.httpclient import HTTPRequest -from tornado.httpclient import AsyncHTTPClient -from tornado.escape import url_escape, url_unescape, json_decode, json_encode +from tornado.escape import url_escape, json_decode from tornado.options import options import tornado.ioloop -import handlers -import base64 import time import json import os -from urlparse import urlparse +from urllib.parse import urlparse from contextlib import contextmanager -from base import BaseAsyncTestCase +from .base import BaseAsyncTestCase -from models import Sharedfile, Sourcefile, Favorite, User, Shake, Shakesharedfile, Post, Notification +from models import Sharedfile, Sourcefile, User, Shakesharedfile, Post from lib.utilities import base36encode @@ -37,7 +31,7 @@ def setUp(self): self.user.save() self.sid = self.sign_in("admin", "asdfasdf") - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.test_file1_path = os.path.abspath("test/files/1.png") self.test_file1_sha1 = Sourcefile.get_sha1_file_key(self.test_file1_path) @@ -46,8 +40,7 @@ def setUp(self): self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) def test_deleting_file_sets_to_true(self): - self.http_client.fetch(HTTPRequest(self.get_url("/p/1/delete"), 'POST', {'Cookie': "_xsrf=%s;sid=%s" % (self.xsrf, self.sid)}, "_xsrf=%s" % (self.xsrf)), self.stop) - response = self.wait() + response = self.fetch("/p/1/delete", method='POST', headers={'Cookie': "_xsrf=%s;sid=%s" % (self.xsrf, self.sid)}, body="_xsrf=%s" % self.xsrf) sf = Sharedfile.get("id=1") self.assertEqual(sf.deleted, 1) @@ -58,14 +51,12 @@ def test_delete_button_only_shows_for_owner(self): bill.save() self.sign_in("bill", "asdfasdf") - self.http_client.fetch(HTTPRequest(self.get_url("/p/1"), 'GET', {'Cookie':'sid=%s' % (self.sid)}), self.stop) - response = self.wait() - self.assertEqual(response.body.find('/p/1/delete'), -1) + response = self.fetch("/p/1", method='GET', headers={'Cookie':'sid=%s' % self.sid}) + self.assertEqual(response.body.find('/p/1/delete'.encode("ascii")), -1) self.sign_in("admin", "asdfasdf") - self.http_client.fetch(HTTPRequest(self.get_url("/p/1"), 'GET', {'Cookie':'sid=%s' % (self.sid)}), self.stop) - response = self.wait() - self.assertTrue(response.body.find('/p/1/delete') > 0) + response = self.fetch("/p/1", method='GET', headers={'Cookie':'sid=%s' % self.sid}) + self.assertIn('/p/1/delete', response.body) def test_delete_button_only_works_for_owner(self): bill = User(name='bill', email='bill@mltshp.com', email_confirmed=1, is_paid=1) @@ -73,8 +64,7 @@ def test_delete_button_only_works_for_owner(self): bill.save() sid = self.sign_in("bill", "asdfasdf") - self.http_client.fetch(HTTPRequest(self.get_url("/p/1/delete"), 'POST', {'Cookie': "_xsrf=%s;sid=%s" % (self.xsrf, sid)}, "_xsrf=%s" % (self.xsrf)), self.stop) - response = self.wait() + response = self.fetch("/p/1/delete", method='POST', headers={'Cookie': "_xsrf=%s;sid=%s" % (self.xsrf, sid)}, body="_xsrf=%s" % self.xsrf) sf = Sharedfile.get("id=1") self.assertEqual(sf.deleted, 0) @@ -94,7 +84,7 @@ def setUp(self): self.sid2 = self.sign_in('user', 'asdfasdf') self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.test_file1_path = os.path.abspath("test/files/1.png") self.test_file1_sha1 = Sourcefile.get_sha1_file_key(self.test_file1_path) @@ -106,18 +96,14 @@ def setUp(self): def test_raw_image_view_counts(self): response = self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - request = HTTPRequest(self.get_url('/user/admin'), 'GET', {"Cookie":"sid=%s" % (self.sid2)}) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find("1.png") > 0) + response = self.fetch('/user/admin', method='GET', headers={"Cookie":"sid=%s" % self.sid2}) + self.assertIn("1.png", response.body) for i in range(0,10): if i % 2 == 0: - request = HTTPRequest(self.get_url('/r/1'), 'GET', {"Cookie":"sid=%s" % (self.sid2)}) + response = self.fetch('/r/1', method='GET', headers={"Cookie":"sid=%s" % self.sid2}) else: - request = HTTPRequest(self.get_url('/r/1'), 'GET') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/r/1', method='GET') imageviews = self.db.query("SELECT id, user_id, sharedfile_id, created_at from fileview") self.assertEqual(len(imageviews), 10) @@ -127,18 +113,14 @@ def test_raw_image_view_counts(self): def test_raw_load_with_extension(self): response = self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - request = HTTPRequest(self.get_url('/user/admin'), 'GET', {"Cookie":"sid=%s" % (self.sid2)}) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find("1.png") > 0) + response = self.fetch('/user/admin', method='GET', headers={"Cookie":"sid=%s" % self.sid2}) + self.assertIn("1.png", response.body) for i in range(0,10): if i % 2 == 0: - request = HTTPRequest(self.get_url('/r/1.jpg'), 'GET', {"Cookie":"sid=%s" % (self.sid2)}) + response = self.fetch('/r/1.jpg', method='GET', headers={"Cookie":"sid=%s" % self.sid2}) else: - request = HTTPRequest(self.get_url('/r/1.jpg'), 'GET') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/r/1.jpg', method='GET') imageviews = self.db.query("SELECT id, user_id, sharedfile_id, created_at from fileview") self.assertEqual(len(imageviews), 10) @@ -153,8 +135,7 @@ def test_delete_image_raw_404s(self): self.assertEqual("1.png", sf.name) sf.delete() - self.http_client.fetch(self.get_url('/r/%s' % (sf.share_key)), self.stop) - response = self.wait() + response = self.fetch('/r/%s' % sf.share_key) self.assertEqual(response.error.code, 404) def test_raw_head_handler(self): @@ -163,10 +144,9 @@ def test_raw_head_handler(self): sf = Sharedfile.get("id=1") self.assertEqual("1.png", sf.name) - self.http_client.fetch(HTTPRequest(self.get_url('/r/%s' % (sf.share_key)), 'HEAD'), self.stop) - response = self.wait() + response = self.fetch('/r/%s' % sf.share_key, method='HEAD') self.assertEqual(response.headers['Content-Type'], 'image/png') - self.assertEqual(response.body, '') + self.assertEqual(response.body, b'') def test_delete_image_permalink_404s(self): response = self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) @@ -174,8 +154,7 @@ def test_delete_image_permalink_404s(self): self.assertEqual("1.png", sf.name) sf.delete() - self.http_client.fetch(self.get_url('/p/%s' % (sf.share_key)), self.stop, follow_redirects=False) - response = self.wait() + response = self.fetch('/p/%s' % sf.share_key, follow_redirects=False) self.assertEqual(response.error.code, 404) @@ -186,8 +165,7 @@ def setUp(self): self.user.set_password('asdfasdf') self.user.save() self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() - + self.xsrf = self.get_xsrf().decode("ascii") self.test_file1_path = os.path.abspath("test/files/1.png") self.test_file1_sha1 = Sourcefile.get_sha1_file_key(self.test_file1_path) @@ -200,8 +178,7 @@ def setUp(self): def test_oembed_response_json(self): with test_option("cdn_host", "cdn-service.com"): response = self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - self.http_client.fetch(self.get_url("/services/oembed?url=http%3A//mltshp.com/p/1"), self.stop) - response = self.wait() + response = self.fetch("/services/oembed?url=http%3A//mltshp.com/p/1") j = json.loads(response.body) self.assertEqual(j['width'], 1) self.assertEqual(j['height'], 1) @@ -214,12 +191,11 @@ def test_oembed_response_json(self): sharedfile = Sharedfile.get('id = %s', 1) file_time_stamp = int(time.mktime(sharedfile.created_at.timetuple())) callback = "jsonp" + str(file_time_stamp) - self.http_client.fetch(self.get_url("/services/oembed?url=http%3A//mltshp.com/p/1&jsoncallback=" + callback), self.stop) - response = self.wait() + response = self.fetch("/services/oembed?url=http%3A//mltshp.com/p/1&jsoncallback=" + callback) j = json.loads(response.body.strip()[len(callback)+1:-1]) self.assertEqual(j['callback'], callback) - self.assertTrue(response.body.startswith(callback)) + self.assertTrue(response.body.startswith(callback.encode("ascii"))) def test_oembed_response_json_for_link(self): url = 'https://vimeo.com/20379529' @@ -232,8 +208,7 @@ def test_oembed_response_json_for_link(self): sharedfile.save() sharedfile = Sharedfile.get('id = %s', 1) file_time_stamp = int(time.mktime(sharedfile.created_at.timetuple())) - self.http_client.fetch(self.get_url("/services/oembed?url=http%3A//mltshp.com/p/1"), self.stop) - response = self.wait() + response = self.fetch("/services/oembed?url=http%3A//mltshp.com/p/1") j_response = json_decode(response.body) self.assertEqual(j_response['type'], "link") self.assertEqual(j_response['url'], url) @@ -241,8 +216,7 @@ def test_oembed_response_json_for_link(self): def test_oembed_malformed_requests(self): malformed_requests = ['http%3A//mltshp.com/p', 'http%3A//mltshp.com/p/', 'http%3A//mltshp.com/', 'http%3A//mltshp.com/r/1', 'NaN', 'http%3A//cnn.com/p/1'] for request in malformed_requests: - self.http_client.fetch(self.get_url("/services/oembed?url=%s" % request), self.stop) - response = self.wait() + response = self.fetch("/services/oembed?url=%s" % request) self.assertEqual(response.code, 404) def test_title_pulls_from_name_if_blank_or_null(self): @@ -252,33 +226,25 @@ def test_title_pulls_from_name_if_blank_or_null(self): def test_quick_edit_title(self): self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - request = HTTPRequest(self.get_url('/p/1/quick-edit-title'), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "_xsrf=%s&title=%s" % (self.xsrf, url_escape("Monkey Business"))) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/1/quick-edit-title', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="_xsrf=%s&title=%s" % (self.xsrf, url_escape("Monkey Business"))) j = json_decode(response.body) self.assertEqual(j['title'], 'Monkey Business') def test_quick_edit_description(self): self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - request = HTTPRequest(self.get_url('/p/1/quick-edit-description'), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "_xsrf=%s&description=%s" % (self.xsrf, url_escape('Bilbo\nbaggins'))) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/1/quick-edit-description', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="_xsrf=%s&description=%s" % (self.xsrf, url_escape('Bilbo\nbaggins'))) j = json_decode(response.body) self.assertEqual(j['description_raw'], 'Bilbo\nbaggins') def test_quick_edit_alt_text(self): self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - request = HTTPRequest(self.get_url('/p/1/quick-edit-alt-text'), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "_xsrf=%s&alt_text=%s" % (self.xsrf, url_escape('A small person carrying a ring'))) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/1/quick-edit-alt-text', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="_xsrf=%s&alt_text=%s" % (self.xsrf, url_escape('A small person carrying a ring'))) j = json_decode(response.body) self.assertEqual(j['alt_text_raw'], 'A small person carrying a ring') def test_quick_edit_source_url(self): self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - request = HTTPRequest(self.get_url('/p/1/quick-edit-source-url'), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "_xsrf=%s&source_url=%s" % (self.xsrf, url_escape('http://www.example.com/'))) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/1/quick-edit-source-url', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="_xsrf=%s&source_url=%s" % (self.xsrf, url_escape('http://www.example.com/'))) j = json_decode(response.body) self.assertEqual(j['source_url'], 'http://www.example.com/') @@ -291,31 +257,24 @@ def setUp(self): self.user.save() self.user_shake = self.user.shake() self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() - - def get_new_ioloop(self): - return tornado.ioloop.IOLoop.instance() + self.xsrf = self.get_xsrf().decode("ascii") def test_save_video_allows_link_to_vimeo_youtube(self): video_sites = {'vimeo':'https://vimeo.com/20379529', 'youtube':'https://www.youtube.com/watch?v=EmcMG4uxiHk', 'flickr':'https://www.flickr.com/photos/dahliablack/5497635343/'} - for site in video_sites.keys(): - request = HTTPRequest(self.get_url('/tools/save-video?url=%s' % (url_escape(video_sites[site]))), 'GET', {"Cookie":"sid=%s" % (self.sid)}) - self.http_client.fetch(request, self.stop) - response = self.wait() + for site in list(video_sites.keys()): + response = self.fetch('/tools/save-video?url=%s' % (url_escape(video_sites[site])), method='GET', headers={"Cookie":"sid=%s" % self.sid}) if site == 'vimeo': - self.assertTrue(response.body.find('value="https://vimeo.com/20379529">') > -1) + self.assertIn('value="https://vimeo.com/20379529">', response.body) elif site == 'youtube': - self.assertTrue(response.body.find('value="https://www.youtube.com/watch?v=EmcMG4uxiHk">') > -1) + self.assertIn('value="https://www.youtube.com/watch?v=EmcMG4uxiHk">', response.body) elif site == 'flickr': - self.assertTrue(response.body.find('value="https://www.flickr.com/photos/dahliablack/5497635343/">') > -1) + self.assertIn('value="https://www.flickr.com/photos/dahliablack/5497635343/">', response.body) def test_save_video_correctly_processes_various_youtube_urls(self): urls = ['https://www.youtube.com/watch?v=EmcMG4uxiHk&recommended=0', 'https://youtu.be/EmcMG4uxiHk', 'https://www.youtube.com/watch?v=EmcMG4uxiHk&feature=rec-LGOUT-real_rev-rn-1r-11-HM'] for url in urls: - request = HTTPRequest(self.get_url('/tools/save-video?url=%s' % (url_escape(url))), 'GET', {"Cookie":"sid=%s" % (self.sid)}) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find('value="https://www.youtube.com/watch?v=EmcMG4uxiHk">') > -1) + response = self.fetch('/tools/save-video?url=%s' % url_escape(url), method='GET', headers={"Cookie":"sid=%s" % self.sid}) + self.assertIn('value="https://www.youtube.com/watch?v=EmcMG4uxiHk">', response.body) def test_adding_video_makes_it_show_up_in_friends_shake(self): user2 = User(name='user2', email='user2@mltshp.com', email_confirmed=1, is_paid=1) @@ -324,9 +283,7 @@ def test_adding_video_makes_it_show_up_in_friends_shake(self): user2.subscribe(self.user.shake()) url = 'https://vimeo.com/20379529' - request = HTTPRequest(self.get_url('/tools/save-video'), 'POST', {"Cookie":"sid=%s;_xsrf=%s" % (self.sid, self.xsrf)}, "url=%s&_xsrf=%s" % (url_escape(url), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/tools/save-video', method='POST', headers={"Cookie":"sid=%s;_xsrf=%s" % (self.sid, self.xsrf)}, body="url=%s&_xsrf=%s" % (url_escape(url), self.xsrf)) sfs = Sharedfile.from_subscriptions(user2.id) self.assertTrue(len(sfs) > 0) self.assertEqual(sfs[0].name , url) @@ -340,31 +297,23 @@ def setUp(self): self.user.save() self.user_shake = self.user.shake() self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.url = 'http://notes.torrez.org/images/categories/television.png?x=1' self.source_url = url_escape('http://notes.torrez.org/') self.description = "This is a multi-\nline\ndescription" - def get_new_ioloop(self): - return tornado.ioloop.IOLoop.instance() - def test_picker_not_authed_displays_sign_in(self): - self.http_client.fetch(self.get_url('/tools/p?url=%s' % (self.url)), self.stop) - response = self.wait() - self.assertTrue(response.body.find('action="/sign-in/"') > 0) + response = self.fetch('/tools/p?url=%s' % self.url) + self.assertIn('action="/sign-in/"', response.body) def test_picker_get_displays_image_passed_in(self): - request = HTTPRequest(self.get_url('/tools/p?url=%s' % (self.url)), 'GET', {"Cookie":"sid=%s" % (self.sid)}) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find('hidden" name="url" value="%s"' % (self.url)) > 0) + response = self.fetch('/tools/p?url=%s' % self.url, method='GET', headers={"Cookie":"sid=%s" % self.sid}) + self.assertIn('hidden" name="url" value="%s"' % self.url, response.body) def test_picker_authenticated_stores_image(self): - request = HTTPRequest(self.get_url('/tools/p'), 'POST', {"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, "_xsrf=%s&url=%s&title=boatmoatgoat" % (self.xsrf, self.url)) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find("ERROR") == -1) + response = self.fetch('/tools/p', method='POST', headers={"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, body="_xsrf=%s&url=%s&title=boatmoatgoat" % (self.xsrf, self.url)) + self.assertNotIn("ERROR", response.body) sf = Sharedfile.get("id=1") self.assertEqual(sf.name, "television.png") self.assertEqual(sf.title, "boatmoatgoat") @@ -383,53 +332,39 @@ def test_picker_errors(self): host = "http://localhost:%s/tools/p?url=" % (self.get_http_port()) bad_urls = ["http://", "hps://sdlfkj.com/asdlkfj", "something.com/file.jpg"] for url in bad_urls: - request = HTTPRequest(self.get_url('/tools/p?url=%s' % (url)), 'GET', {"Cookie":"sid=%s" % (self.sid)}) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/tools/p?url=%s' % url, method='GET', headers={"Cookie":"sid=%s" % self.sid}) self.assertTrue(response.error) def test_picker_stores_image_and_shakesharedfile(self): - request = HTTPRequest(self.get_url('/tools/p'), 'POST', {"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, "_xsrf=%s&url=%s&title=boatmoatgoat" % (self.xsrf, self.url)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/tools/p', method='POST', headers={"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, body="_xsrf=%s&url=%s&title=boatmoatgoat" % (self.xsrf, self.url)) ssf = Shakesharedfile.get("sharedfile_id=1 and shake_id=%s", self.user_shake.id) self.assertTrue(ssf) def test_picker_stores_source_url(self): - request = HTTPRequest(self.get_url('/tools/p'), 'POST', {"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, "_xsrf=%s&url=%s&title=boatmoatgoat&source_url=%s" % (self.xsrf, self.url, self.source_url)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/tools/p', method='POST', headers={"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, body="_xsrf=%s&url=%s&title=boatmoatgoat&source_url=%s" % (self.xsrf, self.url, self.source_url)) sf = Sharedfile.get("id=1") self.assertEqual(sf.source_url, 'http://notes.torrez.org/') def test_picker_stores_description(self): - request = HTTPRequest(self.get_url('/tools/p'), 'POST', {"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, "_xsrf=%s&url=%s&title=boatmoatgoat&description=%s" % (self.xsrf, url_escape(self.url), url_escape(self.description))) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find("ERROR") == -1) + response = self.fetch('/tools/p', method='POST', headers={"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, body="_xsrf=%s&url=%s&title=boatmoatgoat&description=%s" % (self.xsrf, url_escape(self.url), url_escape(self.description))) + self.assertNotIn("ERROR", response.body) sf = Sharedfile.get("id=1") self.assertEqual(sf.description, self.description) def test_picker_doesnt_see_filepile(self): - request = HTTPRequest(self.get_url('/tools/p?url=%s' % (url_escape("http://www.filepile.org/something/something"))), 'GET', {"Cookie":"sid=%s" % (self.sid)}) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertEqual(response.body.find('source: http://www.filepile.org'), -1) + response = self.fetch('/tools/p?url=%s' % url_escape("http://www.filepile.org/something/something"), method='GET', headers={"Cookie":"sid=%s" % self.sid}) + self.assertEqual(response.body.find('source: http://www.filepile.org'.encode("ascii")), -1) def test_picker_strips_google_reader_url(self): - request = HTTPRequest(self.get_url('/tools/p?url=%s&source_url=%s' % (self.url, url_escape("http://laughingsquid.com/skeleton-light-painting/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A laughingsquid %28Laughing Squid%29"))), 'GET', {"Cookie":"sid=%s" % (self.sid)}) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find("source: http://laughingsquid.com/skeleton-light-painting/") > 0) + response = self.fetch('/tools/p?url=%s&source_url=%s' % (self.url, url_escape("http://laughingsquid.com/skeleton-light-painting/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A laughingsquid %28Laughing Squid%29")), method='GET', headers={"Cookie":"sid=%s" % self.sid}) + self.assertIn("source: http://laughingsquid.com/skeleton-light-painting/", response.body) def test_picker_strips_google_img_url(self): """ https://www.google.com/imgres?imgurl=http://cragganmorefarm.com/user/gimage/Baby-Ground-hogs_480_320.jpg&imgrefurl=http://cragganmorefarm.com/&usg=__kpRJbm_WBlbEnqDvfi3A2JuJ9Wg=&h=320&w=480&sz=33&hl=en&start=24&sig2=SyR_NSDovcsOYu5tJYtlig&zoom=1&tbnid=TT5jIOrb76kqbM:&tbnh=130&tbnw=173&ei=f5lJTdjbHoL6lweT2cU3&prev=/images%3Fq%3Dbaby%2Bgroundhogs%26um%3D1%26hl%3Den%26client%3Dfirefox-a%26sa%3DX%26rls%3Dorg.mozilla:en-US:official%26biw%3D1152%26bih%3D709%26tbs%3Disch:10%2C540&um=1&itbs=1&iact=rc&dur=326&oei=YZlJTYuYJsH78AaQh6msDg&esq=2&page=2&ndsp=24&ved=1t:429,r:8,s:24&tx=103&ty=85&biw=1152&bih=709 """ - request = HTTPRequest(self.get_url('/tools/p?url=%s&source_url=%s' % (self.url, url_escape("https://www.google.com/imgres?imgurl=http://cragganmorefarm.com/user/gimage/Baby-Ground-hogs_480_320.jpg&imgrefurl=http://cragganmorefarm.com/&usg=__kpRJbm_WBlbEnqDvfi3A2JuJ9Wg=&h=320&w=480&sz=33&hl=en&start=24&sig2=SyR_NSDovcsOYu5tJYtlig&zoom=1&tbnid=TT5jIOrb76kqbM:&tbnh=130&tbnw=173&ei=f5lJTdjbHoL6lweT2cU3&prev=/images%3Fq%3Dbaby%2Bgroundhogs%26um%3D1%26hl%3Den%26client%3Dfirefox-a%26sa%3DX%26rls%3Dorg.mozilla:en-US:official%26biw%3D1152%26bih%3D709%26tbs%3Disch:10%2C540&um=1&itbs=1&iact=rc&dur=326&oei=YZlJTYuYJsH78AaQh6msDg&esq=2&page=2&ndsp=24&ved=1t:429,r:8,s:24&tx=103&ty=85&biw=1152&bih=709"))), 'GET', {"Cookie":"sid=%s" % (self.sid)}) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find("source: http://cragganmorefarm.com/") > 0) + response = self.fetch('/tools/p?url=%s&source_url=%s' % (self.url, url_escape("https://www.google.com/imgres?imgurl=http://cragganmorefarm.com/user/gimage/Baby-Ground-hogs_480_320.jpg&imgrefurl=http://cragganmorefarm.com/&usg=__kpRJbm_WBlbEnqDvfi3A2JuJ9Wg=&h=320&w=480&sz=33&hl=en&start=24&sig2=SyR_NSDovcsOYu5tJYtlig&zoom=1&tbnid=TT5jIOrb76kqbM:&tbnh=130&tbnw=173&ei=f5lJTdjbHoL6lweT2cU3&prev=/images%3Fq%3Dbaby%2Bgroundhogs%26um%3D1%26hl%3Den%26client%3Dfirefox-a%26sa%3DX%26rls%3Dorg.mozilla:en-US:official%26biw%3D1152%26bih%3D709%26tbs%3Disch:10%2C540&um=1&itbs=1&iact=rc&dur=326&oei=YZlJTYuYJsH78AaQh6msDg&esq=2&page=2&ndsp=24&ved=1t:429,r:8,s:24&tx=103&ty=85&biw=1152&bih=709")), method='GET', headers={"Cookie":"sid=%s" % self.sid}) + self.assertIn("source: http://cragganmorefarm.com/", response.body) class FileUploadTests(BaseAsyncTestCase): def setUp(self): @@ -439,7 +374,7 @@ def setUp(self): self.user.save() self.user_shake = self.user.shake() self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.test_file1_path = os.path.abspath("test/files/1.png") self.test_file1_sha1 = Sourcefile.get_sha1_file_key(self.test_file1_path) @@ -452,6 +387,7 @@ def setUp(self): def test_file_upload_size_check(self): response = self.upload_test_file() sharedfile = Sharedfile.get('id=1') + self.assertIsNotNone(sharedfile) sourcefile = sharedfile.sourcefile() self.assertEqual(sourcefile.width, 640) self.assertEqual(sourcefile.height, 643) @@ -459,6 +395,7 @@ def test_file_upload_size_check(self): def test_file_upload_with_user(self): response = self.upload_test_file() shared_file = Sharedfile.get('id=1') + self.assertIsNotNone(shared_file) self.assertEqual(shared_file.name, "love.gif") self.assertEqual(shared_file.source_id, 1) self.assertEqual(shared_file.user_id, 1) @@ -469,8 +406,7 @@ def test_file_upload_user_missing(self): def test_file_upload_contents(self): response = self.upload_test_file() - self.http_client.fetch(self.get_url('/r/1'), self.stop) - response = self.wait() + response = self.fetch('/r/1') self.assertTrue(response.headers['X-Accel-Redirect'].startswith("/s3/originals/ac7180f6b038d5ae4f2297989e39a900995bb8fc?")) def test_uploading_file_creates_shared_shake_file(self): @@ -481,6 +417,7 @@ def test_uploading_file_creates_shared_shake_file(self): def test_uploading_to_a_shake_saves_shake_id_in_post(self): response = self.upload_test_file() all_posts = Post.all() + self.assertTrue(len(all_posts) > 0) self.assertEqual(1, all_posts[0].shake_id) def test_uploading_when_over_limit(self): @@ -497,14 +434,14 @@ def test_uploading_when_over_limit(self): self.user.stripe_plan_id = "mltshp-single" self.user.save() response = self.upload_test_file() - self.assertEqual(True, response.body.find('Single Scoop Account Limit') > -1) + self.assertEqual(True, response.body.find('Single Scoop Account Limit'.encode("ascii")) > -1) # but if they paid, we're good. self.user.stripe_plan_id = "mltshp-double" self.user.save() response = self.upload_test_file() - self.assertEqual(True, response.body.find('Single Scoop Account Limit') == -1) + self.assertEqual(True, response.body.find('Single Scoop Account Limit'.encode("ascii")) == -1) def test_uploading_file_creates_post_record_for_user(self): response = self.upload_test_file() @@ -524,7 +461,7 @@ def test_uploading_file_with_unsupported_content_type(self): """ response = self.upload_file(self.test_file1_path, self.test_file1_sha1, "image/tiff", 1, self.sid, self.xsrf) self.assertEqual(200, response.code) - self.assertTrue(response.body.find("We don't support that file type.") > 0) + self.assertIn("We don't support that file type.", response.body) posts = Post.all() self.assertEqual(len(posts), 0) diff --git a/test/SimpleTests.py b/test/SimpleTests.py index d9b24e4c..871e5736 100644 --- a/test/SimpleTests.py +++ b/test/SimpleTests.py @@ -2,7 +2,7 @@ import handlers import time from models import Sharedfile, Sourcefile, User -from base import BaseAsyncTestCase +from .base import BaseAsyncTestCase from handlers import base @@ -11,18 +11,15 @@ class TwoHundredTests(BaseAsyncTestCase): def test_sign_in(self): - self.http_client.fetch(self.get_url('/sign-in/'), self.stop) - response = self.wait() + response = self.fetch('/sign-in/') self.assertEqual(response.code, 200) def test_nonexistant(self): - self.http_client.fetch(self.get_url('/asdf/asdf'), self.stop) - response = self.wait() + response = self.fetch('/asdf/asdf') self.assertEqual(response.code, 404) def test_no_access_to_create_users(self): - self.http_client.fetch(self.get_url('/admin/create-users'), self.stop) - response = self.wait() + response = self.fetch('/admin/create-users') self.assertEqual(response.code, 403) def test_non_signed_in_permalink_view(self): @@ -33,11 +30,9 @@ def test_non_signed_in_permalink_view(self): sf = Sharedfile(source_id=src.id, user_id=1, name="some.jpg", title="some", share_key="1", content_type="image/jpg") sf.save() - self.http_client.fetch(self.get_url('/p/1'), self.stop) - response = self.wait() + response = self.fetch('/p/1') self.assertEqual(response.code, 200) def test_twitter_page(self): - self.http_client.fetch(self.get_url('/tools/twitter'), self.stop) - response = self.wait() + response = self.fetch('/tools/twitter') self.assertEqual(response.code, 200) diff --git a/test/SiteFunctionTests.py b/test/SiteFunctionTests.py index 15865ab9..f5555d2b 100644 --- a/test/SiteFunctionTests.py +++ b/test/SiteFunctionTests.py @@ -1,13 +1,7 @@ -from tornado.testing import AsyncHTTPTestCase -from torndb import Connection from tornado.options import options -from tornado.httpclient import HTTPRequest -import handlers -import base64 -import time import os -from base import BaseAsyncTestCase +from .base import BaseAsyncTestCase from models import Sourcefile, User @@ -19,7 +13,7 @@ def setUp(self): self.user.set_password('asdfasdf') self.user.save() self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.test_file1_path = os.path.abspath("test/files/1.png") self.test_file1_sha1 = Sourcefile.get_sha1_file_key(self.test_file1_path) @@ -32,7 +26,7 @@ def setUp(self): def test_account_images_page_works(self): response = self.upload_test_file() response = self.fetch_url('/user/admin') - self.assertTrue(response.body.find("/p/1") > 0) + self.assertIn("/p/1", response.body) def test_no_friends(self): response = self.fetch_url('/friends') @@ -52,7 +46,7 @@ def setUp(self): self.sid2 = self.sign_in("user2", "asdfasdf") self.sid = self.sign_in("admin", "asdfasdf") - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.test_file1_path = os.path.abspath("test/files/1.png") self.test_file1_sha1 = Sourcefile.get_sha1_file_key(self.test_file1_path) @@ -67,62 +61,52 @@ def test_cdn_image_view(self): self.test_file1_content_type, 1, self.sid, self.xsrf) options.use_cdn = True - request = HTTPRequest(self.get_url('/r/1'), 'GET', - {"Cookie": "sid=%s" % (self.sid), "Host": "s.mltshp.com"}, + response = self.fetch('/r/1', method='GET', + headers={"Cookie": "sid=%s" % self.sid, "Host": "s.mltshp.com"}, follow_redirects=False) - self.http_client.fetch(request, self.stop) - response = self.wait() options.use_cdn = False - self.assertEquals(response.headers['location'], 'https://mltshp-cdn.com/r/1') + self.assertEqual(response.headers['location'], 'https://mltshp-cdn.com/r/1') def test_cdn_image_view_with_width(self): response = self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) options.use_cdn = True - request = HTTPRequest(self.get_url('/r/1?width=550'), 'GET', - {"Cookie": "sid=%s" % (self.sid), "Host": "s.mltshp.com"}, + response = self.fetch('/r/1?width=550', method='GET', + headers={"Cookie": "sid=%s" % self.sid, "Host": "s.mltshp.com"}, follow_redirects=False) - self.http_client.fetch(request, self.stop) - response = self.wait() options.use_cdn = False - self.assertEquals(response.headers['location'], 'https://mltshp-cdn.com/r/1?width=550&dpr=1') + self.assertEqual(response.headers['location'], 'https://mltshp-cdn.com/r/1?width=550&dpr=1') def test_cdn_image_view_with_width_and_dpr(self): response = self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) options.use_cdn = True - request = HTTPRequest(self.get_url('/r/1?width=550&dpr=2'), 'GET', - {"Cookie": "sid=%s" % (self.sid), "Host": "s.mltshp.com"}, + response = self.fetch('/r/1?width=550&dpr=2', method='GET', + headers={"Cookie": "sid=%s" % self.sid, "Host": "s.mltshp.com"}, follow_redirects=False) - self.http_client.fetch(request, self.stop) - response = self.wait() options.use_cdn = False - self.assertEquals(response.headers['location'], 'https://mltshp-cdn.com/r/1?width=550&dpr=2') + self.assertEqual(response.headers['location'], 'https://mltshp-cdn.com/r/1?width=550&dpr=2') def test_raw_image_view_counts(self): response = self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - request = HTTPRequest(self.get_url('/user/admin'), 'GET', - {"Cookie":"sid=%s" % (self.sid)}) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find("1.png") > -1) + response = self.fetch('/user/admin', method='GET', + headers={"Cookie":"sid=%s" % self.sid}) + self.assertIn("1.png", response.body) for i in range(0,10): if i % 2 == 0: # views by owner aren't counted - request = HTTPRequest(self.get_url('/r/1'), 'GET', - {"Cookie":"sid=%s" % (self.sid2)}) + response = self.fetch('/r/1', method='GET', + headers={"Cookie":"sid=%s" % self.sid2}) else: # views by non-owner are counted - request = HTTPRequest(self.get_url('/r/1'), 'GET') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/r/1', method='GET') imageviews = self.db.query("SELECT id, user_id, sharedfile_id, created_at from fileview") self.assertEqual(len(imageviews), 10) diff --git a/test/base.py b/test/base.py index 457c6306..0b3a236a 100644 --- a/test/base.py +++ b/test/base.py @@ -1,14 +1,11 @@ -import tornado.ioloop -from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase -from tornado.httpclient import HTTPRequest +from tornado.testing import AsyncHTTPTestCase, ExpectLog from tornado.options import options -import Cookie +import http.cookies from lib.flyingcow import db as _db from main import MltshpApplication from tornado.escape import json_encode from routes import routes -import urllib -import shutil +import urllib.request, urllib.parse, urllib.error import os import time import base64 @@ -20,7 +17,7 @@ from models import User, Sourcefile -class BaseAsyncTestCase(AsyncHTTPTestCase, LogTrapTestCase): +class BaseAsyncTestCase(AsyncHTTPTestCase, ExpectLog): sid = '' def get_app(self): @@ -32,14 +29,12 @@ def get_app(self): def setUp(self): super(BaseAsyncTestCase, self).setUp() self.start_time = time.time() - self.io_loop.make_current() #### this line def get_httpserver_options(self): return {'no_keep_alive':False} def tearDown(self): super(BaseAsyncTestCase, self).tearDown() - diff = time.time() - self.start_time self.db.close() def sign_in(self, name, password): @@ -59,14 +54,14 @@ def sign_out(self): self.sid = None def get_sid(self, response): - cookie = Cookie.BaseCookie(response.headers['Set-Cookie']) + cookie = http.cookies.BaseCookie(response.headers['Set-Cookie']) return cookie['sid'].value def get_xsrf(self): return binascii.b2a_hex(uuid.uuid4().bytes) def create_database(self): - start_time = int(time.time()) + # start_time = int(time.time()) db = _db.connection() db.execute("DROP database IF EXISTS %s" % (options.database_name)) @@ -80,8 +75,8 @@ def create_database(self): for statement in statements: if statement.strip() != "": db.execute(statement.strip()) - end_time = int(time.time()) - #print "Database reset took: %s" % (end_time - start_time) + # end_time = int(time.time()) + # print "Database reset took: %s" % (end_time - start_time) return db def upload_file(self, file_path, sha1, content_type, user_id, sid, xsrf, shake_id=None): @@ -94,10 +89,8 @@ def upload_file(self, file_path, sha1, content_type, user_id, sid, xsrf, shake_i shake_string = '' if shake_id: shake_string="shake_id=%s" % (shake_id) - request = HTTPRequest(self.get_url('/upload?skip_s3=1'), 'POST', {'Cookie':"_xsrf=%s;sid=%s" % (xsrf, sid)}, - "_xsrf=%s&file_name=%s&file_content_type=%s&file_sha1=%s&file_size=%s&file_path=%s&%s" % (xsrf,file_name, content_type, sha1, file_size, file_path, shake_string)) - self.http_client.fetch(request, self.stop) - return self.wait() + return self.fetch('/upload?skip_s3=1', method='POST', headers={'Cookie':"_xsrf=%s;sid=%s" % (xsrf, sid)}, + body="_xsrf=%s&file_name=%s&file_content_type=%s&file_sha1=%s&file_size=%s&file_path=%s&%s" % (xsrf, file_name, content_type, sha1, file_size, file_path, shake_string)) def upload_test_file(self, shake_id=None): arguments = {} @@ -113,14 +106,15 @@ def upload_test_file(self, shake_id=None): def create_signed_value(self, name, value): ### HERE!@! timestamp = str(int(time.time())) - value = base64.b64encode(value) + value = base64.b64encode(value.encode(encoding="utf-8")).decode("ascii") signature = self.cookie_signature(name, value, timestamp) value = "|".join([value, timestamp, signature]) return value def cookie_signature(self, *parts): - hash = hmac.new(options.cookie_secret, digestmod=hashlib.sha1) - for part in parts: hash.update(part) + hash = hmac.new(options.cookie_secret.encode(encoding="utf-8"), digestmod=hashlib.sha1) + for part in parts: + hash.update(type(part) == str and part.encode(encoding="utf-8") or part) return hash.hexdigest() def post_url(self, path, arguments={}, **kwargs): @@ -128,7 +122,7 @@ def post_url(self, path, arguments={}, **kwargs): Posts the URL, if self.sign_in() is called and user is logged in, the user's authenticated cookie will be passed along. """ - xsrf = self.get_xsrf() + xsrf = self.get_xsrf().decode("ascii") headers = {'Cookie':'sid=%s;_xsrf=%s' % (self.sid, xsrf)} if 'headers' in kwargs: headers.update(kwargs['headers']) @@ -136,7 +130,7 @@ def post_url(self, path, arguments={}, **kwargs): else: kwargs['headers'] = headers arguments['_xsrf'] = xsrf - body = urllib.urlencode(arguments) + body = urllib.parse.urlencode(arguments) return self.fetch(path, method="POST", body=body, **kwargs) def fetch_url(self, path, **kwargs): @@ -144,7 +138,7 @@ def fetch_url(self, path, **kwargs): Gets the URL, if self.sign_in() is called and user is logged in, the user's authenticated cookie will be passed along. """ - headers = {'Cookie':'sid=%s' % (self.sid)} + headers = {'Cookie': 'sid=%s' % self.sid} if 'headers' in kwargs: headers.update(kwargs['headers']) kwargs['headers'] = headers @@ -152,8 +146,17 @@ def fetch_url(self, path, **kwargs): kwargs['headers'] = headers return self.fetch(path, method='GET', **kwargs) - def assert_has_string(self, response, string_to_match): - self.assertTrue(response.body.find(string_to_match) > 0) + def assertNotIn(self, needle, haystack): + if isinstance(needle, str): + return super(BaseAsyncTestCase, self).assertNotIn(needle.encode("utf-8"), haystack) + else: + return super(BaseAsyncTestCase, self).assertNotIn(needle, haystack) + + def assertIn(self, needle, haystack): + if isinstance(needle, str): + return super(BaseAsyncTestCase, self).assertIn(needle.encode("utf-8"), haystack) + else: + return super(BaseAsyncTestCase, self).assertIn(needle, haystack) def assert_redirect(self, response, url): self.assertEqual(302, response.code) diff --git a/test/factories.py b/test/factories.py index cf0c7a75..eb0f9f1e 100644 --- a/test/factories.py +++ b/test/factories.py @@ -5,6 +5,7 @@ import models import lib.utilities + def sharedfile(user, **kwargs): """ Returns a sharedfile with a unique source file for the user. diff --git a/test/functional/account_settings_tests.py b/test/functional/account_settings_tests.py index 39afdfbe..f1e7f055 100644 --- a/test/functional/account_settings_tests.py +++ b/test/functional/account_settings_tests.py @@ -224,7 +224,7 @@ def test_unverified_email_notification_on_settings_page(self): self.user.save() response = self.fetch_url("/account/settings") - self.assertTrue(response.body.find('Please check your inbox for verification') > -1) + self.assertIn('Please check your inbox for verification', response.body) def test_resend_verification_email_changes_key(self): self.user.verify_email_token = "asdf" diff --git a/test/functional/account_tests.py b/test/functional/account_tests.py index 96870b36..f9b351a5 100644 --- a/test/functional/account_tests.py +++ b/test/functional/account_tests.py @@ -52,13 +52,13 @@ def test_email_not_confirmed_puts_notice_at_top(self): self.user.save() response = self.fetch_url('/') - self.assertTrue(response.body.find('Please visit settings to confirm your email!') > -1) + self.assertIn('Please visit settings to confirm your email!', response.body) response = self.fetch_url('/incoming') - self.assertTrue(response.body.find('Please visit settings to confirm your email!') > -1) + self.assertIn('Please visit settings to confirm your email!', response.body) response = self.fetch_url('/friends') - self.assertTrue(response.body.find('Please visit settings to confirm your email!') > -1) + self.assertIn('Please visit settings to confirm your email!', response.body) def test_quick_notifications(self): """ diff --git a/test/functional/api_tests.py b/test/functional/api_tests.py index d10a91d1..544e7fee 100644 --- a/test/functional/api_tests.py +++ b/test/functional/api_tests.py @@ -2,21 +2,16 @@ import time from datetime import datetime, timedelta import random -import string -from urlparse import urlparse +from urllib.parse import urlparse from hashlib import md5, sha1 -import urllib +import urllib.request, urllib.parse, urllib.error import hmac import base64 import os -from tornado.testing import AsyncHTTPTestCase -from torndb import Connection -from tornado.httpclient import HTTPRequest from tornado.escape import url_escape, json_decode from tornado.httputil import HTTPHeaders from tornado.options import options -import handlers import test.base from models import Accesstoken, Apihit, App, Authorizationcode, Favorite, \ @@ -44,7 +39,7 @@ def setUp(self): self.user_a.set_password('asdfasdf') self.user_a.save() self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.user_b = User(name='user2', email='user2@mltshp.com', email_confirmed=1, is_paid=1) self.user_b.set_password('asdfasdf') @@ -79,7 +74,7 @@ def test_authorization_code_request_accepts_authtime_redirect(self): response = api_request(self, self.get_url(authorization_url), headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, unsigned=True) self.assertEqual(response.effective_url, self.get_url(authorization_url)) self.assertEqual(response.code, 200) - self.assertTrue('http://client.example.com/return' in response.body) + self.assertIn('http://client.example.com/return', response.body) def test_authorization_code_request_accepts_matching_redirect(self): authorization_url = '/api/authorize?response_type=code&client_id=%s&redirect_uri=http://client.example.com/return' % (self.app.key()) @@ -87,7 +82,7 @@ def test_authorization_code_request_accepts_matching_redirect(self): response = api_request(self, self.get_url(authorization_url), headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, unsigned=True) self.assertEqual(response.effective_url, self.get_url(authorization_url)) self.assertEqual(response.code, 200) - self.assertTrue('http://client.example.com/return' in response.body) + self.assertIn('http://client.example.com/return', response.body) def test_authorization_code_request_error_on_mismatched_redirect(self): authorization_url = '/api/authorize?response_type=code&client_id=%s&redirect_uri=http://othersite.example.com/path' % (self.app.key()) @@ -219,7 +214,7 @@ def setUp(self): self.user_a.set_password('asdfasdf') self.user_a.save() self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.user_b = User(name='user2', email='user2@mltshp.com', email_confirmed=1, is_paid=1) @@ -339,7 +334,7 @@ def setUp(self): self.user_a.set_password('asdfasdf') self.user_a.save() self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.user_b = User(name='user2', email='user2@mltshp.com', email_confirmed=1, is_paid=1) @@ -414,7 +409,7 @@ def setUp(self): self.user_a.set_password('asdfasdf') self.user_a.save() self.sid = self.sign_in('admin', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.test_file1_path = os.path.abspath("test/files/1.png") self.test_file1_sha1 = Sourcefile.get_sha1_file_key(self.test_file1_path) @@ -448,10 +443,7 @@ def setUp(self): self.user_b.subscribe(self.user_a.shake()) def test_bad_signature_denied(self): - request = signed_request(self.access_token, self.get_url('/api/sharedfile/1')) - request.headers['Authorization'] = request.headers['Authorization'].replace('signature="', 'signature="asdf') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/1'), signature=b"asdf") self.assertTrue(response.code, 401) def test_unsigned_resource_query_denied(self): @@ -459,30 +451,23 @@ def test_unsigned_resource_query_denied(self): self.assertEqual(response.code, 401) def test_duplicate_nonce(self): - request = signed_request(self.access_token, self.get_url('/api/sharedfile/1')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/1'), nonce="abcd") self.assertEqual(response.code, 200) self.assertTrue('Www-Authenticate' not in response.headers) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/1'), nonce="abcd") self.assertEqual(response.code, 401) self.assertTrue(response.headers['Www-Authenticate'].find("Duplicate nonce.") > 0) def test_rate_limit(self): - request = signed_request(self.ratelimited_access_token, self.get_url('/api/sharedfile/1')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.ratelimited_access_token, self.get_url('/api/sharedfile/1')) self.assertEqual(response.code, 200) self.assertEqual(response.headers['X-RateLimit-Remaining'], '1') - request = signed_request(self.ratelimited_access_token, self.get_url('/api/sharedfile/1')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.ratelimited_access_token, self.get_url('/api/sharedfile/1')) self.assertEqual(response.code, 400) self.assertEqual(response.headers['X-RateLimit-Remaining'], '0') @@ -494,9 +479,7 @@ def test_query_favorites(self): f.sharedfile_id = 1 f.save() - request = signed_request(self.access_token, self.get_url('/api/favorites')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/favorites')) j_response = json_decode(response.body) self.assertTrue('favorites' in j_response) @@ -509,17 +492,13 @@ def test_query_favorites_before_after(self): sf = self._post_to_shake(self.user_a) self.user_b.add_favorite(sf) - request = signed_request(self.access_token, self.get_url('/api/favorites')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/favorites')) original_favorites = json_decode(response.body) pivot_id = original_favorites['favorites'][5]['pivot_id'] after_pivot_ids = [fav['sharekey'] for fav in original_favorites['favorites'][0:5]] before_pivot_ids = [fav['sharekey'] for fav in original_favorites['favorites'][6:]] - request = signed_request(self.access_token, self.get_url('/api/favorites/before/%s' % pivot_id)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/favorites/before/%s' % pivot_id)) j_response = json_decode(response.body) self.assertTrue('favorites' in j_response) favs = j_response['favorites'] @@ -527,9 +506,7 @@ def test_query_favorites_before_after(self): pivot_ids = [fav['sharekey'] for fav in favs] self.assertEqual(before_pivot_ids, pivot_ids) - request = signed_request(self.access_token, self.get_url('/api/favorites/after/%s' % pivot_id)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/favorites/after/%s' % pivot_id)) j_response = json_decode(response.body) self.assertTrue('favorites' in j_response) favs = j_response['favorites'] @@ -538,9 +515,7 @@ def test_query_favorites_before_after(self): self.assertEqual(after_pivot_ids, pivot_ids) def test_query_file_resource(self): - request = signed_request(self.access_token, self.get_url('/api/sharedfile/1')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/1')) j_response = json_decode(response.body) self.assertEqual(j_response['name'], '1.png') self.assertEqual(j_response['user']['name'], 'admin') @@ -549,9 +524,7 @@ def test_query_sharedfile_resource(self): sf = Sharedfile.get('id=%s', 1) posted = sf.created_at.replace(microsecond=0, tzinfo=None).isoformat() + 'Z' - request = signed_request(self.access_token, self.get_url('/api/sharedfile/1')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/1')) j_response = json_decode(response.body) self.assertEqual(j_response['user']['name'], 'admin') self.assertEqual(j_response['posted_at'], posted) @@ -560,9 +533,7 @@ def test_query_sharedfile_resource(self): def test_can_update_own_sharedfile(self): user_b_file = self._post_to_shake(self.user_b) message_body = "description=newdescription&title=newtitle&alt_text=newalttext" - request = signed_request(self.access_token, self.get_url('/api/sharedfile/%s' % user_b_file.share_key), 'POST', {}, message_body) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/%s' % user_b_file.share_key), 'POST', {}, message_body) self.assertEqual(response.code, 200) user_b_file = Sharedfile.get("id = %s", user_b_file.id) self.assertEqual('newdescription', user_b_file.description) @@ -572,9 +543,7 @@ def test_can_update_own_sharedfile(self): def test_can_not_update_anothers_sharedfile(self): user_a_file = self._post_to_shake(self.user_a) message_body = "description=newdescription&title=newtitle&alt_text=newalt" - request = signed_request(self.access_token, self.get_url('/api/sharedfile/%s' % user_a_file.share_key), 'POST', {}, message_body) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/%s' % user_a_file.share_key), 'POST', {}, message_body) self.assertEqual(response.code, 403) user_a_file = Sharedfile.get("id = %s", user_a_file.id) self.assertNotEqual('newdescription', user_a_file.description) @@ -582,9 +551,7 @@ def test_can_not_update_anothers_sharedfile(self): self.assertNotEqual('newalt', user_a_file.alt_text) def test_query_user_name_resource(self): - request = signed_request(self.access_token, self.get_url('/api/user_name/admin')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/user_name/admin')) j_response = json_decode(response.body) self.assertEqual(j_response['name'], 'admin') self.assertEqual(j_response['profile_image_url'], 'https://mltshp-cdn.com/static/images/default-icon-venti.svg') @@ -594,25 +561,19 @@ def test_query_user_name_resource(self): self.assertEqual(2, len(j_response['shakes'])) def test_query_user_id_resource(self): - request = signed_request(self.access_token, self.get_url('/api/user_id/1')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/user_id/1')) j_response = json_decode(response.body) self.assertEqual(j_response['name'], 'admin') self.assertEqual(j_response['id'], 1) def test_query_user_resource(self): - request = signed_request(self.access_token, self.get_url('/api/user')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/user')) j_response = json_decode(response.body) self.assertEqual(j_response['name'], 'user2') self.assertEqual(j_response['id'], 2) def test_query_usershakes_resource(self): - request = signed_request(self.access_token, self.get_url('/api/shakes')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/shakes')) j_response = json_decode(response.body) self.assertEqual(len(j_response['shakes']), 3) user_shake, group_shake, group_shake_2 = j_response['shakes'] @@ -641,9 +602,7 @@ def test_query_usershakes_resource(self): self.assertEqual(group_shake_2['owner'], {'name': 'admin', 'id': 1, 'profile_image_url': "https://mltshp-cdn.com/static/images/default-icon-venti.svg"}) def test_query_friend_shake(self): - request = signed_request(self.access_token, self.get_url('/api/friends')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/friends')) j_response = json_decode(response.body) self.assertEqual(j_response['friend_shake'][0]['name'], '1.png') self.assertEqual(j_response['friend_shake'][0]['width'], 1) @@ -654,9 +613,7 @@ def test_query_friend_shake_shows_nsfw(self): sf = Sharedfile.get('id=%s', 1) sf.set_nsfw(self.user_a) - request = signed_request(self.access_token, self.get_url('/api/friends')) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/friends')) j_response = json_decode(response.body) self.assertEqual(j_response['friend_shake'][0]['nsfw'], True) @@ -682,24 +639,18 @@ def test_query_friend_shake_before_after(self): sf.add_to_shake(self.user_a.shake()) files.append(sf) - request = signed_request(self.access_token, self.get_url('/api/friends/before/%s' % files[3].share_key)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/friends/before/%s' % files[3].share_key)) j_response = json_decode(response.body) self.assertEqual(4, len(j_response['friend_shake'])) - request = signed_request(self.access_token, self.get_url('/api/friends/after/%s' % files[3].share_key)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/friends/after/%s' % files[3].share_key)) j_response = json_decode(response.body) self.assertEqual(6, len(j_response['friend_shake'])) def test_shake_stream(self): user_shake = self.user_a.shake() url = self.get_url("/api/shakes/%s" % user_shake.id) - request = signed_request(self.access_token, url, 'GET', {}) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, url, 'GET', {}) j_response = json_decode(response.body) self.assertEqual(1, len(j_response['sharedfiles'])) @@ -710,9 +661,7 @@ def test_shake_stream_before(self): sharedfiles = user_shake.sharedfiles() self.assertEqual(3, len(sharedfiles)) url = self.get_url("/api/shakes/%s/before/%s" % (user_shake.id, sharedfiles[1].share_key)) - request = signed_request(self.access_token, url, 'GET', {}) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, url, 'GET', {}) j_response = json_decode(response.body) self.assertEqual(1, len(j_response['sharedfiles'])) self.assertEqual(sharedfiles[2].share_key, j_response['sharedfiles'][0]['sharekey']) @@ -724,9 +673,7 @@ def test_shake_stream_after(self): sharedfiles = user_shake.sharedfiles() self.assertEqual(3, len(sharedfiles)) url = self.get_url("/api/shakes/%s/after/%s" % (user_shake.id, sharedfiles[1].share_key)) - request = signed_request(self.access_token, url, 'GET', {}) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, url, 'GET', {}) j_response = json_decode(response.body) self.assertEqual(1, len(j_response['sharedfiles'])) self.assertEqual(sharedfiles[0].share_key, j_response['sharedfiles'][0]['sharekey']) @@ -734,9 +681,7 @@ def test_shake_stream_after(self): def test_upload_file(self): message = "file_name=%s&file_content_type=%s&file_sha1=%s&file_size=%s&file_path=%s" % \ ("2.png", self.test_file1_content_type, self.test_file1_sha1, 69, self.test_file1_path) - request = signed_request(self.access_token, self.get_url('/api/upload'), 'POST', {}, message) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/upload'), 'POST', {}, message) j_response = json_decode(response.body) self.assertEqual(j_response['name'], '2.png') self.assertEqual(j_response['share_key'], '2') @@ -744,9 +689,7 @@ def test_upload_file(self): def test_upload_file_with_title_description_alt_text(self): message = "file_name=%s&title=%s&description=%s&alt_text=%s&file_content_type=%s&file_sha1=%s&file_size=%s&file_path=%s" % \ ("2.png", "two", "a thing i wrote", "the number two", self.test_file1_content_type, self.test_file1_sha1, 69, self.test_file1_path) - request = signed_request(self.access_token, self.get_url('/api/upload'), 'POST', {}, message) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/upload'), 'POST', {}, message) j_response = json_decode(response.body) sf = Sharedfile.get('share_key = %s', j_response['share_key']) self.assertEqual(sf.title, 'two') @@ -767,7 +710,7 @@ def test_magicfiles_resource(self): testfile_in_another_group = testfile_2.save_to_shake(self.user_a, self.group_shake_2) sid_b = self.sign_in('user2', 'asdfasdf') - xsrf_b = self.get_xsrf() + xsrf_b = self.get_xsrf().decode("ascii") response = self.upload_file(file_path=self.test_file1_path, sha1=self.test_file1_sha1, content_type=self.test_file1_content_type, user_id=2, sid=sid_b, xsrf=xsrf_b, shake_id=self.group_shake.id) @@ -786,9 +729,7 @@ def test_magicfiles_resource(self): calculate_likes(sf.id) # What's best? - request = signed_request(self.access_token, self.get_url('/api/magicfiles'), 'GET') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/magicfiles'), 'GET') j_response = json_decode(response.body) magicfiles = j_response['magicfiles'] @@ -798,22 +739,16 @@ def test_magicfiles_resource(self): self.assertEqual(pivot_ids, ['2', '1']) # Pagination check. - request = signed_request(self.access_token, self.get_url('/api/magicfiles/before/2'), 'GET') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/magicfiles/before/2'), 'GET') j_response = json_decode(response.body) self.assertEqual('1', j_response['magicfiles'][0]['pivot_id']) - request = signed_request(self.access_token, self.get_url('/api/magicfiles/after/1'), 'GET') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/magicfiles/after/1'), 'GET') j_response = json_decode(response.body) self.assertEqual('2', j_response['magicfiles'][0]['pivot_id']) def test_like_resource(self): - request = signed_request(self.access_token, self.get_url('/api/sharedfile/1/like'), 'POST', {}, '') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/1/like'), 'POST', {}, '') self.assertEqual(response.code, 200) j_response = json_decode(response.body) @@ -823,14 +758,10 @@ def test_like_resource(self): self.assertEqual(testfile.like_count, 1) def test_like_resource_already_liked(self): - request = signed_request(self.access_token, self.get_url('/api/sharedfile/1/like'), 'POST', {}, '') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/1/like'), 'POST', {}, '') self.assertEqual(response.code, 200) - request = signed_request(self.access_token, self.get_url('/api/sharedfile/1/like'), 'POST', {}, '') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/1/like'), 'POST', {}, '') self.assertEqual(response.code, 400) j_response = json_decode(response.body) @@ -841,17 +772,13 @@ def test_like_resource_already_liked(self): self.assertEqual(testfile.like_count, 1) def test_like_resource_not_found(self): - request = signed_request(self.access_token, self.get_url('/api/sharedfile/444Z/like'), 'POST') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/444Z/like'), 'POST') self.assertEqual(response.code, 404) j_response = json_decode(response.body) self.assertTrue('error' in j_response) def test_save_sharedfile(self): - request = signed_request(self.access_token, self.get_url('/api/sharedfile/1/save'), 'POST') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/1/save'), 'POST') self.assertEqual(response.code, 200) testfile = Sharedfile.get("id = %s", 1) self.assertEqual(1, testfile.save_count) @@ -859,27 +786,21 @@ def test_save_sharedfile(self): self.assertTrue(1, j_response['saves']) def test_save_nonexistant_sharedfile(self): - request = signed_request(self.access_token, self.get_url('/api/sharedfile/50000/save'), 'POST') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, self.get_url('/api/sharedfile/50000/save'), 'POST') self.assertEqual(response.code, 404) def test_save_own_sharedfile(self): self._post_to_shake(self.user_b) sharedfile = self.user_b.shake().sharedfiles()[0] url = self.get_url('/api/sharedfile/%s/save' % sharedfile.share_key) - request = signed_request(self.access_token, url, 'POST', {}, '') - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, url, 'POST', {}, '') self.assertEqual(response.code, 400) def test_save_to_shake_with_valid_permissions(self): url = self.get_url('/api/sharedfile/1/save') body = "shake_id=%s" % self.group_shake.id self.assertEqual(0, len(self.group_shake.sharedfiles())) - request = signed_request(self.access_token, url, 'POST', body=body) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, url, 'POST', body=body) self.assertEqual(response.code, 200) self.assertEqual(1, len(self.group_shake.sharedfiles())) j_response = json_decode(response.body) @@ -890,9 +811,7 @@ def test_save_to_shake_with_no_permissions(self): shake = self.user_a.shake() body = "shake_id=%s" % shake.id original_num_sharedfiles = len(shake.sharedfiles()) - request = signed_request(self.access_token, url, 'POST', body=body) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = signed_request(self, self.access_token, url, 'POST', body=body) self.assertEqual(original_num_sharedfiles, len(shake.sharedfiles())) self.assertEqual(response.code, 403) @@ -999,19 +918,16 @@ def api_request(obj, url, unsigned=False, arguments={}, headers={}, method='GET' if method == 'GET': body = None elif arguments: - body = urllib.urlencode(arguments) + body = urllib.parse.urlencode(arguments) if unsigned: - request = HTTPRequest(url, method, headers, body) + return obj.fetch(url, method=method, headers=headers, body=body) else: - request = signed_request(obj.access_token, url, headers=headers, method=method, body=body) - obj.http_client.fetch(request, obj.stop) - response = obj.wait() - return response + return signed_request(obj, obj.access_token, url, headers=headers, method=method, body=body) -def signed_request(access_token, url, method='GET', headers={}, body=''): +def signed_request(obj, access_token, url, method='GET', headers={}, body='', signature=None, nonce=None): timestamp = int(time.mktime(datetime.utcnow().timetuple())) - nonce = md5("%s%s" % (str(timestamp), random.random())).hexdigest() + nonce = nonce or md5(("%s%s" % (str(timestamp), random.random())).encode("ascii")).hexdigest() parsed_url = urlparse(url) query_array = [] if parsed_url.query: @@ -1032,12 +948,12 @@ def signed_request(access_token, url, method='GET', headers={}, body=''): parsed_url.port, parsed_url.path, query_array) - digest = hmac.new(access_token.consumer_secret, normalized_string, sha1).digest() - signature = base64.encodestring(digest).strip() - authorization_string = 'MAC token="%s", timestamp="%s", nonce="%s", signature="%s"' % (access_token.consumer_key, str(int(timestamp)), nonce, signature) + digest = hmac.new(access_token.consumer_secret.encode("ascii"), normalized_string.encode("ascii"), sha1).digest() + signature = signature or base64.encodebytes(digest).strip() + authorization_string = 'MAC token="%s", timestamp="%s", nonce="%s", signature="%s"' % (access_token.consumer_key, str(int(timestamp)), nonce, signature.decode("ascii")) if headers: headers.add("Authorization", authorization_string) else: headers = HTTPHeaders({"Authorization": authorization_string}) - return HTTPRequest(url, method, headers, body) + return obj.fetch(url, method=method, headers=headers, body=body) diff --git a/test/functional/comment_favor_tests.py b/test/functional/comment_favor_tests.py index aeec50d0..beefbe21 100644 --- a/test/functional/comment_favor_tests.py +++ b/test/functional/comment_favor_tests.py @@ -1,6 +1,4 @@ import time -from tornado.httpclient import HTTPRequest -from tornado.escape import url_escape import test.base import models @@ -21,10 +19,10 @@ def setUp(self): self.shf = models.Sharedfile(source_id=self.src.id, user_id=self.admin.id, name='shared.jpg', title='shared', share_key='1', content_type='image/jpg') self.shf.save() - print "person who owns the comment" + print("person who owns the comment") self.comment = models.Comment(user_id=self.user2.id, sharedfile_id=self.shf.id, body="just a comment") self.comment.save() - print self.comment.user_id + print(self.comment.user_id) self.sign_in('admin','asdfasdf') response = self.post_url('/p/%s/comment/%s/like?json=1' % (self.shf.share_key, self.comment.id)) @@ -60,7 +58,7 @@ def test_notification_created_for_like(self): for n in notifications: if n.type == 'comment_like': - print n.__dict__ + print(n.__dict__) # #self.assertEqual(len(notifications), 2) #self.assertEqual(notifications[1].sender_id, self.admin.id) diff --git a/test/functional/conversations_tests.py b/test/functional/conversations_tests.py index fefebf8a..23aed1d3 100644 --- a/test/functional/conversations_tests.py +++ b/test/functional/conversations_tests.py @@ -1,5 +1,4 @@ import time -from tornado.httpclient import HTTPRequest from tornado.escape import url_escape import test.base @@ -11,108 +10,84 @@ def setUp(self): self.admin = User(name='admin', email='admin@mltshp.com', email_confirmed=1, is_paid=1) self.admin.set_password('asdfasdf') self.admin.save() - + self.user2 = User(name='user2', email='user2@example.com', email_confirmed=1, is_paid=1) self.user2.set_password('asdfasdf') self.user2.save() - + self.sid = self.sign_in('user2', 'asdfasdf') - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.src = Sourcefile(width=1, height=1, file_key='asdf', thumb_key='qwer') self.src.save() self.shf = Sharedfile(source_id=self.src.id, user_id=self.admin.id, name='shared.jpg', title='shared', share_key='1', content_type='image/jpg') self.shf.save() - + def test_creating_a_new_comment_creates_a_conversation(self): - request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape("a comment"), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - + response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape("a comment"), self.xsrf)) + conversations = Conversation.all() self.assertEqual(len(conversations), 2) def test_creating_a_new_comment_does_not_create_a_duplicate_conversation(self): - request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape("a comment"), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape("a comment"), self.xsrf)) - request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape("a second comment"), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape("a second comment"), self.xsrf)) conversations = Conversation.all() self.assertEqual(len(conversations), 2) - def test_another_user_commenting_will_update_the_files_activity_at(self): - request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape("a comment"), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape("a comment"), self.xsrf)) time.sleep(1) - sf = Sharedfile.get('id=%s', self.shf.id) activity_one = sf.activity_at - request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape("a second comment"), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - + response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape("a second comment"), self.xsrf)) sf = Sharedfile.get('id=%s', self.shf.id) activity_two = sf.activity_at self.assertTrue(activity_two > activity_one) - def test_deleting_a_file_will_set_conversation_to_muted(self): - request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape("a comment"), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape("a comment"), self.xsrf)) - request = HTTPRequest(self.get_url('/p/%s/comment' % self.shf.share_key), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "body=%s&_xsrf=%s" % (url_escape("a second comment"), self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() + response = self.fetch('/p/%s/comment' % self.shf.share_key, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="body=%s&_xsrf=%s" % (url_escape("a second comment"), self.xsrf)) self.shf.delete() - + conversations = Conversation.all() self.assertEqual(conversations[0].muted, 1) self.assertEqual(conversations[1].muted, 1) - - + def test_muting_conversation(self): """ Add a comment, which will create a conversation for the commenter (user2) and sharedfile owner (admin). - + When user2 tries to mute admin's conversation, it should fail and admin's conversation state will remain unchanged. When muting own converastion, "muted" flag should change to true. - + Contingent on user2 being signed in. (see setUp) """ comment = Comment(sharedfile_id=self.shf.id, user_id=self.user2.id, body='test') comment.save() - + admin_conversation = Conversation.get('user_id = %s', self.admin.id) user2_conversation = Conversation.get('user_id = %s', self.user2.id) self.assertEqual(admin_conversation.muted, 0) self.assertEqual(user2_conversation.muted, 0) - - request = HTTPRequest(self.get_url('/conversations/%s/mute' % admin_conversation.id), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "_xsrf=%s" % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - request = HTTPRequest(self.get_url('/conversations/%s/mute' % user2_conversation.id), 'POST', {'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, "_xsrf=%s" % (self.xsrf)) - self.http_client.fetch(request, self.stop) - response = self.wait() - + + response = self.fetch('/conversations/%s/mute' % admin_conversation.id, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="_xsrf=%s" % self.xsrf) + response = self.fetch('/conversations/%s/mute' % user2_conversation.id, method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="_xsrf=%s" % self.xsrf) + # refetch from DB, and verify mute flags remain 0. admin_conversation = Conversation.get('user_id = %s', self.admin.id) user2_conversation = Conversation.get('user_id = %s', self.user2.id) self.assertEqual(admin_conversation.muted, 0) self.assertEqual(user2_conversation.muted, 1) - - + def test_order_of_conversations_changes_when_new_comment_is_created(self): pass diff --git a/test/functional/create_account_tests.py b/test/functional/create_account_tests.py index 43775f5a..d1e55947 100644 --- a/test/functional/create_account_tests.py +++ b/test/functional/create_account_tests.py @@ -10,9 +10,9 @@ def test_username_too_long(self): arguments = self._valid_arguments() arguments['name'] = 'asdfasdfasdfasdfasdfasdfasdfasd' response = self.post_url('/create-account', arguments) - self.assert_has_string( - response, - 'Username should be less than 30 characters.' + self.assertIn( + 'Username should be less than 30 characters.', + response.body ) def test_username_contains_invalid_chars(self): @@ -28,9 +28,9 @@ def test_username_contains_invalid_chars(self): arguments = self._valid_arguments() arguments['name'] = name response = self.post_url('/create-account', arguments) - self.assert_has_string( - response, - 'Username can only contain letters, numbers' + self.assertIn( + 'Username can only contain letters, numbers', + response.body ) def test_username_exists(self): @@ -45,27 +45,27 @@ def test_username_exists(self): arguments = self._valid_arguments() arguments['name'] = existant_user.name response = self.post_url('/create-account', arguments) - self.assert_has_string( - response, - 'Username has already been taken.' + self.assertIn( + 'Username has already been taken.', + response.body ) def test_username_is_blank(self): arguments = self._valid_arguments() arguments['name'] = "" response = self.post_url('/create-account', arguments) - self.assert_has_string( - response, - 'You definitely need a username' + self.assertIn( + 'You definitely need a username', + response.body ) def test_email_is_blank(self): arguments = self._valid_arguments() arguments['email'] = "" response = self.post_url('/create-account', arguments) - self.assert_has_string( - response, - 'You\'ll need an email to verify your account.' + self.assertIn( + 'You\'ll need an email to verify your account.', + response.body ) def test_email_already_exists(self): @@ -80,18 +80,18 @@ def test_email_already_exists(self): arguments = self._valid_arguments() arguments['email'] = existant_user.email response = self.post_url('/create-account', arguments) - self.assert_has_string( - response, - 'This email already has an account.' + self.assertIn( + 'This email already has an account.', + response.body ) def test_email_aint_right(self): arguments = self._valid_arguments() arguments['email'] = "admin-torresdz.org" response = self.post_url('/create-account', arguments) - self.assert_has_string( - response, - 'Email doesn\'t look right.' + self.assertIn( + 'Email doesn\'t look right.', + response.body ) def test_bad_passwords(self): @@ -100,9 +100,9 @@ def test_bad_passwords(self): arguments['password'] = bad_password arguments['password_again'] = bad_password response = self.post_url('/create-account', arguments) - self.assert_has_string( - response, - 'That is not a good password.' + self.assertIn( + 'That is not a good password.', + response.body ) def test_successful_signup(self): diff --git a/test/functional/home_tests.py b/test/functional/home_tests.py index 57a97d05..ab4bfe9e 100644 --- a/test/functional/home_tests.py +++ b/test/functional/home_tests.py @@ -21,7 +21,7 @@ def test_not_logged_in(self): self.sign_out() response = self.fetch_url('/') self.assertEqual(200, response.code) - self.assertTrue(response.body.find('Save, Share & Discover') > -1) + self.assertIn('Save, Share & Discover', response.body) def test_home_page_no_sharedfiles(self): """ @@ -62,7 +62,6 @@ def test_home_page_with_friends(self): self.assertEqual(response.code, 200) self.assertEqual(1, len(Bookmark.all())) - def test_paginating_home_stream(self): """ Test going back and forward in the timeline using /before/{share_key} @@ -79,26 +78,31 @@ def test_paginating_home_stream(self): saved_files = [] for x in range(15): - sf = test.factories.sharedfile(user) + sf = test.factories.sharedfile(user, name=f"sharedfile_{x}.png") sf.add_to_shake(user.shake()) saved_files.append(sf) response = self.fetch_url('/before/%s' % saved_files[5].share_key) self.assertEqual(response.code, 200) - self.assertTrue(response.body.find('sharedfile_0.png')) - self.assertTrue(response.body.find('sharedfile_1.png')) - self.assertTrue(response.body.find('sharedfile_2.png')) - self.assertTrue(response.body.find('sharedfile_3.png')) - self.assertTrue(response.body.find('sharedfile_4.png')) - self.assertTrue(response.body.find('sharedfile_5.png')) - self.assertEqual(-1, response.body.find('sharedfile_6.png')) - response = self.fetch_url('/after/%s' % saved_files[10].share_key) + self.assertIn('sharedfile_0.png', response.body) + self.assertIn('sharedfile_1.png', response.body) + self.assertIn('sharedfile_2.png', response.body) + self.assertIn('sharedfile_3.png', response.body) + self.assertIn('sharedfile_4.png', response.body) + self.assertNotIn('sharedfile_5.png', response.body) + response = self.fetch_url('/after/%s' % saved_files[4].share_key) self.assertEqual(response.code, 200) - self.assertTrue(response.body.find('sharedfile_11.png')) - self.assertTrue(response.body.find('sharedfile_12.png')) - self.assertTrue(response.body.find('sharedfile_13.png')) - self.assertTrue(response.body.find('sharedfile_14.png')) - self.assertEqual(-1, response.body.find('sharedfile_10.png')) + self.assertIn('sharedfile_5.png', response.body) + self.assertIn('sharedfile_6.png', response.body) + self.assertIn('sharedfile_7.png', response.body) + self.assertIn('sharedfile_8.png', response.body) + self.assertIn('sharedfile_9.png', response.body) + self.assertIn('sharedfile_10.png', response.body) + self.assertIn('sharedfile_11.png', response.body) + self.assertIn('sharedfile_12.png', response.body) + self.assertIn('sharedfile_13.png', response.body) + self.assertIn('sharedfile_14.png', response.body) + self.assertNotIn('sharedfile_4.png', response.body) def test_home_page_non_user_request(self): """ @@ -132,6 +136,3 @@ def test_home_page_non_user_request(self): response = self.fetch_url('/friends', ) self.assertEqual(response.code, 200) self.assertEqual(1, len(Bookmark.all())) - - - diff --git a/test/functional/image_like_tests.py b/test/functional/image_like_tests.py index b34c0462..c16c0a57 100644 --- a/test/functional/image_like_tests.py +++ b/test/functional/image_like_tests.py @@ -132,7 +132,7 @@ def test_cannot_like_own(self): json_response = json.loads(response.body) self.assertEqual(response.code, 200) - self.assertTrue(json_response.has_key('error')) + self.assertTrue('error' in json_response) favorite = Favorite.get('user_id= %s and sharedfile_id = %s', self.admin.id, sharedfile.id) self.assertFalse(favorite) diff --git a/test/functional/image_save_tests.py b/test/functional/image_save_tests.py index b0f7706a..a8056021 100644 --- a/test/functional/image_save_tests.py +++ b/test/functional/image_save_tests.py @@ -1,5 +1,5 @@ import json -from urlparse import urlparse +from urllib.parse import urlparse import test.base import lib.utilities diff --git a/test/functional/payments_tests.py b/test/functional/payments_tests.py index 248ec7e8..e4827b85 100644 --- a/test/functional/payments_tests.py +++ b/test/functional/payments_tests.py @@ -23,13 +23,13 @@ def setUp(self): def test_no_subscription_sees_subscription_button(self): response = self.fetch_url("/account/settings") - self.assertTrue(response.body.find('You are currently using a free account.') > -1) + self.assertIn('You are currently using a free account.', response.body) def test_subscriber_sees_subscription(self): self.user.is_paid = 1 self.user.save() response = self.fetch_url('/account/settings') - self.assertTrue(response.body.find('Your recent payment history:') > -1) + self.assertIn('Your recent payment history:', response.body) def test_subcription_webhook_sets_paid_status(self): self.user.stripe_customer_id = "cus_AHgKQnggJErzEA" @@ -116,7 +116,7 @@ def test_subcription_webhook_sets_paid_status(self): } """ response = self.fetch("/webhooks/stripe", method="POST", body=body) - self.assertEquals(response.body, "OK") + self.assertEqual(response.body, "OK".encode("ascii")) user = User.get(self.user.id) self.assertEqual(user.is_paid, 1) diff --git a/test/functional/readonly_tests.py b/test/functional/readonly_tests.py index f0594bb0..5dfb1036 100644 --- a/test/functional/readonly_tests.py +++ b/test/functional/readonly_tests.py @@ -15,7 +15,7 @@ def setUp(self): options.readonly = False self.admin = test.factories.user() self.sid = self.sign_in("admin", "password") - self.xsrf = self.get_xsrf() + self.xsrf = self.get_xsrf().decode("ascii") self.test_file1_path = os.path.abspath("test/files/1.png") self.test_file1_sha1 = Sourcefile.get_sha1_file_key(self.test_file1_path) @@ -40,17 +40,17 @@ def test_user_model_wont_save(self): def test_uploads_return_403(self): options.readonly = True - response = self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.get_xsrf()) + response = self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.get_xsrf().decode("ascii")) self.assertEqual(response.code, 403) def test_no_post_button(self): # when site is writable, "New Post" button is present: response = self.fetch_url('/') self.assertEqual(200, response.code) - self.assertTrue(response.body.find('New Post') > -1) + self.assertIn('New Post', response.body) # when site is readonly, "New Post" button is suppressed: options.readonly = True response = self.fetch_url('/') self.assertEqual(200, response.code) - self.assertTrue(response.body.find('New Post') == -1) + self.assertNotIn('New Post', response.body) diff --git a/test/functional/request_invitation_tests.py b/test/functional/request_invitation_tests.py index 0f95f09f..bae166ad 100644 --- a/test/functional/request_invitation_tests.py +++ b/test/functional/request_invitation_tests.py @@ -54,7 +54,7 @@ def test_posting_request_doesnt_recreate_request(self): def test_no_button_shows_when_request_has_been_made(self): response = self.post_url('/shake/derp/request_invitation?json=1') response = self.fetch_url('/derp') - self.assertTrue(response.body.find('/request_invitation') == -1) + self.assertNotIn('/request_invitation', response.body) def test_shake_manager_gets_notification_created(self): response = self.post_url('/shake/derp/request_invitation?json=1') @@ -110,5 +110,5 @@ def test_already_a_member_do_not_see_request_button(self): self.shake.add_manager(self.user) response = self.fetch_url('/derp') - self.assertTrue(response.body.find('join this shake') == -1) + self.assertNotIn('join this shake', response.body) diff --git a/test/functional/shake_crud_tests.py b/test/functional/shake_crud_tests.py index 02205bd5..e3567089 100644 --- a/test/functional/shake_crud_tests.py +++ b/test/functional/shake_crud_tests.py @@ -43,7 +43,7 @@ def test_shake_create_error_on_name(self): } response = self.post_url('/shake/create', arguments=arguments) self.assertEqual(response.effective_url, self.get_url('/shake/create')) - self.assertTrue(response.body.find('That URL is not valid.') > -1) + self.assertIn('That URL is not valid.', response.body) def test_shake_create_error_on_title(self): """ @@ -58,7 +58,7 @@ def test_shake_create_error_on_title(self): } response = self.post_url('/shake/create', arguments=arguments) self.assertEqual(response.effective_url, self.get_url('/shake/create')) - self.assertTrue(response.body.find("Title can't be blank.") > -1) + self.assertIn("Title can't be blank.", response.body) def test_shake_update_description(self): @@ -99,7 +99,7 @@ def test_shake_duplicate_error(self): 'title' : 'got one' } response = self.post_url('/shake/create', arguments=arguments) - self.assertTrue(response.body.find('That URL is already taken.') > -1) + self.assertIn('That URL is already taken.', response.body) def test_subscribe_unsubscribe_works(self): user_a = User(name='user_a', email='user_a@example.com', email_confirmed=1, is_paid=1, stripe_plan_id="mltshp-double") @@ -142,7 +142,7 @@ def test_cannot_create_shake_if_not_a_plus_member(self): 'title' : 'Shake Test', } response = self.post_url('/shake/create', arguments=arguments) - self.assertTrue(response.body.find('Create up to 100 group shakes') > -1) + self.assertIn('Create up to 100 group shakes', response.body) def test_create_shake_page_works_for_plus_members(self): user_a = User(name='user_a', email='user_a@example.com', email_confirmed=1, diff --git a/test/functional/tag_tests.py b/test/functional/tag_tests.py index e00a25d8..a2710a36 100644 --- a/test/functional/tag_tests.py +++ b/test/functional/tag_tests.py @@ -1,5 +1,5 @@ import json -from urlparse import urlparse +from urllib.parse import urlparse import test.base import lib.utilities @@ -52,7 +52,7 @@ def test_not_signedin_user_cant_creat_tag(self): response = self.post_url('/p/%s/create_tag' % self.sharedfile.share_key, {'tag':'asdf'}) - print self.response.code + print(self.response.code) all_tags = Tag.all() all_tag_shared_files = TagSharedfile.all() diff --git a/test/functional/verify_email_tests.py b/test/functional/verify_email_tests.py index 8cde3282..d2fc7bf9 100644 --- a/test/functional/verify_email_tests.py +++ b/test/functional/verify_email_tests.py @@ -8,7 +8,7 @@ class VerifyEmailTests(test.base.BaseAsyncTestCase): def test_verify_key_success(self): h = hashlib.sha1() - h.update("%s" % time.time()) + h.update(("%s" % time.time()).encode('ascii')) verify_token = h.hexdigest() existant_user = User( diff --git a/test/functional/voucher_tests.py b/test/functional/voucher_tests.py index cee2b99a..1013d4c0 100644 --- a/test/functional/voucher_tests.py +++ b/test/functional/voucher_tests.py @@ -1,6 +1,6 @@ import random import mock -import cStringIO +import io from datetime import datetime, timedelta import tornado.httpclient @@ -49,7 +49,7 @@ def test_create_account_has_code_field(self): self.sign_out() response = self.fetch_url("/create-account") self.assertEqual(200, response.code) - self.assertTrue(response.body.find("Discount code:") > -1) + self.assertIn("Discount code:", response.body) def test_create_account_with_bad_voucher(self): """ @@ -60,7 +60,7 @@ def test_create_account_with_bad_voucher(self): arguments["key"] = "ABCDEFGHIJKL" response = self.post_url("/create-account", arguments=arguments) self.assertEqual(200, response.code) - self.assertTrue(response.body.find("Invalid discount code") > -1) + self.assertIn("Invalid discount code", response.body) def test_create_account_with_unrecognized_voucher(self): """ @@ -71,7 +71,7 @@ def test_create_account_with_unrecognized_voucher(self): arguments["key"] = "foobar" response = self.post_url("/create-account", arguments=arguments) self.assertEqual(200, response.code) - self.assertTrue(response.body.find("Invalid discount code") > -1) + self.assertIn("Invalid discount code", response.body) def test_create_account_with_good_voucher(self): """ @@ -87,10 +87,10 @@ def test_create_account_with_good_voucher(self): self.post_url("/create-account", arguments=arguments) self.sign_in(arguments["name"], arguments["password"]) response = self.fetch_url("/confirm-account") - self.assertTrue(response.body.find( - "Hello, %s!" % arguments["name"]) > -1) + self.assertIn( + ("Hello, %s!" % arguments["name"]), response.body) response = self.fetch_url("/account/settings") - self.assertTrue(response.body.find("5 Years") > -1) + self.assertIn("5 Years", response.body) def test_settings_page_with_credit(self): """ @@ -98,14 +98,14 @@ def test_settings_page_with_credit(self): credit on their account settings. """ response = self.fetch_url("/account/settings") - self.assertTrue(response.body.find("5 Years") > -1) + self.assertIn("5 Years", response.body) def test_redeem_page_with_pro_user(self): """ A pro member shouldn't have access to the redeem page. """ response = self.fetch_url("/account/redeem") - self.assertTrue(response.body.find("Redeem a Coupon") == -1) + self.assertNotIn("Redeem a Coupon", response.body) def test_redeem_voucher_with_bad_voucher(self): self.sign_out() @@ -113,13 +113,13 @@ def test_redeem_voucher_with_bad_voucher(self): self.sign_in(user.name, "password") response = self.fetch_url("/account/settings") # verify this account is currently free - self.assertTrue(response.body.find("You are currently using a free account.") > -1) + self.assertIn("You are currently using a free account.", response.body) arguments = { "key": "abc123" } response = self.post_url("/account/redeem", arguments) - self.assertTrue(response.body.find("Invalid") > -1) + self.assertIn("Invalid", response.body) def test_redeem_voucher_with_good_voucher(self): self.sign_out() @@ -129,7 +129,7 @@ def test_redeem_voucher_with_good_voucher(self): self.sign_in(user.name, "password") response = self.fetch_url("/account/settings") # verify this account is currently free - self.assertTrue(response.body.find("You are currently using a free account.") > -1) + self.assertIn("You are currently using a free account.", response.body) arguments = { "key": "unclaimed" @@ -137,22 +137,22 @@ def test_redeem_voucher_with_good_voucher(self): # this will post and redirect to the settings page which should # then reflect that we are a paid user with 5 years of credit response = self.post_url("/account/redeem", arguments) - self.assertTrue(response.body.find("5 Years") > -1) + self.assertIn("5 Years", response.body) payments = PaymentLog.where("user_id=%s", user.id) - self.assertEquals(len(payments), 1) - self.assertEquals(payments[0].operation, "redeem") - self.assertEquals(payments[0].status, "credit") - self.assertEquals(payments[0].reference_id, str(self.promotion.id)) - self.assertEquals(payments[0].transaction_id, arguments['key']) - self.assertEquals(payments[0].buyer_email, user.email) - self.assertEquals(payments[0].buyer_name, user.name) - # self.assertEquals(payments[0].next_transaction_date, ) + self.assertEqual(len(payments), 1) + self.assertEqual(payments[0].operation, "redeem") + self.assertEqual(payments[0].status, "credit") + self.assertEqual(payments[0].reference_id, str(self.promotion.id)) + self.assertEqual(payments[0].transaction_id, arguments['key']) + self.assertEqual(payments[0].buyer_email, user.email) + self.assertEqual(payments[0].buyer_name, user.name) + # self.assertEqual(payments[0].next_transaction_date, ) voucher = Voucher.get("claimed_by_user_id=%s", user.id) - self.assertEquals(voucher.promotion_id, self.promotion.id) - self.assertEquals(voucher.claimed_by_user_id, user.id) - self.assertEquals(voucher.offered_by_user_id, self.admin.id) + self.assertEqual(voucher.promotion_id, self.promotion.id) + self.assertEqual(voucher.claimed_by_user_id, user.id) + self.assertEqual(voucher.offered_by_user_id, self.admin.id) def test_active_promotion_list(self): promotions = Promotion.active() diff --git a/test/unit/apihit_tests.py b/test/unit/apihit_tests.py index 6b173d6c..77d97660 100644 --- a/test/unit/apihit_tests.py +++ b/test/unit/apihit_tests.py @@ -2,7 +2,7 @@ from functools import wraps from models import Apihit -from base import BaseTestCase +from .base import BaseTestCase class ApihitModelTests(BaseTestCase): diff --git a/test/unit/base.py b/test/unit/base.py index ea523a20..0fd76276 100644 --- a/test/unit/base.py +++ b/test/unit/base.py @@ -27,5 +27,5 @@ def create_database(self, name): return db def generate_string_of_len(self, length): - return ''.join(random.choice(string.letters) for i in xrange(length)) + return ''.join(random.choice(string.ascii_letters) for i in range(length)) diff --git a/test/unit/bookmark_tests.py b/test/unit/bookmark_tests.py index 5b3961a1..24ee2b78 100644 --- a/test/unit/bookmark_tests.py +++ b/test/unit/bookmark_tests.py @@ -2,7 +2,7 @@ from datetime import datetime, timedelta import models -from base import BaseTestCase +from .base import BaseTestCase class BookmarkTests(BaseTestCase): def setUp(self): diff --git a/test/unit/comment_tests.py b/test/unit/comment_tests.py index 5944b174..c648e12a 100644 --- a/test/unit/comment_tests.py +++ b/test/unit/comment_tests.py @@ -1,7 +1,7 @@ from models import Sharedfile, Sourcefile, User, Comment, Conversation from datetime import datetime, timedelta import os, shutil -from base import BaseTestCase +from .base import BaseTestCase class CommentModelTests(BaseTestCase): diff --git a/test/unit/conversation_tests.py b/test/unit/conversation_tests.py index b25c0c92..87a83542 100644 --- a/test/unit/conversation_tests.py +++ b/test/unit/conversation_tests.py @@ -1,6 +1,6 @@ from models import User, Sharedfile, Sourcefile, Shake, Favorite, Comment, Conversation -from base import BaseTestCase +from .base import BaseTestCase class ConversationModelTests(BaseTestCase): diff --git a/test/unit/external_relationship_tests.py b/test/unit/external_relationship_tests.py index dc88f48f..fbab44cf 100644 --- a/test/unit/external_relationship_tests.py +++ b/test/unit/external_relationship_tests.py @@ -1,5 +1,5 @@ from models import User, ExternalRelationship -from base import BaseTestCase +from .base import BaseTestCase class ExternalRelationshipTests(BaseTestCase): diff --git a/test/unit/externalservice_tests.py b/test/unit/externalservice_tests.py index f2876a64..dc1c30bf 100644 --- a/test/unit/externalservice_tests.py +++ b/test/unit/externalservice_tests.py @@ -1,5 +1,5 @@ from models import User, Externalservice, ExternalRelationship -from base import BaseTestCase +from .base import BaseTestCase class ExternalserviceModelTests(BaseTestCase): def setUp(self): diff --git a/test/unit/fileview_tests.py b/test/unit/fileview_tests.py index 273e5112..338b7e1b 100644 --- a/test/unit/fileview_tests.py +++ b/test/unit/fileview_tests.py @@ -1,7 +1,7 @@ import datetime from models import Fileview -from base import BaseTestCase +from .base import BaseTestCase import test.factories class FileviewTests(BaseTestCase): diff --git a/test/unit/notification_tests.py b/test/unit/notification_tests.py index 8cf70196..a79eaa95 100644 --- a/test/unit/notification_tests.py +++ b/test/unit/notification_tests.py @@ -1,5 +1,5 @@ from models import Notification, User, Sourcefile, Sharedfile, Comment, Subscription -from base import BaseTestCase +from .base import BaseTestCase from settings import test_settings as settings class NotificationModelTests(BaseTestCase): diff --git a/test/unit/script_log_tests.py b/test/unit/script_log_tests.py index 2e972ca1..af36a640 100644 --- a/test/unit/script_log_tests.py +++ b/test/unit/script_log_tests.py @@ -1,7 +1,7 @@ import datetime from models import ScriptLog -from base import BaseTestCase +from .base import BaseTestCase import test.factories class ScriptLogTests(BaseTestCase): diff --git a/test/unit/shake_tests.py b/test/unit/shake_tests.py index d2ea1057..c4676971 100644 --- a/test/unit/shake_tests.py +++ b/test/unit/shake_tests.py @@ -1,7 +1,7 @@ from tornado.options import options from models import User, Shake, Sourcefile, Sharedfile, ShakeManager -from base import BaseTestCase +from .base import BaseTestCase class ShakeModelTests(BaseTestCase): diff --git a/test/unit/sharedfile_tests.py b/test/unit/sharedfile_tests.py index b61008a1..f45b2688 100644 --- a/test/unit/sharedfile_tests.py +++ b/test/unit/sharedfile_tests.py @@ -1,7 +1,7 @@ from models import Sharedfile, Sourcefile, User, Comment, Conversation, Shake, Shakesharedfile, Favorite, NSFWLog, Tag, TaggedFile from datetime import datetime, timedelta import os, shutil, calendar -from base import BaseTestCase +from .base import BaseTestCase class SharedfileModelTests(BaseTestCase): diff --git a/test/unit/sourcefile_tests.py b/test/unit/sourcefile_tests.py index eb8faab1..64687b68 100644 --- a/test/unit/sourcefile_tests.py +++ b/test/unit/sourcefile_tests.py @@ -1,5 +1,5 @@ from models import Sharedfile, Sourcefile, User -from base import BaseTestCase +from .base import BaseTestCase import os from tornado.escape import json_decode import re diff --git a/test/unit/task_tests.py b/test/unit/task_tests.py index cb016686..bf70a108 100644 --- a/test/unit/task_tests.py +++ b/test/unit/task_tests.py @@ -1,25 +1,11 @@ -from base import BaseTestCase +from .base import BaseTestCase from models import User, Shake, Sourcefile, Sharedfile, Shakesharedfile, Post, Magicfile from tasks.timeline import add_posts, delete_posts from tornado.options import options -import tweepy from mock import patch -class MockTweepy(object): - count = 0 - - @classmethod - def API(cls, auth_obj): - cls.count = 0 - return cls - - @classmethod - def update_status(cls, *args, **kwargs): - cls.count = cls.count + 1 - - class CeleryTaskTests(BaseTestCase): def setUp(self): @@ -72,23 +58,3 @@ def test_task_timeline_delete_posts(self): posts = Post.all() for post in posts: self.assertTrue(post.deleted) - - @patch('tweepy.API', MockTweepy.API) - def test_tweet_best_posts(self): - old_likes = options.likes_to_tweet - old_magic = options.likes_to_magic - old_debug = options.debug - try: - options.likes_to_tweet = 1 - options.likes_to_magic = 1 - options.debug = False - add_posts(shake_id=self.shake_a.id, sharedfile_id=self.shared_1.id, sourcefile_id=self.source.id) - self.user_b.add_favorite(self.shared_1) - # this like should trigger a tweet - self.assertEqual(MockTweepy.count, 1) - mf = Magicfile.get("sharedfile_id = %s", self.shared_1.id) - self.assertIsNotNone(mf) - finally: - options.likes_to_tweet = old_likes - options.likes_to_magic = old_magic - options.debug = old_debug diff --git a/test/unit/user_tests.py b/test/unit/user_tests.py index 3c9da324..d54528d5 100644 --- a/test/unit/user_tests.py +++ b/test/unit/user_tests.py @@ -1,5 +1,5 @@ from models import User, Sharedfile, Sourcefile, Shake, Favorite, invitation, Shakesharedfile, Subscription, ShakeManager -from base import BaseTestCase +from .base import BaseTestCase import random, os, calendar from datetime import datetime from tornado.options import options From 7075b5a327eeb181e0186b81f0446eadc4ade39c Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 9 Jul 2023 01:44:08 -0500 Subject: [PATCH 02/19] Updates for build process --- .buildkite/steps/test.sh | 4 ++-- Dockerfile.worker | 12 +++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.buildkite/steps/test.sh b/.buildkite/steps/test.sh index b6df82ce..266866ea 100755 --- a/.buildkite/steps/test.sh +++ b/.buildkite/steps/test.sh @@ -11,7 +11,7 @@ docker pull mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} docker tag mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} mltshp/mltshp-web:latest # launch fakes3/mysql/web app -docker-compose -f .buildkite/docker-compose.yml up -d +docker compose -f .buildkite/docker-compose.yml up -d # let's wait and allow mysql/fakes3 to spin up #wait_for localhost 3306 @@ -25,6 +25,6 @@ docker exec -t buildkite_mltshp_1 ./run-tests.sh docker exec -t -e BUILDKITE -e BUILDKITE_JOB_ID -e BUILDKITE_BRANCH -e COVERALLS_REPO_TOKEN buildkite_mltshp_1 ./coveralls-report.sh # tear down containers -docker-compose -f .buildkite/docker-compose.yml down +docker compose -f .buildkite/docker-compose.yml down docker container prune -f diff --git a/Dockerfile.worker b/Dockerfile.worker index 3b5d9678..6c084933 100644 --- a/Dockerfile.worker +++ b/Dockerfile.worker @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM ubuntu:22.04 LABEL maintainer "brad@bradchoate.com" ENV PYTHONUNBUFFERED 1 @@ -10,7 +10,7 @@ RUN apt-get -y update && \ cron \ libmysqlclient-dev \ mysql-client \ - python-dev \ + python3-dev \ libjpeg-dev \ libcurl4-openssl-dev \ curl \ @@ -22,14 +22,8 @@ RUN apt-get -y update && \ libpcre3-dev \ libssl-dev \ libffi-dev \ - python-pip && \ + python3-pip && \ rm -rf /var/lib/apt/lists/* && \ - pip install -U 'pip==20.3.4' 'setuptools==44.0.0' distribute && \ - # Fix for a really weird issue when installing postmark library - # distribute fails to run since it sees a setuptools with "0.7" - # in the name, even though ubuntu:16.04 has pre-installed "20.7.0" - # https://github.com/pypa/setuptools/issues/543 - rm -rf /usr/lib/python2.7/dist-packages/setuptools-20.7.0.egg-info && \ groupadd ubuntu --gid=1010 && \ useradd ubuntu --create-home --home-dir=/home/ubuntu \ --uid=1010 --gid=1010 && \ From 48fc89a509d652773d71982c5bc88fa5ffa153cd Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 9 Jul 2023 02:10:44 -0500 Subject: [PATCH 03/19] Updating README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bc8b5169..b0545e28 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ When you run the application, it launches it into a background process. But if you want to watch the realtime logs emitted by each service, just use this command: - $ docker-compose logs -f + $ docker compose logs -f In addition to that, the web app produces some log files that are captured under the "mounts/logs" folder of your git repository. @@ -147,7 +147,7 @@ containers, just use this command: If you just wish to rebuild the Docker container, use the Docker compose command: - $ docker-compose down + $ docker compose down Then, run another `make run`. From 17c3de7ae95fddcc2410febace39522cef1930e3 Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 9 Jul 2023 02:18:01 -0500 Subject: [PATCH 04/19] Downgrading coverage to satisfy coveralls compatibility --- requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-test.txt b/requirements-test.txt index 87c39233..0f13cc23 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,2 +1,2 @@ -coverage==7.2.7 +coverage==6.5.0 coveralls==3.3.1 From 772fa586c0d6e12a35a8bd4d279cab7ccb863e12 Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 9 Jul 2023 02:19:37 -0500 Subject: [PATCH 05/19] Missed file with branch commit --- torndb.py | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 torndb.py diff --git a/torndb.py b/torndb.py new file mode 100644 index 00000000..c443cea8 --- /dev/null +++ b/torndb.py @@ -0,0 +1,255 @@ +# +# Copyright 2023, MLTSHP +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""A lightweight wrapper around MySQLdb. + +Originally part of the Tornado framework. The tornado.database module +is slated for removal in Tornado 3.0, and it is now available separately +as torndb. + +Updated for Python 3 by Brad Choate, for MLTSHP. ❤️ + +""" + +import copy +import logging +import os +import time + +import MySQLdb.constants +import MySQLdb.converters +import MySQLdb.cursors + +version = "0.4" +version_info = (0, 4, 0, 0) + + +class Connection(object): + """A lightweight wrapper around MySQLdb DB-API connections. + + The main value we provide is wrapping rows in a dict/object so that + columns can be accessed by name. Typical usage:: + + db = torndb.Connection("localhost", "mydatabase") + for article in db.query("SELECT * FROM articles"): + print article.title + + Cursors are hidden by the implementation, but other than that, the methods + are very similar to the DB-API. + + We explicitly set the timezone to UTC and assume the character encoding to + UTF-8 (can be changed) on all connections to avoid time zone and encoding errors. + + The sql_mode parameter is set by default to "traditional", which "gives an error instead of a warning" + (http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html). However, it can be set to + any other mode including blank (None) thereby explicitly clearing the SQL mode. + """ + def __init__(self, host, database, user=None, password=None, + max_idle_time=7 * 3600, connect_timeout=0, + time_zone="+0:00", charset = "utf8", sql_mode="TRADITIONAL"): + self.host = host + self.database = database + self.max_idle_time = float(max_idle_time) + + # removing `conv=CONVERSIONS` + args = dict(use_unicode=True, charset=charset, + db=database, init_command=('SET time_zone = "%s"' % time_zone), + connect_timeout=connect_timeout, sql_mode=sql_mode) + if user is not None: + args["user"] = user + if password is not None: + args["passwd"] = password + + # We accept a path to a MySQL socket file or a host(:port) string + if "/" in host: + args["unix_socket"] = host + else: + self.socket = None + pair = host.split(":") + if len(pair) == 2: + args["host"] = pair[0] + args["port"] = int(pair[1]) + else: + args["host"] = host + args["port"] = 3306 + + self._db = None + self._db_args = args + self._last_use_time = time.time() + try: + self.reconnect() + except Exception: + logging.error("Cannot connect to MySQL on %s", self.host, + exc_info=True) + + def __del__(self): + self.close() + + def close(self): + """Closes this database connection.""" + if getattr(self, "_db", None) is not None: + self._db.close() + self._db = None + + def reconnect(self): + """Closes the existing database connection and re-opens it.""" + self.close() + self._db = MySQLdb.connect(**self._db_args) + self._db.autocommit(True) + + def iter(self, query, *parameters, **kwparameters): + """Returns an iterator for the given query and parameters.""" + self._ensure_connected() + cursor = MySQLdb.cursors.SSCursor(self._db) + try: + self._execute(cursor, query, parameters, kwparameters) + column_names = [d[0] for d in cursor.description] + for row in cursor: + yield Row(list(zip(column_names, row))) + finally: + cursor.close() + + def query(self, query, *parameters, **kwparameters): + """Returns a row list for the given query and parameters.""" + cursor = self._cursor() + try: + self._execute(cursor, query, parameters, kwparameters) + column_names = [d[0] for d in cursor.description] + return [Row(list(zip(column_names, row))) for row in cursor] + finally: + cursor.close() + + def get(self, query, *parameters, **kwparameters): + """Returns the (singular) row returned by the given query. + + If the query has no results, returns None. If it has + more than one result, raises an exception. + """ + rows = self.query(query, *parameters, **kwparameters) + if not rows: + return None + elif len(rows) > 1: + raise Exception("Multiple rows returned for Database.get() query") + else: + return rows[0] + + # rowcount is a more reasonable default return value than lastrowid, + # but for historical compatibility execute() must return lastrowid. + def execute(self, query, *parameters, **kwparameters): + """Executes the given query, returning the lastrowid from the query.""" + return self.execute_lastrowid(query, *parameters, **kwparameters) + + def execute_lastrowid(self, query, *parameters, **kwparameters): + """Executes the given query, returning the lastrowid from the query.""" + cursor = self._cursor() + try: + self._execute(cursor, query, parameters, kwparameters) + return cursor.lastrowid + finally: + cursor.close() + + def execute_rowcount(self, query, *parameters, **kwparameters): + """Executes the given query, returning the rowcount from the query.""" + cursor = self._cursor() + try: + self._execute(cursor, query, parameters, kwparameters) + return cursor.rowcount + finally: + cursor.close() + + def executemany(self, query, parameters): + """Executes the given query against all the given param sequences. + + We return the lastrowid from the query. + """ + return self.executemany_lastrowid(query, parameters) + + def executemany_lastrowid(self, query, parameters): + """Executes the given query against all the given param sequences. + + We return the lastrowid from the query. + """ + cursor = self._cursor() + try: + cursor.executemany(query, parameters) + return cursor.lastrowid + finally: + cursor.close() + + def executemany_rowcount(self, query, parameters): + """Executes the given query against all the given param sequences. + + We return the rowcount from the query. + """ + cursor = self._cursor() + try: + cursor.executemany(query, parameters) + return cursor.rowcount + finally: + cursor.close() + + update = execute_rowcount + updatemany = executemany_rowcount + + insert = execute_lastrowid + insertmany = executemany_lastrowid + + def _ensure_connected(self): + # Mysql by default closes client connections that are idle for + # 8 hours, but the client library does not report this fact until + # you try to perform a query and it fails. Protect against this + # case by preemptively closing and reopening the connection + # if it has been idle for too long (7 hours by default). + if (self._db is None or + (time.time() - self._last_use_time > self.max_idle_time)): + self.reconnect() + self._last_use_time = time.time() + + def _cursor(self): + self._ensure_connected() + return self._db.cursor() + + def _execute(self, cursor, query, parameters, kwparameters): + try: + return cursor.execute(query, kwparameters or parameters) + except OperationalError: + logging.error("Error connecting to MySQL on %s", self.host) + self.close() + raise + + +class Row(dict): + """A dict that allows for object-like property access syntax.""" + def __getattr__(self, name): + try: + return self[name] + except KeyError: + raise AttributeError(name) + +# Fix the access conversions to properly recognize unicode/binary +#FIELD_TYPE = MySQLdb.constants.FIELD_TYPE +#FLAG = MySQLdb.constants.FLAG +#CONVERSIONS = copy.copy(MySQLdb.converters.conversions) + +#field_types = [FIELD_TYPE.BLOB, FIELD_TYPE.STRING, FIELD_TYPE.VAR_STRING] +#if 'VARCHAR' in vars(FIELD_TYPE): +# field_types.append(FIELD_TYPE.VARCHAR) + +#for field_type in field_types: +# CONVERSIONS[field_type] = [(FLAG.BINARY, str)] + [CONVERSIONS[field_type]] + +# Alias some common MySQL exceptions +IntegrityError = MySQLdb.IntegrityError +OperationalError = MySQLdb.OperationalError From ba6ec281bc88ce4c160eae01706944ca0b152654 Mon Sep 17 00:00:00 2001 From: Dan Phiffer Date: Wed, 12 Jul 2023 11:35:29 -0400 Subject: [PATCH 06/19] very basic alt_text picker functionality --- handlers/tools.py | 5 ++++- templates/tools/picker.html | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/handlers/tools.py b/handlers/tools.py index 2d9616e2..8e260915 100644 --- a/handlers/tools.py +++ b/handlers/tools.py @@ -20,6 +20,7 @@ class PickerPopupHandler(BaseHandler): def get(self): url = self.get_argument('url', None) source_url = self.get_argument('source_url', '') + alt_text = self.get_argument('alt', '') file_name = self.get_argument('title', '') current_user = self.get_current_user_object() @@ -71,7 +72,7 @@ def get(self): #replace plus signs with %20's return self.render("tools/picker.html", file_name=file_name, width="", height="", \ url=parsed_url.scheme + "://" + parsed_url.netloc + parsed_url.path + parsed_url_query, \ - source_url=source_url, description='', is_video=is_video, shakes=shakes, + source_url=source_url, description='', alt_text=alt_text, is_video=is_video, shakes=shakes, can_upload_this_month=can_upload_this_month) @tornado.web.authenticated @@ -102,6 +103,7 @@ def on_response(self, response): title = self.get_argument("title", None) source_url = self.get_argument('source_url', None) description = self.get_argument('description', None) + alt_text = self.get_argument('alt_text', None) shake_id = self.get_argument('shake_id', None) if title == file_name: @@ -135,6 +137,7 @@ def on_response(self, response): shake_id = shake_id) sf.source_url = source_url sf.description = description + sf.alt_text = alt_text sf.save() if not options.debug: # file cleanup diff --git a/templates/tools/picker.html b/templates/tools/picker.html index afacc1e8..84a81eeb 100644 --- a/templates/tools/picker.html +++ b/templates/tools/picker.html @@ -40,6 +40,21 @@

In order to post, you have to confirm your email address first. Check your e +
+
+
+ + {% if errors.alt_text %} +
+ + {{errors.alt_text }} + +
+ {% end %} +
+
+
+ {% if len(shakes) > 1 %}
From bc2dc8a5d627398487339ca2af181589495f84c8 Mon Sep 17 00:00:00 2001 From: Dan Phiffer Date: Wed, 12 Jul 2023 17:56:43 -0400 Subject: [PATCH 07/19] tabbed picker interface for alt text --- static/css/main.min.css | 2 +- static/js/tools.js | 35 ++++++++++++++++++++++++----------- templates/tools/picker.html | 11 +++++++++-- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/static/css/main.min.css b/static/css/main.min.css index 0e99a8f4..a8150b61 100644 --- a/static/css/main.min.css +++ b/static/css/main.min.css @@ -5,4 +5,4 @@ * * @see https://github.com/MLTSHP/mltshp-patterns/ */ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{-webkit-text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{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-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}:root{--color-background-content-dark:#263238;--color-background-content-light:#fff;--color-background-page-light:#dbfaff;--color-background-page-dark:#13454c;--color-base-pink:#ff0080;--color-base-pink-dark:#c06;--color-base-pink-pastel-dark:#ff0080;--color-base-pink-pastel-light:#ffcce8;--color-base-blue:#00aeff;--color-base-blue-dark:#008bcc;--color-base-blue-very-light:#f2fdff;--color-base-blue-pastel-dark:#00aeff;--color-base-blue-pastel-medium:#aaf3ff;--color-base-blue-pastel-light:#daf3ff;--color-base-blue-pastel-very-light:#dbfaff;--color-base-blue-gray-light:#e4eff1;--color-base-cyan-very-dark:#13454c;--color-base-green:#09b896;--color-base-green-dark:#079378;--color-base-green-pastel-dark:#0caf8b;--color-base-green-pastel-light:#b9ff8d;--color-base-green-pastel-very-light:#dfffcb;--color-base-orange:#ff9600;--color-base-orange-dark:#cc7800;--color-base-orange-pastel-dark:#ff9600;--color-base-orange-pastel-light:#ffe466;--color-base-red:#f30;--color-base-red-dark:#cc2900;--color-base-red-pastel-dark:#ff1c1c;--color-base-red-pastel-light:#ffd4d4;--color-base-gray:#a1a1a1;--color-base-gray-light:#e9e9e9;--color-base-gray-pastel-dark:#8d8d87;--color-base-gray-pastel-light:#cecec6;--color-base-yellow-pastel-light:#fffbd9;--color-base-yellow-transparent-600:rgba(252,255,0,.66);--color-base-black-transparent:transparent;--color-base-black-transparent-100:rgba(0,0,0,.13);--color-base-black-transparent-200:rgba(0,0,0,.25);--color-base-black-transparent-300:rgba(0,0,0,.35);--color-base-black-transparent-500:rgba(0,0,0,.5);--color-base-black-transparent-600:rgba(0,0,0,.6);--color-base-black-transparent-800:rgba(0,0,0,.8);--color-base-white-transparent:hsla(0,0%,100%,0);--color-base-white-transparent-100:hsla(0,0%,100%,.13);--color-base-white-transparent-200:hsla(0,0%,100%,.25);--color-base-white-transparent-300:hsla(0,0%,100%,.35);--color-base-white-transparent-500:hsla(0,0%,100%,.5);--color-base-white-transparent-600:hsla(0,0%,100%,.65);--color-base-white-transparent-800:hsla(0,0%,100%,.85);--color-base-white:#fff;--color-base-black:#000;--color-base-blue-dark-mode:#263238;--color-base-gray-050:#fafafa;--color-base-gray-100:#f5f5f5;--color-base-gray-200:#eee;--color-base-gray-300:#e0e0e0;--color-base-gray-400:#bdbdbd;--color-base-gray-500:#9e9e9e;--color-base-gray-600:#757575;--color-base-gray-700:#616161;--color-base-gray-800:#424242;--color-base-gray-900:#212121;--color-brand-primary:#ff0080;--color-brand-primary-dark:#c06;--color-brand-primary-pastel-light:#ffcce8;--color-brand-primary-pastel-dark:#ff0080;--color-brand-secondary:#00aeff;--color-brand-secondary-dark:#008bcc;--color-brand-secondary-very-light:#f2fdff;--color-brand-secondary-pastel-light:#daf3ff;--color-brand-secondary-pastel-medium:#aaf3ff;--color-brand-secondary-pastel-dark:#00aeff;--color-status-success:#09b896;--color-status-success-dark:#079378;--color-status-success-pastel-very-light:#dfffcb;--color-status-success-pastel-light:#b9ff8d;--color-status-success-pastel-dark:#0caf8b;--color-status-warning:#ff9600;--color-status-warning-dark:#cc7800;--color-status-warning-pastel-light:#ffe466;--color-status-warning-pastel-dark:#ff9600;--color-status-danger:#f30;--color-status-danger-dark:#cc2900;--color-status-danger-pastel-light:#ffd4d4;--color-status-danger-pastel-dark:#ff1c1c;--color-status-disabled:#a1a1a1;--color-status-disabled-light:#e9e9e9;--color-status-disabled-pastel-light:#cecec6;--color-status-disabled-pastel-dark:#8d8d87;--color-status-edit:rgba(252,255,0,.66);--color-status-highlight:#fffbd9;--color-text-dark:rgba(0,0,0,.8);--color-text-dark-emphasis:#000;--color-text-dark-secondary:rgba(0,0,0,.6);--color-text-light:hsla(0,0%,100%,.85);--color-text-light-emphasis:#fff;--color-text-light-secondary:hsla(0,0%,100%,.65);--color-text-link:#00aeff;--color-text-link-hover:#008bcc;--color-text-link-primary:#ff0080;--color-text-link-primary-hover:#c06;--color-text-link-danger:#f30;--color-text-link-danger-hover:#cc2900;--font-family-system:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--font-family-mono:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;--number-font-weight-light:300;--number-font-weight-normal:400;--number-font-weight-medium:500;--number-font-weight-semibold:600;--number-font-weight-bold:700;--number-font-weight-heavy:800;--size-avatar-tiny:20px;--size-avatar-small:30px;--size-avatar-default:48px;--size-avatar-large:100px;--size-border-radius-default:5px;--size-border-radius-large:10px;--size-breakpoint-xs:320px;--size-breakpoint-sm:375px;--size-breakpoint-md:480px;--size-breakpoint-lg:768px;--size-breakpoint-xl:960px;--size-breakpoint-xxl:1440px;--size-font-base:16px;--size-font-input:16px;--size-spacing-half:5px;--size-spacing-default:10px;--size-spacing-half-again:15px;--size-spacing-double:20px;--size-spacing-triple:30px;--size-spacing-quadruple:40px;--time-speed-instant:0s;--time-speed-immediate:0.1s;--time-speed-quick:0.2s;--time-speed-prompt:0.3s;--time-speed-slow:0.4s;--time-speed-glacial:0.6s}:root{--color-text-link-underline:rgba(0,174,255,.33);--color-text-link-underline-hover:rgba(0,139,204,.33);--color-text-link-primary-underline:rgba(255,0,128,.33);--color-text-link-primary-underline-hover:rgba(204,0,102,.33);--color-text-link-danger-underline:rgba(255,51,0,.33);--color-text-link-danger-underline-hover:rgba(204,41,0,.33)}:root{--color-background-page:var(--color-background-page-light);--color-background-content:var(--color-background-content-light);--color-background-content-secondary:var(--color-base-gray-100);--color-page-text:var(--color-text-dark);--color-page-text-emphasis:var(--color-text-dark-emphasis);--color-page-text-secondary:var(--color-text-dark-secondary);--color-border-default:var(--color-base-gray-300);--color-border-form:var(--color-base-blue-gray-light);--color-border-focus-ring:rgba(0,174,255,.25);--color-form-bg:var(--color-brand-secondary-very-light);--color-form-error-bubble-bg:var(--color-brand-secondary-pastel-medium);--color-form-error-bubble-text:var(--color-page-text);--number-font-weight-bold:bold;--number-font-weight-normal:normal;--letter-spacing:normal;--color-bg-primary-brand-pastel:var(--color-brand-primary-pastel-light);--color-bg-secondary-brand-pastel:var(--color-background-page-light);--color-bg-success-pastel:var(--color-status-success-pastel-very-light);--color-bg-danger-pastel:var(--color-status-danger-pastel-light);--color-bg-warning-pastel:var(--color-status-highlight);--color-background-button-primary:var(--color-brand-primary);--color-background-button-primary-hover:var(--color-brand-primary-dark);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-light);--color-background-button-primary-pastel-hover:var(--color-brand-primary);--color-text-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-text-button-primary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-secondary:var(--color-brand-secondary);--color-background-button-secondary-hover:var(--color-brand-secondary-dark);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-light);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary);--color-text-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-text-button-secondary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-success:var(--color-status-success);--color-background-button-success-hover:var(--color-status-success-dark);--color-background-button-success-pastel:var(--color-status-success-pastel-light);--color-background-button-success-pastel-hover:var(--color-status-success);--color-text-button-success-pastel:var(--color-status-success-pastel-dark);--color-text-button-success-pastel-hover:var(--color-text-light-emphasis);--color-background-button-warning:var(--color-status-warning);--color-background-button-warning-hover:var(--color-status-warning-dark);--color-background-button-warning-pastel:var(--color-status-warning-pastel-light);--color-background-button-warning-pastel-hover:var(--color-status-warning);--color-text-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-text-button-warning-pastel-hover:var(--color-text-light-emphasis);--color-background-button-danger:var(--color-status-danger);--color-background-button-danger-hover:var(--color-status-danger-dark);--color-background-button-danger-pastel:var(--color-status-danger-pastel-light);--color-background-button-danger-pastel-hover:var(--color-status-danger);--color-text-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-text-button-danger-pastel-hover:var(--color-text-light-emphasis);--color-background-choose-shake-link:var(--color-background-content);--color-background-choose-shake-link-hover:var(--color-status-highlight);--color-text-choose-shake-link:var(--color-text-link-primary);--color-text-choose-shake-link-hover:var(--color-text-link-primary-hover)}.t-light{--color-background-page:var(--color-background-page-light);--color-background-content:var(--color-background-content-light);--color-background-content-secondary:var(--color-base-gray-100);--color-page-text:var(--color-text-dark);--color-page-text-emphasis:var(--color-text-dark-emphasis);--color-page-text-secondary:var(--color-text-dark-secondary);--color-border-default:var(--color-base-gray-300);--color-border-form:var(--color-base-blue-gray-light);--color-border-focus-ring:rgba(0,174,255,.25);--color-form-bg:var(--color-brand-secondary-very-light);--color-form-error-bubble-bg:var(--color-brand-secondary-pastel-medium);--color-form-error-bubble-text:var(--color-page-text);--number-font-weight-bold:bold;--number-font-weight-normal:normal;--letter-spacing:normal;--color-bg-primary-brand-pastel:var(--color-brand-primary-pastel-light);--color-bg-secondary-brand-pastel:var(--color-background-page-light);--color-bg-success-pastel:var(--color-status-success-pastel-very-light);--color-bg-danger-pastel:var(--color-status-danger-pastel-light);--color-bg-warning-pastel:var(--color-status-highlight);--color-background-button-primary:var(--color-brand-primary);--color-background-button-primary-hover:var(--color-brand-primary-dark);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-light);--color-background-button-primary-pastel-hover:var(--color-brand-primary);--color-text-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-text-button-primary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-secondary:var(--color-brand-secondary);--color-background-button-secondary-hover:var(--color-brand-secondary-dark);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-light);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary);--color-text-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-text-button-secondary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-success:var(--color-status-success);--color-background-button-success-hover:var(--color-status-success-dark);--color-background-button-success-pastel:var(--color-status-success-pastel-light);--color-background-button-success-pastel-hover:var(--color-status-success);--color-text-button-success-pastel:var(--color-status-success-pastel-dark);--color-text-button-success-pastel-hover:var(--color-text-light-emphasis);--color-background-button-warning:var(--color-status-warning);--color-background-button-warning-hover:var(--color-status-warning-dark);--color-background-button-warning-pastel:var(--color-status-warning-pastel-light);--color-background-button-warning-pastel-hover:var(--color-status-warning);--color-text-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-text-button-warning-pastel-hover:var(--color-text-light-emphasis);--color-background-button-danger:var(--color-status-danger);--color-background-button-danger-hover:var(--color-status-danger-dark);--color-background-button-danger-pastel:var(--color-status-danger-pastel-light);--color-background-button-danger-pastel-hover:var(--color-status-danger);--color-text-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-text-button-danger-pastel-hover:var(--color-text-light-emphasis);--color-background-choose-shake-link:var(--color-background-content);--color-background-choose-shake-link-hover:var(--color-status-highlight);--color-text-choose-shake-link:var(--color-text-link-primary);--color-text-choose-shake-link-hover:var(--color-text-link-primary-hover)}@media screen and (prefers-color-scheme:dark){:root{--color-background-page:var(--color-background-page-dark);--color-background-content:var(--color-background-content-dark);--color-background-content-secondary:var(--color-base-gray-900);--color-page-text:var(--color-text-light);--color-page-text-emphasis:var(--color-text-light-emphasis);--color-page-text-secondary:var(--color-text-light-secondary);--color-border-default:var(--color-base-gray-700);--color-border-form:var(--color-base-gray-800);--color-border-focus-ring:rgba(0,174,255,.4);--color-form-bg:var(--color-background-content-secondary);--color-form-error-bubble-bg:var(--color-base-gray-800);--color-form-error-bubble-text:var(--color-status-danger);--number-font-weight-bold:600;--number-font-weight-normal:300;--letter-spacing:0.025em;--color-bg-primary-brand-pastel:rgba(255,0,128,.1);--color-bg-secondary-brand-pastel:rgba(0,174,255,.1);--color-bg-success-pastel:rgba(9,184,150,.1);--color-bg-danger-pastel:rgba(255,51,0,.1);--color-bg-warning-pastel:rgba(255,150,0,.1);--color-background-button-primary:var(--color-brand-primary-dark);--color-background-button-primary-hover:var(--color-brand-primary);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-background-button-primary-pastel-hover:var(--color-brand-primary-pastel-light);--color-text-button-primary-pastel:var(--color-text-light-emphasis);--color-text-button-primary-pastel-hover:var(--color-brand-primary-pastel-dark);--color-background-button-secondary:var(--color-brand-secondary-dark);--color-background-button-secondary-hover:var(--color-brand-secondary);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-light);--color-text-button-secondary-pastel:var(--color-text-light-emphasis);--color-text-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-dark);--color-background-button-success:var(--color-status-success-dark);--color-background-button-success-hover:var(--color-status-success);--color-background-button-success-pastel:var(--color-status-success-pastel-dark);--color-background-button-success-pastel-hover:var(--color-status-success-pastel-light);--color-text-button-success-pastel:var(--color-text-light-emphasis);--color-text-button-success-pastel-hover:var(--color-status-success-pastel-dark);--color-background-button-warning:var(--color-status-warning-dark);--color-background-button-warning-hover:var(--color-status-warning);--color-background-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-background-button-warning-pastel-hover:var(--color-status-warning-pastel-light);--color-text-button-warning-pastel:var(--color-text-light-emphasis);--color-text-button-warning-pastel-hover:var(--color-status-warning-pastel-dark);--color-background-button-danger:var(--color-status-danger-dark);--color-background-button-danger-hover:var(--color-status-danger);--color-background-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-background-button-danger-pastel-hover:var(--color-status-danger-pastel-light);--color-text-button-danger-pastel:var(--color-text-light-emphasis);--color-text-button-danger-pastel-hover:var(--color-status-danger-pastel-dark);--color-background-choose-shake-link:var(--color-background-button-success);--color-background-choose-shake-link-hover:var(--color-background-button-success-pastel-hover);--color-text-choose-shake-link:var(--color-text-light-emphasis);--color-text-choose-shake-link-hover:var(--color-text-button-success-pastel-hover)}}.t-dark{--color-background-page:var(--color-background-page-dark);--color-background-content:var(--color-background-content-dark);--color-background-content-secondary:var(--color-base-gray-900);--color-page-text:var(--color-text-light);--color-page-text-emphasis:var(--color-text-light-emphasis);--color-page-text-secondary:var(--color-text-light-secondary);--color-border-default:var(--color-base-gray-700);--color-border-form:var(--color-base-gray-800);--color-border-focus-ring:rgba(0,174,255,.4);--color-form-bg:var(--color-background-content-secondary);--color-form-error-bubble-bg:var(--color-base-gray-800);--color-form-error-bubble-text:var(--color-status-danger);--number-font-weight-bold:600;--number-font-weight-normal:300;--letter-spacing:0.025em;--color-bg-primary-brand-pastel:rgba(255,0,128,.1);--color-bg-secondary-brand-pastel:rgba(0,174,255,.1);--color-bg-success-pastel:rgba(9,184,150,.1);--color-bg-danger-pastel:rgba(255,51,0,.1);--color-bg-warning-pastel:rgba(255,150,0,.1);--color-background-button-primary:var(--color-brand-primary-dark);--color-background-button-primary-hover:var(--color-brand-primary);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-background-button-primary-pastel-hover:var(--color-brand-primary-pastel-light);--color-text-button-primary-pastel:var(--color-text-light-emphasis);--color-text-button-primary-pastel-hover:var(--color-brand-primary-pastel-dark);--color-background-button-secondary:var(--color-brand-secondary-dark);--color-background-button-secondary-hover:var(--color-brand-secondary);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-light);--color-text-button-secondary-pastel:var(--color-text-light-emphasis);--color-text-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-dark);--color-background-button-success:var(--color-status-success-dark);--color-background-button-success-hover:var(--color-status-success);--color-background-button-success-pastel:var(--color-status-success-pastel-dark);--color-background-button-success-pastel-hover:var(--color-status-success-pastel-light);--color-text-button-success-pastel:var(--color-text-light-emphasis);--color-text-button-success-pastel-hover:var(--color-status-success-pastel-dark);--color-background-button-warning:var(--color-status-warning-dark);--color-background-button-warning-hover:var(--color-status-warning);--color-background-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-background-button-warning-pastel-hover:var(--color-status-warning-pastel-light);--color-text-button-warning-pastel:var(--color-text-light-emphasis);--color-text-button-warning-pastel-hover:var(--color-status-warning-pastel-dark);--color-background-button-danger:var(--color-status-danger-dark);--color-background-button-danger-hover:var(--color-status-danger);--color-background-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-background-button-danger-pastel-hover:var(--color-status-danger-pastel-light);--color-text-button-danger-pastel:var(--color-text-light-emphasis);--color-text-button-danger-pastel-hover:var(--color-status-danger-pastel-dark);--color-background-choose-shake-link:var(--color-background-button-success);--color-background-choose-shake-link-hover:var(--color-background-button-success-pastel-hover);--color-text-choose-shake-link:var(--color-text-light-emphasis);--color-text-choose-shake-link-hover:var(--color-text-button-success-pastel-hover);background-color:var(--color-background-page);color:var(--color-page-text)}*,:after,:before{box-sizing:border-box}body{background-color:var(--color-background-page);color:var(--color-page-text)}:focus{box-shadow:0 0 0 .25rem var(--color-border-focus-ring);outline:0}a{color:var(--color-text-link);text-decoration-color:var(--color-text-link-underline);transition:background-color var(--time-speed-quick) ease,color var(--time-speed-quick) ease}@media screen and (prefers-reduced-motion:reduce){a{transition:none}}a:active,a:focus,a:hover{color:var(--color-text-link-hover);text-decoration-color:var(--color-text-link-underline-hover)}.link--primary{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.link--primary:active,.link--primary:focus,.link--primary:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.link--primary:active,.link--primary:focus,.link--primary:hover{text-decoration:underline}.link--danger{color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.link--danger:active,.link--danger:focus,.link--danger:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}img,video{height:auto}iframe,img,video{max-width:100%}.data-wrapper,[style*="--aspect-ratio"]{position:relative}.data-wrapper:before,[style*="--aspect-ratio"]:before{content:"";display:block;padding-bottom:calc(100%*var(--aspect-ratio))}.data-wrapper>:first-child,[style*="--aspect-ratio"]>:first-child{height:100%;left:0;position:absolute;top:0;width:100%}.data-wrapper:before{padding-bottom:56.25%}html{font-family:var(--font-family-system);font-size:var(--size-font-base);font-weight:var(--number-font-weight-normal);letter-spacing:var(--letter-spacing);line-height:1.3}h1,h2,h3,h4,h5,h6{line-height:1.15;margin:0}.u-display,h1{font-size:3rem;letter-spacing:-1px}@media screen and (min-width:768px){.u-display,h1{font-size:3.75rem}}b,h1,h2,h3,h4,h5,h6,label,legend,strong,th{font-weight:var(--number-font-weight-bold)}.code,code,pre{font-family:var(--font-family-mono)}pre{overflow-x:auto;width:100%}.sr-only{clip:rect(0 0 0 0);border:0;clip-path:polygon(0 0,0 0,0 0);-webkit-clip-path:polygon(0 0,0 0,0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.clear{clear:both}@media screen and (max-width:480px){.hide-on-small{display:none}}.danger,.error{color:var(--color-status-danger)}.user-name{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.avatar--img{display:block;height:var(--size-avatar-default);max-width:none;object-fit:cover;width:var(--size-avatar-default)}.caret{border:.5em solid transparent;border-top-color:currentcolor;border-width:.5em .433em 0;display:inline-block;position:relative;top:-.1em;transform-origin:center 40%;transition:transform .25s ease}@media screen and (prefers-reduced-motion:reduce){.caret{transition:none}}.is-active .caret{transform:rotate(180deg)}.panel--example{margin:.25em 0 1em}.page{background-color:var(--color-background-page);padding:0 .5em;position:relative}.wrapper{display:flow-root;margin:0 auto;max-width:960px}.content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-triple);margin-top:var(--size-spacing-half-again)}.content:after{clear:both;content:"";display:table}.content-narrow,.content-styleguide{margin:var(--size-spacing-quadruple) auto var(--size-spacing-triple);max-width:700px;padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-narrow,.content-styleguide{padding:var(--size-spacing-quadruple)}}.content-narrow p,.content-styleguide p{font-size:1.625rem;line-height:2.5;margin:2em 0}.content-narrow .extra-info p,.content-styleguide .extra-info p{color:var(--color-page-text-secondary);font-size:1rem;line-height:1.4}.content-styleguide{max-width:none}.content-with-sidebar{display:flex;flex-direction:column}@media screen and (min-width:768px){.content-with-sidebar{flex-flow:row wrap}}.content-with-sidebar>*{min-width:0}.content-with-sidebar .header{flex:0 0 100%}.content-with-sidebar .body{flex:1;padding-right:1px}.content-with-sidebar .sidebar{background:linear-gradient(to top,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));order:2;padding:var(--size-spacing-double)}@media screen and (min-width:768px){.content-with-sidebar .sidebar{background:linear-gradient(to right,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));flex:0 0 325px;order:0}.content-with-sidebar-reversed .sidebar{background:linear-gradient(to left,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));order:2}}.alert{background:var(--color-background-content-secondary);font-size:.9rem;line-height:1.25;padding:var(--size-spacing-half-again)}.alert p{margin:0}.alert a{font-weight:var(--number-font-weight-bold)}.alert--marquee{padding:0}.alert--marquee marquee{padding:var(--size-spacing-half-again)}.alert--warning{background:var(--color-bg-warning-pastel);color:var(--color-status-warning)}.alert--danger{background:var(--color-bg-danger-pastel);color:var(--color-status-danger)}.alert--danger a{color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.alert--danger a:active,.alert--danger a:focus,.alert--danger a:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}.migration-reminder{background:var(--color-background-content-secondary);background:var(--color-bg-warning-pastel);color:var(--color-status-warning);font-size:.9rem;font-weight:var(--number-font-weight-bold);line-height:1.25;padding:var(--size-spacing-half-again)}.migration-reminder p{margin:0}.migration-reminder a{font-weight:var(--number-font-weight-bold)}.bookmark{align-items:flex-start;display:flex;flex-wrap:wrap}.bookmark-flag{background:var(--color-brand-primary);color:var(--color-text-light-emphasis);flex:none;font-size:.75rem;font-weight:var(--number-font-weight-bold);margin-top:-3px;position:relative;z-index:2}.bookmark-flag:after,.bookmark-flag:before{content:"";display:block;position:absolute}.bookmark-flag:before{background:inherit;border-radius:var(--size-border-radius-default) 0 0 var(--size-border-radius-default);height:calc(100% + var(--size-spacing-half));left:calc(var(--size-spacing-half)*-1);padding-bottom:var(--size-spacing-half);top:0;width:var(--size-spacing-half)}.bookmark-flag:after{background:var(--color-base-black-transparent-300);border-radius:3px 0 0 3px;bottom:-4px;height:4px;left:-3px;width:3px}.bookmark-flag--content{display:block;line-height:1;padding:.5em 1em}.bookmark-flag--content:after{border-width:1em;border-bottom:1em solid var(--color-brand-primary);border-left:0 solid var(--color-brand-primary);border-right:.866em solid transparent;border-top:1em solid var(--color-brand-primary);content:"";display:block;position:absolute;right:-.833em;top:0}.jump-back{flex:1;font-size:.6875rem;margin-left:.75em;padding:calc(.5em - 3px) 0 .5em .75em;white-space:nowrap}.jump-back a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.jump-back a:active,.jump-back a:focus,.jump-back a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.jump-back a:active,.jump-back a:focus,.jump-back a:hover{text-decoration:underline}button{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.btn,.label{background-color:var(--color-base-gray-700);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);display:inline-block;font-size:18px;font-weight:500;height:2.2em;letter-spacing:.033em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.btn{cursor:pointer}.btn:focus,.btn:hover{color:var(--color-text-light-emphasis)}.btn:focus{outline:none}.btn:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.btn-pastel,.label-pastel{font-weight:600;letter-spacing:.015em;text-shadow:none}.btn-pastel:disabled,.label-pastel:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.btn-shadow{box-shadow:.22em .22em 0 var(--color-base-black-transparent-100);margin-right:.22em}.btn-shadow:disabled{box-shadow:none!important}.btn-padded{height:2.6em;line-height:2.6em;padding:0 1.25em}.btn-large,.label-large{font-size:24px;letter-spacing:.066em}.btn-large.btn-pastel,.label-large.label-pastel{letter-spacing:.033em}.btn-small,.label-small{font-size:12px;font-weight:400}.btn-tiny,.label-tiny{font-size:10px;font-weight:400;padding:0 .66em}.btn-small.btn-pastel,.btn-tiny.btn-pastel,.label-small.label-pastel,.label-tiny.label-pastel{font-weight:500}.btn-icon{padding:0;width:2.2em}.btn-icon.btn-padded{padding:0;width:2.6em}.btn-primary,.label-primary{background-color:var(--color-background-button-primary)}.btn-primary:focus,.btn-primary:hover{background-color:var(--color-background-button-primary-hover)}.btn-primary.btn-pastel,.label-primary.label-pastel{background-color:var(--color-background-button-primary-pastel);color:var(--color-text-button-primary-pastel)}.btn-primary.btn-pastel:focus,.btn-primary.btn-pastel:hover{background-color:var(--color-background-button-primary-pastel-hover);color:var(--color-text-button-primary-pastel-hover)}.btn-secondary,.label-secondary{background-color:var(--color-background-button-secondary)}.btn-secondary:focus,.btn-secondary:hover{background-color:var(--color-background-button-secondary-hover)}.btn-secondary.btn-pastel,.label-secondary.label-pastel{background-color:var(--color-background-button-secondary-pastel);color:var(--color-text-button-secondary-pastel)}.btn-secondary.btn-pastel:focus,.btn-secondary.btn-pastel:hover{background-color:var(--color-background-button-secondary-pastel-hover);color:var(--color-text-button-secondary-pastel-hover)}.btn-success,.label-success{background-color:var(--color-background-button-success)}.btn-success:focus,.btn-success:hover{background-color:var(--color-background-button-success-hover)}.btn-success.btn-pastel,.label-success.label-pastel{background-color:var(--color-background-button-success-pastel);color:var(--color-text-button-success-pastel)}.btn-success.btn-pastel:focus,.btn-success.btn-pastel:hover{background-color:var(--color-background-button-success-pastel-hover);color:var(--color-text-button-success-pastel-hover)}.btn-warning,.label-warning{background-color:var(--color-background-button-warning)}.btn-warning:focus,.btn-warning:hover{background-color:var(--color-background-button-warning-hover)}.btn-warning.btn-pastel,.label-warning.label-pastel{background-color:var(--color-background-button-warning-pastel);color:var(--color-text-button-warning-pastel)}.btn-warning.btn-pastel:focus,.btn-warning.btn-pastel:hover{background-color:var(--color-background-button-warning-pastel-hover);color:var(--color-text-button-warning-pastel-hover)}.btn-danger,.label-danger{background-color:var(--color-background-button-danger)}.btn-danger:focus,.btn-danger:hover{background-color:var(--color-background-button-danger-hover)}.btn-danger.btn-pastel,.label-danger.label-pastel{background-color:var(--color-background-button-danger-pastel);color:var(--color-text-button-danger-pastel)}.btn-danger.btn-pastel:focus,.btn-danger.btn-pastel:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.choose-a-shake{position:relative;text-align:left;z-index:3}.choose-a-shake--toggle{width:100%}.choose-a-shake.is-expanded .choose-a-shake--toggle{border-bottom-left-radius:0;border-bottom-right-radius:0;box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);outline:none}.choose-a-shake.is-expanded .choose-a-shake--toggle:focus,.choose-a-shake.is-expanded .choose-a-shake--toggle:hover{background-color:var(--color-background-button-success-pastel);color:var(--color-text-button-success-pastel)}.choose-a-shake.is-expanded .choose-a-shake--toggle .caret{transform:rotate(180deg)}.choose-a-shake--dropdown{background-color:var(--color-background-button-success-pastel);border-radius:var(--size-border-radius-large);border-top-right-radius:0;box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);display:none;max-width:400px;padding:var(--size-spacing-default);padding-top:1px;position:absolute;right:0;top:calc(100% - .22em)}@media screen and (min-width:768px){.choose-a-shake--dropdown{border-top-left-radius:0;border-top-right-radius:var(--size-border-radius-large);left:0;right:auto}}.choose-a-shake.is-expanded .choose-a-shake--dropdown{display:block}.choose-a-shake--dropdown .add-a-shake{display:block;float:right;margin-top:var(--size-spacing-default)}.choose-a-shake--dropdown ul{list-style:none;margin:0;padding:0}.choose-a-shake--dropdown ul li a{background-color:var(--color-background-choose-shake-link);border-radius:var(--size-border-radius-large);color:var(--color-text-choose-shake-link);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow:hidden;padding:var(--size-spacing-default) var(--size-spacing-half-again);text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.choose-a-shake--dropdown ul li a:active,.choose-a-shake--dropdown ul li a:focus,.choose-a-shake--dropdown ul li a:hover{text-decoration:underline}.choose-a-shake--dropdown ul li a:active,.choose-a-shake--dropdown ul li a:focus,.choose-a-shake--dropdown ul li a:hover{background-color:var(--color-background-choose-shake-link-hover);color:var(--color-text-choose-shake-link-hover);text-decoration:none}.choose-a-shake--dropdown ul.top-shakes li a{margin:var(--size-spacing-default) 0}.choose-a-shake--dropdown ul.group-shakes{background-color:var(--color-background-choose-shake-link);border-radius:var(--size-border-radius-large)}.conversations-nav h3{color:var(--color-page-text);font-size:1.125rem;margin-bottom:var(--size-spacing-half-again);margin-top:var(--size-spacing-triple)}.conversations-nav ul{list-style:none;margin:0;padding:0}.conversations-nav li{margin:var(--size-spacing-half) 0}.conversations-nav li.selected a{color:var(--color-status-disabled)}.conversations-nav a{color:var(--color-brand-primary);font-size:.875rem;font-weight:var(--number-font-weight-bold);text-decoration:none}.conversation{border-bottom:1px dotted var(--color-border-default);display:flex;margin-top:var(--size-spacing-triple)}.mentions .conversation{padding-left:var(--size-spacing-triple);padding-right:var(--size-spacing-default)}.conversation .thumb{flex:none;margin-left:var(--size-spacing-default);width:50px}@media screen and (min-width:480px){.conversation .thumb{margin-left:var(--size-spacing-triple);width:100px}}.conversation .details-wrapper{flex:1;margin-left:var(--size-spacing-default);margin-right:var(--size-spacing-default);min-width:0}.conversation .sharedfile-title{color:var(--color-page-text-emphasis);font-size:1.875rem;margin-bottom:var(--size-spacing-default)}.conversation .sharedfile-description,.conversation .sharedfile-title{word-wrap:break-word;overflow:hidden;overflow-wrap:break-word;word-break:break-word}.conversation .sharedfile-description{font-size:.875rem;line-height:1.3}.conversation .image-comments{padding-left:0}.conversation .image-comments .body{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.conversation .conversation-meta{display:flex;flex-wrap:wrap;margin-bottom:var(--size-spacing-triple);margin-top:var(--size-spacing-half-again)}.conversation .mute-this-conversation,.conversation .post-a-comment{margin:var(--size-spacing-half) 1em 0 0}.conversation .mute-this-conversation-form{display:none}.error-uh-oh{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 542 257'%3E%3Cg fill='none' transform='translate(-.003 .989)'%3E%3Cpath fill='%239ABD00' d='M68.513.091a164.45 164.45 0 0 0-21.64 2.43l13.3 2.09 8.34-4.52Z'/%3E%3Cpath fill='%23EBCF60' d='M92.823 203.091c-6-19.49-6.64-45.6-6.64-45.6l-11.07-3.42-.08-.78-.87.49-.95-.3.08.78-10.12 5.65s4.84 25.66 3 46c0 0-2.12 24.9 15.84 22.53 18.06-1.43 10.81-25.35 10.81-25.35Z'/%3E%3Cpath fill='%23FFE77C' d='M89.273 203.471c-6-19.49-6.63-45.6-6.63-45.6l-11.07-3.42-.08-.78-.86.49-1-.29.08.78-10.12 5.65s4.84 25.66 3 46c0 0-2.12 24.9 15.84 22.53 18.1-1.45 10.84-25.36 10.84-25.36Z'/%3E%3Cpath fill='%239ABD00' d='M85.053 2.501c1.06.39-6.24 4.31-6.25 6.47-.06 8.57 2.39 25.44 6.12 28.43 9.72 7.8 12.78 6.71 31.17 13.5 1.32.49 10.45-1 11 .63a94 94 0 0 1 5.54 24.22c4.74 45-4.85 82.87-48.14 87.43-42.39 4.46-60.49-30.51-65.25-75.49-4.76-44.98 16.76-84.14 48.13-87.44a37 37 0 0 1 17.68 2.25Z'/%3E%3Cpath fill='%239ABD00' d='M85.663 163.011a165.47 165.47 0 0 1-21.66 2.1l12.57-4.82 9.09 2.72Z'/%3E%3Cpath fill='%23B3D700' d='M114.083 77.701c4.74 45-4.85 82.87-48.14 87.43-42.43 4.47-60.55-30.5-65.29-75.49-4.74-44.99 16.82-84.13 48.14-87.43 31.32-3.3 60.55 30.5 65.29 75.49Z'/%3E%3Cpath fill='%23DEED8D' d='M68.003 5.081s-3.93 10.48 4.67 15.61c0 0-3.13 11.73 6 15.81 0 0-.59 13.48 13.34 12 0 0 3.21 11.41 16.19 4 0 0 3.84-1.75 12-.59l3.91-1.08h3s-15.1-.09-17.39-5.88c0 0-8.54-1.45-7.85-13.94 0 0-9.38-3-8.92-11.47 0 0-15.3-5.1-7.83-17l-7.37.1-9.75 2.44Z'/%3E%3Cpath fill='%23B3D700' d='m91.383 242.251 11.12-4 7.12 4.89-6.67 1.78-5.34-.88-8.45 2.22 2.22-4.01Zm33.81-1.77 4-.45 5.34 3.11-4.89.45-4.45-3.11Zm-17.35 12.01 8.01-3.12 1.33 5.79-4.89.44-4.45-3.11Z'/%3E%3Cpath fill='%23FDEFA2' d='M90.673 53.061s-6.35 20.28 2.62 19.86c0 0 9.49-.46-2.62-19.86Z'/%3E%3Cpath fill='%23FFE77C' d='M46.603 74.291s1.67 10.85-15.27 14.47c0 0-17.1 2.06-15.23 12.36 0 0-4.9-9.19 10.08-13.92 0 0 4.1-1 7.7-1.6 0 .01 11.34-1.97 12.72-11.31Zm17.13-1.8s.63 11 18 11c0 0 17.16-1.54 17.47 8.92 0 0 2.88-10-12.76-11.52 0 0-4.21-.08-7.87 0-.04.02-11.57.44-14.84-8.4Z'/%3E%3Cellipse cx='37.194' cy='108.132' fill='%23FFF' rx='10.67' ry='10.97' transform='rotate(-8.11 37.194 108.132)'/%3E%3Cellipse cx='37.864' cy='106.336' fill='%23000' rx='5.14' ry='5.29' transform='rotate(-8.11 37.864 106.336)'/%3E%3Cellipse cx='64.204' cy='130.129' fill='%23E6231E' rx='6.5' ry='7.65' transform='rotate(-11.19 64.204 130.129)'/%3E%3Cellipse cx='82.063' cy='101.736' fill='%23FFF' rx='10.67' ry='10.97' transform='rotate(-8.11 82.063 101.736)'/%3E%3Cellipse cx='82.732' cy='99.941' fill='%23000' rx='5.14' ry='5.29' transform='rotate(-8.11 82.732 99.941)'/%3E%3Cellipse cx='38.801' cy='103.196' fill='%23FFF' rx='1.32' ry='1' transform='rotate(-8.11 38.801 103.196)'/%3E%3Cellipse cx='83.932' cy='96.754' fill='%23FFF' rx='1.32' ry='1' transform='rotate(-8.11 83.932 96.754)'/%3E%3Cpath fill='%23FF0' d='M76.003 129.931s66.31-14 66.31-40 5.62-64 41.86-64c10.67 0 41-7.41 105.24-9 154-3.81 200.83-4 200.83-4s51 10 51 48 6 116-51 116-91.83 5-144.83 5c-53 0-147.5 6.5-154 0-3.36-3.36-11.86-3.3-23.18-13-10.56-9-24.1-35-24.1-35s-12.08 8.46-32.37 7c-27.87-2-35.76-11-35.76-11Z'/%3E%3Cpath fill='%23FF0180' d='m193.293 118.581-2-46.6 15.83-.67 2 46.6c.41 9.56 6.7 12.71 12.31 12.47 5.61-.24 11.5-3.9 11.1-13.46l-2-46.6 15.83-.67 2 46.6c.72 17-9.91 28.5-26.29 29.19-16.38.69-28.07-9.83-28.78-26.86Zm61.33-49.28 14.07-.6 1.16 27.25a17.33 17.33 0 0 1 8.15-2.32c11.87-.5 20.12 7.18 20.71 21.14l1.12 26.38-14.18.6-1.12-26.38c-.22-5.17-2.36-8.82-7.3-8.61-4.94.21-6.76 4.25-6.55 9.2l1.12 26.38-14.07.6-3.11-73.64Zm50.55 36.83 24-1 .52 12.2-24 1-.52-12.2Zm67.22-42.82a37.84 37.84 0 1 1-36.2 39.41c-.886-20.878 15.321-38.523 36.2-39.41Zm2.53 59.79c12.133-.517 21.552-10.768 21.043-22.902-.509-12.133-10.753-21.559-22.887-21.059-12.133.5-21.567 10.737-21.076 22.871.541 12.137 10.78 21.559 22.92 21.09Zm42.46-60.7 14.07-.6 1.15 27.21a17.32 17.32 0 0 1 8.16-2.33c11.87-.5 20.12 7.18 20.71 21.14l1.12 26.38-14.18.6-1.12-26.38c-.22-5.17-2.36-8.82-7.3-8.61-4.94.21-6.76 4.25-6.55 9.2l1.12 26.38-14.07.6-3.11-73.59Zm53.69 49.39-2.47-58.27 16.18-.69 2.47 58.27-16.18.69Zm8.34 4.09a9.36 9.36 0 0 1 .79 18.7 9.36 9.36 0 0 1-.79-18.7Z'/%3E%3C/g%3E%3C/svg%3E");background-position:top;background-repeat:no-repeat;background-size:282px 133px;font-size:2.25rem;font-weight:var(--number-font-weight-bold);padding-top:160px;text-align:center}.error-p{color:var(--color-page-text-secondary);text-align:center}.error-p.error-p-long{text-align:left}.content-narrow .error-p{font-size:1.125rem;line-height:1.4}@media screen and (min-width:480px){.feature-list{display:flex;flex-wrap:wrap}}@media screen and (min-width:768px){.feature-list{flex-wrap:nowrap}}@media screen and (min-width:960px){.feature-list{flex-wrap:wrap}}.feature{border-top:1px dashed var(--color-border-default);margin:0;padding:var(--size-spacing-half-again);position:relative}@media screen and (min-width:480px){.feature{border-left:1px dashed var(--color-border-default);flex:1 1 50%}}@media screen and (min-width:768px){.feature{flex-basis:25%;padding:calc(var(--size-spacing-default)*2.5)}}@media screen and (min-width:960px){.feature{display:flex;flex-basis:50%;flex-direction:row-reverse;padding:calc(var(--size-spacing-default)*3.5)}}@media screen and (min-width:768px){.feature:first-child{border-left:none}}@media screen and (min-width:480px) and (max-width:767px){.feature:nth-child(2n-1){border-left:none}}@media screen and (min-width:960px){.feature:nth-child(2n-1){border-left:none}}.feature>*+*{margin-top:var(--size-spacing-half-again)}@media screen and (min-width:768px){.feature>*+*{margin-top:calc(var(--size-spacing-default)*2.5)}}@media screen and (min-width:960px){.feature>*+*{margin-right:var(--size-spacing-double);margin-top:0}}@media screen and (min-width:960px){.feature--content{flex:1 1 auto}.feature--image{flex:0 0 163px}}.feature--image-media{border:1px solid var(--color-border-default);box-shadow:3px 3px 0 var(--color-base-black-transparent-100);display:block;margin:0 auto}.feature--title{color:var(--color-page-text-emphasis);font-size:1.375rem;margin:0;text-align:center}@media screen and (min-width:768px){.feature--title{text-align:left}}.feature--body{color:var(--color-page-text);font-size:.875rem;line-height:1.25;margin:var(--size-spacing-default) 0 0}.feature--body p{margin:0}.feature--body p+p{margin-top:1.25em}.feature--cta{text-align:center}.feature--finale .feature--image-media,.feature--primary .feature--image-media{border:none;box-shadow:none}.feature--primary{border:none}@media screen and (min-width:960px){.feature--primary>*{flex:1}}@media screen and (min-width:960px){.feature--primary>*+*{margin-right:70px}}.feature--primary .feature--title{font-size:2.625rem;letter-spacing:-3px;line-height:90%}@media screen and (min-width:768px){.feature--primary .feature--title{font-size:3.75rem}}@media screen and (min-width:960px){.feature--primary .feature--title{font-size:4.5rem}}.feature--primary .feature--body{color:var(--color-page-text-secondary);margin-top:1.25em}.feature--finale{align-items:center}@media screen and (min-width:960px){.feature--finale{padding-left:150px;padding-right:150px}}.feature--finale .feature--image{flex:none}@media screen and (min-width:960px){.feature--flipped{flex-direction:row}}@media screen and (min-width:960px){.feature--flipped>*+*{margin-left:var(--size-spacing-double);margin-right:0}}@media screen and (min-width:960px){.feature--flipped.feature--primary>*+*{margin-left:70px}}button,input,select,textarea{font-family:var(--font-family-system);font-size:var(--size-font-input)}select{width:100%}label,textarea{display:block}textarea{height:8em;padding:.5em;resize:vertical;width:100%}.fun-form{margin-top:var(--size-spacing-double)}.fun-form .field{display:flex;flex-direction:column;position:relative}@media screen and (min-width:768px){.fun-form .field{align-items:center;flex-flow:row wrap}}.fun-form .field+.field{margin-top:var(--size-spacing-double)}.fun-form label{flex:none;font-size:1.125rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-half)}@media screen and (min-width:768px){.fun-form label{flex-basis:190px;margin-bottom:0;margin-right:var(--size-spacing-default);text-align:right}}.fun-form .field-input{flex:1}.fun-form .field-help,.fun-form .field-submit{display:block}@media screen and (min-width:768px){.fun-form .field-help,.fun-form .field-submit{padding-left:200px}}.fun-form .field-help{color:var(--color-page-text-secondary);font-size:.875rem;margin-top:var(--size-spacing-half)}@media screen and (min-width:768px){.fun-form .field-help{flex-basis:100%}}.fun-form .input-text,.fun-form textarea{appearance:none;background:var(--color-form-bg);border-radius:var(--size-border-radius-large);border-width:1px;border-bottom:1px solid var(--color-border-form);border-left:4px solid var(--color-border-form);border-right:1px solid var(--color-border-form);border-top:4px solid var(--color-border-form);color:var(--color-page-text);display:block;font-size:1.125rem;padding:var(--size-spacing-default) var(--size-spacing-half-again);width:100%}.fun-form [type=checkbox],.fun-form [type=radio]{margin-right:.5em}.fun-form .name-prefix{color:var(--color-page-text-secondary);margin-right:var(--size-spacing-half)}.fun-form .error{background:var(--color-form-error-bubble-bg);border-radius:33% 25px;color:var(--color-form-error-bubble-text);display:block;font-size:.75rem;padding:1em 1.25em;position:absolute;right:calc(var(--size-spacing-half-again)*-1);text-align:center;top:25%;transform:translateY(-50%);width:115px;z-index:1}@media screen and (min-width:960px){.fun-form .error{right:-160px}}.fun-form .error:before{background:var(--color-form-error-bubble-bg);bottom:calc(50% - 15px);content:"";display:block;height:22px;left:-48px;-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 52.33 22.01'%3E%3Cg data-name='Layer 2'%3E%3Cpath fill='%23aaf3ff' d='M52.33 0a73.44 73.44 0 0 1-22 13.35C17.66 18 0 19.35 0 19.35S6.33 22 24.5 22s27.83-6 27.83-6Z' data-name='Layer 1'/%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 52.33 22.01'%3E%3Cg data-name='Layer 2'%3E%3Cpath fill='%23aaf3ff' d='M52.33 0a73.44 73.44 0 0 1-22 13.35C17.66 18 0 19.35 0 19.35S6.33 22 24.5 22s27.83-6 27.83-6Z' data-name='Layer 1'/%3E%3C/g%3E%3C/svg%3E");position:absolute;width:52px}.fun-form-stacked .field{display:block}.fun-form-stacked label{margin-bottom:var(--size-spacing-half);margin-right:0;text-align:left}.fun-form-stacked .field-help,.fun-form-stacked .field-submit{padding-left:0}.fun-form-stacked #create-shake-name-field,.fun-form-stacked .field-prefix{display:flex}.fun-form-stacked #create-shake-name-field label,.fun-form-stacked .field-prefix label{flex-basis:100%}.fun-form-stacked .error{top:3em}.fun-form-errors{color:var(--color-status-danger);font-size:1.125rem}.image-comments{max-width:100%;overflow:hidden;padding-left:var(--size-spacing-quadruple)}.image-comments .comments{clear:both}.image-comments .comment{display:flex}.image-comments .comment+.comment{margin-top:var(--size-spacing-double)}.image-comments .comment .avatar{margin-right:var(--size-spacing-default);width:var(--size-avatar-default)}.image-comments .comment .avatar a{display:block}.image-comments .comment .body{flex:1;font-size:.875rem;line-height:1.3;overflow:hidden}.image-comments .comment .body .where-from{color:var(--color-page-text-secondary);font-size:.825em;padding-top:var(--size-spacing-default)}.image-comments .comment .comment-body-text{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-comments .comment .meta{align-items:flex-end;display:flex;flex-wrap:wrap;font-size:.825em;padding-bottom:var(--size-spacing-default)}.image-comments .comment .meta>*+*{margin-left:.825em}.image-comments .comment .meta .user-name,.image-comments .comment .meta .username{font-size:.875rem;font-weight:var(--number-font-weight-bold)}.image-comments .comment .meta .pro-badge{margin-left:.25em}.image-comments .comment .meta .reply-to{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-279 425.6 11.8 11.4'%3E%3Cpath fill='none' d='M-259 357.4h32.1'/%3E%3Cpath fill='%2300aeff' d='M-273.2 426.2v5.6c0 .4-.3.6-.6.7-.1 0-.3 0-.4-.1l-4.5-2.8c-.3-.2-.4-.6-.2-.9l.2-.2 4.5-2.8c.3-.2.7 0 .9.3 0 .1.1.1.1.2z'/%3E%3Cpath fill='%2300aeff' d='M-272.2 430.6c1.8.6 2.8 2.5 2.3 4.3v.1c-.1.2 0 .4.1.6l1.1 1.2c.2.2.5.2.7.1 0 0 .1-.1.1-.2 1.6-2.9.5-6.6-2.4-8.1-.9-.5-1.9-.7-2.9-.7l-.9 2.7c.6-.2 1.2-.2 1.9 0z'/%3E%3C/svg%3E");background-size:12px 11px}.image-comments .comment .meta .delete,.image-comments .comment .meta .reply-to{background-position:0 0;background-repeat:no-repeat;display:none;padding-left:var(--size-spacing-half-again)}.image-comments .comment .meta .delete{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 13 13'%3E%3Crect width='13' height='13' fill='%23cecec6' rx='2.58' ry='2.58'/%3E%3Cpath fill='%238d8d87' d='m3.78 8.91 1.87-2.67-1.79-2.52h1.67l.92 1.43 1-1.43H9L7.27 6.18l1.92 2.73H7.5L6.45 7.3 5.38 8.91z'/%3E%3C/svg%3E");background-size:13px 13px}.image-comments .comment .meta .created-at{color:var(--color-page-text-secondary)}.image-comments .comment:focus .meta .reply-to,.image-comments .comment:hover .meta .reply-to{display:inline}.image-comments .comment:focus .meta form,.image-comments .comment:hover .meta form{display:none}.image-comments .comment:focus .meta .delete,.image-comments .comment:hover .meta .delete{display:inline}.image-comment-form{margin-top:var(--size-spacing-triple);padding-left:var(--size-spacing-quadruple)}.image-comment-form header{display:flex}.image-comment-form .avatar{flex:none}.image-comment-form h3{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 45'%3E%3Cg data-name='Layer 2'%3E%3Cpath fill='%23ffb400' d='M0 26.33S2.25 27 7.54 25a16.47 16.47 0 0 0 7.54-5.33s-3.5-18.33 11-18.33S300 0 300 0v45H90.58s-58.5-1.92-64.25-4.17-7.22-6.45-7.22-6.45-7.53-.55-11.78-2S0 26.33 0 26.33z' data-name='Layer 1'/%3E%3C/g%3E%3C/svg%3E");background-position:0 0;background-repeat:no-repeat;background-size:auto var(--size-avatar-default);border-bottom-right-radius:15px;border-top-right-radius:25px 20px;display:block;left:calc(var(--size-spacing-default)*-1);position:relative}.image-comment-form h3 span{color:var(--color-text-light-emphasis);display:block;font-size:.75rem;height:var(--size-avatar-default);line-height:var(--size-avatar-default);padding-left:35px;padding-right:var(--size-spacing-double);text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);white-space:nowrap}@media screen and (min-width:375px){.image-comment-form h3 span{font-size:.875rem}}@media screen and (min-width:480px){.image-comment-form h3 span{font-size:1rem}}@media screen and (min-width:768px){.image-comment-form h3 span{font-size:1.125rem}}.image-comment-form .field{clear:both;margin:var(--size-spacing-double) 0;text-align:right}.image-comment-form textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);height:8em;padding:var(--size-spacing-half)}.image-medium{margin-right:var(--size-spacing-half-again);padding-bottom:var(--size-spacing-triple)}.image-medium .user-and-title{padding-top:var(--size-spacing-default)}.image-medium .user-and-title a{display:block;float:left;margin-right:var(--size-spacing-default)}.image-medium .user-and-title .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-medium .user-and-title .title{font-size:1.25rem;font-weight:var(--number-font-weight-bold);overflow:hidden}.image-medium .stats{clear:both;color:var(--color-page-text-secondary);display:flex;list-style:none;margin:0;padding:var(--size-spacing-default) 0 0}.image-medium .stats li{float:left;font-size:.75em;padding-right:var(--size-spacing-default)}.image-medium .stats li.saves{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 11.2 8'%3E%3Cg data-name='Layer 2'%3E%3Cpath fill='%23f79d1f' d='m4.69 8-.23-2.73c0-.36-.36-.88-1.3-.75A23.37 23.37 0 0 1 3.68 8h-.53a15.4 15.4 0 0 0-.78-3.68c-.26-.55-.22-.94.57-.73A7.84 7.84 0 0 0 4.15 4c.33 0 .67-.44 1.47-.43S6.75 4 7.08 4a7.63 7.63 0 0 0 1.17-.37c.78-.19.84.23.58.75A15.14 15.14 0 0 0 8.08 8Zm6.47-8-2 .14a1 1 0 0 0-.82.54l-.58 1.16a1.11 1.11 0 0 0-1.07-1h-.06A1.1 1.1 0 0 0 5.6 0a1.1 1.1 0 0 0-1 .84h-.2a1.1 1.1 0 0 0-1 .89L2.86.7A1 1 0 0 0 2 .16L0 0v.56l1.92.1A.52.52 0 0 1 2.4 1l.45 1.05a1 1 0 0 0-.43.77 1 1 0 0 1 .53 0 8.13 8.13 0 0 0 1.2.36c.33 0 .67-.42 1.47-.41s1.13.45 1.46.43a7.92 7.92 0 0 0 1.17-.3.92.92 0 0 1 .53 0 1.12 1.12 0 0 0-.51-.84s.45-.89.52-1a.58.58 0 0 1 .53-.41L11.2.53Z' data-name='Layer 1'/%3E%3C/g%3E%3C/svg%3E");background-position:0 2px;background-repeat:no-repeat;background-size:16px 11px;color:var(--color-status-warning);padding-left:var(--size-spacing-double)}.image-medium .stats li.likes{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 9 7.75'%3E%3Cpath fill='%23ff0200' d='M0 2.37a2.37 2.37 0 0 1 4.5-1 2.37 2.37 0 0 1 4.5 1c0 3.11-4.5 5.38-4.5 5.38S0 5.48 0 2.37'/%3E%3Cpath fill='%23fff' d='M5.81 4.87a.21.21 0 0 0-.28.07 1.19 1.19 0 0 1-2.06 0 .21.21 0 1 0-.36.21 1.6 1.6 0 0 0 2.77 0 .21.21 0 0 0-.07-.28zM2.22 2.65a.84.84 0 0 0 1.68 0 .84.84 0 1 0-1.68 0zm2.88 0a.84.84 0 0 0 1.68 0 .84.84 0 1 0-1.68 0z'/%3E%3C/svg%3E");color:var(--color-status-danger)}.image-medium .stats li.comments a,.image-medium .stats li.likes{background-position:0 2px;background-repeat:no-repeat;background-size:12px 9px;padding-left:var(--size-spacing-half-again)}.image-medium .stats li.comments a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 11.41 9'%3E%3Cpath fill='%2309f' d='M0 3.93a1.62 1.62 0 0 1 1.61-1.6A1.62 1.62 0 0 1 4 1.09a1.62 1.62 0 0 1 3.13.24 1.62 1.62 0 0 1 2.64 1.13 1.62 1.62 0 0 1 0 3.25 1.62 1.62 0 0 1-2.6 1.23 1.62 1.62 0 0 1-2.24.56C3.66 9 .28 9 .28 9c2.91-1.64 2.65-2.42 2.44-2.43-.88 0-1.16-.12-1.16-1A1.62 1.62 0 0 1 0 3.93z'/%3E%3C/svg%3E");color:var(--color-brand-secondary);text-decoration:none}.image-medium-thumb a{display:block}.image-medium-thumb img{text-align:center;vertical-align:middle}.image-title{max-width:100%;padding:var(--size-spacing-half-again) var(--size-spacing-half-again) 0;position:relative}@media screen and (min-width:768px){.image-title{padding:var(--size-spacing-quadruple) var(--size-spacing-quadruple) 0}}.image-title .image-poster{float:left;padding-right:var(--size-spacing-default)}.image-title .image-poster a{display:block}.image-title h1,.image-title h3{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-title h1{font-size:3rem}.image-title h3{font-size:1.875rem;margin-bottom:var(--size-spacing-double)}.image-title .remove-from-shake{height:21px;position:absolute;right:var(--size-spacing-default);top:var(--size-spacing-default);width:21px}@media screen and (min-width:768px){.image-title .remove-from-shake{top:calc(var(--size-spacing-quadruple) + var(--size-spacing-half))}}.image-edit-title-hover,.image-edit-title:focus,.image-edit-title:hover{background-color:var(--color-status-edit)}.image-edit-title-form{display:none;flex-direction:column}@media screen and (min-width:480px){.image-edit-title-form{flex-direction:row}}.image-edit-title-form.is-active{display:flex}.image-edit-title-form .title-input{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);flex:1;font-size:3rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-default);max-width:100%;min-width:0}@media screen and (min-width:480px){.image-edit-title-form .title-input{margin-bottom:0}}.image-content-list .image-edit-title-form .title-input{font-size:1.875rem;min-width:250px;padding:.0666em}.image-edit-title-form .buttons{align-items:center;display:flex;padding-left:var(--size-spacing-default)}.image-edit-title-form .or{color:var(--color-page-text-secondary);display:block;padding:0 var(--size-spacing-default)}.image-content-list .image-edit-title-form{margin-bottom:var(--size-spacing-double)}.image-content-list .image-edit-title-form .btn{font-size:12px;font-weight:400}.image-content{clear:both;max-width:100%;padding:var(--size-spacing-half-again);position:relative}@media screen and (min-width:768px){.image-content{padding:var(--size-spacing-half-again) var(--size-spacing-quadruple) var(--size-spacing-quadruple)}}.image-content .the-image a,.image-content .the-image img{display:block}.image-content img.unsized{height:auto;max-width:100%}.image-content-list .image-content{border-bottom:1px dashed var(--color-border-default);float:none;padding:0 var(--size-spacing-half-again) var(--size-spacing-half-again)}@media screen and (min-width:768px){.image-content-list .image-content{padding:0 var(--size-spacing-quadruple) var(--size-spacing-quadruple)}}.nsfw-cover{background-color:var(--color-base-gray-800);color:var(--color-text-light-emphasis);font-size:.875rem;font-weight:600;padding:var(--size-spacing-quadruple) 0;text-align:center}.nsfw-cover p{margin:var(--size-spacing-double) 0}.image-content .description{color:var(--color-page-text);font-size:.875rem;overflow:hidden;padding-bottom:var(--size-spacing-default)}.image-content .the-description{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-content .the-description.the-description-blank{color:var(--color-status-disabled);font-style:italic}.image-content .the-description a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.image-content .the-description a:active,.image-content .the-description a:focus,.image-content .the-description a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.image-content .description-edit .the-description:focus,.image-content .description-edit .the-description:hover,.image-content .the-description-hover{background-color:var(--color-status-edit)}.image-content .description-edit-form{display:none}.image-content .description-edit-form h3{margin-bottom:5px}.image-content .description-edit-form textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);min-height:100px;padding:var(--size-spacing-half)}.image-content .description-edit-form .buttons{display:flex;padding:var(--size-spacing-default) 0}.image-content .description-edit-form .or{color:var(--color-page-text-secondary);display:block;padding:var(--size-spacing-half) var(--size-spacing-default) var(--size-spacing-half-again) var(--size-spacing-default)}.image-content .alt-text{color:var(--color-page-text);font-size:.875rem;overflow:hidden;padding-bottom:var(--size-spacing-default)}.image-content .alt-text .the-alt-text{word-wrap:break-word;background:var(--color-background-content-secondary);color:var(--color-page-text-secondary);margin-top:var(--size-spacing-half);overflow-wrap:break-word;padding:var(--size-spacing-default) var(--size-spacing-half-again);word-break:break-word}.image-content .alt-text .the-alt-text:focus{box-shadow:none}.image-content .alt-text.alt-text--blank .the-alt-text{color:var(--color-status-disabled);font-style:italic}.image-content .alt-text.alt-text--blank .alt-text-toggle,.image-content .alt-text.alt-text--editing .alt-text-toggle,.image-content .alt-text.alt-text--editing .the-alt-text,.image-content .alt-text.alt-text--hidden .the-alt-text{display:none}.image-content .alt-text-edit .the-alt-text:hover,.image-content .the-alt-text-hover{background-color:var(--color-status-edit)}.image-content .alt-text--editing .alt-text-edit-form{display:block}.image-content .alt-text-edit-form{display:none}.image-content .alt-text-edit-form .alt-text-learn{margin-top:5px}.image-content .alt-text-edit-form textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);min-height:100px;padding:var(--size-spacing-half)}.image-content .alt-text-edit-form .buttons{display:flex;padding:var(--size-spacing-default) 0}.image-content .alt-text-edit-form .or{color:var(--color-page-text-secondary);display:block;padding:var(--size-spacing-half) var(--size-spacing-default) var(--size-spacing-half-again) var(--size-spacing-default)}.image-content .alt-text-toggle{cursor:pointer}.image-interactions{align-items:center;display:flex;flex-direction:row-reverse;float:right;margin-bottom:4px}.image-interactions .save-this{margin-right:var(--size-spacing-half);position:relative}.image-interactions .like-button,.image-interactions .save-this-link{line-height:1}.image-interactions .like-button:focus,.image-interactions .save-this-link:focus{outline:none}.image-interactions .like-button .btn--content,.image-interactions .save-this-link .btn--content{align-items:center;display:flex}.image-interactions .like-button .btn--icon,.image-interactions .save-this-link .btn--icon{height:1.5em;margin-right:.33em}.image-interactions .like-button .btn--caret,.image-interactions .save-this-link .btn--caret{font-size:.75em;margin-left:.33em;vertical-align:bottom}.image-interactions .like-button,.image-interactions .unlike-button{display:none}.image-interactions .like-button.is-active,.image-interactions .unlike-button.is-active{display:inline-block}.image-interactions .unlike-button{background:none;border:none;cursor:pointer;padding:0}.image-interactions .unlike-button img{display:block}.image-interactions .save-this-shake-selector{background-color:var(--color-status-warning);border-radius:var(--size-border-radius-large);min-height:30px;padding:var(--size-spacing-half) 0;position:absolute;right:0;top:0;width:170px;z-index:1}.image-interactions .save-this-shake-selector ul{clear:both;list-style:none;margin:0;padding:0}.image-interactions .save-this-shake-selector a{word-wrap:break-word;border-radius:var(--size-border-radius-default);color:var(--color-text-light-emphasis);display:block;font-weight:500;margin:0 var(--size-spacing-half);overflow-wrap:break-word;padding:var(--size-spacing-half) var(--size-spacing-half);text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500)}.image-interactions .save-this-shake-selector a:focus,.image-interactions .save-this-shake-selector a:hover{background-color:var(--color-base-white-transparent-200)}.image-interactions .save-this-shake-selector .close{color:var(--color-text-light-emphasis);cursor:pointer;display:block;position:absolute;right:var(--size-spacing-default);top:var(--size-spacing-default);transform:rotate(180deg)}.image-interactions .save-this-shake-selector .close:focus,.image-interactions .save-this-shake-selector .close:hover{opacity:.66}.image-interactions .save-this-shake-selector .close:before{bottom:calc(var(--size-spacing-default)*-1);content:"";left:calc(var(--size-spacing-half-again)*-1);position:absolute;right:calc(var(--size-spacing-half-again)*-1);top:calc(var(--size-spacing-double)*-1)}.image-interactions .save-this-shake-selector-loading{min-height:100px}.image-interactions .save-this-shake-selector-loading:after{animation:load8 1.1s linear infinite;border:5px solid var(--color-base-white-transparent-200);border-left-color:var(--color-base-white-transparent-800);border-radius:50%;content:"";display:block;height:50px;margin:1em auto;transform:translateZ(0);width:50px}@media screen and (prefers-reduced-motion:reduce){.image-interactions .save-this-shake-selector-loading:after{animation:none}}@keyframes load8{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.image-content-footer{color:var(--color-page-text-secondary);font-size:.75rem;padding-top:var(--size-spacing-default)}.image-content-footer .originally-posted-by{clear:left;padding-bottom:var(--size-spacing-half)}.image-content-footer .originally-posted-by .avatar--link{display:inline-block;text-decoration:none;vertical-align:middle}.image-content-footer .originally-posted-by .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-content-footer .inline-meta{display:flex;float:left}.image-content-footer .inline-meta>*{flex:none}.image-content-footer .created-at{padding-top:var(--size-spacing-half)}.image-content-footer .created-at a{color:var(--color-page-text-secondary);text-decoration:none}.image-content-footer .created-at a:focus,.image-content-footer .created-at a:hover{color:var(--color-page-text);text-decoration:underline}.image-content-footer .stats{list-style:none;margin:0 0 0 var(--size-spacing-default);padding:0}.image-content-footer .stats li{float:left;padding-right:var(--size-spacing-half)}.image-content-footer .stats a{background-position:var(--size-spacing-half) .5em;background-repeat:no-repeat;background-size:1em 1em;border-top-left-radius:var(--size-border-radius-default);border-top-right-radius:var(--size-border-radius-default);display:block;float:left;padding:var(--size-spacing-half);padding-bottom:var(--size-spacing-default);padding-left:calc(var(--size-spacing-default) + 1em);text-decoration:none}.image-content-footer .stats a:focus,.image-content-footer .stats a:hover{background-color:var(--color-background-content-secondary)}.image-content-footer .stats .views{padding-top:var(--size-spacing-half)}.image-content-footer .stats .saves a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 11.2 8'%3E%3Cg data-name='Layer 2'%3E%3Cpath fill='%23f79d1f' d='m4.69 8-.23-2.73c0-.36-.36-.88-1.3-.75A23.37 23.37 0 0 1 3.68 8h-.53a15.4 15.4 0 0 0-.78-3.68c-.26-.55-.22-.94.57-.73A7.84 7.84 0 0 0 4.15 4c.33 0 .67-.44 1.47-.43S6.75 4 7.08 4a7.63 7.63 0 0 0 1.17-.37c.78-.19.84.23.58.75A15.14 15.14 0 0 0 8.08 8Zm6.47-8-2 .14a1 1 0 0 0-.82.54l-.58 1.16a1.11 1.11 0 0 0-1.07-1h-.06A1.1 1.1 0 0 0 5.6 0a1.1 1.1 0 0 0-1 .84h-.2a1.1 1.1 0 0 0-1 .89L2.86.7A1 1 0 0 0 2 .16L0 0v.56l1.92.1A.52.52 0 0 1 2.4 1l.45 1.05a1 1 0 0 0-.43.77 1 1 0 0 1 .53 0 8.13 8.13 0 0 0 1.2.36c.33 0 .67-.42 1.47-.41s1.13.45 1.46.43a7.92 7.92 0 0 0 1.17-.3.92.92 0 0 1 .53 0 1.12 1.12 0 0 0-.51-.84s.45-.89.52-1a.58.58 0 0 1 .53-.41L11.2.53Z' data-name='Layer 1'/%3E%3C/g%3E%3C/svg%3E");background-size:1.33em 1em;color:var(--color-status-warning);padding-left:calc(var(--size-spacing-default) + 1.33em)}.image-content-footer .stats .likes a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 9 7.75'%3E%3Cpath fill='%23ff0200' d='M0 2.37a2.37 2.37 0 0 1 4.5-1 2.37 2.37 0 0 1 4.5 1c0 3.11-4.5 5.38-4.5 5.38S0 5.48 0 2.37'/%3E%3Cpath fill='%23fff' d='M5.81 4.87a.21.21 0 0 0-.28.07 1.19 1.19 0 0 1-2.06 0 .21.21 0 1 0-.36.21 1.6 1.6 0 0 0 2.77 0 .21.21 0 0 0-.07-.28zM2.22 2.65a.84.84 0 0 0 1.68 0 .84.84 0 1 0-1.68 0zm2.88 0a.84.84 0 0 0 1.68 0 .84.84 0 1 0-1.68 0z'/%3E%3C/svg%3E");color:var(--color-status-danger)}.image-content-footer .stats .comments a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 11.41 9'%3E%3Cpath fill='%2309f' d='M0 3.93a1.62 1.62 0 0 1 1.61-1.6A1.62 1.62 0 0 1 4 1.09a1.62 1.62 0 0 1 3.13.24 1.62 1.62 0 0 1 2.64 1.13 1.62 1.62 0 0 1 0 3.25 1.62 1.62 0 0 1-2.6 1.23 1.62 1.62 0 0 1-2.24.56C3.66 9 .28 9 .28 9c2.91-1.64 2.65-2.42 2.44-2.43-.88 0-1.16-.12-1.16-1A1.62 1.62 0 0 1 0 3.93z'/%3E%3C/svg%3E")}.image-content .inline-details,.image-content-footer .stats .selected a{background-color:var(--color-background-content-secondary)}.image-content .inline-details{border-radius:var(--size-border-radius-default);clear:both;min-height:34px;padding:var(--size-spacing-default)}.image-content .inline-details .user-saves-likes{background-color:var(--color-background-content);min-height:var(--size-avatar-tiny);padding:var(--size-spacing-default)}.image-content .inline-details .user-saves-likes a{align-items:center;color:var(--color-text-link-primary);display:inline-flex;font-weight:var(--number-font-weight-bold);margin-right:var(--size-spacing-default);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.image-content .inline-details .user-saves-likes a:active,.image-content .inline-details .user-saves-likes a:focus,.image-content .inline-details .user-saves-likes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.image-content .inline-details .user-saves-likes a:active,.image-content .inline-details .user-saves-likes a:focus,.image-content .inline-details .user-saves-likes a:hover{text-decoration:underline}.image-content .inline-details .user-saves-likes img{margin-right:var(--size-spacing-half)}.image-content .inline-details .user-saves-likes .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-content .inline-details .comment{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);display:flex;margin-bottom:var(--size-spacing-default);padding:var(--size-spacing-default)}.image-content .inline-details .comment .avatar{flex:none}.image-content .inline-details .comment .avatar a{display:block}.image-content .inline-details .comment .avatar--img{height:var(--size-avatar-small);width:var(--size-avatar-small)}.image-content .inline-details .comment .comment-body{flex:1;overflow:hidden;padding-left:var(--size-spacing-default)}.image-content .inline-details .comment .comment-body-text{word-wrap:break-word;clear:both;color:var(--color-page-text);font-size:.875rem;overflow-wrap:break-word;padding-top:.25em;word-break:break-word}.image-content .inline-details .comment .meta{align-items:flex-end;display:flex;flex-wrap:wrap}.image-content .inline-details .comment .meta>*+*{margin-left:.8em}.image-content .inline-details .comment .delete-form{display:none}.image-content .inline-details .comment .meta .username{font-weight:var(--number-font-weight-bold)}.image-content .inline-details .comment .meta .pro-badge{margin-left:.25em}.image-content .inline-details .comment .created-at{margin-left:var(--size-spacing-default);padding-top:0}.image-content .inline-details .comment .reply-to{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-279 425.6 11.8 11.4'%3E%3Cpath fill='none' d='M-259 357.4h32.1'/%3E%3Cpath fill='%2300aeff' d='M-273.2 426.2v5.6c0 .4-.3.6-.6.7-.1 0-.3 0-.4-.1l-4.5-2.8c-.3-.2-.4-.6-.2-.9l.2-.2 4.5-2.8c.3-.2.7 0 .9.3 0 .1.1.1.1.2z'/%3E%3Cpath fill='%2300aeff' d='M-272.2 430.6c1.8.6 2.8 2.5 2.3 4.3v.1c-.1.2 0 .4.1.6l1.1 1.2c.2.2.5.2.7.1 0 0 .1-.1.1-.2 1.6-2.9.5-6.6-2.4-8.1-.9-.5-1.9-.7-2.9-.7l-.9 2.7c.6-.2 1.2-.2 1.9 0z'/%3E%3C/svg%3E");background-size:12px 11px}.image-content .inline-details .comment .delete,.image-content .inline-details .comment .reply-to{background-position:0 0;background-repeat:no-repeat;display:none;padding-left:var(--size-spacing-half-again)}.image-content .inline-details .comment .delete{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 13 13'%3E%3Crect width='13' height='13' fill='%23cecec6' rx='2.58' ry='2.58'/%3E%3Cpath fill='%238d8d87' d='m3.78 8.91 1.87-2.67-1.79-2.52h1.67l.92 1.43 1-1.43H9L7.27 6.18l1.92 2.73H7.5L6.45 7.3 5.38 8.91z'/%3E%3C/svg%3E");background-size:13px 13px;color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.image-content .inline-details .comment .delete:active,.image-content .inline-details .comment .delete:focus,.image-content .inline-details .comment .delete:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}.image-content .inline-details .comment:focus .delete,.image-content .inline-details .comment:focus .reply-to,.image-content .inline-details .comment:hover .delete,.image-content .inline-details .comment:hover .reply-to{display:block}.image-content .inline-details .show-more-comments{display:block;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default) 0;text-align:right;text-decoration:none}.image-content .inline-details .post-comment-inline textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text-disabled);height:2em;padding:var(--size-spacing-half)}.image-content .inline-details .post-comment-inline .button{display:none;padding-top:var(--size-spacing-half)}.image-content .inline-details .post-comment-inline.post-comment-inline-expanded textarea{color:var(--color-page-text);min-height:100px}.image-content .inline-details .post-comment-inline.post-comment-inline-expanded .button{display:block}.new-post-panel{background-color:var(--color-background-content);border-bottom-left-radius:10px;border-bottom-right-radius:10px;box-shadow:var(--size-spacing-default) var(--size-spacing-default) 0 var(--color-base-black-transparent-200);display:none;left:var(--size-spacing-triple);margin:0 auto;max-width:560px;padding:var(--size-spacing-half-again);position:fixed;right:var(--size-spacing-triple);top:calc(var(--size-spacing-default)*-1);z-index:999}@media screen and (min-width:768px){.new-post-panel{padding:calc(var(--size-spacing-default)*5)}}.new-post-panel h2{font-size:1.75rem;line-height:1.2;margin-bottom:var(--size-spacing-default);position:relative}.new-post-panel h2 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.new-post-panel h2 a:active,.new-post-panel h2 a:focus,.new-post-panel h2 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.new-post-panel h2 a:active,.new-post-panel h2 a:focus,.new-post-panel h2 a:hover{text-decoration:underline}.new-post-panel p{font-size:.875rem;line-height:1.2;margin:var(--size-spacing-double) 0;padding:0 var(--size-spacing-half)}.new-post-panel .upload-image-input{cursor:pointer;height:100%;left:0;opacity:0;position:absolute;top:0;width:100%}.new-post-panel .save-video-form{display:flex}.new-post-panel .save-video-form .field-input{flex:1}.new-post-panel .save-video-form .btn{flex:none;margin-left:var(--size-spacing-default)}.new-post-panel .post-video-form{margin-top:var(--size-spacing-double)}.new-post-panel .shake-selector{position:relative}.new-post-panel .shake-selector h3 a{color:var(--color-page-text);display:block;text-decoration:none}.new-post-panel .shake-selector h3 a .green{word-wrap:break-word;color:var(--color-status-success);overflow-wrap:break-word;word-break:break-word}.new-post-panel .shake-selector h3 a:focus .green,.new-post-panel .shake-selector h3 a:hover .green{color:var(--color-status-success-pastel-dark)}.new-post-panel .shake-selector ul{background-color:var(--color-status-success-pastel-light);border-radius:var(--size-border-radius-large);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-200);display:none;left:var(--size-spacing-triple);list-style:none;margin:0;max-height:66vh;overflow:auto;padding:var(--size-spacing-default) 0 var(--size-spacing-half) 0;position:absolute;top:1.638rem;width:200px;z-index:2}.new-post-panel .shake-selector ul li a{word-wrap:break-word;color:var(--color-status-success-pastel-dark);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;padding:var(--size-spacing-half) var(--size-spacing-half-again);text-decoration:none;word-break:break-word}.new-post-panel .shake-selector ul li a:focus,.new-post-panel .shake-selector ul li a:hover{color:var(--color-text-dark)}.new-post-panel--inner{display:flex;flex-direction:column}@media screen and (min-width:768px){.new-post-panel--inner{flex-direction:row}}.new-post-panel--inner>*{flex:1 1 0;min-width:0;padding-bottom:var(--size-spacing-half-again);padding-top:var(--size-spacing-half-again)}@media screen and (min-width:768px){.new-post-panel--inner>*{padding-bottom:var(--size-spacing-quadruple);padding-top:calc(var(--size-spacing-default)*6)}}.new-post-panel--inner>:first-child{border-bottom:1px dashed var(--color-border-default)}@media screen and (min-width:768px){.new-post-panel--inner>:first-child{border-bottom:0;border-right:1px dashed var(--color-border-default);padding-right:var(--size-spacing-double)}}@media screen and (min-width:768px){.new-post-panel--inner>:last-child{padding-left:var(--size-spacing-double)}}.new-post-panel--inner>:first-child:last-child{border:0;padding:0}.dashboard-new-user{padding:var(--size-spacing-double) var(--size-spacing-triple)}.dashboard-new-user h1{color:var(--color-status-success);font-size:3.125rem;margin-bottom:var(--size-spacing-double)}.dashboard-new-user h2{font-size:1.875rem}.dashboard-new-user h3{font-size:1.375rem;margin-bottom:var(--size-spacing-half)}.dashboard-new-user h4{font-size:.875rem;margin-bottom:var(--size-spacing-half)}.dashboard-new-user p{font-size:.875rem;line-height:1.75;padding-bottom:var(--size-spacing-half-again)}.dashboard-new-user a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.dashboard-new-user a:active,.dashboard-new-user a:focus,.dashboard-new-user a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.dashboard-new-user .two-columns{display:flex}.dashboard-new-user .two-columns .left-column{flex:0 0 70px;margin-right:var(--size-spacing-double);margin-top:var(--size-spacing-default)}@media screen and (min-width:768px){.dashboard-new-user .two-columns .left-column{flex-basis:130px;margin-left:var(--size-spacing-double);margin-right:var(--size-spacing-quadruple)}}.dashboard-new-user .two-columns .right-column{flex:1;margin-top:var(--size-spacing-quadruple)}.new-members{border-top:1px dashed var(--color-border-default);list-style:none;margin:var(--size-spacing-triple) 0 0;padding:var(--size-spacing-triple) 0 0}.new-members>li{border-bottom:1px dashed var(--color-border-default);clear:both;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.new-members>li>a{display:block;float:left;margin-right:var(--size-spacing-default)}.new-members>li h4{word-wrap:break-word;font-size:1.125rem;overflow-wrap:break-word;padding-bottom:var(--size-spacing-half);word-break:break-word}.new-members>li h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.new-members>li h4 a:active,.new-members>li h4 a:focus,.new-members>li h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.new-members>li h4 a:active,.new-members>li h4 a:focus,.new-members>li h4 a:hover{text-decoration:underline}.new-members>li p{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;word-break:break-word}.new-members .image-medium{margin-left:calc(var(--size-spacing-default)*6);padding-bottom:0;padding-top:var(--size-spacing-default)}.new-members .image-medium .user-and-title>a{display:none}.notification-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double)}.notification-block-link a{display:block;text-decoration:none}.notification-block-hd,.notification-block-link a{font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-half-again)}.notification-block-hd{cursor:pointer}.notification-block-bd{display:none;padding:0 var(--size-spacing-default) var(--size-spacing-default)}.notification-block-bd .notification{word-wrap:break-word;background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);color:var(--color-page-text);font-size:.875rem;overflow-wrap:break-word;padding:var(--size-spacing-default);position:relative;word-break:break-word}.notification-block-bd .notification:after{clear:both;content:"";display:table}.notification-block-bd .notification+.notification{margin-top:var(--size-spacing-default)}.notification-block-bd .notification .notification-close{outline:none;position:absolute;right:var(--size-spacing-half);top:var(--size-spacing-half)}.notification-block-bd .notification .thumb{float:left;padding-right:var(--size-spacing-default);text-align:center;width:var(--size-avatar-large)}.notification-block-bd .notification .context{font-size:.75rem;overflow:hidden}.notification-block-bd .clear-all{clear:both;margin-top:var(--size-spacing-default);text-align:right}.notification-block-follow{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-primary)}.notification-block-save{background-color:var(--color-bg-warning-pastel);color:var(--color-status-warning-pastel-dark)}.notification-block-like{background-color:var(--color-bg-danger-pastel);color:var(--color-status-danger-pastel-dark)}.notification-block-comment{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-secondary)}.notification-block-invitation-approved,.notification-block-invitation-request,.notification-block-shakeinvitation{background-color:var(--color-bg-success-pastel);color:var(--color-status-success-pastel-dark)}.notification-block-aggregate{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-primary)}.notification-block-follow .notification{padding-right:var(--size-spacing-triple)}.notification-block-shakeinvitation .shake-thumb{float:left;margin-right:var(--size-spacing-default);width:var(--size-avatar-default)}.notification-block-shakeinvitation .shake-thumb a{display:block}.notification-block-shakeinvitation h4{padding-top:var(--size-spacing-default)}.notification-block-shakeinvitation .shake-context{clear:both;margin-top:var(--size-spacing-default)}.notification-block-shakeinvitation .shake-context a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.notification-block-shakeinvitation .shake-context a:active,.notification-block-shakeinvitation .shake-context a:focus,.notification-block-shakeinvitation .shake-context a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.notification-block-shakeinvitation .shake-context a:active,.notification-block-shakeinvitation .shake-context a:focus,.notification-block-shakeinvitation .shake-context a:hover{text-decoration:underline}.notification-block-shakeinvitation .buttons{display:flex;justify-content:space-between;margin-top:var(--size-spacing-default)}.content-shake .notification-block-shakeinvitation{margin-top:var(--size-spacing-double)}.notification-block-invitation-request .notification a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.notification-block-invitation-request .notification a:active,.notification-block-invitation-request .notification a:focus,.notification-block-invitation-request .notification a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.notification-block-invitation-request .notification a:active,.notification-block-invitation-request .notification a:focus,.notification-block-invitation-request .notification a:hover{text-decoration:underline}.notification-block-invitation-request .notification-actions{clear:both;display:flex;justify-content:space-between;margin-top:var(--size-spacing-default)}.notification-block-tou .notification-block-link a{color:var(--color-page-text-emphasis)}.notification-block-tou .notification-block-link a:focus,.notification-block-tou .notification-block-link a:hover{color:var(--color-text-link-hover)}.account-header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));padding:1.5rem 1.875rem 1rem;width:100%}.account-header .avatar{display:flex}.account-header .avatar img{display:block;height:50px;width:50px}.account-header .avatar-media{flex:none}.account-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.account-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.account-header h2{font-size:2.625rem}}.pagination{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;font-weight:var(--number-font-weight-bold);margin:var(--size-spacing-quadruple);padding:var(--size-spacing-default)}.pagination .current,.pagination a{padding:0 var(--size-spacing-half)}.pagination span.next-link,.pagination span.previous-link{color:var(--color-page-text-secondary)}.pagination .previous-link{flex-basis:50%}@media screen and (min-width:480px){.pagination .previous-link{flex-basis:auto;margin-right:auto}}.pagination .next-link{flex-basis:50%;text-align:right}@media screen and (min-width:480px){.pagination .next-link{flex-basis:auto;margin-left:auto;order:1}}.pagination-inner{align-items:center;background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);display:flex;flex-wrap:wrap;justify-content:center;padding:var(--size-spacing-default)}@media screen and (min-width:480px){.pagination-inner{flex-wrap:none}}.linear-navigation{display:flex;justify-content:space-between;padding:var(--size-spacing-triple) var(--size-spacing-quadruple)}.linear-navigation .newer a,.linear-navigation .older a{display:block}.promotions{list-style:none;margin:var(--size-spacing-default) 0;padding:0}.promotions li{float:left;margin-bottom:var(--size-spacing-default);margin-right:var(--size-spacing-default);overflow:visible;position:relative}.promotions li:focus .promotion-avatar,.promotions li:hover .promotion-avatar{border-color:var(--color-brand-primary)}.promotions li:focus .promotion-name,.promotions li:hover .promotion-name{display:block}.promotions .promotion-avatar{border:4px solid transparent;border-radius:var(--size-border-radius-large);overflow:hidden;transition:all .75s ease 0s}@media screen and (prefers-reduced-motion:reduce){.promotions .promotion-avatar{transition:none}}.promotions .promotion-avatar img{display:block;height:var(--size-avatar-large);width:var(--size-avatar-large)}.promotions .promotion-name{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-primary);border:none;border-radius:.5em;bottom:-3.5em;box-shadow:.22em .22em 0 var(--color-base-black-transparent-100);color:var(--color-text-light-emphasis);cursor:pointer;display:inline-block;display:none;font-size:18px;font-size:.875rem;font-weight:500;height:2.2em;left:50%;letter-spacing:.033em;line-height:2.2em;margin-right:.22em;padding:0 .88em;position:absolute;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transform:translateX(-50%);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap;z-index:1}.promotions .promotion-name:focus,.promotions .promotion-name:hover{color:var(--color-text-light-emphasis)}.promotions .promotion-name:focus{outline:none}.promotions .promotion-name:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.promotions .promotion-name:focus,.promotions .promotion-name:hover{background-color:var(--color-background-button-primary-hover)}.promotions .promotion-name:disabled{box-shadow:none!important}.promo-block{margin:var(--size-spacing-double) auto;max-width:100%;text-align:center;width:285px}@media screen and (max-width:320px){.promo-block{display:none}}.shake-image{cursor:pointer;height:284px;max-width:284px;overflow:hidden;position:relative}.shake-image img{display:block}.shake-image .shake-image-input{cursor:pointer;height:100%;left:0;opacity:0;position:absolute;top:0;width:100%;z-index:1}.shake-image .border{display:none}.shake-image.is-editable:focus .border,.shake-image.is-editable:hover .border,.shake-image.shake-image-hover .border{border:10px solid var(--color-status-edit);display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0}.shake-image .shake-image-placeholder{align-items:center;background:var(--color-background-content-secondary);border:1px dashed var(--color-border-default);display:flex;flex-direction:column;height:100%;justify-content:center;max-width:100%;padding:var(--size-spacing-triple);text-align:center}.shake-image .shake-image-placeholder strong{color:var(--color-brand-primary);display:block}.shake-details .shake-edit-description-form,.shake-details .shake-edit-title-form{display:none}.shake-details .title{word-wrap:break-word;font-size:2.25rem;margin:var(--size-spacing-half-again) 0 var(--size-spacing-default);overflow-wrap:break-word;word-break:break-word}.shake-details .shake-edit-title-hover,.shake-details.is-editable .title:focus,.shake-details.is-editable .title:hover{background-color:var(--color-status-edit)}.shake-details .shake-edit-title-input{border:1px solid var(--color-border-default);font-size:2.25rem;font-weight:var(--number-font-weight-bold);margin-top:var(--size-spacing-default);width:100%}.shake-details .description{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;white-space:pre-wrap;word-break:break-word}.shake-details .description .placeholder{color:var(--color-page-text-secondary);font-style:italic}.shake-details .shake-edit-description-hover,.shake-details.is-editable .description:focus,.shake-details.is-editable .description:hover{background-color:var(--color-status-edit)}.shake-details .shake-edit-description-input{border:1px solid var(--color-border-default);font-size:.875rem;width:100%}.shake-details .buttons{align-items:center;display:flex;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-half)}.shake-details .or{color:var(--color-page-text-secondary);padding:var(--size-spacing-half)}.shake-list{border-top:1px dashed var(--color-border-default);list-style:none;margin:0;padding:0}.shake-list--shake{border-bottom:1px dashed var(--color-border-default);clear:both;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.shake-list--thumb{float:left;margin-right:1em}.shake-list--description,.shake-view-description,.shake-view-featured,.shake-view-title{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.shake-members{list-style:none;margin:0;padding:var(--size-spacing-triple)}.shake-members li{border-bottom:1px dashed var(--color-border-default);display:flex;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.shake-members .member--img{flex:none;margin-right:var(--size-spacing-default)}.shake-members .details{flex:1}.shake-members h4{font-size:1.125rem;padding-bottom:var(--size-spacing-half)}.shake-members h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.shake-members h4 a:active,.shake-members h4 a:focus,.shake-members h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.shake-members h4 a:active,.shake-members h4 a:focus,.shake-members h4 a:hover{text-decoration:underline}.shake-members .about{font-size:.875rem;margin:0}.shake-members .website{display:block;margin-top:var(--size-spacing-half)}.following-wrapper{margin:var(--size-spacing-double) 0}.following-wrapper h3{color:var(--color-page-text);font-size:.875rem;padding-bottom:var(--size-spacing-default);padding-left:var(--size-spacing-half-again);padding-right:var(--size-spacing-half-again)}.following-wrapper h3 a{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-secondary);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);cursor:pointer;display:inline-block;float:right;font-size:18px;font-size:12px;font-weight:500;font-weight:400;height:2.2em;letter-spacing:.033em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.following-wrapper h3 a:focus,.following-wrapper h3 a:hover{color:var(--color-text-light-emphasis)}.following-wrapper h3 a:focus{outline:none}.following-wrapper h3 a:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.following-wrapper h3 a:focus,.following-wrapper h3 a:hover{background-color:var(--color-background-button-secondary-hover)}.following-wrapper h3 span{color:var(--color-page-text-secondary);font-weight:var(--number-font-weight-normal)}.following{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large)}.following ul{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:var(--size-spacing-half-again);padding-right:0}.following ul li{flex:none;margin-bottom:var(--size-spacing-half);margin-right:var(--size-spacing-half)}.following ul li a{display:block}.following br{display:none}.following .view-all-following{display:block;font-size:.75rem;margin-top:calc(var(--size-spacing-default)*-1);padding-bottom:var(--size-spacing-default);padding-right:var(--size-spacing-half-again);text-align:right}.other-shakes-wrapper{margin-bottom:var(--size-spacing-double)}.other-shakes-wrapper h3{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;padding-bottom:var(--size-spacing-default);padding-left:var(--size-spacing-half-again);word-break:break-word}.other-shakes{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-default) var(--size-spacing-half-again)}.other-shakes ul{list-style:none;margin:0;padding:0}.other-shakes a{word-wrap:break-word;color:var(--color-text-link-primary);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;padding:var(--size-spacing-half) 0;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.other-shakes a:active,.other-shakes a:focus,.other-shakes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.other-shakes a:active,.other-shakes a:focus,.other-shakes a:hover{text-decoration:underline}.find-shakes-block{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.find-shakes-block h3{color:var(--color-brand-secondary);font-size:1.25rem}.find-shakes-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.find-shakes-block-content p{margin:0}.find-shakes-block-content a{font-weight:var(--number-font-weight-bold);text-decoration:none}.upgrade-account-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.upgrade-account-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-half);padding:var(--size-spacing-double)}.upgrade-account-block-content h3{font-size:1.125rem;margin-bottom:var(--size-spacing-half)}.upgrade-account-block-content h3 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.upgrade-account-block-content h3 a:active,.upgrade-account-block-content h3 a:focus,.upgrade-account-block-content h3 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.upgrade-account-block-content h3 a:active,.upgrade-account-block-content h3 a:focus,.upgrade-account-block-content h3 a:hover{text-decoration:underline}.upgrade-account-block-content p{margin:0}.cool-tools-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.cool-tools-block h3{color:var(--color-brand-primary);font-size:1.25rem}.cool-tools-block p{font-size:.875rem;margin:var(--size-spacing-half) 0 0}.cool-tools-block a{background-color:var(--color-background-content);background-position:5px 5px;background-repeat:no-repeat;background-size:40px 45px;border-radius:var(--size-border-radius-large);color:var(--color-text-link-primary);display:block;font-size:.75rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default);padding-bottom:var(--size-spacing-double);padding-left:55px;padding-top:var(--size-spacing-double);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.cool-tools-block a:active,.cool-tools-block a:focus,.cool-tools-block a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.cool-tools-block a:active,.cool-tools-block a:focus,.cool-tools-block a:hover{text-decoration:underline}.cool-tools-block .browser-tools{list-style:none;margin:var(--size-spacing-double) 0 var(--size-spacing-default);padding:0}.cool-tools-block .browser-tools li+li{margin-top:var(--size-spacing-default)}.cool-tools-block .bookmarklet a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 34.13 31.65'%3E%3Cdefs%3E%3CradialGradient id='a' cx='21.18' cy='10.62' r='17.07' gradientTransform='matrix(1 0 0 .43 -4.12 -1.84)' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23d6d6d6'/%3E%3Cstop offset='.36' stop-color='%23e1e1e1'/%3E%3Cstop offset='.99' stop-color='%23fff'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath fill='url(%23a)' d='M0 3.08h34.13v6.86H0z'/%3E%3Cpath fill='%23881746' d='M7.06 3.03c0-1.68.75-3 1.68-3h11c-.93 0-3 1.36-3 3z'/%3E%3Cpath fill='%23ed1d7f' d='M25.4 31.65V3.03c0-1.68-.75-3-1.68-3h-15c.93 0 1.68 1.36 1.68 3v28.62l7.52-5.42z'/%3E%3C/svg%3E")}.cool-tools-block .safari a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 66.166 65.804'%3E%3Cdefs%3E%3ClinearGradient id='b'%3E%3Cstop offset='0' stop-color='%2306c2e7'/%3E%3Cstop offset='.25' stop-color='%230db8ec'/%3E%3Cstop offset='.5' stop-color='%2312aef1'/%3E%3Cstop offset='.75' stop-color='%231f86f9'/%3E%3Cstop offset='1' stop-color='%23107ddd'/%3E%3C/linearGradient%3E%3ClinearGradient id='a'%3E%3Cstop offset='0' stop-color='%23bdbdbd'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/linearGradient%3E%3ClinearGradient xlink:href='%23a' id='d' x1='412.975' x2='412.975' y1='237.608' y2='59.392' gradientTransform='translate(206.79 159.773) scale(.35154)' gradientUnits='userSpaceOnUse'/%3E%3Cfilter id='f' width='1.042' height='1.045' x='-.021' y='-.022' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='.958'/%3E%3C/filter%3E%3Cfilter id='c' width='1.096' height='1.096' x='-.048' y='-.048' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='3.564'/%3E%3C/filter%3E%3CradialGradient xlink:href='%23b' id='e' cx='413.061' cy='136.818' r='82.125' fx='413.061' fy='136.818' gradientTransform='translate(194.545 155.58) scale(.38143)' gradientUnits='userSpaceOnUse'/%3E%3C/defs%3E%3Cpath d='M502.083 148.5a89.108 89.108 0 0 1-89.108 89.108 89.108 89.108 0 0 1-89.108-89.108 89.108 89.108 0 0 1 89.108-89.108 89.108 89.108 0 0 1 89.108 89.108z' filter='url(%23c)' opacity='.53' paint-order='markers stroke fill' transform='matrix(.33865 0 0 .3261 -106.77 -14.478)'/%3E%3Cpath fill='url(%23d)' stroke='%23cdcdcd' stroke-linecap='round' stroke-linejoin='round' stroke-width='.093' d='M383.294 211.977a31.325 31.325 0 0 1-31.325 31.325 31.325 31.325 0 0 1-31.326-31.325 31.325 31.325 0 0 1 31.326-31.325 31.325 31.325 0 0 1 31.325 31.325z' paint-order='markers stroke fill' transform='translate(-318.886 -180.595)'/%3E%3Cpath fill='url(%23e)' d='M380.84 211.977a28.87 28.87 0 0 1-28.871 28.87 28.87 28.87 0 0 1-28.871-28.87 28.87 28.87 0 0 1 28.87-28.87 28.87 28.87 0 0 1 28.871 28.87z' paint-order='markers stroke fill' transform='translate(-318.886 -180.595)'/%3E%3Cpath fill='%23f4f2f3' d='M33.083 4.017a.42.42 0 0 0-.421.42v4.856a.42.42 0 1 0 .842 0V4.438a.42.42 0 0 0-.421-.421zm-2.754.174a.42.42 0 0 0-.46.463l.212 2.03a.42.42 0 1 0 .837-.087l-.212-2.03a.42.42 0 0 0-.377-.376zm5.527.002a.42.42 0 0 0-.377.375l-.214 2.03a.42.42 0 1 0 .837.089l.214-2.031a.42.42 0 0 0-.46-.463zM27.5 4.6a.42.42 0 0 0-.41.508l1.005 4.75a.42.42 0 1 0 .824-.174l-1.005-4.75A.42.42 0 0 0 27.5 4.6zm11.183.004a.42.42 0 0 0-.414.333l-1.009 4.75a.42.42 0 1 0 .824.175l1.009-4.75a.42.42 0 0 0-.41-.508zM24.8 5.361a.42.42 0 0 0-.437.55l.632 1.942a.42.42 0 1 0 .8-.26l-.63-1.942a.42.42 0 0 0-.365-.29zm16.568.001a.42.42 0 0 0-.364.29l-.632 1.942a.42.42 0 1 0 .8.26l.632-1.942a.42.42 0 0 0-.436-.55zM22.13 6.34a.42.42 0 0 0-.377.592l1.972 4.437a.42.42 0 1 0 .77-.342L22.523 6.59a.42.42 0 0 0-.393-.25zm21.937.015a.42.42 0 0 0-.392.25l-1.978 4.434a.42.42 0 1 0 .769.343l1.978-4.434a.42.42 0 0 0-.377-.593zM19.654 7.65a.42.42 0 0 0-.394.63l1.02 1.77a.42.42 0 1 0 .73-.421L19.989 7.86a.42.42 0 0 0-.335-.21zm26.858 0a.419.419 0 0 0-.335.21l-1.021 1.769a.42.42 0 1 0 .73.42l1.02-1.768a.42.42 0 0 0-.394-.63zm-29.265 1.5a.422.422 0 0 0-.326.669l2.85 3.93a.42.42 0 1 0 .682-.494l-2.85-3.93a.42.42 0 0 0-.356-.174zm31.702.022a.42.42 0 0 0-.356.174l-2.856 3.926a.42.42 0 1 0 .681.495l2.856-3.926a.42.42 0 0 0-.325-.669zm-33.852 1.783a.42.42 0 0 0-.335.702l1.366 1.518a.42.42 0 1 0 .626-.563l-1.367-1.518a.42.42 0 0 0-.29-.14zm35.975.003a.421.421 0 0 0-.29.139l-1.367 1.517a.42.42 0 1 0 .625.564l1.367-1.518a.42.42 0 0 0-.335-.702zm-38.037 1.977a.42.42 0 0 0-.26.733l3.61 3.248a.42.42 0 1 0 .563-.625l-3.609-3.248a.42.42 0 0 0-.304-.108zm40.109.014a.419.419 0 0 0-.304.108l-3.61 3.245a.42.42 0 1 0 .562.626l3.61-3.245a.42.42 0 0 0-.258-.734zm-41.823 2.19a.42.42 0 0 0-.262.762l1.652 1.2a.42.42 0 1 0 .495-.681l-1.652-1.2a.42.42 0 0 0-.233-.081zm43.535.015a.421.421 0 0 0-.233.08l-1.653 1.2a.42.42 0 1 0 .495.681l1.653-1.2a.42.42 0 0 0-.262-.76zM9.72 17.49a.42.42 0 0 0-.18.785l4.204 2.427a.42.42 0 1 0 .42-.729L9.96 17.546a.42.42 0 0 0-.24-.056zm46.728 0a.417.417 0 0 0-.24.056l-4.205 2.427a.42.42 0 1 0 .42.73l4.206-2.428a.42.42 0 0 0-.181-.785zm-47.94 2.506a.42.42 0 0 0-.18.806l1.866.831a.42.42 0 1 0 .343-.768l-1.866-.832a.42.42 0 0 0-.163-.037zm49.158.017a.42.42 0 0 0-.164.037l-1.865.83a.42.42 0 1 0 .342.77l1.866-.831a.42.42 0 0 0-.179-.806zM7.429 22.615a.42.42 0 0 0-.094.82l4.615 1.504a.42.42 0 1 0 .261-.8l-4.616-1.504a.421.421 0 0 0-.166-.02zm51.314.018a.408.408 0 0 0-.166.02l-4.617 1.5a.42.42 0 1 0 .26.801l4.617-1.5a.42.42 0 0 0-.094-.82zM6.756 25.365a.42.42 0 0 0-.09.833l1.998.424a.42.42 0 1 0 .175-.823l-1.998-.425a.413.413 0 0 0-.085-.009zm52.655.004a.518.518 0 0 0-.085.009l-1.998.424a.42.42 0 1 0 .175.823l1.998-.424a.42.42 0 0 0-.09-.833zM6.247 28.13a.42.42 0 0 0-.003.838l4.828.51a.42.42 0 1 0 .089-.837l-4.829-.51a.432.432 0 0 0-.085 0zm53.676.037a.386.386 0 0 0-.085 0l-4.83.504a.42.42 0 1 0 .088.837l4.83-.504a.42.42 0 0 0-.003-.837zM6.165 30.96a.42.42 0 1 0 0 .842h2.043a.42.42 0 1 0 0-.842zm51.793 0a.42.42 0 1 0 0 .842h2.043a.42.42 0 1 0 0-.842zm-46.803 2.295a.384.384 0 0 0-.085 0l-4.83.504a.42.42 0 1 0 .088.838l4.83-.504a.42.42 0 0 0-.003-.838zm43.853.03a.42.42 0 0 0-.003.838l4.828.51a.42.42 0 1 0 .089-.837l-4.828-.51a.434.434 0 0 0-.086-.001zm-46.26 2.843a.43.43 0 0 0-.085.01l-1.998.424a.42.42 0 1 0 .175.823l1.998-.424a.42.42 0 0 0-.09-.833zm48.67.005a.42.42 0 0 0-.09.833l1.997.424a.42.42 0 1 0 .175-.824l-1.998-.424a.413.413 0 0 0-.085-.01zM12.111 37.79a.408.408 0 0 0-.166.02l-4.617 1.5a.42.42 0 1 0 .26.801l4.617-1.5a.42.42 0 0 0-.094-.82zm41.937.015a.42.42 0 0 0-.094.82l4.616 1.504a.42.42 0 1 0 .26-.8l-4.615-1.504a.421.421 0 0 0-.167-.02zM10.35 41.08a.42.42 0 0 0-.163.036l-1.866.831a.42.42 0 1 0 .342.769l1.866-.83a.42.42 0 0 0-.179-.806zm45.459.016a.42.42 0 0 0-.18.805l1.865.832a.42.42 0 1 0 .343-.769l-1.865-.832a.42.42 0 0 0-.163-.036zm-41.826.912a.417.417 0 0 0-.24.056L9.538 44.49a.42.42 0 1 0 .421.73l4.205-2.428a.42.42 0 0 0-.181-.785zm38.2 0a.42.42 0 0 0-.181.785l4.205 2.427a.42.42 0 1 0 .42-.729l-4.204-2.427a.42.42 0 0 0-.24-.056zM12.934 45.57a.421.421 0 0 0-.233.08l-1.653 1.2a.42.42 0 1 0 .495.682l1.653-1.2a.42.42 0 0 0-.262-.762zm40.288.015a.42.42 0 0 0-.262.762l1.652 1.2a.42.42 0 1 0 .495-.681l-1.652-1.2a.42.42 0 0 0-.233-.081zm-36.544.145a.418.418 0 0 0-.304.108l-3.61 3.245a.42.42 0 1 0 .562.626l3.61-3.245a.42.42 0 0 0-.258-.734zm32.8.011a.421.421 0 0 0-.26.734l3.609 3.248a.42.42 0 1 0 .563-.625l-3.608-3.249a.42.42 0 0 0-.304-.107zm-29.375 3.084a.42.42 0 0 0-.355.173l-2.856 3.927a.42.42 0 1 0 .68.495l2.857-3.926a.42.42 0 0 0-.326-.669zm25.936.018a.421.421 0 0 0-.326.668l2.85 3.93a.42.42 0 1 0 .682-.494l-2.851-3.93a.42.42 0 0 0-.355-.174zm-29.623.606a.421.421 0 0 0-.29.14l-1.367 1.517a.42.42 0 1 0 .625.563l1.367-1.517a.42.42 0 0 0-.335-.703zm33.331.002a.42.42 0 0 0-.335.702l1.366 1.518a.42.42 0 1 0 .626-.563l-1.366-1.518a.42.42 0 0 0-.291-.139zm-25.655 1.684a.419.419 0 0 0-.393.25l-1.978 4.433a.42.42 0 1 0 .77.343l1.977-4.434a.42.42 0 0 0-.376-.592zm17.955.012a.42.42 0 0 0-.377.592l1.972 4.437a.42.42 0 1 0 .77-.342l-1.972-4.437a.42.42 0 0 0-.393-.25zm-21.431 1.359a.419.419 0 0 0-.335.21l-1.021 1.768a.42.42 0 1 0 .729.421l1.02-1.769a.42.42 0 0 0-.393-.63zm24.934 0a.42.42 0 0 0-.394.63l1.021 1.77a.42.42 0 1 0 .73-.422l-1.022-1.769a.42.42 0 0 0-.335-.21zm-17.054.063a.42.42 0 0 0-.415.334l-1.009 4.749a.42.42 0 1 0 .824.175l1.009-4.75a.42.42 0 0 0-.41-.508zm9.16.003a.42.42 0 0 0-.41.508l1.006 4.75a.42.42 0 1 0 .823-.175l-1.006-4.75a.42.42 0 0 0-.414-.333zm-4.573.48a.42.42 0 0 0-.421.42v4.855a.42.42 0 1 0 .842 0v-4.855a.42.42 0 0 0-.421-.42zm-7.727 1.568a.42.42 0 0 0-.364.29l-.631 1.942a.42.42 0 1 0 .8.26l.632-1.942a.42.42 0 0 0-.437-.55zm15.45 0a.42.42 0 0 0-.437.55l.632 1.943a.42.42 0 1 0 .8-.26l-.63-1.942a.42.42 0 0 0-.365-.29zm-10.365 1.083a.42.42 0 0 0-.378.375l-.213 2.03a.42.42 0 1 0 .837.088l.214-2.03a.42.42 0 0 0-.46-.463zm5.267.002a.42.42 0 0 0-.46.463l.212 2.03a.42.42 0 1 0 .837-.088l-.212-2.03a.42.42 0 0 0-.377-.375z' paint-order='markers stroke fill'/%3E%3Cpath d='m469.096 100.607-65.51 38.061-41.42 65.207 60.595-44.882z' filter='url(%23f)' opacity='.409' paint-order='markers stroke fill' transform='translate(-112.095 -20.822) scale(.35154)'/%3E%3Cpath fill='%23ff5150' d='m36.383 34.838-6.6-6.913 23.416-15.752z' paint-order='markers stroke fill'/%3E%3Cpath fill='%23f1f1f1' d='m36.383 34.838-6.6-6.913L12.966 50.59z' paint-order='markers stroke fill'/%3E%3Cpath d='m12.967 50.59 23.416-15.752L53.2 12.173z' opacity='.243'/%3E%3C/svg%3E")}.cool-tools-block .firefox a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 51500 51500'%3E%3CradialGradient id='c' cx='87.4%25' cy='-12.9%25' r='128%25' gradientTransform='matrix(.8 0 0 1 .18 .13)'%3E%3Cstop offset='.13' stop-color='%23ffbd4f'/%3E%3Cstop offset='.28' stop-color='%23ff980e'/%3E%3Cstop offset='.47' stop-color='%23ff3750'/%3E%3Cstop offset='.78' stop-color='%23eb0878'/%3E%3Cstop offset='.86' stop-color='%23e50080'/%3E%3C/radialGradient%3E%3CradialGradient id='d' cx='49%25' cy='40%25' r='128%25' gradientTransform='matrix(.82 0 0 1 .09 0)'%3E%3Cstop offset='.3' stop-color='%23960e18'/%3E%3Cstop offset='.35' stop-color='%23b11927' stop-opacity='.74'/%3E%3Cstop offset='.43' stop-color='%23db293d' stop-opacity='.34'/%3E%3Cstop offset='.5' stop-color='%23f5334b' stop-opacity='.1'/%3E%3Cstop offset='.53' stop-color='%23ff3750' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='e' cx='48%25' cy='-12%25' r='140%25'%3E%3Cstop offset='.13' stop-color='%23fff44f'/%3E%3Cstop offset='.53' stop-color='%23ff980e'/%3E%3C/radialGradient%3E%3CradialGradient id='g' cx='22.76%25' cy='110.11%25' r='100%25'%3E%3Cstop offset='.35' stop-color='%233a8ee6'/%3E%3Cstop offset='.67' stop-color='%239059ff'/%3E%3Cstop offset='1' stop-color='%23c139e6'/%3E%3C/radialGradient%3E%3CradialGradient id='h' cx='52%25' cy='33%25' r='59%25' gradientTransform='scale(.9 1)'%3E%3Cstop offset='.21' stop-color='%239059ff' stop-opacity='0'/%3E%3Cstop offset='.97' stop-color='%236e008b' stop-opacity='.6'/%3E%3C/radialGradient%3E%3CradialGradient id='i' cx='210%25' cy='-100%25' r='290%25'%3E%3Cstop offset='.1' stop-color='%23ffe226'/%3E%3Cstop offset='.79' stop-color='%23ff7139'/%3E%3C/radialGradient%3E%3CradialGradient id='j' cx='84%25' cy='-41%25' r='180%25'%3E%3Cstop offset='.11' stop-color='%23fff44f'/%3E%3Cstop offset='.46' stop-color='%23ff980e'/%3E%3Cstop offset='.72' stop-color='%23ff3647'/%3E%3Cstop offset='.9' stop-color='%23e31587'/%3E%3C/radialGradient%3E%3CradialGradient id='k' cx='16.1%25' cy='-18.6%25' r='348.8%25' gradientTransform='matrix(.10453 .46743 -.99452 .04913 -.05 -.26)'%3E%3Cstop offset='0' stop-color='%23fff44f'/%3E%3Cstop offset='.3' stop-color='%23ff980e'/%3E%3Cstop offset='.57' stop-color='%23ff3647'/%3E%3Cstop offset='.74' stop-color='%23e31587'/%3E%3C/radialGradient%3E%3CradialGradient id='l' cx='18.9%25' cy='-42.5%25' r='238.4%25'%3E%3Cstop offset='.14' stop-color='%23fff44f'/%3E%3Cstop offset='.48' stop-color='%23ff980e'/%3E%3Cstop offset='.66' stop-color='%23ff3647'/%3E%3Cstop offset='.9' stop-color='%23e31587'/%3E%3C/radialGradient%3E%3CradialGradient id='m' cx='159.3%25' cy='-44.72%25' r='313.1%25'%3E%3Cstop offset='.09' stop-color='%23fff44f'/%3E%3Cstop offset='.63' stop-color='%23ff980e'/%3E%3C/radialGradient%3E%3ClinearGradient id='a' x1='87.25%25' x2='9.4%25' y1='15.5%25' y2='93.1%25'%3E%3Cstop offset='.05' stop-color='%23fff44f'/%3E%3Cstop offset='.37' stop-color='%23ff980e'/%3E%3Cstop offset='.53' stop-color='%23ff3647'/%3E%3Cstop offset='.7' stop-color='%23e31587'/%3E%3C/linearGradient%3E%3ClinearGradient id='n' x1='80%25' x2='18%25' y1='14%25' y2='84%25'%3E%3Cstop offset='.17' stop-color='%23fff44f' stop-opacity='.8'/%3E%3Cstop offset='.6' stop-color='%23fff44f' stop-opacity='0'/%3E%3C/linearGradient%3E%3Cpath id='b' d='M47870 16735c-1044-2512-3160-5224-4820-6082 1352 2650 2134 5310 2433 7294 0-6 2 5 4 22l4 26c2268 6147 1032 12398-748 16218-2754 5910-9420 11967-19857 11670-11276-318-21210-8683-23064-19643-338-1728 0-2605 170-4008-207 1080-286 1394-390 3315l-2 123c0 13270 10760 24030 24032 24030 11887 0 21756-8630 23690-19963l110-927c477-4120-53-8453-1560-12075z'/%3E%3Cpath id='f' d='M25677 21050c-40 598-2150 2660-2890 2660-6834 0-7943 4133-7943 4133 303 3480 2726 6348 5660 7865 134 70 270 130 405 193a13277 13277 0 0 0 706 289 10674 10674 0 0 0 3127 603c11978 562 14300-14320 5655-18640 2213-385 4510 505 5794 1407-2100-3672-6025-6150-10530-6150-285 0-564 24-844 43a12025 12025 0 0 0-6614 2549c366 310 780 724 1650 1583 1630 1606 5813 3270 5822 3465z'/%3E%3Cpath fill='url(%23a)' d='M47870 16735c-1044-2512-3160-5224-4820-6082 1352 2650 2134 5310 2433 7294l5 40c-2718-6773-7325-9505-11088-15452l-566-920a7372 7372 0 0 1-265-497 4370 4370 0 0 1-359-950 63 63 0 0 0-55-65 82 82 0 0 0-45 0l-12 7-17 10 10-14c-6037 3536-8085 10076-8274 13350a12025 12025 0 0 0-6614 2548 7136 7136 0 0 0-622-470 11134 11134 0 0 1-68-5873c-2468 1124-4390 2900-5785 4470h-10c-953-1206-886-5187-832-6018-10-52-710 363-802 425a17507 17507 0 0 0-2349 2012 21048 21048 0 0 0-2244 2692l-1 3v-3a20284 20284 0 0 0-3225 7280l-32 160a39700 39700 0 0 0-237 1500l-5 52a22907 22907 0 0 0-390 3316l-1 120c0 13270 10760 24030 24032 24030 11887 0 21756-8630 23690-19963l110-927c477-4120-53-8453-1560-12075zM20170 35545c113 53 220 112 334 164l16 10a12620 12620 0 0 1-350-174zm5506-14493zm19813-3060-3-23 4 26z'/%3E%3Cuse xlink:href='%23b' fill='url(%23c)'/%3E%3Cuse xlink:href='%23b' fill='url(%23d)'/%3E%3Cpath fill='url(%23e)' d='m36192 19560 150 110a13070 13070 0 0 0-2231-2911C26640 9290 32150 563 33080 120l10-13c-6037 3535-8085 10076-8273 13348 280-20 560-43 844-43 4505 0 8430 2477 10530 6150z'/%3E%3Cuse xlink:href='%23f' fill='url(%23g)'/%3E%3Cuse xlink:href='%23f' fill='url(%23h)'/%3E%3Cpath fill='url(%23i)' d='M17083 15204a24404 24404 0 0 1 498 330 11134 11134 0 0 1-67-5874c-2470 1125-4390 2900-5785 4470 115-3 3600-66 5354 1074z'/%3E%3Cpath fill='url(%23j)' d='M1822 26240c1855 10960 11788 19325 23063 19644 10437 296 17104-5762 19858-11670 1780-3820 3016-10070 748-16218v-2l-4-24c-2-17-4-28-4-22l5 40c853 5566-1980 10958-6405 14604l-13 30c-8625 7023-16878 4237-18550 3097a14410 14410 0 0 1-350-174c-5028-2403-7105-6984-6660-10913-4245 0-5693-3580-5693-3580s3812-2718 8836-355c4653 2190 9023 355 9023 354-10-195-4192-1860-5822-3465-872-860-1285-1272-1652-1583a7136 7136 0 0 0-622-470 28293 28293 0 0 0-498-330c-1753-1140-5240-1076-5355-1073h-10c-953-1207-886-5188-832-6020-10-50-710 363-802 426a17507 17507 0 0 0-2349 2012 21048 21048 0 0 0-2244 2692l-1 3v-3a20284 20284 0 0 0-3225 7280c-10 52-865 3784-444 5720z'/%3E%3Cpath fill='url(%23k)' d='M34110 16760a13070 13070 0 0 1 2231 2910l360 296c5450 5020 2594 12120 2380 12626 4426-3646 7258-9038 6405-14604-2716-6774-7323-9506-11086-15453l-566-920a7372 7372 0 0 1-265-497 4370 4370 0 0 1-359-950 63 63 0 0 0-55-65 82 82 0 0 0-45 0l-12 7-17 10c-930 443-6440 9170 1030 16640z'/%3E%3Cpath fill='url(%23l)' d='M36702 19965a4743 4743 0 0 0-360-295l-150-110c-1283-900-3580-1792-5794-1407 8644 4322 6323 19203-5655 18640a10674 10674 0 0 1-3127-603 13451 13451 0 0 1-706-289 9064 9064 0 0 1-405-193l16 10c1670 1140 9924 3925 18550-3097l13-30c213-506 3068-7606-2380-12626z'/%3E%3Cpath fill='url(%23m)' d='M14844 27844s1110-4133 7943-4133c740 0 2850-2062 2890-2660s-4370 1836-9023-354c-5024-2363-8836 354-8836 354s1448 3580 5693 3580c-445 3930 1632 8510 6660 10913 113 53 218 112 334 164-2935-1517-5358-4384-5660-7865z'/%3E%3Cpath fill='url(%23n)' d='M47870 16735c-1044-2512-3160-5224-4820-6082 1352 2650 2134 5310 2433 7294l5 40c-2718-6773-7325-9505-11088-15452l-566-920a7372 7372 0 0 1-265-497 4370 4370 0 0 1-359-950 63 63 0 0 0-55-65 82 82 0 0 0-45 0l-12 7-17 10 10-14c-6037 3536-8085 10076-8274 13350 280-20 560-43 845-43 4505 0 8430 2477 10530 6148-1284-900-3580-1792-5795-1407 8644 4322 6323 19203-5655 18640a10674 10674 0 0 1-3127-603 13451 13451 0 0 1-706-289 9064 9064 0 0 1-405-193l17 10a14410 14410 0 0 1-350-174c112 53 218 112 333 164-2935-1517-5358-4384-5660-7865 0 0 1108-4133 7942-4133 740 0 2850-2062 2890-2660-10-195-4190-1860-5822-3465-870-860-1285-1272-1650-1583a7136 7136 0 0 0-623-470 11134 11134 0 0 1-67-5873c-2470 1124-4390 2900-5785 4470h-10c-953-1207-886-5187-832-6020-10-50-710 363-802 426a17507 17507 0 0 0-2349 2012 21048 21048 0 0 0-2243 2692l-1 3v-3a20284 20284 0 0 0-3225 7280l-32 160a39787 39787 0 0 0-277 1515c-2 18 2-17 0 0a27956 27956 0 0 0-355 3353l-3 122c0 13270 10760 24030 24032 24030 11887 0 21756-8630 23690-19963l110-927c477-4120-53-8453-1560-12075zm-2384 1234 4 26v-2l-4-24z'/%3E%3C/svg%3E")}.cool-tools-block .chrome a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill='none' viewBox='0 0 190 190'%3E%3ClinearGradient id='d' x1='28.3' x2='80.8' y1='75' y2='44.4' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23a52714' stop-opacity='.6'/%3E%3Cstop offset='.7' stop-color='%23a52714' stop-opacity='0'/%3E%3C/linearGradient%3E%3ClinearGradient id='f' x1='109.9' x2='51.5' y1='164.5' y2='130.3' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23055524' stop-opacity='.4'/%3E%3Cstop offset='.3' stop-color='%23055524' stop-opacity='0'/%3E%3C/linearGradient%3E%3ClinearGradient id='h' x1='121.9' x2='136.6' y1='49.8' y2='114.1' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23ea6100' stop-opacity='.3'/%3E%3Cstop offset='.7' stop-color='%23ea6100' stop-opacity='0'/%3E%3C/linearGradient%3E%3CradialGradient id='a' cx='91.2' cy='55' r='84.1' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%233e2723' stop-opacity='.2'/%3E%3Cstop offset='1' stop-color='%233e2723' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient xlink:href='%23a' id='i' cx='20.9' cy='47.5' r='78'/%3E%3CradialGradient id='j' cx='94.8' cy='95.1' r='87.9' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23263238' stop-opacity='.2'/%3E%3Cstop offset='1' stop-color='%23263238' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='k' cx='33.3' cy='31' r='176.8' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23fff' stop-opacity='.1'/%3E%3Cstop offset='1' stop-color='%23fff' stop-opacity='0'/%3E%3C/radialGradient%3E%3CclipPath id='b'%3E%3Ccircle cx='95' cy='95' r='88'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23b)'%3E%3Cuse xlink:href='%23c' fill='%23db4437'/%3E%3Cuse xlink:href='%23c' fill='url(%23d)'/%3E%3Cuse xlink:href='%23e' fill='%230f9d58'/%3E%3Cuse xlink:href='%23e' fill='url(%23f)'/%3E%3Cuse xlink:href='%23g' fill='%23ffcd40'/%3E%3Cuse xlink:href='%23g' fill='url(%23h)'/%3E%3Cg fill-opacity='.1'%3E%3Cpath fill='%233e2723' d='M61.3 114.7 21 47.4l39 67.8z'/%3E%3Cpath fill='%23263238' d='m128.8 116.3-.8-.4-37.3 67 38.3-67z'/%3E%3C/g%3E%3Cpath id='e' d='M7 183h83.8l39-39v-29H60.2L7 23.5z'/%3E%3Cpath id='g' d='m95 55 34.6 60L91 183h92V55z'/%3E%3Cpath id='c' d='M21 7v108h39.4L95 55h88V7z'/%3E%3Cpath fill='url(%23a)' d='M95 55v21l78.4-21z'/%3E%3Cpath fill='url(%23i)' d='m21 47.5 57.2 57.2L60.4 115z'/%3E%3Cpath fill='url(%23j)' d='m90.8 183 21-78.3 17.8 10.3z'/%3E%3Ccircle cx='95' cy='95' r='40' fill='%23f1f1f1'/%3E%3Ccircle cx='95' cy='95' r='32' fill='%234285f4'/%3E%3Ccircle cx='95' cy='95' r='88' fill='url(%23k)'/%3E%3Cg fill='%233e2723' fill-opacity='.1'%3E%3Cpath fill='%23fff' d='M129.6 115a40 40 0 0 1-69.2 0L7 24.5 60.4 116a40 40 0 0 0 69.2 0z'/%3E%3Cpath d='M96 55h-.5a40 40 0 1 1 0 80h.5c22 0 40-18 40-40s-18-40-40-40zm-1 127a88 88 0 0 0 88-87.5v.5A88 88 0 0 1 7 95v-.5A88 88 0 0 0 95 182z'/%3E%3Cg fill-opacity='.2'%3E%3Cpath fill='%23fff' d='M130 116.3a39.3 39.3 0 0 0 3.4-32 38 38 0 0 1-3.8 30.7L92 183l38.2-66.5zM95 8a88 88 0 0 1 88 87.5V95A88 88 0 0 0 7 95v.5A88 88 0 0 1 95 8z'/%3E%3Cpath d='M95 54c-22 0-40 18-40 40v1c0-22 18-40 40-40h88v-1z'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E")}.cool-tools-block .twitter-setup{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 400'%3E%3Cpath fill='none' d='M0 0h400v400H0z'/%3E%3Cpath fill='%231da1f2' d='M153.62 301.59c94.34 0 145.94-78.16 145.94-145.94 0-2.22 0-4.43-.15-6.63A104.36 104.36 0 0 0 325 122.47a102.38 102.38 0 0 1-29.46 8.07 51.47 51.47 0 0 0 22.55-28.37 102.79 102.79 0 0 1-32.57 12.45 51.34 51.34 0 0 0-87.41 46.78A145.62 145.62 0 0 1 92.4 107.81a51.33 51.33 0 0 0 15.88 68.47A50.91 50.91 0 0 1 85 169.86v.65a51.31 51.31 0 0 0 41.15 50.28 51.21 51.21 0 0 1-23.16.88 51.35 51.35 0 0 0 47.92 35.62 102.92 102.92 0 0 1-63.7 22 104.41 104.41 0 0 1-12.21-.74 145.21 145.21 0 0 0 78.62 23'/%3E%3C/svg%3E")}.shake-invite-member-block{background-color:var(--color-brand-primary);border-radius:var(--size-border-radius-large);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);clear:both;margin-bottom:var(--size-spacing-double);margin-right:var(--size-spacing-half);margin-top:var(--size-spacing-double);position:relative}.shake-invite-member-block h3{color:var(--color-text-light-emphasis);padding:var(--size-spacing-half-again) var(--size-spacing-default);text-shadow:.05em .05em .05em var(--color-base-black-transparent-500)}.shake-invite-member-block form{padding:0 var(--size-spacing-default) var(--size-spacing-double)}.shake-invite-member-block .shake-input-wrapper{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);display:flex;padding:var(--size-spacing-half);position:relative}.shake-invite-member-block .input-text{background-color:var(--color-background-content);border:0;border-radius:var(--size-border-radius-default);flex:1}.shake-invite-member-block .invite-button{flex:none;margin-left:var(--size-spacing-half)}.shake-invite-member-block .shake-results{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);display:none;left:0;list-style:none;margin:0;padding:var(--size-spacing-half);position:absolute;top:var(--size-spacing-quadruple);width:220px}.shake-invite-member-block .shake-results li{align-items:center;border-radius:var(--size-border-radius-default);cursor:pointer;display:flex;font-size:.875rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-half)}.shake-invite-member-block .shake-results li:focus,.shake-invite-member-block .shake-results li:hover{background-color:var(--color-bg-success-pastel)}.shake-invite-member-block .shake-results li img{flex:none;height:var(--size-avatar-tiny);margin-right:var(--size-spacing-half);width:var(--size-avatar-tiny)}.shake-invite-member-block .shake-results li span{flex:1}.shake-sidebar-actions{display:flex;margin-bottom:var(--size-spacing-double);margin-top:var(--size-spacing-default)}.shake-sidebar-actions>*{flex:none}.shake-sidebar-actions>*+*{margin-left:var(--size-spacing-default)}.shake-sidebar-actions .follow h4,.shake-sidebar-actions .icon,.shake-sidebar-actions .user-follow h4 a{display:none}.shake-sidebar-editor-block{align-items:center;background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);clear:both;display:flex;font-size:.875rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.shake-sidebar-editor-block a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.shake-sidebar-editor-block a:active,.shake-sidebar-editor-block a:focus,.shake-sidebar-editor-block a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.shake-sidebar-editor-block a:active,.shake-sidebar-editor-block a:focus,.shake-sidebar-editor-block a:hover{text-decoration:underline}.shake-sidebar-editor-block .editor-image{flex:none}.shake-sidebar-editor-block .editor-image .avatar--img{display:block;width:var(--size-avatar-default)}.shake-sidebar-editor-block .editor-details{flex:1;margin-left:var(--size-spacing-default)}.sidebar-flag-nsfw{margin-top:var(--size-spacing-triple)}.sidebar-flag-nsfw .flag-nsfw-button,.sidebar-flag-nsfw .unflag-nsfw-button{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-danger-pastel);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-danger-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .flag-nsfw-button:hover,.sidebar-flag-nsfw .unflag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:hover{color:var(--color-text-light-emphasis)}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:focus{outline:none}.sidebar-flag-nsfw .flag-nsfw-button:disabled,.sidebar-flag-nsfw .unflag-nsfw-button:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.sidebar-flag-nsfw .flag-nsfw-button:disabled,.sidebar-flag-nsfw .unflag-nsfw-button:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .flag-nsfw-button:hover,.sidebar-flag-nsfw .unflag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.flag-image{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-warning-pastel);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-283.8 415 7 9'%3E%3Cpath fill='red' d='M-282.8 415h6v5h-6z'/%3E%3Cpath fill='none' d='M-289 370.5h32.2'/%3E%3Cpath fill='%23695a5a' d='M-283.8 415h1v9h-1z'/%3E%3C/svg%3E");background-position:.5em;background-repeat:no-repeat;background-size:7px 9px;border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-warning-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;margin-top:var(--size-spacing-triple);padding:0 .88em 0 1.5em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.flag-image:focus,.flag-image:hover{color:var(--color-text-light-emphasis)}.flag-image:focus{outline:none}.flag-image:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.flag-image:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.flag-image:focus,.flag-image:hover{background-color:var(--color-background-button-warning-pastel-hover);color:var(--color-text-button-warning-pastel-hover)}.flag-image.flag-image-set{color:var(--color-status-danger)}.delete-post-text{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-danger-pastel);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-danger-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;margin-top:var(--size-spacing-triple);padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.delete-post-text:focus,.delete-post-text:hover{color:var(--color-text-light-emphasis)}.delete-post-text:focus{outline:none}.delete-post-text:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.delete-post-text:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.delete-post-text:focus,.delete-post-text:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.sidebar-stats{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;font-weight:var(--number-font-weight-bold);margin-top:var(--size-spacing-triple)}.sidebar-stats,.sidebar-stats .tab{padding:var(--size-spacing-default)}.sidebar-stats .tab{display:block}.sidebar-stats .selected .tab{background-color:var(--color-background-content);border-top-left-radius:var(--size-border-radius-large);border-top-right-radius:var(--size-border-radius-large)}.sidebar-stats .enable-cursor{cursor:pointer}.sidebar-stats-tabs{display:flex;list-style:none;margin:0;padding:0}.sidebar-stats-tabs>*{flex:1}.sidebar-stats-views{color:var(--color-page-text-secondary)}.sidebar-stats-saves{color:var(--color-status-warning)}.sidebar-stats-hearts{color:var(--color-status-danger)}.sidebar-stats-content{background-color:var(--color-background-content);border-bottom-left-radius:var(--size-border-radius-large);border-bottom-right-radius:var(--size-border-radius-large);display:none;padding:var(--size-spacing-half-again)}.sidebar-stats-content .user-action{align-items:center;display:flex}.sidebar-stats-content .user-action+.user-action{margin-top:var(--size-spacing-default)}.sidebar-stats-content .icon{margin-right:var(--size-spacing-half)}.sidebar-stats-content .icon .avatar--img{display:block;height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.sidebar-stats-content .name{word-wrap:break-word;color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.sidebar-stats-content .name:active,.sidebar-stats-content .name:focus,.sidebar-stats-content .name:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.sidebar-stats-content .name:active,.sidebar-stats-content .name:focus,.sidebar-stats-content .name:hover{text-decoration:underline}.sidebar-stats-content .date{color:var(--color-page-text-secondary);font-size:.75rem;font-weight:400;margin-left:var(--size-spacing-default);white-space:nowrap}.meta-data{margin-top:var(--size-spacing-triple)}.meta-data h4{font-size:.875rem;padding-left:var(--size-spacing-half-again)}.meta-data h4,.meta-data p{color:var(--color-page-text-secondary)}.meta-data p{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-default);font-size:.75rem;margin:var(--size-spacing-half) 0;padding:var(--size-spacing-default) var(--size-spacing-half-again)}.shake-details-title{color:var(--color-page-text-secondary);font-size:.875rem;margin-bottom:var(--size-spacing-half);margin-top:var(--size-spacing-triple);padding-left:var(--size-spacing-half-again)}.in-these-shakes{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;margin:0 0 var(--size-spacing-triple) 0;padding:var(--size-spacing-half-again)}.in-these-shakes ul{list-style:none;margin:0;padding:0}.in-these-shakes li{align-items:center;display:flex;margin-bottom:var(--size-spacing-default)}.in-these-shakes a{word-wrap:break-word;color:var(--color-text-link-primary);flex:1;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.in-these-shakes a:active,.in-these-shakes a:focus,.in-these-shakes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.in-these-shakes a:active,.in-these-shakes a:focus,.in-these-shakes a:hover{text-decoration:underline}.in-these-shakes .delete-from-shakes-form,.in-these-shakes .input-checkbox{flex:none;margin-right:var(--size-spacing-default)}.permalink-social{display:flex;list-style:none;margin:0;padding:0}.permalink-social li{flex:none}.permalink-social li+li{margin-left:var(--size-spacing-default)}.permalink-social a{display:block}.permalink-social a:focus,.permalink-social a:hover{opacity:.66}.permalink-social img{display:block}.permalink-social .tumblr a{background-color:#001935;display:flex;height:21px;width:21px}.permalink-social .tumblr a img{height:12px;margin:auto;width:9px}.site-footer{color:var(--color-page-text-secondary);font-size:.75em;margin:var(--size-spacing-triple);text-align:center}.site-footer p{margin:0}.site-footer p+p{margin-top:.5em}.site-footer a{white-space:nowrap}.site-header{display:flex}.site-branding{margin-right:1em;margin-top:1em;width:211px}.site-branding a{display:block}.site-branding a:focus:not(:focus-visible){box-shadow:none}.site-branding--logo{display:block;height:auto;width:100%}.site-nav{flex:1;margin-top:69px;position:relative;text-align:right}.site-nav--list{display:none;flex-direction:column;list-style:none;margin:0;padding:0;position:absolute;right:0;top:47px;z-index:99}.site-nav.is-expanded .site-nav--list{display:flex}@media screen and (min-width:768px){.site-nav--list{display:flex;flex-direction:row;position:static}}.site-nav--item{display:flex;flex:none;justify-content:flex-end}.site-nav--item>*{flex:1}@media screen and (min-width:768px){.site-nav--item>*{flex:none}}.site-nav--item+.site-nav--item{margin-top:.25em}@media screen and (min-width:768px){.site-nav--item+.site-nav--item{margin-left:1em;margin-top:0}}@media screen and (min-width:768px){.site-nav--toggle{display:none}.site-nav--signup,.site-nav--upload{flex-grow:1}}.site-nav--conversations a,.site-nav--popular a,.site-nav--search a{display:block}.site-nav--signup .call-out{color:var(--color-page-text-secondary);display:none;font-size:.875rem;font-weight:var(--number-font-weight-bold);max-width:18em;padding-right:var(--size-spacing-default);padding-top:3px}@media screen and (min-width:768px){.site-nav--signup .call-out{display:block}}.user-counts{background-color:var(--color-border-default);border-radius:var(--size-border-radius-large);clear:both;margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.user-counts ul{display:flex;justify-content:space-between;list-style:none;margin:0;padding:0}.user-counts li{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);flex-grow:1;flex-shrink:1;margin-right:var(--size-spacing-default);padding:var(--size-spacing-default) 0;text-align:center;width:70px}.user-counts li .num{display:block;font-size:1.125rem;font-weight:var(--number-font-weight-bold)}.user-counts li .label{display:block;font-size:.875rem}.user-counts .views{width:95px}.user-counts .saves{color:var(--color-status-warning)}.user-counts .likes{color:var(--color-status-danger);margin-right:0}.user-follow{display:flex}.user-follow .icon{flex:none;margin-right:var(--size-spacing-default)}.user-follow h4{word-wrap:break-word;font-size:.875rem;margin-bottom:var(--size-spacing-half);overflow-wrap:break-word;word-break:break-word}.user-follow h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);padding-bottom:var(--size-spacing-half);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.user-follow h4 a:active,.user-follow h4 a:focus,.user-follow h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.user-follow h4 a:active,.user-follow h4 a:focus,.user-follow h4 a:hover{text-decoration:underline}.user-follow .follow{flex:1}.user-follow-extended{display:block;margin:var(--size-spacing-double) var(--size-spacing-half)}.user-follow-extended .icon{float:left;margin-right:var(--size-spacing-default)}.user-follow-extended .details{padding-bottom:var(--size-spacing-default)}.user-follow-extended h4 a{font-size:1.125rem}.user-follow-extended .about{display:block;font-size:.875rem;line-height:1.3;margin-bottom:var(--size-spacing-default);white-space:pre-wrap}.user-follow-extended .about,.user-follow-extended .website a{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.user-follow-extended .website a{color:var(--color-text-link-primary);font-size:.8rem;font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.user-follow-extended .website a:active,.user-follow-extended .website a:focus,.user-follow-extended .website a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.user-follow-extended .website a:active,.user-follow-extended .website a:focus,.user-follow-extended .website a:hover{text-decoration:underline}.user-follow-extended:after{clear:both;content:"";display:table}.user-nav{background-color:var(--color-background-content);border-bottom-left-radius:1em;font-size:.9rem;padding:.75em 1.5em;position:absolute;right:0;top:0}.user-nav--list{display:flex;list-style:none;margin:0;padding:0}.user-nav--item+.user-nav--item{margin-left:1.5em}.user-nav--link{font-weight:var(--number-font-weight-bold);text-decoration:none;white-space:nowrap}.admin-nav{list-style:none;margin:0;padding:0}.admin-new-users{border:2px solid var(--color-status-danger)}.admin-new-users .body{padding:var(--size-spacing-triple)}.api-accept,.api-decline{float:left;margin-right:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.content-developer{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-developer{padding:var(--size-spacing-quadruple)}}.content-developer h1{font-size:3.25rem}.content-developer h2{font-size:1.5rem;margin-bottom:var(--size-spacing-half-again);margin-top:var(--size-spacing-half-again)}.content-developer p{line-height:1.4;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-default)}.content-developer dt big{font-size:normal}.content-developer dt big,.content-developer dt em{color:var(--color-page-text-secondary)}.faq-page h1{font-size:2.5rem}.faq-page h2{font-size:1.25rem;margin-top:var(--size-spacing-half-again)}.faq-page li,.faq-page p{font-size:.875rem;line-height:2;margin-top:5px}.faq-page li a,.faq-page p a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.faq-page li a:active,.faq-page li a:focus,.faq-page li a:hover,.faq-page p a:active,.faq-page p a:focus,.faq-page p a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.faq-page li a:active,.faq-page li a:focus,.faq-page li a:hover,.faq-page p a:active,.faq-page p a:focus,.faq-page p a:hover{text-decoration:underline}.content-find-shakes .header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));padding:1.5rem 1.875rem 1rem;width:100%}.content-find-shakes .header .avatar{display:flex}.content-find-shakes .header .avatar img{display:block;height:50px;width:50px}.content-find-shakes .header .avatar-media{flex:none}.content-find-shakes .header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.content-find-shakes .header h2{font-size:2.25rem}}@media screen and (min-width:768px){.content-find-shakes .header h2{font-size:2.625rem}}.content-find-shakes .body{padding:var(--size-spacing-double)}.content-find-shakes .good-folk-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-half-again)}.content-find-shakes .good-folk-block h3{color:var(--color-status-success-pastel-dark);font-size:1.125rem;padding-left:var(--size-spacing-half)}.content-find-shakes .good-folk-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);color:var(--color-page-text);margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.content-find-shakes .good-folk-block-content p{font-size:.875rem;margin-top:0}.content-find-shakes .good-folk-block-content .user-follow{margin-top:var(--size-spacing-double)}.content-find-shakes .find-shakes-navigation{display:flex;list-style:none;margin:0;padding:0}.content-find-shakes .find-shakes-navigation li{background:var(--color-background-content-secondary);flex:1;font-size:1.125rem}.content-find-shakes .find-shakes-navigation li a{display:block;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-double) var(--size-spacing-half);text-align:center;text-decoration:none}.content-find-shakes .find-shakes-navigation .selected a{background-color:var(--color-background-content);color:var(--color-page-text)}.content-find-shakes .featured-shakes{margin-bottom:var(--size-spacing-double)}.content-find-shakes .featured-shakes h3{font-size:1.125rem;margin:var(--size-spacing-double) var(--size-spacing-default)}.content-find-shakes .featured-shakes ul{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.content-find-shakes .featured-shakes li{border:1px solid var(--color-border-default);box-shadow:1px 2px 1px var(--color-base-black-transparent-100);margin-bottom:var(--size-spacing-default);margin-right:var(--size-spacing-default);padding:var(--size-spacing-default);width:calc(172px + var(--size-spacing-default)*2)}.content-find-shakes .featured-shakes li img{height:170px;width:170px}.content-find-shakes .featured-shakes li h4{font-size:1.125rem;line-height:1.2;margin:var(--size-spacing-default) 0 0}.content-find-shakes .featured-shakes li h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.content-find-shakes .featured-shakes li h4 a:active,.content-find-shakes .featured-shakes li h4 a:focus,.content-find-shakes .featured-shakes li h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.content-find-shakes .featured-shakes li h4 a:active,.content-find-shakes .featured-shakes li h4 a:focus,.content-find-shakes .featured-shakes li h4 a:hover{text-decoration:underline}.content-find-shakes .featured-shakes li p{-webkit-box-orient:vertical;-webkit-line-clamp:3;display:-webkit-box;font-size:.75rem;margin:var(--size-spacing-default) 0 0;overflow:hidden}.content-find-shakes .shake-category{border-bottom:1px dashed var(--color-border-default);clear:both}.content-find-shakes .shake-category .category-title{font-size:1.125rem}.content-find-shakes .shake-category .category-title a{display:block;padding:var(--size-spacing-half-again) var(--size-spacing-default);text-decoration:none}.content-find-shakes .shake-category .shake-category-body{display:none;padding:0 var(--size-spacing-default)}.content-find-shakes .shake-category.shake-category-selected .category-title a{color:var(--color-page-text-secondary)}.content-find-shakes .shake-category.shake-category-selected .shake-category-body{display:block}.content-find-shakes .shake-tips{background:var(--color-background-content-secondary);clear:both;color:var(--color-page-text);font-size:.75rem;margin:var(--size-spacing-triple) 0;padding:var(--size-spacing-half-again)}.content-find-shakes .friend+.friend{border-top:1px dashed var(--color-border-default)}.content-find-shakes .user-follow-extended .website a{color:var(--color-text-link)}.content-find-shakes .user-follow-extended .website a:active,.content-find-shakes .user-follow-extended .website a:focus,.content-find-shakes .user-follow-extended .website a:hover{color:var(--color-text-link-hover)}.content-find-shakes .body .loading{color:var(--color-page-text-secondary);font-size:1.125rem;margin:var(--size-spacing-quadruple) auto;text-align:center}.content-find-shakes .body .loading img{margin-right:var(--size-spacing-double);position:relative;top:var(--size-spacing-double)}.content-find-shakes .message{color:var(--color-page-text-secondary);font-size:1.125rem;margin:var(--size-spacing-quadruple) var(--size-spacing-triple);text-align:center}.content-find-shakes .intro{border-bottom:1px solid var(--color-border-default);font-size:.875rem;padding:var(--size-spacing-double) var(--size-spacing-half)}.content-find-shakes .refresh-friends{margin:var(--size-spacing-triple) 0}.content-incoming .tip-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.content-incoming .tip-block h3{color:var(--color-brand-primary);font-size:1.3rem;margin:0 0 var(--size-spacing-half-again)}.content-incoming .tip-block p{color:var(--color-page-text-secondary);font-size:.875rem;margin:0}.incoming-header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));cursor:pointer;padding:1.5rem 1.875rem 1rem;padding:var(--size-spacing-half-again) var(--size-spacing-double);width:100%}.incoming-header .avatar{display:flex}.incoming-header .avatar img{display:block;height:50px;width:50px}.incoming-header .avatar-media{flex:none}.incoming-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.incoming-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.incoming-header h2{font-size:2.625rem}.incoming-header{align-items:center;display:flex;flex-direction:row}}.incoming-header:before{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 786 229'%3E%3Cg fill='none' transform='translate(-.002)'%3E%3Cpath fill='%23FF0' d='M142.262 137.93s66.31-14 66.31-40 5.62-64 41.86-64c10.67 0 41-7.41 105.24-9 154-3.81 379-4 379-4s51 10 51 48 6 116-51 116-270 5-323 5-147.5 6.5-154 0c-3.36-3.36-11.86-3.3-23.18-13-10.56-9-24.1-35-24.1-35s-12.08 8.46-32.37 7c-27.82-2-35.76-11-35.76-11Z'/%3E%3Cpath fill='%23926F35' d='M140.322 186.17c0 6.73-.16 17.42-.06 33.21h-10.74v-8.13c-.06-1.71-.06-2.07-.06-3.64 0-2.91-.79-5-4.68-4.95-4.19 0-4.05 3.51-4.09 6.71v10h-11.38c.1-15.74.23-27.25.28-34'/%3E%3Cpath fill='%23000' d='M103.762 228.69s-.55-4.54 3.59-5.14v-7.31s-.26-2.77 2-2.88h11.38s1.86.21 1.86 2.89v7.63s1.15 1.26.93 4.82m22.48-.01s.56-4.54-3.59-5.14v-7.31s.26-2.77-2-2.88h-11.41s-1.86.21-1.86 2.89v7.63s-1.15 1.26-.93 4.82'/%3E%3Cpath fill='%2300D4DE' d='M140.752 205.49c9.42-1.15 16-4.29 21.52-8.84 18.66-15.39 21.61-49 20.71-77.52 0-43.37-15-76.79-59.16-76.21-44.21-.58-59 33.35-59 79.28-.68 36 6.9 77.14 45.91 83.19.04 0 15.51 2.3 30.02.1Z'/%3E%3Cpath fill='%2300F2F2' d='M117.642 205.39c-39-6.05-44.26-53.55-44.79-83.19-.49-27.57 3.7-79.25 54.42-79.25h-3.46c-44.21-.58-59 33.35-59 79.28-.68 36 6.9 77.14 45.91 83.19a122.92 122.92 0 0 0 19.52 1 118.24 118.24 0 0 1-12.6-1.03Z'/%3E%3Cellipse cx='126.332' cy='135.97' fill='%23FF3560' rx='32.12' ry='23.45'/%3E%3Cpath fill='%23E31451' d='M126.332 139.15a21 21 0 0 0-20.09 14.3 36.81 36.81 0 0 0 40.19 0 21 21 0 0 0-20.1-14.3Z'/%3E%3Cpath fill='%2300F2F2' d='M131.892 205.39c39-6.05 49.79-47.16 49.11-83.19 0-44.73-10.31-78-58.75-79.25h3.46c44.21-.58 59 33.35 59 79.28.68 36-6.9 77.14-45.91 83.19a122.93 122.93 0 0 1-19.52 1 118.29 118.29 0 0 0 12.61-1.03Z'/%3E%3Cpath fill='%23B3C535' d='M71.002 182.25s14 9.62 44 10.33a11.15 11.15 0 0 1 6.56 2.85c2.21 2.16 3.42 6.14 3.42 11.4l-50.7 5.38s-.36-17.14-3.28-29.96Z'/%3E%3Cpath fill='%23B3C535' d='M178.002 183.69s-12.24 7.69-44.66 8.89a11.15 11.15 0 0 0-6.55 2.85c-2.21 2.16-3.42 6.14-3.42 11.4l50.7 5.38s1.01-15.7 3.93-28.52Z'/%3E%3Cpath fill='%23FFF' d='M117.482 110.43s-34.5.16-38.58 37.87c0 0-1.45 5.45 1.62 12.86 0 0 3.37 6.33 7 1.28 0 0 4 7.58 7.1.92 0 0 4 2.25 1.79-5 0 0-7.12-14.63-1.94-28.91.03 0 3.41-12.71 23.01-19.02Zm18.07-.56s34.5.16 38.58 37.87c0 0 1.45 5.45-1.62 12.86 0 0-3.37 6.33-7 1.28 0 0-4 7.58-7.1.92 0 0-4 2.25-1.79-5 0 0 7.12-14.63 1.94-28.91-.02 0-3.41-12.71-23.01-19.02Z'/%3E%3Cpath fill='%23FF3560' d='M116.312 136.27c-2.52 2.65-4.43 5.27-9 6.78 0 0-15.48 4.21-11-11.27 0 0 6.28-20.63 31.85-20.63 0 0 22.43.45 27.59 20.63 0 0 2 8.07-2 10.77 0 0-8.07 6.06-13.91-3.36-1.15-2.13-4.73-6.16-6.62-7.18a12.41 12.41 0 0 0-11.66-.22 30.6 30.6 0 0 0-5.25 4.48Z'/%3E%3Cpath fill='%23FFF' d='M132.482 111.56a25.8 25.8 0 0 0-4.28-.51c-11.14 0-18.61 3.91-23.5 8.33a4.42 4.42 0 0 0 3.56 2.2c2.1 0 3.9-1.92 4.69-4.66.79 2.74 2.59 4.66 4.69 4.66 2.28 0 4.21-2.27 4.88-5.4.66 3.13 2.59 5.4 4.88 5.4 2.45 0 4.49-2.61 5-6.1.51 3.49 2.55 6.1 5 6.1 2.16 0 4-2 4.75-4.88.76 2.86 2.6 4.88 4.75 4.88a4 4 0 0 0 2.78-1.24c-5.43-5.75-12.68-7.95-17.2-8.78Z'/%3E%3Cpath fill='%23FFD265' d='m78.782 201.24 3.52-4.83 3.46 4.83-3.49-2.41z'/%3E%3Cpath fill='%233FAEB1' d='m76.622 194.6 3.15 3.54-.99 3.1 3.51-1.36 3.47 1.36-1.08-3.12 3.17-3.48-11.23-.04z'/%3E%3Cpath fill='%23FFEB6D' d='m82.272 190.49 1.34 4.11h4.31l-3.49 2.54 1.33 4.1-3.49-2.54-3.49 2.54 1.33-4.1-3.49-2.54h4.32l1.33-4.11z'/%3E%3Cpath fill='%23FFD265' d='m76.622 194.6 5.59 1.81-2.1.73-3.49-2.54Zm11.24 0-5.59 1.81 2.1.73 3.49-2.54Zm76.44 6.64 3.52-4.83 3.46 4.83-3.49-2.41-3.49 2.41Z'/%3E%3Cpath fill='%233FAEB1' d='m162.152 194.6 3.14 3.54-.99 3.1 3.51-1.36 3.47 1.36-1.08-3.12 3.18-3.48-11.23-.04z'/%3E%3Cpath fill='%23FFEB6D' d='m167.792 190.49 1.34 4.11h4.31l-3.49 2.54 1.33 4.1-3.49-2.54-3.49 2.54 1.34-4.1-3.49-2.54h4.31z'/%3E%3Cpath fill='%23FFD265' d='m162.152 194.6 5.59 1.81-2.1.73-3.49-2.54Zm11.24 0-5.6 1.81 2.11.73 3.49-2.54Z'/%3E%3Cpath fill='%2300F2FF' d='M181.842 153.94a26.74 26.74 0 0 0 19.29-15.09s5.48-.26 7.44 2.87c0 0-8.23 20-29.86 19.72'/%3E%3Cpath fill='%23000' d='M207.802 142.39a6.86 6.86 0 0 0 2.58-5.15c.29-4.31-4.31-4.59-4.31-4.59.57-4.83-1-5.1-1-5.1a1.15 1.15 0 0 0-1.57.72 8.1 8.1 0 0 0-.37 2.18c-.08 1.2-.21 2.51-.37 3.49a28 28 0 0 1-1.28 4.88s-.53 1.68 2.16 2.88a8 8 0 0 0 4.16.69Z'/%3E%3Cpath fill='%2300F2FF' d='M70.552 162s-11.1 1.18-17.24 14.5c0 0-5.48.26-7.44-2.87 0 0 8.23-20 29.86-19.72'/%3E%3Cpath fill='%23000' d='M53.912 175.42s0 5.37-4.3 5.08c0 0-4.69.1-5-3.91a3.36 3.36 0 0 1 2.15-3.22 14.39 14.39 0 0 1 7.15 2.05Z'/%3E%3Cpath fill='%23FF0180' d='M252.552 151.62 249.602 82l15-.63 3 69.62-15.05.63Zm56.39-72.13 14-.59 2.56 60.37c.31 7.27-3.73 10.36-9 10.58-3.74.16-6.65-2.11-8.46-5.47l-20-37.87 1.82 43.49-14 .59-2.56-60.37c-.31-7.27 3.72-10.36 9-10.58 4.78-.2 7.58 2 9.28 5.23l19.11 35.83-1.75-41.21Zm59.43-2.49 4.16-.18.6 14.24-4.36.19c-13.72.58-22.59 9.91-22.09 21.55s10.12 20.18 23.84 19.6l4.36-.19.6 14.24-4.16.18c-21.41.91-38.73-12.41-39.61-33.19-.88-20.78 15.29-35.56 36.66-36.44Zm44.01-2.83a35.78 35.78 0 1 1-34.23 37.26c-.835-19.74 14.49-36.422 34.23-37.26Zm2.4 56.53a20.8 20.8 0 1 0-21.66-19.9c.535 11.452 10.204 20.33 21.66 19.89v.01Zm69.08 6.54-13.16-35.56 1.72 40.63-14 .59-2.53-59.64c-.32-7.59 4.94-11.14 9.51-11.33 5.61-.24 9.32 3.67 11.2 8.89l13.19 36.08 10.09-37.07c1.44-5.37 4.8-9.57 10.41-9.81 4.57-.19 10.12 2.9 10.44 10.49l2.53 59.64-14 .59-1.72-40.63-10.11 36.55c-1.18 4.11-3.51 5.67-6.52 5.79-3.01.12-5.52-1.21-7.05-5.21Zm47.68 2.56-3-69.62 15-.63 3 69.62-15 .63Zm56.38-72.14 14-.59 2.56 60.37c.31 7.27-3.72 10.36-9 10.58-3.74.16-6.65-2.11-8.46-5.47l-20.02-37.87 1.85 43.54-14 .59-2.56-60.37c-.31-7.27 3.73-10.36 9-10.58 4.78-.2 7.58 2 9.28 5.23l19.11 35.83-1.76-41.26Zm63.99 11.55-5.4.23c-9.87.42-21.67 6.85-21 22.33a19.57 19.57 0 0 0 20.37 18.92l-1.25-28.89 15-.63 1.32 31.07c.25 6-.94 12.12-10.61 12.52l-2.49.11c-19.85.84-36.27-13.35-37.09-32.57-.86-20.26 13.46-36.17 35.9-37.12l4.68-.2.57 14.23Zm17.85 33.73-2.35-55.35 15.38-.65 2.35 55.35-15.38.65Zm7.92 3.89a8.893 8.893 0 1 1 .75 17.77 8.893 8.893 0 0 1-.75-17.77Zm18.96-5.02-2.34-55.36 15.38-.65 2.32 55.35-15.36.66Zm7.92 3.89a8.893 8.893 0 1 1 .75 17.77 8.893 8.893 0 0 1-.75-17.77Zm18.96-5.03-2.35-55.35 15.38-.65 2.35 55.33-15.38.67Zm7.92 3.89a8.893 8.893 0 1 1 .75 17.77 8.893 8.893 0 0 1-.75-17.77Z'/%3E%3Cpath fill='%23FF0' d='M184.692 123.52c-1.2.6-2.417 1.18-3.65 1.74-18.22 8.33-38.77 12.67-38.77 12.67s7.93 9 35.76 11l1.16.07c1.42.08 3.66.1 5 .09 1.11-8.44.61-17.09.5-25.57Z'/%3E%3Cpath fill='%23FFF' d='m160.262 58-3.2 2.6a28 28 0 0 1 7.58 13.22l4.83-.75a30.91 30.91 0 0 0-9.21-15.07Zm-.78-.7a38.33 38.33 0 0 0-12.08-7.11l-1.5 2.56a41.31 41.31 0 0 1 10.42 7.12l3.16-2.57Zm-6.7 6.7a26.88 26.88 0 0 1 5.22 10.82l5.58-.86a27.08 27.08 0 0 0-7.36-12.72l-3.44 2.76Zm-7.42-10.43-1.65 2.81a29.23 29.23 0 0 1 8.41 6.82l3.37-2.74a40.24 40.24 0 0 0-10.13-6.84v-.05Z'/%3E%3Cpath fill='%2300D4DE' d='M227.172 69.06c23.45-11.72 40.37-33.56 1-69.06 0 0 30.75 45.87-53.94 46.38v31.25s30.77 2.52 52.94-8.57Z'/%3E%3Cpath fill='%2300B7DD' d='m236.882 63.34-11.22 5.3 11.59-19.16-18.93 9.06 17.42-17.63-18.43 9.55 11.88-15.09-5.6.6c19.41-13.69 4.59-35.89 4.59-35.89 33.6 30.31 26.18 50.66 8.7 63.26Z'/%3E%3Cpath fill='%2300D4DE' d='M24.522 69.06C1.072 57.34-15.848 35.5 23.512 0c0 0-30.75 45.87 53.94 46.38v31.25s-30.75 2.52-52.93-8.57Z'/%3E%3Cpath fill='%23000' d='m66.392 112.27.06.13.08.13c.2.32 5 7.91.57 13.94l4.9 3.6a15.56 15.56 0 0 0 3-9 22.83 22.83 0 0 0-3.2-11.67 21.43 21.43 0 0 1-2.49-10.4 17.31 17.31 0 0 1 1.58-7.11L65.482 89c-.23.46-5.59 10.71.91 23.27Zm116.89 12.11.07.13.08.13c.2.32 5 7.91.57 13.94l4.94 3.61a15.56 15.56 0 0 0 3-9 22.81 22.81 0 0 0-3.29-11.68 21.44 21.44 0 0 1-2.49-10.37 17.24 17.24 0 0 1 1.58-7.11l-5.4-2.89c-.19.44-5.56 10.69.94 23.24Z'/%3E%3Cpath fill='%23B3C535' d='M193.932 99c-3.19-6.9-2.36-17.26-2.06-22.46.82-14.31-.62-21.19-3.75-31.85-4.62-15.8-22.59-24.69-40.5-28.07-12.57-2.39-30.6-2.39-45.34 0-17.99 2.92-33.92 13.38-40.51 28.09-3.32 7.44-4.35 20.74-3.77 31.85.3 5.19.92 18.21-2 22.44a15 15 0 0 1-5.36 4.77c10.5.6 21.46 0 32-.35 14.1-.45 70.53-.45 84.63 0 10.56.34 21.52.94 32 .35a9.64 9.64 0 0 1-5.34-4.77Z'/%3E%3Cpath fill='%2300B7DD' d='m14.812 63.34 11.19 5.3-11.56-19.16 21.17 14.62-19.61-23.19 21.63 9.21-15.13-14.75 5.6.6C8.642 22.28 23.512.08 23.512.08c-33.6 30.31-26.18 50.66-8.7 63.26Z'/%3E%3Cpath fill='%23E3EB9A' d='m171.582 27.84-4.15 3.72c7.13 4.88 12 10.73 14.62 17.32a29 29 0 0 0 3.29-5.57c-2.71-6.31-7.58-11.31-13.76-15.47Zm-5 3.14 4.14-3.71a57.28 57.28 0 0 0-17.35-7.77c-1.59.76-4.36 2-5.84 2.8a78.09 78.09 0 0 1 16.11 6.86 32.665 32.665 0 0 1 2.93 1.84l.01-.02Zm.1 1.27-4.68 4.19c7.35 5.07 12.76 11.23 15.49 18a18.65 18.65 0 0 0 3.9-4.72c-2.45-6.66-7.39-12.52-14.72-17.48l.01.01Zm-3.54-2.25a77.53 77.53 0 0 0-16.91-7c-2.19 1.15-4.25 2.28-6.13 3.35a77.56 77.56 0 0 1 21 9.54l4.67-4.19a64.087 64.087 0 0 0-2.63-1.7Z'/%3E%3Cpath fill='%23CADE35' d='M81.672 25.89s-20.19 10.77-20.19 37c0 0-.34 19.86 0 24.23 0 0 1 10.77-4 14.14 0 0 7.74-2 7.74-15.82V63.59s.29-25.24 16.45-37.7Z'/%3E%3Cpath fill='%2300F2F2' d='M17.252 8S.892 24.81 1.432 39.33c0 0-.67 11.11 13.13 22.21 0 0-10.92-9.57-10.77-22.89 0 .01-.47-12.19 13.46-30.65Zm217.36 0s16.39 16.81 15.81 31.33c0 0 .67 11.11-13.13 22.21 0 0 10.92-9.57 10.77-22.89.01.01.47-12.19-13.45-30.65Z'/%3E%3Ccircle cx='143.162' cy='81.55' r='10.39' fill='%23FFF'/%3E%3Ccircle cx='144.162' cy='79.55' r='3.23' fill='%23ED4928'/%3E%3Ccircle cx='102.592' cy='81.55' r='10.39' fill='%23FFF'/%3E%3Ccircle cx='103.592' cy='79.55' r='3.23' fill='%23ED4928'/%3E%3C/g%3E%3C/svg%3E") 50% no-repeat;background-size:100%;content:"";display:block;margin:auto;max-width:100%;padding-top:29%}@media screen and (min-width:480px){.incoming-header:before{margin:0;padding-top:115px;width:395px}}.incoming-header h2{font-size:1.5rem;line-height:1.2;margin-top:var(--size-spacing-half-again);padding-left:0;text-align:center}@media screen and (min-width:768px){.incoming-header h2{font-size:1.75rem;margin-top:0;padding-left:var(--size-spacing-default)}}.code-of-conduct h2{font-size:1.875rem;text-align:center}.code-of-conduct h3{font-size:1.5rem;margin-top:1.4em}.code-of-conduct p{font-size:1rem;line-height:1.4;margin:1em 0}.code-of-conduct li{margin-bottom:.4em}.terms-of-use p{font-size:1rem;line-height:1.4;margin:1em 0}.terms-of-use li{font-size:1rem;list-style-type:lower-alpha;margin-left:var(--size-spacing-quadruple)}.terms-of-use .terms-center{text-align:center}.tou-notice-page p{font-size:1rem;line-height:1.4;margin:1em 0}.content-membership{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-membership{padding:var(--size-spacing-quadruple)}}.content-membership h1{line-height:1;text-shadow:2px 4px 1px var(--color-base-black-transparent-100)}.content-membership ul{padding-left:1em}.content-membership li+li{margin-top:1em}.content-membership .fine-print{font-size:.75rem}.content-membership .subscribe-plan-quantity-wrapper{color:var(--color-page-text-secondary);font-size:2rem}.content-membership .input-plan-quantity{display:inline-block;font-size:inherit;font-weight:var(--number-font-weight-bold);max-width:150px;width:auto}.content-membership .input-plan-quantity:invalid{color:var(--color-status-danger)}.content-membership .subscribe-plan-quantity-wrapper i{color:var(--color-page-text-secondary);font-size:1.5rem;font-style:normal}.content-migrate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 732 961'%3E%3Cg fill='none' transform='translate(-.003 -.005)'%3E%3Cpath fill='%23C2E3AE' d='M123.803 229.565c16.75 1.23 32.6 7.32 45.13 20.1 34.36 35 35.75 97.29 35.75 142.88 0 23 8.43 85.06-26.79 87.53 19 4.86 32.91 22.67 47 35.1a282.7 282.7 0 0 0 39.4-43.76c20.18-27.82 25-60.21 23.2-93.78-1.3-24.71-4.42-61.13-28.5-75.34-35-20.64-76.66-71.73-61.81-144.31 26.82-130.98 181.09-207.27 288.36-110.04 61.17 55.44 30.16 145.86-27.2 192.54-65 52.9-138.55 66.14-138.55 66.14a352 352 0 0 1 5.55 76.27c-3.29 85.7-74.56 158.94-129.18 218.78-11.66 12.77-31 30.37-30.71 49.62.49 27.64 33.78 38.32 56.79 35.23 17.71-2.38 59.46-15.84 67.7-33.71-11.86 25.72-34.42 43.56-62.41 48.32-25.57 4.35-62.75-1.06-75.2-27.48-18.41-39.12 32.67-98.65 51.29-130.86 2.33-4-20.21-26.2-23.2-29.16-6.24-6.16-27.91-23.6-37.55-20.18 0 0-126.18 21.14-135.77-52.44-9.59-73.58-3.34-149.79 29.2-179.32 21.7-19.69 56.19-34.43 87.5-32.13Z'/%3E%3Cpath fill='%23BEE6F8' d='M434.703 489.865s-115.1-3.09-97-118.24c5.88-37.34 31-62.83 67.4-71.48 34-8.09 83-6.85 114.39 8.7 38.84 19.26 63.78 60.12 57.33 103.84-5.61 38-37.46 80.38-79.26 80.38 0 0 36.86 53.88 75.67 12.79 28.44-30.09 29.29-77.85 25.77-116.5-1.43-15.66-5.88-89.71-25.47-93.18-48-8.51-86.38-30-98.19-81-9.34-40.52-8.24-109.17 33.52-130.34 66.36-33.68 204.26-6.27 221.14 77.65 9.77 48.25-40.37 141.52-100.27 130.22 0 0 27.82 101.83 1.07 167.88-9.8 24.08-23.74 49.42-44.47 66.07.34-.27 5.41 26.55 5.47 28.1 1.33 36.41-13.65 78.82-47.05 97.29-36.23 20-78.63 15.5-115.68 1.72 33.16-2 64.6-16.64 95.15-28.46 23.64-9.14 38.34-24.9 42.13-50.65.44-3-1.78-35.11-4.49-34.64-2.93.51-6.62 2.08-9.88 3.32-28.59 10.88-48.78.21-72.62-17.77-9.31-7-18.24-14.64-27.35-21.91-3.15-2.5-13.17-13.65-17.31-13.79Z'/%3E%3Cpath fill='%23DAFFC2' d='M120.803 225.555c16.75 1.23 32.6 7.32 45.13 20.1 34.36 35 35.75 97.29 35.75 142.88 0 23 8.43 85.06-26.79 87.53 19 4.86 32.91 22.67 47 35.1a282.7 282.7 0 0 0 39.4-43.76c20.18-27.82 25-60.21 23.2-93.78-1.3-24.71-4.42-61.13-28.5-75.34-35-20.64-76.66-71.73-61.81-144.31 26.82-130.97 181.09-207.26 288.36-110.03 61.17 55.44 30.16 145.86-27.2 192.54-65 52.9-138.55 66.14-138.55 66.14a352 352 0 0 1 5.55 76.27c-3.29 85.7-74.56 158.94-129.18 218.78-11.66 12.77-31 30.37-30.71 49.62.49 27.64 33.78 38.32 56.79 35.23 17.71-2.38 59.46-15.84 67.7-33.71-11.86 25.72-34.42 43.56-62.41 48.32-25.57 4.35-62.75-1.06-75.2-27.48-18.41-39.12 32.67-98.65 51.29-130.86 2.33-4-20.21-26.2-23.2-29.16-6.24-6.16-27.91-23.6-37.55-20.18 0 0-126.18 21.14-135.77-52.44-9.59-73.58-3.35-149.8 29.2-179.33 21.7-19.68 56.19-34.43 87.5-32.13Z'/%3E%3Cellipse cx='356.323' cy='925.975' fill='%23D9D9D9' rx='115.69' ry='34.9'/%3E%3Cpath fill='%2317B6E1' d='M300.923 917.625a19.13 19.13 0 0 0-.49 7.88s.61 3.89 6.19 6.65c3.08 1.52 8.58 3 16 3 0 0 12.64.16 20.44-3 4.92-1.71 9.6-5 11.08-8.86 0 0 1.48-8.62.25-12.8 0 0-1.44-5.32-11.08-4.43-.04-.06-35.99 6.39-42.39 11.56Zm112.21-.35a22.19 22.19 0 0 1 1 8.23s-.05 3.89-5.63 6.65c-3.08 1.52-8.58 3-16 3 0 0-12.64.16-20.44-3-4.92-1.71-9.6-5-11.08-8.86 0 0-1.48-8.62-.25-12.8 0 0 2.21-5.43 11.08-4.43.03-.06 34.91 6.04 41.32 11.21Z'/%3E%3Cpath fill='%2317B6E1' d='M399.203 751.005c.37 23.93 2.62 150.75 3 154.58a46.15 46.15 0 0 1 5.89 4.51c9 8 2.47 15.48-15.06 16.43-27.26.82-31.28-41.13-31.27-41.13l-.41-21.19s.34-7.71-5-7.71c-3.16 0-4.31 1.85-4.31 7.83 0 3.43.32 8.11.19 13v31.41c.07.29.15.57.19.87.24 1.35.268 2.73.08 4.09-.43 5-5 14-32.27 13.18-16.36-.63-7.63-15.5-7.32-21.58 0-3.59.28-119.62.25-129.23v-14.06m87.23-9.35a52.13 52.13 0 0 0-49.78-8.89'/%3E%3Cpath fill='%2300A3DE' d='m465.003 774.295-12.25-16.71 1.37-1.88c-.39-9.71 2.88-11.82 2.88-11.82 3.38-2.77 6.39.54 6.67.85l.31-.33c5.43-9 11.38-4.77 11.38-4.77 10 12.44-10.36 34.66-10.36 34.66Z'/%3E%3Cpath fill='%2317B6E1' d='M398.683 772.275c12.71 2.2 29.87 2.43 55.44-16.77l13.44 17.32c-26.13 19.61-47.75 22.36-75.28 19'/%3E%3Cpath fill='%2300A3DE' d='m246.713 774.295 12.29-16.71-1.37-1.88c.39-9.71-2.88-11.82-2.88-11.82-3.38-2.77-6.39.54-6.67.85l-.31-.33c-5.43-9-11.38-4.77-11.38-4.77-10.03 12.44 10.32 34.66 10.32 34.66Z'/%3E%3Cpath fill='%2317B6E1' d='M313.003 772.275c-12.71 2.2-29.87 2.43-55.44-16.77l-13.44 17.32c26.13 19.61 47.75 22.36 75.28 19m33.07 84.8c-6 3.78-15.83 3.71-21.61 3.71-14 0-18.5-4.47-18.5-4.47l.45 28.65a55.56 55.56 0 0 0-8.33 6.28c-8.68 8.51-1.34 16 15.26 16.67 27.67.83 32.57-8.15 33-13.25.16-9.21-.27-37.59-.27-37.59Zm9.53 37.58c.44 5.1 4.6 14.08 32.27 13.25 16.6-.63 23.4-7.64 15.27-16.67a40.06 40.06 0 0 0-7.38-6s-.12-13-.4-29.63c0 0-6.09 4.85-18.78 5-5.77.08-14.85 0-21.45-3.53-.02 0 .42 27.84.47 37.58Z'/%3E%3Cpath fill='%23FFF' d='M487.673 635.095c0 67.92-60.48 123-135.07 123s-135.05-55.09-135.05-123 60.46-123 135.05-123 135.07 55.09 135.07 123Z'/%3E%3Cpath fill='%2317B6E1' d='M356.493 489.485s-163.49-5.88-156.35 147.09h.06c8.12 125.27 121.92 132.81 154.2 132.81 39.54 0 146.14-9.32 152.77-132.81 6.63-123.49 12.05-142.77-150.68-147.09Z'/%3E%3Cpath fill='%2317B6E1' d='M354.413 755.885c-29.93 0-128.72-6.26-152.33-102.07 17 108.74 121.59 115.57 152.32 115.57 37.34 0 134.47-8.31 150.76-113.22-22.59 92.14-114.44 99.72-150.75 99.72Z'/%3E%3Cpath fill='%23FF0' d='M354.843 559.515c-114.71-2-109.25 71.1-109.25 71.1-.28 8.25-1.77 107.51 109.22 107.51 111 0 109.5-99.26 109.22-107.51.06 0 5.52-73.12-109.19-71.1Z'/%3E%3Ccircle cx='353.993' cy='606.165' r='3.86' fill='red'/%3E%3Cpath fill='%23000' d='M324.003 591.155c-.203 6.459-5.572 11.548-12.033 11.407-6.46-.142-11.6-5.462-11.52-11.923.08-6.462 5.35-11.653 11.813-11.634 6.584.142 11.824 5.564 11.74 12.15Zm80.63 0c-.198 6.462-5.562 11.56-12.026 11.427-6.464-.132-11.616-5.445-11.548-11.91.067-6.465 5.328-11.67 11.794-11.667 6.6.12 11.863 5.549 11.78 12.15Z'/%3E%3Ccircle cx='274.873' cy='617.915' r='1.86' fill='red'/%3E%3Ccircle cx='285.763' cy='617.915' r='1.86' fill='red'/%3E%3Ccircle cx='280.463' cy='623.215' r='1.86' fill='red'/%3E%3Ccircle cx='421.793' cy='617.915' r='1.86' fill='red'/%3E%3Ccircle cx='432.673' cy='617.915' r='1.86' fill='red'/%3E%3Ccircle cx='427.373' cy='623.215' r='1.86' fill='red'/%3E%3Cpath fill='%23FFF' d='m354.783 776.315-15.9 25.32h11.19l-17.66 32.38 40.62-39.45h-11.78l12.37-18.25h-18.84z'/%3E%3Ccircle cx='425.603' cy='531.805' r='6.99' fill='%232BD7F5'/%3E%3Cpath fill='%23EB2D74' d='M363.633 635.145a258.34 258.34 0 0 0 50.63-6.66c-2.8 23-19.37 42-41.58 49.12-4.31-4.83-11.38-8-19.37-8s-15.13 3.18-19.43 8c-22.33-7.07-39-26.1-41.82-49.19a258.82 258.82 0 0 0 51.49 6.69s10.35.57 20.08.04Z'/%3E%3Cpath fill='%23C00058' d='M353.313 669.635c-8 0-15.13 3.18-19.43 8a64.21 64.21 0 0 0 38.8-.07c-4.32-4.79-11.38-7.93-19.37-7.93Z'/%3E%3Cpath fill='%23DAFFC2' d='M286.943 648.865c-8.24 17.88-50 31.33-67.7 33.71a61.21 61.21 0 0 1-9.77.48c2 5.38.72 10.68 3.16 15.41 2.91-.23 9.2-.83 11.9-1.28 27.99-4.76 50.56-22.6 62.41-48.32Z'/%3E%3Cpath fill='%23B3DDC2' d='M93.063 399.675c-2.21.45-3.51 3-3.14 5.24.37 2.24 4.52 6.33 16.68 9.6a64.83 64.83 0 0 0 23.84 1.33c11.07-1.22 21.43-5.89 31.39-10.86 11.9-5.94 23.63-12.48 33.62-21.25a10.07 10.07 0 0 0 2.62-3.06c1.15-2.4.29-5.41-1.49-7.39a15.85 15.85 0 0 0-6.82-4 46.14 46.14 0 0 0-11.46-2.39l-85.24 32.78Z'/%3E%3Cpath fill='%23FFA103' d='M179.283 355.345c-1.21-.12-2.19.85-3.31 1.24-3 1-9.35 1.68-11.08-.46a19.12 19.12 0 0 1-1.58-2.93 28.27 28.27 0 0 0-4-.9 45.83 45.83 0 0 0-9.38-.22 36.1 36.1 0 0 0-13 4.43 293.63 293.63 0 0 1-29.8 14.15c-9.62 4-18.2 5.51-21.29 14.57-2.49 7.33 2.63 15.65 11.67 18.69 3.65 1.23 12 3.23 15.85 4a44 44 0 0 0 17.62.39c14.09-2.12 19.95-7.25 29-12 10.55-5.61 16.92-9.36 26.88-15.73 5.73-3.66 6.81-7.42 6.92-9 .48-6.57.03-14.74-14.5-16.23Z'/%3E%3Cpath fill='%23791F1F' d='M122.883 409.705a44.94 44.94 0 0 1-9.57-1.1c-3.71-.78-12.2-2.81-15.93-4.07a19.29 19.29 0 0 1-11.26-9.37 13.21 13.21 0 0 1-.82-10.16c2.64-7.76 9.17-10.21 16.73-13 1.6-.6 3.25-1.22 4.93-1.91a293.35 293.35 0 0 0 29.74-14.12 36.13 36.13 0 0 1 13.2-4.5 27.746 27.746 0 0 1 3.16-.17c2.15 0 4.29.2 6.36.39a28.42 28.42 0 0 1 4.09.92l.29.09.12.28c.413.969.912 1.9 1.49 2.78.61.75 2.2 1.36 4.37 1.36a12.51 12.51 0 0 0 6-1.11c.33-.15.69-.31 1-.51a4.32 4.32 0 0 1 2.54-.76c14.19 1.46 15.65 9 15.12 16.88-.1 1.55-1 5.58-7.22 9.55-10.2 6.52-16.49 10.21-26.93 15.76-2 1-3.77 2.1-5.53 3.13-6.42 3.75-12.48 7.3-23.63 9a54.48 54.48 0 0 1-8.25.64Zm30.17-57.13a26.4 26.4 0 0 0-3 .16 34.89 34.89 0 0 0-12.73 4.35 294.38 294.38 0 0 1-29.86 14.18c-1.7.7-3.36 1.32-5 1.93-7.55 2.83-13.51 5.07-16 12.25a11.91 11.91 0 0 0 .75 9.15 18 18 0 0 0 10.51 8.71c3.68 1.24 12.1 3.25 15.78 4a43.67 43.67 0 0 0 9.3 1.08 53.21 53.21 0 0 0 8.08-.7c10.9-1.64 16.85-5.12 23.16-8.81 1.77-1 3.61-2.11 5.58-3.16 10.4-5.53 16.67-9.2 26.84-15.7 5.86-3.75 6.55-7.48 6.62-8.54.45-6.76-.21-14.09-13.95-15.49a3.17 3.17 0 0 0-1.77.6 7.85 7.85 0 0 1-1.26.6 21.72 21.72 0 0 1-6.43 1c-1.84 0-4.26-.29-5.37-1.65a16.65 16.65 0 0 1-1.54-2.8 25 25 0 0 0-3.55-.79c-1.96-.18-4.06-.38-6.16-.38v.01Z'/%3E%3Cpath fill='%23FFE166' d='M179.283 355.345s-29.28 17.54-59.23 31.42a18.06 18.06 0 0 0-4.11 2.51 23 23 0 0 0-3.38 4.24s-4.18 7.52-11.67 4.82c0 0-6.83-2.89-3.52-11.56a9 9 0 0 1 2.61-3.5 20.51 20.51 0 0 1 5.1-2.67s45.46-20.71 58.39-27.36c0 0 .55 4.85 7.23 4.42a25.86 25.86 0 0 0 8.58-2.32Z'/%3E%3Cpath fill='%23791F1F' d='M104.083 399.565a10 10 0 0 1-3.41-.62c-2.59-1.1-6.7-5.08-3.91-12.41a9.62 9.62 0 0 1 2.8-3.76 21.21 21.21 0 0 1 5.27-2.77c.44-.2 45.58-20.77 58.34-27.34a.65.65 0 0 1 .95.51c0 .15.54 3.87 5.77 3.87h.77a25.48 25.48 0 0 0 8.36-2.25.65.65 0 1 1 .61 1.15c-.29.18-29.66 17.71-59.29 31.45a17.6 17.6 0 0 0-4 2.42 14.55 14.55 0 0 0-2.43 2.94c-.26.38-.53.76-.81 1.15-.29.49-3.41 5.66-9.02 5.66Zm58.92-45.37c-13.71 7-57.22 26.79-57.67 27a20.53 20.53 0 0 0-4.94 2.57 8.5 8.5 0 0 0-2.39 3.24c-3 8 2.91 10.64 3.16 10.75a8.71 8.71 0 0 0 2.93.53c5 0 7.89-5 7.91-5.07v-.07c.28-.39.55-.77.81-1.15a15.45 15.45 0 0 1 2.67-3.21 18.59 18.59 0 0 1 4.28-2.61c22.09-10.24 44.06-22.61 53.85-28.27a20.26 20.26 0 0 1-2.93.43h-.84c-4.31.01-6.22-2.33-6.84-4.14Z'/%3E%3Cpath fill='%23F06303' d='M63.003 320.755c-2.51-2.38-13.11-30.79-16.12-42-.38-1.42-.62-3.24.58-4.09a8.8 8.8 0 0 0 1.4-1 1.35 1.35 0 0 0-1.35-2.08 13.86 13.86 0 0 0-2.4.58l-.46-2.8c-.42-2.24-1.91-1.5-2-.86-.25 1.09.42 2.92.62 4-.16-.73-1.47-4.79-2.9-4-1.17.53.15 3.57.6 5.22-.83-1.71-2-4.67-3.26-3.7-1.61.98 1.29 5.74 2.53 7.98 2.06 3.88 10.69 31.58 21.93 49l.83-6.25Z'/%3E%3Cpath fill='%23791F1F' d='M61.613 327.355c-8.31-12.85-15.31-31.59-19.06-41.67-1.38-3.69-2.37-6.35-2.89-7.34l-.54-1c-1.64-3-3.69-6.69-1.87-7.95a1.4 1.4 0 0 1 1.21-.27 1.8 1.8 0 0 1 .7.36 1.6 1.6 0 0 1 .86-1.68c.392-.2.857-.2 1.25 0 .25.124.473.293.66.5a1.56 1.56 0 0 1 1.51-1.15c.43 0 1.45.2 1.8 2l.35 2.09a8.08 8.08 0 0 1 1.78-.39 2.08 2.08 0 0 1 2.11 1.2 1.78 1.78 0 0 1-.25 2 6.91 6.91 0 0 1-1.23.95l-.25.16c-.69.49-.8 1.63-.33 3.39 3.55 13.28 13.9 39.81 15.93 41.74l-.9.95c-2.72-2.58-13.43-31.63-16.3-42.35-.23-.87-.94-3.53.83-4.79l.31-.2a5.82 5.82 0 0 0 1-.76.55.55 0 0 0 0-.53.77.77 0 0 0-.83-.44 7 7 0 0 0-1.5.34l-.78.22-.69.17-.58-3.5c-.14-.72-.38-.95-.49-1a.33.33 0 0 0-.26.14 7.18 7.18 0 0 0 .37 2.65c.1.4.2.78.26 1.11l-1.28.26c-.21-.94-1.12-3.26-1.83-3.59h-.13c-.22.1-.27.74.57 3.33.13.42.26.81.35 1.14l-1.22.46-.3-.64c-.45-1-1.3-2.75-1.85-2.88l-.13.05c-.87.6 1.48 4.86 2.25 6.26l.55 1c.56 1.06 1.57 3.76 3 7.49 3.74 10 10.7 28.68 18.94 41.41l-1.1.76Z'/%3E%3Cpath fill='%23F06303' d='M67.603 393.865a106.59 106.59 0 0 0 2.31 14.28c.29 1.16 2.06 8.22 7.54 8.76 11.06 1.11 22.32-7.49 32.11-10.86 1.07 2.52 1.14 4.18 2.61 5.35 1.16.93 2.74 1.39 3.71-.27 1.5-2.33 0-4.78-1.24-8.15-.69-1.92-1.32-3.24-3.63-3.19-3 .05-21.91 9.86-29.66 11.24a3.45 3.45 0 0 1-2.45-.24c-2.41-1.5-4-15-3.53-19.68l-7.77 2.76Z'/%3E%3Cpath fill='%23791F1F' d='M79.463 417.675c-.667 0-1.357-.034-2.07-.1-5.37-.54-7.46-6.65-8.11-9.26a107.53 107.53 0 0 1-2.28-14.37l1.3-.15a106.47 106.47 0 0 0 2.3 14.2c.58 2.34 2.43 7.82 7 8.27 7.34.73 14.78-2.92 22-6.45a88.48 88.48 0 0 1 9.87-4.38l.58-.2.24.56c.34.81.59 1.53.8 2.17a5.61 5.61 0 0 0 1.61 2.93 2.44 2.44 0 0 0 1.8.67c.408-.101.75-.38.93-.76 1-1.63.36-3.37-.59-5.77-.23-.58-.47-1.19-.7-1.83-.69-1.94-1.19-2.8-3-2.76-1.17 0-5.77 2-10.64 4.14-6.8 3-14.51 6.31-18.92 7.09a4.07 4.07 0 0 1-2.91-.32c-2.83-1.77-4.31-15.86-3.84-20.3l1.3.14c-.56 5.22 1.34 17.87 3.23 19.05a2.94 2.94 0 0 0 2 .15c4.26-.76 12.23-4.22 18.63-7 6.2-2.69 9.77-4.22 11.13-4.25 2.94 0 3.67 2 4.26 3.63.22.63.46 1.22.69 1.79 1 2.55 1.88 4.76.49 6.93a2.64 2.64 0 0 1-1.81 1.36 3.64 3.64 0 0 1-2.86-.94 6.72 6.72 0 0 1-2-3.53c-.15-.46-.32-1-.53-1.51a94.69 94.69 0 0 0-9.13 4.11c-6.86 3.3-13.76 6.69-20.77 6.69Z'/%3E%3Cpath fill='%23F06303' d='M75.093 401.115a17.84 17.84 0 0 0 8.14-1.49c4.17-2 6.95-6.22 8.29-10.66 1.34-4.44 1.48-9.13 1.48-13.76l1.1-63.36c.15-8.6.82-19-6.17-24.29-8-5-19.88-1.87-24.48 6.35-2.7 4.82-3 10.59-3.17 16.11-.88 24.19-1.76 48.39-1.19 72.58.11 4.63.39 9.61 3.26 13.25 2.87 3.64 8 5.16 12.74 5.27Z'/%3E%3Cpath fill='%23791F1F' d='M75.693 401.775h-.61c-3.37-.07-9.56-.89-13.21-5.52-2.93-3.72-3.28-8.57-3.4-13.64-.57-24.23.33-48.83 1.19-72.62.19-5.32.41-11.34 3.25-16.41 3.08-5.51 9.49-9.061 16.32-9.061a17.229 17.229 0 0 1 9.08 2.48c6.84 5.13 6.64 14.76 6.46 23.26v1.56l-1.1 63.36c-.08 4.62-.16 9.39-1.54 13.95-1.55 5.13-4.7 9.16-8.63 11.06a17.74 17.74 0 0 1-7.81 1.58Zm3.55-116c-6.37 0-12.33 3.29-15.18 8.39-2.69 4.84-2.9 10.7-3.06 15.84-.87 23.77-1.76 48.36-1.19 72.55.11 4.83.44 9.45 3.12 12.86 3.32 4.21 9.07 5 12.21 5h.58a16.48 16.48 0 0 0 7.23-1.41c3.61-1.74 6.5-5.48 7.95-10.26 1.32-4.38 1.41-9.06 1.48-13.59v-.09l1.1-63.27v-1.56c.17-8.17.36-17.44-5.92-22.17a15.92 15.92 0 0 0-8.33-2.24l.01-.05Z'/%3E%3Cpath fill='%23F06303' d='M81.553 396.285c-6.24-2.29-20.07-7.08-32.22-2.68-5.5 2-9.86 6.64-17.69 17.67-1 1.44-3 3.73-5.26 3.75a7.72 7.72 0 0 1-3.27-.85 36.72 36.72 0 0 1-4.65-2.25c-2.64-1.62-2.28-3.6-1.37-4.53a4 4 0 0 1 3.84-.62 11.94 11.94 0 0 0 3.86.72c4.26-.09 10.33-14.81 21.36-19.45.3-.13 14.92-5.26 37.7 2.65l-2.3 5.59Z'/%3E%3Cpath fill='%23791F1F' d='M26.333 415.675a8.2 8.2 0 0 1-3.41-.88h-.07l-1.26-.55a25 25 0 0 1-3.47-1.73 4.31 4.31 0 0 1-2.33-3.08 3 3 0 0 1 .84-2.47 4.58 4.58 0 0 1 4.48-.79h.17c1.119.41 2.298.63 3.49.65 1.53 0 3.72-2.81 6.26-6 3.75-4.75 8.43-10.67 14.86-13.38.14-.06 14.86-5.45 38.17 2.64l-.43 1.23c-22.7-7.88-37.1-2.72-37.24-2.67-6.12 2.58-10.68 8.35-14.34 13-3 3.76-5.11 6.47-7.26 6.52a11.87 11.87 0 0 1-3.88-.7h-.17a3.36 3.36 0 0 0-3.19.45 1.71 1.71 0 0 0-.48 1.4 3.19 3.19 0 0 0 1.72 2.12 24 24 0 0 0 3.3 1.65l1.28.56h.07a7 7 0 0 0 2.92.76c2.05 0 4-2.44 4.73-3.48 8.34-11.74 12.55-15.93 18-17.9 11.46-4.15 24.14-.46 32.67 2.68l-.45 1.23c-8.34-3.07-20.71-6.69-31.77-2.68-5.1 1.85-9.32 6.08-17.38 17.43-1.06 1.49-3.22 4-5.78 4l-.05-.01Z'/%3E%3Cpath fill='%23FFCB00' d='M75.733 326.005c.193.369.276.785.24 1.2-.13 4.15-6.93 8.41-4.72 12.19.69 1.18 4.06 3 5 4.1 2.21 2.41 1.38 3.45-.16 5.9-1.69 2.68-4.36 4-3.47 6.87.58 1.85 4.92 6.31 5.31 8.2.5 2.45-2 4.61-4.21 6.47-2.21 1.86-3.51 4.17-3 6.66.6 3.28-1.15 5.39-3.63 4.94a4.89 4.89 0 0 1-4.1-4.41c-.15-2.84 1.85-6.07 4.12-8.35 2.43-2.44 3.93-3.21 4-5.34 0-2.31-5.27-6.12-5.42-8.43-.11-1.73 1.28-3.29 2.41-4.81 1.13-1.52 3.44-3.6 2.4-5.13a39.11 39.11 0 0 0-4.12-3.42c-3.88-3.28-2.25-6.68-.49-10.58 1.09-2.42 2.72-6.1 5.7-7.32a3.46 3.46 0 0 1 4.14 1.26Z'/%3E%3Cpath fill='%23791F1F' d='M67.723 383.215a4.29 4.29 0 0 1-.77-.07 5.56 5.56 0 0 1-4.64-5c-.14-2.68 1.47-6 4.31-8.84.57-.57 1.08-1 1.54-1.47 1.57-1.46 2.21-2.11 2.22-3.42 0-.93-1.36-2.5-2.57-3.88-1.42-1.62-2.76-3.15-2.84-4.5-.12-1.77 1-3.25 2.12-4.69l.42-.55c.23-.31.5-.64.79-1 1-1.14 2.15-2.57 1.59-3.4a23.67 23.67 0 0 0-2.89-2.39c-.47-.36-.87-.66-1.1-.86-4.19-3.54-2.52-7.23-.76-11.14l.13-.28c1.13-2.5 2.83-6.27 6-7.58a4.166 4.166 0 0 1 1.58-.31 4.002 4.002 0 0 1 3.44 1.85 2.81 2.81 0 0 1 .33 1.55c-.06 2-1.43 3.9-2.76 5.75-1.59 2.21-3.09 4.31-2 6.08.79.866 1.7 1.617 2.7 2.23a15.87 15.87 0 0 1 2.2 1.71c2.45 2.67 1.52 4.14.11 6.37l-.2.32a17.64 17.64 0 0 1-1.76 2.31c-1.37 1.56-2.12 2.51-1.64 4.07a18.12 18.12 0 0 0 2.42 3.62c1.38 1.8 2.67 3.5 2.91 4.64.58 2.84-2.2 5.2-4.43 7.1-1.55 1.32-3.31 3.44-2.83 6a5.41 5.41 0 0 1-.88 4.51 3.491 3.491 0 0 1-2.74 1.27Zm5.13-58.11a2.841 2.841 0 0 0-1.09.21c-2.71 1.11-4.28 4.6-5.32 6.91l-.13.28c-1.69 3.75-3 6.71.41 9.61.2.17.59.47 1 .81a19 19 0 0 1 3.28 2.75c1.11 1.64-.43 3.49-1.67 5-.27.33-.53.64-.74.92l-.42.56c-1 1.25-1.94 2.54-1.85 3.81.06.91 1.37 2.4 2.52 3.72 1.5 1.71 2.91 3.32 2.9 4.76 0 1.93-1.06 2.9-2.64 4.36-.45.41-1 .88-1.5 1.43-2 2-4.07 5.12-3.93 7.85a4.24 4.24 0 0 0 3.57 3.8 2.28 2.28 0 0 0 2.26-.74 4.21 4.21 0 0 0 .6-3.44c-.47-2.55.66-5.06 3.27-7.28 2.07-1.76 4.42-3.76 4-5.84-.17-.85-1.5-2.58-2.67-4.11a17.66 17.66 0 0 1-2.63-4c-.73-2.34.55-3.79 1.9-5.32a16.32 16.32 0 0 0 1.59-2.15l.2-.32c1.39-2.19 1.8-2.85 0-4.79a15.89 15.89 0 0 0-2-1.57 11.75 11.75 0 0 1-3.08-2.64c-1.47-2.51.35-5 2.11-7.51 1.21-1.69 2.47-3.44 2.52-5a1.53 1.53 0 0 0-.15-.85 2.72 2.72 0 0 0-2.31-1.21v-.01Z'/%3E%3Cpath fill='%23FFF' d='M83.403 301.865a3.18 3.18 0 0 0 3.63-1.7c.26-.57.73-2.69-.63-4a3.5 3.5 0 0 0-3.4-.74 3.42 3.42 0 0 0-2.15 3.49 3.58 3.58 0 0 0 2.55 2.95Z'/%3E%3Cpath fill='%23791F1F' d='M84.163 302.625a3.38 3.38 0 0 1-.94-.13 4.24 4.24 0 0 1-3-3.46 4.07 4.07 0 0 1 2.61-4.19 4.47 4.47 0 0 1 1.28-.18 4 4 0 0 1 2.76 1.08 4.41 4.41 0 0 1 .78 4.7 3.88 3.88 0 0 1-3.49 2.18Zm-.07-6.62a3.17 3.17 0 0 0-.89.13 2.78 2.78 0 0 0-1.69 2.79 2.94 2.94 0 0 0 2.08 2.35c.188.053.384.08.58.08a2.53 2.53 0 0 0 2.27-1.41c.14-.32.64-2.17-.48-3.21a2.65 2.65 0 0 0-1.87-.73Z'/%3E%3Ccircle cx='83.983' cy='298.485' r='1' fill='%23791F1F'/%3E%3Cpath fill='%23FFF' d='M65.213 301.665a3.18 3.18 0 0 0 3.63-1.7c.26-.57.73-2.69-.63-4a3.5 3.5 0 0 0-3.4-.74 3.42 3.42 0 0 0-2.15 3.49 3.58 3.58 0 0 0 2.55 2.95Z'/%3E%3Cpath fill='%23791F1F' d='M66.003 302.425a3.41 3.41 0 0 1-.94-.13 4.24 4.24 0 0 1-3-3.46 4.07 4.07 0 0 1 2.61-4.19 4.47 4.47 0 0 1 1.28-.18 4 4 0 0 1 2.76 1.08 4.41 4.41 0 0 1 .78 4.7 3.87 3.87 0 0 1-3.49 2.18Zm-.07-6.66a3.18 3.18 0 0 0-.89.13 2.78 2.78 0 0 0-1.69 2.79 2.94 2.94 0 0 0 2.05 2.32 2.1 2.1 0 0 0 .58.08 2.53 2.53 0 0 0 2.27-1.41c.14-.32.64-2.17-.48-3.21a2.65 2.65 0 0 0-1.87-.7h.03Z'/%3E%3Ccircle cx='65.263' cy='298.295' r='1' fill='%23791F1F'/%3E%3Cpath fill='%23F06303' d='M92.623 320.755c2.51-2.38 13.11-30.79 16.12-42 .38-1.42.62-3.24-.58-4.09a8.8 8.8 0 0 1-1.4-1 1.35 1.35 0 0 1 1.35-2.08 13.86 13.86 0 0 1 2.4.58l.46-2.8c.42-2.24 1.91-1.5 2-.86.25 1.09-.42 2.92-.62 4 .16-.73 1.47-4.79 2.9-4 1.17.53-.15 3.57-.6 5.22.83-1.71 2-4.67 3.26-3.7 1.52 1.05-1.41 5.81-2.62 8.09-2.06 3.88-10.69 31.58-21.93 49l-.74-6.36Z'/%3E%3Cpath fill='%23791F1F' d='m94.003 327.355-1.1-.71c8.24-12.73 15.2-31.39 18.94-41.41 1.39-3.73 2.4-6.43 3-7.49l.55-1c.77-1.4 3.12-5.66 2.27-6.24-.11-.08-.14-.08-.15-.07-.55.13-1.4 1.92-1.85 2.88l-.3.63-1.22-.46c.09-.34.22-.73.35-1.14.84-2.59.79-3.23.61-3.31-.18-.08-.1 0-.17 0-.71.33-1.62 2.65-1.83 3.59l-1.28-.26c.06-.33.16-.71.26-1.11a7.15 7.15 0 0 0 .37-2.65.31.31 0 0 0-.26-.14c-.11 0-.36.24-.5 1l-.58 3.48-.69-.17-.78-.22a7 7 0 0 0-1.5-.34.78.78 0 0 0-.83.44.55.55 0 0 0 0 .53c.305.288.64.543 1 .76l.31.2c1.77 1.26 1.06 3.92.83 4.79-2.87 10.73-13.58 39.77-16.3 42.35l-.9-.95c2-1.93 12.39-28.46 15.93-41.74.47-1.76.36-2.9-.33-3.39l-.25-.16a6.91 6.91 0 0 1-1.23-.93 1.78 1.78 0 0 1-.25-2 2.08 2.08 0 0 1 2.11-1.2 8.08 8.08 0 0 1 1.79.39l.34-2.08c.35-1.85 1.38-2.05 1.8-2.05a1.57 1.57 0 0 1 1.52 1.18 2.22 2.22 0 0 1 .66-.5 1.4 1.4 0 0 1 1.29 0c.605.312.94.978.83 1.65a1.8 1.8 0 0 1 .7-.36 1.41 1.41 0 0 1 1.23.28c1.79 1.24-.26 4.95-1.9 7.93l-.54 1c-.52 1-1.52 3.65-2.89 7.34-3.8 10.06-10.79 28.81-19.06 41.66Z'/%3E%3Cpath fill='%23B3DDC2' d='M97.583 446.385c-9 3.21-18.26 6.54-33.58 4.64a14 14 0 0 1-7.67-3.9 2.73 2.73 0 0 1-.92-1.62c-.81-3.84 1.6-6.82 5.42-9 3.82-2.18 9-3.52 14.1-4.88l22.72-6c1.48.32 2 1.43 1.91 2.08-.09.65 0 1.62 1.48 2.14a4.48 4.48 0 0 0 2 .17c2.28-.25 4-.94 6.19-1.23a15.92 15.92 0 0 1 8.13 1.17c2.93 1.3 4.39 3.31 4.13 4.67-.26 1.36-1.85 2.23-3.44 3a173.64 173.64 0 0 1-20.47 8.76Z'/%3E%3Cpath fill='%23791F1F' d='M73.783 313.835a12 12 0 0 1-5.59-1.43 10.07 10.07 0 0 1-4.68-7.76l1.74-.07a8.26 8.26 0 0 0 3.75 6.29 9.92 9.92 0 0 0 8.27.59 9 9 0 0 0 5.73-6.59l1.73.18a10.69 10.69 0 0 1-6.9 8 11.73 11.73 0 0 1-4.05.79Z'/%3E%3Cellipse cx='451.825' cy='103.609' fill='%23DEBD53' rx='3.9' ry='1.83' transform='rotate(-29.78 451.825 103.609)'/%3E%3Cpath fill='%23DEBD53' d='m447.711 98.544 1.831-1.048 2.826 4.938-1.83 1.048zm8.939-5.193 1.831-1.048 2.827 4.939-1.832 1.048zm10.03-4.183 1.83-1.048 2.826 4.94-1.831 1.047z'/%3E%3Cellipse cx='460.872' cy='98.697' fill='%23DEBD53' rx='3.9' ry='1.84' transform='rotate(-29.77 460.872 98.697)'/%3E%3Cellipse cx='470.522' cy='94.112' fill='%23DEBD53' rx='3.9' ry='1.84' transform='rotate(-29.78 470.522 94.112)'/%3E%3Cellipse cx='436.557' cy='77.626' fill='%23DEBD53' rx='3.9' ry='1.84' transform='rotate(-29.78 436.557 77.626)'/%3E%3Cpath fill='%23DEBD53' d='m436.028 78.843 1.831-1.049 2.827 4.938-1.83 1.049zm8.28-5.159 1.831-1.048 2.826 4.939-1.83 1.048zm9.338-6.514 1.832-1.047 2.822 4.94-1.832 1.048z'/%3E%3Cpath fill='%23C75B8D' d='M335.793 144.005c-15.42-28.18-34.27-19.86-34.27-19.86-9.29 2.85-14.29 13.82-14.29 13.82-2.5 6.72-5.8 9-5.8 9-6.86 4.63-16.73-.29-16.73-.29-18.71-6.21-31.41 1.76-31.41 1.76-38.84 23.28-15.59 68.83-15.59 68.83a44.06 44.06 0 0 0 1.95 4.14l1.62 3.33s1.22 2.12 3.15 4.89c0 0 26.47 41.94 66.21 20.26 0 0 13.31-6.91 17.44-26.19 0 0 .75-11 8.23-14.57a20.13 20.13 0 0 1 12.44-.43s8.31 1.59 17.37-5.36c0 0 16.91-11.09.24-39.61l-3.95-6.9-1.75-4.29-4.86-8.53Z'/%3E%3Cpath fill='%23E8D268' d='M232.923 147.755c-20.35 12.2-23.57 30.54-22.68 43.77a75 75 0 0 0 6.84 26 43.56 43.56 0 0 0 2 4.16l1.61 3.3c.07.13 1.27 2.22 3.2 5a70.72 70.72 0 0 0 18.38 18.44c10.73 7.2 27.89 13.4 48.73 2 .54-.28 13.64-7.28 17.78-26.65v-.1c0-.11.82-10.65 7.83-14a19.66 19.66 0 0 1 12-.38c.4.08 8.77 1.58 18-5.48a18.9 18.9 0 0 0 6.61-9.43c1.5-4.43 2.19-11.27-1.24-20.71a63.7 63.7 0 0 0-4.95-10.36l-4.03-6.88-1.75-4.29-4.88-8.54c-8.09-14.78-17.18-19.49-23.37-20.84a20.39 20.39 0 0 0-11.74.67c-9.45 2.93-14.47 13.72-14.68 14.18-2.4 6.43-5.53 8.65-5.56 8.67-6.44 4.35-15.94-.29-16-.34-18.9-6.26-31.57 1.48-32.1 1.81Zm-11.06 76.54-1.6-3.29a43 43 0 0 1-1.93-4.1 59.26 59.26 0 0 1-2.45-5.88 72.91 72.91 0 0 1-4.27-19.63c-1.24-18.68 6.18-33 22-42.47.14-.09 12.67-7.72 30.81-1.71.77.38 10.39 4.93 17.37.22.14-.1 3.51-2.44 6.06-9.29 0-.06 5-10.68 13.85-13.4a19.4 19.4 0 0 1 11-.58c8.57 1.84 16.3 8.84 22.49 20.12l4.85 8.47 1.75 4.29 4 6.94a62.64 62.64 0 0 1 4.85 10.15c2.69 7.4 3.13 14.09 1.26 19.7a17.92 17.92 0 0 1-6.13 8.84c-8.73 6.69-16.79 5.27-16.87 5.26a20.57 20.57 0 0 0-12.85.47c-7.61 3.64-8.57 14.24-8.63 15.09-4 18.68-16.94 25.62-17.07 25.69-16.24 8.86-32.14 8.21-47.27-1.94a69.29 69.29 0 0 1-18-18.08c-1.91-2.73-3.12-4.84-3.13-4.87h-.09Z'/%3E%3Cpath fill='%23282B26' d='m308.003 159.495 121.89-69s-5.16-6.56 3.45-9.37c0 0 6.41 2 10.77-.88a94.93 94.93 0 0 0 16-15.15l3.45.49a5.61 5.61 0 0 0 4.1 4.7s5.43 1.48 3.83 4.85c0 0 4.1-.23 2.77 5.46 0 0-.26 5.09 2.47 7.4l-1.63 2.69s-8.61-2.12-21.85 6.16c0 0-4.74 3.42-5.69 8.54 0 0-5.19 7.55-10-2l-120.68 72.19-8.88-16.08Zm-68.91 10.76s-2.37 2.79 2 2.16c0 0 9.65-1.89 16.79-8.93 0 0 3.1 1.87 10.48-.59 0 0 10.49-3.38 13.53 1.64a2.83 2.83 0 0 1-1.17 3.71s-2.35.92-3-.75c0 0-.72-1.7 1.76-2.53 0 0-3.79-3.07-12.31 1.38 0 0-7 3.81-8.5 7.31a13.16 13.16 0 0 0-8.93.21s-9.86 2.85-13.37-1.06a4.28 4.28 0 0 1-.28-6.1s1.8-2.47 3.83.34c.08-.04 1.14 1.49-.83 3.21Zm35.06 61.27s-3.61.63-.85-2.81c0 0 6.52-7.37 16.21-9.95 0 0 0-3.61 5.82-8.74 0 0 8.23-7.33 5.44-12.5a2.83 2.83 0 0 0-3.8-.87s-2 1.56-.85 2.94c0 0 1.1 1.48 3.08-.24 0 0 .73 4.82-7.43 9.91 0 0-6.81 4.06-10.61 3.62a13.17 13.17 0 0 1-4.7 7.59s-7.46 7.06-5.86 12.06a4.28 4.28 0 0 0 5.12 3.33s3-.3 1.65-3.48c-.02.02-.74-1.69-3.22-.86Zm-59.15-16.67 32-21.11 9.27 16.19-35.62 17.21s-1.71 1.33-5-5.61c-.04 0-2.21-4.4-.65-6.68Zm37.195-32.237 3.185-1.823 17.022 29.733-3.185 1.823z'/%3E%3Cpath fill='%23D74C83' d='m285.483 194.595 56.09-33.15s7.33 11-8.86 22.69l-37.28 26.61s-11.75 10.25-8.88-4.43c0 0 1.55-8.99-1.07-11.72Z'/%3E%3Cpath fill='%23E6E1C5' d='m285.113 194.545.2.21c2.51 2.62 1 11.44 1 11.52-.85 4.33-.5 7 1 7.86 2.7 1.56 8-3 8.23-3.21l37.26-26.6c16.19-11.73 9-22.9 8.92-23l-.12-.18-56.49 33.4Zm56.38-32.79a9.85 9.85 0 0 1 .69 1.53c1.27 3.5 2.49 11.9-9.61 20.67l-37.28 26.61c-.07.06-5.29 4.57-7.71 3.17a2.23 2.23 0 0 1-.95-1.23c-.46-1.26-.41-3.32.14-6.14.06-.32 1.13-6.69-.19-10.31a4.77 4.77 0 0 0-.78-1.39l55.69-32.91Z'/%3E%3Cpath fill='%23E3E6DE' d='m307.784 159.976.799-.457 8.508 14.867-.799.457zm2.398-1.346.798-.456 8.506 14.87-.799.456zm2.641-1.555.799-.457 8.508 14.868-.799.456zm2.93-1.629.798-.456 8.506 14.869-.8.457zm3.451-2.005.799-.457 8.507 14.868-.798.457zm3.711-2.147.798-.457 8.205 14.338-.798.457zm3.731-2.074.799-.458 8.202 14.34-.798.457zm4.244-2.456.799-.457 7.9 13.81-.8.457zm3.975-2.279.799-.457 7.897 13.8-.799.457zm4.251-2.407.799-.457 8.05 14.07-.798.456zm4.769-2.778.799-.457 7.748 13.54-.799.457zm5.324-2.98.798-.456 7.895 13.802-.798.456zm5.571-3.204.798-.457 7.895 13.802-.799.457zm5.563-3.217.798-.457 7.748 13.54-.798.456zm5.594-3.172.798-.457 7.748 13.54-.798.457zm6.896-3.983.798-.457 7.595 13.27-.799.458zm6.907-3.925.799-.457 7.594 13.27-.799.457zm7.156-4.13.799-.457 7.594 13.27-.799.458zm7.441-4.219.799-.456 7.288 12.742-.798.457zm7.7-4.403.798-.457 7.292 12.741-.799.457zm8.214-4.749.799-.456 7.142 12.48-.799.457zm9.031-5.135.798-.457 6.988 12.212-.798.457zm9.106-5.194 1.197-.686 7.445 13.01-1.197.686z'/%3E%3Cpath fill='%23E3E6DE' d='m257.863 190.005-10.06 6.46.25.39 10.05-6.46 173.12-98.3-.23-.4-173.13 98.31Zm1.63 2.28-10.62 6.08.23.4 10.62-6.08 171.92-98.01-.23-.4-171.92 98.01Zm1.37 2.39-10.62 6.08.22.4 10.62-6.08 171.92-98.01-.22-.4-171.92 98.01Zm-9.199 8.557 181.992-104.44.229.4L251.893 203.63zm11.929-3.777-10.62 6.08.23.4 10.62-6.08 171.46-98.81-.22-.4-171.47 98.81Zm1.37 2.39-10.62 6.07.22.4 10.62-6.07 171.43-99.5-.23-.39-171.42 99.49Z'/%3E%3Ccircle cx='441.695' cy='86.365' r='1.83' fill='%23DEBD53' transform='rotate(-29.78 441.695 86.365)'/%3E%3Ccircle cx='447.555' cy='95.713' r='1.84' fill='%23DEBD53' transform='rotate(-29.78 447.555 95.713)'/%3E%3Ccircle cx='450.415' cy='81.083' r='1.83' fill='%23DEBD53' transform='rotate(-29.78 450.415 81.083)'/%3E%3Ccircle cx='456.305' cy='90.469' r='1.84' fill='%23DEBD53' transform='rotate(-29.76 456.305 90.47)'/%3E%3Ccircle cx='459.55' cy='75.36' r='1.84' fill='%23DEBD53' transform='rotate(-29.76 459.55 75.36)'/%3E%3Ccircle cx='465.411' cy='84.693' r='1.83' fill='%23DEBD53' transform='rotate(-29.77 465.41 84.693)'/%3E%3Cellipse cx='444.702' cy='72.311' fill='%23DEBD53' rx='3.9' ry='1.83' transform='rotate(-29.77 444.702 72.31)'/%3E%3Cellipse cx='454.121' cy='66.229' fill='%23DEBD53' rx='3.9' ry='1.83' transform='rotate(-29.78 454.12 66.229)'/%3E%3Cpath fill='%23DBFAFF' d='M431.703 483.865s-115.1-3.09-97-118.24c5.88-37.34 31-62.83 67.4-71.48 34-8.09 83-6.85 114.39 8.7 38.84 19.26 63.78 60.12 57.33 103.84-5.61 38-37.46 80.38-79.26 80.38 0 0 36.86 53.88 75.67 12.79 28.44-30.09 29.29-77.85 25.77-116.51-1.43-15.66-5.88-89.71-25.47-93.18-48-8.51-86.38-30-98.19-81-9.34-40.51-8.24-109.16 33.52-130.34 66.35-33.67 204.26-6.27 221.14 77.65 9.73 48.25-40.41 141.52-100.31 130.22 0 0 27.82 101.83 1.07 167.88-9.76 24.08-23.7 49.43-44.43 66.08.34-.27 5.41 26.55 5.47 28.1 1.33 36.41-13.65 78.82-47.05 97.29-36.23 20-78.63 15.5-115.68 1.72 33.16-2 64.6-16.64 95.15-28.46 23.64-9.14 38.34-24.9 42.13-50.65.44-3-1.78-35.11-4.49-34.64-2.93.51-6.62 2.08-9.88 3.32-28.59 10.88-48.78.21-72.62-17.77-9.31-7-18.24-14.64-27.35-21.91-3.16-2.5-13.17-13.65-17.31-13.79Z'/%3E%3Ccircle cx='578.063' cy='199.075' r='1.62' fill='%23FFF'/%3E%3Ccircle cx='616.743' cy='182.675' r='1.62' fill='%23FFF'/%3E%3Cpath fill='%23DC4472' d='M661.194 199.485a17.908 17.908 0 0 1-.111 1.88c-.37 3.29-.35 12.82-5.62 16a20.79 20.79 0 0 1-3.7 1.79c.37 5.62.87 19.33-1.86 20.82-1.93 1.05-2.58 1.23-4.76.53-1.07-.34-2.77-.24-2.41-2.81a4.16 4.16 0 0 1 3.32-3.33 2.66 2.66 0 0 0 1-.34c.39-.17.41-2.7.46-3.52.1-1.83-.14-8-.19-9.81v-.64c-1.84.12-3.74 0-6 0 .35 6 .69 15.23-1.91 16.65-1.93 1.05-2.84 1.75-5 1.06-1.07-.34-2.5-.77-2.15-3.33a4.17 4.17 0 0 1 3.32-3.33 2.68 2.68 0 0 0 1-.34c.39-.17.41-2.7.45-3.52.1-1.83-.14-4.66-.19-6.49v-.68c-6.14.08-23.25 0-29.66-.43.36 5.81.78 18.94-1.88 20.39a6.46 6.46 0 0 1-4.78.72c-1.11-.14-2.29-.69-2.39-3-.1-2.31 2.32-3.14 3.32-3.33a2.67 2.67 0 0 0 1-.34c.39-.17.41-6 .45-6.84.1-1.83-.14-4.66-.18-6.49v-1.47c-2.054-.194-4.087-.43-6.1-.71.36 5.23 1 16.59-1.81 18.14-1.93 1.05-2.83 1.53-5 .83-1.07-.34-2.51-.54-2.16-3.1a4.17 4.17 0 0 1 3.32-3.33 2.67 2.67 0 0 0 1-.34c.39-.17.41-2.7.45-3.52.1-1.83-.14-4.66-.18-6.49 0-1.21-.07-2.15-.1-2.9l-.36-.07c-4.22-.78-8.19-7.09-9.64-11.3-1.05-3.06-.66-8.64-.66-11.89 0-1-6.78-.3-13.32-.79-4.86-.37-12.79-.37-16.49-4.81-2.29-2.75-2.06-8-1.77-11.05 1.36-14.08 12.09-9.17 21.57-10.26.3-5.77-.43-11.23.47-16.77a10.52 10.52 0 0 1 9.31-9 43.52 43.52 0 0 1 25.76.83c.188.094.372.197.55.31 5.26 3.33 6.78 8.25 7 12.81.2 3.78-.35 5.49-.35 9.3 0 6.84-1.18 14-.6 20.73 9.67 0 27.37.59 37 1.6a11.94 11.94 0 0 1 8.6 4.86c.129.166.25.34.36.52a13.64 13.64 0 0 1 1.62 6.6Z'/%3E%3Cellipse cx='554.633' cy='171.025' fill='%23422D2D' rx='7.96' ry='5.36'/%3E%3Cpath fill='%2349BDFF' d='M614.253 178.755s-7.85 14.91-36.1 15.3l.39 9s25.46 3.95 40.81-17.24l-5.1-7.06Zm-55.01 21.65-2.51 7.66s-1.16 3.55 2.51 3.66c0 0 2.64-.08 2-3.45l-2-7.87Z'/%3E%3Cpath fill='%23422D2D' d='m575.843 237.135-3.12-12.27h-.06c-.82-4.06-11.14-7.28-23.75-7.28s-22.81 3.18-23.74 7.21l-3.18 11-.39 1.36c-.1.18 0 .38 0 .57 0 6.09 12.33 11 27.3 11s27.12-4.94 27.12-11a1.09 1.09 0 0 0-.18-.59Z'/%3E%3Cpath fill='%2349BDFF' d='M549.003 233.145c10.49 0 19.39-2.22 22.57-5.3-3.18-3.08-12.08-5.3-22.57-5.3s-19.39 2.22-22.57 5.3c3.15 3.08 12.06 5.3 22.57 5.3Z'/%3E%3Cpath fill='%23FFF' d='M546.853 226.865h-.12c-2.88 1.94-4.87.09-5 0l-.06-.05h-.07c-3 1.79-4.48.17-4.55.1l-.05-.06h-.07a4.91 4.91 0 0 1-5.05.09l-.12.17a5.11 5.11 0 0 0 5.2 0c.3.28 1.86 1.55 4.68-.08a4.12 4.12 0 0 0 5.09 0 4.38 4.38 0 0 0 5.15-.12l-.12-.17a4.1 4.1 0 0 1-4.91.12Zm9.91 2.91h-.12c-2.88 1.94-4.87.09-5 0l-.06-.05h-.07c-3 1.78-4.49.17-4.55.1l-.05-.06h-.07a4.91 4.91 0 0 1-5.05.09l-.12.17a5.1 5.1 0 0 0 5.2 0c.3.28 1.86 1.55 4.68-.08a4.12 4.12 0 0 0 5.09 0 4.38 4.38 0 0 0 5.15-.12l-.12-.17a4.1 4.1 0 0 1-4.91.12Z'/%3E%3Ccircle cx='584.753' cy='198.615' r='1.62' fill='%23FFF'/%3E%3Ccircle cx='594.563' cy='197.225' r='1.62' fill='%23FFF'/%3E%3Ccircle cx='605.063' cy='193.185' r='1.62' fill='%23FFF'/%3E%3Ccircle cx='613.263' cy='187.645' r='1.62' fill='%23FFF'/%3E%3Ccircle cx='580.543' cy='160.295' r='5.27' fill='%23FFF'/%3E%3Ccircle cx='594.053' cy='160.295' r='5.27' fill='%23FFF'/%3E%3Ccircle cx='580.473' cy='160.305' r='3.26' fill='%23422D2D'/%3E%3Ccircle cx='594.113' cy='160.305' r='3.26' fill='%23422D2D'/%3E%3Cpath fill='%23DC4472' d='M587.643 142.435s9.39-60.22-6.87-60c0 0-10.53-2.06-9.62 20.15 0 0 .92 34.81 4.12 51.07l12.37-11.22Z'/%3E%3Cpath fill='%23FF5688' d='M579.863 86.555s-7.56.23-6.18 18.09c0 0 .69 26.33 3.21 39.39a31.1 31.1 0 0 1 7.79-3s6.17-56.54-4.82-54.48Z'/%3E%3Cpath fill='%23DC4472' d='M609.173 148.615s10.07-66.41-6.17-66.18c0 0-10.53-2.06-9.62 20.15 0 0 .92 34.81 4.12 51.07l11.67-5.04Z'/%3E%3Cpath fill='%23FF5688' d='M602.073 86.555s-7.56.23-6.18 18.09c0 0 .69 26.33 3.21 39.39 0 0 3.89 3.43 7.56 2.75-.01 0 7.77-62.78-4.59-60.23Zm-27.37 66.75s9.85 1.37 10.08-6c0 0-.34 5.84-10.76 5.5l.68.5Zm24.51-.8s-9.85 1.37-10.08-6c0 0 .34 5.84 10.76 5.5l-.68.5Zm-43.21 38.36s3.44-4.69 7.67.11c0 0 .8 4.24-3.32 4.81.04 0-4.77.8-4.35-4.92Z'/%3E%3Cpath fill='%23DC4472' d='M583.233 208.435s2.23 6.7.86 8.93c0 0 2.06-.86 1.55-6.87l-2.41-2.06Z'/%3E%3Cpath fill='%23DC4472' d='M585.643 210.005s.52 6-.17 9.27c0 0 2.23-5.67 2.06-8.76l-1.89-.51Z'/%3E%3Cpath fill='%23DC4472' d='M586.673 210.665s.52 6-.17 9.27c0 0 2.23-5.67 2.06-8.76l-1.89-.51Z'/%3E%3Cpath fill='%23DC4472' d='M587.533 211.525s.52 6-.17 9.27c0 0 2.23-5.67 2.06-8.76l-1.89-.51Z'/%3E%3Cpath fill='%23DC4472' d='M589.133 214.215s-1 4.58-.34 7.9a28.57 28.57 0 0 1 2.4-7.79l-2.06-.11Z'/%3E%3Cpath fill='%23DC4472' d='M590.393 214.905s-1 4.58-.34 7.9a28.57 28.57 0 0 1 2.4-7.79l-2.06-.11Z'/%3E%3Cpath fill='%23DC4472' d='M591.543 214.905s-1 4.58-.34 7.9a28.57 28.57 0 0 1 2.4-7.79l-2.06-.11Zm4.8 2.86s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Zm4.01.69s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M601.503 219.005s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Zm-4.01-1.12s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M598.753 217.425s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Z'/%3E%3Cpath fill='%23DC4472' d='M599.553 218.115s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Zm26.68 1.71s.11 5.6 1.72 7.66c0 0-1-4.8-.11-8.24l-1.61.58Z'/%3E%3Cpath fill='%23DC4472' d='M627.493 219.375s-.11 5.72 1 7.66c0 0-.34-6.06.11-8.35l-1.11.69Z'/%3E%3Cpath fill='%23DC4472' d='M628.293 220.055s-.11 5.72 1 7.66c0 0-.34-6.06.11-8.35l-1.11.69Z'/%3E%3Cpath fill='%23DC4472' d='M629.003 219.515s.11 6.18 1.72 8.45c0 0-1-5.3-.11-9.08l-1.61.63Z'/%3E%3Cpath fill='%23DC4472' d='M630.243 219.005s-.11 6.3 1 8.45c0 0-.34-6.68.11-9.2l-1.11.75Z'/%3E%3Cpath fill='%23DC4472' d='M631.003 219.765s-.11 6.3 1 8.45c0 0-.34-6.68.11-9.2l-1.11.75Zm-21.6-.62s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M610.543 219.715s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Zm-4.01-1.14s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M607.793 218.115s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Z'/%3E%3Cpath fill='%23DC4472' d='M608.603 218.795s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Zm5.83 1.15s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M615.473 219.255s.23 6.87 1.83 8.93a28.69 28.69 0 0 1-.23-9.5l-1.6.57Z'/%3E%3Cpath fill='%23DC4472' d='M616.003 218.455s.23 6.87 1.83 8.93a28.69 28.69 0 0 1-.23-9.5l-1.6.57Z'/%3E%3Cpath fill='%23DC4472' d='M617.073 218.795s.23 6.87 1.83 8.93a28.69 28.69 0 0 1-.23-9.5l-1.6.57Zm-5.5.58s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M612.833 218.915s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Z'/%3E%3Cpath fill='%23DC4472' d='M613.633 219.605s.34 5.72 1.49 7.67c0 0-.8-6.07-.34-8.36l-1.15.69Zm5.5.45s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M620.273 219.005s.11 7.21 1.72 9.27a28.55 28.55 0 0 1-.11-9.85l-1.61.58Zm-1.94.71s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Zm13.85.04s.11 6.07 1.72 8.3c0 0-1-5.2-.11-8.92l-1.61.62Z'/%3E%3Cpath fill='%23DC4472' d='M633.213 219.005s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.29l-1.6.62Z'/%3E%3Cpath fill='%23DC4472' d='M633.783 218.135s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.29l-1.6.62Z'/%3E%3Cpath fill='%23DC4472' d='M634.823 218.515s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.29l-1.6.62Zm-3.44.86s.34 6.2 1.49 8.3c0 0-.8-6.57-.34-9l-1.15.7Zm5.5.5s.11 6.07 1.72 8.3c0 0-1-5.2-.11-8.92l-1.61.62Z'/%3E%3Cpath fill='%23DC4472' d='M638.003 218.765s.11 7.81 1.72 10a33.4 33.4 0 0 1-.11-10.66l-1.61.66Zm-1.93.74s-.11 6.2 1 8.3c0 0-.34-6.57.11-9l-1.11.7Zm4.7-.1s.11 6.07 1.72 8.3c0 0-1-5.2-.11-8.92l-1.61.62Z'/%3E%3Cpath fill='%23DC4472' d='M641.803 218.665s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.33l-1.6.66Z'/%3E%3Cpath fill='%23DC4472' d='M642.373 217.795s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.29l-1.6.62Z'/%3E%3Cpath fill='%23DC4472' d='M643.403 218.165s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.29l-1.6.62Zm-3.4.84s.34 6.2 1.49 8.3c0 0-.8-6.57-.34-9l-1.15.7Zm5.46.53s.11 6.07 1.72 8.3c0 0-1-5.2-.11-8.92l-1.61.62Z'/%3E%3Cpath fill='%23DC4472' d='M646.613 218.415s.11 7.81 1.72 10a33.4 33.4 0 0 1-.11-10.66l-1.61.66Zm-1.95.75s-.11 6.2 1 8.3c0 0-.34-6.57.11-9l-1.11.7Zm6.64-2.43s.23 8.09 1.83 10.51a39.63 39.63 0 0 1-.23-11.19l-1.6.68Z'/%3E%3Cpath fill='%23DC4472' d='M651.883 215.795s.23 8.09 1.83 10.51a39.63 39.63 0 0 1-.23-11.19l-1.6.68Z'/%3E%3Cpath fill='%23DC4472' d='M652.913 216.195s.23 8.09 1.83 10.51a39.63 39.63 0 0 1-.23-11.19l-1.6.68Zm2.74-.57s.23 8.09 1.83 10.51a39.63 39.63 0 0 1-.23-11.19l-1.6.68Z'/%3E%3Cpath fill='%23DC4472' d='M654.623 215.625s.23 8.09 1.83 10.51a39.63 39.63 0 0 1-.23-11.19l-1.6.68Z'/%3E%3Cpath fill='%23DC4472' d='M654.163 217.275s-.11 6.74 1 9c0 0-.34-7.14.11-9.84l-1.11.84Zm-29.99 1.64s.11 7.21 1.72 9.27a28.55 28.55 0 0 1-.11-9.85l-1.61.58Z'/%3E%3Cpath fill='%23DC4472' d='M625.083 219.005s.11 7.21 1.72 9.27a28.56 28.56 0 0 1-.11-9.85l-1.61.58Zm-3.78.48s.11 6.41 1.72 8.47a24.92 24.92 0 0 1-.11-9l-1.61.53Z'/%3E%3Cpath fill='%23DC4472' d='M622.333 219.255s.11 6.3 1.26 8.24c0 0-.34-6.07.11-8.36l-1.37.12Z'/%3E%3Cpath fill='%23DC4472' d='M623.363 219.485s.34 6.76 1.49 8.7c0 0-.8-6.07-.34-9.39l-1.15.69Zm36.99-11.22s-1.49 14.88 3.43 18.09c0 0-6.76-2.52-5.27-15.57l1.84-2.52Z'/%3E%3Cpath fill='%23DC4472' d='M658.523 212.845s-.46 9.5 3.32 13.17c0 0-5.61-3-4.12-14l.8.83Z'/%3E%3Cpath fill='%23DC4472' d='M657.713 214.565s-.23 6.87 2.4 11.45c0 0-4.12-3.78-3.21-11.91l.81.46Z'/%3E%3Cpath fill='%23DC4472' d='M657.143 215.005s-.69 6.53 1.95 11.11c0 0-4.12-3.78-3.21-11.91l1.26.8Zm-42.65-58.61s-.17 6.87 2.23 11.34c0 0-3.26-2.75-3.43-8.59l1.2-2.75Z'/%3E%3Cpath fill='%23DC4472' d='M614.003 159.315s-.17 6.87 2.23 11.34c0 0-3.26-2.75-3.43-8.59l1.2-2.75Z'/%3E%3Cpath fill='%23DC4472' d='M613.803 163.615s-.17 6.87 2.23 11.34c0 0-3.26-2.75-3.43-8.59l1.2-2.75Z'/%3E%3Cpath fill='%23DC4472' d='M613.293 165.675s-.17 6.87 2.23 11.34c0 0-3.26-2.75-3.43-8.59l1.2-2.75Zm47.23 29.54s2.92 13.05 15.29 10.48c0 0-11 5.5-17.35-6.53l2.06-3.95Zm-62.8-50.72s1.37-2.52 1.6-9.39c0 0 .23 7.33 1.37 11.22l-2.97-1.83Z'/%3E%3Cpath fill='%23DC4472' d='M599.553 145.865s1.37-2.52 1.6-9.39c0 0 .23 7.33 1.37 11.22l-2.97-1.83Z'/%3E%3Cpath fill='%23DC4472' d='M601.153 146.785s1.37-2.52 1.6-9.39c0 0 .23 7.33 1.37 11.22l-2.97-1.83Z'/%3E%3Cpath fill='%23DC4472' d='M603.003 147.005s1.37-2.52 1.6-9.39c0 0 .23 7.33 1.37 11.22l-2.97-1.83Z'/%3E%3Cpath fill='%23DC4472' d='M604.823 147.465s1.37-2.52 1.6-9.39c0 0 .23 7.33 1.37 11.22l-2.97-1.83Z'/%3E%3Cpath fill='%23514944' d='M464.433 311.005h-.55l-.8.1h-.13l-.33.06a8.92 8.92 0 0 0-1 .28c-.73.09-4.27.79-8 6.31a2.32 2.32 0 0 0-1.67-.6 1.92 1.92 0 0 0-1.53 1.16v.19a18.19 18.19 0 0 0-.62 4.62 13.5 13.5 0 0 0 .16 2c-1.65 3.25-5.27 9.07-8.08 11.69-.34.29-6.65 5.67-12.22 11.55a73.39 73.39 0 0 0-13.66 18.84c-5 9.91-10.49 27.57-10.75 43 0 .39-.36 6-.36 12.25a100.09 100.09 0 0 0 .51 11.13v.14c0 .18 1.09 4.32 4.28 6a6.23 6.23 0 0 0 4.49.44 3.51 3.51 0 0 0 1.11 2.3 3.08 3.08 0 0 0 2.82.52h-.19c.52 0 5.05-.57 6.5-10.51a5.17 5.17 0 0 0 3.46-2.93 5.63 5.63 0 0 0 .39-2.12 14.9 14.9 0 0 0-2.34-6.94c0-.74.06-1.47.06-2.2v-.52c0-.27.28-4.32.59-8.32v1.6l.18 1.62.08.59.09.56a44.82 44.82 0 0 0 5.63 16l.08.13v-.07l.56.89.39.58v2.28c0 3-.08 6.12-.16 9.11-.07 2.69-.14 5.24-.15 7.7v.63a64.24 64.24 0 0 0 .52 9.23v.08a7.67 7.67 0 0 0 3.14 5c.8.9 4.75 4.42 18.27 6.07 5.61.59 8.54-.22 11.23-1.61h.11a6.81 6.81 0 0 0 2.71-4.28 7 7 0 0 0-1.83-5.44 12.49 12.49 0 0 0-2.18-2l-2-1.61s-1.07-1-1.21-4.13a7.08 7.08 0 0 0 2.57 3.25c.8.9 4.75 4.42 18.27 6.07 5.6.59 8.54-.22 11.23-1.61h.11a6.8 6.8 0 0 0 2.71-4.28 7 7 0 0 0-1.83-5.45 12.57 12.57 0 0 0-2.18-1.95l-2-1.61s-1.2-1.12-1.2-4.77v-2.99c0-1.77.1-3.93.28-6.58 4.48-2.94 11.19-9.67 11.19-23.64 0-.74-.02-1.504-.06-2.29v-.42a53.76 53.76 0 0 0-2.45-14.43c3.67-2 5.93-4.69 6.67-8.09.181-.845.272-1.706.27-2.57 0-6.92-5.38-13.63-5.91-14.28a5.47 5.47 0 0 1-.54-1.18 5.39 5.39 0 0 0-.88-1.67c-1.42-2.1-4.42-3-5.51-3.26a9.33 9.33 0 0 0-2.77-2.62l-.27-.12h-.3a4.52 4.52 0 0 0-3.76 2.16l-.09.17v.19a5.76 5.76 0 0 0 0 .71v.3l-.28.13c-.25-.46-.5-.92-.77-1.36a79.06 79.06 0 0 1-7.66-16 15.18 15.18 0 0 1-1-5.36 28.18 28.18 0 0 1 .62-5.16l.15-.81a26.82 26.82 0 0 0 1.27-8c0-3.8-1-8.15-4.64-11.11a9.86 9.86 0 0 0-1.8-1.09l-.17-.09a14.94 14.94 0 0 0-3.81-1.34h-.07l-1-.15h-2l.01-.07Zm-.36 2.24h2.08l.79.12h.07a12.64 12.64 0 0 1 3.22 1.14l.19.1a7.9 7.9 0 0 1 1.41.84c3 2.41 3.79 6.1 3.79 9.4a25.32 25.32 0 0 1-1.15 7.31v.08l-.18 1a30 30 0 0 0-.64 5.54 17.47 17.47 0 0 0 1.11 6.14 82 82 0 0 0 7.85 16.37c.43.7.83 1.42 1.23 2.18l.5.95 3.68-1.78-.21-.9a2.6 2.6 0 0 1-.08-.67v-.19a2.67 2.67 0 0 1 1.58-.78 9.28 9.28 0 0 1 2.12 2.07l.26.33.41.08c.91.17 3.5.95 4.5 2.41l.06.09.07.07c.218.305.386.642.5 1a7.24 7.24 0 0 0 .82 1.69c.06.07 5.45 6.62 5.45 12.93a9.865 9.865 0 0 1-.22 2.11c-.64 3-2.76 5.31-6.3 7l-.86.41.28.91a53 53 0 0 1 2.6 14.9v.39c.04.793.06 1.563.06 2.31 0 12.67-5.69 19-10.63 22l-.48.3v.56c-.22 3-.33 5.34-.33 7.29v3c0 4.59 1.68 6.21 2 6.51l2 1.63a11 11 0 0 1 1.82 1.6 5.25 5.25 0 0 1 1.39 3.25c.01.153.01.307 0 .46a4.53 4.53 0 0 1-1.72 2.75c-2 1-4.35 1.92-9.89 1.34-13.73-1.68-16.89-5.36-16.92-5.4l-.13-.15-.19-.12a5.44 5.44 0 0 1-2.26-3.65 64 64 0 0 1-.49-8.84v-4.58l-2.72.05-.08 1c-.42 4.77-.62 8.25-.62 10.92v2.99c0 4.61 1.69 6.23 2 6.52l2 1.62c.664.464 1.275 1 1.82 1.6a5.25 5.25 0 0 1 1.39 3.25c.01.153.01.307 0 .46a4.53 4.53 0 0 1-1.72 2.75c-2 1-4.35 1.92-9.89 1.34-13.73-1.68-16.89-5.36-16.92-5.4l-.13-.15-.19-.12a5.44 5.44 0 0 1-2.26-3.66 62.46 62.46 0 0 1-.49-8.85v-.62c0-2.44.08-5 .15-7.65.08-3 .16-6.11.16-9.17v-3l-.2-.3-.55-.81-.55-.87a42.58 42.58 0 0 1-5.33-15.18l-.08-.55v-.05l-.07-.48-.18-1.58-.05-.62v-.18c-.11-2.29-.16-6.08-.16-11.26v-4l-2.19-.24c-.67 3-1.13 5.48-1.43 7.57-.5 3.47-1.23 15-1.26 15.52v.08c0 1 0 1.95-.06 3v.34l.17.29c1.88 3.14 2.6 5.83 2 7.38a3.17 3.17 0 0 1-2.35 1.76h-.17l-.93.62-.06.5c-1.12 8.63-4.51 9.13-4.54 9.14h-.16a1.15 1.15 0 0 1-.89-.05c-.23-.19-.45-.82-.38-2l.09-1.6-1.85.58h-.08a4.42 4.42 0 0 1-3.79 0c-2.18-1.13-3-4.15-3.14-4.49a98.16 98.16 0 0 1-.49-10.74c0-6.35.34-12.08.35-12.17.23-14.39 5.47-32 10.51-42.05a71.23 71.23 0 0 1 13.26-18.18c5.63-5.94 12-11.38 12.09-11.43 3.1-2.87 6.92-9 8.65-12.5l.08-.19.12-.3-.06-.31a10.79 10.79 0 0 1-.17-2 16.41 16.41 0 0 1 .41-3.54c.208.193.399.403.57.63l1 1.28.83-1.39c3.66-6.1 7.08-6.46 7.12-6.47h.19l.16-.05a6.8 6.8 0 0 1 .91-.25h.36l.64-.24Z'/%3E%3Cpath fill='%2342BBA2' d='M491.183 409.955c-1.61 15.4-2.67 25.12-2.4 30.93-.3 5.31 1.62 6.74 1.62 6.74 2.07 1.75 3.17 2.37 4 3.37 4 4.94-.64 8.11-.64 8.11-2.31 1.2-5 2.08-10.58 1.49-14.64-1.79-17.67-5.81-17.67-5.81a6.59 6.59 0 0 1-2.79-4.4c-1.1-7.44 0-18.57-.21-28.92-.21-12.66-1.94-14.47-1.94-16.76l30.61 5.25Z'/%3E%3Cpath fill='%2317A786' d='M491.143 457.945c-14.64-1.79-17.67-5.81-17.67-5.81a6.59 6.59 0 0 1-2.79-4.4c-1.1-7.44 0-18.57-.21-28.92a66.54 66.54 0 0 0-1.11-12.59l-8.8-1.51c0 2.29 1.74 4.1 1.94 16.76.17 10.36-.89 21.49.21 28.92a6.59 6.59 0 0 0 2.79 4.4s3 4 17.67 5.81c5.61.59 8.27-.29 10.58-1.49a5.89 5.89 0 0 0 1-1 30.36 30.36 0 0 1-3.61-.17Z'/%3E%3Cpath fill='%2342BBA2' d='M463.493 419.705c-1.61 15.4-2.67 25.12-2.41 30.93-.3 5.32 1.62 6.74 1.62 6.74 2.07 1.75 3.17 2.37 4 3.37 4 4.93-.64 8.11-.64 8.11-2.31 1.2-5 2.09-10.58 1.49-14.64-1.79-17.67-5.82-17.67-5.82a6.58 6.58 0 0 1-2.79-4.4c-1.1-7.44 0-18.57-.21-28.93-.21-12.66-1.94-14.47-1.94-16.76l30.62 5.27Z'/%3E%3Cpath fill='%2342BBA2' d='M472.533 314.685c-3.63-2.55-7.83-3.1-10.74-2.07 0 0-6 .29-10.82 12.92-1.55 3.1-5.32 9.3-8.42 12.18 0 0-13.73 13.51-14.58 27.28 0 0-.72 34.93-.24 45 0 0 .72 16.14 11.08 25.53 0 0 23.85 6.27 45.53-1.69 0 0 17-4.1 15.81-27.46 0 0 0-18-12.85-37 0 0-.52-3.38-3.77-8.71a80.51 80.51 0 0 1-7.75-16.17c-1.78-4.84-.89-8.42-.26-12 0 .12 4.25-12.72-2.99-17.81Z'/%3E%3Cpath fill='%2317A786' d='M426.163 402.265c2.36-16.54 15.05-53.34 13.59-60.85a69.94 69.94 0 0 0-22.75 26.29c-5.09 10.08-10.39 27.78-10.63 42.53 0 0-.89 14.47.15 23.19 0 0 1.92 7.83 8.42 5.32 0 0 10.19-2.44 10-21-.04-.05.72-12.01 1.22-15.48Z'/%3E%3Cpath fill='%2342BBA2' d='M490.513 363.755s5-1.7 10 5.32c0 0 12.59 16-4.91 22.85l-20.34-20s10.82-7.51 15.25-8.17Z'/%3E%3Cpath fill='%2317A786' d='M454.153 467.375c-1.27-2.44-4.93-3.42-6.79-5.14-3.34-3.11-2.09-8.75-1.72-13 .4-4.66 2.52-8.92 2.38-13.65-.12-4.07-1.93-6.87-3.46-10.55-2.12-5.07-2.1-10.54-1.5-16 .6-5.46 2.93-10.41 4.07-15.65.68-3.11 2.07-5.91 3.55-11.82 4.14-14.47 1.77-21.27 1.18-39.58-.32-9.94 5-29.24 18.9-28.38a11.72 11.72 0 0 0-9-1s-6 .29-10.82 12.92c-1.55 3.1-5.32 9.3-8.42 12.18 0 0-34.71 29.39-14.58 27.28 0 0-.72 34.93-.24 45 0 0 .53 11.68 7.06 21v.21c.17 10.36-.89 21.49.21 28.93a6.58 6.58 0 0 0 2.79 4.4s2.93 3.88 16.87 5.71a6.06 6.06 0 0 0-.48-2.86Z'/%3E%3Cellipse cx='464.563' cy='324.695' fill='%23FFF' rx='2.88' ry='4.76'/%3E%3Cpath fill='%23514944' d='M461.373 324.695c0 2.84 1.4 5.07 3.19 5.07s3.19-2.23 3.19-5.07c0-2.84-1.4-5.07-3.19-5.07s-3.19 2.22-3.19 5.07Zm.62 0c0-2.41 1.18-4.45 2.57-4.45s2.57 2 2.57 4.45-1.18 4.45-2.57 4.45-2.56-2.04-2.56-4.45h-.01Z'/%3E%3Cellipse cx='465.443' cy='325.535' fill='%23514E42' rx='1.77' ry='3.32'/%3E%3Cellipse cx='473.273' cy='324.835' fill='%23FFF' rx='2.88' ry='4.76'/%3E%3Cpath fill='%23514944' d='M470.083 324.835c0 2.84 1.4 5.07 3.19 5.07s3.19-2.23 3.19-5.07c0-2.84-1.4-5.07-3.19-5.07s-3.19 2.24-3.19 5.07Zm.62 0c0-2.41 1.18-4.45 2.57-4.45s2.57 2 2.57 4.45-1.18 4.45-2.57 4.45-2.57-2.03-2.57-4.45Z'/%3E%3Cellipse cx='474.163' cy='325.685' fill='%23514E42' rx='1.77' ry='3.32'/%3E%3Cpath fill='%2317A786' d='M415.523 436.825s-1.48 5.91 2.36 5c0 0 5.91-.29 5.91-16.24l-8.27 11.24Z'/%3E%3Cpath fill='%2317A786' d='M423.723 419.005s7.75 10.41.07 12.48l-1.79 1.2s-3-9.23 1.72-13.68Zm68.75-41.26a35.89 35.89 0 0 1 6.2 12.41s8.12-3.25 1.33-10.34c0 0-2.21-2.96-7.53-2.07Zm-10.6-21.44a54.16 54.16 0 0 0-11.11.47l8.86 20.68s6.5 3.25 10-2.36c.04-.01-2.73-16.72-7.75-18.79Zm-26.69-34.53s-2.66-5.1-3.77-2.88c0 0-2.22 7.53 2 10.63l1.77-7.75Z'/%3E%3Cpath fill='%23514944' d='M426.603 408.005h.89a90.07 90.07 0 0 1 5.73-34.59l-.82-.34a91 91 0 0 0-5.8 34.93Zm63.91-31.89c.05.06 5.07 6.08 8.1 19.53l.86-.2c-3.09-13.68-8.08-19.66-8.29-19.91l-.67.58Z'/%3E%3Cpath fill='%23514944' d='M478.123 354.745a1.5 1.5 0 0 0 .54 1.1l1.65 20.78v.07c0 1.91 4.26 2.12 6.09 2.12s6.09-.21 6.09-2.12v.06l2.39-21c.256-.27.4-.628.4-1 0-2.58-7.14-2.71-8.57-2.71-1.43 0-8.59.12-8.59 2.7Zm4 21.94v.12-.12Zm4.62-22.87a17 17 0 0 1 6.58 1h-.05l-.09.38-2.4 21.22a11.08 11.08 0 0 1-4.33.63 11.26 11.26 0 0 1-4.32-.62l-1.71-21.55-.2-.13a17.29 17.29 0 0 1 6.47-.94l.05.01Z'/%3E%3Cpath fill='%23DC443F' d='m494.233 354.745-14.63-.15 1.75 22a.28.28 0 0 0 0 .06c0 .68 2.33 1.23 5.21 1.23s5.21-.55 5.21-1.23l2.46-21.91Z'/%3E%3Cpath fill='%2342BBA2' d='M490.073 361.765s5.1-.45 6.65 1.55c0 0 3.77 2.88.44 7.09l-1.33 6.6s-3.25 1.55-7.38-1.25c0 0-2.81-2.07.74-3.4 0 0-3.1-1.11-1.55-4a3.86 3.86 0 0 1 2.66-1.11s-3.54-.44-3.1-4a3.49 3.49 0 0 1 2.87-1.48Z'/%3E%3Cpath fill='%23C2322B' d='m486.393 354.885-6.68-.07 1.75 22a.28.28 0 0 0 0 .06c0 .65 2.1 1.18 4.78 1.23l.15-23.22Z'/%3E%3Cellipse cx='486.843' cy='354.745' fill='%23FFF' rx='7.68' ry='1.82'/%3E%3Cpath fill='%23514944' d='m488.853 337.875.06 17.15-3.1-.34v-.8c0-3.63.08-8.88.08-11.9v-1.76l-12.89-3.84.89-3 14.96 4.49Zm-1.43 24.05a2.7 2.7 0 0 0-.9 2.09v.21a4.32 4.32 0 0 0 2 3.09 3.38 3.38 0 0 0-1.18.85 2.63 2.63 0 0 0-.64 1.77v.29a3.21 3.21 0 0 0 .86 2.15c.108.082.226.153.35.21a2.25 2.25 0 0 0-.78 1.73c-.006.1-.006.2 0 .3v.05a2.36 2.36 0 0 0 .62 1.53c.79.91 2.22 1.42 4.23 1.52v-.89c-2.17-.1-3.15-.7-3.59-1.19a1.47 1.47 0 0 1-.42-1v-.09a1.58 1.58 0 0 1 1.22-1.73l-.16-.87a1.14 1.14 0 0 1-.94-.25 2.33 2.33 0 0 1-.56-1.53 1.85 1.85 0 0 1 .41-1.44 3.78 3.78 0 0 1 2.28-1v-.89c-2.45 0-2.87-2.61-2.88-2.72a1.85 1.85 0 0 1 .6-1.55c1.6-1.45 5.71-.83 5.76-.83l.14-.88c-.12-.01-4.51-.67-6.42 1.07Zm-37.56-38.6a14.29 14.29 0 0 0 1.15 5.52l.79-.4a13.64 13.64 0 0 1-1.06-5.12h-.88Z'/%3E%3Cpath fill='%2317A786' d='M468.693 445.385a42.13 42.13 0 0 0 18.31-12.7s-7.09 6.5-23.33 5l5.02 7.7Z'/%3E%3Cpath fill='%23514944' d='M461.863 437.415h-.4l-.07.4a64.09 64.09 0 0 0 0 16.3l.88-.1a75 75 0 0 1-.49-8.45 54.08 54.08 0 0 1 .43-7.21c2.53.13 17.12.55 28.66-6.74l-.47-.75c-12.4 7.77-28.4 6.56-28.54 6.55Z'/%3E%3Cpath fill='%23FF6068' d='m473.393 335.495 13.51 4.14c.09.82 0 15.07 0 15.07l1.32.15v-16.24l-14.35-4.39-.48 1.27Z'/%3E%3Cpath fill='%23FF0' d='M235.433 571.895s-3.38-37.68-80.43-48.89a17.83 17.83 0 0 0 9 14.62s-8.44 6.75 5.06 15.19c0 0-11.25 9.56 8.44 18 0-.04 22.65 11.52 57.93 1.08Zm237.34 0s3.37-37.68 80.43-48.93a17.83 17.83 0 0 1-9 14.62s8.44 6.75-5.06 15.19c0 0 11.25 9.56-8.44 18 0 0-22.7 11.56-57.93 1.12Z'/%3E%3C/g%3E%3C/svg%3E");background-position:bottom var(--size-spacing-half-again) center;background-repeat:no-repeat;background-size:366px 481px;padding-bottom:530px}@media screen and (min-width:768px){.content-migrate{background-position:var(--size-spacing-quadruple) var(--size-spacing-quadruple);min-height:calc(481px + var(--size-spacing-quadruple));padding-bottom:0;padding-left:430px}}.content-migrate p{font-size:1.25rem;margin:var(--size-spacing-triple) 0}.content-membership-thanks{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 625 821'%3E%3Cg fill='none' transform='translate(.904)'%3E%3Cpath fill='%23FFFFCF' d='M27.336 191.6s-63.8-86.76 33.89-180.69c0 0 5.52-4.18 4.58 1.47-4.32 26.23-18.65 112.72 28.29 170.32l-66.76 8.9Zm569.15 0s63.79-86.76-33.89-180.69c0 0-5.52-4.18-4.58 1.47 4.32 26.23 18.65 112.71-28.29 170.32l66.76 8.9Z'/%3E%3Cpath fill='%23000' d='M623.096 122.64s0 .07-.06.11c-.41-4-3.46-7.18-7.21-7.18a7.14 7.14 0 0 0-6.11 3.67 6.84 6.84 0 0 0-4.79-2 7.11 7.11 0 0 0-6 3.45 6.77 6.77 0 0 0-11 .12 7.12 7.12 0 0 0-6-3.47 6.89 6.89 0 0 0-4.93 2.15 7.15 7.15 0 0 0-6.17-3.79 6.9 6.9 0 0 0-5 2.21 7.09 7.09 0 0 0-5.89-3.35c-3.65 0-6.65 3-7.18 6.85-7.63 47.16-51.46 53.63-80.38 53.33-17.54-20.12-36.23-37.81-36.23-37.81-24.19-22-39.57-36.11-53.87-50.86C331.586 33.69 316.406 0 316.406 0c-3.92 7.7-14.31 29.89-60.44 79.09-14.51 15.48-37.09 37.81-58.27 57.82 0 0-16.33 16.58-33.38 37.25-26.29 1.89-84.47 2.26-93.37-52.77-.53-3.87-3.53-6.85-7.18-6.85a7.09 7.09 0 0 0-5.89 3.35 6.9 6.9 0 0 0-5-2.21 7.15 7.15 0 0 0-6.17 3.79 6.89 6.89 0 0 0-4.93-2.15 7.12 7.12 0 0 0-6 3.47 6.77 6.77 0 0 0-11-.12 7.11 7.11 0 0 0-6-3.45 6.85 6.85 0 0 0-4.79 2 7.14 7.14 0 0 0-6.11-3.67c-3.75 0-6.81 3.15-7.21 7.18 0 0 0-.07-.06-.11-.06-.04-15.33 114.51 111.16 139.31-12.91 33-19.79 70.82-13 111.74 0 0 11.1 116.08 142.18 151.19-17.94 8.37-28.2 17.14-28.2 17.14a136.11 136.11 0 0 0-15.09 15.13v-.29c-20.42 26.62-41.47 29.09-41.47 29.09a48.71 48.71 0 0 0 24.23-3l-.09.17c-12.8 12.11-23.71 14.45-23.71 14.45a51.13 51.13 0 0 0 18.29-2.85c-5.89 13.94-10.79 30.56-14.27 50.69-2 7.38-8.44 41.8 15.23 41.4 23.67-.4 26.93-18.43 23-33.67 5.14-28.32 15-65.43 45.73-88.65 0 12.44 1.66 203.5.87 216.36a167 167 0 0 0-15.53 11.17c-15.35 13.65-2.27 27.1 25.79 28.17 46.76 1.41 55-13.77 55.79-22.39.27-15.49-.7-63.44-.82-63.41.24-10.47-.2-20.55.68-26.62.77-5.35 3.54-7.62 7-7.73h.52c3.42.11 6.19 2.38 7 7.73.88 6.07.43 16.15.68 26.62-.12 0-1.09 47.92-.83 63.41.75 8.62 9 23.79 55.8 22.39 28.06-1.07 41.15-14.52 25.79-28.17-3.6-3.2-11.83-7.41-15-9.42-.8-12.87.3-205.78.34-218.22v-3.33c2.79.57 4.37.77 4.37.77a133.62 133.62 0 0 0 30.9.31c26.47.41 39.2 11.47 39.2 11.47a49.64 49.64 0 0 0-16.46-14.83c16.95 1.49 25.68 7.75 25.68 7.75a50.59 50.59 0 0 0-15.24-12c14.37-5 30.08-12.61 47.45-23.66 6.84-3.46 34.69-19.15 20.39-39.12a33.76 33.76 0 0 0-3.55-5.37c2.1-2.68 10.48-16.33 4.62-21.5-5.86-5.17-13.71-1-24.09 14.77a5.15 5.15 0 0 1-.48.59s-5.27-6.75-11.42-2c0 0-5.49 3.3-5.5 19.12-23.86 14.9-57 32.09-93.79 26.22 132.69-37 140.6-149.9 140.6-149.9 6.62-41.12.32-80.31-12.11-114.21 114.39-29.1 99.85-136.8 99.85-136.8Z'/%3E%3Cellipse cx='283.036' cy='160.51' fill='%23FFFFCF' rx='24.96' ry='33.33'/%3E%3Cellipse cx='283.026' cy='160.52' fill='%23000' rx='14.58' ry='19.47'/%3E%3Cellipse cx='341.796' cy='160.51' fill='%23FFFFCF' rx='24.96' ry='33.33'/%3E%3Cellipse cx='341.786' cy='160.52' fill='%23000' rx='14.58' ry='19.47'/%3E%3Ccircle cx='312.406' cy='203.84' r='7.18' fill='%23FF005D'/%3E%3Cpath fill='%23F09' d='M349.756 261c49.37-1.78 95.9-8.11 137.51-18.09-7.62 62.5-52.62 114-112.94 133.41-11.71-13.11-30.9-21.66-52.6-21.66-21.7 0-41.08 8.64-52.78 21.85-60.64-19.21-105.93-70.88-113.58-133.61 42.27 10.14 89.61 16.52 139.84 18.18 0 .04 28.13 1.38 54.55-.08Z'/%3E%3Cpath fill='%23A80058' d='M321.726 354.7c-21.81 0-41.08 8.64-52.78 21.85a174.37 174.37 0 0 0 105.38-.2c-11.71-13.1-30.9-21.65-52.6-21.65Z'/%3E%3Crect width='70.28' height='70.28' x='282.996' y='547.64' fill='%23F09' rx='3.75'/%3E%3Cpath fill='%23FFF' d='M333.306 594.29a2.38 2.38 0 0 0-3.25.87 13.75 13.75 0 0 1-23.83 0 2.382 2.382 0 0 0-4.12 2.39 18.51 18.51 0 0 0 32.08 0 2.38 2.38 0 0 0-.88-3.26Zm-41.56-25.73a9.74 9.74 0 1 0 19.47-.624 9.74 9.74 0 0 0-19.47.624Zm33.35 0a9.74 9.74 0 1 0 19.47-.624 9.74 9.74 0 0 0-19.47.624Z'/%3E%3C/g%3E%3C/svg%3E");background-position:bottom var(--size-spacing-half-again) center;background-repeat:no-repeat;background-size:312px 410px;padding-bottom:450px}@media screen and (min-width:768px){.content-membership-thanks{background-position:var(--size-spacing-quadruple) var(--size-spacing-quadruple);min-height:calc(410px + var(--size-spacing-quadruple));padding-bottom:0;padding-left:380px}}.content-membership-thanks a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.content-membership-thanks a:active,.content-membership-thanks a:focus,.content-membership-thanks a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.content-membership-thanks a:active,.content-membership-thanks a:focus,.content-membership-thanks a:hover{text-decoration:underline}.membership-header{margin-bottom:var(--size-spacing-double)}@media screen and (min-width:480px){.membership-header{display:flex}}@media screen and (min-width:768px){.membership-header{margin-bottom:var(--size-spacing-quadruple)}}.membership-header h2{font-size:2rem;line-height:1.1;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-double)}.membership-header p{font-size:1.2rem;margin:0}.membership-header--illustration{display:none}@media screen and (min-width:480px){.membership-header--illustration{display:block;flex:none;margin-right:var(--size-spacing-half-again);width:75px}}@media screen and (min-width:768px){.membership-header--illustration{margin-right:var(--size-spacing-quadruple);width:140px}}@media screen and (min-width:480px){.membership-header--content{flex:1}}.membership-options>*{margin:var(--size-spacing-double) 0}@media screen and (min-width:768px){.membership-options{display:flex}.membership-options>*{margin:0}.membership-options>*+*{margin-left:var(--size-spacing-quadruple)}}@media screen and (min-width:768px){.membership-options--plan{flex:1}}.membership-options--plan .label{margin-bottom:var(--size-spacing-half)}.membership-options--plan .btn{text-transform:uppercase}.membership-options--plan h3{font-size:2rem}.membership-options--plan p{margin-top:.25em}.membership-options--plan ul{margin-bottom:2em}@media screen and (min-width:768px){.is-active-member .membership-options--plan{padding-top:var(--size-spacing-quadruple)}.is-active-member .membership-options--plan.is-active-plan{padding-top:0}}.membership-options--plan-title{color:var(--color-brand-primary);text-transform:uppercase}.membership-options--plan-tagline{font-size:1.25rem}.membership-options--or{align-items:center;display:flex;justify-content:center}@media screen and (min-width:768px){.membership-options--or{flex-direction:column}}.membership-options--or:after,.membership-options--or:before{background:var(--color-brand-primary);content:"";flex:1;height:1px}@media screen and (min-width:768px){.membership-options--or:after,.membership-options--or:before{height:auto;width:1px}}@media screen and (min-width:768px){.membership-options--or:before{flex-basis:var(--size-spacing-triple);flex-grow:0}}.membership-options--or-bullet{background-color:var(--color-brand-primary);border-radius:50%;color:var(--color-text-light-emphasis);display:flex;font-size:1.5rem;height:3em;margin-left:auto;margin-right:auto;width:3em}.membership-options--or-bullet span{margin:auto}.membership-footer{margin-top:var(--size-spacing-half-again);text-align:center}@media screen and (min-width:768px){.membership-footer{margin-top:var(--size-spacing-quadruple)}}.membership-footer p{margin:0}.content-permalink{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-permalink{padding:var(--size-spacing-triple)}}.content-permalink>*{margin:0!important;padding:0!important}.content-permalink>*+*{margin-top:var(--size-spacing-half-again)!important}@media screen and (min-width:768px){.content-permalink>*+*{margin-top:var(--size-spacing-triple)!important}}@media screen and (min-width:768px){.content-permalink>.permalink-sidebar{float:right;width:330px}}@media screen and (min-width:768px){.content-permalink>.image-comment-form,.content-permalink>.image-comments,.content-permalink>.image-content{float:left;width:calc(100% - 360px)}}@supports (display:grid){@media screen and (min-width:768px){.content-permalink{grid-gap:var(--size-spacing-triple);display:grid;grid-template-areas:"title title" "image sidebar" "comments sidebar" "post-comment sidebar";grid-template-columns:1fr 330px;grid-template-rows:repeat(3,min-content) 1fr}.content-permalink>*{margin:0!important}.content-permalink:after{content:none}.content-permalink>.image-comment-form,.content-permalink>.image-comments,.content-permalink>.image-content,.content-permalink>.permalink-sidebar{width:auto}.content-permalink>.image-title{grid-area:title}.content-permalink>.image-content{grid-area:image}.content-permalink>.permalink-sidebar{grid-area:sidebar}.content-permalink>.image-comments{grid-area:comments}.content-permalink>.image-comment-form{grid-area:post-comment}}}.permalink-sidebar{position:relative}.tools-page{background-color:var(--color-background-page)}.tools-page a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.tools-page a:active,.tools-page a:focus,.tools-page a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.tools-page .header{color:var(--color-page-text-secondary);font-size:.75rem;height:55px;margin:0 var(--size-spacing-double);padding-top:var(--size-spacing-half);position:relative;text-align:right}.tools-page .header a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.tools-page .header a:active,.tools-page .header a:focus,.tools-page .header a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.tools-page .header a:active,.tools-page .header a:focus,.tools-page .header a:hover{text-decoration:underline}.tools-page .header img{height:49px;left:0;position:absolute;top:3px;width:100px}.tools-page .content{background:var(--color-background-content);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.tools-page .content-inner{display:flex;flex-direction:column;margin:0 auto}@media screen and (min-width:768px){.tools-page .content-inner{flex-direction:row;justify-content:space-between}}.tools-page .content-inner>*{min-width:0}@media screen and (min-width:768px){.tools-page .content-inner>*{flex-basis:calc(50% - 10px)}}.tools-page .content-inner>*+*{margin-top:20px}@media screen and (min-width:768px){.tools-page .content-inner>*+*{margin-top:0}}@media screen and (min-width:768px){.tools-page .content-inner .full-width{flex:1}}.tools-page .tools-fun-form{margin-top:0}.tools-page .tools-fun-form .tools-field-title .input-text{font-weight:var(--number-font-weight-bold)}.tools-page .tools-saved-it{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 616 256'%3E%3Cg fill='none'%3E%3Cpath fill='%23ED003A' d='M181.23 158.14c3.76-5.57 2-6.88 2-6.88a1.59 1.59 0 0 0-2.38-.11 11.18 11.18 0 0 0-1.84 2.44c-.86 1.42-1.84 2.95-2.67 4a38.64 38.64 0 0 1-4.66 5.17l-.42-.21c-15.16 11.83-28.86 7.38-30.19 6.91v-3.11c-12.08-9.74-24.6-7.22-24.6-7.22-17.11 0-19.12 7.22-19.12 7.22l.1 3.64-1-.57S82.1 175 66.14 162.56l-.42.21a38.62 38.62 0 0 1-4.66-5.17c-.82-1.09-1.81-2.62-2.67-4a11.18 11.18 0 0 0-1.84-2.44 1.59 1.59 0 0 0-2.38.11s-1.75 1.31 2 6.88c0 0-5.45 3.26-2.37 8.36a9.83 9.83 0 0 0 5.06 4.34s17.06 14.46 38.75 9.23c0 9.62-.05 31.32-.09 46a3.15 3.15 0 0 1-.32-.25l.23 14.44a28 28 0 0 0-4.2 3.16 5 5 0 0 0-1.84 3.64 9.35 9.35 0 0 0-.2 3.79s.31 2 3.12 3.35a18.88 18.88 0 0 0 8.05 1.53s6.37.08 10.3-1.53c2.48-.86 4.84-2.55 5.59-4.47 0 0 .74-4.35.12-6.46a2.68 2.68 0 0 0-.8-1.13c0-6-.15-15.94-.15-15.94l-.11.06c0-2.35-.1-4.59-.1-6.25 0-3 .58-3.95 2.17-3.95 2.7 0 2.53 3.89 2.53 3.89l.12 6.28s.16 10.17.22 16.22a3.06 3.06 0 0 0-.6.85c-.62 2.11.12 6.46.12 6.46.74 1.92 3.11 3.61 5.59 4.47 3.93 1.61 10.3 1.53 10.3 1.53a18.88 18.88 0 0 0 8.05-1.53c2.81-1.39 2.84-3.35 2.84-3.35a11.19 11.19 0 0 0-.5-4.15l-.11-.08a6.12 6.12 0 0 0-1.71-3.19 20.13 20.13 0 0 0-3.71-3v-.25c0-1.24-.07-7.24-.2-14.69l-.2.15c-.35-14.13-.79-34.5-1-45.21 21.09 4.39 37.48-9.51 37.48-9.51a9.83 9.83 0 0 0 5.06-4.34c3.02-5.22-2.43-8.48-2.43-8.48Z'/%3E%3Cpath fill='%23FFF' d='m118.12 171-12.09 19.27h8.51l-13.45 24.64 30.92-30.02h-8.96l9.41-13.89z'/%3E%3Cpath fill='%2317B6E1' d='m59.92 54.48-25.3-21.62-1.82 12.32L0 20.47l36.85 51.15 1.92-12.97 18.09 16.58zm116.47 0 25.3-21.62 1.82 12.32 32.8-24.71-36.85 51.15-1.92-12.97-18.09 16.58z'/%3E%3Cellipse cx='117.5' cy='104.42' fill='%23FFF' rx='68.1' ry='62'/%3E%3Cpath fill='%23FF0042' d='M119.45 31.21s-82.19-3-78.59 73.94c4.08 63 61.29 66.76 77.51 66.76 19.88 0 73.46-4.69 76.8-66.76 0 0 6.08-71.77-75.72-73.94Z'/%3E%3Cpath fill='%23FFF' d='M119.45 33.18c69.72 1.85 75.81 54.1 76.07 69.76.21-12.11-2.62-70-76.07-72 0 0-79.83-2.87-78.94 70.53.71-71.09 78.94-68.29 78.94-68.29Z'/%3E%3Cpath fill='%23D9002B' d='M195.2 99.1c-3.35 62.27-56.86 66.9-76.8 66.9-16.28 0-73.4-3.86-77.5-67 0-.58-.023-1.153-.07-1.72-.08 2.51-.1 5.1 0 7.8 4.1 63.16 61.29 67 77.57 67 19.94 0 73.45-4.71 76.79-67a54.87 54.87 0 0 0 0-6.45c.02.37.01.47.01.47Z'/%3E%3Ccircle cx='148.47' cy='44.32' r='1.58' fill='%23FFF'/%3E%3Cpath fill='%2300DCD7' d='M118.37 56.81c-67-1.18-63.78 41.51-63.78 41.51-.16 4.82-1 62.77 63.77 62.77 64.78 0 63.93-58 63.77-62.77.02.01 3.22-42.69-63.76-41.51Z'/%3E%3Cpath fill='%2317B6E1' d='M54.58 101.33C54.92 93 60 60.71 118.37 61.74c58.36-1 63.44 31.25 63.78 39.58v-3s3.19-42.69-63.78-41.51c-67-1.18-63.78 41.51-63.78 41.51-.02.5-.04 1.54-.01 3.01Z'/%3E%3Ccircle cx='117.88' cy='91.49' r='2.25' fill='red'/%3E%3Cpath fill='%23FFF' d='M102.09 80.62c-.225 6.41-5.563 11.445-11.974 11.297-6.411-.149-11.51-5.426-11.438-11.839.072-6.413 5.289-11.574 11.702-11.578a11.91 11.91 0 0 1 11.71 12.12Z'/%3E%3Cellipse cx='90.34' cy='80.65' fill='%23231F20' rx='3.72' ry='3.85'/%3E%3Cpath fill='%23FFF' d='M133.78 80.62c.225 6.41 5.563 11.445 11.974 11.297 6.411-.149 11.51-5.426 11.438-11.839-.072-6.413-5.289-11.574-11.702-11.578a11.91 11.91 0 0 0-11.71 12.12Z'/%3E%3Cellipse cx='145.54' cy='80.65' fill='%23231F20' rx='3.72' ry='3.85'/%3E%3Cpath fill='%23EB2D74' d='M125.11 114a159.49 159.49 0 0 0 31.26-4.11c-1.73 14.21-12 25.92-25.67 30.33-6.634-6.611-17.366-6.611-24 0-13.7-4.32-24.04-16.06-25.78-30.32a159.79 159.79 0 0 0 31.79 4.1s6.39.34 12.4 0Z'/%3E%3Cpath fill='%23C00058' d='M118.73 135.3a16 16 0 0 0-12 5 39.64 39.64 0 0 0 24 0 16 16 0 0 0-12-5Z'/%3E%3Cpath fill='%23FF0' d='M159.59 117s66.31-14 66.31-40 5.62-64 41.86-64c10.67 0 41-7.41 105.24-9C527 .19 563.83 0 563.83 0s51 10 51 48 6 116-51 116S482 169 429 169c-53 0-147.5 6.5-154 0-3.36-3.36-11.86-3.3-23.18-13-10.56-9-24.1-35-24.1-35s-12.08 8.46-32.37 7c-27.83-2-35.76-11-35.76-11Z'/%3E%3Cpath fill='%23FF0180' d='M263.62 123.39 263 109a39.67 39.67 0 0 0 18.49 4.12c4.9-.21 8.59-2.87 8.41-7-.19-4.5-4.37-6.32-7.1-6.91l-6.47-1.53c-6.07-1.44-15.4-6.75-15.93-19.34-.52-12.19 10-19.94 21.65-20.44a41.17 41.17 0 0 1 16.2 2l.61 14.39A28.15 28.15 0 0 0 284 70.67c-4.9.21-9.38 3.1-9.18 7.8.18 4.2 4.65 5.81 7.38 6.49l5.46 1.37c8.29 2.05 16.12 7.22 16.6 18.51.58 13.69-9.3 21.41-22.19 22a41.21 41.21 0 0 1-18.45-3.45Zm90.26-.52-12.29.52-.1-2.4a18.31 18.31 0 0 1-10.65 3.75c-12.19.52-22.21-9.27-22.73-21.46a22.45 22.45 0 0 1 44.86-1.9l.91 21.49Zm-23.82-30.62c-5.546.208-9.873 4.872-9.666 10.419.207 5.546 4.871 9.874 10.417 9.667 5.546-.207 9.875-4.87 9.669-10.416a10.05 10.05 0 0 0-10.42-9.67Zm35.09 25.13-13.7-37.56 13.29-.56 8.39 25.57 6.3-26.19 13.19-.56-10.37 38.58c-1.13 4.15-4.15 6.08-8.34 6.26-4.19.18-7.29-1.5-8.76-5.54Zm71.44-12.53s-4.14 15.59-20.53 16.28a22.45 22.45 0 0 1-1.9-44.86c11.69-.5 22 8.38 22.39 18.07.19 4.6-2 8.29-7.45 8.52l-22.68 1a9.57 9.57 0 0 0 16.28 1.61l13.89-.62ZM423.3 93.7a8.94 8.94 0 0 0-8.63-5.14 9.23 9.23 0 0 0-8.67 5.87l17.3-.73Zm61.45 2.1c.525 12.388-9.092 22.855-21.48 23.38-12.388.525-22.855-9.092-23.38-21.48-.52-12.29 8.64-22.89 20.83-23.4a18 18 0 0 1 10.42 2.56l-1.1-26 12.79-.54 1.92 45.48Zm-22.91-9.14c-5.546.208-9.873 4.872-9.666 10.419.207 5.546 4.871 9.874 10.417 9.667 5.546-.207 9.875-4.87 9.669-10.416a10.05 10.05 0 0 0-10.42-9.67ZM515 52a7.6 7.6 0 1 1 .646 15.186A7.6 7.6 0 0 1 515 52Zm-3.68 64.22-1.82-43.09 12.79-.54 1.83 43.06-12.8.57ZM527.16 53l12.84-.58.82 19.38 8.09-.34.5 11.89-8.09.34.53 12.49c.2 4.8 3.39 6.66 6.88 6.51l2.2-.09.5 11.89-2.2.09c-13.49.57-19.76-8.07-20.18-17.86L527.16 53Zm29.77 41-2.26-53.41L569.5 40l2.26 53.41-14.83.59Zm7.65 3.75a8.58 8.58 0 1 1 1.12 17.123 8.58 8.58 0 0 1-1.12-17.123Z'/%3E%3C/g%3E%3C/svg%3E") 0 0 no-repeat;background-size:contain;font-size:1.75rem;padding-top:calc(41.5625% + var(--size-spacing-double))}@media screen and (min-width:768px){.tools-page .tools-saved-it{background-size:320px 133px;padding-top:160px}}.tools-page .content-sign-in{margin-top:var(--size-spacing-double)}.tools-page .content-sign-in h1{color:var(--color-brand-secondary);font-size:1.5rem;padding:var(--size-spacing-triple)}.tools-page .tools-signin-logo{margin-top:var(--size-spacing-default);text-align:center}.tools-page .sign-in-fun-form{margin-top:0;padding-bottom:var(--size-spacing-double)}@media screen and (min-width:768px){.tools-page .sign-in-fun-form label{flex-basis:120px}.tools-page .sign-in-fun-form .field-submit{padding-left:130px}}.tools-page .email-unverified h2,.tools-page .over-upload-limit h2{color:var(--color-brand-primary);font-size:1.5rem;text-align:center}.content-relationships .body .friend,.content-relationships .body .shake{border-bottom:1px dashed var(--color-border-default);padding:0 var(--size-spacing-quadruple)}.content-relationships .body .user-follow-extended .website a{color:var(--color-brand-secondary)}.content-relationships .user-info .avatar{float:left;margin-right:var(--size-spacing-half-again)}.content-relationships .user-info .avatar--img{height:var(--size-avatar-large);width:var(--size-avatar-large)}.content-relationships .user-info .details h3{color:var(--color-page-text);font-size:1.125rem;font-weight:var(--number-font-weight-bold);padding-top:var(--size-spacing-half)}.content-search{list-style:none}.content-search .search-empty-results{margin:var(--size-spacing-double)}.sidebar-search-block{margin-bottom:var(--size-spacing-double)}.sidebar-search-block .field-help{padding-left:0}.settings-header{align-items:center;background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));display:flex;flex-direction:column;justify-content:space-between;padding:1.5rem 1.875rem 1rem;width:100%}.settings-header .avatar{display:flex}.settings-header .avatar img{display:block;height:50px;width:50px}.settings-header .avatar-media{flex:none}.settings-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.settings-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.settings-header h2{font-size:2.625rem}.settings-header{flex-direction:row}}@media screen and (min-width:768px){.settings-header>*{flex:1;max-width:calc(50% - var(--size-spacing-double))}}.settings-header h1{font-size:3rem;margin-bottom:var(--size-spacing-half-again)}@media screen and (min-width:768px){.settings-header h1{margin-bottom:0}}.settings-navigation ul{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);display:flex;list-style:none;margin:0;padding:var(--size-spacing-half-again);padding-bottom:0}@media screen and (min-width:480px){.settings-navigation ul{padding-left:var(--size-spacing-double)}}@media screen and (min-width:480px){.settings-navigation li+li{margin-left:var(--size-spacing-half-again)}}.settings-navigation a{border-top-left-radius:var(--size-border-radius-default);border-top-right-radius:var(--size-border-radius-default);color:var(--color-text-link-primary);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default);padding-bottom:var(--size-spacing-half-again);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.settings-navigation a:active,.settings-navigation a:focus,.settings-navigation a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.settings-navigation a:active,.settings-navigation a:focus,.settings-navigation a:hover{text-decoration:underline}.settings-navigation .selected a{background-color:var(--color-background-content)}.settings-body{display:flex;flex-direction:column;justify-content:space-between;padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.settings-body{flex-direction:row;padding:var(--size-spacing-quadruple)}}.settings-body>*{margin:0}@media screen and (min-width:768px){.settings-body>*{flex:1;max-width:calc(50% - var(--size-spacing-double))}}.settings-body>*+*{margin-top:var(--size-spacing-triple)}@media screen and (min-width:768px){.settings-body>*+*{margin-top:0}}.settings-subscription-sidebar h4{border-bottom:1px dashed var(--color-border-default);font-size:1.125rem;margin-bottom:var(--size-spacing-default);padding-bottom:.25em}.settings-subscription-sidebar .member-status-block,.settings-subscription-sidebar .migration-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-half-again)}.settings-subscription-sidebar .member-status-block h3,.settings-subscription-sidebar .migration-block h3{color:var(--color-status-success-pastel-dark)}.settings-subscription-sidebar .member-status-block:first-child,.settings-subscription-sidebar .migration-block:first-child{margin-top:0}.settings-subscription-sidebar .member-status-block:last-child,.settings-subscription-sidebar .migration-block:last-child{margin-bottom:0}.settings-subscription-sidebar .migration-block{background-color:var(--color-bg-secondary-brand-pastel);margin-bottom:var(--size-spacing-double)}.settings-subscription-sidebar .migration-block h3{color:var(--color-brand-secondary)}.settings-subscription-sidebar .member-status-block-content,.settings-subscription-sidebar .migration-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.settings-subscription-sidebar .transaction-list li{margin-bottom:var(--size-spacing-default)}.settings-subscription-sidebar .transaction-list li .id{color:var(--color-page-text-secondary)}.settings-subscription-sidebar .transaction-list li .amount{font-weight:var(--number-font-weight-bold)}.settings-subscription-sidebar p{margin:var(--size-spacing-double) 0}.settings-body-content .fun-form{margin-top:0}.settings-body-sidebar .profile-photo{display:flex;flex-direction:column}@media screen and (min-width:480px){.settings-body-sidebar .profile-photo{flex-direction:row}}.settings-body-sidebar .profile-photo-media{flex:none}.settings-body-sidebar .profile-photo-media .avatar--img{height:var(--size-avatar-large);width:var(--size-avatar-large)}.settings-body-sidebar .profile-photo-meta{flex:1;padding-top:var(--size-spacing-default)}@media screen and (min-width:480px){.settings-body-sidebar .profile-photo-meta{margin-left:var(--size-spacing-default);padding-top:var(--size-spacing-half)}}.settings-body-sidebar .profile-photo-meta h3{font-size:1.125rem;margin-bottom:var(--size-spacing-double)}.settings-body-sidebar .profile-photo-meta h4{font-size:.875rem;margin-bottom:var(--size-spacing-default)}.settings-body-sidebar .profile-photo-meta .settings-photo-upload{color:var(--color-page-text-secondary);font-size:.75rem}.settings-body-sidebar .info-block{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);margin-top:var(--size-spacing-quadruple);padding:var(--size-spacing-half-again)}@media screen and (min-width:480px){.settings-body-sidebar .info-block{padding:var(--size-spacing-double)}}.settings-body-sidebar .info-block h3{font-size:1rem;margin-bottom:var(--size-spacing-default)}.settings-body-sidebar .info-block p{font-size:.875rem;margin-bottom:0}.settings-body-connections{display:block}.settings-body-connections>*{max-width:none}.settings-body-connections h3{font-size:1.125rem}.settings-body-connections .apps{list-style:none;margin:var(--size-spacing-triple) 0;padding:0}.settings-body-connections .apps li{margin-bottom:var(--size-spacing-double)}.settings-body-connections .apps h4{color:var(--color-page-text-secondary);font-size:1.125rem;margin-bottom:var(--size-spacing-half)}.settings-body-connections .apps h4 .title{color:var(--color-brand-primary)}.settings-body-connections .apps h4 .by{font-size:.875rem}.settings-body-connections .apps h4 .by a{text-decoration:none}.settings-body-connections .apps p{font-size:.875rem;margin-bottom:var(--size-spacing-half)}.settings-body-connections .apps .disconnect{font-size:.75rem;text-decoration:none}#sign-in-form .field-submit{text-align:right}.forgot-password-field{padding-top:var(--size-spacing-double);text-align:right}.password-manager-icon{float:left;padding-right:var(--size-spacing-double)}.steps{display:none}.steps p{text-align:center} \ No newline at end of file +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{-webkit-text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{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-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}:root{--color-background-content-dark:#263238;--color-background-content-light:#fff;--color-background-page-light:#dbfaff;--color-background-page-dark:#13454c;--color-base-pink:#ff0080;--color-base-pink-dark:#c06;--color-base-pink-pastel-dark:#ff0080;--color-base-pink-pastel-light:#ffcce8;--color-base-blue:#00aeff;--color-base-blue-dark:#008bcc;--color-base-blue-very-light:#f2fdff;--color-base-blue-pastel-dark:#00aeff;--color-base-blue-pastel-medium:#aaf3ff;--color-base-blue-pastel-light:#daf3ff;--color-base-blue-pastel-very-light:#dbfaff;--color-base-blue-gray-light:#e4eff1;--color-base-cyan-very-dark:#13454c;--color-base-green:#09b896;--color-base-green-dark:#079378;--color-base-green-pastel-dark:#0caf8b;--color-base-green-pastel-light:#b9ff8d;--color-base-green-pastel-very-light:#dfffcb;--color-base-orange:#ff9600;--color-base-orange-dark:#cc7800;--color-base-orange-pastel-dark:#ff9600;--color-base-orange-pastel-light:#ffe466;--color-base-red:#f30;--color-base-red-dark:#cc2900;--color-base-red-pastel-dark:#ff1c1c;--color-base-red-pastel-light:#ffd4d4;--color-base-gray:#a1a1a1;--color-base-gray-light:#e9e9e9;--color-base-gray-pastel-dark:#8d8d87;--color-base-gray-pastel-light:#cecec6;--color-base-yellow-pastel-light:#fffbd9;--color-base-yellow-transparent-600:rgba(252,255,0,.66);--color-base-black-transparent:transparent;--color-base-black-transparent-100:rgba(0,0,0,.13);--color-base-black-transparent-200:rgba(0,0,0,.25);--color-base-black-transparent-300:rgba(0,0,0,.35);--color-base-black-transparent-500:rgba(0,0,0,.5);--color-base-black-transparent-600:rgba(0,0,0,.6);--color-base-black-transparent-800:rgba(0,0,0,.8);--color-base-white-transparent:hsla(0,0%,100%,0);--color-base-white-transparent-100:hsla(0,0%,100%,.13);--color-base-white-transparent-200:hsla(0,0%,100%,.25);--color-base-white-transparent-300:hsla(0,0%,100%,.35);--color-base-white-transparent-500:hsla(0,0%,100%,.5);--color-base-white-transparent-600:hsla(0,0%,100%,.65);--color-base-white-transparent-800:hsla(0,0%,100%,.85);--color-base-white:#fff;--color-base-black:#000;--color-base-blue-dark-mode:#263238;--color-base-gray-050:#fafafa;--color-base-gray-100:#f5f5f5;--color-base-gray-200:#eee;--color-base-gray-300:#e0e0e0;--color-base-gray-400:#bdbdbd;--color-base-gray-500:#9e9e9e;--color-base-gray-600:#757575;--color-base-gray-700:#616161;--color-base-gray-800:#424242;--color-base-gray-900:#212121;--color-brand-primary:#ff0080;--color-brand-primary-dark:#c06;--color-brand-primary-pastel-light:#ffcce8;--color-brand-primary-pastel-dark:#ff0080;--color-brand-secondary:#00aeff;--color-brand-secondary-dark:#008bcc;--color-brand-secondary-very-light:#f2fdff;--color-brand-secondary-pastel-light:#daf3ff;--color-brand-secondary-pastel-medium:#aaf3ff;--color-brand-secondary-pastel-dark:#00aeff;--color-status-success:#09b896;--color-status-success-dark:#079378;--color-status-success-pastel-very-light:#dfffcb;--color-status-success-pastel-light:#b9ff8d;--color-status-success-pastel-dark:#0caf8b;--color-status-warning:#ff9600;--color-status-warning-dark:#cc7800;--color-status-warning-pastel-light:#ffe466;--color-status-warning-pastel-dark:#ff9600;--color-status-danger:#f30;--color-status-danger-dark:#cc2900;--color-status-danger-pastel-light:#ffd4d4;--color-status-danger-pastel-dark:#ff1c1c;--color-status-disabled:#a1a1a1;--color-status-disabled-light:#e9e9e9;--color-status-disabled-pastel-light:#cecec6;--color-status-disabled-pastel-dark:#8d8d87;--color-status-edit:rgba(252,255,0,.66);--color-status-highlight:#fffbd9;--color-text-dark:rgba(0,0,0,.8);--color-text-dark-emphasis:#000;--color-text-dark-secondary:rgba(0,0,0,.6);--color-text-light:hsla(0,0%,100%,.85);--color-text-light-emphasis:#fff;--color-text-light-secondary:hsla(0,0%,100%,.65);--color-text-link:#00aeff;--color-text-link-hover:#008bcc;--color-text-link-primary:#ff0080;--color-text-link-primary-hover:#c06;--color-text-link-danger:#f30;--color-text-link-danger-hover:#cc2900;--font-family-system:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--font-family-mono:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;--number-font-weight-light:300;--number-font-weight-normal:400;--number-font-weight-medium:500;--number-font-weight-semibold:600;--number-font-weight-bold:700;--number-font-weight-heavy:800;--size-avatar-tiny:20px;--size-avatar-small:30px;--size-avatar-default:48px;--size-avatar-large:100px;--size-border-radius-default:5px;--size-border-radius-large:10px;--size-breakpoint-xs:320px;--size-breakpoint-sm:375px;--size-breakpoint-md:480px;--size-breakpoint-lg:768px;--size-breakpoint-xl:960px;--size-breakpoint-xxl:1440px;--size-font-base:16px;--size-font-input:16px;--size-spacing-half:5px;--size-spacing-default:10px;--size-spacing-half-again:15px;--size-spacing-double:20px;--size-spacing-triple:30px;--size-spacing-quadruple:40px;--time-speed-instant:0s;--time-speed-immediate:0.1s;--time-speed-quick:0.2s;--time-speed-prompt:0.3s;--time-speed-slow:0.4s;--time-speed-glacial:0.6s}:root{--color-text-link-underline:rgba(0,174,255,.33);--color-text-link-underline-hover:rgba(0,139,204,.33);--color-text-link-primary-underline:rgba(255,0,128,.33);--color-text-link-primary-underline-hover:rgba(204,0,102,.33);--color-text-link-danger-underline:rgba(255,51,0,.33);--color-text-link-danger-underline-hover:rgba(204,41,0,.33)}:root{--color-background-page:var(--color-background-page-light);--color-background-content:var(--color-background-content-light);--color-background-content-secondary:var(--color-base-gray-100);--color-page-text:var(--color-text-dark);--color-page-text-emphasis:var(--color-text-dark-emphasis);--color-page-text-secondary:var(--color-text-dark-secondary);--color-border-default:var(--color-base-gray-300);--color-border-form:var(--color-base-blue-gray-light);--color-border-focus-ring:rgba(0,174,255,.25);--color-form-bg:var(--color-brand-secondary-very-light);--color-form-error-bubble-bg:var(--color-brand-secondary-pastel-medium);--color-form-error-bubble-text:var(--color-page-text);--number-font-weight-bold:bold;--number-font-weight-normal:normal;--letter-spacing:normal;--color-bg-primary-brand-pastel:var(--color-brand-primary-pastel-light);--color-bg-secondary-brand-pastel:var(--color-background-page-light);--color-bg-success-pastel:var(--color-status-success-pastel-very-light);--color-bg-danger-pastel:var(--color-status-danger-pastel-light);--color-bg-warning-pastel:var(--color-status-highlight);--color-background-button-primary:var(--color-brand-primary);--color-background-button-primary-hover:var(--color-brand-primary-dark);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-light);--color-background-button-primary-pastel-hover:var(--color-brand-primary);--color-text-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-text-button-primary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-secondary:var(--color-brand-secondary);--color-background-button-secondary-hover:var(--color-brand-secondary-dark);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-light);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary);--color-text-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-text-button-secondary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-success:var(--color-status-success);--color-background-button-success-hover:var(--color-status-success-dark);--color-background-button-success-pastel:var(--color-status-success-pastel-light);--color-background-button-success-pastel-hover:var(--color-status-success);--color-text-button-success-pastel:var(--color-status-success-pastel-dark);--color-text-button-success-pastel-hover:var(--color-text-light-emphasis);--color-background-button-warning:var(--color-status-warning);--color-background-button-warning-hover:var(--color-status-warning-dark);--color-background-button-warning-pastel:var(--color-status-warning-pastel-light);--color-background-button-warning-pastel-hover:var(--color-status-warning);--color-text-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-text-button-warning-pastel-hover:var(--color-text-light-emphasis);--color-background-button-danger:var(--color-status-danger);--color-background-button-danger-hover:var(--color-status-danger-dark);--color-background-button-danger-pastel:var(--color-status-danger-pastel-light);--color-background-button-danger-pastel-hover:var(--color-status-danger);--color-text-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-text-button-danger-pastel-hover:var(--color-text-light-emphasis);--color-background-choose-shake-link:var(--color-background-content);--color-background-choose-shake-link-hover:var(--color-status-highlight);--color-text-choose-shake-link:var(--color-text-link-primary);--color-text-choose-shake-link-hover:var(--color-text-link-primary-hover)}.t-light{--color-background-page:var(--color-background-page-light);--color-background-content:var(--color-background-content-light);--color-background-content-secondary:var(--color-base-gray-100);--color-page-text:var(--color-text-dark);--color-page-text-emphasis:var(--color-text-dark-emphasis);--color-page-text-secondary:var(--color-text-dark-secondary);--color-border-default:var(--color-base-gray-300);--color-border-form:var(--color-base-blue-gray-light);--color-border-focus-ring:rgba(0,174,255,.25);--color-form-bg:var(--color-brand-secondary-very-light);--color-form-error-bubble-bg:var(--color-brand-secondary-pastel-medium);--color-form-error-bubble-text:var(--color-page-text);--number-font-weight-bold:bold;--number-font-weight-normal:normal;--letter-spacing:normal;--color-bg-primary-brand-pastel:var(--color-brand-primary-pastel-light);--color-bg-secondary-brand-pastel:var(--color-background-page-light);--color-bg-success-pastel:var(--color-status-success-pastel-very-light);--color-bg-danger-pastel:var(--color-status-danger-pastel-light);--color-bg-warning-pastel:var(--color-status-highlight);--color-background-button-primary:var(--color-brand-primary);--color-background-button-primary-hover:var(--color-brand-primary-dark);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-light);--color-background-button-primary-pastel-hover:var(--color-brand-primary);--color-text-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-text-button-primary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-secondary:var(--color-brand-secondary);--color-background-button-secondary-hover:var(--color-brand-secondary-dark);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-light);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary);--color-text-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-text-button-secondary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-success:var(--color-status-success);--color-background-button-success-hover:var(--color-status-success-dark);--color-background-button-success-pastel:var(--color-status-success-pastel-light);--color-background-button-success-pastel-hover:var(--color-status-success);--color-text-button-success-pastel:var(--color-status-success-pastel-dark);--color-text-button-success-pastel-hover:var(--color-text-light-emphasis);--color-background-button-warning:var(--color-status-warning);--color-background-button-warning-hover:var(--color-status-warning-dark);--color-background-button-warning-pastel:var(--color-status-warning-pastel-light);--color-background-button-warning-pastel-hover:var(--color-status-warning);--color-text-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-text-button-warning-pastel-hover:var(--color-text-light-emphasis);--color-background-button-danger:var(--color-status-danger);--color-background-button-danger-hover:var(--color-status-danger-dark);--color-background-button-danger-pastel:var(--color-status-danger-pastel-light);--color-background-button-danger-pastel-hover:var(--color-status-danger);--color-text-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-text-button-danger-pastel-hover:var(--color-text-light-emphasis);--color-background-choose-shake-link:var(--color-background-content);--color-background-choose-shake-link-hover:var(--color-status-highlight);--color-text-choose-shake-link:var(--color-text-link-primary);--color-text-choose-shake-link-hover:var(--color-text-link-primary-hover)}@media screen and (prefers-color-scheme:dark){:root{--color-background-page:var(--color-background-page-dark);--color-background-content:var(--color-background-content-dark);--color-background-content-secondary:var(--color-base-gray-900);--color-page-text:var(--color-text-light);--color-page-text-emphasis:var(--color-text-light-emphasis);--color-page-text-secondary:var(--color-text-light-secondary);--color-border-default:var(--color-base-gray-700);--color-border-form:var(--color-base-gray-800);--color-border-focus-ring:rgba(0,174,255,.4);--color-form-bg:var(--color-background-content-secondary);--color-form-error-bubble-bg:var(--color-base-gray-800);--color-form-error-bubble-text:var(--color-status-danger);--number-font-weight-bold:600;--number-font-weight-normal:300;--letter-spacing:0.025em;--color-bg-primary-brand-pastel:rgba(255,0,128,.1);--color-bg-secondary-brand-pastel:rgba(0,174,255,.1);--color-bg-success-pastel:rgba(9,184,150,.1);--color-bg-danger-pastel:rgba(255,51,0,.1);--color-bg-warning-pastel:rgba(255,150,0,.1);--color-background-button-primary:var(--color-brand-primary-dark);--color-background-button-primary-hover:var(--color-brand-primary);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-background-button-primary-pastel-hover:var(--color-brand-primary-pastel-light);--color-text-button-primary-pastel:var(--color-text-light-emphasis);--color-text-button-primary-pastel-hover:var(--color-brand-primary-pastel-dark);--color-background-button-secondary:var(--color-brand-secondary-dark);--color-background-button-secondary-hover:var(--color-brand-secondary);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-light);--color-text-button-secondary-pastel:var(--color-text-light-emphasis);--color-text-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-dark);--color-background-button-success:var(--color-status-success-dark);--color-background-button-success-hover:var(--color-status-success);--color-background-button-success-pastel:var(--color-status-success-pastel-dark);--color-background-button-success-pastel-hover:var(--color-status-success-pastel-light);--color-text-button-success-pastel:var(--color-text-light-emphasis);--color-text-button-success-pastel-hover:var(--color-status-success-pastel-dark);--color-background-button-warning:var(--color-status-warning-dark);--color-background-button-warning-hover:var(--color-status-warning);--color-background-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-background-button-warning-pastel-hover:var(--color-status-warning-pastel-light);--color-text-button-warning-pastel:var(--color-text-light-emphasis);--color-text-button-warning-pastel-hover:var(--color-status-warning-pastel-dark);--color-background-button-danger:var(--color-status-danger-dark);--color-background-button-danger-hover:var(--color-status-danger);--color-background-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-background-button-danger-pastel-hover:var(--color-status-danger-pastel-light);--color-text-button-danger-pastel:var(--color-text-light-emphasis);--color-text-button-danger-pastel-hover:var(--color-status-danger-pastel-dark);--color-background-choose-shake-link:var(--color-background-button-success);--color-background-choose-shake-link-hover:var(--color-background-button-success-pastel-hover);--color-text-choose-shake-link:var(--color-text-light-emphasis);--color-text-choose-shake-link-hover:var(--color-text-button-success-pastel-hover)}}.t-dark{--color-background-page:var(--color-background-page-dark);--color-background-content:var(--color-background-content-dark);--color-background-content-secondary:var(--color-base-gray-900);--color-page-text:var(--color-text-light);--color-page-text-emphasis:var(--color-text-light-emphasis);--color-page-text-secondary:var(--color-text-light-secondary);--color-border-default:var(--color-base-gray-700);--color-border-form:var(--color-base-gray-800);--color-border-focus-ring:rgba(0,174,255,.4);--color-form-bg:var(--color-background-content-secondary);--color-form-error-bubble-bg:var(--color-base-gray-800);--color-form-error-bubble-text:var(--color-status-danger);--number-font-weight-bold:600;--number-font-weight-normal:300;--letter-spacing:0.025em;--color-bg-primary-brand-pastel:rgba(255,0,128,.1);--color-bg-secondary-brand-pastel:rgba(0,174,255,.1);--color-bg-success-pastel:rgba(9,184,150,.1);--color-bg-danger-pastel:rgba(255,51,0,.1);--color-bg-warning-pastel:rgba(255,150,0,.1);--color-background-button-primary:var(--color-brand-primary-dark);--color-background-button-primary-hover:var(--color-brand-primary);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-background-button-primary-pastel-hover:var(--color-brand-primary-pastel-light);--color-text-button-primary-pastel:var(--color-text-light-emphasis);--color-text-button-primary-pastel-hover:var(--color-brand-primary-pastel-dark);--color-background-button-secondary:var(--color-brand-secondary-dark);--color-background-button-secondary-hover:var(--color-brand-secondary);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-light);--color-text-button-secondary-pastel:var(--color-text-light-emphasis);--color-text-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-dark);--color-background-button-success:var(--color-status-success-dark);--color-background-button-success-hover:var(--color-status-success);--color-background-button-success-pastel:var(--color-status-success-pastel-dark);--color-background-button-success-pastel-hover:var(--color-status-success-pastel-light);--color-text-button-success-pastel:var(--color-text-light-emphasis);--color-text-button-success-pastel-hover:var(--color-status-success-pastel-dark);--color-background-button-warning:var(--color-status-warning-dark);--color-background-button-warning-hover:var(--color-status-warning);--color-background-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-background-button-warning-pastel-hover:var(--color-status-warning-pastel-light);--color-text-button-warning-pastel:var(--color-text-light-emphasis);--color-text-button-warning-pastel-hover:var(--color-status-warning-pastel-dark);--color-background-button-danger:var(--color-status-danger-dark);--color-background-button-danger-hover:var(--color-status-danger);--color-background-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-background-button-danger-pastel-hover:var(--color-status-danger-pastel-light);--color-text-button-danger-pastel:var(--color-text-light-emphasis);--color-text-button-danger-pastel-hover:var(--color-status-danger-pastel-dark);--color-background-choose-shake-link:var(--color-background-button-success);--color-background-choose-shake-link-hover:var(--color-background-button-success-pastel-hover);--color-text-choose-shake-link:var(--color-text-light-emphasis);--color-text-choose-shake-link-hover:var(--color-text-button-success-pastel-hover);background-color:var(--color-background-page);color:var(--color-page-text)}*,:after,:before{box-sizing:border-box}body{background-color:var(--color-background-page);color:var(--color-page-text)}:focus{box-shadow:0 0 0 .25rem var(--color-border-focus-ring);outline:0}a{color:var(--color-text-link);text-decoration-color:var(--color-text-link-underline);transition:background-color var(--time-speed-quick) ease,color var(--time-speed-quick) ease}@media screen and (prefers-reduced-motion:reduce){a{transition:none}}a:active,a:focus,a:hover{color:var(--color-text-link-hover);text-decoration-color:var(--color-text-link-underline-hover)}.link--primary{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.link--primary:active,.link--primary:focus,.link--primary:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.link--primary:active,.link--primary:focus,.link--primary:hover{text-decoration:underline}.link--danger{color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.link--danger:active,.link--danger:focus,.link--danger:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}img,video{height:auto}iframe,img,video{max-width:100%}.data-wrapper,[style*="--aspect-ratio"]{position:relative}.data-wrapper:before,[style*="--aspect-ratio"]:before{content:"";display:block;padding-bottom:calc(100%*var(--aspect-ratio))}.data-wrapper>:first-child,[style*="--aspect-ratio"]>:first-child{height:100%;left:0;position:absolute;top:0;width:100%}.data-wrapper:before{padding-bottom:56.25%}html{font-family:var(--font-family-system);font-size:var(--size-font-base);font-weight:var(--number-font-weight-normal);letter-spacing:var(--letter-spacing);line-height:1.3}h1,h2,h3,h4,h5,h6{line-height:1.15;margin:0}.u-display,h1{font-size:3rem;letter-spacing:-1px}@media screen and (min-width:768px){.u-display,h1{font-size:3.75rem}}b,h1,h2,h3,h4,h5,h6,label,legend,strong,th{font-weight:var(--number-font-weight-bold)}.code,code,pre{font-family:var(--font-family-mono)}pre{overflow-x:auto;width:100%}.sr-only{clip:rect(0 0 0 0);border:0;clip-path:polygon(0 0,0 0,0 0);-webkit-clip-path:polygon(0 0,0 0,0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.clear{clear:both}@media screen and (max-width:480px){.hide-on-small{display:none}}.danger,.error{color:var(--color-status-danger)}.user-name{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.avatar--img{display:block;height:var(--size-avatar-default);max-width:none;object-fit:cover;width:var(--size-avatar-default)}.caret{border:.5em solid transparent;border-top-color:currentcolor;border-width:.5em .433em 0;display:inline-block;position:relative;top:-.1em;transform-origin:center 40%;transition:transform .25s ease}@media screen and (prefers-reduced-motion:reduce){.caret{transition:none}}.is-active .caret{transform:rotate(180deg)}.panel--example{margin:.25em 0 1em}.page{background-color:var(--color-background-page);padding:0 .5em;position:relative}.wrapper{display:flow-root;margin:0 auto;max-width:960px}.content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-triple);margin-top:var(--size-spacing-half-again)}.content:after{clear:both;content:"";display:table}.content-narrow,.content-styleguide{margin:var(--size-spacing-quadruple) auto var(--size-spacing-triple);max-width:700px;padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-narrow,.content-styleguide{padding:var(--size-spacing-quadruple)}}.content-narrow p,.content-styleguide p{font-size:1.625rem;line-height:2.5;margin:2em 0}.content-narrow .extra-info p,.content-styleguide .extra-info p{color:var(--color-page-text-secondary);font-size:1rem;line-height:1.4}.content-styleguide{max-width:none}.content-with-sidebar{display:flex;flex-direction:column}@media screen and (min-width:768px){.content-with-sidebar{flex-flow:row wrap}}.content-with-sidebar>*{min-width:0}.content-with-sidebar .header{flex:0 0 100%}.content-with-sidebar .body{flex:1;padding-right:1px}.content-with-sidebar .sidebar{background:linear-gradient(to top,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));order:2;padding:var(--size-spacing-double)}@media screen and (min-width:768px){.content-with-sidebar .sidebar{background:linear-gradient(to right,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));flex:0 0 325px;order:0}.content-with-sidebar-reversed .sidebar{background:linear-gradient(to left,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));order:2}}.alert{background:var(--color-background-content-secondary);font-size:.9rem;line-height:1.25;padding:var(--size-spacing-half-again)}.alert p{margin:0}.alert a{font-weight:var(--number-font-weight-bold)}.alert--marquee{padding:0}.alert--marquee marquee{padding:var(--size-spacing-half-again)}.alert--warning{background:var(--color-bg-warning-pastel);color:var(--color-status-warning)}.alert--danger{background:var(--color-bg-danger-pastel);color:var(--color-status-danger)}.alert--danger a{color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.alert--danger a:active,.alert--danger a:focus,.alert--danger a:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}.migration-reminder{background:var(--color-background-content-secondary);background:var(--color-bg-warning-pastel);color:var(--color-status-warning);font-size:.9rem;font-weight:var(--number-font-weight-bold);line-height:1.25;padding:var(--size-spacing-half-again)}.migration-reminder p{margin:0}.migration-reminder a{font-weight:var(--number-font-weight-bold)}.bookmark{align-items:flex-start;display:flex;flex-wrap:wrap}.bookmark-flag{background:var(--color-brand-primary);color:var(--color-text-light-emphasis);flex:none;font-size:.75rem;font-weight:var(--number-font-weight-bold);margin-top:-3px;position:relative;z-index:2}.bookmark-flag:after,.bookmark-flag:before{content:"";display:block;position:absolute}.bookmark-flag:before{background:inherit;border-radius:var(--size-border-radius-default) 0 0 var(--size-border-radius-default);height:calc(100% + var(--size-spacing-half));left:calc(var(--size-spacing-half)*-1);padding-bottom:var(--size-spacing-half);top:0;width:var(--size-spacing-half)}.bookmark-flag:after{background:var(--color-base-black-transparent-300);border-radius:3px 0 0 3px;bottom:-4px;height:4px;left:-3px;width:3px}.bookmark-flag--content{display:block;line-height:1;padding:.5em 1em}.bookmark-flag--content:after{border-width:1em;border-bottom:1em solid var(--color-brand-primary);border-left:0 solid var(--color-brand-primary);border-right:.866em solid transparent;border-top:1em solid var(--color-brand-primary);content:"";display:block;position:absolute;right:-.833em;top:0}.jump-back{flex:1;font-size:.6875rem;margin-left:.75em;padding:calc(.5em - 3px) 0 .5em .75em;white-space:nowrap}.jump-back a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.jump-back a:active,.jump-back a:focus,.jump-back a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.jump-back a:active,.jump-back a:focus,.jump-back a:hover{text-decoration:underline}button{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.btn,.label{background-color:var(--color-base-gray-700);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);display:inline-block;font-size:18px;font-weight:500;height:2.2em;letter-spacing:.033em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.btn{cursor:pointer}.btn:focus,.btn:hover{color:var(--color-text-light-emphasis)}.btn:focus{outline:none}.btn:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.btn-pastel,.label-pastel{font-weight:600;letter-spacing:.015em;text-shadow:none}.btn-pastel:disabled,.label-pastel:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.btn-shadow{box-shadow:.22em .22em 0 var(--color-base-black-transparent-100);margin-right:.22em}.btn-shadow:disabled{box-shadow:none!important}.btn-padded{height:2.6em;line-height:2.6em;padding:0 1.25em}.btn-large,.label-large{font-size:24px;letter-spacing:.066em}.btn-large.btn-pastel,.label-large.label-pastel{letter-spacing:.033em}.btn-small,.label-small{font-size:12px;font-weight:400}.btn-tiny,.label-tiny{font-size:10px;font-weight:400;padding:0 .66em}.btn-small.btn-pastel,.btn-tiny.btn-pastel,.label-small.label-pastel,.label-tiny.label-pastel{font-weight:500}.btn-icon{padding:0;width:2.2em}.btn-icon.btn-padded{padding:0;width:2.6em}.btn-primary,.label-primary{background-color:var(--color-background-button-primary)}.btn-primary:focus,.btn-primary:hover{background-color:var(--color-background-button-primary-hover)}.btn-primary.btn-pastel,.label-primary.label-pastel{background-color:var(--color-background-button-primary-pastel);color:var(--color-text-button-primary-pastel)}.btn-primary.btn-pastel:focus,.btn-primary.btn-pastel:hover{background-color:var(--color-background-button-primary-pastel-hover);color:var(--color-text-button-primary-pastel-hover)}.btn-secondary,.label-secondary{background-color:var(--color-background-button-secondary)}.btn-secondary:focus,.btn-secondary:hover{background-color:var(--color-background-button-secondary-hover)}.btn-secondary.btn-pastel,.label-secondary.label-pastel{background-color:var(--color-background-button-secondary-pastel);color:var(--color-text-button-secondary-pastel)}.btn-secondary.btn-pastel:focus,.btn-secondary.btn-pastel:hover{background-color:var(--color-background-button-secondary-pastel-hover);color:var(--color-text-button-secondary-pastel-hover)}.btn-success,.label-success{background-color:var(--color-background-button-success)}.btn-success:focus,.btn-success:hover{background-color:var(--color-background-button-success-hover)}.btn-success.btn-pastel,.label-success.label-pastel{background-color:var(--color-background-button-success-pastel);color:var(--color-text-button-success-pastel)}.btn-success.btn-pastel:focus,.btn-success.btn-pastel:hover{background-color:var(--color-background-button-success-pastel-hover);color:var(--color-text-button-success-pastel-hover)}.btn-warning,.label-warning{background-color:var(--color-background-button-warning)}.btn-warning:focus,.btn-warning:hover{background-color:var(--color-background-button-warning-hover)}.btn-warning.btn-pastel,.label-warning.label-pastel{background-color:var(--color-background-button-warning-pastel);color:var(--color-text-button-warning-pastel)}.btn-warning.btn-pastel:focus,.btn-warning.btn-pastel:hover{background-color:var(--color-background-button-warning-pastel-hover);color:var(--color-text-button-warning-pastel-hover)}.btn-danger,.label-danger{background-color:var(--color-background-button-danger)}.btn-danger:focus,.btn-danger:hover{background-color:var(--color-background-button-danger-hover)}.btn-danger.btn-pastel,.label-danger.label-pastel{background-color:var(--color-background-button-danger-pastel);color:var(--color-text-button-danger-pastel)}.btn-danger.btn-pastel:focus,.btn-danger.btn-pastel:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.choose-a-shake{position:relative;text-align:left;z-index:3}.choose-a-shake--toggle{width:100%}.choose-a-shake.is-expanded .choose-a-shake--toggle{border-bottom-left-radius:0;border-bottom-right-radius:0;box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);outline:none}.choose-a-shake.is-expanded .choose-a-shake--toggle:focus,.choose-a-shake.is-expanded .choose-a-shake--toggle:hover{background-color:var(--color-background-button-success-pastel);color:var(--color-text-button-success-pastel)}.choose-a-shake.is-expanded .choose-a-shake--toggle .caret{transform:rotate(180deg)}.choose-a-shake--dropdown{background-color:var(--color-background-button-success-pastel);border-radius:var(--size-border-radius-large);border-top-right-radius:0;box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);display:none;max-width:400px;padding:var(--size-spacing-default);padding-top:1px;position:absolute;right:0;top:calc(100% - .22em)}@media screen and (min-width:768px){.choose-a-shake--dropdown{border-top-left-radius:0;border-top-right-radius:var(--size-border-radius-large);left:0;right:auto}}.choose-a-shake.is-expanded .choose-a-shake--dropdown{display:block}.choose-a-shake--dropdown .add-a-shake{display:block;float:right;margin-top:var(--size-spacing-default)}.choose-a-shake--dropdown ul{list-style:none;margin:0;padding:0}.choose-a-shake--dropdown ul li a{background-color:var(--color-background-choose-shake-link);border-radius:var(--size-border-radius-large);color:var(--color-text-choose-shake-link);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow:hidden;padding:var(--size-spacing-default) var(--size-spacing-half-again);text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.choose-a-shake--dropdown ul li a:active,.choose-a-shake--dropdown ul li a:focus,.choose-a-shake--dropdown ul li a:hover{text-decoration:underline}.choose-a-shake--dropdown ul li a:active,.choose-a-shake--dropdown ul li a:focus,.choose-a-shake--dropdown ul li a:hover{background-color:var(--color-background-choose-shake-link-hover);color:var(--color-text-choose-shake-link-hover);text-decoration:none}.choose-a-shake--dropdown ul.top-shakes li a{margin:var(--size-spacing-default) 0}.choose-a-shake--dropdown ul.group-shakes{background-color:var(--color-background-choose-shake-link);border-radius:var(--size-border-radius-large)}.conversations-nav h3{color:var(--color-page-text);font-size:1.125rem;margin-bottom:var(--size-spacing-half-again);margin-top:var(--size-spacing-triple)}.conversations-nav ul{list-style:none;margin:0;padding:0}.conversations-nav li{margin:var(--size-spacing-half) 0}.conversations-nav li.selected a{color:var(--color-status-disabled)}.conversations-nav a{color:var(--color-brand-primary);font-size:.875rem;font-weight:var(--number-font-weight-bold);text-decoration:none}.conversation{border-bottom:1px dotted var(--color-border-default);display:flex;margin-top:var(--size-spacing-triple)}.mentions .conversation{padding-left:var(--size-spacing-triple);padding-right:var(--size-spacing-default)}.conversation .thumb{flex:none;margin-left:var(--size-spacing-default);width:50px}@media screen and (min-width:480px){.conversation .thumb{margin-left:var(--size-spacing-triple);width:100px}}.conversation .details-wrapper{flex:1;margin-left:var(--size-spacing-default);margin-right:var(--size-spacing-default);min-width:0}.conversation .sharedfile-title{color:var(--color-page-text-emphasis);font-size:1.875rem;margin-bottom:var(--size-spacing-default)}.conversation .sharedfile-description,.conversation .sharedfile-title{word-wrap:break-word;overflow:hidden;overflow-wrap:break-word;word-break:break-word}.conversation .sharedfile-description{font-size:.875rem;line-height:1.3}.conversation .image-comments{padding-left:0}.conversation .image-comments .body{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.conversation .conversation-meta{display:flex;flex-wrap:wrap;margin-bottom:var(--size-spacing-triple);margin-top:var(--size-spacing-half-again)}.conversation .mute-this-conversation,.conversation .post-a-comment{margin:var(--size-spacing-half) 1em 0 0}.conversation .mute-this-conversation-form{display:none}.error-uh-oh{background-image:svg-load("illustrations/tools-uh-oh.svg");background-position:top;background-repeat:no-repeat;background-size:282px 133px;font-size:2.25rem;font-weight:var(--number-font-weight-bold);padding-top:160px;text-align:center}.error-p{color:var(--color-page-text-secondary);text-align:center}.error-p.error-p-long{text-align:left}.content-narrow .error-p{font-size:1.125rem;line-height:1.4}@media screen and (min-width:480px){.feature-list{display:flex;flex-wrap:wrap}}@media screen and (min-width:768px){.feature-list{flex-wrap:nowrap}}@media screen and (min-width:960px){.feature-list{flex-wrap:wrap}}.feature{border-top:1px dashed var(--color-border-default);margin:0;padding:var(--size-spacing-half-again);position:relative}@media screen and (min-width:480px){.feature{border-left:1px dashed var(--color-border-default);flex:1 1 50%}}@media screen and (min-width:768px){.feature{flex-basis:25%;padding:calc(var(--size-spacing-default)*2.5)}}@media screen and (min-width:960px){.feature{display:flex;flex-basis:50%;flex-direction:row-reverse;padding:calc(var(--size-spacing-default)*3.5)}}@media screen and (min-width:768px){.feature:first-child{border-left:none}}@media screen and (min-width:480px) and (max-width:767px){.feature:nth-child(2n-1){border-left:none}}@media screen and (min-width:960px){.feature:nth-child(2n-1){border-left:none}}.feature>*+*{margin-top:var(--size-spacing-half-again)}@media screen and (min-width:768px){.feature>*+*{margin-top:calc(var(--size-spacing-default)*2.5)}}@media screen and (min-width:960px){.feature>*+*{margin-right:var(--size-spacing-double);margin-top:0}}@media screen and (min-width:960px){.feature--content{flex:1 1 auto}.feature--image{flex:0 0 163px}}.feature--image-media{border:1px solid var(--color-border-default);box-shadow:3px 3px 0 var(--color-base-black-transparent-100);display:block;margin:0 auto}.feature--title{color:var(--color-page-text-emphasis);font-size:1.375rem;margin:0;text-align:center}@media screen and (min-width:768px){.feature--title{text-align:left}}.feature--body{color:var(--color-page-text);font-size:.875rem;line-height:1.25;margin:var(--size-spacing-default) 0 0}.feature--body p{margin:0}.feature--body p+p{margin-top:1.25em}.feature--cta{text-align:center}.feature--finale .feature--image-media,.feature--primary .feature--image-media{border:none;box-shadow:none}.feature--primary{border:none}@media screen and (min-width:960px){.feature--primary>*{flex:1}}@media screen and (min-width:960px){.feature--primary>*+*{margin-right:70px}}.feature--primary .feature--title{font-size:2.625rem;letter-spacing:-3px;line-height:90%}@media screen and (min-width:768px){.feature--primary .feature--title{font-size:3.75rem}}@media screen and (min-width:960px){.feature--primary .feature--title{font-size:4.5rem}}.feature--primary .feature--body{color:var(--color-page-text-secondary);margin-top:1.25em}.feature--finale{align-items:center}@media screen and (min-width:960px){.feature--finale{padding-left:150px;padding-right:150px}}.feature--finale .feature--image{flex:none}@media screen and (min-width:960px){.feature--flipped{flex-direction:row}}@media screen and (min-width:960px){.feature--flipped>*+*{margin-left:var(--size-spacing-double);margin-right:0}}@media screen and (min-width:960px){.feature--flipped.feature--primary>*+*{margin-left:70px}}button,input,select,textarea{font-family:var(--font-family-system);font-size:var(--size-font-input)}select{width:100%}label,textarea{display:block}textarea{height:8em;padding:.5em;resize:vertical;width:100%}.fun-form{margin-top:var(--size-spacing-double)}.fun-form .field{display:flex;flex-direction:column;position:relative}@media screen and (min-width:768px){.fun-form .field{align-items:center;flex-flow:row wrap}}.fun-form .field+.field{margin-top:var(--size-spacing-double)}.fun-form label{flex:none;font-size:1.125rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-half)}@media screen and (min-width:768px){.fun-form label{flex-basis:190px;margin-bottom:0;margin-right:var(--size-spacing-default);text-align:right}}.fun-form .field-input{flex:1}.fun-form .field-help,.fun-form .field-submit{display:block}@media screen and (min-width:768px){.fun-form .field-help,.fun-form .field-submit{padding-left:200px}}.fun-form .field-help{color:var(--color-page-text-secondary);font-size:.875rem;margin-top:var(--size-spacing-half)}@media screen and (min-width:768px){.fun-form .field-help{flex-basis:100%}}.fun-form .input-text,.fun-form textarea{appearance:none;background:var(--color-form-bg);border-radius:var(--size-border-radius-large);border-width:1px;border-bottom:1px solid var(--color-border-form);border-left:4px solid var(--color-border-form);border-right:1px solid var(--color-border-form);border-top:4px solid var(--color-border-form);color:var(--color-page-text);display:block;font-size:1.125rem;padding:var(--size-spacing-default) var(--size-spacing-half-again);width:100%}.fun-form [type=checkbox],.fun-form [type=radio]{margin-right:.5em}.fun-form .name-prefix{color:var(--color-page-text-secondary);margin-right:var(--size-spacing-half)}.fun-form .error{background:var(--color-form-error-bubble-bg);border-radius:33% 25px;color:var(--color-form-error-bubble-text);display:block;font-size:.75rem;padding:1em 1.25em;position:absolute;right:calc(var(--size-spacing-half-again)*-1);text-align:center;top:25%;transform:translateY(-50%);width:115px;z-index:1}@media screen and (min-width:960px){.fun-form .error{right:-160px}}.fun-form .error:before{background:var(--color-form-error-bubble-bg);bottom:calc(50% - 15px);content:"";display:block;height:22px;left:-48px;-webkit-mask-image:svg-load("balloons/error-tail.svg");mask-image:svg-load("balloons/error-tail.svg");position:absolute;width:52px}.fun-form-stacked .field{display:block}.fun-form-stacked label{margin-bottom:var(--size-spacing-half);margin-right:0;text-align:left}.fun-form-stacked .field-help,.fun-form-stacked .field-submit{padding-left:0}.fun-form-stacked #create-shake-name-field,.fun-form-stacked .field-prefix{display:flex}.fun-form-stacked #create-shake-name-field label,.fun-form-stacked .field-prefix label{flex-basis:100%}.fun-form-stacked .error{top:3em}.fun-form-errors{color:var(--color-status-danger);font-size:1.125rem}.image-comments{max-width:100%;overflow:hidden;padding-left:var(--size-spacing-quadruple)}.image-comments .comments{clear:both}.image-comments .comment{display:flex}.image-comments .comment+.comment{margin-top:var(--size-spacing-double)}.image-comments .comment .avatar{margin-right:var(--size-spacing-default);width:var(--size-avatar-default)}.image-comments .comment .avatar a{display:block}.image-comments .comment .body{flex:1;font-size:.875rem;line-height:1.3;overflow:hidden}.image-comments .comment .body .where-from{color:var(--color-page-text-secondary);font-size:.825em;padding-top:var(--size-spacing-default)}.image-comments .comment .comment-body-text{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-comments .comment .meta{align-items:flex-end;display:flex;flex-wrap:wrap;font-size:.825em;padding-bottom:var(--size-spacing-default)}.image-comments .comment .meta>*+*{margin-left:.825em}.image-comments .comment .meta .user-name,.image-comments .comment .meta .username{font-size:.875rem;font-weight:var(--number-font-weight-bold)}.image-comments .comment .meta .pro-badge{margin-left:.25em}.image-comments .comment .meta .reply-to{background-image:svg-load("icons/reply.svg");background-size:12px 11px}.image-comments .comment .meta .delete,.image-comments .comment .meta .reply-to{background-position:0 0;background-repeat:no-repeat;display:none;padding-left:var(--size-spacing-half-again)}.image-comments .comment .meta .delete{background-image:svg-load("icons/delete-comment.svg");background-size:13px 13px}.image-comments .comment .meta .created-at{color:var(--color-page-text-secondary)}.image-comments .comment:focus .meta .reply-to,.image-comments .comment:hover .meta .reply-to{display:inline}.image-comments .comment:focus .meta form,.image-comments .comment:hover .meta form{display:none}.image-comments .comment:focus .meta .delete,.image-comments .comment:hover .meta .delete{display:inline}.image-comment-form{margin-top:var(--size-spacing-triple);padding-left:var(--size-spacing-quadruple)}.image-comment-form header{display:flex}.image-comment-form .avatar{flex:none}.image-comment-form h3{background-image:svg-load("balloons/comment-quip-left.svg");background-position:0 0;background-repeat:no-repeat;background-size:auto var(--size-avatar-default);border-bottom-right-radius:15px;border-top-right-radius:25px 20px;display:block;left:calc(var(--size-spacing-default)*-1);position:relative}.image-comment-form h3 span{color:var(--color-text-light-emphasis);display:block;font-size:.75rem;height:var(--size-avatar-default);line-height:var(--size-avatar-default);padding-left:35px;padding-right:var(--size-spacing-double);text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);white-space:nowrap}@media screen and (min-width:375px){.image-comment-form h3 span{font-size:.875rem}}@media screen and (min-width:480px){.image-comment-form h3 span{font-size:1rem}}@media screen and (min-width:768px){.image-comment-form h3 span{font-size:1.125rem}}.image-comment-form .field{clear:both;margin:var(--size-spacing-double) 0;text-align:right}.image-comment-form textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);height:8em;padding:var(--size-spacing-half)}.image-medium{margin-right:var(--size-spacing-half-again);padding-bottom:var(--size-spacing-triple)}.image-medium .user-and-title{padding-top:var(--size-spacing-default)}.image-medium .user-and-title a{display:block;float:left;margin-right:var(--size-spacing-default)}.image-medium .user-and-title .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-medium .user-and-title .title{font-size:1.25rem;font-weight:var(--number-font-weight-bold);overflow:hidden}.image-medium .stats{clear:both;color:var(--color-page-text-secondary);display:flex;list-style:none;margin:0;padding:var(--size-spacing-default) 0 0}.image-medium .stats li{float:left;font-size:.75em;padding-right:var(--size-spacing-default)}.image-medium .stats li.saves{background-image:svg-load("icons/save-tiny.svg");background-position:0 2px;background-repeat:no-repeat;background-size:16px 11px;color:var(--color-status-warning);padding-left:var(--size-spacing-double)}.image-medium .stats li.likes{background-image:svg-load("icons/like.svg");color:var(--color-status-danger)}.image-medium .stats li.comments a,.image-medium .stats li.likes{background-position:0 2px;background-repeat:no-repeat;background-size:12px 9px;padding-left:var(--size-spacing-half-again)}.image-medium .stats li.comments a{background-image:svg-load("icons/comment.svg");color:var(--color-brand-secondary);text-decoration:none}.image-medium-thumb a{display:block}.image-medium-thumb img{text-align:center;vertical-align:middle}.image-title{max-width:100%;padding:var(--size-spacing-half-again) var(--size-spacing-half-again) 0;position:relative}@media screen and (min-width:768px){.image-title{padding:var(--size-spacing-quadruple) var(--size-spacing-quadruple) 0}}.image-title .image-poster{float:left;padding-right:var(--size-spacing-default)}.image-title .image-poster a{display:block}.image-title h1,.image-title h3{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-title h1{font-size:3rem}.image-title h3{font-size:1.875rem;margin-bottom:var(--size-spacing-double)}.image-title .remove-from-shake{height:21px;position:absolute;right:var(--size-spacing-default);top:var(--size-spacing-default);width:21px}@media screen and (min-width:768px){.image-title .remove-from-shake{top:calc(var(--size-spacing-quadruple) + var(--size-spacing-half))}}.image-edit-title-hover,.image-edit-title:focus,.image-edit-title:hover{background-color:var(--color-status-edit)}.image-edit-title-form{display:none;flex-direction:column}@media screen and (min-width:480px){.image-edit-title-form{flex-direction:row}}.image-edit-title-form.is-active{display:flex}.image-edit-title-form .title-input{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);flex:1;font-size:3rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-default);max-width:100%;min-width:0}@media screen and (min-width:480px){.image-edit-title-form .title-input{margin-bottom:0}}.image-content-list .image-edit-title-form .title-input{font-size:1.875rem;min-width:250px;padding:.0666em}.image-edit-title-form .buttons{align-items:center;display:flex;padding-left:var(--size-spacing-default)}.image-edit-title-form .or{color:var(--color-page-text-secondary);display:block;padding:0 var(--size-spacing-default)}.image-content-list .image-edit-title-form{margin-bottom:var(--size-spacing-double)}.image-content-list .image-edit-title-form .btn{font-size:12px;font-weight:400}.image-content{clear:both;max-width:100%;padding:var(--size-spacing-half-again);position:relative}@media screen and (min-width:768px){.image-content{padding:var(--size-spacing-half-again) var(--size-spacing-quadruple) var(--size-spacing-quadruple)}}.image-content .the-image a,.image-content .the-image img{display:block}.image-content img.unsized{height:auto;max-width:100%}.image-content-list .image-content{border-bottom:1px dashed var(--color-border-default);float:none;padding:0 var(--size-spacing-half-again) var(--size-spacing-half-again)}@media screen and (min-width:768px){.image-content-list .image-content{padding:0 var(--size-spacing-quadruple) var(--size-spacing-quadruple)}}.nsfw-cover{background-color:var(--color-base-gray-800);color:var(--color-text-light-emphasis);font-size:.875rem;font-weight:600;padding:var(--size-spacing-quadruple) 0;text-align:center}.nsfw-cover p{margin:var(--size-spacing-double) 0}.image-content .description{color:var(--color-page-text);font-size:.875rem;overflow:hidden;padding-bottom:var(--size-spacing-default)}.image-content .the-description{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-content .the-description.the-description-blank{color:var(--color-status-disabled);font-style:italic}.image-content .the-description a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.image-content .the-description a:active,.image-content .the-description a:focus,.image-content .the-description a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.image-content .description-edit .the-description:focus,.image-content .description-edit .the-description:hover,.image-content .the-description-hover{background-color:var(--color-status-edit)}.image-content .description-edit-form{display:none}.image-content .description-edit-form textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);min-height:100px;padding:var(--size-spacing-half)}.image-content .description-edit-form .buttons{display:flex;padding:var(--size-spacing-default) 0}.image-content .description-edit-form .or{color:var(--color-page-text-secondary);display:block;padding:var(--size-spacing-half) var(--size-spacing-default) var(--size-spacing-half-again) var(--size-spacing-default)}.image-interactions{align-items:center;display:flex;flex-direction:row-reverse;float:right;margin-bottom:4px}.image-interactions .save-this{margin-right:var(--size-spacing-half);position:relative}.image-interactions .like-button,.image-interactions .save-this-link{line-height:1}.image-interactions .like-button:focus,.image-interactions .save-this-link:focus{outline:none}.image-interactions .like-button .btn--content,.image-interactions .save-this-link .btn--content{align-items:center;display:flex}.image-interactions .like-button .btn--icon,.image-interactions .save-this-link .btn--icon{height:1.5em;margin-right:.33em}.image-interactions .like-button .btn--caret,.image-interactions .save-this-link .btn--caret{font-size:.75em;margin-left:.33em;vertical-align:bottom}.image-interactions .like-button,.image-interactions .unlike-button{display:none}.image-interactions .like-button.is-active,.image-interactions .unlike-button.is-active{display:inline-block}.image-interactions .unlike-button{background:none;border:none;cursor:pointer;padding:0}.image-interactions .unlike-button img{display:block}.image-interactions .save-this-shake-selector{background-color:var(--color-status-warning);border-radius:var(--size-border-radius-large);min-height:30px;padding:var(--size-spacing-half) 0;position:absolute;right:0;top:0;width:170px;z-index:1}.image-interactions .save-this-shake-selector ul{clear:both;list-style:none;margin:0;padding:0}.image-interactions .save-this-shake-selector a{word-wrap:break-word;border-radius:var(--size-border-radius-default);color:var(--color-text-light-emphasis);display:block;font-weight:500;margin:0 var(--size-spacing-half);overflow-wrap:break-word;padding:var(--size-spacing-half) var(--size-spacing-half);text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500)}.image-interactions .save-this-shake-selector a:focus,.image-interactions .save-this-shake-selector a:hover{background-color:var(--color-base-white-transparent-200)}.image-interactions .save-this-shake-selector .close{color:var(--color-text-light-emphasis);cursor:pointer;display:block;position:absolute;right:var(--size-spacing-default);top:var(--size-spacing-default);transform:rotate(180deg)}.image-interactions .save-this-shake-selector .close:focus,.image-interactions .save-this-shake-selector .close:hover{opacity:.66}.image-interactions .save-this-shake-selector .close:before{bottom:calc(var(--size-spacing-default)*-1);content:"";left:calc(var(--size-spacing-half-again)*-1);position:absolute;right:calc(var(--size-spacing-half-again)*-1);top:calc(var(--size-spacing-double)*-1)}.image-interactions .save-this-shake-selector-loading{min-height:100px}.image-interactions .save-this-shake-selector-loading:after{animation:load8 1.1s linear infinite;border:5px solid var(--color-base-white-transparent-200);border-left-color:var(--color-base-white-transparent-800);border-radius:50%;content:"";display:block;height:50px;margin:1em auto;transform:translateZ(0);width:50px}@media screen and (prefers-reduced-motion:reduce){.image-interactions .save-this-shake-selector-loading:after{animation:none}}@keyframes load8{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.image-content-footer{color:var(--color-page-text-secondary);font-size:.75rem;padding-top:var(--size-spacing-default)}.image-content-footer .originally-posted-by{clear:left;padding-bottom:var(--size-spacing-half)}.image-content-footer .originally-posted-by .avatar--link{display:inline-block;text-decoration:none;vertical-align:middle}.image-content-footer .originally-posted-by .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-content-footer .inline-meta{display:flex;float:left}.image-content-footer .inline-meta>*{flex:none}.image-content-footer .created-at{padding-top:var(--size-spacing-half)}.image-content-footer .created-at a{color:var(--color-page-text-secondary);text-decoration:none}.image-content-footer .created-at a:focus,.image-content-footer .created-at a:hover{color:var(--color-page-text);text-decoration:underline}.image-content-footer .stats{list-style:none;margin:0 0 0 var(--size-spacing-default);padding:0}.image-content-footer .stats li{float:left;padding-right:var(--size-spacing-half)}.image-content-footer .stats a{background-position:var(--size-spacing-half) .5em;background-repeat:no-repeat;background-size:1em 1em;border-top-left-radius:var(--size-border-radius-default);border-top-right-radius:var(--size-border-radius-default);display:block;float:left;padding:var(--size-spacing-half);padding-bottom:var(--size-spacing-default);padding-left:calc(var(--size-spacing-default) + 1em);text-decoration:none}.image-content-footer .stats a:focus,.image-content-footer .stats a:hover{background-color:var(--color-background-content-secondary)}.image-content-footer .stats .views{padding-top:var(--size-spacing-half)}.image-content-footer .stats .saves a{background-image:svg-load("icons/save-tiny.svg");background-size:1.33em 1em;color:var(--color-status-warning);padding-left:calc(var(--size-spacing-default) + 1.33em)}.image-content-footer .stats .likes a{background-image:svg-load("icons/like.svg");color:var(--color-status-danger)}.image-content-footer .stats .comments a{background-image:svg-load("icons/comment.svg")}.image-content .inline-details,.image-content-footer .stats .selected a{background-color:var(--color-background-content-secondary)}.image-content .inline-details{border-radius:var(--size-border-radius-default);clear:both;min-height:34px;padding:var(--size-spacing-default)}.image-content .inline-details .user-saves-likes{background-color:var(--color-background-content);min-height:var(--size-avatar-tiny);padding:var(--size-spacing-default)}.image-content .inline-details .user-saves-likes a{align-items:center;color:var(--color-text-link-primary);display:inline-flex;font-weight:var(--number-font-weight-bold);margin-right:var(--size-spacing-default);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.image-content .inline-details .user-saves-likes a:active,.image-content .inline-details .user-saves-likes a:focus,.image-content .inline-details .user-saves-likes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.image-content .inline-details .user-saves-likes a:active,.image-content .inline-details .user-saves-likes a:focus,.image-content .inline-details .user-saves-likes a:hover{text-decoration:underline}.image-content .inline-details .user-saves-likes img{margin-right:var(--size-spacing-half)}.image-content .inline-details .user-saves-likes .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-content .inline-details .comment{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);display:flex;margin-bottom:var(--size-spacing-default);padding:var(--size-spacing-default)}.image-content .inline-details .comment .avatar{flex:none}.image-content .inline-details .comment .avatar a{display:block}.image-content .inline-details .comment .avatar--img{height:var(--size-avatar-small);width:var(--size-avatar-small)}.image-content .inline-details .comment .comment-body{flex:1;overflow:hidden;padding-left:var(--size-spacing-default)}.image-content .inline-details .comment .comment-body-text{word-wrap:break-word;clear:both;color:var(--color-page-text);font-size:.875rem;overflow-wrap:break-word;padding-top:.25em;word-break:break-word}.image-content .inline-details .comment .meta{align-items:flex-end;display:flex;flex-wrap:wrap}.image-content .inline-details .comment .meta>*+*{margin-left:.8em}.image-content .inline-details .comment .delete-form{display:none}.image-content .inline-details .comment .meta .username{font-weight:var(--number-font-weight-bold)}.image-content .inline-details .comment .meta .pro-badge{margin-left:.25em}.image-content .inline-details .comment .created-at{margin-left:var(--size-spacing-default);padding-top:0}.image-content .inline-details .comment .reply-to{background-image:svg-load("icons/reply.svg");background-size:12px 11px}.image-content .inline-details .comment .delete,.image-content .inline-details .comment .reply-to{background-position:0 0;background-repeat:no-repeat;display:none;padding-left:var(--size-spacing-half-again)}.image-content .inline-details .comment .delete{background-image:svg-load("icons/delete-comment.svg");background-size:13px 13px;color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.image-content .inline-details .comment .delete:active,.image-content .inline-details .comment .delete:focus,.image-content .inline-details .comment .delete:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}.image-content .inline-details .comment:focus .delete,.image-content .inline-details .comment:focus .reply-to,.image-content .inline-details .comment:hover .delete,.image-content .inline-details .comment:hover .reply-to{display:block}.image-content .inline-details .show-more-comments{display:block;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default) 0;text-align:right;text-decoration:none}.image-content .inline-details .post-comment-inline textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text-disabled);height:2em;padding:var(--size-spacing-half)}.image-content .inline-details .post-comment-inline .button{display:none;padding-top:var(--size-spacing-half)}.image-content .inline-details .post-comment-inline.post-comment-inline-expanded textarea{color:var(--color-page-text);min-height:100px}.image-content .inline-details .post-comment-inline.post-comment-inline-expanded .button{display:block}.new-post-panel{background-color:var(--color-background-content);border-bottom-left-radius:10px;border-bottom-right-radius:10px;box-shadow:var(--size-spacing-default) var(--size-spacing-default) 0 var(--color-base-black-transparent-200);display:none;left:var(--size-spacing-triple);margin:0 auto;max-width:560px;padding:var(--size-spacing-half-again);position:fixed;right:var(--size-spacing-triple);top:calc(var(--size-spacing-default)*-1);z-index:999}@media screen and (min-width:768px){.new-post-panel{padding:calc(var(--size-spacing-default)*5)}}.new-post-panel h2{font-size:1.75rem;line-height:1.2;margin-bottom:var(--size-spacing-default);position:relative}.new-post-panel h2 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.new-post-panel h2 a:active,.new-post-panel h2 a:focus,.new-post-panel h2 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.new-post-panel h2 a:active,.new-post-panel h2 a:focus,.new-post-panel h2 a:hover{text-decoration:underline}.new-post-panel p{font-size:.875rem;line-height:1.2;margin:var(--size-spacing-double) 0;padding:0 var(--size-spacing-half)}.new-post-panel .upload-image-input{cursor:pointer;height:100%;left:0;opacity:0;position:absolute;top:0;width:100%}.new-post-panel .save-video-form{display:flex}.new-post-panel .save-video-form .field-input{flex:1}.new-post-panel .save-video-form .btn{flex:none;margin-left:var(--size-spacing-default)}.new-post-panel .post-video-form{margin-top:var(--size-spacing-double)}.new-post-panel .shake-selector{position:relative}.new-post-panel .shake-selector h3 a{color:var(--color-page-text);display:block;text-decoration:none}.new-post-panel .shake-selector h3 a .green{word-wrap:break-word;color:var(--color-status-success);overflow-wrap:break-word;word-break:break-word}.new-post-panel .shake-selector h3 a:focus .green,.new-post-panel .shake-selector h3 a:hover .green{color:var(--color-status-success-pastel-dark)}.new-post-panel .shake-selector ul{background-color:var(--color-status-success-pastel-light);border-radius:var(--size-border-radius-large);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-200);display:none;left:var(--size-spacing-triple);list-style:none;margin:0;max-height:66vh;overflow:auto;padding:var(--size-spacing-default) 0 var(--size-spacing-half) 0;position:absolute;top:1.638rem;width:200px;z-index:2}.new-post-panel .shake-selector ul li a{word-wrap:break-word;color:var(--color-status-success-pastel-dark);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;padding:var(--size-spacing-half) var(--size-spacing-half-again);text-decoration:none;word-break:break-word}.new-post-panel .shake-selector ul li a:focus,.new-post-panel .shake-selector ul li a:hover{color:var(--color-text-dark)}.new-post-panel--inner{display:flex;flex-direction:column}@media screen and (min-width:768px){.new-post-panel--inner{flex-direction:row}}.new-post-panel--inner>*{flex:1 1 0;min-width:0;padding-bottom:var(--size-spacing-half-again);padding-top:var(--size-spacing-half-again)}@media screen and (min-width:768px){.new-post-panel--inner>*{padding-bottom:var(--size-spacing-quadruple);padding-top:calc(var(--size-spacing-default)*6)}}.new-post-panel--inner>:first-child{border-bottom:1px dashed var(--color-border-default)}@media screen and (min-width:768px){.new-post-panel--inner>:first-child{border-bottom:0;border-right:1px dashed var(--color-border-default);padding-right:var(--size-spacing-double)}}@media screen and (min-width:768px){.new-post-panel--inner>:last-child{padding-left:var(--size-spacing-double)}}.new-post-panel--inner>:first-child:last-child{border:0;padding:0}.dashboard-new-user{padding:var(--size-spacing-double) var(--size-spacing-triple)}.dashboard-new-user h1{color:var(--color-status-success);font-size:3.125rem;margin-bottom:var(--size-spacing-double)}.dashboard-new-user h2{font-size:1.875rem}.dashboard-new-user h3{font-size:1.375rem;margin-bottom:var(--size-spacing-half)}.dashboard-new-user h4{font-size:.875rem;margin-bottom:var(--size-spacing-half)}.dashboard-new-user p{font-size:.875rem;line-height:1.75;padding-bottom:var(--size-spacing-half-again)}.dashboard-new-user a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.dashboard-new-user a:active,.dashboard-new-user a:focus,.dashboard-new-user a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.dashboard-new-user .two-columns{display:flex}.dashboard-new-user .two-columns .left-column{flex:0 0 70px;margin-right:var(--size-spacing-double);margin-top:var(--size-spacing-default)}@media screen and (min-width:768px){.dashboard-new-user .two-columns .left-column{flex-basis:130px;margin-left:var(--size-spacing-double);margin-right:var(--size-spacing-quadruple)}}.dashboard-new-user .two-columns .right-column{flex:1;margin-top:var(--size-spacing-quadruple)}.new-members{border-top:1px dashed var(--color-border-default);list-style:none;margin:var(--size-spacing-triple) 0 0;padding:var(--size-spacing-triple) 0 0}.new-members>li{border-bottom:1px dashed var(--color-border-default);clear:both;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.new-members>li>a{display:block;float:left;margin-right:var(--size-spacing-default)}.new-members>li h4{word-wrap:break-word;font-size:1.125rem;overflow-wrap:break-word;padding-bottom:var(--size-spacing-half);word-break:break-word}.new-members>li h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.new-members>li h4 a:active,.new-members>li h4 a:focus,.new-members>li h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.new-members>li h4 a:active,.new-members>li h4 a:focus,.new-members>li h4 a:hover{text-decoration:underline}.new-members>li p{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;word-break:break-word}.new-members .image-medium{margin-left:calc(var(--size-spacing-default)*6);padding-bottom:0;padding-top:var(--size-spacing-default)}.new-members .image-medium .user-and-title>a{display:none}.notification-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double)}.notification-block-link a{display:block;text-decoration:none}.notification-block-hd,.notification-block-link a{font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-half-again)}.notification-block-hd{cursor:pointer}.notification-block-bd{display:none;padding:0 var(--size-spacing-default) var(--size-spacing-default)}.notification-block-bd .notification{word-wrap:break-word;background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);color:var(--color-page-text);font-size:.875rem;overflow-wrap:break-word;padding:var(--size-spacing-default);position:relative;word-break:break-word}.notification-block-bd .notification:after{clear:both;content:"";display:table}.notification-block-bd .notification+.notification{margin-top:var(--size-spacing-default)}.notification-block-bd .notification .notification-close{outline:none;position:absolute;right:var(--size-spacing-half);top:var(--size-spacing-half)}.notification-block-bd .notification .thumb{float:left;padding-right:var(--size-spacing-default);text-align:center;width:var(--size-avatar-large)}.notification-block-bd .notification .context{font-size:.75rem;overflow:hidden}.notification-block-bd .clear-all{clear:both;margin-top:var(--size-spacing-default);text-align:right}.notification-block-follow{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-primary)}.notification-block-save{background-color:var(--color-bg-warning-pastel);color:var(--color-status-warning-pastel-dark)}.notification-block-like{background-color:var(--color-bg-danger-pastel);color:var(--color-status-danger-pastel-dark)}.notification-block-comment{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-secondary)}.notification-block-invitation-approved,.notification-block-invitation-request,.notification-block-shakeinvitation{background-color:var(--color-bg-success-pastel);color:var(--color-status-success-pastel-dark)}.notification-block-aggregate{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-primary)}.notification-block-follow .notification{padding-right:var(--size-spacing-triple)}.notification-block-shakeinvitation .shake-thumb{float:left;margin-right:var(--size-spacing-default);width:var(--size-avatar-default)}.notification-block-shakeinvitation .shake-thumb a{display:block}.notification-block-shakeinvitation h4{padding-top:var(--size-spacing-default)}.notification-block-shakeinvitation .shake-context{clear:both;margin-top:var(--size-spacing-default)}.notification-block-shakeinvitation .shake-context a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.notification-block-shakeinvitation .shake-context a:active,.notification-block-shakeinvitation .shake-context a:focus,.notification-block-shakeinvitation .shake-context a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.notification-block-shakeinvitation .shake-context a:active,.notification-block-shakeinvitation .shake-context a:focus,.notification-block-shakeinvitation .shake-context a:hover{text-decoration:underline}.notification-block-shakeinvitation .buttons{display:flex;justify-content:space-between;margin-top:var(--size-spacing-default)}.content-shake .notification-block-shakeinvitation{margin-top:var(--size-spacing-double)}.notification-block-invitation-request .notification a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.notification-block-invitation-request .notification a:active,.notification-block-invitation-request .notification a:focus,.notification-block-invitation-request .notification a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.notification-block-invitation-request .notification a:active,.notification-block-invitation-request .notification a:focus,.notification-block-invitation-request .notification a:hover{text-decoration:underline}.notification-block-invitation-request .notification-actions{clear:both;display:flex;justify-content:space-between;margin-top:var(--size-spacing-default)}.notification-block-tou .notification-block-link a{color:var(--color-page-text-emphasis)}.notification-block-tou .notification-block-link a:focus,.notification-block-tou .notification-block-link a:hover{color:var(--color-text-link-hover)}.account-header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));padding:1.5rem 1.875rem 1rem;width:100%}.account-header .avatar{display:flex}.account-header .avatar img{display:block;height:50px;width:50px}.account-header .avatar-media{flex:none}.account-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.account-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.account-header h2{font-size:2.625rem}}.pagination{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;font-weight:var(--number-font-weight-bold);margin:var(--size-spacing-quadruple);padding:var(--size-spacing-default)}.pagination .current,.pagination a{padding:0 var(--size-spacing-half)}.pagination span.next-link,.pagination span.previous-link{color:var(--color-page-text-secondary)}.pagination .previous-link{flex-basis:50%}@media screen and (min-width:480px){.pagination .previous-link{flex-basis:auto;margin-right:auto}}.pagination .next-link{flex-basis:50%;text-align:right}@media screen and (min-width:480px){.pagination .next-link{flex-basis:auto;margin-left:auto;order:1}}.pagination-inner{align-items:center;background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);display:flex;flex-wrap:wrap;justify-content:center;padding:var(--size-spacing-default)}@media screen and (min-width:480px){.pagination-inner{flex-wrap:none}}.linear-navigation{display:flex;justify-content:space-between;padding:var(--size-spacing-triple) var(--size-spacing-quadruple)}.linear-navigation .newer a,.linear-navigation .older a{display:block}.promotions{list-style:none;margin:var(--size-spacing-default) 0;padding:0}.promotions li{float:left;margin-bottom:var(--size-spacing-default);margin-right:var(--size-spacing-default);overflow:visible;position:relative}.promotions li:focus .promotion-avatar,.promotions li:hover .promotion-avatar{border-color:var(--color-brand-primary)}.promotions li:focus .promotion-name,.promotions li:hover .promotion-name{display:block}.promotions .promotion-avatar{border:4px solid transparent;border-radius:var(--size-border-radius-large);overflow:hidden;transition:all .75s ease 0s}@media screen and (prefers-reduced-motion:reduce){.promotions .promotion-avatar{transition:none}}.promotions .promotion-avatar img{display:block;height:var(--size-avatar-large);width:var(--size-avatar-large)}.promotions .promotion-name{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-primary);border:none;border-radius:.5em;bottom:-3.5em;box-shadow:.22em .22em 0 var(--color-base-black-transparent-100);color:var(--color-text-light-emphasis);cursor:pointer;display:inline-block;display:none;font-size:18px;font-size:.875rem;font-weight:500;height:2.2em;left:50%;letter-spacing:.033em;line-height:2.2em;margin-right:.22em;padding:0 .88em;position:absolute;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transform:translateX(-50%);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap;z-index:1}.promotions .promotion-name:focus,.promotions .promotion-name:hover{color:var(--color-text-light-emphasis)}.promotions .promotion-name:focus{outline:none}.promotions .promotion-name:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.promotions .promotion-name:focus,.promotions .promotion-name:hover{background-color:var(--color-background-button-primary-hover)}.promotions .promotion-name:disabled{box-shadow:none!important}.promo-block{margin:var(--size-spacing-double) auto;max-width:100%;text-align:center;width:285px}@media screen and (max-width:320px){.promo-block{display:none}}.shake-image{cursor:pointer;height:284px;max-width:284px;overflow:hidden;position:relative}.shake-image img{display:block}.shake-image .shake-image-input{cursor:pointer;height:100%;left:0;opacity:0;position:absolute;top:0;width:100%;z-index:1}.shake-image .border{display:none}.shake-image.is-editable:focus .border,.shake-image.is-editable:hover .border,.shake-image.shake-image-hover .border{border:10px solid var(--color-status-edit);display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0}.shake-image .shake-image-placeholder{align-items:center;background:var(--color-background-content-secondary);border:1px dashed var(--color-border-default);display:flex;flex-direction:column;height:100%;justify-content:center;max-width:100%;padding:var(--size-spacing-triple);text-align:center}.shake-image .shake-image-placeholder strong{color:var(--color-brand-primary);display:block}.shake-details .shake-edit-description-form,.shake-details .shake-edit-title-form{display:none}.shake-details .title{word-wrap:break-word;font-size:2.25rem;margin:var(--size-spacing-half-again) 0 var(--size-spacing-default);overflow-wrap:break-word;word-break:break-word}.shake-details .shake-edit-title-hover,.shake-details.is-editable .title:focus,.shake-details.is-editable .title:hover{background-color:var(--color-status-edit)}.shake-details .shake-edit-title-input{border:1px solid var(--color-border-default);font-size:2.25rem;font-weight:var(--number-font-weight-bold);margin-top:var(--size-spacing-default);width:100%}.shake-details .description{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;white-space:pre-wrap;word-break:break-word}.shake-details .description .placeholder{color:var(--color-page-text-secondary);font-style:italic}.shake-details .shake-edit-description-hover,.shake-details.is-editable .description:focus,.shake-details.is-editable .description:hover{background-color:var(--color-status-edit)}.shake-details .shake-edit-description-input{border:1px solid var(--color-border-default);font-size:.875rem;width:100%}.shake-details .buttons{align-items:center;display:flex;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-half)}.shake-details .or{color:var(--color-page-text-secondary);padding:var(--size-spacing-half)}.shake-list{border-top:1px dashed var(--color-border-default);list-style:none;margin:0;padding:0}.shake-list--shake{border-bottom:1px dashed var(--color-border-default);clear:both;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.shake-list--thumb{float:left;margin-right:1em}.shake-list--description,.shake-view-description,.shake-view-featured,.shake-view-title{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.shake-members{list-style:none;margin:0;padding:var(--size-spacing-triple)}.shake-members li{border-bottom:1px dashed var(--color-border-default);display:flex;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.shake-members .member--img{flex:none;margin-right:var(--size-spacing-default)}.shake-members .details{flex:1}.shake-members h4{font-size:1.125rem;padding-bottom:var(--size-spacing-half)}.shake-members h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.shake-members h4 a:active,.shake-members h4 a:focus,.shake-members h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.shake-members h4 a:active,.shake-members h4 a:focus,.shake-members h4 a:hover{text-decoration:underline}.shake-members .about{font-size:.875rem;margin:0}.shake-members .website{display:block;margin-top:var(--size-spacing-half)}.following-wrapper{margin:var(--size-spacing-double) 0}.following-wrapper h3{color:var(--color-page-text);font-size:.875rem;padding-bottom:var(--size-spacing-default);padding-left:var(--size-spacing-half-again);padding-right:var(--size-spacing-half-again)}.following-wrapper h3 a{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-secondary);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);cursor:pointer;display:inline-block;float:right;font-size:18px;font-size:12px;font-weight:500;font-weight:400;height:2.2em;letter-spacing:.033em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.following-wrapper h3 a:focus,.following-wrapper h3 a:hover{color:var(--color-text-light-emphasis)}.following-wrapper h3 a:focus{outline:none}.following-wrapper h3 a:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.following-wrapper h3 a:focus,.following-wrapper h3 a:hover{background-color:var(--color-background-button-secondary-hover)}.following-wrapper h3 span{color:var(--color-page-text-secondary);font-weight:var(--number-font-weight-normal)}.following{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large)}.following ul{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:var(--size-spacing-half-again);padding-right:0}.following ul li{flex:none;margin-bottom:var(--size-spacing-half);margin-right:var(--size-spacing-half)}.following ul li a{display:block}.following br{display:none}.following .view-all-following{display:block;font-size:.75rem;margin-top:calc(var(--size-spacing-default)*-1);padding-bottom:var(--size-spacing-default);padding-right:var(--size-spacing-half-again);text-align:right}.other-shakes-wrapper{margin-bottom:var(--size-spacing-double)}.other-shakes-wrapper h3{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;padding-bottom:var(--size-spacing-default);padding-left:var(--size-spacing-half-again);word-break:break-word}.other-shakes{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-default) var(--size-spacing-half-again)}.other-shakes ul{list-style:none;margin:0;padding:0}.other-shakes a{word-wrap:break-word;color:var(--color-text-link-primary);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;padding:var(--size-spacing-half) 0;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.other-shakes a:active,.other-shakes a:focus,.other-shakes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.other-shakes a:active,.other-shakes a:focus,.other-shakes a:hover{text-decoration:underline}.find-shakes-block{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.find-shakes-block h3{color:var(--color-brand-secondary);font-size:1.25rem}.find-shakes-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.find-shakes-block-content p{margin:0}.find-shakes-block-content a{font-weight:var(--number-font-weight-bold);text-decoration:none}.upgrade-account-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.upgrade-account-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-half);padding:var(--size-spacing-double)}.upgrade-account-block-content h3{font-size:1.125rem;margin-bottom:var(--size-spacing-half)}.upgrade-account-block-content h3 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.upgrade-account-block-content h3 a:active,.upgrade-account-block-content h3 a:focus,.upgrade-account-block-content h3 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.upgrade-account-block-content h3 a:active,.upgrade-account-block-content h3 a:focus,.upgrade-account-block-content h3 a:hover{text-decoration:underline}.upgrade-account-block-content p{margin:0}.cool-tools-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.cool-tools-block h3{color:var(--color-brand-primary);font-size:1.25rem}.cool-tools-block p{font-size:.875rem;margin:var(--size-spacing-half) 0 0}.cool-tools-block a{background-color:var(--color-background-content);background-position:5px 5px;background-repeat:no-repeat;background-size:40px 45px;border-radius:var(--size-border-radius-large);color:var(--color-text-link-primary);display:block;font-size:.75rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default);padding-bottom:var(--size-spacing-double);padding-left:55px;padding-top:var(--size-spacing-double);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.cool-tools-block a:active,.cool-tools-block a:focus,.cool-tools-block a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.cool-tools-block a:active,.cool-tools-block a:focus,.cool-tools-block a:hover{text-decoration:underline}.cool-tools-block .browser-tools{list-style:none;margin:var(--size-spacing-double) 0 var(--size-spacing-default);padding:0}.cool-tools-block .browser-tools li+li{margin-top:var(--size-spacing-default)}.cool-tools-block .bookmarklet a{background-image:svg-load("icons/bookmark.svg")}.cool-tools-block .safari a{background-image:svg-load("tools/safari.svg")}.cool-tools-block .firefox a{background-image:svg-load("tools/firefox.svg")}.cool-tools-block .chrome a{background-image:svg-load("tools/chrome.svg")}.cool-tools-block .twitter-setup{background-image:svg-load("tools/twitter.svg")}.shake-invite-member-block{background-color:var(--color-brand-primary);border-radius:var(--size-border-radius-large);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);clear:both;margin-bottom:var(--size-spacing-double);margin-right:var(--size-spacing-half);margin-top:var(--size-spacing-double);position:relative}.shake-invite-member-block h3{color:var(--color-text-light-emphasis);padding:var(--size-spacing-half-again) var(--size-spacing-default);text-shadow:.05em .05em .05em var(--color-base-black-transparent-500)}.shake-invite-member-block form{padding:0 var(--size-spacing-default) var(--size-spacing-double)}.shake-invite-member-block .shake-input-wrapper{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);display:flex;padding:var(--size-spacing-half);position:relative}.shake-invite-member-block .input-text{background-color:var(--color-background-content);border:0;border-radius:var(--size-border-radius-default);flex:1}.shake-invite-member-block .invite-button{flex:none;margin-left:var(--size-spacing-half)}.shake-invite-member-block .shake-results{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);display:none;left:0;list-style:none;margin:0;padding:var(--size-spacing-half);position:absolute;top:var(--size-spacing-quadruple);width:220px}.shake-invite-member-block .shake-results li{align-items:center;border-radius:var(--size-border-radius-default);cursor:pointer;display:flex;font-size:.875rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-half)}.shake-invite-member-block .shake-results li:focus,.shake-invite-member-block .shake-results li:hover{background-color:var(--color-bg-success-pastel)}.shake-invite-member-block .shake-results li img{flex:none;height:var(--size-avatar-tiny);margin-right:var(--size-spacing-half);width:var(--size-avatar-tiny)}.shake-invite-member-block .shake-results li span{flex:1}.shake-sidebar-actions{display:flex;margin-bottom:var(--size-spacing-double);margin-top:var(--size-spacing-default)}.shake-sidebar-actions>*{flex:none}.shake-sidebar-actions>*+*{margin-left:var(--size-spacing-default)}.shake-sidebar-actions .follow h4,.shake-sidebar-actions .icon,.shake-sidebar-actions .user-follow h4 a{display:none}.shake-sidebar-editor-block{align-items:center;background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);clear:both;display:flex;font-size:.875rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.shake-sidebar-editor-block a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.shake-sidebar-editor-block a:active,.shake-sidebar-editor-block a:focus,.shake-sidebar-editor-block a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.shake-sidebar-editor-block a:active,.shake-sidebar-editor-block a:focus,.shake-sidebar-editor-block a:hover{text-decoration:underline}.shake-sidebar-editor-block .editor-image{flex:none}.shake-sidebar-editor-block .editor-image .avatar--img{display:block;width:var(--size-avatar-default)}.shake-sidebar-editor-block .editor-details{flex:1;margin-left:var(--size-spacing-default)}.sidebar-flag-nsfw{margin-top:var(--size-spacing-triple)}.sidebar-flag-nsfw .flag-nsfw-button,.sidebar-flag-nsfw .unflag-nsfw-button{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-danger-pastel);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-danger-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .flag-nsfw-button:hover,.sidebar-flag-nsfw .unflag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:hover{color:var(--color-text-light-emphasis)}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:focus{outline:none}.sidebar-flag-nsfw .flag-nsfw-button:disabled,.sidebar-flag-nsfw .unflag-nsfw-button:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.sidebar-flag-nsfw .flag-nsfw-button:disabled,.sidebar-flag-nsfw .unflag-nsfw-button:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .flag-nsfw-button:hover,.sidebar-flag-nsfw .unflag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.flag-image{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-warning-pastel);background-image:svg-load("icons/flag.svg");background-position:.5em;background-repeat:no-repeat;background-size:7px 9px;border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-warning-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;margin-top:var(--size-spacing-triple);padding:0 .88em 0 1.5em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.flag-image:focus,.flag-image:hover{color:var(--color-text-light-emphasis)}.flag-image:focus{outline:none}.flag-image:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.flag-image:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.flag-image:focus,.flag-image:hover{background-color:var(--color-background-button-warning-pastel-hover);color:var(--color-text-button-warning-pastel-hover)}.flag-image.flag-image-set{color:var(--color-status-danger)}.delete-post-text{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-danger-pastel);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-danger-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;margin-top:var(--size-spacing-triple);padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.delete-post-text:focus,.delete-post-text:hover{color:var(--color-text-light-emphasis)}.delete-post-text:focus{outline:none}.delete-post-text:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.delete-post-text:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.delete-post-text:focus,.delete-post-text:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.sidebar-stats{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;font-weight:var(--number-font-weight-bold);margin-top:var(--size-spacing-triple)}.sidebar-stats,.sidebar-stats .tab{padding:var(--size-spacing-default)}.sidebar-stats .tab{display:block}.sidebar-stats .selected .tab{background-color:var(--color-background-content);border-top-left-radius:var(--size-border-radius-large);border-top-right-radius:var(--size-border-radius-large)}.sidebar-stats .enable-cursor{cursor:pointer}.sidebar-stats-tabs{display:flex;list-style:none;margin:0;padding:0}.sidebar-stats-tabs>*{flex:1}.sidebar-stats-views{color:var(--color-page-text-secondary)}.sidebar-stats-saves{color:var(--color-status-warning)}.sidebar-stats-hearts{color:var(--color-status-danger)}.sidebar-stats-content{background-color:var(--color-background-content);border-bottom-left-radius:var(--size-border-radius-large);border-bottom-right-radius:var(--size-border-radius-large);display:none;padding:var(--size-spacing-half-again)}.sidebar-stats-content .user-action{align-items:center;display:flex}.sidebar-stats-content .user-action+.user-action{margin-top:var(--size-spacing-default)}.sidebar-stats-content .icon{margin-right:var(--size-spacing-half)}.sidebar-stats-content .icon .avatar--img{display:block;height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.sidebar-stats-content .name{word-wrap:break-word;color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.sidebar-stats-content .name:active,.sidebar-stats-content .name:focus,.sidebar-stats-content .name:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.sidebar-stats-content .name:active,.sidebar-stats-content .name:focus,.sidebar-stats-content .name:hover{text-decoration:underline}.sidebar-stats-content .date{color:var(--color-page-text-secondary);font-size:.75rem;font-weight:400;margin-left:var(--size-spacing-default);white-space:nowrap}.meta-data{margin-top:var(--size-spacing-triple)}.meta-data h4{font-size:.875rem;padding-left:var(--size-spacing-half-again)}.meta-data h4,.meta-data p{color:var(--color-page-text-secondary)}.meta-data p{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-default);font-size:.75rem;margin:var(--size-spacing-half) 0;padding:var(--size-spacing-default) var(--size-spacing-half-again)}.shake-details-title{color:var(--color-page-text-secondary);font-size:.875rem;margin-bottom:var(--size-spacing-half);margin-top:var(--size-spacing-triple);padding-left:var(--size-spacing-half-again)}.in-these-shakes{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;margin:0 0 var(--size-spacing-triple) 0;padding:var(--size-spacing-half-again)}.in-these-shakes ul{list-style:none;margin:0;padding:0}.in-these-shakes li{align-items:center;display:flex;margin-bottom:var(--size-spacing-default)}.in-these-shakes a{word-wrap:break-word;color:var(--color-text-link-primary);flex:1;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.in-these-shakes a:active,.in-these-shakes a:focus,.in-these-shakes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.in-these-shakes a:active,.in-these-shakes a:focus,.in-these-shakes a:hover{text-decoration:underline}.in-these-shakes .delete-from-shakes-form,.in-these-shakes .input-checkbox{flex:none;margin-right:var(--size-spacing-default)}.permalink-social{display:flex;list-style:none;margin:0;padding:0}.permalink-social li{flex:none}.permalink-social li+li{margin-left:var(--size-spacing-default)}.permalink-social a{display:block}.permalink-social a:focus,.permalink-social a:hover{opacity:.66}.permalink-social img{display:block}.permalink-social .tumblr a{background-color:#001935;display:flex;height:21px;width:21px}.permalink-social .tumblr a img{height:12px;margin:auto;width:9px}.site-footer{color:var(--color-page-text-secondary);font-size:.75em;margin:var(--size-spacing-triple);text-align:center}.site-footer p{margin:0}.site-footer p+p{margin-top:.5em}.site-footer a{white-space:nowrap}.site-header{display:flex}.site-branding{margin-right:1em;margin-top:1em;width:211px}.site-branding a{display:block}.site-branding a:focus:not(:focus-visible){box-shadow:none}.site-branding--logo{display:block;height:auto;width:100%}.site-nav{flex:1;margin-top:69px;position:relative;text-align:right}.site-nav--list{display:none;flex-direction:column;list-style:none;margin:0;padding:0;position:absolute;right:0;top:47px;z-index:99}.site-nav.is-expanded .site-nav--list{display:flex}@media screen and (min-width:768px){.site-nav--list{display:flex;flex-direction:row;position:static}}.site-nav--item{display:flex;flex:none;justify-content:flex-end}.site-nav--item>*{flex:1}@media screen and (min-width:768px){.site-nav--item>*{flex:none}}.site-nav--item+.site-nav--item{margin-top:.25em}@media screen and (min-width:768px){.site-nav--item+.site-nav--item{margin-left:1em;margin-top:0}}@media screen and (min-width:768px){.site-nav--toggle{display:none}.site-nav--signup,.site-nav--upload{flex-grow:1}}.site-nav--conversations a,.site-nav--popular a,.site-nav--search a{display:block}.site-nav--signup .call-out{color:var(--color-page-text-secondary);display:none;font-size:.875rem;font-weight:var(--number-font-weight-bold);max-width:18em;padding-right:var(--size-spacing-default);padding-top:3px}@media screen and (min-width:768px){.site-nav--signup .call-out{display:block}}.user-counts{background-color:var(--color-border-default);border-radius:var(--size-border-radius-large);clear:both;margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.user-counts ul{display:flex;justify-content:space-between;list-style:none;margin:0;padding:0}.user-counts li{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);flex-grow:1;flex-shrink:1;margin-right:var(--size-spacing-default);padding:var(--size-spacing-default) 0;text-align:center;width:70px}.user-counts li .num{display:block;font-size:1.125rem;font-weight:var(--number-font-weight-bold)}.user-counts li .label{display:block;font-size:.875rem}.user-counts .views{width:95px}.user-counts .saves{color:var(--color-status-warning)}.user-counts .likes{color:var(--color-status-danger);margin-right:0}.user-follow{display:flex}.user-follow .icon{flex:none;margin-right:var(--size-spacing-default)}.user-follow h4{word-wrap:break-word;font-size:.875rem;margin-bottom:var(--size-spacing-half);overflow-wrap:break-word;word-break:break-word}.user-follow h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);padding-bottom:var(--size-spacing-half);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.user-follow h4 a:active,.user-follow h4 a:focus,.user-follow h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.user-follow h4 a:active,.user-follow h4 a:focus,.user-follow h4 a:hover{text-decoration:underline}.user-follow .follow{flex:1}.user-follow-extended{display:block;margin:var(--size-spacing-double) var(--size-spacing-half)}.user-follow-extended .icon{float:left;margin-right:var(--size-spacing-default)}.user-follow-extended .details{padding-bottom:var(--size-spacing-default)}.user-follow-extended h4 a{font-size:1.125rem}.user-follow-extended .about{display:block;font-size:.875rem;line-height:1.3;margin-bottom:var(--size-spacing-default);white-space:pre-wrap}.user-follow-extended .about,.user-follow-extended .website a{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.user-follow-extended .website a{color:var(--color-text-link-primary);font-size:.8rem;font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.user-follow-extended .website a:active,.user-follow-extended .website a:focus,.user-follow-extended .website a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.user-follow-extended .website a:active,.user-follow-extended .website a:focus,.user-follow-extended .website a:hover{text-decoration:underline}.user-follow-extended:after{clear:both;content:"";display:table}.user-nav{background-color:var(--color-background-content);border-bottom-left-radius:1em;font-size:.9rem;padding:.75em 1.5em;position:absolute;right:0;top:0}.user-nav--list{display:flex;list-style:none;margin:0;padding:0}.user-nav--item+.user-nav--item{margin-left:1.5em}.user-nav--link{font-weight:var(--number-font-weight-bold);text-decoration:none;white-space:nowrap}.admin-nav{list-style:none;margin:0;padding:0}.admin-new-users{border:2px solid var(--color-status-danger)}.admin-new-users .body{padding:var(--size-spacing-triple)}.api-accept,.api-decline{float:left;margin-right:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.content-developer{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-developer{padding:var(--size-spacing-quadruple)}}.content-developer h1{font-size:3.25rem}.content-developer h2{font-size:1.5rem;margin-bottom:var(--size-spacing-half-again);margin-top:var(--size-spacing-half-again)}.content-developer p{line-height:1.4;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-default)}.content-developer dt big{font-size:normal}.content-developer dt big,.content-developer dt em{color:var(--color-page-text-secondary)}.faq-page h1{font-size:2.5rem}.faq-page h2{font-size:1.25rem;margin-top:var(--size-spacing-half-again)}.faq-page li,.faq-page p{font-size:.875rem;line-height:2;margin-top:5px}.faq-page li a,.faq-page p a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.faq-page li a:active,.faq-page li a:focus,.faq-page li a:hover,.faq-page p a:active,.faq-page p a:focus,.faq-page p a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.faq-page li a:active,.faq-page li a:focus,.faq-page li a:hover,.faq-page p a:active,.faq-page p a:focus,.faq-page p a:hover{text-decoration:underline}.content-find-shakes .header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));padding:1.5rem 1.875rem 1rem;width:100%}.content-find-shakes .header .avatar{display:flex}.content-find-shakes .header .avatar img{display:block;height:50px;width:50px}.content-find-shakes .header .avatar-media{flex:none}.content-find-shakes .header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.content-find-shakes .header h2{font-size:2.25rem}}@media screen and (min-width:768px){.content-find-shakes .header h2{font-size:2.625rem}}.content-find-shakes .body{padding:var(--size-spacing-double)}.content-find-shakes .good-folk-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-half-again)}.content-find-shakes .good-folk-block h3{color:var(--color-status-success-pastel-dark);font-size:1.125rem;padding-left:var(--size-spacing-half)}.content-find-shakes .good-folk-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);color:var(--color-page-text);margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.content-find-shakes .good-folk-block-content p{font-size:.875rem;margin-top:0}.content-find-shakes .good-folk-block-content .user-follow{margin-top:var(--size-spacing-double)}.content-find-shakes .find-shakes-navigation{display:flex;list-style:none;margin:0;padding:0}.content-find-shakes .find-shakes-navigation li{background:var(--color-background-content-secondary);flex:1;font-size:1.125rem}.content-find-shakes .find-shakes-navigation li a{display:block;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-double) var(--size-spacing-half);text-align:center;text-decoration:none}.content-find-shakes .find-shakes-navigation .selected a{background-color:var(--color-background-content);color:var(--color-page-text)}.content-find-shakes .featured-shakes{margin-bottom:var(--size-spacing-double)}.content-find-shakes .featured-shakes h3{font-size:1.125rem;margin:var(--size-spacing-double) var(--size-spacing-default)}.content-find-shakes .featured-shakes ul{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.content-find-shakes .featured-shakes li{border:1px solid var(--color-border-default);box-shadow:1px 2px 1px var(--color-base-black-transparent-100);margin-bottom:var(--size-spacing-default);margin-right:var(--size-spacing-default);padding:var(--size-spacing-default);width:calc(172px + var(--size-spacing-default)*2)}.content-find-shakes .featured-shakes li img{height:170px;width:170px}.content-find-shakes .featured-shakes li h4{font-size:1.125rem;line-height:1.2;margin:var(--size-spacing-default) 0 0}.content-find-shakes .featured-shakes li h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.content-find-shakes .featured-shakes li h4 a:active,.content-find-shakes .featured-shakes li h4 a:focus,.content-find-shakes .featured-shakes li h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.content-find-shakes .featured-shakes li h4 a:active,.content-find-shakes .featured-shakes li h4 a:focus,.content-find-shakes .featured-shakes li h4 a:hover{text-decoration:underline}.content-find-shakes .featured-shakes li p{-webkit-box-orient:vertical;-webkit-line-clamp:3;display:-webkit-box;font-size:.75rem;margin:var(--size-spacing-default) 0 0;overflow:hidden}.content-find-shakes .shake-category{border-bottom:1px dashed var(--color-border-default);clear:both}.content-find-shakes .shake-category .category-title{font-size:1.125rem}.content-find-shakes .shake-category .category-title a{display:block;padding:var(--size-spacing-half-again) var(--size-spacing-default);text-decoration:none}.content-find-shakes .shake-category .shake-category-body{display:none;padding:0 var(--size-spacing-default)}.content-find-shakes .shake-category.shake-category-selected .category-title a{color:var(--color-page-text-secondary)}.content-find-shakes .shake-category.shake-category-selected .shake-category-body{display:block}.content-find-shakes .shake-tips{background:var(--color-background-content-secondary);clear:both;color:var(--color-page-text);font-size:.75rem;margin:var(--size-spacing-triple) 0;padding:var(--size-spacing-half-again)}.content-find-shakes .friend+.friend{border-top:1px dashed var(--color-border-default)}.content-find-shakes .user-follow-extended .website a{color:var(--color-text-link)}.content-find-shakes .user-follow-extended .website a:active,.content-find-shakes .user-follow-extended .website a:focus,.content-find-shakes .user-follow-extended .website a:hover{color:var(--color-text-link-hover)}.content-find-shakes .body .loading{color:var(--color-page-text-secondary);font-size:1.125rem;margin:var(--size-spacing-quadruple) auto;text-align:center}.content-find-shakes .body .loading img{margin-right:var(--size-spacing-double);position:relative;top:var(--size-spacing-double)}.content-find-shakes .message{color:var(--color-page-text-secondary);font-size:1.125rem;margin:var(--size-spacing-quadruple) var(--size-spacing-triple);text-align:center}.content-find-shakes .intro{border-bottom:1px solid var(--color-border-default);font-size:.875rem;padding:var(--size-spacing-double) var(--size-spacing-half)}.content-find-shakes .refresh-friends{margin:var(--size-spacing-triple) 0}.content-incoming .tip-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.content-incoming .tip-block h3{color:var(--color-brand-primary);font-size:1.3rem;margin:0 0 var(--size-spacing-half-again)}.content-incoming .tip-block p{color:var(--color-page-text-secondary);font-size:.875rem;margin:0}.incoming-header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));cursor:pointer;padding:1.5rem 1.875rem 1rem;padding:var(--size-spacing-half-again) var(--size-spacing-double);width:100%}.incoming-header .avatar{display:flex}.incoming-header .avatar img{display:block;height:50px;width:50px}.incoming-header .avatar-media{flex:none}.incoming-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.incoming-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.incoming-header h2{font-size:2.625rem}.incoming-header{align-items:center;display:flex;flex-direction:row}}.incoming-header:before{background:svg-load("illustrations/incoming-header.svg") 50% no-repeat;background-size:100%;content:"";display:block;margin:auto;max-width:100%;padding-top:29%}@media screen and (min-width:480px){.incoming-header:before{margin:0;padding-top:115px;width:395px}}.incoming-header h2{font-size:1.5rem;line-height:1.2;margin-top:var(--size-spacing-half-again);padding-left:0;text-align:center}@media screen and (min-width:768px){.incoming-header h2{font-size:1.75rem;margin-top:0;padding-left:var(--size-spacing-default)}}.code-of-conduct h2{font-size:1.875rem;text-align:center}.code-of-conduct h3{font-size:1.5rem;margin-top:1.4em}.code-of-conduct p{font-size:1rem;line-height:1.4;margin:1em 0}.code-of-conduct li{margin-bottom:.4em}.terms-of-use p{font-size:1rem;line-height:1.4;margin:1em 0}.terms-of-use li{font-size:1rem;list-style-type:lower-alpha;margin-left:var(--size-spacing-quadruple)}.terms-of-use .terms-center{text-align:center}.tou-notice-page p{font-size:1rem;line-height:1.4;margin:1em 0}.content-membership{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-membership{padding:var(--size-spacing-quadruple)}}.content-membership h1{line-height:1;text-shadow:2px 4px 1px var(--color-base-black-transparent-100)}.content-membership ul{padding-left:1em}.content-membership li+li{margin-top:1em}.content-membership .fine-print{font-size:.75rem}.content-membership .subscribe-plan-quantity-wrapper{color:var(--color-page-text-secondary);font-size:2rem}.content-membership .input-plan-quantity{display:inline-block;font-size:inherit;font-weight:var(--number-font-weight-bold);max-width:150px;width:auto}.content-membership .input-plan-quantity:invalid{color:var(--color-status-danger)}.content-membership .subscribe-plan-quantity-wrapper i{color:var(--color-page-text-secondary);font-size:1.5rem;font-style:normal}.content-migrate{background-image:svg-load("illustrations/burger-upgrade.svg");background-position:bottom var(--size-spacing-half-again) center;background-repeat:no-repeat;background-size:366px 481px;padding-bottom:530px}@media screen and (min-width:768px){.content-migrate{background-position:var(--size-spacing-quadruple) var(--size-spacing-quadruple);min-height:calc(481px + var(--size-spacing-quadruple));padding-bottom:0;padding-left:430px}}.content-migrate p{font-size:1.25rem;margin:var(--size-spacing-triple) 0}.content-membership-thanks{background-image:svg-load("illustrations/thankyou-dude.svg");background-position:bottom var(--size-spacing-half-again) center;background-repeat:no-repeat;background-size:312px 410px;padding-bottom:450px}@media screen and (min-width:768px){.content-membership-thanks{background-position:var(--size-spacing-quadruple) var(--size-spacing-quadruple);min-height:calc(410px + var(--size-spacing-quadruple));padding-bottom:0;padding-left:380px}}.content-membership-thanks a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.content-membership-thanks a:active,.content-membership-thanks a:focus,.content-membership-thanks a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.content-membership-thanks a:active,.content-membership-thanks a:focus,.content-membership-thanks a:hover{text-decoration:underline}.membership-header{margin-bottom:var(--size-spacing-double)}@media screen and (min-width:480px){.membership-header{display:flex}}@media screen and (min-width:768px){.membership-header{margin-bottom:var(--size-spacing-quadruple)}}.membership-header h2{font-size:2rem;line-height:1.1;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-double)}.membership-header p{font-size:1.2rem;margin:0}.membership-header--illustration{display:none}@media screen and (min-width:480px){.membership-header--illustration{display:block;flex:none;margin-right:var(--size-spacing-half-again);width:75px}}@media screen and (min-width:768px){.membership-header--illustration{margin-right:var(--size-spacing-quadruple);width:140px}}@media screen and (min-width:480px){.membership-header--content{flex:1}}.membership-options>*{margin:var(--size-spacing-double) 0}@media screen and (min-width:768px){.membership-options{display:flex}.membership-options>*{margin:0}.membership-options>*+*{margin-left:var(--size-spacing-quadruple)}}@media screen and (min-width:768px){.membership-options--plan{flex:1}}.membership-options--plan .label{margin-bottom:var(--size-spacing-half)}.membership-options--plan .btn{text-transform:uppercase}.membership-options--plan h3{font-size:2rem}.membership-options--plan p{margin-top:.25em}.membership-options--plan ul{margin-bottom:2em}@media screen and (min-width:768px){.is-active-member .membership-options--plan{padding-top:var(--size-spacing-quadruple)}.is-active-member .membership-options--plan.is-active-plan{padding-top:0}}.membership-options--plan-title{color:var(--color-brand-primary);text-transform:uppercase}.membership-options--plan-tagline{font-size:1.25rem}.membership-options--or{align-items:center;display:flex;justify-content:center}@media screen and (min-width:768px){.membership-options--or{flex-direction:column}}.membership-options--or:after,.membership-options--or:before{background:var(--color-brand-primary);content:"";flex:1;height:1px}@media screen and (min-width:768px){.membership-options--or:after,.membership-options--or:before{height:auto;width:1px}}@media screen and (min-width:768px){.membership-options--or:before{flex-basis:var(--size-spacing-triple);flex-grow:0}}.membership-options--or-bullet{background-color:var(--color-brand-primary);border-radius:50%;color:var(--color-text-light-emphasis);display:flex;font-size:1.5rem;height:3em;margin-left:auto;margin-right:auto;width:3em}.membership-options--or-bullet span{margin:auto}.membership-footer{margin-top:var(--size-spacing-half-again);text-align:center}@media screen and (min-width:768px){.membership-footer{margin-top:var(--size-spacing-quadruple)}}.membership-footer p{margin:0}.content-permalink{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-permalink{padding:var(--size-spacing-triple)}}.content-permalink>*{margin:0!important;padding:0!important}.content-permalink>*+*{margin-top:var(--size-spacing-half-again)!important}@media screen and (min-width:768px){.content-permalink>*+*{margin-top:var(--size-spacing-triple)!important}}@media screen and (min-width:768px){.content-permalink>.permalink-sidebar{float:right;width:330px}}@media screen and (min-width:768px){.content-permalink>.image-comment-form,.content-permalink>.image-comments,.content-permalink>.image-content{float:left;width:calc(100% - 360px)}}@supports (display:grid){@media screen and (min-width:768px){.content-permalink{grid-gap:var(--size-spacing-triple);display:grid;grid-template-areas:"title title" "image sidebar" "comments sidebar" "post-comment sidebar";grid-template-columns:1fr 330px;grid-template-rows:repeat(3,min-content) 1fr}.content-permalink>*{margin:0!important}.content-permalink:after{content:none}.content-permalink>.image-comment-form,.content-permalink>.image-comments,.content-permalink>.image-content,.content-permalink>.permalink-sidebar{width:auto}.content-permalink>.image-title{grid-area:title}.content-permalink>.image-content{grid-area:image}.content-permalink>.permalink-sidebar{grid-area:sidebar}.content-permalink>.image-comments{grid-area:comments}.content-permalink>.image-comment-form{grid-area:post-comment}}}.permalink-sidebar{position:relative}.tools-page{background-color:var(--color-background-page)}.tools-page a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.tools-page a:active,.tools-page a:focus,.tools-page a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.tools-page .header{color:var(--color-page-text-secondary);font-size:.75rem;height:55px;margin:0 var(--size-spacing-double);padding-top:var(--size-spacing-half);position:relative;text-align:right}.tools-page .header a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.tools-page .header a:active,.tools-page .header a:focus,.tools-page .header a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.tools-page .header a:active,.tools-page .header a:focus,.tools-page .header a:hover{text-decoration:underline}.tools-page .header img{height:49px;left:0;position:absolute;top:3px;width:100px}.tools-page .content{background:var(--color-background-content);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.tools-page .content-inner{display:flex;flex-direction:column;margin:0 auto}@media screen and (min-width:768px){.tools-page .content-inner{flex-direction:row;justify-content:space-between}}.tools-page .content-inner>*{min-width:0}@media screen and (min-width:768px){.tools-page .content-inner>*{flex-basis:calc(50% - 10px)}}.tools-page .content-inner>*+*{margin-top:20px}@media screen and (min-width:768px){.tools-page .content-inner>*+*{margin-top:0}}@media screen and (min-width:768px){.tools-page .content-inner .full-width{flex:1}}.tools-page .tools-fun-form{margin-top:0}.tools-page .tools-fun-form .tools-field-title .input-text{font-weight:var(--number-font-weight-bold)}.tools-page .tools-fun-form .field+.field{margin-top:0}.tools-page .tools-fun-form .field-textarea{height:0;overflow:hidden}.tools-page .tools-fun-form .field-textarea.field-textarea--selected{height:auto}.tools-page .tools-fun-form .textarea-navigation ul{display:flex;list-style:none;margin:0;padding:var(--size-spacing-half-again);padding-bottom:0}@media screen and (min-width:480px){.tools-page .tools-fun-form .textarea-navigation ul{padding-left:var(--size-spacing-double)}}@media screen and (min-width:480px){.tools-page .tools-fun-form .textarea-navigation li+li{margin-left:var(--size-spacing-half-again)}}.tools-page .tools-fun-form .textarea-navigation .tab{border-top-left-radius:var(--size-border-radius-default);border-top-right-radius:var(--size-border-radius-default);cursor:pointer;display:block;font-size:.875rem;padding:var(--size-spacing-default)}@media (hover){.tools-page .tools-fun-form .textarea-navigation .tab:hover{background-color:var(--color-form-bg)}}.tools-page .tools-fun-form .textarea-navigation .selected .tab{background-color:var(--color-border-form);font-weight:700}.tools-page .tools-saved-it{background:svg-load("illustrations/tools-saved-it.svg") 0 0 no-repeat;background-size:contain;font-size:1.75rem;padding-top:calc(41.5625% + var(--size-spacing-double))}@media screen and (min-width:768px){.tools-page .tools-saved-it{background-size:320px 133px;padding-top:160px}}.tools-page .content-sign-in{margin-top:var(--size-spacing-double)}.tools-page .content-sign-in h1{color:var(--color-brand-secondary);font-size:1.5rem;padding:var(--size-spacing-triple)}.tools-page .tools-signin-logo{margin-top:var(--size-spacing-default);text-align:center}.tools-page .sign-in-fun-form{margin-top:0;padding-bottom:var(--size-spacing-double)}@media screen and (min-width:768px){.tools-page .sign-in-fun-form label{flex-basis:120px}.tools-page .sign-in-fun-form .field-submit{padding-left:130px}}.tools-page .email-unverified h2,.tools-page .over-upload-limit h2{color:var(--color-brand-primary);font-size:1.5rem;text-align:center}.content-relationships .body .friend,.content-relationships .body .shake{border-bottom:1px dashed var(--color-border-default);padding:0 var(--size-spacing-quadruple)}.content-relationships .body .user-follow-extended .website a{color:var(--color-brand-secondary)}.content-relationships .user-info .avatar{float:left;margin-right:var(--size-spacing-half-again)}.content-relationships .user-info .avatar--img{height:var(--size-avatar-large);width:var(--size-avatar-large)}.content-relationships .user-info .details h3{color:var(--color-page-text);font-size:1.125rem;font-weight:var(--number-font-weight-bold);padding-top:var(--size-spacing-half)}.content-search{list-style:none}.content-search .search-empty-results{margin:var(--size-spacing-double)}.sidebar-search-block{margin-bottom:var(--size-spacing-double)}.sidebar-search-block .field-help{padding-left:0}.settings-header{align-items:center;background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));display:flex;flex-direction:column;justify-content:space-between;padding:1.5rem 1.875rem 1rem;width:100%}.settings-header .avatar{display:flex}.settings-header .avatar img{display:block;height:50px;width:50px}.settings-header .avatar-media{flex:none}.settings-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.settings-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.settings-header h2{font-size:2.625rem}.settings-header{flex-direction:row}}@media screen and (min-width:768px){.settings-header>*{flex:1;max-width:calc(50% - var(--size-spacing-double))}}.settings-header h1{font-size:3rem;margin-bottom:var(--size-spacing-half-again)}@media screen and (min-width:768px){.settings-header h1{margin-bottom:0}}.settings-navigation ul{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);display:flex;list-style:none;margin:0;padding:var(--size-spacing-half-again);padding-bottom:0}@media screen and (min-width:480px){.settings-navigation ul{padding-left:var(--size-spacing-double)}}@media screen and (min-width:480px){.settings-navigation li+li{margin-left:var(--size-spacing-half-again)}}.settings-navigation a{border-top-left-radius:var(--size-border-radius-default);border-top-right-radius:var(--size-border-radius-default);color:var(--color-text-link-primary);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default);padding-bottom:var(--size-spacing-half-again);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.settings-navigation a:active,.settings-navigation a:focus,.settings-navigation a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.settings-navigation a:active,.settings-navigation a:focus,.settings-navigation a:hover{text-decoration:underline}.settings-navigation .selected a{background-color:var(--color-background-content)}.settings-body{display:flex;flex-direction:column;justify-content:space-between;padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.settings-body{flex-direction:row;padding:var(--size-spacing-quadruple)}}.settings-body>*{margin:0}@media screen and (min-width:768px){.settings-body>*{flex:1;max-width:calc(50% - var(--size-spacing-double))}}.settings-body>*+*{margin-top:var(--size-spacing-triple)}@media screen and (min-width:768px){.settings-body>*+*{margin-top:0}}.settings-subscription-sidebar h4{border-bottom:1px dashed var(--color-border-default);font-size:1.125rem;margin-bottom:var(--size-spacing-default);padding-bottom:.25em}.settings-subscription-sidebar .member-status-block,.settings-subscription-sidebar .migration-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-half-again)}.settings-subscription-sidebar .member-status-block h3,.settings-subscription-sidebar .migration-block h3{color:var(--color-status-success-pastel-dark)}.settings-subscription-sidebar .member-status-block:first-child,.settings-subscription-sidebar .migration-block:first-child{margin-top:0}.settings-subscription-sidebar .member-status-block:last-child,.settings-subscription-sidebar .migration-block:last-child{margin-bottom:0}.settings-subscription-sidebar .migration-block{background-color:var(--color-bg-secondary-brand-pastel);margin-bottom:var(--size-spacing-double)}.settings-subscription-sidebar .migration-block h3{color:var(--color-brand-secondary)}.settings-subscription-sidebar .member-status-block-content,.settings-subscription-sidebar .migration-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.settings-subscription-sidebar .transaction-list li{margin-bottom:var(--size-spacing-default)}.settings-subscription-sidebar .transaction-list li .id{color:var(--color-page-text-secondary)}.settings-subscription-sidebar .transaction-list li .amount{font-weight:var(--number-font-weight-bold)}.settings-subscription-sidebar p{margin:var(--size-spacing-double) 0}.settings-body-content .fun-form{margin-top:0}.settings-body-sidebar .profile-photo{display:flex;flex-direction:column}@media screen and (min-width:480px){.settings-body-sidebar .profile-photo{flex-direction:row}}.settings-body-sidebar .profile-photo-media{flex:none}.settings-body-sidebar .profile-photo-media .avatar--img{height:var(--size-avatar-large);width:var(--size-avatar-large)}.settings-body-sidebar .profile-photo-meta{flex:1;padding-top:var(--size-spacing-default)}@media screen and (min-width:480px){.settings-body-sidebar .profile-photo-meta{margin-left:var(--size-spacing-default);padding-top:var(--size-spacing-half)}}.settings-body-sidebar .profile-photo-meta h3{font-size:1.125rem;margin-bottom:var(--size-spacing-double)}.settings-body-sidebar .profile-photo-meta h4{font-size:.875rem;margin-bottom:var(--size-spacing-default)}.settings-body-sidebar .profile-photo-meta .settings-photo-upload{color:var(--color-page-text-secondary);font-size:.75rem}.settings-body-sidebar .info-block{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);margin-top:var(--size-spacing-quadruple);padding:var(--size-spacing-half-again)}@media screen and (min-width:480px){.settings-body-sidebar .info-block{padding:var(--size-spacing-double)}}.settings-body-sidebar .info-block h3{font-size:1rem;margin-bottom:var(--size-spacing-default)}.settings-body-sidebar .info-block p{font-size:.875rem;margin-bottom:0}.settings-body-connections{display:block}.settings-body-connections>*{max-width:none}.settings-body-connections h3{font-size:1.125rem}.settings-body-connections .apps{list-style:none;margin:var(--size-spacing-triple) 0;padding:0}.settings-body-connections .apps li{margin-bottom:var(--size-spacing-double)}.settings-body-connections .apps h4{color:var(--color-page-text-secondary);font-size:1.125rem;margin-bottom:var(--size-spacing-half)}.settings-body-connections .apps h4 .title{color:var(--color-brand-primary)}.settings-body-connections .apps h4 .by{font-size:.875rem}.settings-body-connections .apps h4 .by a{text-decoration:none}.settings-body-connections .apps p{font-size:.875rem;margin-bottom:var(--size-spacing-half)}.settings-body-connections .apps .disconnect{font-size:.75rem;text-decoration:none}#sign-in-form .field-submit{text-align:right}.forgot-password-field{padding-top:var(--size-spacing-double);text-align:right}.password-manager-icon{float:left;padding-right:var(--size-spacing-double)}.steps{display:none}.steps p{text-align:center} \ No newline at end of file diff --git a/static/js/tools.js b/static/js/tools.js index 259f8de9..7555bdec 100644 --- a/static/js/tools.js +++ b/static/js/tools.js @@ -1,29 +1,29 @@ /* JS for the tools page */ (function($) { - + $.fn.hint = function(text) { - + var default_text = text; var field = this; var original_color = $(this).css('color'); - + var setDefault = function() { if ($(field).val() == '') { $(field).css('color', '#999').val(default_text); } }; - + var resetStyles = function() { $(field).css('color', original_color); }; - + var clearField = function() { $(field).val(''); }; - + setDefault(); - + this.focus(function() { if ($(this).val() == default_text) { resetStyles(); @@ -32,22 +32,35 @@ }).blur(function() { setDefault(); }); - + // on submit, clear out the hint before submit. this.parents('form:first').submit(function() { if ($(field).val() == default_text) { clearField(); } }); - + return this; }; - + + $('.picker-content .textarea-navigation .tab').click(function(e) { + let tabId = e.target.getAttribute('data-tab'); + let tab = document.getElementById(tabId); + if (tab && !$(tab).hasClass('field-textarea--selected')) { + $('.picker-content .textarea-navigation li').removeClass('selected'); + $(e.target).closest('li').addClass('selected'); + $('#description').removeClass('field-textarea--selected'); + $('#alt-text').removeClass('field-textarea--selected'); + $(tab).addClass('field-textarea--selected'); + } + }); + + })(jQuery); $(document).ready(function() { - + $('#description-field').hint('Write a description if you\'d like!'); }); diff --git a/templates/tools/picker.html b/templates/tools/picker.html index 84a81eeb..31d4ac0e 100644 --- a/templates/tools/picker.html +++ b/templates/tools/picker.html @@ -25,7 +25,14 @@

In order to post, you have to confirm your email address first. Check your e

-
+
+
    +
  • Description
  • +
  • Alt Text
  • +
+
+ +
@@ -40,7 +47,7 @@

In order to post, you have to confirm your email address first. Check your e

-
+
From a7cccc8daf9c2ea0641721d02bccc19bec202647 Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sat, 15 Jul 2023 21:07:43 -0500 Subject: [PATCH 08/19] Revert "Picker alt text" (#725) --- handlers/tools.py | 5 +---- static/css/main.min.css | 2 +- static/js/tools.js | 35 +++++++++++------------------------ templates/tools/picker.html | 24 +----------------------- 4 files changed, 14 insertions(+), 52 deletions(-) diff --git a/handlers/tools.py b/handlers/tools.py index 8e260915..2d9616e2 100644 --- a/handlers/tools.py +++ b/handlers/tools.py @@ -20,7 +20,6 @@ class PickerPopupHandler(BaseHandler): def get(self): url = self.get_argument('url', None) source_url = self.get_argument('source_url', '') - alt_text = self.get_argument('alt', '') file_name = self.get_argument('title', '') current_user = self.get_current_user_object() @@ -72,7 +71,7 @@ def get(self): #replace plus signs with %20's return self.render("tools/picker.html", file_name=file_name, width="", height="", \ url=parsed_url.scheme + "://" + parsed_url.netloc + parsed_url.path + parsed_url_query, \ - source_url=source_url, description='', alt_text=alt_text, is_video=is_video, shakes=shakes, + source_url=source_url, description='', is_video=is_video, shakes=shakes, can_upload_this_month=can_upload_this_month) @tornado.web.authenticated @@ -103,7 +102,6 @@ def on_response(self, response): title = self.get_argument("title", None) source_url = self.get_argument('source_url', None) description = self.get_argument('description', None) - alt_text = self.get_argument('alt_text', None) shake_id = self.get_argument('shake_id', None) if title == file_name: @@ -137,7 +135,6 @@ def on_response(self, response): shake_id = shake_id) sf.source_url = source_url sf.description = description - sf.alt_text = alt_text sf.save() if not options.debug: # file cleanup diff --git a/static/css/main.min.css b/static/css/main.min.css index a8150b61..0e99a8f4 100644 --- a/static/css/main.min.css +++ b/static/css/main.min.css @@ -5,4 +5,4 @@ * * @see https://github.com/MLTSHP/mltshp-patterns/ */ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{-webkit-text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{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-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}:root{--color-background-content-dark:#263238;--color-background-content-light:#fff;--color-background-page-light:#dbfaff;--color-background-page-dark:#13454c;--color-base-pink:#ff0080;--color-base-pink-dark:#c06;--color-base-pink-pastel-dark:#ff0080;--color-base-pink-pastel-light:#ffcce8;--color-base-blue:#00aeff;--color-base-blue-dark:#008bcc;--color-base-blue-very-light:#f2fdff;--color-base-blue-pastel-dark:#00aeff;--color-base-blue-pastel-medium:#aaf3ff;--color-base-blue-pastel-light:#daf3ff;--color-base-blue-pastel-very-light:#dbfaff;--color-base-blue-gray-light:#e4eff1;--color-base-cyan-very-dark:#13454c;--color-base-green:#09b896;--color-base-green-dark:#079378;--color-base-green-pastel-dark:#0caf8b;--color-base-green-pastel-light:#b9ff8d;--color-base-green-pastel-very-light:#dfffcb;--color-base-orange:#ff9600;--color-base-orange-dark:#cc7800;--color-base-orange-pastel-dark:#ff9600;--color-base-orange-pastel-light:#ffe466;--color-base-red:#f30;--color-base-red-dark:#cc2900;--color-base-red-pastel-dark:#ff1c1c;--color-base-red-pastel-light:#ffd4d4;--color-base-gray:#a1a1a1;--color-base-gray-light:#e9e9e9;--color-base-gray-pastel-dark:#8d8d87;--color-base-gray-pastel-light:#cecec6;--color-base-yellow-pastel-light:#fffbd9;--color-base-yellow-transparent-600:rgba(252,255,0,.66);--color-base-black-transparent:transparent;--color-base-black-transparent-100:rgba(0,0,0,.13);--color-base-black-transparent-200:rgba(0,0,0,.25);--color-base-black-transparent-300:rgba(0,0,0,.35);--color-base-black-transparent-500:rgba(0,0,0,.5);--color-base-black-transparent-600:rgba(0,0,0,.6);--color-base-black-transparent-800:rgba(0,0,0,.8);--color-base-white-transparent:hsla(0,0%,100%,0);--color-base-white-transparent-100:hsla(0,0%,100%,.13);--color-base-white-transparent-200:hsla(0,0%,100%,.25);--color-base-white-transparent-300:hsla(0,0%,100%,.35);--color-base-white-transparent-500:hsla(0,0%,100%,.5);--color-base-white-transparent-600:hsla(0,0%,100%,.65);--color-base-white-transparent-800:hsla(0,0%,100%,.85);--color-base-white:#fff;--color-base-black:#000;--color-base-blue-dark-mode:#263238;--color-base-gray-050:#fafafa;--color-base-gray-100:#f5f5f5;--color-base-gray-200:#eee;--color-base-gray-300:#e0e0e0;--color-base-gray-400:#bdbdbd;--color-base-gray-500:#9e9e9e;--color-base-gray-600:#757575;--color-base-gray-700:#616161;--color-base-gray-800:#424242;--color-base-gray-900:#212121;--color-brand-primary:#ff0080;--color-brand-primary-dark:#c06;--color-brand-primary-pastel-light:#ffcce8;--color-brand-primary-pastel-dark:#ff0080;--color-brand-secondary:#00aeff;--color-brand-secondary-dark:#008bcc;--color-brand-secondary-very-light:#f2fdff;--color-brand-secondary-pastel-light:#daf3ff;--color-brand-secondary-pastel-medium:#aaf3ff;--color-brand-secondary-pastel-dark:#00aeff;--color-status-success:#09b896;--color-status-success-dark:#079378;--color-status-success-pastel-very-light:#dfffcb;--color-status-success-pastel-light:#b9ff8d;--color-status-success-pastel-dark:#0caf8b;--color-status-warning:#ff9600;--color-status-warning-dark:#cc7800;--color-status-warning-pastel-light:#ffe466;--color-status-warning-pastel-dark:#ff9600;--color-status-danger:#f30;--color-status-danger-dark:#cc2900;--color-status-danger-pastel-light:#ffd4d4;--color-status-danger-pastel-dark:#ff1c1c;--color-status-disabled:#a1a1a1;--color-status-disabled-light:#e9e9e9;--color-status-disabled-pastel-light:#cecec6;--color-status-disabled-pastel-dark:#8d8d87;--color-status-edit:rgba(252,255,0,.66);--color-status-highlight:#fffbd9;--color-text-dark:rgba(0,0,0,.8);--color-text-dark-emphasis:#000;--color-text-dark-secondary:rgba(0,0,0,.6);--color-text-light:hsla(0,0%,100%,.85);--color-text-light-emphasis:#fff;--color-text-light-secondary:hsla(0,0%,100%,.65);--color-text-link:#00aeff;--color-text-link-hover:#008bcc;--color-text-link-primary:#ff0080;--color-text-link-primary-hover:#c06;--color-text-link-danger:#f30;--color-text-link-danger-hover:#cc2900;--font-family-system:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--font-family-mono:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;--number-font-weight-light:300;--number-font-weight-normal:400;--number-font-weight-medium:500;--number-font-weight-semibold:600;--number-font-weight-bold:700;--number-font-weight-heavy:800;--size-avatar-tiny:20px;--size-avatar-small:30px;--size-avatar-default:48px;--size-avatar-large:100px;--size-border-radius-default:5px;--size-border-radius-large:10px;--size-breakpoint-xs:320px;--size-breakpoint-sm:375px;--size-breakpoint-md:480px;--size-breakpoint-lg:768px;--size-breakpoint-xl:960px;--size-breakpoint-xxl:1440px;--size-font-base:16px;--size-font-input:16px;--size-spacing-half:5px;--size-spacing-default:10px;--size-spacing-half-again:15px;--size-spacing-double:20px;--size-spacing-triple:30px;--size-spacing-quadruple:40px;--time-speed-instant:0s;--time-speed-immediate:0.1s;--time-speed-quick:0.2s;--time-speed-prompt:0.3s;--time-speed-slow:0.4s;--time-speed-glacial:0.6s}:root{--color-text-link-underline:rgba(0,174,255,.33);--color-text-link-underline-hover:rgba(0,139,204,.33);--color-text-link-primary-underline:rgba(255,0,128,.33);--color-text-link-primary-underline-hover:rgba(204,0,102,.33);--color-text-link-danger-underline:rgba(255,51,0,.33);--color-text-link-danger-underline-hover:rgba(204,41,0,.33)}:root{--color-background-page:var(--color-background-page-light);--color-background-content:var(--color-background-content-light);--color-background-content-secondary:var(--color-base-gray-100);--color-page-text:var(--color-text-dark);--color-page-text-emphasis:var(--color-text-dark-emphasis);--color-page-text-secondary:var(--color-text-dark-secondary);--color-border-default:var(--color-base-gray-300);--color-border-form:var(--color-base-blue-gray-light);--color-border-focus-ring:rgba(0,174,255,.25);--color-form-bg:var(--color-brand-secondary-very-light);--color-form-error-bubble-bg:var(--color-brand-secondary-pastel-medium);--color-form-error-bubble-text:var(--color-page-text);--number-font-weight-bold:bold;--number-font-weight-normal:normal;--letter-spacing:normal;--color-bg-primary-brand-pastel:var(--color-brand-primary-pastel-light);--color-bg-secondary-brand-pastel:var(--color-background-page-light);--color-bg-success-pastel:var(--color-status-success-pastel-very-light);--color-bg-danger-pastel:var(--color-status-danger-pastel-light);--color-bg-warning-pastel:var(--color-status-highlight);--color-background-button-primary:var(--color-brand-primary);--color-background-button-primary-hover:var(--color-brand-primary-dark);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-light);--color-background-button-primary-pastel-hover:var(--color-brand-primary);--color-text-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-text-button-primary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-secondary:var(--color-brand-secondary);--color-background-button-secondary-hover:var(--color-brand-secondary-dark);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-light);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary);--color-text-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-text-button-secondary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-success:var(--color-status-success);--color-background-button-success-hover:var(--color-status-success-dark);--color-background-button-success-pastel:var(--color-status-success-pastel-light);--color-background-button-success-pastel-hover:var(--color-status-success);--color-text-button-success-pastel:var(--color-status-success-pastel-dark);--color-text-button-success-pastel-hover:var(--color-text-light-emphasis);--color-background-button-warning:var(--color-status-warning);--color-background-button-warning-hover:var(--color-status-warning-dark);--color-background-button-warning-pastel:var(--color-status-warning-pastel-light);--color-background-button-warning-pastel-hover:var(--color-status-warning);--color-text-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-text-button-warning-pastel-hover:var(--color-text-light-emphasis);--color-background-button-danger:var(--color-status-danger);--color-background-button-danger-hover:var(--color-status-danger-dark);--color-background-button-danger-pastel:var(--color-status-danger-pastel-light);--color-background-button-danger-pastel-hover:var(--color-status-danger);--color-text-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-text-button-danger-pastel-hover:var(--color-text-light-emphasis);--color-background-choose-shake-link:var(--color-background-content);--color-background-choose-shake-link-hover:var(--color-status-highlight);--color-text-choose-shake-link:var(--color-text-link-primary);--color-text-choose-shake-link-hover:var(--color-text-link-primary-hover)}.t-light{--color-background-page:var(--color-background-page-light);--color-background-content:var(--color-background-content-light);--color-background-content-secondary:var(--color-base-gray-100);--color-page-text:var(--color-text-dark);--color-page-text-emphasis:var(--color-text-dark-emphasis);--color-page-text-secondary:var(--color-text-dark-secondary);--color-border-default:var(--color-base-gray-300);--color-border-form:var(--color-base-blue-gray-light);--color-border-focus-ring:rgba(0,174,255,.25);--color-form-bg:var(--color-brand-secondary-very-light);--color-form-error-bubble-bg:var(--color-brand-secondary-pastel-medium);--color-form-error-bubble-text:var(--color-page-text);--number-font-weight-bold:bold;--number-font-weight-normal:normal;--letter-spacing:normal;--color-bg-primary-brand-pastel:var(--color-brand-primary-pastel-light);--color-bg-secondary-brand-pastel:var(--color-background-page-light);--color-bg-success-pastel:var(--color-status-success-pastel-very-light);--color-bg-danger-pastel:var(--color-status-danger-pastel-light);--color-bg-warning-pastel:var(--color-status-highlight);--color-background-button-primary:var(--color-brand-primary);--color-background-button-primary-hover:var(--color-brand-primary-dark);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-light);--color-background-button-primary-pastel-hover:var(--color-brand-primary);--color-text-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-text-button-primary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-secondary:var(--color-brand-secondary);--color-background-button-secondary-hover:var(--color-brand-secondary-dark);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-light);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary);--color-text-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-text-button-secondary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-success:var(--color-status-success);--color-background-button-success-hover:var(--color-status-success-dark);--color-background-button-success-pastel:var(--color-status-success-pastel-light);--color-background-button-success-pastel-hover:var(--color-status-success);--color-text-button-success-pastel:var(--color-status-success-pastel-dark);--color-text-button-success-pastel-hover:var(--color-text-light-emphasis);--color-background-button-warning:var(--color-status-warning);--color-background-button-warning-hover:var(--color-status-warning-dark);--color-background-button-warning-pastel:var(--color-status-warning-pastel-light);--color-background-button-warning-pastel-hover:var(--color-status-warning);--color-text-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-text-button-warning-pastel-hover:var(--color-text-light-emphasis);--color-background-button-danger:var(--color-status-danger);--color-background-button-danger-hover:var(--color-status-danger-dark);--color-background-button-danger-pastel:var(--color-status-danger-pastel-light);--color-background-button-danger-pastel-hover:var(--color-status-danger);--color-text-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-text-button-danger-pastel-hover:var(--color-text-light-emphasis);--color-background-choose-shake-link:var(--color-background-content);--color-background-choose-shake-link-hover:var(--color-status-highlight);--color-text-choose-shake-link:var(--color-text-link-primary);--color-text-choose-shake-link-hover:var(--color-text-link-primary-hover)}@media screen and (prefers-color-scheme:dark){:root{--color-background-page:var(--color-background-page-dark);--color-background-content:var(--color-background-content-dark);--color-background-content-secondary:var(--color-base-gray-900);--color-page-text:var(--color-text-light);--color-page-text-emphasis:var(--color-text-light-emphasis);--color-page-text-secondary:var(--color-text-light-secondary);--color-border-default:var(--color-base-gray-700);--color-border-form:var(--color-base-gray-800);--color-border-focus-ring:rgba(0,174,255,.4);--color-form-bg:var(--color-background-content-secondary);--color-form-error-bubble-bg:var(--color-base-gray-800);--color-form-error-bubble-text:var(--color-status-danger);--number-font-weight-bold:600;--number-font-weight-normal:300;--letter-spacing:0.025em;--color-bg-primary-brand-pastel:rgba(255,0,128,.1);--color-bg-secondary-brand-pastel:rgba(0,174,255,.1);--color-bg-success-pastel:rgba(9,184,150,.1);--color-bg-danger-pastel:rgba(255,51,0,.1);--color-bg-warning-pastel:rgba(255,150,0,.1);--color-background-button-primary:var(--color-brand-primary-dark);--color-background-button-primary-hover:var(--color-brand-primary);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-background-button-primary-pastel-hover:var(--color-brand-primary-pastel-light);--color-text-button-primary-pastel:var(--color-text-light-emphasis);--color-text-button-primary-pastel-hover:var(--color-brand-primary-pastel-dark);--color-background-button-secondary:var(--color-brand-secondary-dark);--color-background-button-secondary-hover:var(--color-brand-secondary);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-light);--color-text-button-secondary-pastel:var(--color-text-light-emphasis);--color-text-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-dark);--color-background-button-success:var(--color-status-success-dark);--color-background-button-success-hover:var(--color-status-success);--color-background-button-success-pastel:var(--color-status-success-pastel-dark);--color-background-button-success-pastel-hover:var(--color-status-success-pastel-light);--color-text-button-success-pastel:var(--color-text-light-emphasis);--color-text-button-success-pastel-hover:var(--color-status-success-pastel-dark);--color-background-button-warning:var(--color-status-warning-dark);--color-background-button-warning-hover:var(--color-status-warning);--color-background-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-background-button-warning-pastel-hover:var(--color-status-warning-pastel-light);--color-text-button-warning-pastel:var(--color-text-light-emphasis);--color-text-button-warning-pastel-hover:var(--color-status-warning-pastel-dark);--color-background-button-danger:var(--color-status-danger-dark);--color-background-button-danger-hover:var(--color-status-danger);--color-background-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-background-button-danger-pastel-hover:var(--color-status-danger-pastel-light);--color-text-button-danger-pastel:var(--color-text-light-emphasis);--color-text-button-danger-pastel-hover:var(--color-status-danger-pastel-dark);--color-background-choose-shake-link:var(--color-background-button-success);--color-background-choose-shake-link-hover:var(--color-background-button-success-pastel-hover);--color-text-choose-shake-link:var(--color-text-light-emphasis);--color-text-choose-shake-link-hover:var(--color-text-button-success-pastel-hover)}}.t-dark{--color-background-page:var(--color-background-page-dark);--color-background-content:var(--color-background-content-dark);--color-background-content-secondary:var(--color-base-gray-900);--color-page-text:var(--color-text-light);--color-page-text-emphasis:var(--color-text-light-emphasis);--color-page-text-secondary:var(--color-text-light-secondary);--color-border-default:var(--color-base-gray-700);--color-border-form:var(--color-base-gray-800);--color-border-focus-ring:rgba(0,174,255,.4);--color-form-bg:var(--color-background-content-secondary);--color-form-error-bubble-bg:var(--color-base-gray-800);--color-form-error-bubble-text:var(--color-status-danger);--number-font-weight-bold:600;--number-font-weight-normal:300;--letter-spacing:0.025em;--color-bg-primary-brand-pastel:rgba(255,0,128,.1);--color-bg-secondary-brand-pastel:rgba(0,174,255,.1);--color-bg-success-pastel:rgba(9,184,150,.1);--color-bg-danger-pastel:rgba(255,51,0,.1);--color-bg-warning-pastel:rgba(255,150,0,.1);--color-background-button-primary:var(--color-brand-primary-dark);--color-background-button-primary-hover:var(--color-brand-primary);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-background-button-primary-pastel-hover:var(--color-brand-primary-pastel-light);--color-text-button-primary-pastel:var(--color-text-light-emphasis);--color-text-button-primary-pastel-hover:var(--color-brand-primary-pastel-dark);--color-background-button-secondary:var(--color-brand-secondary-dark);--color-background-button-secondary-hover:var(--color-brand-secondary);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-light);--color-text-button-secondary-pastel:var(--color-text-light-emphasis);--color-text-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-dark);--color-background-button-success:var(--color-status-success-dark);--color-background-button-success-hover:var(--color-status-success);--color-background-button-success-pastel:var(--color-status-success-pastel-dark);--color-background-button-success-pastel-hover:var(--color-status-success-pastel-light);--color-text-button-success-pastel:var(--color-text-light-emphasis);--color-text-button-success-pastel-hover:var(--color-status-success-pastel-dark);--color-background-button-warning:var(--color-status-warning-dark);--color-background-button-warning-hover:var(--color-status-warning);--color-background-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-background-button-warning-pastel-hover:var(--color-status-warning-pastel-light);--color-text-button-warning-pastel:var(--color-text-light-emphasis);--color-text-button-warning-pastel-hover:var(--color-status-warning-pastel-dark);--color-background-button-danger:var(--color-status-danger-dark);--color-background-button-danger-hover:var(--color-status-danger);--color-background-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-background-button-danger-pastel-hover:var(--color-status-danger-pastel-light);--color-text-button-danger-pastel:var(--color-text-light-emphasis);--color-text-button-danger-pastel-hover:var(--color-status-danger-pastel-dark);--color-background-choose-shake-link:var(--color-background-button-success);--color-background-choose-shake-link-hover:var(--color-background-button-success-pastel-hover);--color-text-choose-shake-link:var(--color-text-light-emphasis);--color-text-choose-shake-link-hover:var(--color-text-button-success-pastel-hover);background-color:var(--color-background-page);color:var(--color-page-text)}*,:after,:before{box-sizing:border-box}body{background-color:var(--color-background-page);color:var(--color-page-text)}:focus{box-shadow:0 0 0 .25rem var(--color-border-focus-ring);outline:0}a{color:var(--color-text-link);text-decoration-color:var(--color-text-link-underline);transition:background-color var(--time-speed-quick) ease,color var(--time-speed-quick) ease}@media screen and (prefers-reduced-motion:reduce){a{transition:none}}a:active,a:focus,a:hover{color:var(--color-text-link-hover);text-decoration-color:var(--color-text-link-underline-hover)}.link--primary{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.link--primary:active,.link--primary:focus,.link--primary:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.link--primary:active,.link--primary:focus,.link--primary:hover{text-decoration:underline}.link--danger{color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.link--danger:active,.link--danger:focus,.link--danger:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}img,video{height:auto}iframe,img,video{max-width:100%}.data-wrapper,[style*="--aspect-ratio"]{position:relative}.data-wrapper:before,[style*="--aspect-ratio"]:before{content:"";display:block;padding-bottom:calc(100%*var(--aspect-ratio))}.data-wrapper>:first-child,[style*="--aspect-ratio"]>:first-child{height:100%;left:0;position:absolute;top:0;width:100%}.data-wrapper:before{padding-bottom:56.25%}html{font-family:var(--font-family-system);font-size:var(--size-font-base);font-weight:var(--number-font-weight-normal);letter-spacing:var(--letter-spacing);line-height:1.3}h1,h2,h3,h4,h5,h6{line-height:1.15;margin:0}.u-display,h1{font-size:3rem;letter-spacing:-1px}@media screen and (min-width:768px){.u-display,h1{font-size:3.75rem}}b,h1,h2,h3,h4,h5,h6,label,legend,strong,th{font-weight:var(--number-font-weight-bold)}.code,code,pre{font-family:var(--font-family-mono)}pre{overflow-x:auto;width:100%}.sr-only{clip:rect(0 0 0 0);border:0;clip-path:polygon(0 0,0 0,0 0);-webkit-clip-path:polygon(0 0,0 0,0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.clear{clear:both}@media screen and (max-width:480px){.hide-on-small{display:none}}.danger,.error{color:var(--color-status-danger)}.user-name{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.avatar--img{display:block;height:var(--size-avatar-default);max-width:none;object-fit:cover;width:var(--size-avatar-default)}.caret{border:.5em solid transparent;border-top-color:currentcolor;border-width:.5em .433em 0;display:inline-block;position:relative;top:-.1em;transform-origin:center 40%;transition:transform .25s ease}@media screen and (prefers-reduced-motion:reduce){.caret{transition:none}}.is-active .caret{transform:rotate(180deg)}.panel--example{margin:.25em 0 1em}.page{background-color:var(--color-background-page);padding:0 .5em;position:relative}.wrapper{display:flow-root;margin:0 auto;max-width:960px}.content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-triple);margin-top:var(--size-spacing-half-again)}.content:after{clear:both;content:"";display:table}.content-narrow,.content-styleguide{margin:var(--size-spacing-quadruple) auto var(--size-spacing-triple);max-width:700px;padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-narrow,.content-styleguide{padding:var(--size-spacing-quadruple)}}.content-narrow p,.content-styleguide p{font-size:1.625rem;line-height:2.5;margin:2em 0}.content-narrow .extra-info p,.content-styleguide .extra-info p{color:var(--color-page-text-secondary);font-size:1rem;line-height:1.4}.content-styleguide{max-width:none}.content-with-sidebar{display:flex;flex-direction:column}@media screen and (min-width:768px){.content-with-sidebar{flex-flow:row wrap}}.content-with-sidebar>*{min-width:0}.content-with-sidebar .header{flex:0 0 100%}.content-with-sidebar .body{flex:1;padding-right:1px}.content-with-sidebar .sidebar{background:linear-gradient(to top,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));order:2;padding:var(--size-spacing-double)}@media screen and (min-width:768px){.content-with-sidebar .sidebar{background:linear-gradient(to right,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));flex:0 0 325px;order:0}.content-with-sidebar-reversed .sidebar{background:linear-gradient(to left,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));order:2}}.alert{background:var(--color-background-content-secondary);font-size:.9rem;line-height:1.25;padding:var(--size-spacing-half-again)}.alert p{margin:0}.alert a{font-weight:var(--number-font-weight-bold)}.alert--marquee{padding:0}.alert--marquee marquee{padding:var(--size-spacing-half-again)}.alert--warning{background:var(--color-bg-warning-pastel);color:var(--color-status-warning)}.alert--danger{background:var(--color-bg-danger-pastel);color:var(--color-status-danger)}.alert--danger a{color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.alert--danger a:active,.alert--danger a:focus,.alert--danger a:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}.migration-reminder{background:var(--color-background-content-secondary);background:var(--color-bg-warning-pastel);color:var(--color-status-warning);font-size:.9rem;font-weight:var(--number-font-weight-bold);line-height:1.25;padding:var(--size-spacing-half-again)}.migration-reminder p{margin:0}.migration-reminder a{font-weight:var(--number-font-weight-bold)}.bookmark{align-items:flex-start;display:flex;flex-wrap:wrap}.bookmark-flag{background:var(--color-brand-primary);color:var(--color-text-light-emphasis);flex:none;font-size:.75rem;font-weight:var(--number-font-weight-bold);margin-top:-3px;position:relative;z-index:2}.bookmark-flag:after,.bookmark-flag:before{content:"";display:block;position:absolute}.bookmark-flag:before{background:inherit;border-radius:var(--size-border-radius-default) 0 0 var(--size-border-radius-default);height:calc(100% + var(--size-spacing-half));left:calc(var(--size-spacing-half)*-1);padding-bottom:var(--size-spacing-half);top:0;width:var(--size-spacing-half)}.bookmark-flag:after{background:var(--color-base-black-transparent-300);border-radius:3px 0 0 3px;bottom:-4px;height:4px;left:-3px;width:3px}.bookmark-flag--content{display:block;line-height:1;padding:.5em 1em}.bookmark-flag--content:after{border-width:1em;border-bottom:1em solid var(--color-brand-primary);border-left:0 solid var(--color-brand-primary);border-right:.866em solid transparent;border-top:1em solid var(--color-brand-primary);content:"";display:block;position:absolute;right:-.833em;top:0}.jump-back{flex:1;font-size:.6875rem;margin-left:.75em;padding:calc(.5em - 3px) 0 .5em .75em;white-space:nowrap}.jump-back a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.jump-back a:active,.jump-back a:focus,.jump-back a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.jump-back a:active,.jump-back a:focus,.jump-back a:hover{text-decoration:underline}button{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.btn,.label{background-color:var(--color-base-gray-700);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);display:inline-block;font-size:18px;font-weight:500;height:2.2em;letter-spacing:.033em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.btn{cursor:pointer}.btn:focus,.btn:hover{color:var(--color-text-light-emphasis)}.btn:focus{outline:none}.btn:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.btn-pastel,.label-pastel{font-weight:600;letter-spacing:.015em;text-shadow:none}.btn-pastel:disabled,.label-pastel:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.btn-shadow{box-shadow:.22em .22em 0 var(--color-base-black-transparent-100);margin-right:.22em}.btn-shadow:disabled{box-shadow:none!important}.btn-padded{height:2.6em;line-height:2.6em;padding:0 1.25em}.btn-large,.label-large{font-size:24px;letter-spacing:.066em}.btn-large.btn-pastel,.label-large.label-pastel{letter-spacing:.033em}.btn-small,.label-small{font-size:12px;font-weight:400}.btn-tiny,.label-tiny{font-size:10px;font-weight:400;padding:0 .66em}.btn-small.btn-pastel,.btn-tiny.btn-pastel,.label-small.label-pastel,.label-tiny.label-pastel{font-weight:500}.btn-icon{padding:0;width:2.2em}.btn-icon.btn-padded{padding:0;width:2.6em}.btn-primary,.label-primary{background-color:var(--color-background-button-primary)}.btn-primary:focus,.btn-primary:hover{background-color:var(--color-background-button-primary-hover)}.btn-primary.btn-pastel,.label-primary.label-pastel{background-color:var(--color-background-button-primary-pastel);color:var(--color-text-button-primary-pastel)}.btn-primary.btn-pastel:focus,.btn-primary.btn-pastel:hover{background-color:var(--color-background-button-primary-pastel-hover);color:var(--color-text-button-primary-pastel-hover)}.btn-secondary,.label-secondary{background-color:var(--color-background-button-secondary)}.btn-secondary:focus,.btn-secondary:hover{background-color:var(--color-background-button-secondary-hover)}.btn-secondary.btn-pastel,.label-secondary.label-pastel{background-color:var(--color-background-button-secondary-pastel);color:var(--color-text-button-secondary-pastel)}.btn-secondary.btn-pastel:focus,.btn-secondary.btn-pastel:hover{background-color:var(--color-background-button-secondary-pastel-hover);color:var(--color-text-button-secondary-pastel-hover)}.btn-success,.label-success{background-color:var(--color-background-button-success)}.btn-success:focus,.btn-success:hover{background-color:var(--color-background-button-success-hover)}.btn-success.btn-pastel,.label-success.label-pastel{background-color:var(--color-background-button-success-pastel);color:var(--color-text-button-success-pastel)}.btn-success.btn-pastel:focus,.btn-success.btn-pastel:hover{background-color:var(--color-background-button-success-pastel-hover);color:var(--color-text-button-success-pastel-hover)}.btn-warning,.label-warning{background-color:var(--color-background-button-warning)}.btn-warning:focus,.btn-warning:hover{background-color:var(--color-background-button-warning-hover)}.btn-warning.btn-pastel,.label-warning.label-pastel{background-color:var(--color-background-button-warning-pastel);color:var(--color-text-button-warning-pastel)}.btn-warning.btn-pastel:focus,.btn-warning.btn-pastel:hover{background-color:var(--color-background-button-warning-pastel-hover);color:var(--color-text-button-warning-pastel-hover)}.btn-danger,.label-danger{background-color:var(--color-background-button-danger)}.btn-danger:focus,.btn-danger:hover{background-color:var(--color-background-button-danger-hover)}.btn-danger.btn-pastel,.label-danger.label-pastel{background-color:var(--color-background-button-danger-pastel);color:var(--color-text-button-danger-pastel)}.btn-danger.btn-pastel:focus,.btn-danger.btn-pastel:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.choose-a-shake{position:relative;text-align:left;z-index:3}.choose-a-shake--toggle{width:100%}.choose-a-shake.is-expanded .choose-a-shake--toggle{border-bottom-left-radius:0;border-bottom-right-radius:0;box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);outline:none}.choose-a-shake.is-expanded .choose-a-shake--toggle:focus,.choose-a-shake.is-expanded .choose-a-shake--toggle:hover{background-color:var(--color-background-button-success-pastel);color:var(--color-text-button-success-pastel)}.choose-a-shake.is-expanded .choose-a-shake--toggle .caret{transform:rotate(180deg)}.choose-a-shake--dropdown{background-color:var(--color-background-button-success-pastel);border-radius:var(--size-border-radius-large);border-top-right-radius:0;box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);display:none;max-width:400px;padding:var(--size-spacing-default);padding-top:1px;position:absolute;right:0;top:calc(100% - .22em)}@media screen and (min-width:768px){.choose-a-shake--dropdown{border-top-left-radius:0;border-top-right-radius:var(--size-border-radius-large);left:0;right:auto}}.choose-a-shake.is-expanded .choose-a-shake--dropdown{display:block}.choose-a-shake--dropdown .add-a-shake{display:block;float:right;margin-top:var(--size-spacing-default)}.choose-a-shake--dropdown ul{list-style:none;margin:0;padding:0}.choose-a-shake--dropdown ul li a{background-color:var(--color-background-choose-shake-link);border-radius:var(--size-border-radius-large);color:var(--color-text-choose-shake-link);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow:hidden;padding:var(--size-spacing-default) var(--size-spacing-half-again);text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.choose-a-shake--dropdown ul li a:active,.choose-a-shake--dropdown ul li a:focus,.choose-a-shake--dropdown ul li a:hover{text-decoration:underline}.choose-a-shake--dropdown ul li a:active,.choose-a-shake--dropdown ul li a:focus,.choose-a-shake--dropdown ul li a:hover{background-color:var(--color-background-choose-shake-link-hover);color:var(--color-text-choose-shake-link-hover);text-decoration:none}.choose-a-shake--dropdown ul.top-shakes li a{margin:var(--size-spacing-default) 0}.choose-a-shake--dropdown ul.group-shakes{background-color:var(--color-background-choose-shake-link);border-radius:var(--size-border-radius-large)}.conversations-nav h3{color:var(--color-page-text);font-size:1.125rem;margin-bottom:var(--size-spacing-half-again);margin-top:var(--size-spacing-triple)}.conversations-nav ul{list-style:none;margin:0;padding:0}.conversations-nav li{margin:var(--size-spacing-half) 0}.conversations-nav li.selected a{color:var(--color-status-disabled)}.conversations-nav a{color:var(--color-brand-primary);font-size:.875rem;font-weight:var(--number-font-weight-bold);text-decoration:none}.conversation{border-bottom:1px dotted var(--color-border-default);display:flex;margin-top:var(--size-spacing-triple)}.mentions .conversation{padding-left:var(--size-spacing-triple);padding-right:var(--size-spacing-default)}.conversation .thumb{flex:none;margin-left:var(--size-spacing-default);width:50px}@media screen and (min-width:480px){.conversation .thumb{margin-left:var(--size-spacing-triple);width:100px}}.conversation .details-wrapper{flex:1;margin-left:var(--size-spacing-default);margin-right:var(--size-spacing-default);min-width:0}.conversation .sharedfile-title{color:var(--color-page-text-emphasis);font-size:1.875rem;margin-bottom:var(--size-spacing-default)}.conversation .sharedfile-description,.conversation .sharedfile-title{word-wrap:break-word;overflow:hidden;overflow-wrap:break-word;word-break:break-word}.conversation .sharedfile-description{font-size:.875rem;line-height:1.3}.conversation .image-comments{padding-left:0}.conversation .image-comments .body{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.conversation .conversation-meta{display:flex;flex-wrap:wrap;margin-bottom:var(--size-spacing-triple);margin-top:var(--size-spacing-half-again)}.conversation .mute-this-conversation,.conversation .post-a-comment{margin:var(--size-spacing-half) 1em 0 0}.conversation .mute-this-conversation-form{display:none}.error-uh-oh{background-image:svg-load("illustrations/tools-uh-oh.svg");background-position:top;background-repeat:no-repeat;background-size:282px 133px;font-size:2.25rem;font-weight:var(--number-font-weight-bold);padding-top:160px;text-align:center}.error-p{color:var(--color-page-text-secondary);text-align:center}.error-p.error-p-long{text-align:left}.content-narrow .error-p{font-size:1.125rem;line-height:1.4}@media screen and (min-width:480px){.feature-list{display:flex;flex-wrap:wrap}}@media screen and (min-width:768px){.feature-list{flex-wrap:nowrap}}@media screen and (min-width:960px){.feature-list{flex-wrap:wrap}}.feature{border-top:1px dashed var(--color-border-default);margin:0;padding:var(--size-spacing-half-again);position:relative}@media screen and (min-width:480px){.feature{border-left:1px dashed var(--color-border-default);flex:1 1 50%}}@media screen and (min-width:768px){.feature{flex-basis:25%;padding:calc(var(--size-spacing-default)*2.5)}}@media screen and (min-width:960px){.feature{display:flex;flex-basis:50%;flex-direction:row-reverse;padding:calc(var(--size-spacing-default)*3.5)}}@media screen and (min-width:768px){.feature:first-child{border-left:none}}@media screen and (min-width:480px) and (max-width:767px){.feature:nth-child(2n-1){border-left:none}}@media screen and (min-width:960px){.feature:nth-child(2n-1){border-left:none}}.feature>*+*{margin-top:var(--size-spacing-half-again)}@media screen and (min-width:768px){.feature>*+*{margin-top:calc(var(--size-spacing-default)*2.5)}}@media screen and (min-width:960px){.feature>*+*{margin-right:var(--size-spacing-double);margin-top:0}}@media screen and (min-width:960px){.feature--content{flex:1 1 auto}.feature--image{flex:0 0 163px}}.feature--image-media{border:1px solid var(--color-border-default);box-shadow:3px 3px 0 var(--color-base-black-transparent-100);display:block;margin:0 auto}.feature--title{color:var(--color-page-text-emphasis);font-size:1.375rem;margin:0;text-align:center}@media screen and (min-width:768px){.feature--title{text-align:left}}.feature--body{color:var(--color-page-text);font-size:.875rem;line-height:1.25;margin:var(--size-spacing-default) 0 0}.feature--body p{margin:0}.feature--body p+p{margin-top:1.25em}.feature--cta{text-align:center}.feature--finale .feature--image-media,.feature--primary .feature--image-media{border:none;box-shadow:none}.feature--primary{border:none}@media screen and (min-width:960px){.feature--primary>*{flex:1}}@media screen and (min-width:960px){.feature--primary>*+*{margin-right:70px}}.feature--primary .feature--title{font-size:2.625rem;letter-spacing:-3px;line-height:90%}@media screen and (min-width:768px){.feature--primary .feature--title{font-size:3.75rem}}@media screen and (min-width:960px){.feature--primary .feature--title{font-size:4.5rem}}.feature--primary .feature--body{color:var(--color-page-text-secondary);margin-top:1.25em}.feature--finale{align-items:center}@media screen and (min-width:960px){.feature--finale{padding-left:150px;padding-right:150px}}.feature--finale .feature--image{flex:none}@media screen and (min-width:960px){.feature--flipped{flex-direction:row}}@media screen and (min-width:960px){.feature--flipped>*+*{margin-left:var(--size-spacing-double);margin-right:0}}@media screen and (min-width:960px){.feature--flipped.feature--primary>*+*{margin-left:70px}}button,input,select,textarea{font-family:var(--font-family-system);font-size:var(--size-font-input)}select{width:100%}label,textarea{display:block}textarea{height:8em;padding:.5em;resize:vertical;width:100%}.fun-form{margin-top:var(--size-spacing-double)}.fun-form .field{display:flex;flex-direction:column;position:relative}@media screen and (min-width:768px){.fun-form .field{align-items:center;flex-flow:row wrap}}.fun-form .field+.field{margin-top:var(--size-spacing-double)}.fun-form label{flex:none;font-size:1.125rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-half)}@media screen and (min-width:768px){.fun-form label{flex-basis:190px;margin-bottom:0;margin-right:var(--size-spacing-default);text-align:right}}.fun-form .field-input{flex:1}.fun-form .field-help,.fun-form .field-submit{display:block}@media screen and (min-width:768px){.fun-form .field-help,.fun-form .field-submit{padding-left:200px}}.fun-form .field-help{color:var(--color-page-text-secondary);font-size:.875rem;margin-top:var(--size-spacing-half)}@media screen and (min-width:768px){.fun-form .field-help{flex-basis:100%}}.fun-form .input-text,.fun-form textarea{appearance:none;background:var(--color-form-bg);border-radius:var(--size-border-radius-large);border-width:1px;border-bottom:1px solid var(--color-border-form);border-left:4px solid var(--color-border-form);border-right:1px solid var(--color-border-form);border-top:4px solid var(--color-border-form);color:var(--color-page-text);display:block;font-size:1.125rem;padding:var(--size-spacing-default) var(--size-spacing-half-again);width:100%}.fun-form [type=checkbox],.fun-form [type=radio]{margin-right:.5em}.fun-form .name-prefix{color:var(--color-page-text-secondary);margin-right:var(--size-spacing-half)}.fun-form .error{background:var(--color-form-error-bubble-bg);border-radius:33% 25px;color:var(--color-form-error-bubble-text);display:block;font-size:.75rem;padding:1em 1.25em;position:absolute;right:calc(var(--size-spacing-half-again)*-1);text-align:center;top:25%;transform:translateY(-50%);width:115px;z-index:1}@media screen and (min-width:960px){.fun-form .error{right:-160px}}.fun-form .error:before{background:var(--color-form-error-bubble-bg);bottom:calc(50% - 15px);content:"";display:block;height:22px;left:-48px;-webkit-mask-image:svg-load("balloons/error-tail.svg");mask-image:svg-load("balloons/error-tail.svg");position:absolute;width:52px}.fun-form-stacked .field{display:block}.fun-form-stacked label{margin-bottom:var(--size-spacing-half);margin-right:0;text-align:left}.fun-form-stacked .field-help,.fun-form-stacked .field-submit{padding-left:0}.fun-form-stacked #create-shake-name-field,.fun-form-stacked .field-prefix{display:flex}.fun-form-stacked #create-shake-name-field label,.fun-form-stacked .field-prefix label{flex-basis:100%}.fun-form-stacked .error{top:3em}.fun-form-errors{color:var(--color-status-danger);font-size:1.125rem}.image-comments{max-width:100%;overflow:hidden;padding-left:var(--size-spacing-quadruple)}.image-comments .comments{clear:both}.image-comments .comment{display:flex}.image-comments .comment+.comment{margin-top:var(--size-spacing-double)}.image-comments .comment .avatar{margin-right:var(--size-spacing-default);width:var(--size-avatar-default)}.image-comments .comment .avatar a{display:block}.image-comments .comment .body{flex:1;font-size:.875rem;line-height:1.3;overflow:hidden}.image-comments .comment .body .where-from{color:var(--color-page-text-secondary);font-size:.825em;padding-top:var(--size-spacing-default)}.image-comments .comment .comment-body-text{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-comments .comment .meta{align-items:flex-end;display:flex;flex-wrap:wrap;font-size:.825em;padding-bottom:var(--size-spacing-default)}.image-comments .comment .meta>*+*{margin-left:.825em}.image-comments .comment .meta .user-name,.image-comments .comment .meta .username{font-size:.875rem;font-weight:var(--number-font-weight-bold)}.image-comments .comment .meta .pro-badge{margin-left:.25em}.image-comments .comment .meta .reply-to{background-image:svg-load("icons/reply.svg");background-size:12px 11px}.image-comments .comment .meta .delete,.image-comments .comment .meta .reply-to{background-position:0 0;background-repeat:no-repeat;display:none;padding-left:var(--size-spacing-half-again)}.image-comments .comment .meta .delete{background-image:svg-load("icons/delete-comment.svg");background-size:13px 13px}.image-comments .comment .meta .created-at{color:var(--color-page-text-secondary)}.image-comments .comment:focus .meta .reply-to,.image-comments .comment:hover .meta .reply-to{display:inline}.image-comments .comment:focus .meta form,.image-comments .comment:hover .meta form{display:none}.image-comments .comment:focus .meta .delete,.image-comments .comment:hover .meta .delete{display:inline}.image-comment-form{margin-top:var(--size-spacing-triple);padding-left:var(--size-spacing-quadruple)}.image-comment-form header{display:flex}.image-comment-form .avatar{flex:none}.image-comment-form h3{background-image:svg-load("balloons/comment-quip-left.svg");background-position:0 0;background-repeat:no-repeat;background-size:auto var(--size-avatar-default);border-bottom-right-radius:15px;border-top-right-radius:25px 20px;display:block;left:calc(var(--size-spacing-default)*-1);position:relative}.image-comment-form h3 span{color:var(--color-text-light-emphasis);display:block;font-size:.75rem;height:var(--size-avatar-default);line-height:var(--size-avatar-default);padding-left:35px;padding-right:var(--size-spacing-double);text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);white-space:nowrap}@media screen and (min-width:375px){.image-comment-form h3 span{font-size:.875rem}}@media screen and (min-width:480px){.image-comment-form h3 span{font-size:1rem}}@media screen and (min-width:768px){.image-comment-form h3 span{font-size:1.125rem}}.image-comment-form .field{clear:both;margin:var(--size-spacing-double) 0;text-align:right}.image-comment-form textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);height:8em;padding:var(--size-spacing-half)}.image-medium{margin-right:var(--size-spacing-half-again);padding-bottom:var(--size-spacing-triple)}.image-medium .user-and-title{padding-top:var(--size-spacing-default)}.image-medium .user-and-title a{display:block;float:left;margin-right:var(--size-spacing-default)}.image-medium .user-and-title .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-medium .user-and-title .title{font-size:1.25rem;font-weight:var(--number-font-weight-bold);overflow:hidden}.image-medium .stats{clear:both;color:var(--color-page-text-secondary);display:flex;list-style:none;margin:0;padding:var(--size-spacing-default) 0 0}.image-medium .stats li{float:left;font-size:.75em;padding-right:var(--size-spacing-default)}.image-medium .stats li.saves{background-image:svg-load("icons/save-tiny.svg");background-position:0 2px;background-repeat:no-repeat;background-size:16px 11px;color:var(--color-status-warning);padding-left:var(--size-spacing-double)}.image-medium .stats li.likes{background-image:svg-load("icons/like.svg");color:var(--color-status-danger)}.image-medium .stats li.comments a,.image-medium .stats li.likes{background-position:0 2px;background-repeat:no-repeat;background-size:12px 9px;padding-left:var(--size-spacing-half-again)}.image-medium .stats li.comments a{background-image:svg-load("icons/comment.svg");color:var(--color-brand-secondary);text-decoration:none}.image-medium-thumb a{display:block}.image-medium-thumb img{text-align:center;vertical-align:middle}.image-title{max-width:100%;padding:var(--size-spacing-half-again) var(--size-spacing-half-again) 0;position:relative}@media screen and (min-width:768px){.image-title{padding:var(--size-spacing-quadruple) var(--size-spacing-quadruple) 0}}.image-title .image-poster{float:left;padding-right:var(--size-spacing-default)}.image-title .image-poster a{display:block}.image-title h1,.image-title h3{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-title h1{font-size:3rem}.image-title h3{font-size:1.875rem;margin-bottom:var(--size-spacing-double)}.image-title .remove-from-shake{height:21px;position:absolute;right:var(--size-spacing-default);top:var(--size-spacing-default);width:21px}@media screen and (min-width:768px){.image-title .remove-from-shake{top:calc(var(--size-spacing-quadruple) + var(--size-spacing-half))}}.image-edit-title-hover,.image-edit-title:focus,.image-edit-title:hover{background-color:var(--color-status-edit)}.image-edit-title-form{display:none;flex-direction:column}@media screen and (min-width:480px){.image-edit-title-form{flex-direction:row}}.image-edit-title-form.is-active{display:flex}.image-edit-title-form .title-input{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);flex:1;font-size:3rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-default);max-width:100%;min-width:0}@media screen and (min-width:480px){.image-edit-title-form .title-input{margin-bottom:0}}.image-content-list .image-edit-title-form .title-input{font-size:1.875rem;min-width:250px;padding:.0666em}.image-edit-title-form .buttons{align-items:center;display:flex;padding-left:var(--size-spacing-default)}.image-edit-title-form .or{color:var(--color-page-text-secondary);display:block;padding:0 var(--size-spacing-default)}.image-content-list .image-edit-title-form{margin-bottom:var(--size-spacing-double)}.image-content-list .image-edit-title-form .btn{font-size:12px;font-weight:400}.image-content{clear:both;max-width:100%;padding:var(--size-spacing-half-again);position:relative}@media screen and (min-width:768px){.image-content{padding:var(--size-spacing-half-again) var(--size-spacing-quadruple) var(--size-spacing-quadruple)}}.image-content .the-image a,.image-content .the-image img{display:block}.image-content img.unsized{height:auto;max-width:100%}.image-content-list .image-content{border-bottom:1px dashed var(--color-border-default);float:none;padding:0 var(--size-spacing-half-again) var(--size-spacing-half-again)}@media screen and (min-width:768px){.image-content-list .image-content{padding:0 var(--size-spacing-quadruple) var(--size-spacing-quadruple)}}.nsfw-cover{background-color:var(--color-base-gray-800);color:var(--color-text-light-emphasis);font-size:.875rem;font-weight:600;padding:var(--size-spacing-quadruple) 0;text-align:center}.nsfw-cover p{margin:var(--size-spacing-double) 0}.image-content .description{color:var(--color-page-text);font-size:.875rem;overflow:hidden;padding-bottom:var(--size-spacing-default)}.image-content .the-description{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-content .the-description.the-description-blank{color:var(--color-status-disabled);font-style:italic}.image-content .the-description a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.image-content .the-description a:active,.image-content .the-description a:focus,.image-content .the-description a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.image-content .description-edit .the-description:focus,.image-content .description-edit .the-description:hover,.image-content .the-description-hover{background-color:var(--color-status-edit)}.image-content .description-edit-form{display:none}.image-content .description-edit-form textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);min-height:100px;padding:var(--size-spacing-half)}.image-content .description-edit-form .buttons{display:flex;padding:var(--size-spacing-default) 0}.image-content .description-edit-form .or{color:var(--color-page-text-secondary);display:block;padding:var(--size-spacing-half) var(--size-spacing-default) var(--size-spacing-half-again) var(--size-spacing-default)}.image-interactions{align-items:center;display:flex;flex-direction:row-reverse;float:right;margin-bottom:4px}.image-interactions .save-this{margin-right:var(--size-spacing-half);position:relative}.image-interactions .like-button,.image-interactions .save-this-link{line-height:1}.image-interactions .like-button:focus,.image-interactions .save-this-link:focus{outline:none}.image-interactions .like-button .btn--content,.image-interactions .save-this-link .btn--content{align-items:center;display:flex}.image-interactions .like-button .btn--icon,.image-interactions .save-this-link .btn--icon{height:1.5em;margin-right:.33em}.image-interactions .like-button .btn--caret,.image-interactions .save-this-link .btn--caret{font-size:.75em;margin-left:.33em;vertical-align:bottom}.image-interactions .like-button,.image-interactions .unlike-button{display:none}.image-interactions .like-button.is-active,.image-interactions .unlike-button.is-active{display:inline-block}.image-interactions .unlike-button{background:none;border:none;cursor:pointer;padding:0}.image-interactions .unlike-button img{display:block}.image-interactions .save-this-shake-selector{background-color:var(--color-status-warning);border-radius:var(--size-border-radius-large);min-height:30px;padding:var(--size-spacing-half) 0;position:absolute;right:0;top:0;width:170px;z-index:1}.image-interactions .save-this-shake-selector ul{clear:both;list-style:none;margin:0;padding:0}.image-interactions .save-this-shake-selector a{word-wrap:break-word;border-radius:var(--size-border-radius-default);color:var(--color-text-light-emphasis);display:block;font-weight:500;margin:0 var(--size-spacing-half);overflow-wrap:break-word;padding:var(--size-spacing-half) var(--size-spacing-half);text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500)}.image-interactions .save-this-shake-selector a:focus,.image-interactions .save-this-shake-selector a:hover{background-color:var(--color-base-white-transparent-200)}.image-interactions .save-this-shake-selector .close{color:var(--color-text-light-emphasis);cursor:pointer;display:block;position:absolute;right:var(--size-spacing-default);top:var(--size-spacing-default);transform:rotate(180deg)}.image-interactions .save-this-shake-selector .close:focus,.image-interactions .save-this-shake-selector .close:hover{opacity:.66}.image-interactions .save-this-shake-selector .close:before{bottom:calc(var(--size-spacing-default)*-1);content:"";left:calc(var(--size-spacing-half-again)*-1);position:absolute;right:calc(var(--size-spacing-half-again)*-1);top:calc(var(--size-spacing-double)*-1)}.image-interactions .save-this-shake-selector-loading{min-height:100px}.image-interactions .save-this-shake-selector-loading:after{animation:load8 1.1s linear infinite;border:5px solid var(--color-base-white-transparent-200);border-left-color:var(--color-base-white-transparent-800);border-radius:50%;content:"";display:block;height:50px;margin:1em auto;transform:translateZ(0);width:50px}@media screen and (prefers-reduced-motion:reduce){.image-interactions .save-this-shake-selector-loading:after{animation:none}}@keyframes load8{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.image-content-footer{color:var(--color-page-text-secondary);font-size:.75rem;padding-top:var(--size-spacing-default)}.image-content-footer .originally-posted-by{clear:left;padding-bottom:var(--size-spacing-half)}.image-content-footer .originally-posted-by .avatar--link{display:inline-block;text-decoration:none;vertical-align:middle}.image-content-footer .originally-posted-by .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-content-footer .inline-meta{display:flex;float:left}.image-content-footer .inline-meta>*{flex:none}.image-content-footer .created-at{padding-top:var(--size-spacing-half)}.image-content-footer .created-at a{color:var(--color-page-text-secondary);text-decoration:none}.image-content-footer .created-at a:focus,.image-content-footer .created-at a:hover{color:var(--color-page-text);text-decoration:underline}.image-content-footer .stats{list-style:none;margin:0 0 0 var(--size-spacing-default);padding:0}.image-content-footer .stats li{float:left;padding-right:var(--size-spacing-half)}.image-content-footer .stats a{background-position:var(--size-spacing-half) .5em;background-repeat:no-repeat;background-size:1em 1em;border-top-left-radius:var(--size-border-radius-default);border-top-right-radius:var(--size-border-radius-default);display:block;float:left;padding:var(--size-spacing-half);padding-bottom:var(--size-spacing-default);padding-left:calc(var(--size-spacing-default) + 1em);text-decoration:none}.image-content-footer .stats a:focus,.image-content-footer .stats a:hover{background-color:var(--color-background-content-secondary)}.image-content-footer .stats .views{padding-top:var(--size-spacing-half)}.image-content-footer .stats .saves a{background-image:svg-load("icons/save-tiny.svg");background-size:1.33em 1em;color:var(--color-status-warning);padding-left:calc(var(--size-spacing-default) + 1.33em)}.image-content-footer .stats .likes a{background-image:svg-load("icons/like.svg");color:var(--color-status-danger)}.image-content-footer .stats .comments a{background-image:svg-load("icons/comment.svg")}.image-content .inline-details,.image-content-footer .stats .selected a{background-color:var(--color-background-content-secondary)}.image-content .inline-details{border-radius:var(--size-border-radius-default);clear:both;min-height:34px;padding:var(--size-spacing-default)}.image-content .inline-details .user-saves-likes{background-color:var(--color-background-content);min-height:var(--size-avatar-tiny);padding:var(--size-spacing-default)}.image-content .inline-details .user-saves-likes a{align-items:center;color:var(--color-text-link-primary);display:inline-flex;font-weight:var(--number-font-weight-bold);margin-right:var(--size-spacing-default);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.image-content .inline-details .user-saves-likes a:active,.image-content .inline-details .user-saves-likes a:focus,.image-content .inline-details .user-saves-likes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.image-content .inline-details .user-saves-likes a:active,.image-content .inline-details .user-saves-likes a:focus,.image-content .inline-details .user-saves-likes a:hover{text-decoration:underline}.image-content .inline-details .user-saves-likes img{margin-right:var(--size-spacing-half)}.image-content .inline-details .user-saves-likes .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-content .inline-details .comment{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);display:flex;margin-bottom:var(--size-spacing-default);padding:var(--size-spacing-default)}.image-content .inline-details .comment .avatar{flex:none}.image-content .inline-details .comment .avatar a{display:block}.image-content .inline-details .comment .avatar--img{height:var(--size-avatar-small);width:var(--size-avatar-small)}.image-content .inline-details .comment .comment-body{flex:1;overflow:hidden;padding-left:var(--size-spacing-default)}.image-content .inline-details .comment .comment-body-text{word-wrap:break-word;clear:both;color:var(--color-page-text);font-size:.875rem;overflow-wrap:break-word;padding-top:.25em;word-break:break-word}.image-content .inline-details .comment .meta{align-items:flex-end;display:flex;flex-wrap:wrap}.image-content .inline-details .comment .meta>*+*{margin-left:.8em}.image-content .inline-details .comment .delete-form{display:none}.image-content .inline-details .comment .meta .username{font-weight:var(--number-font-weight-bold)}.image-content .inline-details .comment .meta .pro-badge{margin-left:.25em}.image-content .inline-details .comment .created-at{margin-left:var(--size-spacing-default);padding-top:0}.image-content .inline-details .comment .reply-to{background-image:svg-load("icons/reply.svg");background-size:12px 11px}.image-content .inline-details .comment .delete,.image-content .inline-details .comment .reply-to{background-position:0 0;background-repeat:no-repeat;display:none;padding-left:var(--size-spacing-half-again)}.image-content .inline-details .comment .delete{background-image:svg-load("icons/delete-comment.svg");background-size:13px 13px;color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.image-content .inline-details .comment .delete:active,.image-content .inline-details .comment .delete:focus,.image-content .inline-details .comment .delete:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}.image-content .inline-details .comment:focus .delete,.image-content .inline-details .comment:focus .reply-to,.image-content .inline-details .comment:hover .delete,.image-content .inline-details .comment:hover .reply-to{display:block}.image-content .inline-details .show-more-comments{display:block;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default) 0;text-align:right;text-decoration:none}.image-content .inline-details .post-comment-inline textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text-disabled);height:2em;padding:var(--size-spacing-half)}.image-content .inline-details .post-comment-inline .button{display:none;padding-top:var(--size-spacing-half)}.image-content .inline-details .post-comment-inline.post-comment-inline-expanded textarea{color:var(--color-page-text);min-height:100px}.image-content .inline-details .post-comment-inline.post-comment-inline-expanded .button{display:block}.new-post-panel{background-color:var(--color-background-content);border-bottom-left-radius:10px;border-bottom-right-radius:10px;box-shadow:var(--size-spacing-default) var(--size-spacing-default) 0 var(--color-base-black-transparent-200);display:none;left:var(--size-spacing-triple);margin:0 auto;max-width:560px;padding:var(--size-spacing-half-again);position:fixed;right:var(--size-spacing-triple);top:calc(var(--size-spacing-default)*-1);z-index:999}@media screen and (min-width:768px){.new-post-panel{padding:calc(var(--size-spacing-default)*5)}}.new-post-panel h2{font-size:1.75rem;line-height:1.2;margin-bottom:var(--size-spacing-default);position:relative}.new-post-panel h2 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.new-post-panel h2 a:active,.new-post-panel h2 a:focus,.new-post-panel h2 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.new-post-panel h2 a:active,.new-post-panel h2 a:focus,.new-post-panel h2 a:hover{text-decoration:underline}.new-post-panel p{font-size:.875rem;line-height:1.2;margin:var(--size-spacing-double) 0;padding:0 var(--size-spacing-half)}.new-post-panel .upload-image-input{cursor:pointer;height:100%;left:0;opacity:0;position:absolute;top:0;width:100%}.new-post-panel .save-video-form{display:flex}.new-post-panel .save-video-form .field-input{flex:1}.new-post-panel .save-video-form .btn{flex:none;margin-left:var(--size-spacing-default)}.new-post-panel .post-video-form{margin-top:var(--size-spacing-double)}.new-post-panel .shake-selector{position:relative}.new-post-panel .shake-selector h3 a{color:var(--color-page-text);display:block;text-decoration:none}.new-post-panel .shake-selector h3 a .green{word-wrap:break-word;color:var(--color-status-success);overflow-wrap:break-word;word-break:break-word}.new-post-panel .shake-selector h3 a:focus .green,.new-post-panel .shake-selector h3 a:hover .green{color:var(--color-status-success-pastel-dark)}.new-post-panel .shake-selector ul{background-color:var(--color-status-success-pastel-light);border-radius:var(--size-border-radius-large);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-200);display:none;left:var(--size-spacing-triple);list-style:none;margin:0;max-height:66vh;overflow:auto;padding:var(--size-spacing-default) 0 var(--size-spacing-half) 0;position:absolute;top:1.638rem;width:200px;z-index:2}.new-post-panel .shake-selector ul li a{word-wrap:break-word;color:var(--color-status-success-pastel-dark);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;padding:var(--size-spacing-half) var(--size-spacing-half-again);text-decoration:none;word-break:break-word}.new-post-panel .shake-selector ul li a:focus,.new-post-panel .shake-selector ul li a:hover{color:var(--color-text-dark)}.new-post-panel--inner{display:flex;flex-direction:column}@media screen and (min-width:768px){.new-post-panel--inner{flex-direction:row}}.new-post-panel--inner>*{flex:1 1 0;min-width:0;padding-bottom:var(--size-spacing-half-again);padding-top:var(--size-spacing-half-again)}@media screen and (min-width:768px){.new-post-panel--inner>*{padding-bottom:var(--size-spacing-quadruple);padding-top:calc(var(--size-spacing-default)*6)}}.new-post-panel--inner>:first-child{border-bottom:1px dashed var(--color-border-default)}@media screen and (min-width:768px){.new-post-panel--inner>:first-child{border-bottom:0;border-right:1px dashed var(--color-border-default);padding-right:var(--size-spacing-double)}}@media screen and (min-width:768px){.new-post-panel--inner>:last-child{padding-left:var(--size-spacing-double)}}.new-post-panel--inner>:first-child:last-child{border:0;padding:0}.dashboard-new-user{padding:var(--size-spacing-double) var(--size-spacing-triple)}.dashboard-new-user h1{color:var(--color-status-success);font-size:3.125rem;margin-bottom:var(--size-spacing-double)}.dashboard-new-user h2{font-size:1.875rem}.dashboard-new-user h3{font-size:1.375rem;margin-bottom:var(--size-spacing-half)}.dashboard-new-user h4{font-size:.875rem;margin-bottom:var(--size-spacing-half)}.dashboard-new-user p{font-size:.875rem;line-height:1.75;padding-bottom:var(--size-spacing-half-again)}.dashboard-new-user a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.dashboard-new-user a:active,.dashboard-new-user a:focus,.dashboard-new-user a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.dashboard-new-user .two-columns{display:flex}.dashboard-new-user .two-columns .left-column{flex:0 0 70px;margin-right:var(--size-spacing-double);margin-top:var(--size-spacing-default)}@media screen and (min-width:768px){.dashboard-new-user .two-columns .left-column{flex-basis:130px;margin-left:var(--size-spacing-double);margin-right:var(--size-spacing-quadruple)}}.dashboard-new-user .two-columns .right-column{flex:1;margin-top:var(--size-spacing-quadruple)}.new-members{border-top:1px dashed var(--color-border-default);list-style:none;margin:var(--size-spacing-triple) 0 0;padding:var(--size-spacing-triple) 0 0}.new-members>li{border-bottom:1px dashed var(--color-border-default);clear:both;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.new-members>li>a{display:block;float:left;margin-right:var(--size-spacing-default)}.new-members>li h4{word-wrap:break-word;font-size:1.125rem;overflow-wrap:break-word;padding-bottom:var(--size-spacing-half);word-break:break-word}.new-members>li h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.new-members>li h4 a:active,.new-members>li h4 a:focus,.new-members>li h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.new-members>li h4 a:active,.new-members>li h4 a:focus,.new-members>li h4 a:hover{text-decoration:underline}.new-members>li p{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;word-break:break-word}.new-members .image-medium{margin-left:calc(var(--size-spacing-default)*6);padding-bottom:0;padding-top:var(--size-spacing-default)}.new-members .image-medium .user-and-title>a{display:none}.notification-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double)}.notification-block-link a{display:block;text-decoration:none}.notification-block-hd,.notification-block-link a{font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-half-again)}.notification-block-hd{cursor:pointer}.notification-block-bd{display:none;padding:0 var(--size-spacing-default) var(--size-spacing-default)}.notification-block-bd .notification{word-wrap:break-word;background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);color:var(--color-page-text);font-size:.875rem;overflow-wrap:break-word;padding:var(--size-spacing-default);position:relative;word-break:break-word}.notification-block-bd .notification:after{clear:both;content:"";display:table}.notification-block-bd .notification+.notification{margin-top:var(--size-spacing-default)}.notification-block-bd .notification .notification-close{outline:none;position:absolute;right:var(--size-spacing-half);top:var(--size-spacing-half)}.notification-block-bd .notification .thumb{float:left;padding-right:var(--size-spacing-default);text-align:center;width:var(--size-avatar-large)}.notification-block-bd .notification .context{font-size:.75rem;overflow:hidden}.notification-block-bd .clear-all{clear:both;margin-top:var(--size-spacing-default);text-align:right}.notification-block-follow{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-primary)}.notification-block-save{background-color:var(--color-bg-warning-pastel);color:var(--color-status-warning-pastel-dark)}.notification-block-like{background-color:var(--color-bg-danger-pastel);color:var(--color-status-danger-pastel-dark)}.notification-block-comment{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-secondary)}.notification-block-invitation-approved,.notification-block-invitation-request,.notification-block-shakeinvitation{background-color:var(--color-bg-success-pastel);color:var(--color-status-success-pastel-dark)}.notification-block-aggregate{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-primary)}.notification-block-follow .notification{padding-right:var(--size-spacing-triple)}.notification-block-shakeinvitation .shake-thumb{float:left;margin-right:var(--size-spacing-default);width:var(--size-avatar-default)}.notification-block-shakeinvitation .shake-thumb a{display:block}.notification-block-shakeinvitation h4{padding-top:var(--size-spacing-default)}.notification-block-shakeinvitation .shake-context{clear:both;margin-top:var(--size-spacing-default)}.notification-block-shakeinvitation .shake-context a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.notification-block-shakeinvitation .shake-context a:active,.notification-block-shakeinvitation .shake-context a:focus,.notification-block-shakeinvitation .shake-context a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.notification-block-shakeinvitation .shake-context a:active,.notification-block-shakeinvitation .shake-context a:focus,.notification-block-shakeinvitation .shake-context a:hover{text-decoration:underline}.notification-block-shakeinvitation .buttons{display:flex;justify-content:space-between;margin-top:var(--size-spacing-default)}.content-shake .notification-block-shakeinvitation{margin-top:var(--size-spacing-double)}.notification-block-invitation-request .notification a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.notification-block-invitation-request .notification a:active,.notification-block-invitation-request .notification a:focus,.notification-block-invitation-request .notification a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.notification-block-invitation-request .notification a:active,.notification-block-invitation-request .notification a:focus,.notification-block-invitation-request .notification a:hover{text-decoration:underline}.notification-block-invitation-request .notification-actions{clear:both;display:flex;justify-content:space-between;margin-top:var(--size-spacing-default)}.notification-block-tou .notification-block-link a{color:var(--color-page-text-emphasis)}.notification-block-tou .notification-block-link a:focus,.notification-block-tou .notification-block-link a:hover{color:var(--color-text-link-hover)}.account-header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));padding:1.5rem 1.875rem 1rem;width:100%}.account-header .avatar{display:flex}.account-header .avatar img{display:block;height:50px;width:50px}.account-header .avatar-media{flex:none}.account-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.account-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.account-header h2{font-size:2.625rem}}.pagination{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;font-weight:var(--number-font-weight-bold);margin:var(--size-spacing-quadruple);padding:var(--size-spacing-default)}.pagination .current,.pagination a{padding:0 var(--size-spacing-half)}.pagination span.next-link,.pagination span.previous-link{color:var(--color-page-text-secondary)}.pagination .previous-link{flex-basis:50%}@media screen and (min-width:480px){.pagination .previous-link{flex-basis:auto;margin-right:auto}}.pagination .next-link{flex-basis:50%;text-align:right}@media screen and (min-width:480px){.pagination .next-link{flex-basis:auto;margin-left:auto;order:1}}.pagination-inner{align-items:center;background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);display:flex;flex-wrap:wrap;justify-content:center;padding:var(--size-spacing-default)}@media screen and (min-width:480px){.pagination-inner{flex-wrap:none}}.linear-navigation{display:flex;justify-content:space-between;padding:var(--size-spacing-triple) var(--size-spacing-quadruple)}.linear-navigation .newer a,.linear-navigation .older a{display:block}.promotions{list-style:none;margin:var(--size-spacing-default) 0;padding:0}.promotions li{float:left;margin-bottom:var(--size-spacing-default);margin-right:var(--size-spacing-default);overflow:visible;position:relative}.promotions li:focus .promotion-avatar,.promotions li:hover .promotion-avatar{border-color:var(--color-brand-primary)}.promotions li:focus .promotion-name,.promotions li:hover .promotion-name{display:block}.promotions .promotion-avatar{border:4px solid transparent;border-radius:var(--size-border-radius-large);overflow:hidden;transition:all .75s ease 0s}@media screen and (prefers-reduced-motion:reduce){.promotions .promotion-avatar{transition:none}}.promotions .promotion-avatar img{display:block;height:var(--size-avatar-large);width:var(--size-avatar-large)}.promotions .promotion-name{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-primary);border:none;border-radius:.5em;bottom:-3.5em;box-shadow:.22em .22em 0 var(--color-base-black-transparent-100);color:var(--color-text-light-emphasis);cursor:pointer;display:inline-block;display:none;font-size:18px;font-size:.875rem;font-weight:500;height:2.2em;left:50%;letter-spacing:.033em;line-height:2.2em;margin-right:.22em;padding:0 .88em;position:absolute;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transform:translateX(-50%);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap;z-index:1}.promotions .promotion-name:focus,.promotions .promotion-name:hover{color:var(--color-text-light-emphasis)}.promotions .promotion-name:focus{outline:none}.promotions .promotion-name:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.promotions .promotion-name:focus,.promotions .promotion-name:hover{background-color:var(--color-background-button-primary-hover)}.promotions .promotion-name:disabled{box-shadow:none!important}.promo-block{margin:var(--size-spacing-double) auto;max-width:100%;text-align:center;width:285px}@media screen and (max-width:320px){.promo-block{display:none}}.shake-image{cursor:pointer;height:284px;max-width:284px;overflow:hidden;position:relative}.shake-image img{display:block}.shake-image .shake-image-input{cursor:pointer;height:100%;left:0;opacity:0;position:absolute;top:0;width:100%;z-index:1}.shake-image .border{display:none}.shake-image.is-editable:focus .border,.shake-image.is-editable:hover .border,.shake-image.shake-image-hover .border{border:10px solid var(--color-status-edit);display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0}.shake-image .shake-image-placeholder{align-items:center;background:var(--color-background-content-secondary);border:1px dashed var(--color-border-default);display:flex;flex-direction:column;height:100%;justify-content:center;max-width:100%;padding:var(--size-spacing-triple);text-align:center}.shake-image .shake-image-placeholder strong{color:var(--color-brand-primary);display:block}.shake-details .shake-edit-description-form,.shake-details .shake-edit-title-form{display:none}.shake-details .title{word-wrap:break-word;font-size:2.25rem;margin:var(--size-spacing-half-again) 0 var(--size-spacing-default);overflow-wrap:break-word;word-break:break-word}.shake-details .shake-edit-title-hover,.shake-details.is-editable .title:focus,.shake-details.is-editable .title:hover{background-color:var(--color-status-edit)}.shake-details .shake-edit-title-input{border:1px solid var(--color-border-default);font-size:2.25rem;font-weight:var(--number-font-weight-bold);margin-top:var(--size-spacing-default);width:100%}.shake-details .description{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;white-space:pre-wrap;word-break:break-word}.shake-details .description .placeholder{color:var(--color-page-text-secondary);font-style:italic}.shake-details .shake-edit-description-hover,.shake-details.is-editable .description:focus,.shake-details.is-editable .description:hover{background-color:var(--color-status-edit)}.shake-details .shake-edit-description-input{border:1px solid var(--color-border-default);font-size:.875rem;width:100%}.shake-details .buttons{align-items:center;display:flex;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-half)}.shake-details .or{color:var(--color-page-text-secondary);padding:var(--size-spacing-half)}.shake-list{border-top:1px dashed var(--color-border-default);list-style:none;margin:0;padding:0}.shake-list--shake{border-bottom:1px dashed var(--color-border-default);clear:both;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.shake-list--thumb{float:left;margin-right:1em}.shake-list--description,.shake-view-description,.shake-view-featured,.shake-view-title{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.shake-members{list-style:none;margin:0;padding:var(--size-spacing-triple)}.shake-members li{border-bottom:1px dashed var(--color-border-default);display:flex;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.shake-members .member--img{flex:none;margin-right:var(--size-spacing-default)}.shake-members .details{flex:1}.shake-members h4{font-size:1.125rem;padding-bottom:var(--size-spacing-half)}.shake-members h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.shake-members h4 a:active,.shake-members h4 a:focus,.shake-members h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.shake-members h4 a:active,.shake-members h4 a:focus,.shake-members h4 a:hover{text-decoration:underline}.shake-members .about{font-size:.875rem;margin:0}.shake-members .website{display:block;margin-top:var(--size-spacing-half)}.following-wrapper{margin:var(--size-spacing-double) 0}.following-wrapper h3{color:var(--color-page-text);font-size:.875rem;padding-bottom:var(--size-spacing-default);padding-left:var(--size-spacing-half-again);padding-right:var(--size-spacing-half-again)}.following-wrapper h3 a{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-secondary);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);cursor:pointer;display:inline-block;float:right;font-size:18px;font-size:12px;font-weight:500;font-weight:400;height:2.2em;letter-spacing:.033em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.following-wrapper h3 a:focus,.following-wrapper h3 a:hover{color:var(--color-text-light-emphasis)}.following-wrapper h3 a:focus{outline:none}.following-wrapper h3 a:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.following-wrapper h3 a:focus,.following-wrapper h3 a:hover{background-color:var(--color-background-button-secondary-hover)}.following-wrapper h3 span{color:var(--color-page-text-secondary);font-weight:var(--number-font-weight-normal)}.following{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large)}.following ul{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:var(--size-spacing-half-again);padding-right:0}.following ul li{flex:none;margin-bottom:var(--size-spacing-half);margin-right:var(--size-spacing-half)}.following ul li a{display:block}.following br{display:none}.following .view-all-following{display:block;font-size:.75rem;margin-top:calc(var(--size-spacing-default)*-1);padding-bottom:var(--size-spacing-default);padding-right:var(--size-spacing-half-again);text-align:right}.other-shakes-wrapper{margin-bottom:var(--size-spacing-double)}.other-shakes-wrapper h3{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;padding-bottom:var(--size-spacing-default);padding-left:var(--size-spacing-half-again);word-break:break-word}.other-shakes{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-default) var(--size-spacing-half-again)}.other-shakes ul{list-style:none;margin:0;padding:0}.other-shakes a{word-wrap:break-word;color:var(--color-text-link-primary);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;padding:var(--size-spacing-half) 0;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.other-shakes a:active,.other-shakes a:focus,.other-shakes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.other-shakes a:active,.other-shakes a:focus,.other-shakes a:hover{text-decoration:underline}.find-shakes-block{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.find-shakes-block h3{color:var(--color-brand-secondary);font-size:1.25rem}.find-shakes-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.find-shakes-block-content p{margin:0}.find-shakes-block-content a{font-weight:var(--number-font-weight-bold);text-decoration:none}.upgrade-account-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.upgrade-account-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-half);padding:var(--size-spacing-double)}.upgrade-account-block-content h3{font-size:1.125rem;margin-bottom:var(--size-spacing-half)}.upgrade-account-block-content h3 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.upgrade-account-block-content h3 a:active,.upgrade-account-block-content h3 a:focus,.upgrade-account-block-content h3 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.upgrade-account-block-content h3 a:active,.upgrade-account-block-content h3 a:focus,.upgrade-account-block-content h3 a:hover{text-decoration:underline}.upgrade-account-block-content p{margin:0}.cool-tools-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.cool-tools-block h3{color:var(--color-brand-primary);font-size:1.25rem}.cool-tools-block p{font-size:.875rem;margin:var(--size-spacing-half) 0 0}.cool-tools-block a{background-color:var(--color-background-content);background-position:5px 5px;background-repeat:no-repeat;background-size:40px 45px;border-radius:var(--size-border-radius-large);color:var(--color-text-link-primary);display:block;font-size:.75rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default);padding-bottom:var(--size-spacing-double);padding-left:55px;padding-top:var(--size-spacing-double);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.cool-tools-block a:active,.cool-tools-block a:focus,.cool-tools-block a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.cool-tools-block a:active,.cool-tools-block a:focus,.cool-tools-block a:hover{text-decoration:underline}.cool-tools-block .browser-tools{list-style:none;margin:var(--size-spacing-double) 0 var(--size-spacing-default);padding:0}.cool-tools-block .browser-tools li+li{margin-top:var(--size-spacing-default)}.cool-tools-block .bookmarklet a{background-image:svg-load("icons/bookmark.svg")}.cool-tools-block .safari a{background-image:svg-load("tools/safari.svg")}.cool-tools-block .firefox a{background-image:svg-load("tools/firefox.svg")}.cool-tools-block .chrome a{background-image:svg-load("tools/chrome.svg")}.cool-tools-block .twitter-setup{background-image:svg-load("tools/twitter.svg")}.shake-invite-member-block{background-color:var(--color-brand-primary);border-radius:var(--size-border-radius-large);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);clear:both;margin-bottom:var(--size-spacing-double);margin-right:var(--size-spacing-half);margin-top:var(--size-spacing-double);position:relative}.shake-invite-member-block h3{color:var(--color-text-light-emphasis);padding:var(--size-spacing-half-again) var(--size-spacing-default);text-shadow:.05em .05em .05em var(--color-base-black-transparent-500)}.shake-invite-member-block form{padding:0 var(--size-spacing-default) var(--size-spacing-double)}.shake-invite-member-block .shake-input-wrapper{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);display:flex;padding:var(--size-spacing-half);position:relative}.shake-invite-member-block .input-text{background-color:var(--color-background-content);border:0;border-radius:var(--size-border-radius-default);flex:1}.shake-invite-member-block .invite-button{flex:none;margin-left:var(--size-spacing-half)}.shake-invite-member-block .shake-results{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);display:none;left:0;list-style:none;margin:0;padding:var(--size-spacing-half);position:absolute;top:var(--size-spacing-quadruple);width:220px}.shake-invite-member-block .shake-results li{align-items:center;border-radius:var(--size-border-radius-default);cursor:pointer;display:flex;font-size:.875rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-half)}.shake-invite-member-block .shake-results li:focus,.shake-invite-member-block .shake-results li:hover{background-color:var(--color-bg-success-pastel)}.shake-invite-member-block .shake-results li img{flex:none;height:var(--size-avatar-tiny);margin-right:var(--size-spacing-half);width:var(--size-avatar-tiny)}.shake-invite-member-block .shake-results li span{flex:1}.shake-sidebar-actions{display:flex;margin-bottom:var(--size-spacing-double);margin-top:var(--size-spacing-default)}.shake-sidebar-actions>*{flex:none}.shake-sidebar-actions>*+*{margin-left:var(--size-spacing-default)}.shake-sidebar-actions .follow h4,.shake-sidebar-actions .icon,.shake-sidebar-actions .user-follow h4 a{display:none}.shake-sidebar-editor-block{align-items:center;background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);clear:both;display:flex;font-size:.875rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.shake-sidebar-editor-block a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.shake-sidebar-editor-block a:active,.shake-sidebar-editor-block a:focus,.shake-sidebar-editor-block a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.shake-sidebar-editor-block a:active,.shake-sidebar-editor-block a:focus,.shake-sidebar-editor-block a:hover{text-decoration:underline}.shake-sidebar-editor-block .editor-image{flex:none}.shake-sidebar-editor-block .editor-image .avatar--img{display:block;width:var(--size-avatar-default)}.shake-sidebar-editor-block .editor-details{flex:1;margin-left:var(--size-spacing-default)}.sidebar-flag-nsfw{margin-top:var(--size-spacing-triple)}.sidebar-flag-nsfw .flag-nsfw-button,.sidebar-flag-nsfw .unflag-nsfw-button{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-danger-pastel);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-danger-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .flag-nsfw-button:hover,.sidebar-flag-nsfw .unflag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:hover{color:var(--color-text-light-emphasis)}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:focus{outline:none}.sidebar-flag-nsfw .flag-nsfw-button:disabled,.sidebar-flag-nsfw .unflag-nsfw-button:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.sidebar-flag-nsfw .flag-nsfw-button:disabled,.sidebar-flag-nsfw .unflag-nsfw-button:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .flag-nsfw-button:hover,.sidebar-flag-nsfw .unflag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.flag-image{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-warning-pastel);background-image:svg-load("icons/flag.svg");background-position:.5em;background-repeat:no-repeat;background-size:7px 9px;border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-warning-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;margin-top:var(--size-spacing-triple);padding:0 .88em 0 1.5em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.flag-image:focus,.flag-image:hover{color:var(--color-text-light-emphasis)}.flag-image:focus{outline:none}.flag-image:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.flag-image:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.flag-image:focus,.flag-image:hover{background-color:var(--color-background-button-warning-pastel-hover);color:var(--color-text-button-warning-pastel-hover)}.flag-image.flag-image-set{color:var(--color-status-danger)}.delete-post-text{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-danger-pastel);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-danger-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;margin-top:var(--size-spacing-triple);padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.delete-post-text:focus,.delete-post-text:hover{color:var(--color-text-light-emphasis)}.delete-post-text:focus{outline:none}.delete-post-text:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.delete-post-text:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.delete-post-text:focus,.delete-post-text:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.sidebar-stats{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;font-weight:var(--number-font-weight-bold);margin-top:var(--size-spacing-triple)}.sidebar-stats,.sidebar-stats .tab{padding:var(--size-spacing-default)}.sidebar-stats .tab{display:block}.sidebar-stats .selected .tab{background-color:var(--color-background-content);border-top-left-radius:var(--size-border-radius-large);border-top-right-radius:var(--size-border-radius-large)}.sidebar-stats .enable-cursor{cursor:pointer}.sidebar-stats-tabs{display:flex;list-style:none;margin:0;padding:0}.sidebar-stats-tabs>*{flex:1}.sidebar-stats-views{color:var(--color-page-text-secondary)}.sidebar-stats-saves{color:var(--color-status-warning)}.sidebar-stats-hearts{color:var(--color-status-danger)}.sidebar-stats-content{background-color:var(--color-background-content);border-bottom-left-radius:var(--size-border-radius-large);border-bottom-right-radius:var(--size-border-radius-large);display:none;padding:var(--size-spacing-half-again)}.sidebar-stats-content .user-action{align-items:center;display:flex}.sidebar-stats-content .user-action+.user-action{margin-top:var(--size-spacing-default)}.sidebar-stats-content .icon{margin-right:var(--size-spacing-half)}.sidebar-stats-content .icon .avatar--img{display:block;height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.sidebar-stats-content .name{word-wrap:break-word;color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.sidebar-stats-content .name:active,.sidebar-stats-content .name:focus,.sidebar-stats-content .name:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.sidebar-stats-content .name:active,.sidebar-stats-content .name:focus,.sidebar-stats-content .name:hover{text-decoration:underline}.sidebar-stats-content .date{color:var(--color-page-text-secondary);font-size:.75rem;font-weight:400;margin-left:var(--size-spacing-default);white-space:nowrap}.meta-data{margin-top:var(--size-spacing-triple)}.meta-data h4{font-size:.875rem;padding-left:var(--size-spacing-half-again)}.meta-data h4,.meta-data p{color:var(--color-page-text-secondary)}.meta-data p{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-default);font-size:.75rem;margin:var(--size-spacing-half) 0;padding:var(--size-spacing-default) var(--size-spacing-half-again)}.shake-details-title{color:var(--color-page-text-secondary);font-size:.875rem;margin-bottom:var(--size-spacing-half);margin-top:var(--size-spacing-triple);padding-left:var(--size-spacing-half-again)}.in-these-shakes{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;margin:0 0 var(--size-spacing-triple) 0;padding:var(--size-spacing-half-again)}.in-these-shakes ul{list-style:none;margin:0;padding:0}.in-these-shakes li{align-items:center;display:flex;margin-bottom:var(--size-spacing-default)}.in-these-shakes a{word-wrap:break-word;color:var(--color-text-link-primary);flex:1;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.in-these-shakes a:active,.in-these-shakes a:focus,.in-these-shakes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.in-these-shakes a:active,.in-these-shakes a:focus,.in-these-shakes a:hover{text-decoration:underline}.in-these-shakes .delete-from-shakes-form,.in-these-shakes .input-checkbox{flex:none;margin-right:var(--size-spacing-default)}.permalink-social{display:flex;list-style:none;margin:0;padding:0}.permalink-social li{flex:none}.permalink-social li+li{margin-left:var(--size-spacing-default)}.permalink-social a{display:block}.permalink-social a:focus,.permalink-social a:hover{opacity:.66}.permalink-social img{display:block}.permalink-social .tumblr a{background-color:#001935;display:flex;height:21px;width:21px}.permalink-social .tumblr a img{height:12px;margin:auto;width:9px}.site-footer{color:var(--color-page-text-secondary);font-size:.75em;margin:var(--size-spacing-triple);text-align:center}.site-footer p{margin:0}.site-footer p+p{margin-top:.5em}.site-footer a{white-space:nowrap}.site-header{display:flex}.site-branding{margin-right:1em;margin-top:1em;width:211px}.site-branding a{display:block}.site-branding a:focus:not(:focus-visible){box-shadow:none}.site-branding--logo{display:block;height:auto;width:100%}.site-nav{flex:1;margin-top:69px;position:relative;text-align:right}.site-nav--list{display:none;flex-direction:column;list-style:none;margin:0;padding:0;position:absolute;right:0;top:47px;z-index:99}.site-nav.is-expanded .site-nav--list{display:flex}@media screen and (min-width:768px){.site-nav--list{display:flex;flex-direction:row;position:static}}.site-nav--item{display:flex;flex:none;justify-content:flex-end}.site-nav--item>*{flex:1}@media screen and (min-width:768px){.site-nav--item>*{flex:none}}.site-nav--item+.site-nav--item{margin-top:.25em}@media screen and (min-width:768px){.site-nav--item+.site-nav--item{margin-left:1em;margin-top:0}}@media screen and (min-width:768px){.site-nav--toggle{display:none}.site-nav--signup,.site-nav--upload{flex-grow:1}}.site-nav--conversations a,.site-nav--popular a,.site-nav--search a{display:block}.site-nav--signup .call-out{color:var(--color-page-text-secondary);display:none;font-size:.875rem;font-weight:var(--number-font-weight-bold);max-width:18em;padding-right:var(--size-spacing-default);padding-top:3px}@media screen and (min-width:768px){.site-nav--signup .call-out{display:block}}.user-counts{background-color:var(--color-border-default);border-radius:var(--size-border-radius-large);clear:both;margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.user-counts ul{display:flex;justify-content:space-between;list-style:none;margin:0;padding:0}.user-counts li{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);flex-grow:1;flex-shrink:1;margin-right:var(--size-spacing-default);padding:var(--size-spacing-default) 0;text-align:center;width:70px}.user-counts li .num{display:block;font-size:1.125rem;font-weight:var(--number-font-weight-bold)}.user-counts li .label{display:block;font-size:.875rem}.user-counts .views{width:95px}.user-counts .saves{color:var(--color-status-warning)}.user-counts .likes{color:var(--color-status-danger);margin-right:0}.user-follow{display:flex}.user-follow .icon{flex:none;margin-right:var(--size-spacing-default)}.user-follow h4{word-wrap:break-word;font-size:.875rem;margin-bottom:var(--size-spacing-half);overflow-wrap:break-word;word-break:break-word}.user-follow h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);padding-bottom:var(--size-spacing-half);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.user-follow h4 a:active,.user-follow h4 a:focus,.user-follow h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.user-follow h4 a:active,.user-follow h4 a:focus,.user-follow h4 a:hover{text-decoration:underline}.user-follow .follow{flex:1}.user-follow-extended{display:block;margin:var(--size-spacing-double) var(--size-spacing-half)}.user-follow-extended .icon{float:left;margin-right:var(--size-spacing-default)}.user-follow-extended .details{padding-bottom:var(--size-spacing-default)}.user-follow-extended h4 a{font-size:1.125rem}.user-follow-extended .about{display:block;font-size:.875rem;line-height:1.3;margin-bottom:var(--size-spacing-default);white-space:pre-wrap}.user-follow-extended .about,.user-follow-extended .website a{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.user-follow-extended .website a{color:var(--color-text-link-primary);font-size:.8rem;font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.user-follow-extended .website a:active,.user-follow-extended .website a:focus,.user-follow-extended .website a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.user-follow-extended .website a:active,.user-follow-extended .website a:focus,.user-follow-extended .website a:hover{text-decoration:underline}.user-follow-extended:after{clear:both;content:"";display:table}.user-nav{background-color:var(--color-background-content);border-bottom-left-radius:1em;font-size:.9rem;padding:.75em 1.5em;position:absolute;right:0;top:0}.user-nav--list{display:flex;list-style:none;margin:0;padding:0}.user-nav--item+.user-nav--item{margin-left:1.5em}.user-nav--link{font-weight:var(--number-font-weight-bold);text-decoration:none;white-space:nowrap}.admin-nav{list-style:none;margin:0;padding:0}.admin-new-users{border:2px solid var(--color-status-danger)}.admin-new-users .body{padding:var(--size-spacing-triple)}.api-accept,.api-decline{float:left;margin-right:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.content-developer{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-developer{padding:var(--size-spacing-quadruple)}}.content-developer h1{font-size:3.25rem}.content-developer h2{font-size:1.5rem;margin-bottom:var(--size-spacing-half-again);margin-top:var(--size-spacing-half-again)}.content-developer p{line-height:1.4;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-default)}.content-developer dt big{font-size:normal}.content-developer dt big,.content-developer dt em{color:var(--color-page-text-secondary)}.faq-page h1{font-size:2.5rem}.faq-page h2{font-size:1.25rem;margin-top:var(--size-spacing-half-again)}.faq-page li,.faq-page p{font-size:.875rem;line-height:2;margin-top:5px}.faq-page li a,.faq-page p a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.faq-page li a:active,.faq-page li a:focus,.faq-page li a:hover,.faq-page p a:active,.faq-page p a:focus,.faq-page p a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.faq-page li a:active,.faq-page li a:focus,.faq-page li a:hover,.faq-page p a:active,.faq-page p a:focus,.faq-page p a:hover{text-decoration:underline}.content-find-shakes .header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));padding:1.5rem 1.875rem 1rem;width:100%}.content-find-shakes .header .avatar{display:flex}.content-find-shakes .header .avatar img{display:block;height:50px;width:50px}.content-find-shakes .header .avatar-media{flex:none}.content-find-shakes .header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.content-find-shakes .header h2{font-size:2.25rem}}@media screen and (min-width:768px){.content-find-shakes .header h2{font-size:2.625rem}}.content-find-shakes .body{padding:var(--size-spacing-double)}.content-find-shakes .good-folk-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-half-again)}.content-find-shakes .good-folk-block h3{color:var(--color-status-success-pastel-dark);font-size:1.125rem;padding-left:var(--size-spacing-half)}.content-find-shakes .good-folk-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);color:var(--color-page-text);margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.content-find-shakes .good-folk-block-content p{font-size:.875rem;margin-top:0}.content-find-shakes .good-folk-block-content .user-follow{margin-top:var(--size-spacing-double)}.content-find-shakes .find-shakes-navigation{display:flex;list-style:none;margin:0;padding:0}.content-find-shakes .find-shakes-navigation li{background:var(--color-background-content-secondary);flex:1;font-size:1.125rem}.content-find-shakes .find-shakes-navigation li a{display:block;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-double) var(--size-spacing-half);text-align:center;text-decoration:none}.content-find-shakes .find-shakes-navigation .selected a{background-color:var(--color-background-content);color:var(--color-page-text)}.content-find-shakes .featured-shakes{margin-bottom:var(--size-spacing-double)}.content-find-shakes .featured-shakes h3{font-size:1.125rem;margin:var(--size-spacing-double) var(--size-spacing-default)}.content-find-shakes .featured-shakes ul{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.content-find-shakes .featured-shakes li{border:1px solid var(--color-border-default);box-shadow:1px 2px 1px var(--color-base-black-transparent-100);margin-bottom:var(--size-spacing-default);margin-right:var(--size-spacing-default);padding:var(--size-spacing-default);width:calc(172px + var(--size-spacing-default)*2)}.content-find-shakes .featured-shakes li img{height:170px;width:170px}.content-find-shakes .featured-shakes li h4{font-size:1.125rem;line-height:1.2;margin:var(--size-spacing-default) 0 0}.content-find-shakes .featured-shakes li h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.content-find-shakes .featured-shakes li h4 a:active,.content-find-shakes .featured-shakes li h4 a:focus,.content-find-shakes .featured-shakes li h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.content-find-shakes .featured-shakes li h4 a:active,.content-find-shakes .featured-shakes li h4 a:focus,.content-find-shakes .featured-shakes li h4 a:hover{text-decoration:underline}.content-find-shakes .featured-shakes li p{-webkit-box-orient:vertical;-webkit-line-clamp:3;display:-webkit-box;font-size:.75rem;margin:var(--size-spacing-default) 0 0;overflow:hidden}.content-find-shakes .shake-category{border-bottom:1px dashed var(--color-border-default);clear:both}.content-find-shakes .shake-category .category-title{font-size:1.125rem}.content-find-shakes .shake-category .category-title a{display:block;padding:var(--size-spacing-half-again) var(--size-spacing-default);text-decoration:none}.content-find-shakes .shake-category .shake-category-body{display:none;padding:0 var(--size-spacing-default)}.content-find-shakes .shake-category.shake-category-selected .category-title a{color:var(--color-page-text-secondary)}.content-find-shakes .shake-category.shake-category-selected .shake-category-body{display:block}.content-find-shakes .shake-tips{background:var(--color-background-content-secondary);clear:both;color:var(--color-page-text);font-size:.75rem;margin:var(--size-spacing-triple) 0;padding:var(--size-spacing-half-again)}.content-find-shakes .friend+.friend{border-top:1px dashed var(--color-border-default)}.content-find-shakes .user-follow-extended .website a{color:var(--color-text-link)}.content-find-shakes .user-follow-extended .website a:active,.content-find-shakes .user-follow-extended .website a:focus,.content-find-shakes .user-follow-extended .website a:hover{color:var(--color-text-link-hover)}.content-find-shakes .body .loading{color:var(--color-page-text-secondary);font-size:1.125rem;margin:var(--size-spacing-quadruple) auto;text-align:center}.content-find-shakes .body .loading img{margin-right:var(--size-spacing-double);position:relative;top:var(--size-spacing-double)}.content-find-shakes .message{color:var(--color-page-text-secondary);font-size:1.125rem;margin:var(--size-spacing-quadruple) var(--size-spacing-triple);text-align:center}.content-find-shakes .intro{border-bottom:1px solid var(--color-border-default);font-size:.875rem;padding:var(--size-spacing-double) var(--size-spacing-half)}.content-find-shakes .refresh-friends{margin:var(--size-spacing-triple) 0}.content-incoming .tip-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.content-incoming .tip-block h3{color:var(--color-brand-primary);font-size:1.3rem;margin:0 0 var(--size-spacing-half-again)}.content-incoming .tip-block p{color:var(--color-page-text-secondary);font-size:.875rem;margin:0}.incoming-header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));cursor:pointer;padding:1.5rem 1.875rem 1rem;padding:var(--size-spacing-half-again) var(--size-spacing-double);width:100%}.incoming-header .avatar{display:flex}.incoming-header .avatar img{display:block;height:50px;width:50px}.incoming-header .avatar-media{flex:none}.incoming-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.incoming-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.incoming-header h2{font-size:2.625rem}.incoming-header{align-items:center;display:flex;flex-direction:row}}.incoming-header:before{background:svg-load("illustrations/incoming-header.svg") 50% no-repeat;background-size:100%;content:"";display:block;margin:auto;max-width:100%;padding-top:29%}@media screen and (min-width:480px){.incoming-header:before{margin:0;padding-top:115px;width:395px}}.incoming-header h2{font-size:1.5rem;line-height:1.2;margin-top:var(--size-spacing-half-again);padding-left:0;text-align:center}@media screen and (min-width:768px){.incoming-header h2{font-size:1.75rem;margin-top:0;padding-left:var(--size-spacing-default)}}.code-of-conduct h2{font-size:1.875rem;text-align:center}.code-of-conduct h3{font-size:1.5rem;margin-top:1.4em}.code-of-conduct p{font-size:1rem;line-height:1.4;margin:1em 0}.code-of-conduct li{margin-bottom:.4em}.terms-of-use p{font-size:1rem;line-height:1.4;margin:1em 0}.terms-of-use li{font-size:1rem;list-style-type:lower-alpha;margin-left:var(--size-spacing-quadruple)}.terms-of-use .terms-center{text-align:center}.tou-notice-page p{font-size:1rem;line-height:1.4;margin:1em 0}.content-membership{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-membership{padding:var(--size-spacing-quadruple)}}.content-membership h1{line-height:1;text-shadow:2px 4px 1px var(--color-base-black-transparent-100)}.content-membership ul{padding-left:1em}.content-membership li+li{margin-top:1em}.content-membership .fine-print{font-size:.75rem}.content-membership .subscribe-plan-quantity-wrapper{color:var(--color-page-text-secondary);font-size:2rem}.content-membership .input-plan-quantity{display:inline-block;font-size:inherit;font-weight:var(--number-font-weight-bold);max-width:150px;width:auto}.content-membership .input-plan-quantity:invalid{color:var(--color-status-danger)}.content-membership .subscribe-plan-quantity-wrapper i{color:var(--color-page-text-secondary);font-size:1.5rem;font-style:normal}.content-migrate{background-image:svg-load("illustrations/burger-upgrade.svg");background-position:bottom var(--size-spacing-half-again) center;background-repeat:no-repeat;background-size:366px 481px;padding-bottom:530px}@media screen and (min-width:768px){.content-migrate{background-position:var(--size-spacing-quadruple) var(--size-spacing-quadruple);min-height:calc(481px + var(--size-spacing-quadruple));padding-bottom:0;padding-left:430px}}.content-migrate p{font-size:1.25rem;margin:var(--size-spacing-triple) 0}.content-membership-thanks{background-image:svg-load("illustrations/thankyou-dude.svg");background-position:bottom var(--size-spacing-half-again) center;background-repeat:no-repeat;background-size:312px 410px;padding-bottom:450px}@media screen and (min-width:768px){.content-membership-thanks{background-position:var(--size-spacing-quadruple) var(--size-spacing-quadruple);min-height:calc(410px + var(--size-spacing-quadruple));padding-bottom:0;padding-left:380px}}.content-membership-thanks a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.content-membership-thanks a:active,.content-membership-thanks a:focus,.content-membership-thanks a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.content-membership-thanks a:active,.content-membership-thanks a:focus,.content-membership-thanks a:hover{text-decoration:underline}.membership-header{margin-bottom:var(--size-spacing-double)}@media screen and (min-width:480px){.membership-header{display:flex}}@media screen and (min-width:768px){.membership-header{margin-bottom:var(--size-spacing-quadruple)}}.membership-header h2{font-size:2rem;line-height:1.1;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-double)}.membership-header p{font-size:1.2rem;margin:0}.membership-header--illustration{display:none}@media screen and (min-width:480px){.membership-header--illustration{display:block;flex:none;margin-right:var(--size-spacing-half-again);width:75px}}@media screen and (min-width:768px){.membership-header--illustration{margin-right:var(--size-spacing-quadruple);width:140px}}@media screen and (min-width:480px){.membership-header--content{flex:1}}.membership-options>*{margin:var(--size-spacing-double) 0}@media screen and (min-width:768px){.membership-options{display:flex}.membership-options>*{margin:0}.membership-options>*+*{margin-left:var(--size-spacing-quadruple)}}@media screen and (min-width:768px){.membership-options--plan{flex:1}}.membership-options--plan .label{margin-bottom:var(--size-spacing-half)}.membership-options--plan .btn{text-transform:uppercase}.membership-options--plan h3{font-size:2rem}.membership-options--plan p{margin-top:.25em}.membership-options--plan ul{margin-bottom:2em}@media screen and (min-width:768px){.is-active-member .membership-options--plan{padding-top:var(--size-spacing-quadruple)}.is-active-member .membership-options--plan.is-active-plan{padding-top:0}}.membership-options--plan-title{color:var(--color-brand-primary);text-transform:uppercase}.membership-options--plan-tagline{font-size:1.25rem}.membership-options--or{align-items:center;display:flex;justify-content:center}@media screen and (min-width:768px){.membership-options--or{flex-direction:column}}.membership-options--or:after,.membership-options--or:before{background:var(--color-brand-primary);content:"";flex:1;height:1px}@media screen and (min-width:768px){.membership-options--or:after,.membership-options--or:before{height:auto;width:1px}}@media screen and (min-width:768px){.membership-options--or:before{flex-basis:var(--size-spacing-triple);flex-grow:0}}.membership-options--or-bullet{background-color:var(--color-brand-primary);border-radius:50%;color:var(--color-text-light-emphasis);display:flex;font-size:1.5rem;height:3em;margin-left:auto;margin-right:auto;width:3em}.membership-options--or-bullet span{margin:auto}.membership-footer{margin-top:var(--size-spacing-half-again);text-align:center}@media screen and (min-width:768px){.membership-footer{margin-top:var(--size-spacing-quadruple)}}.membership-footer p{margin:0}.content-permalink{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-permalink{padding:var(--size-spacing-triple)}}.content-permalink>*{margin:0!important;padding:0!important}.content-permalink>*+*{margin-top:var(--size-spacing-half-again)!important}@media screen and (min-width:768px){.content-permalink>*+*{margin-top:var(--size-spacing-triple)!important}}@media screen and (min-width:768px){.content-permalink>.permalink-sidebar{float:right;width:330px}}@media screen and (min-width:768px){.content-permalink>.image-comment-form,.content-permalink>.image-comments,.content-permalink>.image-content{float:left;width:calc(100% - 360px)}}@supports (display:grid){@media screen and (min-width:768px){.content-permalink{grid-gap:var(--size-spacing-triple);display:grid;grid-template-areas:"title title" "image sidebar" "comments sidebar" "post-comment sidebar";grid-template-columns:1fr 330px;grid-template-rows:repeat(3,min-content) 1fr}.content-permalink>*{margin:0!important}.content-permalink:after{content:none}.content-permalink>.image-comment-form,.content-permalink>.image-comments,.content-permalink>.image-content,.content-permalink>.permalink-sidebar{width:auto}.content-permalink>.image-title{grid-area:title}.content-permalink>.image-content{grid-area:image}.content-permalink>.permalink-sidebar{grid-area:sidebar}.content-permalink>.image-comments{grid-area:comments}.content-permalink>.image-comment-form{grid-area:post-comment}}}.permalink-sidebar{position:relative}.tools-page{background-color:var(--color-background-page)}.tools-page a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.tools-page a:active,.tools-page a:focus,.tools-page a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.tools-page .header{color:var(--color-page-text-secondary);font-size:.75rem;height:55px;margin:0 var(--size-spacing-double);padding-top:var(--size-spacing-half);position:relative;text-align:right}.tools-page .header a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.tools-page .header a:active,.tools-page .header a:focus,.tools-page .header a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.tools-page .header a:active,.tools-page .header a:focus,.tools-page .header a:hover{text-decoration:underline}.tools-page .header img{height:49px;left:0;position:absolute;top:3px;width:100px}.tools-page .content{background:var(--color-background-content);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.tools-page .content-inner{display:flex;flex-direction:column;margin:0 auto}@media screen and (min-width:768px){.tools-page .content-inner{flex-direction:row;justify-content:space-between}}.tools-page .content-inner>*{min-width:0}@media screen and (min-width:768px){.tools-page .content-inner>*{flex-basis:calc(50% - 10px)}}.tools-page .content-inner>*+*{margin-top:20px}@media screen and (min-width:768px){.tools-page .content-inner>*+*{margin-top:0}}@media screen and (min-width:768px){.tools-page .content-inner .full-width{flex:1}}.tools-page .tools-fun-form{margin-top:0}.tools-page .tools-fun-form .tools-field-title .input-text{font-weight:var(--number-font-weight-bold)}.tools-page .tools-fun-form .field+.field{margin-top:0}.tools-page .tools-fun-form .field-textarea{height:0;overflow:hidden}.tools-page .tools-fun-form .field-textarea.field-textarea--selected{height:auto}.tools-page .tools-fun-form .textarea-navigation ul{display:flex;list-style:none;margin:0;padding:var(--size-spacing-half-again);padding-bottom:0}@media screen and (min-width:480px){.tools-page .tools-fun-form .textarea-navigation ul{padding-left:var(--size-spacing-double)}}@media screen and (min-width:480px){.tools-page .tools-fun-form .textarea-navigation li+li{margin-left:var(--size-spacing-half-again)}}.tools-page .tools-fun-form .textarea-navigation .tab{border-top-left-radius:var(--size-border-radius-default);border-top-right-radius:var(--size-border-radius-default);cursor:pointer;display:block;font-size:.875rem;padding:var(--size-spacing-default)}@media (hover){.tools-page .tools-fun-form .textarea-navigation .tab:hover{background-color:var(--color-form-bg)}}.tools-page .tools-fun-form .textarea-navigation .selected .tab{background-color:var(--color-border-form);font-weight:700}.tools-page .tools-saved-it{background:svg-load("illustrations/tools-saved-it.svg") 0 0 no-repeat;background-size:contain;font-size:1.75rem;padding-top:calc(41.5625% + var(--size-spacing-double))}@media screen and (min-width:768px){.tools-page .tools-saved-it{background-size:320px 133px;padding-top:160px}}.tools-page .content-sign-in{margin-top:var(--size-spacing-double)}.tools-page .content-sign-in h1{color:var(--color-brand-secondary);font-size:1.5rem;padding:var(--size-spacing-triple)}.tools-page .tools-signin-logo{margin-top:var(--size-spacing-default);text-align:center}.tools-page .sign-in-fun-form{margin-top:0;padding-bottom:var(--size-spacing-double)}@media screen and (min-width:768px){.tools-page .sign-in-fun-form label{flex-basis:120px}.tools-page .sign-in-fun-form .field-submit{padding-left:130px}}.tools-page .email-unverified h2,.tools-page .over-upload-limit h2{color:var(--color-brand-primary);font-size:1.5rem;text-align:center}.content-relationships .body .friend,.content-relationships .body .shake{border-bottom:1px dashed var(--color-border-default);padding:0 var(--size-spacing-quadruple)}.content-relationships .body .user-follow-extended .website a{color:var(--color-brand-secondary)}.content-relationships .user-info .avatar{float:left;margin-right:var(--size-spacing-half-again)}.content-relationships .user-info .avatar--img{height:var(--size-avatar-large);width:var(--size-avatar-large)}.content-relationships .user-info .details h3{color:var(--color-page-text);font-size:1.125rem;font-weight:var(--number-font-weight-bold);padding-top:var(--size-spacing-half)}.content-search{list-style:none}.content-search .search-empty-results{margin:var(--size-spacing-double)}.sidebar-search-block{margin-bottom:var(--size-spacing-double)}.sidebar-search-block .field-help{padding-left:0}.settings-header{align-items:center;background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));display:flex;flex-direction:column;justify-content:space-between;padding:1.5rem 1.875rem 1rem;width:100%}.settings-header .avatar{display:flex}.settings-header .avatar img{display:block;height:50px;width:50px}.settings-header .avatar-media{flex:none}.settings-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.settings-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.settings-header h2{font-size:2.625rem}.settings-header{flex-direction:row}}@media screen and (min-width:768px){.settings-header>*{flex:1;max-width:calc(50% - var(--size-spacing-double))}}.settings-header h1{font-size:3rem;margin-bottom:var(--size-spacing-half-again)}@media screen and (min-width:768px){.settings-header h1{margin-bottom:0}}.settings-navigation ul{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);display:flex;list-style:none;margin:0;padding:var(--size-spacing-half-again);padding-bottom:0}@media screen and (min-width:480px){.settings-navigation ul{padding-left:var(--size-spacing-double)}}@media screen and (min-width:480px){.settings-navigation li+li{margin-left:var(--size-spacing-half-again)}}.settings-navigation a{border-top-left-radius:var(--size-border-radius-default);border-top-right-radius:var(--size-border-radius-default);color:var(--color-text-link-primary);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default);padding-bottom:var(--size-spacing-half-again);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.settings-navigation a:active,.settings-navigation a:focus,.settings-navigation a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.settings-navigation a:active,.settings-navigation a:focus,.settings-navigation a:hover{text-decoration:underline}.settings-navigation .selected a{background-color:var(--color-background-content)}.settings-body{display:flex;flex-direction:column;justify-content:space-between;padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.settings-body{flex-direction:row;padding:var(--size-spacing-quadruple)}}.settings-body>*{margin:0}@media screen and (min-width:768px){.settings-body>*{flex:1;max-width:calc(50% - var(--size-spacing-double))}}.settings-body>*+*{margin-top:var(--size-spacing-triple)}@media screen and (min-width:768px){.settings-body>*+*{margin-top:0}}.settings-subscription-sidebar h4{border-bottom:1px dashed var(--color-border-default);font-size:1.125rem;margin-bottom:var(--size-spacing-default);padding-bottom:.25em}.settings-subscription-sidebar .member-status-block,.settings-subscription-sidebar .migration-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-half-again)}.settings-subscription-sidebar .member-status-block h3,.settings-subscription-sidebar .migration-block h3{color:var(--color-status-success-pastel-dark)}.settings-subscription-sidebar .member-status-block:first-child,.settings-subscription-sidebar .migration-block:first-child{margin-top:0}.settings-subscription-sidebar .member-status-block:last-child,.settings-subscription-sidebar .migration-block:last-child{margin-bottom:0}.settings-subscription-sidebar .migration-block{background-color:var(--color-bg-secondary-brand-pastel);margin-bottom:var(--size-spacing-double)}.settings-subscription-sidebar .migration-block h3{color:var(--color-brand-secondary)}.settings-subscription-sidebar .member-status-block-content,.settings-subscription-sidebar .migration-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.settings-subscription-sidebar .transaction-list li{margin-bottom:var(--size-spacing-default)}.settings-subscription-sidebar .transaction-list li .id{color:var(--color-page-text-secondary)}.settings-subscription-sidebar .transaction-list li .amount{font-weight:var(--number-font-weight-bold)}.settings-subscription-sidebar p{margin:var(--size-spacing-double) 0}.settings-body-content .fun-form{margin-top:0}.settings-body-sidebar .profile-photo{display:flex;flex-direction:column}@media screen and (min-width:480px){.settings-body-sidebar .profile-photo{flex-direction:row}}.settings-body-sidebar .profile-photo-media{flex:none}.settings-body-sidebar .profile-photo-media .avatar--img{height:var(--size-avatar-large);width:var(--size-avatar-large)}.settings-body-sidebar .profile-photo-meta{flex:1;padding-top:var(--size-spacing-default)}@media screen and (min-width:480px){.settings-body-sidebar .profile-photo-meta{margin-left:var(--size-spacing-default);padding-top:var(--size-spacing-half)}}.settings-body-sidebar .profile-photo-meta h3{font-size:1.125rem;margin-bottom:var(--size-spacing-double)}.settings-body-sidebar .profile-photo-meta h4{font-size:.875rem;margin-bottom:var(--size-spacing-default)}.settings-body-sidebar .profile-photo-meta .settings-photo-upload{color:var(--color-page-text-secondary);font-size:.75rem}.settings-body-sidebar .info-block{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);margin-top:var(--size-spacing-quadruple);padding:var(--size-spacing-half-again)}@media screen and (min-width:480px){.settings-body-sidebar .info-block{padding:var(--size-spacing-double)}}.settings-body-sidebar .info-block h3{font-size:1rem;margin-bottom:var(--size-spacing-default)}.settings-body-sidebar .info-block p{font-size:.875rem;margin-bottom:0}.settings-body-connections{display:block}.settings-body-connections>*{max-width:none}.settings-body-connections h3{font-size:1.125rem}.settings-body-connections .apps{list-style:none;margin:var(--size-spacing-triple) 0;padding:0}.settings-body-connections .apps li{margin-bottom:var(--size-spacing-double)}.settings-body-connections .apps h4{color:var(--color-page-text-secondary);font-size:1.125rem;margin-bottom:var(--size-spacing-half)}.settings-body-connections .apps h4 .title{color:var(--color-brand-primary)}.settings-body-connections .apps h4 .by{font-size:.875rem}.settings-body-connections .apps h4 .by a{text-decoration:none}.settings-body-connections .apps p{font-size:.875rem;margin-bottom:var(--size-spacing-half)}.settings-body-connections .apps .disconnect{font-size:.75rem;text-decoration:none}#sign-in-form .field-submit{text-align:right}.forgot-password-field{padding-top:var(--size-spacing-double);text-align:right}.password-manager-icon{float:left;padding-right:var(--size-spacing-double)}.steps{display:none}.steps p{text-align:center} \ No newline at end of file +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{-webkit-text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{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-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}:root{--color-background-content-dark:#263238;--color-background-content-light:#fff;--color-background-page-light:#dbfaff;--color-background-page-dark:#13454c;--color-base-pink:#ff0080;--color-base-pink-dark:#c06;--color-base-pink-pastel-dark:#ff0080;--color-base-pink-pastel-light:#ffcce8;--color-base-blue:#00aeff;--color-base-blue-dark:#008bcc;--color-base-blue-very-light:#f2fdff;--color-base-blue-pastel-dark:#00aeff;--color-base-blue-pastel-medium:#aaf3ff;--color-base-blue-pastel-light:#daf3ff;--color-base-blue-pastel-very-light:#dbfaff;--color-base-blue-gray-light:#e4eff1;--color-base-cyan-very-dark:#13454c;--color-base-green:#09b896;--color-base-green-dark:#079378;--color-base-green-pastel-dark:#0caf8b;--color-base-green-pastel-light:#b9ff8d;--color-base-green-pastel-very-light:#dfffcb;--color-base-orange:#ff9600;--color-base-orange-dark:#cc7800;--color-base-orange-pastel-dark:#ff9600;--color-base-orange-pastel-light:#ffe466;--color-base-red:#f30;--color-base-red-dark:#cc2900;--color-base-red-pastel-dark:#ff1c1c;--color-base-red-pastel-light:#ffd4d4;--color-base-gray:#a1a1a1;--color-base-gray-light:#e9e9e9;--color-base-gray-pastel-dark:#8d8d87;--color-base-gray-pastel-light:#cecec6;--color-base-yellow-pastel-light:#fffbd9;--color-base-yellow-transparent-600:rgba(252,255,0,.66);--color-base-black-transparent:transparent;--color-base-black-transparent-100:rgba(0,0,0,.13);--color-base-black-transparent-200:rgba(0,0,0,.25);--color-base-black-transparent-300:rgba(0,0,0,.35);--color-base-black-transparent-500:rgba(0,0,0,.5);--color-base-black-transparent-600:rgba(0,0,0,.6);--color-base-black-transparent-800:rgba(0,0,0,.8);--color-base-white-transparent:hsla(0,0%,100%,0);--color-base-white-transparent-100:hsla(0,0%,100%,.13);--color-base-white-transparent-200:hsla(0,0%,100%,.25);--color-base-white-transparent-300:hsla(0,0%,100%,.35);--color-base-white-transparent-500:hsla(0,0%,100%,.5);--color-base-white-transparent-600:hsla(0,0%,100%,.65);--color-base-white-transparent-800:hsla(0,0%,100%,.85);--color-base-white:#fff;--color-base-black:#000;--color-base-blue-dark-mode:#263238;--color-base-gray-050:#fafafa;--color-base-gray-100:#f5f5f5;--color-base-gray-200:#eee;--color-base-gray-300:#e0e0e0;--color-base-gray-400:#bdbdbd;--color-base-gray-500:#9e9e9e;--color-base-gray-600:#757575;--color-base-gray-700:#616161;--color-base-gray-800:#424242;--color-base-gray-900:#212121;--color-brand-primary:#ff0080;--color-brand-primary-dark:#c06;--color-brand-primary-pastel-light:#ffcce8;--color-brand-primary-pastel-dark:#ff0080;--color-brand-secondary:#00aeff;--color-brand-secondary-dark:#008bcc;--color-brand-secondary-very-light:#f2fdff;--color-brand-secondary-pastel-light:#daf3ff;--color-brand-secondary-pastel-medium:#aaf3ff;--color-brand-secondary-pastel-dark:#00aeff;--color-status-success:#09b896;--color-status-success-dark:#079378;--color-status-success-pastel-very-light:#dfffcb;--color-status-success-pastel-light:#b9ff8d;--color-status-success-pastel-dark:#0caf8b;--color-status-warning:#ff9600;--color-status-warning-dark:#cc7800;--color-status-warning-pastel-light:#ffe466;--color-status-warning-pastel-dark:#ff9600;--color-status-danger:#f30;--color-status-danger-dark:#cc2900;--color-status-danger-pastel-light:#ffd4d4;--color-status-danger-pastel-dark:#ff1c1c;--color-status-disabled:#a1a1a1;--color-status-disabled-light:#e9e9e9;--color-status-disabled-pastel-light:#cecec6;--color-status-disabled-pastel-dark:#8d8d87;--color-status-edit:rgba(252,255,0,.66);--color-status-highlight:#fffbd9;--color-text-dark:rgba(0,0,0,.8);--color-text-dark-emphasis:#000;--color-text-dark-secondary:rgba(0,0,0,.6);--color-text-light:hsla(0,0%,100%,.85);--color-text-light-emphasis:#fff;--color-text-light-secondary:hsla(0,0%,100%,.65);--color-text-link:#00aeff;--color-text-link-hover:#008bcc;--color-text-link-primary:#ff0080;--color-text-link-primary-hover:#c06;--color-text-link-danger:#f30;--color-text-link-danger-hover:#cc2900;--font-family-system:-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--font-family-mono:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;--number-font-weight-light:300;--number-font-weight-normal:400;--number-font-weight-medium:500;--number-font-weight-semibold:600;--number-font-weight-bold:700;--number-font-weight-heavy:800;--size-avatar-tiny:20px;--size-avatar-small:30px;--size-avatar-default:48px;--size-avatar-large:100px;--size-border-radius-default:5px;--size-border-radius-large:10px;--size-breakpoint-xs:320px;--size-breakpoint-sm:375px;--size-breakpoint-md:480px;--size-breakpoint-lg:768px;--size-breakpoint-xl:960px;--size-breakpoint-xxl:1440px;--size-font-base:16px;--size-font-input:16px;--size-spacing-half:5px;--size-spacing-default:10px;--size-spacing-half-again:15px;--size-spacing-double:20px;--size-spacing-triple:30px;--size-spacing-quadruple:40px;--time-speed-instant:0s;--time-speed-immediate:0.1s;--time-speed-quick:0.2s;--time-speed-prompt:0.3s;--time-speed-slow:0.4s;--time-speed-glacial:0.6s}:root{--color-text-link-underline:rgba(0,174,255,.33);--color-text-link-underline-hover:rgba(0,139,204,.33);--color-text-link-primary-underline:rgba(255,0,128,.33);--color-text-link-primary-underline-hover:rgba(204,0,102,.33);--color-text-link-danger-underline:rgba(255,51,0,.33);--color-text-link-danger-underline-hover:rgba(204,41,0,.33)}:root{--color-background-page:var(--color-background-page-light);--color-background-content:var(--color-background-content-light);--color-background-content-secondary:var(--color-base-gray-100);--color-page-text:var(--color-text-dark);--color-page-text-emphasis:var(--color-text-dark-emphasis);--color-page-text-secondary:var(--color-text-dark-secondary);--color-border-default:var(--color-base-gray-300);--color-border-form:var(--color-base-blue-gray-light);--color-border-focus-ring:rgba(0,174,255,.25);--color-form-bg:var(--color-brand-secondary-very-light);--color-form-error-bubble-bg:var(--color-brand-secondary-pastel-medium);--color-form-error-bubble-text:var(--color-page-text);--number-font-weight-bold:bold;--number-font-weight-normal:normal;--letter-spacing:normal;--color-bg-primary-brand-pastel:var(--color-brand-primary-pastel-light);--color-bg-secondary-brand-pastel:var(--color-background-page-light);--color-bg-success-pastel:var(--color-status-success-pastel-very-light);--color-bg-danger-pastel:var(--color-status-danger-pastel-light);--color-bg-warning-pastel:var(--color-status-highlight);--color-background-button-primary:var(--color-brand-primary);--color-background-button-primary-hover:var(--color-brand-primary-dark);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-light);--color-background-button-primary-pastel-hover:var(--color-brand-primary);--color-text-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-text-button-primary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-secondary:var(--color-brand-secondary);--color-background-button-secondary-hover:var(--color-brand-secondary-dark);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-light);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary);--color-text-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-text-button-secondary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-success:var(--color-status-success);--color-background-button-success-hover:var(--color-status-success-dark);--color-background-button-success-pastel:var(--color-status-success-pastel-light);--color-background-button-success-pastel-hover:var(--color-status-success);--color-text-button-success-pastel:var(--color-status-success-pastel-dark);--color-text-button-success-pastel-hover:var(--color-text-light-emphasis);--color-background-button-warning:var(--color-status-warning);--color-background-button-warning-hover:var(--color-status-warning-dark);--color-background-button-warning-pastel:var(--color-status-warning-pastel-light);--color-background-button-warning-pastel-hover:var(--color-status-warning);--color-text-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-text-button-warning-pastel-hover:var(--color-text-light-emphasis);--color-background-button-danger:var(--color-status-danger);--color-background-button-danger-hover:var(--color-status-danger-dark);--color-background-button-danger-pastel:var(--color-status-danger-pastel-light);--color-background-button-danger-pastel-hover:var(--color-status-danger);--color-text-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-text-button-danger-pastel-hover:var(--color-text-light-emphasis);--color-background-choose-shake-link:var(--color-background-content);--color-background-choose-shake-link-hover:var(--color-status-highlight);--color-text-choose-shake-link:var(--color-text-link-primary);--color-text-choose-shake-link-hover:var(--color-text-link-primary-hover)}.t-light{--color-background-page:var(--color-background-page-light);--color-background-content:var(--color-background-content-light);--color-background-content-secondary:var(--color-base-gray-100);--color-page-text:var(--color-text-dark);--color-page-text-emphasis:var(--color-text-dark-emphasis);--color-page-text-secondary:var(--color-text-dark-secondary);--color-border-default:var(--color-base-gray-300);--color-border-form:var(--color-base-blue-gray-light);--color-border-focus-ring:rgba(0,174,255,.25);--color-form-bg:var(--color-brand-secondary-very-light);--color-form-error-bubble-bg:var(--color-brand-secondary-pastel-medium);--color-form-error-bubble-text:var(--color-page-text);--number-font-weight-bold:bold;--number-font-weight-normal:normal;--letter-spacing:normal;--color-bg-primary-brand-pastel:var(--color-brand-primary-pastel-light);--color-bg-secondary-brand-pastel:var(--color-background-page-light);--color-bg-success-pastel:var(--color-status-success-pastel-very-light);--color-bg-danger-pastel:var(--color-status-danger-pastel-light);--color-bg-warning-pastel:var(--color-status-highlight);--color-background-button-primary:var(--color-brand-primary);--color-background-button-primary-hover:var(--color-brand-primary-dark);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-light);--color-background-button-primary-pastel-hover:var(--color-brand-primary);--color-text-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-text-button-primary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-secondary:var(--color-brand-secondary);--color-background-button-secondary-hover:var(--color-brand-secondary-dark);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-light);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary);--color-text-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-text-button-secondary-pastel-hover:var(--color-text-light-emphasis);--color-background-button-success:var(--color-status-success);--color-background-button-success-hover:var(--color-status-success-dark);--color-background-button-success-pastel:var(--color-status-success-pastel-light);--color-background-button-success-pastel-hover:var(--color-status-success);--color-text-button-success-pastel:var(--color-status-success-pastel-dark);--color-text-button-success-pastel-hover:var(--color-text-light-emphasis);--color-background-button-warning:var(--color-status-warning);--color-background-button-warning-hover:var(--color-status-warning-dark);--color-background-button-warning-pastel:var(--color-status-warning-pastel-light);--color-background-button-warning-pastel-hover:var(--color-status-warning);--color-text-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-text-button-warning-pastel-hover:var(--color-text-light-emphasis);--color-background-button-danger:var(--color-status-danger);--color-background-button-danger-hover:var(--color-status-danger-dark);--color-background-button-danger-pastel:var(--color-status-danger-pastel-light);--color-background-button-danger-pastel-hover:var(--color-status-danger);--color-text-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-text-button-danger-pastel-hover:var(--color-text-light-emphasis);--color-background-choose-shake-link:var(--color-background-content);--color-background-choose-shake-link-hover:var(--color-status-highlight);--color-text-choose-shake-link:var(--color-text-link-primary);--color-text-choose-shake-link-hover:var(--color-text-link-primary-hover)}@media screen and (prefers-color-scheme:dark){:root{--color-background-page:var(--color-background-page-dark);--color-background-content:var(--color-background-content-dark);--color-background-content-secondary:var(--color-base-gray-900);--color-page-text:var(--color-text-light);--color-page-text-emphasis:var(--color-text-light-emphasis);--color-page-text-secondary:var(--color-text-light-secondary);--color-border-default:var(--color-base-gray-700);--color-border-form:var(--color-base-gray-800);--color-border-focus-ring:rgba(0,174,255,.4);--color-form-bg:var(--color-background-content-secondary);--color-form-error-bubble-bg:var(--color-base-gray-800);--color-form-error-bubble-text:var(--color-status-danger);--number-font-weight-bold:600;--number-font-weight-normal:300;--letter-spacing:0.025em;--color-bg-primary-brand-pastel:rgba(255,0,128,.1);--color-bg-secondary-brand-pastel:rgba(0,174,255,.1);--color-bg-success-pastel:rgba(9,184,150,.1);--color-bg-danger-pastel:rgba(255,51,0,.1);--color-bg-warning-pastel:rgba(255,150,0,.1);--color-background-button-primary:var(--color-brand-primary-dark);--color-background-button-primary-hover:var(--color-brand-primary);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-background-button-primary-pastel-hover:var(--color-brand-primary-pastel-light);--color-text-button-primary-pastel:var(--color-text-light-emphasis);--color-text-button-primary-pastel-hover:var(--color-brand-primary-pastel-dark);--color-background-button-secondary:var(--color-brand-secondary-dark);--color-background-button-secondary-hover:var(--color-brand-secondary);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-light);--color-text-button-secondary-pastel:var(--color-text-light-emphasis);--color-text-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-dark);--color-background-button-success:var(--color-status-success-dark);--color-background-button-success-hover:var(--color-status-success);--color-background-button-success-pastel:var(--color-status-success-pastel-dark);--color-background-button-success-pastel-hover:var(--color-status-success-pastel-light);--color-text-button-success-pastel:var(--color-text-light-emphasis);--color-text-button-success-pastel-hover:var(--color-status-success-pastel-dark);--color-background-button-warning:var(--color-status-warning-dark);--color-background-button-warning-hover:var(--color-status-warning);--color-background-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-background-button-warning-pastel-hover:var(--color-status-warning-pastel-light);--color-text-button-warning-pastel:var(--color-text-light-emphasis);--color-text-button-warning-pastel-hover:var(--color-status-warning-pastel-dark);--color-background-button-danger:var(--color-status-danger-dark);--color-background-button-danger-hover:var(--color-status-danger);--color-background-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-background-button-danger-pastel-hover:var(--color-status-danger-pastel-light);--color-text-button-danger-pastel:var(--color-text-light-emphasis);--color-text-button-danger-pastel-hover:var(--color-status-danger-pastel-dark);--color-background-choose-shake-link:var(--color-background-button-success);--color-background-choose-shake-link-hover:var(--color-background-button-success-pastel-hover);--color-text-choose-shake-link:var(--color-text-light-emphasis);--color-text-choose-shake-link-hover:var(--color-text-button-success-pastel-hover)}}.t-dark{--color-background-page:var(--color-background-page-dark);--color-background-content:var(--color-background-content-dark);--color-background-content-secondary:var(--color-base-gray-900);--color-page-text:var(--color-text-light);--color-page-text-emphasis:var(--color-text-light-emphasis);--color-page-text-secondary:var(--color-text-light-secondary);--color-border-default:var(--color-base-gray-700);--color-border-form:var(--color-base-gray-800);--color-border-focus-ring:rgba(0,174,255,.4);--color-form-bg:var(--color-background-content-secondary);--color-form-error-bubble-bg:var(--color-base-gray-800);--color-form-error-bubble-text:var(--color-status-danger);--number-font-weight-bold:600;--number-font-weight-normal:300;--letter-spacing:0.025em;--color-bg-primary-brand-pastel:rgba(255,0,128,.1);--color-bg-secondary-brand-pastel:rgba(0,174,255,.1);--color-bg-success-pastel:rgba(9,184,150,.1);--color-bg-danger-pastel:rgba(255,51,0,.1);--color-bg-warning-pastel:rgba(255,150,0,.1);--color-background-button-primary:var(--color-brand-primary-dark);--color-background-button-primary-hover:var(--color-brand-primary);--color-background-button-primary-pastel:var(--color-brand-primary-pastel-dark);--color-background-button-primary-pastel-hover:var(--color-brand-primary-pastel-light);--color-text-button-primary-pastel:var(--color-text-light-emphasis);--color-text-button-primary-pastel-hover:var(--color-brand-primary-pastel-dark);--color-background-button-secondary:var(--color-brand-secondary-dark);--color-background-button-secondary-hover:var(--color-brand-secondary);--color-background-button-secondary-pastel:var(--color-brand-secondary-pastel-dark);--color-background-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-light);--color-text-button-secondary-pastel:var(--color-text-light-emphasis);--color-text-button-secondary-pastel-hover:var(--color-brand-secondary-pastel-dark);--color-background-button-success:var(--color-status-success-dark);--color-background-button-success-hover:var(--color-status-success);--color-background-button-success-pastel:var(--color-status-success-pastel-dark);--color-background-button-success-pastel-hover:var(--color-status-success-pastel-light);--color-text-button-success-pastel:var(--color-text-light-emphasis);--color-text-button-success-pastel-hover:var(--color-status-success-pastel-dark);--color-background-button-warning:var(--color-status-warning-dark);--color-background-button-warning-hover:var(--color-status-warning);--color-background-button-warning-pastel:var(--color-status-warning-pastel-dark);--color-background-button-warning-pastel-hover:var(--color-status-warning-pastel-light);--color-text-button-warning-pastel:var(--color-text-light-emphasis);--color-text-button-warning-pastel-hover:var(--color-status-warning-pastel-dark);--color-background-button-danger:var(--color-status-danger-dark);--color-background-button-danger-hover:var(--color-status-danger);--color-background-button-danger-pastel:var(--color-status-danger-pastel-dark);--color-background-button-danger-pastel-hover:var(--color-status-danger-pastel-light);--color-text-button-danger-pastel:var(--color-text-light-emphasis);--color-text-button-danger-pastel-hover:var(--color-status-danger-pastel-dark);--color-background-choose-shake-link:var(--color-background-button-success);--color-background-choose-shake-link-hover:var(--color-background-button-success-pastel-hover);--color-text-choose-shake-link:var(--color-text-light-emphasis);--color-text-choose-shake-link-hover:var(--color-text-button-success-pastel-hover);background-color:var(--color-background-page);color:var(--color-page-text)}*,:after,:before{box-sizing:border-box}body{background-color:var(--color-background-page);color:var(--color-page-text)}:focus{box-shadow:0 0 0 .25rem var(--color-border-focus-ring);outline:0}a{color:var(--color-text-link);text-decoration-color:var(--color-text-link-underline);transition:background-color var(--time-speed-quick) ease,color var(--time-speed-quick) ease}@media screen and (prefers-reduced-motion:reduce){a{transition:none}}a:active,a:focus,a:hover{color:var(--color-text-link-hover);text-decoration-color:var(--color-text-link-underline-hover)}.link--primary{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.link--primary:active,.link--primary:focus,.link--primary:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.link--primary:active,.link--primary:focus,.link--primary:hover{text-decoration:underline}.link--danger{color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.link--danger:active,.link--danger:focus,.link--danger:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}img,video{height:auto}iframe,img,video{max-width:100%}.data-wrapper,[style*="--aspect-ratio"]{position:relative}.data-wrapper:before,[style*="--aspect-ratio"]:before{content:"";display:block;padding-bottom:calc(100%*var(--aspect-ratio))}.data-wrapper>:first-child,[style*="--aspect-ratio"]>:first-child{height:100%;left:0;position:absolute;top:0;width:100%}.data-wrapper:before{padding-bottom:56.25%}html{font-family:var(--font-family-system);font-size:var(--size-font-base);font-weight:var(--number-font-weight-normal);letter-spacing:var(--letter-spacing);line-height:1.3}h1,h2,h3,h4,h5,h6{line-height:1.15;margin:0}.u-display,h1{font-size:3rem;letter-spacing:-1px}@media screen and (min-width:768px){.u-display,h1{font-size:3.75rem}}b,h1,h2,h3,h4,h5,h6,label,legend,strong,th{font-weight:var(--number-font-weight-bold)}.code,code,pre{font-family:var(--font-family-mono)}pre{overflow-x:auto;width:100%}.sr-only{clip:rect(0 0 0 0);border:0;clip-path:polygon(0 0,0 0,0 0);-webkit-clip-path:polygon(0 0,0 0,0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;white-space:nowrap;width:1px}.clear{clear:both}@media screen and (max-width:480px){.hide-on-small{display:none}}.danger,.error{color:var(--color-status-danger)}.user-name{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.avatar--img{display:block;height:var(--size-avatar-default);max-width:none;object-fit:cover;width:var(--size-avatar-default)}.caret{border:.5em solid transparent;border-top-color:currentcolor;border-width:.5em .433em 0;display:inline-block;position:relative;top:-.1em;transform-origin:center 40%;transition:transform .25s ease}@media screen and (prefers-reduced-motion:reduce){.caret{transition:none}}.is-active .caret{transform:rotate(180deg)}.panel--example{margin:.25em 0 1em}.page{background-color:var(--color-background-page);padding:0 .5em;position:relative}.wrapper{display:flow-root;margin:0 auto;max-width:960px}.content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-triple);margin-top:var(--size-spacing-half-again)}.content:after{clear:both;content:"";display:table}.content-narrow,.content-styleguide{margin:var(--size-spacing-quadruple) auto var(--size-spacing-triple);max-width:700px;padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-narrow,.content-styleguide{padding:var(--size-spacing-quadruple)}}.content-narrow p,.content-styleguide p{font-size:1.625rem;line-height:2.5;margin:2em 0}.content-narrow .extra-info p,.content-styleguide .extra-info p{color:var(--color-page-text-secondary);font-size:1rem;line-height:1.4}.content-styleguide{max-width:none}.content-with-sidebar{display:flex;flex-direction:column}@media screen and (min-width:768px){.content-with-sidebar{flex-flow:row wrap}}.content-with-sidebar>*{min-width:0}.content-with-sidebar .header{flex:0 0 100%}.content-with-sidebar .body{flex:1;padding-right:1px}.content-with-sidebar .sidebar{background:linear-gradient(to top,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));order:2;padding:var(--size-spacing-double)}@media screen and (min-width:768px){.content-with-sidebar .sidebar{background:linear-gradient(to right,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));flex:0 0 325px;order:0}.content-with-sidebar-reversed .sidebar{background:linear-gradient(to left,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));order:2}}.alert{background:var(--color-background-content-secondary);font-size:.9rem;line-height:1.25;padding:var(--size-spacing-half-again)}.alert p{margin:0}.alert a{font-weight:var(--number-font-weight-bold)}.alert--marquee{padding:0}.alert--marquee marquee{padding:var(--size-spacing-half-again)}.alert--warning{background:var(--color-bg-warning-pastel);color:var(--color-status-warning)}.alert--danger{background:var(--color-bg-danger-pastel);color:var(--color-status-danger)}.alert--danger a{color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.alert--danger a:active,.alert--danger a:focus,.alert--danger a:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}.migration-reminder{background:var(--color-background-content-secondary);background:var(--color-bg-warning-pastel);color:var(--color-status-warning);font-size:.9rem;font-weight:var(--number-font-weight-bold);line-height:1.25;padding:var(--size-spacing-half-again)}.migration-reminder p{margin:0}.migration-reminder a{font-weight:var(--number-font-weight-bold)}.bookmark{align-items:flex-start;display:flex;flex-wrap:wrap}.bookmark-flag{background:var(--color-brand-primary);color:var(--color-text-light-emphasis);flex:none;font-size:.75rem;font-weight:var(--number-font-weight-bold);margin-top:-3px;position:relative;z-index:2}.bookmark-flag:after,.bookmark-flag:before{content:"";display:block;position:absolute}.bookmark-flag:before{background:inherit;border-radius:var(--size-border-radius-default) 0 0 var(--size-border-radius-default);height:calc(100% + var(--size-spacing-half));left:calc(var(--size-spacing-half)*-1);padding-bottom:var(--size-spacing-half);top:0;width:var(--size-spacing-half)}.bookmark-flag:after{background:var(--color-base-black-transparent-300);border-radius:3px 0 0 3px;bottom:-4px;height:4px;left:-3px;width:3px}.bookmark-flag--content{display:block;line-height:1;padding:.5em 1em}.bookmark-flag--content:after{border-width:1em;border-bottom:1em solid var(--color-brand-primary);border-left:0 solid var(--color-brand-primary);border-right:.866em solid transparent;border-top:1em solid var(--color-brand-primary);content:"";display:block;position:absolute;right:-.833em;top:0}.jump-back{flex:1;font-size:.6875rem;margin-left:.75em;padding:calc(.5em - 3px) 0 .5em .75em;white-space:nowrap}.jump-back a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.jump-back a:active,.jump-back a:focus,.jump-back a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.jump-back a:active,.jump-back a:focus,.jump-back a:hover{text-decoration:underline}button{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.btn,.label{background-color:var(--color-base-gray-700);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);display:inline-block;font-size:18px;font-weight:500;height:2.2em;letter-spacing:.033em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.btn{cursor:pointer}.btn:focus,.btn:hover{color:var(--color-text-light-emphasis)}.btn:focus{outline:none}.btn:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.btn-pastel,.label-pastel{font-weight:600;letter-spacing:.015em;text-shadow:none}.btn-pastel:disabled,.label-pastel:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.btn-shadow{box-shadow:.22em .22em 0 var(--color-base-black-transparent-100);margin-right:.22em}.btn-shadow:disabled{box-shadow:none!important}.btn-padded{height:2.6em;line-height:2.6em;padding:0 1.25em}.btn-large,.label-large{font-size:24px;letter-spacing:.066em}.btn-large.btn-pastel,.label-large.label-pastel{letter-spacing:.033em}.btn-small,.label-small{font-size:12px;font-weight:400}.btn-tiny,.label-tiny{font-size:10px;font-weight:400;padding:0 .66em}.btn-small.btn-pastel,.btn-tiny.btn-pastel,.label-small.label-pastel,.label-tiny.label-pastel{font-weight:500}.btn-icon{padding:0;width:2.2em}.btn-icon.btn-padded{padding:0;width:2.6em}.btn-primary,.label-primary{background-color:var(--color-background-button-primary)}.btn-primary:focus,.btn-primary:hover{background-color:var(--color-background-button-primary-hover)}.btn-primary.btn-pastel,.label-primary.label-pastel{background-color:var(--color-background-button-primary-pastel);color:var(--color-text-button-primary-pastel)}.btn-primary.btn-pastel:focus,.btn-primary.btn-pastel:hover{background-color:var(--color-background-button-primary-pastel-hover);color:var(--color-text-button-primary-pastel-hover)}.btn-secondary,.label-secondary{background-color:var(--color-background-button-secondary)}.btn-secondary:focus,.btn-secondary:hover{background-color:var(--color-background-button-secondary-hover)}.btn-secondary.btn-pastel,.label-secondary.label-pastel{background-color:var(--color-background-button-secondary-pastel);color:var(--color-text-button-secondary-pastel)}.btn-secondary.btn-pastel:focus,.btn-secondary.btn-pastel:hover{background-color:var(--color-background-button-secondary-pastel-hover);color:var(--color-text-button-secondary-pastel-hover)}.btn-success,.label-success{background-color:var(--color-background-button-success)}.btn-success:focus,.btn-success:hover{background-color:var(--color-background-button-success-hover)}.btn-success.btn-pastel,.label-success.label-pastel{background-color:var(--color-background-button-success-pastel);color:var(--color-text-button-success-pastel)}.btn-success.btn-pastel:focus,.btn-success.btn-pastel:hover{background-color:var(--color-background-button-success-pastel-hover);color:var(--color-text-button-success-pastel-hover)}.btn-warning,.label-warning{background-color:var(--color-background-button-warning)}.btn-warning:focus,.btn-warning:hover{background-color:var(--color-background-button-warning-hover)}.btn-warning.btn-pastel,.label-warning.label-pastel{background-color:var(--color-background-button-warning-pastel);color:var(--color-text-button-warning-pastel)}.btn-warning.btn-pastel:focus,.btn-warning.btn-pastel:hover{background-color:var(--color-background-button-warning-pastel-hover);color:var(--color-text-button-warning-pastel-hover)}.btn-danger,.label-danger{background-color:var(--color-background-button-danger)}.btn-danger:focus,.btn-danger:hover{background-color:var(--color-background-button-danger-hover)}.btn-danger.btn-pastel,.label-danger.label-pastel{background-color:var(--color-background-button-danger-pastel);color:var(--color-text-button-danger-pastel)}.btn-danger.btn-pastel:focus,.btn-danger.btn-pastel:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.choose-a-shake{position:relative;text-align:left;z-index:3}.choose-a-shake--toggle{width:100%}.choose-a-shake.is-expanded .choose-a-shake--toggle{border-bottom-left-radius:0;border-bottom-right-radius:0;box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);outline:none}.choose-a-shake.is-expanded .choose-a-shake--toggle:focus,.choose-a-shake.is-expanded .choose-a-shake--toggle:hover{background-color:var(--color-background-button-success-pastel);color:var(--color-text-button-success-pastel)}.choose-a-shake.is-expanded .choose-a-shake--toggle .caret{transform:rotate(180deg)}.choose-a-shake--dropdown{background-color:var(--color-background-button-success-pastel);border-radius:var(--size-border-radius-large);border-top-right-radius:0;box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);display:none;max-width:400px;padding:var(--size-spacing-default);padding-top:1px;position:absolute;right:0;top:calc(100% - .22em)}@media screen and (min-width:768px){.choose-a-shake--dropdown{border-top-left-radius:0;border-top-right-radius:var(--size-border-radius-large);left:0;right:auto}}.choose-a-shake.is-expanded .choose-a-shake--dropdown{display:block}.choose-a-shake--dropdown .add-a-shake{display:block;float:right;margin-top:var(--size-spacing-default)}.choose-a-shake--dropdown ul{list-style:none;margin:0;padding:0}.choose-a-shake--dropdown ul li a{background-color:var(--color-background-choose-shake-link);border-radius:var(--size-border-radius-large);color:var(--color-text-choose-shake-link);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow:hidden;padding:var(--size-spacing-default) var(--size-spacing-half-again);text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.choose-a-shake--dropdown ul li a:active,.choose-a-shake--dropdown ul li a:focus,.choose-a-shake--dropdown ul li a:hover{text-decoration:underline}.choose-a-shake--dropdown ul li a:active,.choose-a-shake--dropdown ul li a:focus,.choose-a-shake--dropdown ul li a:hover{background-color:var(--color-background-choose-shake-link-hover);color:var(--color-text-choose-shake-link-hover);text-decoration:none}.choose-a-shake--dropdown ul.top-shakes li a{margin:var(--size-spacing-default) 0}.choose-a-shake--dropdown ul.group-shakes{background-color:var(--color-background-choose-shake-link);border-radius:var(--size-border-radius-large)}.conversations-nav h3{color:var(--color-page-text);font-size:1.125rem;margin-bottom:var(--size-spacing-half-again);margin-top:var(--size-spacing-triple)}.conversations-nav ul{list-style:none;margin:0;padding:0}.conversations-nav li{margin:var(--size-spacing-half) 0}.conversations-nav li.selected a{color:var(--color-status-disabled)}.conversations-nav a{color:var(--color-brand-primary);font-size:.875rem;font-weight:var(--number-font-weight-bold);text-decoration:none}.conversation{border-bottom:1px dotted var(--color-border-default);display:flex;margin-top:var(--size-spacing-triple)}.mentions .conversation{padding-left:var(--size-spacing-triple);padding-right:var(--size-spacing-default)}.conversation .thumb{flex:none;margin-left:var(--size-spacing-default);width:50px}@media screen and (min-width:480px){.conversation .thumb{margin-left:var(--size-spacing-triple);width:100px}}.conversation .details-wrapper{flex:1;margin-left:var(--size-spacing-default);margin-right:var(--size-spacing-default);min-width:0}.conversation .sharedfile-title{color:var(--color-page-text-emphasis);font-size:1.875rem;margin-bottom:var(--size-spacing-default)}.conversation .sharedfile-description,.conversation .sharedfile-title{word-wrap:break-word;overflow:hidden;overflow-wrap:break-word;word-break:break-word}.conversation .sharedfile-description{font-size:.875rem;line-height:1.3}.conversation .image-comments{padding-left:0}.conversation .image-comments .body{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.conversation .conversation-meta{display:flex;flex-wrap:wrap;margin-bottom:var(--size-spacing-triple);margin-top:var(--size-spacing-half-again)}.conversation .mute-this-conversation,.conversation .post-a-comment{margin:var(--size-spacing-half) 1em 0 0}.conversation .mute-this-conversation-form{display:none}.error-uh-oh{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 542 257'%3E%3Cg fill='none' transform='translate(-.003 .989)'%3E%3Cpath fill='%239ABD00' d='M68.513.091a164.45 164.45 0 0 0-21.64 2.43l13.3 2.09 8.34-4.52Z'/%3E%3Cpath fill='%23EBCF60' d='M92.823 203.091c-6-19.49-6.64-45.6-6.64-45.6l-11.07-3.42-.08-.78-.87.49-.95-.3.08.78-10.12 5.65s4.84 25.66 3 46c0 0-2.12 24.9 15.84 22.53 18.06-1.43 10.81-25.35 10.81-25.35Z'/%3E%3Cpath fill='%23FFE77C' d='M89.273 203.471c-6-19.49-6.63-45.6-6.63-45.6l-11.07-3.42-.08-.78-.86.49-1-.29.08.78-10.12 5.65s4.84 25.66 3 46c0 0-2.12 24.9 15.84 22.53 18.1-1.45 10.84-25.36 10.84-25.36Z'/%3E%3Cpath fill='%239ABD00' d='M85.053 2.501c1.06.39-6.24 4.31-6.25 6.47-.06 8.57 2.39 25.44 6.12 28.43 9.72 7.8 12.78 6.71 31.17 13.5 1.32.49 10.45-1 11 .63a94 94 0 0 1 5.54 24.22c4.74 45-4.85 82.87-48.14 87.43-42.39 4.46-60.49-30.51-65.25-75.49-4.76-44.98 16.76-84.14 48.13-87.44a37 37 0 0 1 17.68 2.25Z'/%3E%3Cpath fill='%239ABD00' d='M85.663 163.011a165.47 165.47 0 0 1-21.66 2.1l12.57-4.82 9.09 2.72Z'/%3E%3Cpath fill='%23B3D700' d='M114.083 77.701c4.74 45-4.85 82.87-48.14 87.43-42.43 4.47-60.55-30.5-65.29-75.49-4.74-44.99 16.82-84.13 48.14-87.43 31.32-3.3 60.55 30.5 65.29 75.49Z'/%3E%3Cpath fill='%23DEED8D' d='M68.003 5.081s-3.93 10.48 4.67 15.61c0 0-3.13 11.73 6 15.81 0 0-.59 13.48 13.34 12 0 0 3.21 11.41 16.19 4 0 0 3.84-1.75 12-.59l3.91-1.08h3s-15.1-.09-17.39-5.88c0 0-8.54-1.45-7.85-13.94 0 0-9.38-3-8.92-11.47 0 0-15.3-5.1-7.83-17l-7.37.1-9.75 2.44Z'/%3E%3Cpath fill='%23B3D700' d='m91.383 242.251 11.12-4 7.12 4.89-6.67 1.78-5.34-.88-8.45 2.22 2.22-4.01Zm33.81-1.77 4-.45 5.34 3.11-4.89.45-4.45-3.11Zm-17.35 12.01 8.01-3.12 1.33 5.79-4.89.44-4.45-3.11Z'/%3E%3Cpath fill='%23FDEFA2' d='M90.673 53.061s-6.35 20.28 2.62 19.86c0 0 9.49-.46-2.62-19.86Z'/%3E%3Cpath fill='%23FFE77C' d='M46.603 74.291s1.67 10.85-15.27 14.47c0 0-17.1 2.06-15.23 12.36 0 0-4.9-9.19 10.08-13.92 0 0 4.1-1 7.7-1.6 0 .01 11.34-1.97 12.72-11.31Zm17.13-1.8s.63 11 18 11c0 0 17.16-1.54 17.47 8.92 0 0 2.88-10-12.76-11.52 0 0-4.21-.08-7.87 0-.04.02-11.57.44-14.84-8.4Z'/%3E%3Cellipse cx='37.194' cy='108.132' fill='%23FFF' rx='10.67' ry='10.97' transform='rotate(-8.11 37.194 108.132)'/%3E%3Cellipse cx='37.864' cy='106.336' fill='%23000' rx='5.14' ry='5.29' transform='rotate(-8.11 37.864 106.336)'/%3E%3Cellipse cx='64.204' cy='130.129' fill='%23E6231E' rx='6.5' ry='7.65' transform='rotate(-11.19 64.204 130.129)'/%3E%3Cellipse cx='82.063' cy='101.736' fill='%23FFF' rx='10.67' ry='10.97' transform='rotate(-8.11 82.063 101.736)'/%3E%3Cellipse cx='82.732' cy='99.941' fill='%23000' rx='5.14' ry='5.29' transform='rotate(-8.11 82.732 99.941)'/%3E%3Cellipse cx='38.801' cy='103.196' fill='%23FFF' rx='1.32' ry='1' transform='rotate(-8.11 38.801 103.196)'/%3E%3Cellipse cx='83.932' cy='96.754' fill='%23FFF' rx='1.32' ry='1' transform='rotate(-8.11 83.932 96.754)'/%3E%3Cpath fill='%23FF0' d='M76.003 129.931s66.31-14 66.31-40 5.62-64 41.86-64c10.67 0 41-7.41 105.24-9 154-3.81 200.83-4 200.83-4s51 10 51 48 6 116-51 116-91.83 5-144.83 5c-53 0-147.5 6.5-154 0-3.36-3.36-11.86-3.3-23.18-13-10.56-9-24.1-35-24.1-35s-12.08 8.46-32.37 7c-27.87-2-35.76-11-35.76-11Z'/%3E%3Cpath fill='%23FF0180' d='m193.293 118.581-2-46.6 15.83-.67 2 46.6c.41 9.56 6.7 12.71 12.31 12.47 5.61-.24 11.5-3.9 11.1-13.46l-2-46.6 15.83-.67 2 46.6c.72 17-9.91 28.5-26.29 29.19-16.38.69-28.07-9.83-28.78-26.86Zm61.33-49.28 14.07-.6 1.16 27.25a17.33 17.33 0 0 1 8.15-2.32c11.87-.5 20.12 7.18 20.71 21.14l1.12 26.38-14.18.6-1.12-26.38c-.22-5.17-2.36-8.82-7.3-8.61-4.94.21-6.76 4.25-6.55 9.2l1.12 26.38-14.07.6-3.11-73.64Zm50.55 36.83 24-1 .52 12.2-24 1-.52-12.2Zm67.22-42.82a37.84 37.84 0 1 1-36.2 39.41c-.886-20.878 15.321-38.523 36.2-39.41Zm2.53 59.79c12.133-.517 21.552-10.768 21.043-22.902-.509-12.133-10.753-21.559-22.887-21.059-12.133.5-21.567 10.737-21.076 22.871.541 12.137 10.78 21.559 22.92 21.09Zm42.46-60.7 14.07-.6 1.15 27.21a17.32 17.32 0 0 1 8.16-2.33c11.87-.5 20.12 7.18 20.71 21.14l1.12 26.38-14.18.6-1.12-26.38c-.22-5.17-2.36-8.82-7.3-8.61-4.94.21-6.76 4.25-6.55 9.2l1.12 26.38-14.07.6-3.11-73.59Zm53.69 49.39-2.47-58.27 16.18-.69 2.47 58.27-16.18.69Zm8.34 4.09a9.36 9.36 0 0 1 .79 18.7 9.36 9.36 0 0 1-.79-18.7Z'/%3E%3C/g%3E%3C/svg%3E");background-position:top;background-repeat:no-repeat;background-size:282px 133px;font-size:2.25rem;font-weight:var(--number-font-weight-bold);padding-top:160px;text-align:center}.error-p{color:var(--color-page-text-secondary);text-align:center}.error-p.error-p-long{text-align:left}.content-narrow .error-p{font-size:1.125rem;line-height:1.4}@media screen and (min-width:480px){.feature-list{display:flex;flex-wrap:wrap}}@media screen and (min-width:768px){.feature-list{flex-wrap:nowrap}}@media screen and (min-width:960px){.feature-list{flex-wrap:wrap}}.feature{border-top:1px dashed var(--color-border-default);margin:0;padding:var(--size-spacing-half-again);position:relative}@media screen and (min-width:480px){.feature{border-left:1px dashed var(--color-border-default);flex:1 1 50%}}@media screen and (min-width:768px){.feature{flex-basis:25%;padding:calc(var(--size-spacing-default)*2.5)}}@media screen and (min-width:960px){.feature{display:flex;flex-basis:50%;flex-direction:row-reverse;padding:calc(var(--size-spacing-default)*3.5)}}@media screen and (min-width:768px){.feature:first-child{border-left:none}}@media screen and (min-width:480px) and (max-width:767px){.feature:nth-child(2n-1){border-left:none}}@media screen and (min-width:960px){.feature:nth-child(2n-1){border-left:none}}.feature>*+*{margin-top:var(--size-spacing-half-again)}@media screen and (min-width:768px){.feature>*+*{margin-top:calc(var(--size-spacing-default)*2.5)}}@media screen and (min-width:960px){.feature>*+*{margin-right:var(--size-spacing-double);margin-top:0}}@media screen and (min-width:960px){.feature--content{flex:1 1 auto}.feature--image{flex:0 0 163px}}.feature--image-media{border:1px solid var(--color-border-default);box-shadow:3px 3px 0 var(--color-base-black-transparent-100);display:block;margin:0 auto}.feature--title{color:var(--color-page-text-emphasis);font-size:1.375rem;margin:0;text-align:center}@media screen and (min-width:768px){.feature--title{text-align:left}}.feature--body{color:var(--color-page-text);font-size:.875rem;line-height:1.25;margin:var(--size-spacing-default) 0 0}.feature--body p{margin:0}.feature--body p+p{margin-top:1.25em}.feature--cta{text-align:center}.feature--finale .feature--image-media,.feature--primary .feature--image-media{border:none;box-shadow:none}.feature--primary{border:none}@media screen and (min-width:960px){.feature--primary>*{flex:1}}@media screen and (min-width:960px){.feature--primary>*+*{margin-right:70px}}.feature--primary .feature--title{font-size:2.625rem;letter-spacing:-3px;line-height:90%}@media screen and (min-width:768px){.feature--primary .feature--title{font-size:3.75rem}}@media screen and (min-width:960px){.feature--primary .feature--title{font-size:4.5rem}}.feature--primary .feature--body{color:var(--color-page-text-secondary);margin-top:1.25em}.feature--finale{align-items:center}@media screen and (min-width:960px){.feature--finale{padding-left:150px;padding-right:150px}}.feature--finale .feature--image{flex:none}@media screen and (min-width:960px){.feature--flipped{flex-direction:row}}@media screen and (min-width:960px){.feature--flipped>*+*{margin-left:var(--size-spacing-double);margin-right:0}}@media screen and (min-width:960px){.feature--flipped.feature--primary>*+*{margin-left:70px}}button,input,select,textarea{font-family:var(--font-family-system);font-size:var(--size-font-input)}select{width:100%}label,textarea{display:block}textarea{height:8em;padding:.5em;resize:vertical;width:100%}.fun-form{margin-top:var(--size-spacing-double)}.fun-form .field{display:flex;flex-direction:column;position:relative}@media screen and (min-width:768px){.fun-form .field{align-items:center;flex-flow:row wrap}}.fun-form .field+.field{margin-top:var(--size-spacing-double)}.fun-form label{flex:none;font-size:1.125rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-half)}@media screen and (min-width:768px){.fun-form label{flex-basis:190px;margin-bottom:0;margin-right:var(--size-spacing-default);text-align:right}}.fun-form .field-input{flex:1}.fun-form .field-help,.fun-form .field-submit{display:block}@media screen and (min-width:768px){.fun-form .field-help,.fun-form .field-submit{padding-left:200px}}.fun-form .field-help{color:var(--color-page-text-secondary);font-size:.875rem;margin-top:var(--size-spacing-half)}@media screen and (min-width:768px){.fun-form .field-help{flex-basis:100%}}.fun-form .input-text,.fun-form textarea{appearance:none;background:var(--color-form-bg);border-radius:var(--size-border-radius-large);border-width:1px;border-bottom:1px solid var(--color-border-form);border-left:4px solid var(--color-border-form);border-right:1px solid var(--color-border-form);border-top:4px solid var(--color-border-form);color:var(--color-page-text);display:block;font-size:1.125rem;padding:var(--size-spacing-default) var(--size-spacing-half-again);width:100%}.fun-form [type=checkbox],.fun-form [type=radio]{margin-right:.5em}.fun-form .name-prefix{color:var(--color-page-text-secondary);margin-right:var(--size-spacing-half)}.fun-form .error{background:var(--color-form-error-bubble-bg);border-radius:33% 25px;color:var(--color-form-error-bubble-text);display:block;font-size:.75rem;padding:1em 1.25em;position:absolute;right:calc(var(--size-spacing-half-again)*-1);text-align:center;top:25%;transform:translateY(-50%);width:115px;z-index:1}@media screen and (min-width:960px){.fun-form .error{right:-160px}}.fun-form .error:before{background:var(--color-form-error-bubble-bg);bottom:calc(50% - 15px);content:"";display:block;height:22px;left:-48px;-webkit-mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 52.33 22.01'%3E%3Cg data-name='Layer 2'%3E%3Cpath fill='%23aaf3ff' d='M52.33 0a73.44 73.44 0 0 1-22 13.35C17.66 18 0 19.35 0 19.35S6.33 22 24.5 22s27.83-6 27.83-6Z' data-name='Layer 1'/%3E%3C/g%3E%3C/svg%3E");mask-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 52.33 22.01'%3E%3Cg data-name='Layer 2'%3E%3Cpath fill='%23aaf3ff' d='M52.33 0a73.44 73.44 0 0 1-22 13.35C17.66 18 0 19.35 0 19.35S6.33 22 24.5 22s27.83-6 27.83-6Z' data-name='Layer 1'/%3E%3C/g%3E%3C/svg%3E");position:absolute;width:52px}.fun-form-stacked .field{display:block}.fun-form-stacked label{margin-bottom:var(--size-spacing-half);margin-right:0;text-align:left}.fun-form-stacked .field-help,.fun-form-stacked .field-submit{padding-left:0}.fun-form-stacked #create-shake-name-field,.fun-form-stacked .field-prefix{display:flex}.fun-form-stacked #create-shake-name-field label,.fun-form-stacked .field-prefix label{flex-basis:100%}.fun-form-stacked .error{top:3em}.fun-form-errors{color:var(--color-status-danger);font-size:1.125rem}.image-comments{max-width:100%;overflow:hidden;padding-left:var(--size-spacing-quadruple)}.image-comments .comments{clear:both}.image-comments .comment{display:flex}.image-comments .comment+.comment{margin-top:var(--size-spacing-double)}.image-comments .comment .avatar{margin-right:var(--size-spacing-default);width:var(--size-avatar-default)}.image-comments .comment .avatar a{display:block}.image-comments .comment .body{flex:1;font-size:.875rem;line-height:1.3;overflow:hidden}.image-comments .comment .body .where-from{color:var(--color-page-text-secondary);font-size:.825em;padding-top:var(--size-spacing-default)}.image-comments .comment .comment-body-text{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-comments .comment .meta{align-items:flex-end;display:flex;flex-wrap:wrap;font-size:.825em;padding-bottom:var(--size-spacing-default)}.image-comments .comment .meta>*+*{margin-left:.825em}.image-comments .comment .meta .user-name,.image-comments .comment .meta .username{font-size:.875rem;font-weight:var(--number-font-weight-bold)}.image-comments .comment .meta .pro-badge{margin-left:.25em}.image-comments .comment .meta .reply-to{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-279 425.6 11.8 11.4'%3E%3Cpath fill='none' d='M-259 357.4h32.1'/%3E%3Cpath fill='%2300aeff' d='M-273.2 426.2v5.6c0 .4-.3.6-.6.7-.1 0-.3 0-.4-.1l-4.5-2.8c-.3-.2-.4-.6-.2-.9l.2-.2 4.5-2.8c.3-.2.7 0 .9.3 0 .1.1.1.1.2z'/%3E%3Cpath fill='%2300aeff' d='M-272.2 430.6c1.8.6 2.8 2.5 2.3 4.3v.1c-.1.2 0 .4.1.6l1.1 1.2c.2.2.5.2.7.1 0 0 .1-.1.1-.2 1.6-2.9.5-6.6-2.4-8.1-.9-.5-1.9-.7-2.9-.7l-.9 2.7c.6-.2 1.2-.2 1.9 0z'/%3E%3C/svg%3E");background-size:12px 11px}.image-comments .comment .meta .delete,.image-comments .comment .meta .reply-to{background-position:0 0;background-repeat:no-repeat;display:none;padding-left:var(--size-spacing-half-again)}.image-comments .comment .meta .delete{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 13 13'%3E%3Crect width='13' height='13' fill='%23cecec6' rx='2.58' ry='2.58'/%3E%3Cpath fill='%238d8d87' d='m3.78 8.91 1.87-2.67-1.79-2.52h1.67l.92 1.43 1-1.43H9L7.27 6.18l1.92 2.73H7.5L6.45 7.3 5.38 8.91z'/%3E%3C/svg%3E");background-size:13px 13px}.image-comments .comment .meta .created-at{color:var(--color-page-text-secondary)}.image-comments .comment:focus .meta .reply-to,.image-comments .comment:hover .meta .reply-to{display:inline}.image-comments .comment:focus .meta form,.image-comments .comment:hover .meta form{display:none}.image-comments .comment:focus .meta .delete,.image-comments .comment:hover .meta .delete{display:inline}.image-comment-form{margin-top:var(--size-spacing-triple);padding-left:var(--size-spacing-quadruple)}.image-comment-form header{display:flex}.image-comment-form .avatar{flex:none}.image-comment-form h3{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 45'%3E%3Cg data-name='Layer 2'%3E%3Cpath fill='%23ffb400' d='M0 26.33S2.25 27 7.54 25a16.47 16.47 0 0 0 7.54-5.33s-3.5-18.33 11-18.33S300 0 300 0v45H90.58s-58.5-1.92-64.25-4.17-7.22-6.45-7.22-6.45-7.53-.55-11.78-2S0 26.33 0 26.33z' data-name='Layer 1'/%3E%3C/g%3E%3C/svg%3E");background-position:0 0;background-repeat:no-repeat;background-size:auto var(--size-avatar-default);border-bottom-right-radius:15px;border-top-right-radius:25px 20px;display:block;left:calc(var(--size-spacing-default)*-1);position:relative}.image-comment-form h3 span{color:var(--color-text-light-emphasis);display:block;font-size:.75rem;height:var(--size-avatar-default);line-height:var(--size-avatar-default);padding-left:35px;padding-right:var(--size-spacing-double);text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);white-space:nowrap}@media screen and (min-width:375px){.image-comment-form h3 span{font-size:.875rem}}@media screen and (min-width:480px){.image-comment-form h3 span{font-size:1rem}}@media screen and (min-width:768px){.image-comment-form h3 span{font-size:1.125rem}}.image-comment-form .field{clear:both;margin:var(--size-spacing-double) 0;text-align:right}.image-comment-form textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);height:8em;padding:var(--size-spacing-half)}.image-medium{margin-right:var(--size-spacing-half-again);padding-bottom:var(--size-spacing-triple)}.image-medium .user-and-title{padding-top:var(--size-spacing-default)}.image-medium .user-and-title a{display:block;float:left;margin-right:var(--size-spacing-default)}.image-medium .user-and-title .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-medium .user-and-title .title{font-size:1.25rem;font-weight:var(--number-font-weight-bold);overflow:hidden}.image-medium .stats{clear:both;color:var(--color-page-text-secondary);display:flex;list-style:none;margin:0;padding:var(--size-spacing-default) 0 0}.image-medium .stats li{float:left;font-size:.75em;padding-right:var(--size-spacing-default)}.image-medium .stats li.saves{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 11.2 8'%3E%3Cg data-name='Layer 2'%3E%3Cpath fill='%23f79d1f' d='m4.69 8-.23-2.73c0-.36-.36-.88-1.3-.75A23.37 23.37 0 0 1 3.68 8h-.53a15.4 15.4 0 0 0-.78-3.68c-.26-.55-.22-.94.57-.73A7.84 7.84 0 0 0 4.15 4c.33 0 .67-.44 1.47-.43S6.75 4 7.08 4a7.63 7.63 0 0 0 1.17-.37c.78-.19.84.23.58.75A15.14 15.14 0 0 0 8.08 8Zm6.47-8-2 .14a1 1 0 0 0-.82.54l-.58 1.16a1.11 1.11 0 0 0-1.07-1h-.06A1.1 1.1 0 0 0 5.6 0a1.1 1.1 0 0 0-1 .84h-.2a1.1 1.1 0 0 0-1 .89L2.86.7A1 1 0 0 0 2 .16L0 0v.56l1.92.1A.52.52 0 0 1 2.4 1l.45 1.05a1 1 0 0 0-.43.77 1 1 0 0 1 .53 0 8.13 8.13 0 0 0 1.2.36c.33 0 .67-.42 1.47-.41s1.13.45 1.46.43a7.92 7.92 0 0 0 1.17-.3.92.92 0 0 1 .53 0 1.12 1.12 0 0 0-.51-.84s.45-.89.52-1a.58.58 0 0 1 .53-.41L11.2.53Z' data-name='Layer 1'/%3E%3C/g%3E%3C/svg%3E");background-position:0 2px;background-repeat:no-repeat;background-size:16px 11px;color:var(--color-status-warning);padding-left:var(--size-spacing-double)}.image-medium .stats li.likes{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 9 7.75'%3E%3Cpath fill='%23ff0200' d='M0 2.37a2.37 2.37 0 0 1 4.5-1 2.37 2.37 0 0 1 4.5 1c0 3.11-4.5 5.38-4.5 5.38S0 5.48 0 2.37'/%3E%3Cpath fill='%23fff' d='M5.81 4.87a.21.21 0 0 0-.28.07 1.19 1.19 0 0 1-2.06 0 .21.21 0 1 0-.36.21 1.6 1.6 0 0 0 2.77 0 .21.21 0 0 0-.07-.28zM2.22 2.65a.84.84 0 0 0 1.68 0 .84.84 0 1 0-1.68 0zm2.88 0a.84.84 0 0 0 1.68 0 .84.84 0 1 0-1.68 0z'/%3E%3C/svg%3E");color:var(--color-status-danger)}.image-medium .stats li.comments a,.image-medium .stats li.likes{background-position:0 2px;background-repeat:no-repeat;background-size:12px 9px;padding-left:var(--size-spacing-half-again)}.image-medium .stats li.comments a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 11.41 9'%3E%3Cpath fill='%2309f' d='M0 3.93a1.62 1.62 0 0 1 1.61-1.6A1.62 1.62 0 0 1 4 1.09a1.62 1.62 0 0 1 3.13.24 1.62 1.62 0 0 1 2.64 1.13 1.62 1.62 0 0 1 0 3.25 1.62 1.62 0 0 1-2.6 1.23 1.62 1.62 0 0 1-2.24.56C3.66 9 .28 9 .28 9c2.91-1.64 2.65-2.42 2.44-2.43-.88 0-1.16-.12-1.16-1A1.62 1.62 0 0 1 0 3.93z'/%3E%3C/svg%3E");color:var(--color-brand-secondary);text-decoration:none}.image-medium-thumb a{display:block}.image-medium-thumb img{text-align:center;vertical-align:middle}.image-title{max-width:100%;padding:var(--size-spacing-half-again) var(--size-spacing-half-again) 0;position:relative}@media screen and (min-width:768px){.image-title{padding:var(--size-spacing-quadruple) var(--size-spacing-quadruple) 0}}.image-title .image-poster{float:left;padding-right:var(--size-spacing-default)}.image-title .image-poster a{display:block}.image-title h1,.image-title h3{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-title h1{font-size:3rem}.image-title h3{font-size:1.875rem;margin-bottom:var(--size-spacing-double)}.image-title .remove-from-shake{height:21px;position:absolute;right:var(--size-spacing-default);top:var(--size-spacing-default);width:21px}@media screen and (min-width:768px){.image-title .remove-from-shake{top:calc(var(--size-spacing-quadruple) + var(--size-spacing-half))}}.image-edit-title-hover,.image-edit-title:focus,.image-edit-title:hover{background-color:var(--color-status-edit)}.image-edit-title-form{display:none;flex-direction:column}@media screen and (min-width:480px){.image-edit-title-form{flex-direction:row}}.image-edit-title-form.is-active{display:flex}.image-edit-title-form .title-input{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);flex:1;font-size:3rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-default);max-width:100%;min-width:0}@media screen and (min-width:480px){.image-edit-title-form .title-input{margin-bottom:0}}.image-content-list .image-edit-title-form .title-input{font-size:1.875rem;min-width:250px;padding:.0666em}.image-edit-title-form .buttons{align-items:center;display:flex;padding-left:var(--size-spacing-default)}.image-edit-title-form .or{color:var(--color-page-text-secondary);display:block;padding:0 var(--size-spacing-default)}.image-content-list .image-edit-title-form{margin-bottom:var(--size-spacing-double)}.image-content-list .image-edit-title-form .btn{font-size:12px;font-weight:400}.image-content{clear:both;max-width:100%;padding:var(--size-spacing-half-again);position:relative}@media screen and (min-width:768px){.image-content{padding:var(--size-spacing-half-again) var(--size-spacing-quadruple) var(--size-spacing-quadruple)}}.image-content .the-image a,.image-content .the-image img{display:block}.image-content img.unsized{height:auto;max-width:100%}.image-content-list .image-content{border-bottom:1px dashed var(--color-border-default);float:none;padding:0 var(--size-spacing-half-again) var(--size-spacing-half-again)}@media screen and (min-width:768px){.image-content-list .image-content{padding:0 var(--size-spacing-quadruple) var(--size-spacing-quadruple)}}.nsfw-cover{background-color:var(--color-base-gray-800);color:var(--color-text-light-emphasis);font-size:.875rem;font-weight:600;padding:var(--size-spacing-quadruple) 0;text-align:center}.nsfw-cover p{margin:var(--size-spacing-double) 0}.image-content .description{color:var(--color-page-text);font-size:.875rem;overflow:hidden;padding-bottom:var(--size-spacing-default)}.image-content .the-description{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.image-content .the-description.the-description-blank{color:var(--color-status-disabled);font-style:italic}.image-content .the-description a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.image-content .the-description a:active,.image-content .the-description a:focus,.image-content .the-description a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.image-content .description-edit .the-description:focus,.image-content .description-edit .the-description:hover,.image-content .the-description-hover{background-color:var(--color-status-edit)}.image-content .description-edit-form{display:none}.image-content .description-edit-form h3{margin-bottom:5px}.image-content .description-edit-form textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);min-height:100px;padding:var(--size-spacing-half)}.image-content .description-edit-form .buttons{display:flex;padding:var(--size-spacing-default) 0}.image-content .description-edit-form .or{color:var(--color-page-text-secondary);display:block;padding:var(--size-spacing-half) var(--size-spacing-default) var(--size-spacing-half-again) var(--size-spacing-default)}.image-content .alt-text{color:var(--color-page-text);font-size:.875rem;overflow:hidden;padding-bottom:var(--size-spacing-default)}.image-content .alt-text .the-alt-text{word-wrap:break-word;background:var(--color-background-content-secondary);color:var(--color-page-text-secondary);margin-top:var(--size-spacing-half);overflow-wrap:break-word;padding:var(--size-spacing-default) var(--size-spacing-half-again);word-break:break-word}.image-content .alt-text .the-alt-text:focus{box-shadow:none}.image-content .alt-text.alt-text--blank .the-alt-text{color:var(--color-status-disabled);font-style:italic}.image-content .alt-text.alt-text--blank .alt-text-toggle,.image-content .alt-text.alt-text--editing .alt-text-toggle,.image-content .alt-text.alt-text--editing .the-alt-text,.image-content .alt-text.alt-text--hidden .the-alt-text{display:none}.image-content .alt-text-edit .the-alt-text:hover,.image-content .the-alt-text-hover{background-color:var(--color-status-edit)}.image-content .alt-text--editing .alt-text-edit-form{display:block}.image-content .alt-text-edit-form{display:none}.image-content .alt-text-edit-form .alt-text-learn{margin-top:5px}.image-content .alt-text-edit-form textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text);min-height:100px;padding:var(--size-spacing-half)}.image-content .alt-text-edit-form .buttons{display:flex;padding:var(--size-spacing-default) 0}.image-content .alt-text-edit-form .or{color:var(--color-page-text-secondary);display:block;padding:var(--size-spacing-half) var(--size-spacing-default) var(--size-spacing-half-again) var(--size-spacing-default)}.image-content .alt-text-toggle{cursor:pointer}.image-interactions{align-items:center;display:flex;flex-direction:row-reverse;float:right;margin-bottom:4px}.image-interactions .save-this{margin-right:var(--size-spacing-half);position:relative}.image-interactions .like-button,.image-interactions .save-this-link{line-height:1}.image-interactions .like-button:focus,.image-interactions .save-this-link:focus{outline:none}.image-interactions .like-button .btn--content,.image-interactions .save-this-link .btn--content{align-items:center;display:flex}.image-interactions .like-button .btn--icon,.image-interactions .save-this-link .btn--icon{height:1.5em;margin-right:.33em}.image-interactions .like-button .btn--caret,.image-interactions .save-this-link .btn--caret{font-size:.75em;margin-left:.33em;vertical-align:bottom}.image-interactions .like-button,.image-interactions .unlike-button{display:none}.image-interactions .like-button.is-active,.image-interactions .unlike-button.is-active{display:inline-block}.image-interactions .unlike-button{background:none;border:none;cursor:pointer;padding:0}.image-interactions .unlike-button img{display:block}.image-interactions .save-this-shake-selector{background-color:var(--color-status-warning);border-radius:var(--size-border-radius-large);min-height:30px;padding:var(--size-spacing-half) 0;position:absolute;right:0;top:0;width:170px;z-index:1}.image-interactions .save-this-shake-selector ul{clear:both;list-style:none;margin:0;padding:0}.image-interactions .save-this-shake-selector a{word-wrap:break-word;border-radius:var(--size-border-radius-default);color:var(--color-text-light-emphasis);display:block;font-weight:500;margin:0 var(--size-spacing-half);overflow-wrap:break-word;padding:var(--size-spacing-half) var(--size-spacing-half);text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500)}.image-interactions .save-this-shake-selector a:focus,.image-interactions .save-this-shake-selector a:hover{background-color:var(--color-base-white-transparent-200)}.image-interactions .save-this-shake-selector .close{color:var(--color-text-light-emphasis);cursor:pointer;display:block;position:absolute;right:var(--size-spacing-default);top:var(--size-spacing-default);transform:rotate(180deg)}.image-interactions .save-this-shake-selector .close:focus,.image-interactions .save-this-shake-selector .close:hover{opacity:.66}.image-interactions .save-this-shake-selector .close:before{bottom:calc(var(--size-spacing-default)*-1);content:"";left:calc(var(--size-spacing-half-again)*-1);position:absolute;right:calc(var(--size-spacing-half-again)*-1);top:calc(var(--size-spacing-double)*-1)}.image-interactions .save-this-shake-selector-loading{min-height:100px}.image-interactions .save-this-shake-selector-loading:after{animation:load8 1.1s linear infinite;border:5px solid var(--color-base-white-transparent-200);border-left-color:var(--color-base-white-transparent-800);border-radius:50%;content:"";display:block;height:50px;margin:1em auto;transform:translateZ(0);width:50px}@media screen and (prefers-reduced-motion:reduce){.image-interactions .save-this-shake-selector-loading:after{animation:none}}@keyframes load8{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.image-content-footer{color:var(--color-page-text-secondary);font-size:.75rem;padding-top:var(--size-spacing-default)}.image-content-footer .originally-posted-by{clear:left;padding-bottom:var(--size-spacing-half)}.image-content-footer .originally-posted-by .avatar--link{display:inline-block;text-decoration:none;vertical-align:middle}.image-content-footer .originally-posted-by .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-content-footer .inline-meta{display:flex;float:left}.image-content-footer .inline-meta>*{flex:none}.image-content-footer .created-at{padding-top:var(--size-spacing-half)}.image-content-footer .created-at a{color:var(--color-page-text-secondary);text-decoration:none}.image-content-footer .created-at a:focus,.image-content-footer .created-at a:hover{color:var(--color-page-text);text-decoration:underline}.image-content-footer .stats{list-style:none;margin:0 0 0 var(--size-spacing-default);padding:0}.image-content-footer .stats li{float:left;padding-right:var(--size-spacing-half)}.image-content-footer .stats a{background-position:var(--size-spacing-half) .5em;background-repeat:no-repeat;background-size:1em 1em;border-top-left-radius:var(--size-border-radius-default);border-top-right-radius:var(--size-border-radius-default);display:block;float:left;padding:var(--size-spacing-half);padding-bottom:var(--size-spacing-default);padding-left:calc(var(--size-spacing-default) + 1em);text-decoration:none}.image-content-footer .stats a:focus,.image-content-footer .stats a:hover{background-color:var(--color-background-content-secondary)}.image-content-footer .stats .views{padding-top:var(--size-spacing-half)}.image-content-footer .stats .saves a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 11.2 8'%3E%3Cg data-name='Layer 2'%3E%3Cpath fill='%23f79d1f' d='m4.69 8-.23-2.73c0-.36-.36-.88-1.3-.75A23.37 23.37 0 0 1 3.68 8h-.53a15.4 15.4 0 0 0-.78-3.68c-.26-.55-.22-.94.57-.73A7.84 7.84 0 0 0 4.15 4c.33 0 .67-.44 1.47-.43S6.75 4 7.08 4a7.63 7.63 0 0 0 1.17-.37c.78-.19.84.23.58.75A15.14 15.14 0 0 0 8.08 8Zm6.47-8-2 .14a1 1 0 0 0-.82.54l-.58 1.16a1.11 1.11 0 0 0-1.07-1h-.06A1.1 1.1 0 0 0 5.6 0a1.1 1.1 0 0 0-1 .84h-.2a1.1 1.1 0 0 0-1 .89L2.86.7A1 1 0 0 0 2 .16L0 0v.56l1.92.1A.52.52 0 0 1 2.4 1l.45 1.05a1 1 0 0 0-.43.77 1 1 0 0 1 .53 0 8.13 8.13 0 0 0 1.2.36c.33 0 .67-.42 1.47-.41s1.13.45 1.46.43a7.92 7.92 0 0 0 1.17-.3.92.92 0 0 1 .53 0 1.12 1.12 0 0 0-.51-.84s.45-.89.52-1a.58.58 0 0 1 .53-.41L11.2.53Z' data-name='Layer 1'/%3E%3C/g%3E%3C/svg%3E");background-size:1.33em 1em;color:var(--color-status-warning);padding-left:calc(var(--size-spacing-default) + 1.33em)}.image-content-footer .stats .likes a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 9 7.75'%3E%3Cpath fill='%23ff0200' d='M0 2.37a2.37 2.37 0 0 1 4.5-1 2.37 2.37 0 0 1 4.5 1c0 3.11-4.5 5.38-4.5 5.38S0 5.48 0 2.37'/%3E%3Cpath fill='%23fff' d='M5.81 4.87a.21.21 0 0 0-.28.07 1.19 1.19 0 0 1-2.06 0 .21.21 0 1 0-.36.21 1.6 1.6 0 0 0 2.77 0 .21.21 0 0 0-.07-.28zM2.22 2.65a.84.84 0 0 0 1.68 0 .84.84 0 1 0-1.68 0zm2.88 0a.84.84 0 0 0 1.68 0 .84.84 0 1 0-1.68 0z'/%3E%3C/svg%3E");color:var(--color-status-danger)}.image-content-footer .stats .comments a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 11.41 9'%3E%3Cpath fill='%2309f' d='M0 3.93a1.62 1.62 0 0 1 1.61-1.6A1.62 1.62 0 0 1 4 1.09a1.62 1.62 0 0 1 3.13.24 1.62 1.62 0 0 1 2.64 1.13 1.62 1.62 0 0 1 0 3.25 1.62 1.62 0 0 1-2.6 1.23 1.62 1.62 0 0 1-2.24.56C3.66 9 .28 9 .28 9c2.91-1.64 2.65-2.42 2.44-2.43-.88 0-1.16-.12-1.16-1A1.62 1.62 0 0 1 0 3.93z'/%3E%3C/svg%3E")}.image-content .inline-details,.image-content-footer .stats .selected a{background-color:var(--color-background-content-secondary)}.image-content .inline-details{border-radius:var(--size-border-radius-default);clear:both;min-height:34px;padding:var(--size-spacing-default)}.image-content .inline-details .user-saves-likes{background-color:var(--color-background-content);min-height:var(--size-avatar-tiny);padding:var(--size-spacing-default)}.image-content .inline-details .user-saves-likes a{align-items:center;color:var(--color-text-link-primary);display:inline-flex;font-weight:var(--number-font-weight-bold);margin-right:var(--size-spacing-default);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.image-content .inline-details .user-saves-likes a:active,.image-content .inline-details .user-saves-likes a:focus,.image-content .inline-details .user-saves-likes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.image-content .inline-details .user-saves-likes a:active,.image-content .inline-details .user-saves-likes a:focus,.image-content .inline-details .user-saves-likes a:hover{text-decoration:underline}.image-content .inline-details .user-saves-likes img{margin-right:var(--size-spacing-half)}.image-content .inline-details .user-saves-likes .avatar--img{height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.image-content .inline-details .comment{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);display:flex;margin-bottom:var(--size-spacing-default);padding:var(--size-spacing-default)}.image-content .inline-details .comment .avatar{flex:none}.image-content .inline-details .comment .avatar a{display:block}.image-content .inline-details .comment .avatar--img{height:var(--size-avatar-small);width:var(--size-avatar-small)}.image-content .inline-details .comment .comment-body{flex:1;overflow:hidden;padding-left:var(--size-spacing-default)}.image-content .inline-details .comment .comment-body-text{word-wrap:break-word;clear:both;color:var(--color-page-text);font-size:.875rem;overflow-wrap:break-word;padding-top:.25em;word-break:break-word}.image-content .inline-details .comment .meta{align-items:flex-end;display:flex;flex-wrap:wrap}.image-content .inline-details .comment .meta>*+*{margin-left:.8em}.image-content .inline-details .comment .delete-form{display:none}.image-content .inline-details .comment .meta .username{font-weight:var(--number-font-weight-bold)}.image-content .inline-details .comment .meta .pro-badge{margin-left:.25em}.image-content .inline-details .comment .created-at{margin-left:var(--size-spacing-default);padding-top:0}.image-content .inline-details .comment .reply-to{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-279 425.6 11.8 11.4'%3E%3Cpath fill='none' d='M-259 357.4h32.1'/%3E%3Cpath fill='%2300aeff' d='M-273.2 426.2v5.6c0 .4-.3.6-.6.7-.1 0-.3 0-.4-.1l-4.5-2.8c-.3-.2-.4-.6-.2-.9l.2-.2 4.5-2.8c.3-.2.7 0 .9.3 0 .1.1.1.1.2z'/%3E%3Cpath fill='%2300aeff' d='M-272.2 430.6c1.8.6 2.8 2.5 2.3 4.3v.1c-.1.2 0 .4.1.6l1.1 1.2c.2.2.5.2.7.1 0 0 .1-.1.1-.2 1.6-2.9.5-6.6-2.4-8.1-.9-.5-1.9-.7-2.9-.7l-.9 2.7c.6-.2 1.2-.2 1.9 0z'/%3E%3C/svg%3E");background-size:12px 11px}.image-content .inline-details .comment .delete,.image-content .inline-details .comment .reply-to{background-position:0 0;background-repeat:no-repeat;display:none;padding-left:var(--size-spacing-half-again)}.image-content .inline-details .comment .delete{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 13 13'%3E%3Crect width='13' height='13' fill='%23cecec6' rx='2.58' ry='2.58'/%3E%3Cpath fill='%238d8d87' d='m3.78 8.91 1.87-2.67-1.79-2.52h1.67l.92 1.43 1-1.43H9L7.27 6.18l1.92 2.73H7.5L6.45 7.3 5.38 8.91z'/%3E%3C/svg%3E");background-size:13px 13px;color:var(--color-text-link-danger);text-decoration-color:var(--color-text-link-danger-underline)}.image-content .inline-details .comment .delete:active,.image-content .inline-details .comment .delete:focus,.image-content .inline-details .comment .delete:hover{color:var(--color-text-link-danger-hover);text-decoration-color:var(--color-text-link-danger-underline-hover)}.image-content .inline-details .comment:focus .delete,.image-content .inline-details .comment:focus .reply-to,.image-content .inline-details .comment:hover .delete,.image-content .inline-details .comment:hover .reply-to{display:block}.image-content .inline-details .show-more-comments{display:block;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default) 0;text-align:right;text-decoration:none}.image-content .inline-details .post-comment-inline textarea{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);color:var(--color-page-text-disabled);height:2em;padding:var(--size-spacing-half)}.image-content .inline-details .post-comment-inline .button{display:none;padding-top:var(--size-spacing-half)}.image-content .inline-details .post-comment-inline.post-comment-inline-expanded textarea{color:var(--color-page-text);min-height:100px}.image-content .inline-details .post-comment-inline.post-comment-inline-expanded .button{display:block}.new-post-panel{background-color:var(--color-background-content);border-bottom-left-radius:10px;border-bottom-right-radius:10px;box-shadow:var(--size-spacing-default) var(--size-spacing-default) 0 var(--color-base-black-transparent-200);display:none;left:var(--size-spacing-triple);margin:0 auto;max-width:560px;padding:var(--size-spacing-half-again);position:fixed;right:var(--size-spacing-triple);top:calc(var(--size-spacing-default)*-1);z-index:999}@media screen and (min-width:768px){.new-post-panel{padding:calc(var(--size-spacing-default)*5)}}.new-post-panel h2{font-size:1.75rem;line-height:1.2;margin-bottom:var(--size-spacing-default);position:relative}.new-post-panel h2 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.new-post-panel h2 a:active,.new-post-panel h2 a:focus,.new-post-panel h2 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.new-post-panel h2 a:active,.new-post-panel h2 a:focus,.new-post-panel h2 a:hover{text-decoration:underline}.new-post-panel p{font-size:.875rem;line-height:1.2;margin:var(--size-spacing-double) 0;padding:0 var(--size-spacing-half)}.new-post-panel .upload-image-input{cursor:pointer;height:100%;left:0;opacity:0;position:absolute;top:0;width:100%}.new-post-panel .save-video-form{display:flex}.new-post-panel .save-video-form .field-input{flex:1}.new-post-panel .save-video-form .btn{flex:none;margin-left:var(--size-spacing-default)}.new-post-panel .post-video-form{margin-top:var(--size-spacing-double)}.new-post-panel .shake-selector{position:relative}.new-post-panel .shake-selector h3 a{color:var(--color-page-text);display:block;text-decoration:none}.new-post-panel .shake-selector h3 a .green{word-wrap:break-word;color:var(--color-status-success);overflow-wrap:break-word;word-break:break-word}.new-post-panel .shake-selector h3 a:focus .green,.new-post-panel .shake-selector h3 a:hover .green{color:var(--color-status-success-pastel-dark)}.new-post-panel .shake-selector ul{background-color:var(--color-status-success-pastel-light);border-radius:var(--size-border-radius-large);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-200);display:none;left:var(--size-spacing-triple);list-style:none;margin:0;max-height:66vh;overflow:auto;padding:var(--size-spacing-default) 0 var(--size-spacing-half) 0;position:absolute;top:1.638rem;width:200px;z-index:2}.new-post-panel .shake-selector ul li a{word-wrap:break-word;color:var(--color-status-success-pastel-dark);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;padding:var(--size-spacing-half) var(--size-spacing-half-again);text-decoration:none;word-break:break-word}.new-post-panel .shake-selector ul li a:focus,.new-post-panel .shake-selector ul li a:hover{color:var(--color-text-dark)}.new-post-panel--inner{display:flex;flex-direction:column}@media screen and (min-width:768px){.new-post-panel--inner{flex-direction:row}}.new-post-panel--inner>*{flex:1 1 0;min-width:0;padding-bottom:var(--size-spacing-half-again);padding-top:var(--size-spacing-half-again)}@media screen and (min-width:768px){.new-post-panel--inner>*{padding-bottom:var(--size-spacing-quadruple);padding-top:calc(var(--size-spacing-default)*6)}}.new-post-panel--inner>:first-child{border-bottom:1px dashed var(--color-border-default)}@media screen and (min-width:768px){.new-post-panel--inner>:first-child{border-bottom:0;border-right:1px dashed var(--color-border-default);padding-right:var(--size-spacing-double)}}@media screen and (min-width:768px){.new-post-panel--inner>:last-child{padding-left:var(--size-spacing-double)}}.new-post-panel--inner>:first-child:last-child{border:0;padding:0}.dashboard-new-user{padding:var(--size-spacing-double) var(--size-spacing-triple)}.dashboard-new-user h1{color:var(--color-status-success);font-size:3.125rem;margin-bottom:var(--size-spacing-double)}.dashboard-new-user h2{font-size:1.875rem}.dashboard-new-user h3{font-size:1.375rem;margin-bottom:var(--size-spacing-half)}.dashboard-new-user h4{font-size:.875rem;margin-bottom:var(--size-spacing-half)}.dashboard-new-user p{font-size:.875rem;line-height:1.75;padding-bottom:var(--size-spacing-half-again)}.dashboard-new-user a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.dashboard-new-user a:active,.dashboard-new-user a:focus,.dashboard-new-user a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.dashboard-new-user .two-columns{display:flex}.dashboard-new-user .two-columns .left-column{flex:0 0 70px;margin-right:var(--size-spacing-double);margin-top:var(--size-spacing-default)}@media screen and (min-width:768px){.dashboard-new-user .two-columns .left-column{flex-basis:130px;margin-left:var(--size-spacing-double);margin-right:var(--size-spacing-quadruple)}}.dashboard-new-user .two-columns .right-column{flex:1;margin-top:var(--size-spacing-quadruple)}.new-members{border-top:1px dashed var(--color-border-default);list-style:none;margin:var(--size-spacing-triple) 0 0;padding:var(--size-spacing-triple) 0 0}.new-members>li{border-bottom:1px dashed var(--color-border-default);clear:both;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.new-members>li>a{display:block;float:left;margin-right:var(--size-spacing-default)}.new-members>li h4{word-wrap:break-word;font-size:1.125rem;overflow-wrap:break-word;padding-bottom:var(--size-spacing-half);word-break:break-word}.new-members>li h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.new-members>li h4 a:active,.new-members>li h4 a:focus,.new-members>li h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.new-members>li h4 a:active,.new-members>li h4 a:focus,.new-members>li h4 a:hover{text-decoration:underline}.new-members>li p{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;word-break:break-word}.new-members .image-medium{margin-left:calc(var(--size-spacing-default)*6);padding-bottom:0;padding-top:var(--size-spacing-default)}.new-members .image-medium .user-and-title>a{display:none}.notification-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double)}.notification-block-link a{display:block;text-decoration:none}.notification-block-hd,.notification-block-link a{font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-half-again)}.notification-block-hd{cursor:pointer}.notification-block-bd{display:none;padding:0 var(--size-spacing-default) var(--size-spacing-default)}.notification-block-bd .notification{word-wrap:break-word;background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);color:var(--color-page-text);font-size:.875rem;overflow-wrap:break-word;padding:var(--size-spacing-default);position:relative;word-break:break-word}.notification-block-bd .notification:after{clear:both;content:"";display:table}.notification-block-bd .notification+.notification{margin-top:var(--size-spacing-default)}.notification-block-bd .notification .notification-close{outline:none;position:absolute;right:var(--size-spacing-half);top:var(--size-spacing-half)}.notification-block-bd .notification .thumb{float:left;padding-right:var(--size-spacing-default);text-align:center;width:var(--size-avatar-large)}.notification-block-bd .notification .context{font-size:.75rem;overflow:hidden}.notification-block-bd .clear-all{clear:both;margin-top:var(--size-spacing-default);text-align:right}.notification-block-follow{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-primary)}.notification-block-save{background-color:var(--color-bg-warning-pastel);color:var(--color-status-warning-pastel-dark)}.notification-block-like{background-color:var(--color-bg-danger-pastel);color:var(--color-status-danger-pastel-dark)}.notification-block-comment{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-secondary)}.notification-block-invitation-approved,.notification-block-invitation-request,.notification-block-shakeinvitation{background-color:var(--color-bg-success-pastel);color:var(--color-status-success-pastel-dark)}.notification-block-aggregate{background-color:var(--color-bg-secondary-brand-pastel);color:var(--color-brand-primary)}.notification-block-follow .notification{padding-right:var(--size-spacing-triple)}.notification-block-shakeinvitation .shake-thumb{float:left;margin-right:var(--size-spacing-default);width:var(--size-avatar-default)}.notification-block-shakeinvitation .shake-thumb a{display:block}.notification-block-shakeinvitation h4{padding-top:var(--size-spacing-default)}.notification-block-shakeinvitation .shake-context{clear:both;margin-top:var(--size-spacing-default)}.notification-block-shakeinvitation .shake-context a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.notification-block-shakeinvitation .shake-context a:active,.notification-block-shakeinvitation .shake-context a:focus,.notification-block-shakeinvitation .shake-context a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.notification-block-shakeinvitation .shake-context a:active,.notification-block-shakeinvitation .shake-context a:focus,.notification-block-shakeinvitation .shake-context a:hover{text-decoration:underline}.notification-block-shakeinvitation .buttons{display:flex;justify-content:space-between;margin-top:var(--size-spacing-default)}.content-shake .notification-block-shakeinvitation{margin-top:var(--size-spacing-double)}.notification-block-invitation-request .notification a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.notification-block-invitation-request .notification a:active,.notification-block-invitation-request .notification a:focus,.notification-block-invitation-request .notification a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.notification-block-invitation-request .notification a:active,.notification-block-invitation-request .notification a:focus,.notification-block-invitation-request .notification a:hover{text-decoration:underline}.notification-block-invitation-request .notification-actions{clear:both;display:flex;justify-content:space-between;margin-top:var(--size-spacing-default)}.notification-block-tou .notification-block-link a{color:var(--color-page-text-emphasis)}.notification-block-tou .notification-block-link a:focus,.notification-block-tou .notification-block-link a:hover{color:var(--color-text-link-hover)}.account-header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));padding:1.5rem 1.875rem 1rem;width:100%}.account-header .avatar{display:flex}.account-header .avatar img{display:block;height:50px;width:50px}.account-header .avatar-media{flex:none}.account-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.account-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.account-header h2{font-size:2.625rem}}.pagination{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;font-weight:var(--number-font-weight-bold);margin:var(--size-spacing-quadruple);padding:var(--size-spacing-default)}.pagination .current,.pagination a{padding:0 var(--size-spacing-half)}.pagination span.next-link,.pagination span.previous-link{color:var(--color-page-text-secondary)}.pagination .previous-link{flex-basis:50%}@media screen and (min-width:480px){.pagination .previous-link{flex-basis:auto;margin-right:auto}}.pagination .next-link{flex-basis:50%;text-align:right}@media screen and (min-width:480px){.pagination .next-link{flex-basis:auto;margin-left:auto;order:1}}.pagination-inner{align-items:center;background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);display:flex;flex-wrap:wrap;justify-content:center;padding:var(--size-spacing-default)}@media screen and (min-width:480px){.pagination-inner{flex-wrap:none}}.linear-navigation{display:flex;justify-content:space-between;padding:var(--size-spacing-triple) var(--size-spacing-quadruple)}.linear-navigation .newer a,.linear-navigation .older a{display:block}.promotions{list-style:none;margin:var(--size-spacing-default) 0;padding:0}.promotions li{float:left;margin-bottom:var(--size-spacing-default);margin-right:var(--size-spacing-default);overflow:visible;position:relative}.promotions li:focus .promotion-avatar,.promotions li:hover .promotion-avatar{border-color:var(--color-brand-primary)}.promotions li:focus .promotion-name,.promotions li:hover .promotion-name{display:block}.promotions .promotion-avatar{border:4px solid transparent;border-radius:var(--size-border-radius-large);overflow:hidden;transition:all .75s ease 0s}@media screen and (prefers-reduced-motion:reduce){.promotions .promotion-avatar{transition:none}}.promotions .promotion-avatar img{display:block;height:var(--size-avatar-large);width:var(--size-avatar-large)}.promotions .promotion-name{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-primary);border:none;border-radius:.5em;bottom:-3.5em;box-shadow:.22em .22em 0 var(--color-base-black-transparent-100);color:var(--color-text-light-emphasis);cursor:pointer;display:inline-block;display:none;font-size:18px;font-size:.875rem;font-weight:500;height:2.2em;left:50%;letter-spacing:.033em;line-height:2.2em;margin-right:.22em;padding:0 .88em;position:absolute;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transform:translateX(-50%);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap;z-index:1}.promotions .promotion-name:focus,.promotions .promotion-name:hover{color:var(--color-text-light-emphasis)}.promotions .promotion-name:focus{outline:none}.promotions .promotion-name:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.promotions .promotion-name:focus,.promotions .promotion-name:hover{background-color:var(--color-background-button-primary-hover)}.promotions .promotion-name:disabled{box-shadow:none!important}.promo-block{margin:var(--size-spacing-double) auto;max-width:100%;text-align:center;width:285px}@media screen and (max-width:320px){.promo-block{display:none}}.shake-image{cursor:pointer;height:284px;max-width:284px;overflow:hidden;position:relative}.shake-image img{display:block}.shake-image .shake-image-input{cursor:pointer;height:100%;left:0;opacity:0;position:absolute;top:0;width:100%;z-index:1}.shake-image .border{display:none}.shake-image.is-editable:focus .border,.shake-image.is-editable:hover .border,.shake-image.shake-image-hover .border{border:10px solid var(--color-status-edit);display:block;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0}.shake-image .shake-image-placeholder{align-items:center;background:var(--color-background-content-secondary);border:1px dashed var(--color-border-default);display:flex;flex-direction:column;height:100%;justify-content:center;max-width:100%;padding:var(--size-spacing-triple);text-align:center}.shake-image .shake-image-placeholder strong{color:var(--color-brand-primary);display:block}.shake-details .shake-edit-description-form,.shake-details .shake-edit-title-form{display:none}.shake-details .title{word-wrap:break-word;font-size:2.25rem;margin:var(--size-spacing-half-again) 0 var(--size-spacing-default);overflow-wrap:break-word;word-break:break-word}.shake-details .shake-edit-title-hover,.shake-details.is-editable .title:focus,.shake-details.is-editable .title:hover{background-color:var(--color-status-edit)}.shake-details .shake-edit-title-input{border:1px solid var(--color-border-default);font-size:2.25rem;font-weight:var(--number-font-weight-bold);margin-top:var(--size-spacing-default);width:100%}.shake-details .description{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;white-space:pre-wrap;word-break:break-word}.shake-details .description .placeholder{color:var(--color-page-text-secondary);font-style:italic}.shake-details .shake-edit-description-hover,.shake-details.is-editable .description:focus,.shake-details.is-editable .description:hover{background-color:var(--color-status-edit)}.shake-details .shake-edit-description-input{border:1px solid var(--color-border-default);font-size:.875rem;width:100%}.shake-details .buttons{align-items:center;display:flex;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-half)}.shake-details .or{color:var(--color-page-text-secondary);padding:var(--size-spacing-half)}.shake-list{border-top:1px dashed var(--color-border-default);list-style:none;margin:0;padding:0}.shake-list--shake{border-bottom:1px dashed var(--color-border-default);clear:both;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.shake-list--thumb{float:left;margin-right:1em}.shake-list--description,.shake-view-description,.shake-view-featured,.shake-view-title{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.shake-members{list-style:none;margin:0;padding:var(--size-spacing-triple)}.shake-members li{border-bottom:1px dashed var(--color-border-default);display:flex;margin-bottom:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.shake-members .member--img{flex:none;margin-right:var(--size-spacing-default)}.shake-members .details{flex:1}.shake-members h4{font-size:1.125rem;padding-bottom:var(--size-spacing-half)}.shake-members h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.shake-members h4 a:active,.shake-members h4 a:focus,.shake-members h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.shake-members h4 a:active,.shake-members h4 a:focus,.shake-members h4 a:hover{text-decoration:underline}.shake-members .about{font-size:.875rem;margin:0}.shake-members .website{display:block;margin-top:var(--size-spacing-half)}.following-wrapper{margin:var(--size-spacing-double) 0}.following-wrapper h3{color:var(--color-page-text);font-size:.875rem;padding-bottom:var(--size-spacing-default);padding-left:var(--size-spacing-half-again);padding-right:var(--size-spacing-half-again)}.following-wrapper h3 a{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-secondary);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);cursor:pointer;display:inline-block;float:right;font-size:18px;font-size:12px;font-weight:500;font-weight:400;height:2.2em;letter-spacing:.033em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.following-wrapper h3 a:focus,.following-wrapper h3 a:hover{color:var(--color-text-light-emphasis)}.following-wrapper h3 a:focus{outline:none}.following-wrapper h3 a:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.following-wrapper h3 a:focus,.following-wrapper h3 a:hover{background-color:var(--color-background-button-secondary-hover)}.following-wrapper h3 span{color:var(--color-page-text-secondary);font-weight:var(--number-font-weight-normal)}.following{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large)}.following ul{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:var(--size-spacing-half-again);padding-right:0}.following ul li{flex:none;margin-bottom:var(--size-spacing-half);margin-right:var(--size-spacing-half)}.following ul li a{display:block}.following br{display:none}.following .view-all-following{display:block;font-size:.75rem;margin-top:calc(var(--size-spacing-default)*-1);padding-bottom:var(--size-spacing-default);padding-right:var(--size-spacing-half-again);text-align:right}.other-shakes-wrapper{margin-bottom:var(--size-spacing-double)}.other-shakes-wrapper h3{word-wrap:break-word;font-size:.875rem;overflow-wrap:break-word;padding-bottom:var(--size-spacing-default);padding-left:var(--size-spacing-half-again);word-break:break-word}.other-shakes{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-default) var(--size-spacing-half-again)}.other-shakes ul{list-style:none;margin:0;padding:0}.other-shakes a{word-wrap:break-word;color:var(--color-text-link-primary);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;padding:var(--size-spacing-half) 0;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.other-shakes a:active,.other-shakes a:focus,.other-shakes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.other-shakes a:active,.other-shakes a:focus,.other-shakes a:hover{text-decoration:underline}.find-shakes-block{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.find-shakes-block h3{color:var(--color-brand-secondary);font-size:1.25rem}.find-shakes-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.find-shakes-block-content p{margin:0}.find-shakes-block-content a{font-weight:var(--number-font-weight-bold);text-decoration:none}.upgrade-account-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.upgrade-account-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-half);padding:var(--size-spacing-double)}.upgrade-account-block-content h3{font-size:1.125rem;margin-bottom:var(--size-spacing-half)}.upgrade-account-block-content h3 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.upgrade-account-block-content h3 a:active,.upgrade-account-block-content h3 a:focus,.upgrade-account-block-content h3 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.upgrade-account-block-content h3 a:active,.upgrade-account-block-content h3 a:focus,.upgrade-account-block-content h3 a:hover{text-decoration:underline}.upgrade-account-block-content p{margin:0}.cool-tools-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.cool-tools-block h3{color:var(--color-brand-primary);font-size:1.25rem}.cool-tools-block p{font-size:.875rem;margin:var(--size-spacing-half) 0 0}.cool-tools-block a{background-color:var(--color-background-content);background-position:5px 5px;background-repeat:no-repeat;background-size:40px 45px;border-radius:var(--size-border-radius-large);color:var(--color-text-link-primary);display:block;font-size:.75rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default);padding-bottom:var(--size-spacing-double);padding-left:55px;padding-top:var(--size-spacing-double);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.cool-tools-block a:active,.cool-tools-block a:focus,.cool-tools-block a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.cool-tools-block a:active,.cool-tools-block a:focus,.cool-tools-block a:hover{text-decoration:underline}.cool-tools-block .browser-tools{list-style:none;margin:var(--size-spacing-double) 0 var(--size-spacing-default);padding:0}.cool-tools-block .browser-tools li+li{margin-top:var(--size-spacing-default)}.cool-tools-block .bookmarklet a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' data-name='Layer 1' viewBox='0 0 34.13 31.65'%3E%3Cdefs%3E%3CradialGradient id='a' cx='21.18' cy='10.62' r='17.07' gradientTransform='matrix(1 0 0 .43 -4.12 -1.84)' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23d6d6d6'/%3E%3Cstop offset='.36' stop-color='%23e1e1e1'/%3E%3Cstop offset='.99' stop-color='%23fff'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/radialGradient%3E%3C/defs%3E%3Cpath fill='url(%23a)' d='M0 3.08h34.13v6.86H0z'/%3E%3Cpath fill='%23881746' d='M7.06 3.03c0-1.68.75-3 1.68-3h11c-.93 0-3 1.36-3 3z'/%3E%3Cpath fill='%23ed1d7f' d='M25.4 31.65V3.03c0-1.68-.75-3-1.68-3h-15c.93 0 1.68 1.36 1.68 3v28.62l7.52-5.42z'/%3E%3C/svg%3E")}.cool-tools-block .safari a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 66.166 65.804'%3E%3Cdefs%3E%3ClinearGradient id='b'%3E%3Cstop offset='0' stop-color='%2306c2e7'/%3E%3Cstop offset='.25' stop-color='%230db8ec'/%3E%3Cstop offset='.5' stop-color='%2312aef1'/%3E%3Cstop offset='.75' stop-color='%231f86f9'/%3E%3Cstop offset='1' stop-color='%23107ddd'/%3E%3C/linearGradient%3E%3ClinearGradient id='a'%3E%3Cstop offset='0' stop-color='%23bdbdbd'/%3E%3Cstop offset='1' stop-color='%23fff'/%3E%3C/linearGradient%3E%3ClinearGradient xlink:href='%23a' id='d' x1='412.975' x2='412.975' y1='237.608' y2='59.392' gradientTransform='translate(206.79 159.773) scale(.35154)' gradientUnits='userSpaceOnUse'/%3E%3Cfilter id='f' width='1.042' height='1.045' x='-.021' y='-.022' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='.958'/%3E%3C/filter%3E%3Cfilter id='c' width='1.096' height='1.096' x='-.048' y='-.048' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='3.564'/%3E%3C/filter%3E%3CradialGradient xlink:href='%23b' id='e' cx='413.061' cy='136.818' r='82.125' fx='413.061' fy='136.818' gradientTransform='translate(194.545 155.58) scale(.38143)' gradientUnits='userSpaceOnUse'/%3E%3C/defs%3E%3Cpath d='M502.083 148.5a89.108 89.108 0 0 1-89.108 89.108 89.108 89.108 0 0 1-89.108-89.108 89.108 89.108 0 0 1 89.108-89.108 89.108 89.108 0 0 1 89.108 89.108z' filter='url(%23c)' opacity='.53' paint-order='markers stroke fill' transform='matrix(.33865 0 0 .3261 -106.77 -14.478)'/%3E%3Cpath fill='url(%23d)' stroke='%23cdcdcd' stroke-linecap='round' stroke-linejoin='round' stroke-width='.093' d='M383.294 211.977a31.325 31.325 0 0 1-31.325 31.325 31.325 31.325 0 0 1-31.326-31.325 31.325 31.325 0 0 1 31.326-31.325 31.325 31.325 0 0 1 31.325 31.325z' paint-order='markers stroke fill' transform='translate(-318.886 -180.595)'/%3E%3Cpath fill='url(%23e)' d='M380.84 211.977a28.87 28.87 0 0 1-28.871 28.87 28.87 28.87 0 0 1-28.871-28.87 28.87 28.87 0 0 1 28.87-28.87 28.87 28.87 0 0 1 28.871 28.87z' paint-order='markers stroke fill' transform='translate(-318.886 -180.595)'/%3E%3Cpath fill='%23f4f2f3' d='M33.083 4.017a.42.42 0 0 0-.421.42v4.856a.42.42 0 1 0 .842 0V4.438a.42.42 0 0 0-.421-.421zm-2.754.174a.42.42 0 0 0-.46.463l.212 2.03a.42.42 0 1 0 .837-.087l-.212-2.03a.42.42 0 0 0-.377-.376zm5.527.002a.42.42 0 0 0-.377.375l-.214 2.03a.42.42 0 1 0 .837.089l.214-2.031a.42.42 0 0 0-.46-.463zM27.5 4.6a.42.42 0 0 0-.41.508l1.005 4.75a.42.42 0 1 0 .824-.174l-1.005-4.75A.42.42 0 0 0 27.5 4.6zm11.183.004a.42.42 0 0 0-.414.333l-1.009 4.75a.42.42 0 1 0 .824.175l1.009-4.75a.42.42 0 0 0-.41-.508zM24.8 5.361a.42.42 0 0 0-.437.55l.632 1.942a.42.42 0 1 0 .8-.26l-.63-1.942a.42.42 0 0 0-.365-.29zm16.568.001a.42.42 0 0 0-.364.29l-.632 1.942a.42.42 0 1 0 .8.26l.632-1.942a.42.42 0 0 0-.436-.55zM22.13 6.34a.42.42 0 0 0-.377.592l1.972 4.437a.42.42 0 1 0 .77-.342L22.523 6.59a.42.42 0 0 0-.393-.25zm21.937.015a.42.42 0 0 0-.392.25l-1.978 4.434a.42.42 0 1 0 .769.343l1.978-4.434a.42.42 0 0 0-.377-.593zM19.654 7.65a.42.42 0 0 0-.394.63l1.02 1.77a.42.42 0 1 0 .73-.421L19.989 7.86a.42.42 0 0 0-.335-.21zm26.858 0a.419.419 0 0 0-.335.21l-1.021 1.769a.42.42 0 1 0 .73.42l1.02-1.768a.42.42 0 0 0-.394-.63zm-29.265 1.5a.422.422 0 0 0-.326.669l2.85 3.93a.42.42 0 1 0 .682-.494l-2.85-3.93a.42.42 0 0 0-.356-.174zm31.702.022a.42.42 0 0 0-.356.174l-2.856 3.926a.42.42 0 1 0 .681.495l2.856-3.926a.42.42 0 0 0-.325-.669zm-33.852 1.783a.42.42 0 0 0-.335.702l1.366 1.518a.42.42 0 1 0 .626-.563l-1.367-1.518a.42.42 0 0 0-.29-.14zm35.975.003a.421.421 0 0 0-.29.139l-1.367 1.517a.42.42 0 1 0 .625.564l1.367-1.518a.42.42 0 0 0-.335-.702zm-38.037 1.977a.42.42 0 0 0-.26.733l3.61 3.248a.42.42 0 1 0 .563-.625l-3.609-3.248a.42.42 0 0 0-.304-.108zm40.109.014a.419.419 0 0 0-.304.108l-3.61 3.245a.42.42 0 1 0 .562.626l3.61-3.245a.42.42 0 0 0-.258-.734zm-41.823 2.19a.42.42 0 0 0-.262.762l1.652 1.2a.42.42 0 1 0 .495-.681l-1.652-1.2a.42.42 0 0 0-.233-.081zm43.535.015a.421.421 0 0 0-.233.08l-1.653 1.2a.42.42 0 1 0 .495.681l1.653-1.2a.42.42 0 0 0-.262-.76zM9.72 17.49a.42.42 0 0 0-.18.785l4.204 2.427a.42.42 0 1 0 .42-.729L9.96 17.546a.42.42 0 0 0-.24-.056zm46.728 0a.417.417 0 0 0-.24.056l-4.205 2.427a.42.42 0 1 0 .42.73l4.206-2.428a.42.42 0 0 0-.181-.785zm-47.94 2.506a.42.42 0 0 0-.18.806l1.866.831a.42.42 0 1 0 .343-.768l-1.866-.832a.42.42 0 0 0-.163-.037zm49.158.017a.42.42 0 0 0-.164.037l-1.865.83a.42.42 0 1 0 .342.77l1.866-.831a.42.42 0 0 0-.179-.806zM7.429 22.615a.42.42 0 0 0-.094.82l4.615 1.504a.42.42 0 1 0 .261-.8l-4.616-1.504a.421.421 0 0 0-.166-.02zm51.314.018a.408.408 0 0 0-.166.02l-4.617 1.5a.42.42 0 1 0 .26.801l4.617-1.5a.42.42 0 0 0-.094-.82zM6.756 25.365a.42.42 0 0 0-.09.833l1.998.424a.42.42 0 1 0 .175-.823l-1.998-.425a.413.413 0 0 0-.085-.009zm52.655.004a.518.518 0 0 0-.085.009l-1.998.424a.42.42 0 1 0 .175.823l1.998-.424a.42.42 0 0 0-.09-.833zM6.247 28.13a.42.42 0 0 0-.003.838l4.828.51a.42.42 0 1 0 .089-.837l-4.829-.51a.432.432 0 0 0-.085 0zm53.676.037a.386.386 0 0 0-.085 0l-4.83.504a.42.42 0 1 0 .088.837l4.83-.504a.42.42 0 0 0-.003-.837zM6.165 30.96a.42.42 0 1 0 0 .842h2.043a.42.42 0 1 0 0-.842zm51.793 0a.42.42 0 1 0 0 .842h2.043a.42.42 0 1 0 0-.842zm-46.803 2.295a.384.384 0 0 0-.085 0l-4.83.504a.42.42 0 1 0 .088.838l4.83-.504a.42.42 0 0 0-.003-.838zm43.853.03a.42.42 0 0 0-.003.838l4.828.51a.42.42 0 1 0 .089-.837l-4.828-.51a.434.434 0 0 0-.086-.001zm-46.26 2.843a.43.43 0 0 0-.085.01l-1.998.424a.42.42 0 1 0 .175.823l1.998-.424a.42.42 0 0 0-.09-.833zm48.67.005a.42.42 0 0 0-.09.833l1.997.424a.42.42 0 1 0 .175-.824l-1.998-.424a.413.413 0 0 0-.085-.01zM12.111 37.79a.408.408 0 0 0-.166.02l-4.617 1.5a.42.42 0 1 0 .26.801l4.617-1.5a.42.42 0 0 0-.094-.82zm41.937.015a.42.42 0 0 0-.094.82l4.616 1.504a.42.42 0 1 0 .26-.8l-4.615-1.504a.421.421 0 0 0-.167-.02zM10.35 41.08a.42.42 0 0 0-.163.036l-1.866.831a.42.42 0 1 0 .342.769l1.866-.83a.42.42 0 0 0-.179-.806zm45.459.016a.42.42 0 0 0-.18.805l1.865.832a.42.42 0 1 0 .343-.769l-1.865-.832a.42.42 0 0 0-.163-.036zm-41.826.912a.417.417 0 0 0-.24.056L9.538 44.49a.42.42 0 1 0 .421.73l4.205-2.428a.42.42 0 0 0-.181-.785zm38.2 0a.42.42 0 0 0-.181.785l4.205 2.427a.42.42 0 1 0 .42-.729l-4.204-2.427a.42.42 0 0 0-.24-.056zM12.934 45.57a.421.421 0 0 0-.233.08l-1.653 1.2a.42.42 0 1 0 .495.682l1.653-1.2a.42.42 0 0 0-.262-.762zm40.288.015a.42.42 0 0 0-.262.762l1.652 1.2a.42.42 0 1 0 .495-.681l-1.652-1.2a.42.42 0 0 0-.233-.081zm-36.544.145a.418.418 0 0 0-.304.108l-3.61 3.245a.42.42 0 1 0 .562.626l3.61-3.245a.42.42 0 0 0-.258-.734zm32.8.011a.421.421 0 0 0-.26.734l3.609 3.248a.42.42 0 1 0 .563-.625l-3.608-3.249a.42.42 0 0 0-.304-.107zm-29.375 3.084a.42.42 0 0 0-.355.173l-2.856 3.927a.42.42 0 1 0 .68.495l2.857-3.926a.42.42 0 0 0-.326-.669zm25.936.018a.421.421 0 0 0-.326.668l2.85 3.93a.42.42 0 1 0 .682-.494l-2.851-3.93a.42.42 0 0 0-.355-.174zm-29.623.606a.421.421 0 0 0-.29.14l-1.367 1.517a.42.42 0 1 0 .625.563l1.367-1.517a.42.42 0 0 0-.335-.703zm33.331.002a.42.42 0 0 0-.335.702l1.366 1.518a.42.42 0 1 0 .626-.563l-1.366-1.518a.42.42 0 0 0-.291-.139zm-25.655 1.684a.419.419 0 0 0-.393.25l-1.978 4.433a.42.42 0 1 0 .77.343l1.977-4.434a.42.42 0 0 0-.376-.592zm17.955.012a.42.42 0 0 0-.377.592l1.972 4.437a.42.42 0 1 0 .77-.342l-1.972-4.437a.42.42 0 0 0-.393-.25zm-21.431 1.359a.419.419 0 0 0-.335.21l-1.021 1.768a.42.42 0 1 0 .729.421l1.02-1.769a.42.42 0 0 0-.393-.63zm24.934 0a.42.42 0 0 0-.394.63l1.021 1.77a.42.42 0 1 0 .73-.422l-1.022-1.769a.42.42 0 0 0-.335-.21zm-17.054.063a.42.42 0 0 0-.415.334l-1.009 4.749a.42.42 0 1 0 .824.175l1.009-4.75a.42.42 0 0 0-.41-.508zm9.16.003a.42.42 0 0 0-.41.508l1.006 4.75a.42.42 0 1 0 .823-.175l-1.006-4.75a.42.42 0 0 0-.414-.333zm-4.573.48a.42.42 0 0 0-.421.42v4.855a.42.42 0 1 0 .842 0v-4.855a.42.42 0 0 0-.421-.42zm-7.727 1.568a.42.42 0 0 0-.364.29l-.631 1.942a.42.42 0 1 0 .8.26l.632-1.942a.42.42 0 0 0-.437-.55zm15.45 0a.42.42 0 0 0-.437.55l.632 1.943a.42.42 0 1 0 .8-.26l-.63-1.942a.42.42 0 0 0-.365-.29zm-10.365 1.083a.42.42 0 0 0-.378.375l-.213 2.03a.42.42 0 1 0 .837.088l.214-2.03a.42.42 0 0 0-.46-.463zm5.267.002a.42.42 0 0 0-.46.463l.212 2.03a.42.42 0 1 0 .837-.088l-.212-2.03a.42.42 0 0 0-.377-.375z' paint-order='markers stroke fill'/%3E%3Cpath d='m469.096 100.607-65.51 38.061-41.42 65.207 60.595-44.882z' filter='url(%23f)' opacity='.409' paint-order='markers stroke fill' transform='translate(-112.095 -20.822) scale(.35154)'/%3E%3Cpath fill='%23ff5150' d='m36.383 34.838-6.6-6.913 23.416-15.752z' paint-order='markers stroke fill'/%3E%3Cpath fill='%23f1f1f1' d='m36.383 34.838-6.6-6.913L12.966 50.59z' paint-order='markers stroke fill'/%3E%3Cpath d='m12.967 50.59 23.416-15.752L53.2 12.173z' opacity='.243'/%3E%3C/svg%3E")}.cool-tools-block .firefox a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 51500 51500'%3E%3CradialGradient id='c' cx='87.4%25' cy='-12.9%25' r='128%25' gradientTransform='matrix(.8 0 0 1 .18 .13)'%3E%3Cstop offset='.13' stop-color='%23ffbd4f'/%3E%3Cstop offset='.28' stop-color='%23ff980e'/%3E%3Cstop offset='.47' stop-color='%23ff3750'/%3E%3Cstop offset='.78' stop-color='%23eb0878'/%3E%3Cstop offset='.86' stop-color='%23e50080'/%3E%3C/radialGradient%3E%3CradialGradient id='d' cx='49%25' cy='40%25' r='128%25' gradientTransform='matrix(.82 0 0 1 .09 0)'%3E%3Cstop offset='.3' stop-color='%23960e18'/%3E%3Cstop offset='.35' stop-color='%23b11927' stop-opacity='.74'/%3E%3Cstop offset='.43' stop-color='%23db293d' stop-opacity='.34'/%3E%3Cstop offset='.5' stop-color='%23f5334b' stop-opacity='.1'/%3E%3Cstop offset='.53' stop-color='%23ff3750' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='e' cx='48%25' cy='-12%25' r='140%25'%3E%3Cstop offset='.13' stop-color='%23fff44f'/%3E%3Cstop offset='.53' stop-color='%23ff980e'/%3E%3C/radialGradient%3E%3CradialGradient id='g' cx='22.76%25' cy='110.11%25' r='100%25'%3E%3Cstop offset='.35' stop-color='%233a8ee6'/%3E%3Cstop offset='.67' stop-color='%239059ff'/%3E%3Cstop offset='1' stop-color='%23c139e6'/%3E%3C/radialGradient%3E%3CradialGradient id='h' cx='52%25' cy='33%25' r='59%25' gradientTransform='scale(.9 1)'%3E%3Cstop offset='.21' stop-color='%239059ff' stop-opacity='0'/%3E%3Cstop offset='.97' stop-color='%236e008b' stop-opacity='.6'/%3E%3C/radialGradient%3E%3CradialGradient id='i' cx='210%25' cy='-100%25' r='290%25'%3E%3Cstop offset='.1' stop-color='%23ffe226'/%3E%3Cstop offset='.79' stop-color='%23ff7139'/%3E%3C/radialGradient%3E%3CradialGradient id='j' cx='84%25' cy='-41%25' r='180%25'%3E%3Cstop offset='.11' stop-color='%23fff44f'/%3E%3Cstop offset='.46' stop-color='%23ff980e'/%3E%3Cstop offset='.72' stop-color='%23ff3647'/%3E%3Cstop offset='.9' stop-color='%23e31587'/%3E%3C/radialGradient%3E%3CradialGradient id='k' cx='16.1%25' cy='-18.6%25' r='348.8%25' gradientTransform='matrix(.10453 .46743 -.99452 .04913 -.05 -.26)'%3E%3Cstop offset='0' stop-color='%23fff44f'/%3E%3Cstop offset='.3' stop-color='%23ff980e'/%3E%3Cstop offset='.57' stop-color='%23ff3647'/%3E%3Cstop offset='.74' stop-color='%23e31587'/%3E%3C/radialGradient%3E%3CradialGradient id='l' cx='18.9%25' cy='-42.5%25' r='238.4%25'%3E%3Cstop offset='.14' stop-color='%23fff44f'/%3E%3Cstop offset='.48' stop-color='%23ff980e'/%3E%3Cstop offset='.66' stop-color='%23ff3647'/%3E%3Cstop offset='.9' stop-color='%23e31587'/%3E%3C/radialGradient%3E%3CradialGradient id='m' cx='159.3%25' cy='-44.72%25' r='313.1%25'%3E%3Cstop offset='.09' stop-color='%23fff44f'/%3E%3Cstop offset='.63' stop-color='%23ff980e'/%3E%3C/radialGradient%3E%3ClinearGradient id='a' x1='87.25%25' x2='9.4%25' y1='15.5%25' y2='93.1%25'%3E%3Cstop offset='.05' stop-color='%23fff44f'/%3E%3Cstop offset='.37' stop-color='%23ff980e'/%3E%3Cstop offset='.53' stop-color='%23ff3647'/%3E%3Cstop offset='.7' stop-color='%23e31587'/%3E%3C/linearGradient%3E%3ClinearGradient id='n' x1='80%25' x2='18%25' y1='14%25' y2='84%25'%3E%3Cstop offset='.17' stop-color='%23fff44f' stop-opacity='.8'/%3E%3Cstop offset='.6' stop-color='%23fff44f' stop-opacity='0'/%3E%3C/linearGradient%3E%3Cpath id='b' d='M47870 16735c-1044-2512-3160-5224-4820-6082 1352 2650 2134 5310 2433 7294 0-6 2 5 4 22l4 26c2268 6147 1032 12398-748 16218-2754 5910-9420 11967-19857 11670-11276-318-21210-8683-23064-19643-338-1728 0-2605 170-4008-207 1080-286 1394-390 3315l-2 123c0 13270 10760 24030 24032 24030 11887 0 21756-8630 23690-19963l110-927c477-4120-53-8453-1560-12075z'/%3E%3Cpath id='f' d='M25677 21050c-40 598-2150 2660-2890 2660-6834 0-7943 4133-7943 4133 303 3480 2726 6348 5660 7865 134 70 270 130 405 193a13277 13277 0 0 0 706 289 10674 10674 0 0 0 3127 603c11978 562 14300-14320 5655-18640 2213-385 4510 505 5794 1407-2100-3672-6025-6150-10530-6150-285 0-564 24-844 43a12025 12025 0 0 0-6614 2549c366 310 780 724 1650 1583 1630 1606 5813 3270 5822 3465z'/%3E%3Cpath fill='url(%23a)' d='M47870 16735c-1044-2512-3160-5224-4820-6082 1352 2650 2134 5310 2433 7294l5 40c-2718-6773-7325-9505-11088-15452l-566-920a7372 7372 0 0 1-265-497 4370 4370 0 0 1-359-950 63 63 0 0 0-55-65 82 82 0 0 0-45 0l-12 7-17 10 10-14c-6037 3536-8085 10076-8274 13350a12025 12025 0 0 0-6614 2548 7136 7136 0 0 0-622-470 11134 11134 0 0 1-68-5873c-2468 1124-4390 2900-5785 4470h-10c-953-1206-886-5187-832-6018-10-52-710 363-802 425a17507 17507 0 0 0-2349 2012 21048 21048 0 0 0-2244 2692l-1 3v-3a20284 20284 0 0 0-3225 7280l-32 160a39700 39700 0 0 0-237 1500l-5 52a22907 22907 0 0 0-390 3316l-1 120c0 13270 10760 24030 24032 24030 11887 0 21756-8630 23690-19963l110-927c477-4120-53-8453-1560-12075zM20170 35545c113 53 220 112 334 164l16 10a12620 12620 0 0 1-350-174zm5506-14493zm19813-3060-3-23 4 26z'/%3E%3Cuse xlink:href='%23b' fill='url(%23c)'/%3E%3Cuse xlink:href='%23b' fill='url(%23d)'/%3E%3Cpath fill='url(%23e)' d='m36192 19560 150 110a13070 13070 0 0 0-2231-2911C26640 9290 32150 563 33080 120l10-13c-6037 3535-8085 10076-8273 13348 280-20 560-43 844-43 4505 0 8430 2477 10530 6150z'/%3E%3Cuse xlink:href='%23f' fill='url(%23g)'/%3E%3Cuse xlink:href='%23f' fill='url(%23h)'/%3E%3Cpath fill='url(%23i)' d='M17083 15204a24404 24404 0 0 1 498 330 11134 11134 0 0 1-67-5874c-2470 1125-4390 2900-5785 4470 115-3 3600-66 5354 1074z'/%3E%3Cpath fill='url(%23j)' d='M1822 26240c1855 10960 11788 19325 23063 19644 10437 296 17104-5762 19858-11670 1780-3820 3016-10070 748-16218v-2l-4-24c-2-17-4-28-4-22l5 40c853 5566-1980 10958-6405 14604l-13 30c-8625 7023-16878 4237-18550 3097a14410 14410 0 0 1-350-174c-5028-2403-7105-6984-6660-10913-4245 0-5693-3580-5693-3580s3812-2718 8836-355c4653 2190 9023 355 9023 354-10-195-4192-1860-5822-3465-872-860-1285-1272-1652-1583a7136 7136 0 0 0-622-470 28293 28293 0 0 0-498-330c-1753-1140-5240-1076-5355-1073h-10c-953-1207-886-5188-832-6020-10-50-710 363-802 426a17507 17507 0 0 0-2349 2012 21048 21048 0 0 0-2244 2692l-1 3v-3a20284 20284 0 0 0-3225 7280c-10 52-865 3784-444 5720z'/%3E%3Cpath fill='url(%23k)' d='M34110 16760a13070 13070 0 0 1 2231 2910l360 296c5450 5020 2594 12120 2380 12626 4426-3646 7258-9038 6405-14604-2716-6774-7323-9506-11086-15453l-566-920a7372 7372 0 0 1-265-497 4370 4370 0 0 1-359-950 63 63 0 0 0-55-65 82 82 0 0 0-45 0l-12 7-17 10c-930 443-6440 9170 1030 16640z'/%3E%3Cpath fill='url(%23l)' d='M36702 19965a4743 4743 0 0 0-360-295l-150-110c-1283-900-3580-1792-5794-1407 8644 4322 6323 19203-5655 18640a10674 10674 0 0 1-3127-603 13451 13451 0 0 1-706-289 9064 9064 0 0 1-405-193l16 10c1670 1140 9924 3925 18550-3097l13-30c213-506 3068-7606-2380-12626z'/%3E%3Cpath fill='url(%23m)' d='M14844 27844s1110-4133 7943-4133c740 0 2850-2062 2890-2660s-4370 1836-9023-354c-5024-2363-8836 354-8836 354s1448 3580 5693 3580c-445 3930 1632 8510 6660 10913 113 53 218 112 334 164-2935-1517-5358-4384-5660-7865z'/%3E%3Cpath fill='url(%23n)' d='M47870 16735c-1044-2512-3160-5224-4820-6082 1352 2650 2134 5310 2433 7294l5 40c-2718-6773-7325-9505-11088-15452l-566-920a7372 7372 0 0 1-265-497 4370 4370 0 0 1-359-950 63 63 0 0 0-55-65 82 82 0 0 0-45 0l-12 7-17 10 10-14c-6037 3536-8085 10076-8274 13350 280-20 560-43 845-43 4505 0 8430 2477 10530 6148-1284-900-3580-1792-5795-1407 8644 4322 6323 19203-5655 18640a10674 10674 0 0 1-3127-603 13451 13451 0 0 1-706-289 9064 9064 0 0 1-405-193l17 10a14410 14410 0 0 1-350-174c112 53 218 112 333 164-2935-1517-5358-4384-5660-7865 0 0 1108-4133 7942-4133 740 0 2850-2062 2890-2660-10-195-4190-1860-5822-3465-870-860-1285-1272-1650-1583a7136 7136 0 0 0-623-470 11134 11134 0 0 1-67-5873c-2470 1124-4390 2900-5785 4470h-10c-953-1207-886-5187-832-6020-10-50-710 363-802 426a17507 17507 0 0 0-2349 2012 21048 21048 0 0 0-2243 2692l-1 3v-3a20284 20284 0 0 0-3225 7280l-32 160a39787 39787 0 0 0-277 1515c-2 18 2-17 0 0a27956 27956 0 0 0-355 3353l-3 122c0 13270 10760 24030 24032 24030 11887 0 21756-8630 23690-19963l110-927c477-4120-53-8453-1560-12075zm-2384 1234 4 26v-2l-4-24z'/%3E%3C/svg%3E")}.cool-tools-block .chrome a{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' fill='none' viewBox='0 0 190 190'%3E%3ClinearGradient id='d' x1='28.3' x2='80.8' y1='75' y2='44.4' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23a52714' stop-opacity='.6'/%3E%3Cstop offset='.7' stop-color='%23a52714' stop-opacity='0'/%3E%3C/linearGradient%3E%3ClinearGradient id='f' x1='109.9' x2='51.5' y1='164.5' y2='130.3' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23055524' stop-opacity='.4'/%3E%3Cstop offset='.3' stop-color='%23055524' stop-opacity='0'/%3E%3C/linearGradient%3E%3ClinearGradient id='h' x1='121.9' x2='136.6' y1='49.8' y2='114.1' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23ea6100' stop-opacity='.3'/%3E%3Cstop offset='.7' stop-color='%23ea6100' stop-opacity='0'/%3E%3C/linearGradient%3E%3CradialGradient id='a' cx='91.2' cy='55' r='84.1' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%233e2723' stop-opacity='.2'/%3E%3Cstop offset='1' stop-color='%233e2723' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient xlink:href='%23a' id='i' cx='20.9' cy='47.5' r='78'/%3E%3CradialGradient id='j' cx='94.8' cy='95.1' r='87.9' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23263238' stop-opacity='.2'/%3E%3Cstop offset='1' stop-color='%23263238' stop-opacity='0'/%3E%3C/radialGradient%3E%3CradialGradient id='k' cx='33.3' cy='31' r='176.8' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23fff' stop-opacity='.1'/%3E%3Cstop offset='1' stop-color='%23fff' stop-opacity='0'/%3E%3C/radialGradient%3E%3CclipPath id='b'%3E%3Ccircle cx='95' cy='95' r='88'/%3E%3C/clipPath%3E%3Cg clip-path='url(%23b)'%3E%3Cuse xlink:href='%23c' fill='%23db4437'/%3E%3Cuse xlink:href='%23c' fill='url(%23d)'/%3E%3Cuse xlink:href='%23e' fill='%230f9d58'/%3E%3Cuse xlink:href='%23e' fill='url(%23f)'/%3E%3Cuse xlink:href='%23g' fill='%23ffcd40'/%3E%3Cuse xlink:href='%23g' fill='url(%23h)'/%3E%3Cg fill-opacity='.1'%3E%3Cpath fill='%233e2723' d='M61.3 114.7 21 47.4l39 67.8z'/%3E%3Cpath fill='%23263238' d='m128.8 116.3-.8-.4-37.3 67 38.3-67z'/%3E%3C/g%3E%3Cpath id='e' d='M7 183h83.8l39-39v-29H60.2L7 23.5z'/%3E%3Cpath id='g' d='m95 55 34.6 60L91 183h92V55z'/%3E%3Cpath id='c' d='M21 7v108h39.4L95 55h88V7z'/%3E%3Cpath fill='url(%23a)' d='M95 55v21l78.4-21z'/%3E%3Cpath fill='url(%23i)' d='m21 47.5 57.2 57.2L60.4 115z'/%3E%3Cpath fill='url(%23j)' d='m90.8 183 21-78.3 17.8 10.3z'/%3E%3Ccircle cx='95' cy='95' r='40' fill='%23f1f1f1'/%3E%3Ccircle cx='95' cy='95' r='32' fill='%234285f4'/%3E%3Ccircle cx='95' cy='95' r='88' fill='url(%23k)'/%3E%3Cg fill='%233e2723' fill-opacity='.1'%3E%3Cpath fill='%23fff' d='M129.6 115a40 40 0 0 1-69.2 0L7 24.5 60.4 116a40 40 0 0 0 69.2 0z'/%3E%3Cpath d='M96 55h-.5a40 40 0 1 1 0 80h.5c22 0 40-18 40-40s-18-40-40-40zm-1 127a88 88 0 0 0 88-87.5v.5A88 88 0 0 1 7 95v-.5A88 88 0 0 0 95 182z'/%3E%3Cg fill-opacity='.2'%3E%3Cpath fill='%23fff' d='M130 116.3a39.3 39.3 0 0 0 3.4-32 38 38 0 0 1-3.8 30.7L92 183l38.2-66.5zM95 8a88 88 0 0 1 88 87.5V95A88 88 0 0 0 7 95v.5A88 88 0 0 1 95 8z'/%3E%3Cpath d='M95 54c-22 0-40 18-40 40v1c0-22 18-40 40-40h88v-1z'/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E")}.cool-tools-block .twitter-setup{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 400 400'%3E%3Cpath fill='none' d='M0 0h400v400H0z'/%3E%3Cpath fill='%231da1f2' d='M153.62 301.59c94.34 0 145.94-78.16 145.94-145.94 0-2.22 0-4.43-.15-6.63A104.36 104.36 0 0 0 325 122.47a102.38 102.38 0 0 1-29.46 8.07 51.47 51.47 0 0 0 22.55-28.37 102.79 102.79 0 0 1-32.57 12.45 51.34 51.34 0 0 0-87.41 46.78A145.62 145.62 0 0 1 92.4 107.81a51.33 51.33 0 0 0 15.88 68.47A50.91 50.91 0 0 1 85 169.86v.65a51.31 51.31 0 0 0 41.15 50.28 51.21 51.21 0 0 1-23.16.88 51.35 51.35 0 0 0 47.92 35.62 102.92 102.92 0 0 1-63.7 22 104.41 104.41 0 0 1-12.21-.74 145.21 145.21 0 0 0 78.62 23'/%3E%3C/svg%3E")}.shake-invite-member-block{background-color:var(--color-brand-primary);border-radius:var(--size-border-radius-large);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);clear:both;margin-bottom:var(--size-spacing-double);margin-right:var(--size-spacing-half);margin-top:var(--size-spacing-double);position:relative}.shake-invite-member-block h3{color:var(--color-text-light-emphasis);padding:var(--size-spacing-half-again) var(--size-spacing-default);text-shadow:.05em .05em .05em var(--color-base-black-transparent-500)}.shake-invite-member-block form{padding:0 var(--size-spacing-default) var(--size-spacing-double)}.shake-invite-member-block .shake-input-wrapper{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);display:flex;padding:var(--size-spacing-half);position:relative}.shake-invite-member-block .input-text{background-color:var(--color-background-content);border:0;border-radius:var(--size-border-radius-default);flex:1}.shake-invite-member-block .invite-button{flex:none;margin-left:var(--size-spacing-half)}.shake-invite-member-block .shake-results{background-color:var(--color-background-content);border:1px solid var(--color-border-default);border-radius:var(--size-border-radius-default);box-shadow:var(--size-spacing-half) var(--size-spacing-half) 0 var(--color-base-black-transparent-100);display:none;left:0;list-style:none;margin:0;padding:var(--size-spacing-half);position:absolute;top:var(--size-spacing-quadruple);width:220px}.shake-invite-member-block .shake-results li{align-items:center;border-radius:var(--size-border-radius-default);cursor:pointer;display:flex;font-size:.875rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-half)}.shake-invite-member-block .shake-results li:focus,.shake-invite-member-block .shake-results li:hover{background-color:var(--color-bg-success-pastel)}.shake-invite-member-block .shake-results li img{flex:none;height:var(--size-avatar-tiny);margin-right:var(--size-spacing-half);width:var(--size-avatar-tiny)}.shake-invite-member-block .shake-results li span{flex:1}.shake-sidebar-actions{display:flex;margin-bottom:var(--size-spacing-double);margin-top:var(--size-spacing-default)}.shake-sidebar-actions>*{flex:none}.shake-sidebar-actions>*+*{margin-left:var(--size-spacing-default)}.shake-sidebar-actions .follow h4,.shake-sidebar-actions .icon,.shake-sidebar-actions .user-follow h4 a{display:none}.shake-sidebar-editor-block{align-items:center;background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);clear:both;display:flex;font-size:.875rem;font-weight:var(--number-font-weight-bold);margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.shake-sidebar-editor-block a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.shake-sidebar-editor-block a:active,.shake-sidebar-editor-block a:focus,.shake-sidebar-editor-block a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.shake-sidebar-editor-block a:active,.shake-sidebar-editor-block a:focus,.shake-sidebar-editor-block a:hover{text-decoration:underline}.shake-sidebar-editor-block .editor-image{flex:none}.shake-sidebar-editor-block .editor-image .avatar--img{display:block;width:var(--size-avatar-default)}.shake-sidebar-editor-block .editor-details{flex:1;margin-left:var(--size-spacing-default)}.sidebar-flag-nsfw{margin-top:var(--size-spacing-triple)}.sidebar-flag-nsfw .flag-nsfw-button,.sidebar-flag-nsfw .unflag-nsfw-button{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-danger-pastel);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-danger-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .flag-nsfw-button:hover,.sidebar-flag-nsfw .unflag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:hover{color:var(--color-text-light-emphasis)}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:focus{outline:none}.sidebar-flag-nsfw .flag-nsfw-button:disabled,.sidebar-flag-nsfw .unflag-nsfw-button:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.sidebar-flag-nsfw .flag-nsfw-button:disabled,.sidebar-flag-nsfw .unflag-nsfw-button:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.sidebar-flag-nsfw .flag-nsfw-button:focus,.sidebar-flag-nsfw .flag-nsfw-button:hover,.sidebar-flag-nsfw .unflag-nsfw-button:focus,.sidebar-flag-nsfw .unflag-nsfw-button:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.flag-image{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-warning-pastel);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-283.8 415 7 9'%3E%3Cpath fill='red' d='M-282.8 415h6v5h-6z'/%3E%3Cpath fill='none' d='M-289 370.5h32.2'/%3E%3Cpath fill='%23695a5a' d='M-283.8 415h1v9h-1z'/%3E%3C/svg%3E");background-position:.5em;background-repeat:no-repeat;background-size:7px 9px;border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-warning-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;margin-top:var(--size-spacing-triple);padding:0 .88em 0 1.5em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.flag-image:focus,.flag-image:hover{color:var(--color-text-light-emphasis)}.flag-image:focus{outline:none}.flag-image:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.flag-image:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.flag-image:focus,.flag-image:hover{background-color:var(--color-background-button-warning-pastel-hover);color:var(--color-text-button-warning-pastel-hover)}.flag-image.flag-image-set{color:var(--color-status-danger)}.delete-post-text{background-color:var(--color-base-gray-700);background-color:var(--color-background-button-danger-pastel);border:none;border-radius:.5em;color:var(--color-text-light-emphasis);color:var(--color-text-button-danger-pastel);cursor:pointer;display:inline-block;font-size:18px;font-size:12px;font-weight:500;font-weight:400;font-weight:600;height:2.2em;letter-spacing:.033em;letter-spacing:.015em;line-height:2.2em;margin-top:var(--size-spacing-triple);padding:0 .88em;text-align:center;text-decoration:none;text-shadow:.05em .05em .05em var(--color-base-black-transparent-500);text-shadow:none;transition-duration:var(--time-speed-quick);transition-property:background-color,color;user-select:none;vertical-align:middle;white-space:nowrap}.delete-post-text:focus,.delete-post-text:hover{color:var(--color-text-light-emphasis)}.delete-post-text:focus{outline:none}.delete-post-text:disabled{background-color:var(--color-status-disabled)!important;color:var(--color-text-light)!important;cursor:default;text-shadow:none}.delete-post-text:disabled{background-color:var(--color-status-disabled-pastel-light)!important;color:var(--color-status-disabled-pastel-dark)!important}.delete-post-text:focus,.delete-post-text:hover{background-color:var(--color-background-button-danger-pastel-hover);color:var(--color-text-button-danger-pastel-hover)}.sidebar-stats{background-color:var(--color-bg-secondary-brand-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;font-weight:var(--number-font-weight-bold);margin-top:var(--size-spacing-triple)}.sidebar-stats,.sidebar-stats .tab{padding:var(--size-spacing-default)}.sidebar-stats .tab{display:block}.sidebar-stats .selected .tab{background-color:var(--color-background-content);border-top-left-radius:var(--size-border-radius-large);border-top-right-radius:var(--size-border-radius-large)}.sidebar-stats .enable-cursor{cursor:pointer}.sidebar-stats-tabs{display:flex;list-style:none;margin:0;padding:0}.sidebar-stats-tabs>*{flex:1}.sidebar-stats-views{color:var(--color-page-text-secondary)}.sidebar-stats-saves{color:var(--color-status-warning)}.sidebar-stats-hearts{color:var(--color-status-danger)}.sidebar-stats-content{background-color:var(--color-background-content);border-bottom-left-radius:var(--size-border-radius-large);border-bottom-right-radius:var(--size-border-radius-large);display:none;padding:var(--size-spacing-half-again)}.sidebar-stats-content .user-action{align-items:center;display:flex}.sidebar-stats-content .user-action+.user-action{margin-top:var(--size-spacing-default)}.sidebar-stats-content .icon{margin-right:var(--size-spacing-half)}.sidebar-stats-content .icon .avatar--img{display:block;height:var(--size-avatar-tiny);width:var(--size-avatar-tiny)}.sidebar-stats-content .name{word-wrap:break-word;color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.sidebar-stats-content .name:active,.sidebar-stats-content .name:focus,.sidebar-stats-content .name:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.sidebar-stats-content .name:active,.sidebar-stats-content .name:focus,.sidebar-stats-content .name:hover{text-decoration:underline}.sidebar-stats-content .date{color:var(--color-page-text-secondary);font-size:.75rem;font-weight:400;margin-left:var(--size-spacing-default);white-space:nowrap}.meta-data{margin-top:var(--size-spacing-triple)}.meta-data h4{font-size:.875rem;padding-left:var(--size-spacing-half-again)}.meta-data h4,.meta-data p{color:var(--color-page-text-secondary)}.meta-data p{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-default);font-size:.75rem;margin:var(--size-spacing-half) 0;padding:var(--size-spacing-default) var(--size-spacing-half-again)}.shake-details-title{color:var(--color-page-text-secondary);font-size:.875rem;margin-bottom:var(--size-spacing-half);margin-top:var(--size-spacing-triple);padding-left:var(--size-spacing-half-again)}.in-these-shakes{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);font-size:.875rem;margin:0 0 var(--size-spacing-triple) 0;padding:var(--size-spacing-half-again)}.in-these-shakes ul{list-style:none;margin:0;padding:0}.in-these-shakes li{align-items:center;display:flex;margin-bottom:var(--size-spacing-default)}.in-these-shakes a{word-wrap:break-word;color:var(--color-text-link-primary);flex:1;font-weight:var(--number-font-weight-bold);overflow-wrap:break-word;text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none;word-break:break-word}.in-these-shakes a:active,.in-these-shakes a:focus,.in-these-shakes a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.in-these-shakes a:active,.in-these-shakes a:focus,.in-these-shakes a:hover{text-decoration:underline}.in-these-shakes .delete-from-shakes-form,.in-these-shakes .input-checkbox{flex:none;margin-right:var(--size-spacing-default)}.permalink-social{display:flex;list-style:none;margin:0;padding:0}.permalink-social li{flex:none}.permalink-social li+li{margin-left:var(--size-spacing-default)}.permalink-social a{display:block}.permalink-social a:focus,.permalink-social a:hover{opacity:.66}.permalink-social img{display:block}.permalink-social .tumblr a{background-color:#001935;display:flex;height:21px;width:21px}.permalink-social .tumblr a img{height:12px;margin:auto;width:9px}.site-footer{color:var(--color-page-text-secondary);font-size:.75em;margin:var(--size-spacing-triple);text-align:center}.site-footer p{margin:0}.site-footer p+p{margin-top:.5em}.site-footer a{white-space:nowrap}.site-header{display:flex}.site-branding{margin-right:1em;margin-top:1em;width:211px}.site-branding a{display:block}.site-branding a:focus:not(:focus-visible){box-shadow:none}.site-branding--logo{display:block;height:auto;width:100%}.site-nav{flex:1;margin-top:69px;position:relative;text-align:right}.site-nav--list{display:none;flex-direction:column;list-style:none;margin:0;padding:0;position:absolute;right:0;top:47px;z-index:99}.site-nav.is-expanded .site-nav--list{display:flex}@media screen and (min-width:768px){.site-nav--list{display:flex;flex-direction:row;position:static}}.site-nav--item{display:flex;flex:none;justify-content:flex-end}.site-nav--item>*{flex:1}@media screen and (min-width:768px){.site-nav--item>*{flex:none}}.site-nav--item+.site-nav--item{margin-top:.25em}@media screen and (min-width:768px){.site-nav--item+.site-nav--item{margin-left:1em;margin-top:0}}@media screen and (min-width:768px){.site-nav--toggle{display:none}.site-nav--signup,.site-nav--upload{flex-grow:1}}.site-nav--conversations a,.site-nav--popular a,.site-nav--search a{display:block}.site-nav--signup .call-out{color:var(--color-page-text-secondary);display:none;font-size:.875rem;font-weight:var(--number-font-weight-bold);max-width:18em;padding-right:var(--size-spacing-default);padding-top:3px}@media screen and (min-width:768px){.site-nav--signup .call-out{display:block}}.user-counts{background-color:var(--color-border-default);border-radius:var(--size-border-radius-large);clear:both;margin-bottom:var(--size-spacing-double);padding:var(--size-spacing-half-again)}.user-counts ul{display:flex;justify-content:space-between;list-style:none;margin:0;padding:0}.user-counts li{background-color:var(--color-background-content);border-radius:var(--size-border-radius-default);flex-grow:1;flex-shrink:1;margin-right:var(--size-spacing-default);padding:var(--size-spacing-default) 0;text-align:center;width:70px}.user-counts li .num{display:block;font-size:1.125rem;font-weight:var(--number-font-weight-bold)}.user-counts li .label{display:block;font-size:.875rem}.user-counts .views{width:95px}.user-counts .saves{color:var(--color-status-warning)}.user-counts .likes{color:var(--color-status-danger);margin-right:0}.user-follow{display:flex}.user-follow .icon{flex:none;margin-right:var(--size-spacing-default)}.user-follow h4{word-wrap:break-word;font-size:.875rem;margin-bottom:var(--size-spacing-half);overflow-wrap:break-word;word-break:break-word}.user-follow h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);padding-bottom:var(--size-spacing-half);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.user-follow h4 a:active,.user-follow h4 a:focus,.user-follow h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.user-follow h4 a:active,.user-follow h4 a:focus,.user-follow h4 a:hover{text-decoration:underline}.user-follow .follow{flex:1}.user-follow-extended{display:block;margin:var(--size-spacing-double) var(--size-spacing-half)}.user-follow-extended .icon{float:left;margin-right:var(--size-spacing-default)}.user-follow-extended .details{padding-bottom:var(--size-spacing-default)}.user-follow-extended h4 a{font-size:1.125rem}.user-follow-extended .about{display:block;font-size:.875rem;line-height:1.3;margin-bottom:var(--size-spacing-default);white-space:pre-wrap}.user-follow-extended .about,.user-follow-extended .website a{word-wrap:break-word;overflow-wrap:break-word;word-break:break-word}.user-follow-extended .website a{color:var(--color-text-link-primary);font-size:.8rem;font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.user-follow-extended .website a:active,.user-follow-extended .website a:focus,.user-follow-extended .website a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.user-follow-extended .website a:active,.user-follow-extended .website a:focus,.user-follow-extended .website a:hover{text-decoration:underline}.user-follow-extended:after{clear:both;content:"";display:table}.user-nav{background-color:var(--color-background-content);border-bottom-left-radius:1em;font-size:.9rem;padding:.75em 1.5em;position:absolute;right:0;top:0}.user-nav--list{display:flex;list-style:none;margin:0;padding:0}.user-nav--item+.user-nav--item{margin-left:1.5em}.user-nav--link{font-weight:var(--number-font-weight-bold);text-decoration:none;white-space:nowrap}.admin-nav{list-style:none;margin:0;padding:0}.admin-new-users{border:2px solid var(--color-status-danger)}.admin-new-users .body{padding:var(--size-spacing-triple)}.api-accept,.api-decline{float:left;margin-right:var(--size-spacing-triple);padding-bottom:var(--size-spacing-triple)}.content-developer{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-developer{padding:var(--size-spacing-quadruple)}}.content-developer h1{font-size:3.25rem}.content-developer h2{font-size:1.5rem;margin-bottom:var(--size-spacing-half-again);margin-top:var(--size-spacing-half-again)}.content-developer p{line-height:1.4;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-default)}.content-developer dt big{font-size:normal}.content-developer dt big,.content-developer dt em{color:var(--color-page-text-secondary)}.faq-page h1{font-size:2.5rem}.faq-page h2{font-size:1.25rem;margin-top:var(--size-spacing-half-again)}.faq-page li,.faq-page p{font-size:.875rem;line-height:2;margin-top:5px}.faq-page li a,.faq-page p a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.faq-page li a:active,.faq-page li a:focus,.faq-page li a:hover,.faq-page p a:active,.faq-page p a:focus,.faq-page p a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.faq-page li a:active,.faq-page li a:focus,.faq-page li a:hover,.faq-page p a:active,.faq-page p a:focus,.faq-page p a:hover{text-decoration:underline}.content-find-shakes .header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));padding:1.5rem 1.875rem 1rem;width:100%}.content-find-shakes .header .avatar{display:flex}.content-find-shakes .header .avatar img{display:block;height:50px;width:50px}.content-find-shakes .header .avatar-media{flex:none}.content-find-shakes .header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.content-find-shakes .header h2{font-size:2.25rem}}@media screen and (min-width:768px){.content-find-shakes .header h2{font-size:2.625rem}}.content-find-shakes .body{padding:var(--size-spacing-double)}.content-find-shakes .good-folk-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-half-again)}.content-find-shakes .good-folk-block h3{color:var(--color-status-success-pastel-dark);font-size:1.125rem;padding-left:var(--size-spacing-half)}.content-find-shakes .good-folk-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);color:var(--color-page-text);margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.content-find-shakes .good-folk-block-content p{font-size:.875rem;margin-top:0}.content-find-shakes .good-folk-block-content .user-follow{margin-top:var(--size-spacing-double)}.content-find-shakes .find-shakes-navigation{display:flex;list-style:none;margin:0;padding:0}.content-find-shakes .find-shakes-navigation li{background:var(--color-background-content-secondary);flex:1;font-size:1.125rem}.content-find-shakes .find-shakes-navigation li a{display:block;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-double) var(--size-spacing-half);text-align:center;text-decoration:none}.content-find-shakes .find-shakes-navigation .selected a{background-color:var(--color-background-content);color:var(--color-page-text)}.content-find-shakes .featured-shakes{margin-bottom:var(--size-spacing-double)}.content-find-shakes .featured-shakes h3{font-size:1.125rem;margin:var(--size-spacing-double) var(--size-spacing-default)}.content-find-shakes .featured-shakes ul{display:flex;flex-wrap:wrap;list-style:none;margin:0;padding:0}.content-find-shakes .featured-shakes li{border:1px solid var(--color-border-default);box-shadow:1px 2px 1px var(--color-base-black-transparent-100);margin-bottom:var(--size-spacing-default);margin-right:var(--size-spacing-default);padding:var(--size-spacing-default);width:calc(172px + var(--size-spacing-default)*2)}.content-find-shakes .featured-shakes li img{height:170px;width:170px}.content-find-shakes .featured-shakes li h4{font-size:1.125rem;line-height:1.2;margin:var(--size-spacing-default) 0 0}.content-find-shakes .featured-shakes li h4 a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.content-find-shakes .featured-shakes li h4 a:active,.content-find-shakes .featured-shakes li h4 a:focus,.content-find-shakes .featured-shakes li h4 a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.content-find-shakes .featured-shakes li h4 a:active,.content-find-shakes .featured-shakes li h4 a:focus,.content-find-shakes .featured-shakes li h4 a:hover{text-decoration:underline}.content-find-shakes .featured-shakes li p{-webkit-box-orient:vertical;-webkit-line-clamp:3;display:-webkit-box;font-size:.75rem;margin:var(--size-spacing-default) 0 0;overflow:hidden}.content-find-shakes .shake-category{border-bottom:1px dashed var(--color-border-default);clear:both}.content-find-shakes .shake-category .category-title{font-size:1.125rem}.content-find-shakes .shake-category .category-title a{display:block;padding:var(--size-spacing-half-again) var(--size-spacing-default);text-decoration:none}.content-find-shakes .shake-category .shake-category-body{display:none;padding:0 var(--size-spacing-default)}.content-find-shakes .shake-category.shake-category-selected .category-title a{color:var(--color-page-text-secondary)}.content-find-shakes .shake-category.shake-category-selected .shake-category-body{display:block}.content-find-shakes .shake-tips{background:var(--color-background-content-secondary);clear:both;color:var(--color-page-text);font-size:.75rem;margin:var(--size-spacing-triple) 0;padding:var(--size-spacing-half-again)}.content-find-shakes .friend+.friend{border-top:1px dashed var(--color-border-default)}.content-find-shakes .user-follow-extended .website a{color:var(--color-text-link)}.content-find-shakes .user-follow-extended .website a:active,.content-find-shakes .user-follow-extended .website a:focus,.content-find-shakes .user-follow-extended .website a:hover{color:var(--color-text-link-hover)}.content-find-shakes .body .loading{color:var(--color-page-text-secondary);font-size:1.125rem;margin:var(--size-spacing-quadruple) auto;text-align:center}.content-find-shakes .body .loading img{margin-right:var(--size-spacing-double);position:relative;top:var(--size-spacing-double)}.content-find-shakes .message{color:var(--color-page-text-secondary);font-size:1.125rem;margin:var(--size-spacing-quadruple) var(--size-spacing-triple);text-align:center}.content-find-shakes .intro{border-bottom:1px solid var(--color-border-default);font-size:.875rem;padding:var(--size-spacing-double) var(--size-spacing-half)}.content-find-shakes .refresh-friends{margin:var(--size-spacing-triple) 0}.content-incoming .tip-block{background:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.content-incoming .tip-block h3{color:var(--color-brand-primary);font-size:1.3rem;margin:0 0 var(--size-spacing-half-again)}.content-incoming .tip-block p{color:var(--color-page-text-secondary);font-size:.875rem;margin:0}.incoming-header{background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));cursor:pointer;padding:1.5rem 1.875rem 1rem;padding:var(--size-spacing-half-again) var(--size-spacing-double);width:100%}.incoming-header .avatar{display:flex}.incoming-header .avatar img{display:block;height:50px;width:50px}.incoming-header .avatar-media{flex:none}.incoming-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.incoming-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.incoming-header h2{font-size:2.625rem}.incoming-header{align-items:center;display:flex;flex-direction:row}}.incoming-header:before{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 786 229'%3E%3Cg fill='none' transform='translate(-.002)'%3E%3Cpath fill='%23FF0' d='M142.262 137.93s66.31-14 66.31-40 5.62-64 41.86-64c10.67 0 41-7.41 105.24-9 154-3.81 379-4 379-4s51 10 51 48 6 116-51 116-270 5-323 5-147.5 6.5-154 0c-3.36-3.36-11.86-3.3-23.18-13-10.56-9-24.1-35-24.1-35s-12.08 8.46-32.37 7c-27.82-2-35.76-11-35.76-11Z'/%3E%3Cpath fill='%23926F35' d='M140.322 186.17c0 6.73-.16 17.42-.06 33.21h-10.74v-8.13c-.06-1.71-.06-2.07-.06-3.64 0-2.91-.79-5-4.68-4.95-4.19 0-4.05 3.51-4.09 6.71v10h-11.38c.1-15.74.23-27.25.28-34'/%3E%3Cpath fill='%23000' d='M103.762 228.69s-.55-4.54 3.59-5.14v-7.31s-.26-2.77 2-2.88h11.38s1.86.21 1.86 2.89v7.63s1.15 1.26.93 4.82m22.48-.01s.56-4.54-3.59-5.14v-7.31s.26-2.77-2-2.88h-11.41s-1.86.21-1.86 2.89v7.63s-1.15 1.26-.93 4.82'/%3E%3Cpath fill='%2300D4DE' d='M140.752 205.49c9.42-1.15 16-4.29 21.52-8.84 18.66-15.39 21.61-49 20.71-77.52 0-43.37-15-76.79-59.16-76.21-44.21-.58-59 33.35-59 79.28-.68 36 6.9 77.14 45.91 83.19.04 0 15.51 2.3 30.02.1Z'/%3E%3Cpath fill='%2300F2F2' d='M117.642 205.39c-39-6.05-44.26-53.55-44.79-83.19-.49-27.57 3.7-79.25 54.42-79.25h-3.46c-44.21-.58-59 33.35-59 79.28-.68 36 6.9 77.14 45.91 83.19a122.92 122.92 0 0 0 19.52 1 118.24 118.24 0 0 1-12.6-1.03Z'/%3E%3Cellipse cx='126.332' cy='135.97' fill='%23FF3560' rx='32.12' ry='23.45'/%3E%3Cpath fill='%23E31451' d='M126.332 139.15a21 21 0 0 0-20.09 14.3 36.81 36.81 0 0 0 40.19 0 21 21 0 0 0-20.1-14.3Z'/%3E%3Cpath fill='%2300F2F2' d='M131.892 205.39c39-6.05 49.79-47.16 49.11-83.19 0-44.73-10.31-78-58.75-79.25h3.46c44.21-.58 59 33.35 59 79.28.68 36-6.9 77.14-45.91 83.19a122.93 122.93 0 0 1-19.52 1 118.29 118.29 0 0 0 12.61-1.03Z'/%3E%3Cpath fill='%23B3C535' d='M71.002 182.25s14 9.62 44 10.33a11.15 11.15 0 0 1 6.56 2.85c2.21 2.16 3.42 6.14 3.42 11.4l-50.7 5.38s-.36-17.14-3.28-29.96Z'/%3E%3Cpath fill='%23B3C535' d='M178.002 183.69s-12.24 7.69-44.66 8.89a11.15 11.15 0 0 0-6.55 2.85c-2.21 2.16-3.42 6.14-3.42 11.4l50.7 5.38s1.01-15.7 3.93-28.52Z'/%3E%3Cpath fill='%23FFF' d='M117.482 110.43s-34.5.16-38.58 37.87c0 0-1.45 5.45 1.62 12.86 0 0 3.37 6.33 7 1.28 0 0 4 7.58 7.1.92 0 0 4 2.25 1.79-5 0 0-7.12-14.63-1.94-28.91.03 0 3.41-12.71 23.01-19.02Zm18.07-.56s34.5.16 38.58 37.87c0 0 1.45 5.45-1.62 12.86 0 0-3.37 6.33-7 1.28 0 0-4 7.58-7.1.92 0 0-4 2.25-1.79-5 0 0 7.12-14.63 1.94-28.91-.02 0-3.41-12.71-23.01-19.02Z'/%3E%3Cpath fill='%23FF3560' d='M116.312 136.27c-2.52 2.65-4.43 5.27-9 6.78 0 0-15.48 4.21-11-11.27 0 0 6.28-20.63 31.85-20.63 0 0 22.43.45 27.59 20.63 0 0 2 8.07-2 10.77 0 0-8.07 6.06-13.91-3.36-1.15-2.13-4.73-6.16-6.62-7.18a12.41 12.41 0 0 0-11.66-.22 30.6 30.6 0 0 0-5.25 4.48Z'/%3E%3Cpath fill='%23FFF' d='M132.482 111.56a25.8 25.8 0 0 0-4.28-.51c-11.14 0-18.61 3.91-23.5 8.33a4.42 4.42 0 0 0 3.56 2.2c2.1 0 3.9-1.92 4.69-4.66.79 2.74 2.59 4.66 4.69 4.66 2.28 0 4.21-2.27 4.88-5.4.66 3.13 2.59 5.4 4.88 5.4 2.45 0 4.49-2.61 5-6.1.51 3.49 2.55 6.1 5 6.1 2.16 0 4-2 4.75-4.88.76 2.86 2.6 4.88 4.75 4.88a4 4 0 0 0 2.78-1.24c-5.43-5.75-12.68-7.95-17.2-8.78Z'/%3E%3Cpath fill='%23FFD265' d='m78.782 201.24 3.52-4.83 3.46 4.83-3.49-2.41z'/%3E%3Cpath fill='%233FAEB1' d='m76.622 194.6 3.15 3.54-.99 3.1 3.51-1.36 3.47 1.36-1.08-3.12 3.17-3.48-11.23-.04z'/%3E%3Cpath fill='%23FFEB6D' d='m82.272 190.49 1.34 4.11h4.31l-3.49 2.54 1.33 4.1-3.49-2.54-3.49 2.54 1.33-4.1-3.49-2.54h4.32l1.33-4.11z'/%3E%3Cpath fill='%23FFD265' d='m76.622 194.6 5.59 1.81-2.1.73-3.49-2.54Zm11.24 0-5.59 1.81 2.1.73 3.49-2.54Zm76.44 6.64 3.52-4.83 3.46 4.83-3.49-2.41-3.49 2.41Z'/%3E%3Cpath fill='%233FAEB1' d='m162.152 194.6 3.14 3.54-.99 3.1 3.51-1.36 3.47 1.36-1.08-3.12 3.18-3.48-11.23-.04z'/%3E%3Cpath fill='%23FFEB6D' d='m167.792 190.49 1.34 4.11h4.31l-3.49 2.54 1.33 4.1-3.49-2.54-3.49 2.54 1.34-4.1-3.49-2.54h4.31z'/%3E%3Cpath fill='%23FFD265' d='m162.152 194.6 5.59 1.81-2.1.73-3.49-2.54Zm11.24 0-5.6 1.81 2.11.73 3.49-2.54Z'/%3E%3Cpath fill='%2300F2FF' d='M181.842 153.94a26.74 26.74 0 0 0 19.29-15.09s5.48-.26 7.44 2.87c0 0-8.23 20-29.86 19.72'/%3E%3Cpath fill='%23000' d='M207.802 142.39a6.86 6.86 0 0 0 2.58-5.15c.29-4.31-4.31-4.59-4.31-4.59.57-4.83-1-5.1-1-5.1a1.15 1.15 0 0 0-1.57.72 8.1 8.1 0 0 0-.37 2.18c-.08 1.2-.21 2.51-.37 3.49a28 28 0 0 1-1.28 4.88s-.53 1.68 2.16 2.88a8 8 0 0 0 4.16.69Z'/%3E%3Cpath fill='%2300F2FF' d='M70.552 162s-11.1 1.18-17.24 14.5c0 0-5.48.26-7.44-2.87 0 0 8.23-20 29.86-19.72'/%3E%3Cpath fill='%23000' d='M53.912 175.42s0 5.37-4.3 5.08c0 0-4.69.1-5-3.91a3.36 3.36 0 0 1 2.15-3.22 14.39 14.39 0 0 1 7.15 2.05Z'/%3E%3Cpath fill='%23FF0180' d='M252.552 151.62 249.602 82l15-.63 3 69.62-15.05.63Zm56.39-72.13 14-.59 2.56 60.37c.31 7.27-3.73 10.36-9 10.58-3.74.16-6.65-2.11-8.46-5.47l-20-37.87 1.82 43.49-14 .59-2.56-60.37c-.31-7.27 3.72-10.36 9-10.58 4.78-.2 7.58 2 9.28 5.23l19.11 35.83-1.75-41.21Zm59.43-2.49 4.16-.18.6 14.24-4.36.19c-13.72.58-22.59 9.91-22.09 21.55s10.12 20.18 23.84 19.6l4.36-.19.6 14.24-4.16.18c-21.41.91-38.73-12.41-39.61-33.19-.88-20.78 15.29-35.56 36.66-36.44Zm44.01-2.83a35.78 35.78 0 1 1-34.23 37.26c-.835-19.74 14.49-36.422 34.23-37.26Zm2.4 56.53a20.8 20.8 0 1 0-21.66-19.9c.535 11.452 10.204 20.33 21.66 19.89v.01Zm69.08 6.54-13.16-35.56 1.72 40.63-14 .59-2.53-59.64c-.32-7.59 4.94-11.14 9.51-11.33 5.61-.24 9.32 3.67 11.2 8.89l13.19 36.08 10.09-37.07c1.44-5.37 4.8-9.57 10.41-9.81 4.57-.19 10.12 2.9 10.44 10.49l2.53 59.64-14 .59-1.72-40.63-10.11 36.55c-1.18 4.11-3.51 5.67-6.52 5.79-3.01.12-5.52-1.21-7.05-5.21Zm47.68 2.56-3-69.62 15-.63 3 69.62-15 .63Zm56.38-72.14 14-.59 2.56 60.37c.31 7.27-3.72 10.36-9 10.58-3.74.16-6.65-2.11-8.46-5.47l-20.02-37.87 1.85 43.54-14 .59-2.56-60.37c-.31-7.27 3.73-10.36 9-10.58 4.78-.2 7.58 2 9.28 5.23l19.11 35.83-1.76-41.26Zm63.99 11.55-5.4.23c-9.87.42-21.67 6.85-21 22.33a19.57 19.57 0 0 0 20.37 18.92l-1.25-28.89 15-.63 1.32 31.07c.25 6-.94 12.12-10.61 12.52l-2.49.11c-19.85.84-36.27-13.35-37.09-32.57-.86-20.26 13.46-36.17 35.9-37.12l4.68-.2.57 14.23Zm17.85 33.73-2.35-55.35 15.38-.65 2.35 55.35-15.38.65Zm7.92 3.89a8.893 8.893 0 1 1 .75 17.77 8.893 8.893 0 0 1-.75-17.77Zm18.96-5.02-2.34-55.36 15.38-.65 2.32 55.35-15.36.66Zm7.92 3.89a8.893 8.893 0 1 1 .75 17.77 8.893 8.893 0 0 1-.75-17.77Zm18.96-5.03-2.35-55.35 15.38-.65 2.35 55.33-15.38.67Zm7.92 3.89a8.893 8.893 0 1 1 .75 17.77 8.893 8.893 0 0 1-.75-17.77Z'/%3E%3Cpath fill='%23FF0' d='M184.692 123.52c-1.2.6-2.417 1.18-3.65 1.74-18.22 8.33-38.77 12.67-38.77 12.67s7.93 9 35.76 11l1.16.07c1.42.08 3.66.1 5 .09 1.11-8.44.61-17.09.5-25.57Z'/%3E%3Cpath fill='%23FFF' d='m160.262 58-3.2 2.6a28 28 0 0 1 7.58 13.22l4.83-.75a30.91 30.91 0 0 0-9.21-15.07Zm-.78-.7a38.33 38.33 0 0 0-12.08-7.11l-1.5 2.56a41.31 41.31 0 0 1 10.42 7.12l3.16-2.57Zm-6.7 6.7a26.88 26.88 0 0 1 5.22 10.82l5.58-.86a27.08 27.08 0 0 0-7.36-12.72l-3.44 2.76Zm-7.42-10.43-1.65 2.81a29.23 29.23 0 0 1 8.41 6.82l3.37-2.74a40.24 40.24 0 0 0-10.13-6.84v-.05Z'/%3E%3Cpath fill='%2300D4DE' d='M227.172 69.06c23.45-11.72 40.37-33.56 1-69.06 0 0 30.75 45.87-53.94 46.38v31.25s30.77 2.52 52.94-8.57Z'/%3E%3Cpath fill='%2300B7DD' d='m236.882 63.34-11.22 5.3 11.59-19.16-18.93 9.06 17.42-17.63-18.43 9.55 11.88-15.09-5.6.6c19.41-13.69 4.59-35.89 4.59-35.89 33.6 30.31 26.18 50.66 8.7 63.26Z'/%3E%3Cpath fill='%2300D4DE' d='M24.522 69.06C1.072 57.34-15.848 35.5 23.512 0c0 0-30.75 45.87 53.94 46.38v31.25s-30.75 2.52-52.93-8.57Z'/%3E%3Cpath fill='%23000' d='m66.392 112.27.06.13.08.13c.2.32 5 7.91.57 13.94l4.9 3.6a15.56 15.56 0 0 0 3-9 22.83 22.83 0 0 0-3.2-11.67 21.43 21.43 0 0 1-2.49-10.4 17.31 17.31 0 0 1 1.58-7.11L65.482 89c-.23.46-5.59 10.71.91 23.27Zm116.89 12.11.07.13.08.13c.2.32 5 7.91.57 13.94l4.94 3.61a15.56 15.56 0 0 0 3-9 22.81 22.81 0 0 0-3.29-11.68 21.44 21.44 0 0 1-2.49-10.37 17.24 17.24 0 0 1 1.58-7.11l-5.4-2.89c-.19.44-5.56 10.69.94 23.24Z'/%3E%3Cpath fill='%23B3C535' d='M193.932 99c-3.19-6.9-2.36-17.26-2.06-22.46.82-14.31-.62-21.19-3.75-31.85-4.62-15.8-22.59-24.69-40.5-28.07-12.57-2.39-30.6-2.39-45.34 0-17.99 2.92-33.92 13.38-40.51 28.09-3.32 7.44-4.35 20.74-3.77 31.85.3 5.19.92 18.21-2 22.44a15 15 0 0 1-5.36 4.77c10.5.6 21.46 0 32-.35 14.1-.45 70.53-.45 84.63 0 10.56.34 21.52.94 32 .35a9.64 9.64 0 0 1-5.34-4.77Z'/%3E%3Cpath fill='%2300B7DD' d='m14.812 63.34 11.19 5.3-11.56-19.16 21.17 14.62-19.61-23.19 21.63 9.21-15.13-14.75 5.6.6C8.642 22.28 23.512.08 23.512.08c-33.6 30.31-26.18 50.66-8.7 63.26Z'/%3E%3Cpath fill='%23E3EB9A' d='m171.582 27.84-4.15 3.72c7.13 4.88 12 10.73 14.62 17.32a29 29 0 0 0 3.29-5.57c-2.71-6.31-7.58-11.31-13.76-15.47Zm-5 3.14 4.14-3.71a57.28 57.28 0 0 0-17.35-7.77c-1.59.76-4.36 2-5.84 2.8a78.09 78.09 0 0 1 16.11 6.86 32.665 32.665 0 0 1 2.93 1.84l.01-.02Zm.1 1.27-4.68 4.19c7.35 5.07 12.76 11.23 15.49 18a18.65 18.65 0 0 0 3.9-4.72c-2.45-6.66-7.39-12.52-14.72-17.48l.01.01Zm-3.54-2.25a77.53 77.53 0 0 0-16.91-7c-2.19 1.15-4.25 2.28-6.13 3.35a77.56 77.56 0 0 1 21 9.54l4.67-4.19a64.087 64.087 0 0 0-2.63-1.7Z'/%3E%3Cpath fill='%23CADE35' d='M81.672 25.89s-20.19 10.77-20.19 37c0 0-.34 19.86 0 24.23 0 0 1 10.77-4 14.14 0 0 7.74-2 7.74-15.82V63.59s.29-25.24 16.45-37.7Z'/%3E%3Cpath fill='%2300F2F2' d='M17.252 8S.892 24.81 1.432 39.33c0 0-.67 11.11 13.13 22.21 0 0-10.92-9.57-10.77-22.89 0 .01-.47-12.19 13.46-30.65Zm217.36 0s16.39 16.81 15.81 31.33c0 0 .67 11.11-13.13 22.21 0 0 10.92-9.57 10.77-22.89.01.01.47-12.19-13.45-30.65Z'/%3E%3Ccircle cx='143.162' cy='81.55' r='10.39' fill='%23FFF'/%3E%3Ccircle cx='144.162' cy='79.55' r='3.23' fill='%23ED4928'/%3E%3Ccircle cx='102.592' cy='81.55' r='10.39' fill='%23FFF'/%3E%3Ccircle cx='103.592' cy='79.55' r='3.23' fill='%23ED4928'/%3E%3C/g%3E%3C/svg%3E") 50% no-repeat;background-size:100%;content:"";display:block;margin:auto;max-width:100%;padding-top:29%}@media screen and (min-width:480px){.incoming-header:before{margin:0;padding-top:115px;width:395px}}.incoming-header h2{font-size:1.5rem;line-height:1.2;margin-top:var(--size-spacing-half-again);padding-left:0;text-align:center}@media screen and (min-width:768px){.incoming-header h2{font-size:1.75rem;margin-top:0;padding-left:var(--size-spacing-default)}}.code-of-conduct h2{font-size:1.875rem;text-align:center}.code-of-conduct h3{font-size:1.5rem;margin-top:1.4em}.code-of-conduct p{font-size:1rem;line-height:1.4;margin:1em 0}.code-of-conduct li{margin-bottom:.4em}.terms-of-use p{font-size:1rem;line-height:1.4;margin:1em 0}.terms-of-use li{font-size:1rem;list-style-type:lower-alpha;margin-left:var(--size-spacing-quadruple)}.terms-of-use .terms-center{text-align:center}.tou-notice-page p{font-size:1rem;line-height:1.4;margin:1em 0}.content-membership{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-membership{padding:var(--size-spacing-quadruple)}}.content-membership h1{line-height:1;text-shadow:2px 4px 1px var(--color-base-black-transparent-100)}.content-membership ul{padding-left:1em}.content-membership li+li{margin-top:1em}.content-membership .fine-print{font-size:.75rem}.content-membership .subscribe-plan-quantity-wrapper{color:var(--color-page-text-secondary);font-size:2rem}.content-membership .input-plan-quantity{display:inline-block;font-size:inherit;font-weight:var(--number-font-weight-bold);max-width:150px;width:auto}.content-membership .input-plan-quantity:invalid{color:var(--color-status-danger)}.content-membership .subscribe-plan-quantity-wrapper i{color:var(--color-page-text-secondary);font-size:1.5rem;font-style:normal}.content-migrate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 732 961'%3E%3Cg fill='none' transform='translate(-.003 -.005)'%3E%3Cpath fill='%23C2E3AE' d='M123.803 229.565c16.75 1.23 32.6 7.32 45.13 20.1 34.36 35 35.75 97.29 35.75 142.88 0 23 8.43 85.06-26.79 87.53 19 4.86 32.91 22.67 47 35.1a282.7 282.7 0 0 0 39.4-43.76c20.18-27.82 25-60.21 23.2-93.78-1.3-24.71-4.42-61.13-28.5-75.34-35-20.64-76.66-71.73-61.81-144.31 26.82-130.98 181.09-207.27 288.36-110.04 61.17 55.44 30.16 145.86-27.2 192.54-65 52.9-138.55 66.14-138.55 66.14a352 352 0 0 1 5.55 76.27c-3.29 85.7-74.56 158.94-129.18 218.78-11.66 12.77-31 30.37-30.71 49.62.49 27.64 33.78 38.32 56.79 35.23 17.71-2.38 59.46-15.84 67.7-33.71-11.86 25.72-34.42 43.56-62.41 48.32-25.57 4.35-62.75-1.06-75.2-27.48-18.41-39.12 32.67-98.65 51.29-130.86 2.33-4-20.21-26.2-23.2-29.16-6.24-6.16-27.91-23.6-37.55-20.18 0 0-126.18 21.14-135.77-52.44-9.59-73.58-3.34-149.79 29.2-179.32 21.7-19.69 56.19-34.43 87.5-32.13Z'/%3E%3Cpath fill='%23BEE6F8' d='M434.703 489.865s-115.1-3.09-97-118.24c5.88-37.34 31-62.83 67.4-71.48 34-8.09 83-6.85 114.39 8.7 38.84 19.26 63.78 60.12 57.33 103.84-5.61 38-37.46 80.38-79.26 80.38 0 0 36.86 53.88 75.67 12.79 28.44-30.09 29.29-77.85 25.77-116.5-1.43-15.66-5.88-89.71-25.47-93.18-48-8.51-86.38-30-98.19-81-9.34-40.52-8.24-109.17 33.52-130.34 66.36-33.68 204.26-6.27 221.14 77.65 9.77 48.25-40.37 141.52-100.27 130.22 0 0 27.82 101.83 1.07 167.88-9.8 24.08-23.74 49.42-44.47 66.07.34-.27 5.41 26.55 5.47 28.1 1.33 36.41-13.65 78.82-47.05 97.29-36.23 20-78.63 15.5-115.68 1.72 33.16-2 64.6-16.64 95.15-28.46 23.64-9.14 38.34-24.9 42.13-50.65.44-3-1.78-35.11-4.49-34.64-2.93.51-6.62 2.08-9.88 3.32-28.59 10.88-48.78.21-72.62-17.77-9.31-7-18.24-14.64-27.35-21.91-3.15-2.5-13.17-13.65-17.31-13.79Z'/%3E%3Cpath fill='%23DAFFC2' d='M120.803 225.555c16.75 1.23 32.6 7.32 45.13 20.1 34.36 35 35.75 97.29 35.75 142.88 0 23 8.43 85.06-26.79 87.53 19 4.86 32.91 22.67 47 35.1a282.7 282.7 0 0 0 39.4-43.76c20.18-27.82 25-60.21 23.2-93.78-1.3-24.71-4.42-61.13-28.5-75.34-35-20.64-76.66-71.73-61.81-144.31 26.82-130.97 181.09-207.26 288.36-110.03 61.17 55.44 30.16 145.86-27.2 192.54-65 52.9-138.55 66.14-138.55 66.14a352 352 0 0 1 5.55 76.27c-3.29 85.7-74.56 158.94-129.18 218.78-11.66 12.77-31 30.37-30.71 49.62.49 27.64 33.78 38.32 56.79 35.23 17.71-2.38 59.46-15.84 67.7-33.71-11.86 25.72-34.42 43.56-62.41 48.32-25.57 4.35-62.75-1.06-75.2-27.48-18.41-39.12 32.67-98.65 51.29-130.86 2.33-4-20.21-26.2-23.2-29.16-6.24-6.16-27.91-23.6-37.55-20.18 0 0-126.18 21.14-135.77-52.44-9.59-73.58-3.35-149.8 29.2-179.33 21.7-19.68 56.19-34.43 87.5-32.13Z'/%3E%3Cellipse cx='356.323' cy='925.975' fill='%23D9D9D9' rx='115.69' ry='34.9'/%3E%3Cpath fill='%2317B6E1' d='M300.923 917.625a19.13 19.13 0 0 0-.49 7.88s.61 3.89 6.19 6.65c3.08 1.52 8.58 3 16 3 0 0 12.64.16 20.44-3 4.92-1.71 9.6-5 11.08-8.86 0 0 1.48-8.62.25-12.8 0 0-1.44-5.32-11.08-4.43-.04-.06-35.99 6.39-42.39 11.56Zm112.21-.35a22.19 22.19 0 0 1 1 8.23s-.05 3.89-5.63 6.65c-3.08 1.52-8.58 3-16 3 0 0-12.64.16-20.44-3-4.92-1.71-9.6-5-11.08-8.86 0 0-1.48-8.62-.25-12.8 0 0 2.21-5.43 11.08-4.43.03-.06 34.91 6.04 41.32 11.21Z'/%3E%3Cpath fill='%2317B6E1' d='M399.203 751.005c.37 23.93 2.62 150.75 3 154.58a46.15 46.15 0 0 1 5.89 4.51c9 8 2.47 15.48-15.06 16.43-27.26.82-31.28-41.13-31.27-41.13l-.41-21.19s.34-7.71-5-7.71c-3.16 0-4.31 1.85-4.31 7.83 0 3.43.32 8.11.19 13v31.41c.07.29.15.57.19.87.24 1.35.268 2.73.08 4.09-.43 5-5 14-32.27 13.18-16.36-.63-7.63-15.5-7.32-21.58 0-3.59.28-119.62.25-129.23v-14.06m87.23-9.35a52.13 52.13 0 0 0-49.78-8.89'/%3E%3Cpath fill='%2300A3DE' d='m465.003 774.295-12.25-16.71 1.37-1.88c-.39-9.71 2.88-11.82 2.88-11.82 3.38-2.77 6.39.54 6.67.85l.31-.33c5.43-9 11.38-4.77 11.38-4.77 10 12.44-10.36 34.66-10.36 34.66Z'/%3E%3Cpath fill='%2317B6E1' d='M398.683 772.275c12.71 2.2 29.87 2.43 55.44-16.77l13.44 17.32c-26.13 19.61-47.75 22.36-75.28 19'/%3E%3Cpath fill='%2300A3DE' d='m246.713 774.295 12.29-16.71-1.37-1.88c.39-9.71-2.88-11.82-2.88-11.82-3.38-2.77-6.39.54-6.67.85l-.31-.33c-5.43-9-11.38-4.77-11.38-4.77-10.03 12.44 10.32 34.66 10.32 34.66Z'/%3E%3Cpath fill='%2317B6E1' d='M313.003 772.275c-12.71 2.2-29.87 2.43-55.44-16.77l-13.44 17.32c26.13 19.61 47.75 22.36 75.28 19m33.07 84.8c-6 3.78-15.83 3.71-21.61 3.71-14 0-18.5-4.47-18.5-4.47l.45 28.65a55.56 55.56 0 0 0-8.33 6.28c-8.68 8.51-1.34 16 15.26 16.67 27.67.83 32.57-8.15 33-13.25.16-9.21-.27-37.59-.27-37.59Zm9.53 37.58c.44 5.1 4.6 14.08 32.27 13.25 16.6-.63 23.4-7.64 15.27-16.67a40.06 40.06 0 0 0-7.38-6s-.12-13-.4-29.63c0 0-6.09 4.85-18.78 5-5.77.08-14.85 0-21.45-3.53-.02 0 .42 27.84.47 37.58Z'/%3E%3Cpath fill='%23FFF' d='M487.673 635.095c0 67.92-60.48 123-135.07 123s-135.05-55.09-135.05-123 60.46-123 135.05-123 135.07 55.09 135.07 123Z'/%3E%3Cpath fill='%2317B6E1' d='M356.493 489.485s-163.49-5.88-156.35 147.09h.06c8.12 125.27 121.92 132.81 154.2 132.81 39.54 0 146.14-9.32 152.77-132.81 6.63-123.49 12.05-142.77-150.68-147.09Z'/%3E%3Cpath fill='%2317B6E1' d='M354.413 755.885c-29.93 0-128.72-6.26-152.33-102.07 17 108.74 121.59 115.57 152.32 115.57 37.34 0 134.47-8.31 150.76-113.22-22.59 92.14-114.44 99.72-150.75 99.72Z'/%3E%3Cpath fill='%23FF0' d='M354.843 559.515c-114.71-2-109.25 71.1-109.25 71.1-.28 8.25-1.77 107.51 109.22 107.51 111 0 109.5-99.26 109.22-107.51.06 0 5.52-73.12-109.19-71.1Z'/%3E%3Ccircle cx='353.993' cy='606.165' r='3.86' fill='red'/%3E%3Cpath fill='%23000' d='M324.003 591.155c-.203 6.459-5.572 11.548-12.033 11.407-6.46-.142-11.6-5.462-11.52-11.923.08-6.462 5.35-11.653 11.813-11.634 6.584.142 11.824 5.564 11.74 12.15Zm80.63 0c-.198 6.462-5.562 11.56-12.026 11.427-6.464-.132-11.616-5.445-11.548-11.91.067-6.465 5.328-11.67 11.794-11.667 6.6.12 11.863 5.549 11.78 12.15Z'/%3E%3Ccircle cx='274.873' cy='617.915' r='1.86' fill='red'/%3E%3Ccircle cx='285.763' cy='617.915' r='1.86' fill='red'/%3E%3Ccircle cx='280.463' cy='623.215' r='1.86' fill='red'/%3E%3Ccircle cx='421.793' cy='617.915' r='1.86' fill='red'/%3E%3Ccircle cx='432.673' cy='617.915' r='1.86' fill='red'/%3E%3Ccircle cx='427.373' cy='623.215' r='1.86' fill='red'/%3E%3Cpath fill='%23FFF' d='m354.783 776.315-15.9 25.32h11.19l-17.66 32.38 40.62-39.45h-11.78l12.37-18.25h-18.84z'/%3E%3Ccircle cx='425.603' cy='531.805' r='6.99' fill='%232BD7F5'/%3E%3Cpath fill='%23EB2D74' d='M363.633 635.145a258.34 258.34 0 0 0 50.63-6.66c-2.8 23-19.37 42-41.58 49.12-4.31-4.83-11.38-8-19.37-8s-15.13 3.18-19.43 8c-22.33-7.07-39-26.1-41.82-49.19a258.82 258.82 0 0 0 51.49 6.69s10.35.57 20.08.04Z'/%3E%3Cpath fill='%23C00058' d='M353.313 669.635c-8 0-15.13 3.18-19.43 8a64.21 64.21 0 0 0 38.8-.07c-4.32-4.79-11.38-7.93-19.37-7.93Z'/%3E%3Cpath fill='%23DAFFC2' d='M286.943 648.865c-8.24 17.88-50 31.33-67.7 33.71a61.21 61.21 0 0 1-9.77.48c2 5.38.72 10.68 3.16 15.41 2.91-.23 9.2-.83 11.9-1.28 27.99-4.76 50.56-22.6 62.41-48.32Z'/%3E%3Cpath fill='%23B3DDC2' d='M93.063 399.675c-2.21.45-3.51 3-3.14 5.24.37 2.24 4.52 6.33 16.68 9.6a64.83 64.83 0 0 0 23.84 1.33c11.07-1.22 21.43-5.89 31.39-10.86 11.9-5.94 23.63-12.48 33.62-21.25a10.07 10.07 0 0 0 2.62-3.06c1.15-2.4.29-5.41-1.49-7.39a15.85 15.85 0 0 0-6.82-4 46.14 46.14 0 0 0-11.46-2.39l-85.24 32.78Z'/%3E%3Cpath fill='%23FFA103' d='M179.283 355.345c-1.21-.12-2.19.85-3.31 1.24-3 1-9.35 1.68-11.08-.46a19.12 19.12 0 0 1-1.58-2.93 28.27 28.27 0 0 0-4-.9 45.83 45.83 0 0 0-9.38-.22 36.1 36.1 0 0 0-13 4.43 293.63 293.63 0 0 1-29.8 14.15c-9.62 4-18.2 5.51-21.29 14.57-2.49 7.33 2.63 15.65 11.67 18.69 3.65 1.23 12 3.23 15.85 4a44 44 0 0 0 17.62.39c14.09-2.12 19.95-7.25 29-12 10.55-5.61 16.92-9.36 26.88-15.73 5.73-3.66 6.81-7.42 6.92-9 .48-6.57.03-14.74-14.5-16.23Z'/%3E%3Cpath fill='%23791F1F' d='M122.883 409.705a44.94 44.94 0 0 1-9.57-1.1c-3.71-.78-12.2-2.81-15.93-4.07a19.29 19.29 0 0 1-11.26-9.37 13.21 13.21 0 0 1-.82-10.16c2.64-7.76 9.17-10.21 16.73-13 1.6-.6 3.25-1.22 4.93-1.91a293.35 293.35 0 0 0 29.74-14.12 36.13 36.13 0 0 1 13.2-4.5 27.746 27.746 0 0 1 3.16-.17c2.15 0 4.29.2 6.36.39a28.42 28.42 0 0 1 4.09.92l.29.09.12.28c.413.969.912 1.9 1.49 2.78.61.75 2.2 1.36 4.37 1.36a12.51 12.51 0 0 0 6-1.11c.33-.15.69-.31 1-.51a4.32 4.32 0 0 1 2.54-.76c14.19 1.46 15.65 9 15.12 16.88-.1 1.55-1 5.58-7.22 9.55-10.2 6.52-16.49 10.21-26.93 15.76-2 1-3.77 2.1-5.53 3.13-6.42 3.75-12.48 7.3-23.63 9a54.48 54.48 0 0 1-8.25.64Zm30.17-57.13a26.4 26.4 0 0 0-3 .16 34.89 34.89 0 0 0-12.73 4.35 294.38 294.38 0 0 1-29.86 14.18c-1.7.7-3.36 1.32-5 1.93-7.55 2.83-13.51 5.07-16 12.25a11.91 11.91 0 0 0 .75 9.15 18 18 0 0 0 10.51 8.71c3.68 1.24 12.1 3.25 15.78 4a43.67 43.67 0 0 0 9.3 1.08 53.21 53.21 0 0 0 8.08-.7c10.9-1.64 16.85-5.12 23.16-8.81 1.77-1 3.61-2.11 5.58-3.16 10.4-5.53 16.67-9.2 26.84-15.7 5.86-3.75 6.55-7.48 6.62-8.54.45-6.76-.21-14.09-13.95-15.49a3.17 3.17 0 0 0-1.77.6 7.85 7.85 0 0 1-1.26.6 21.72 21.72 0 0 1-6.43 1c-1.84 0-4.26-.29-5.37-1.65a16.65 16.65 0 0 1-1.54-2.8 25 25 0 0 0-3.55-.79c-1.96-.18-4.06-.38-6.16-.38v.01Z'/%3E%3Cpath fill='%23FFE166' d='M179.283 355.345s-29.28 17.54-59.23 31.42a18.06 18.06 0 0 0-4.11 2.51 23 23 0 0 0-3.38 4.24s-4.18 7.52-11.67 4.82c0 0-6.83-2.89-3.52-11.56a9 9 0 0 1 2.61-3.5 20.51 20.51 0 0 1 5.1-2.67s45.46-20.71 58.39-27.36c0 0 .55 4.85 7.23 4.42a25.86 25.86 0 0 0 8.58-2.32Z'/%3E%3Cpath fill='%23791F1F' d='M104.083 399.565a10 10 0 0 1-3.41-.62c-2.59-1.1-6.7-5.08-3.91-12.41a9.62 9.62 0 0 1 2.8-3.76 21.21 21.21 0 0 1 5.27-2.77c.44-.2 45.58-20.77 58.34-27.34a.65.65 0 0 1 .95.51c0 .15.54 3.87 5.77 3.87h.77a25.48 25.48 0 0 0 8.36-2.25.65.65 0 1 1 .61 1.15c-.29.18-29.66 17.71-59.29 31.45a17.6 17.6 0 0 0-4 2.42 14.55 14.55 0 0 0-2.43 2.94c-.26.38-.53.76-.81 1.15-.29.49-3.41 5.66-9.02 5.66Zm58.92-45.37c-13.71 7-57.22 26.79-57.67 27a20.53 20.53 0 0 0-4.94 2.57 8.5 8.5 0 0 0-2.39 3.24c-3 8 2.91 10.64 3.16 10.75a8.71 8.71 0 0 0 2.93.53c5 0 7.89-5 7.91-5.07v-.07c.28-.39.55-.77.81-1.15a15.45 15.45 0 0 1 2.67-3.21 18.59 18.59 0 0 1 4.28-2.61c22.09-10.24 44.06-22.61 53.85-28.27a20.26 20.26 0 0 1-2.93.43h-.84c-4.31.01-6.22-2.33-6.84-4.14Z'/%3E%3Cpath fill='%23F06303' d='M63.003 320.755c-2.51-2.38-13.11-30.79-16.12-42-.38-1.42-.62-3.24.58-4.09a8.8 8.8 0 0 0 1.4-1 1.35 1.35 0 0 0-1.35-2.08 13.86 13.86 0 0 0-2.4.58l-.46-2.8c-.42-2.24-1.91-1.5-2-.86-.25 1.09.42 2.92.62 4-.16-.73-1.47-4.79-2.9-4-1.17.53.15 3.57.6 5.22-.83-1.71-2-4.67-3.26-3.7-1.61.98 1.29 5.74 2.53 7.98 2.06 3.88 10.69 31.58 21.93 49l.83-6.25Z'/%3E%3Cpath fill='%23791F1F' d='M61.613 327.355c-8.31-12.85-15.31-31.59-19.06-41.67-1.38-3.69-2.37-6.35-2.89-7.34l-.54-1c-1.64-3-3.69-6.69-1.87-7.95a1.4 1.4 0 0 1 1.21-.27 1.8 1.8 0 0 1 .7.36 1.6 1.6 0 0 1 .86-1.68c.392-.2.857-.2 1.25 0 .25.124.473.293.66.5a1.56 1.56 0 0 1 1.51-1.15c.43 0 1.45.2 1.8 2l.35 2.09a8.08 8.08 0 0 1 1.78-.39 2.08 2.08 0 0 1 2.11 1.2 1.78 1.78 0 0 1-.25 2 6.91 6.91 0 0 1-1.23.95l-.25.16c-.69.49-.8 1.63-.33 3.39 3.55 13.28 13.9 39.81 15.93 41.74l-.9.95c-2.72-2.58-13.43-31.63-16.3-42.35-.23-.87-.94-3.53.83-4.79l.31-.2a5.82 5.82 0 0 0 1-.76.55.55 0 0 0 0-.53.77.77 0 0 0-.83-.44 7 7 0 0 0-1.5.34l-.78.22-.69.17-.58-3.5c-.14-.72-.38-.95-.49-1a.33.33 0 0 0-.26.14 7.18 7.18 0 0 0 .37 2.65c.1.4.2.78.26 1.11l-1.28.26c-.21-.94-1.12-3.26-1.83-3.59h-.13c-.22.1-.27.74.57 3.33.13.42.26.81.35 1.14l-1.22.46-.3-.64c-.45-1-1.3-2.75-1.85-2.88l-.13.05c-.87.6 1.48 4.86 2.25 6.26l.55 1c.56 1.06 1.57 3.76 3 7.49 3.74 10 10.7 28.68 18.94 41.41l-1.1.76Z'/%3E%3Cpath fill='%23F06303' d='M67.603 393.865a106.59 106.59 0 0 0 2.31 14.28c.29 1.16 2.06 8.22 7.54 8.76 11.06 1.11 22.32-7.49 32.11-10.86 1.07 2.52 1.14 4.18 2.61 5.35 1.16.93 2.74 1.39 3.71-.27 1.5-2.33 0-4.78-1.24-8.15-.69-1.92-1.32-3.24-3.63-3.19-3 .05-21.91 9.86-29.66 11.24a3.45 3.45 0 0 1-2.45-.24c-2.41-1.5-4-15-3.53-19.68l-7.77 2.76Z'/%3E%3Cpath fill='%23791F1F' d='M79.463 417.675c-.667 0-1.357-.034-2.07-.1-5.37-.54-7.46-6.65-8.11-9.26a107.53 107.53 0 0 1-2.28-14.37l1.3-.15a106.47 106.47 0 0 0 2.3 14.2c.58 2.34 2.43 7.82 7 8.27 7.34.73 14.78-2.92 22-6.45a88.48 88.48 0 0 1 9.87-4.38l.58-.2.24.56c.34.81.59 1.53.8 2.17a5.61 5.61 0 0 0 1.61 2.93 2.44 2.44 0 0 0 1.8.67c.408-.101.75-.38.93-.76 1-1.63.36-3.37-.59-5.77-.23-.58-.47-1.19-.7-1.83-.69-1.94-1.19-2.8-3-2.76-1.17 0-5.77 2-10.64 4.14-6.8 3-14.51 6.31-18.92 7.09a4.07 4.07 0 0 1-2.91-.32c-2.83-1.77-4.31-15.86-3.84-20.3l1.3.14c-.56 5.22 1.34 17.87 3.23 19.05a2.94 2.94 0 0 0 2 .15c4.26-.76 12.23-4.22 18.63-7 6.2-2.69 9.77-4.22 11.13-4.25 2.94 0 3.67 2 4.26 3.63.22.63.46 1.22.69 1.79 1 2.55 1.88 4.76.49 6.93a2.64 2.64 0 0 1-1.81 1.36 3.64 3.64 0 0 1-2.86-.94 6.72 6.72 0 0 1-2-3.53c-.15-.46-.32-1-.53-1.51a94.69 94.69 0 0 0-9.13 4.11c-6.86 3.3-13.76 6.69-20.77 6.69Z'/%3E%3Cpath fill='%23F06303' d='M75.093 401.115a17.84 17.84 0 0 0 8.14-1.49c4.17-2 6.95-6.22 8.29-10.66 1.34-4.44 1.48-9.13 1.48-13.76l1.1-63.36c.15-8.6.82-19-6.17-24.29-8-5-19.88-1.87-24.48 6.35-2.7 4.82-3 10.59-3.17 16.11-.88 24.19-1.76 48.39-1.19 72.58.11 4.63.39 9.61 3.26 13.25 2.87 3.64 8 5.16 12.74 5.27Z'/%3E%3Cpath fill='%23791F1F' d='M75.693 401.775h-.61c-3.37-.07-9.56-.89-13.21-5.52-2.93-3.72-3.28-8.57-3.4-13.64-.57-24.23.33-48.83 1.19-72.62.19-5.32.41-11.34 3.25-16.41 3.08-5.51 9.49-9.061 16.32-9.061a17.229 17.229 0 0 1 9.08 2.48c6.84 5.13 6.64 14.76 6.46 23.26v1.56l-1.1 63.36c-.08 4.62-.16 9.39-1.54 13.95-1.55 5.13-4.7 9.16-8.63 11.06a17.74 17.74 0 0 1-7.81 1.58Zm3.55-116c-6.37 0-12.33 3.29-15.18 8.39-2.69 4.84-2.9 10.7-3.06 15.84-.87 23.77-1.76 48.36-1.19 72.55.11 4.83.44 9.45 3.12 12.86 3.32 4.21 9.07 5 12.21 5h.58a16.48 16.48 0 0 0 7.23-1.41c3.61-1.74 6.5-5.48 7.95-10.26 1.32-4.38 1.41-9.06 1.48-13.59v-.09l1.1-63.27v-1.56c.17-8.17.36-17.44-5.92-22.17a15.92 15.92 0 0 0-8.33-2.24l.01-.05Z'/%3E%3Cpath fill='%23F06303' d='M81.553 396.285c-6.24-2.29-20.07-7.08-32.22-2.68-5.5 2-9.86 6.64-17.69 17.67-1 1.44-3 3.73-5.26 3.75a7.72 7.72 0 0 1-3.27-.85 36.72 36.72 0 0 1-4.65-2.25c-2.64-1.62-2.28-3.6-1.37-4.53a4 4 0 0 1 3.84-.62 11.94 11.94 0 0 0 3.86.72c4.26-.09 10.33-14.81 21.36-19.45.3-.13 14.92-5.26 37.7 2.65l-2.3 5.59Z'/%3E%3Cpath fill='%23791F1F' d='M26.333 415.675a8.2 8.2 0 0 1-3.41-.88h-.07l-1.26-.55a25 25 0 0 1-3.47-1.73 4.31 4.31 0 0 1-2.33-3.08 3 3 0 0 1 .84-2.47 4.58 4.58 0 0 1 4.48-.79h.17c1.119.41 2.298.63 3.49.65 1.53 0 3.72-2.81 6.26-6 3.75-4.75 8.43-10.67 14.86-13.38.14-.06 14.86-5.45 38.17 2.64l-.43 1.23c-22.7-7.88-37.1-2.72-37.24-2.67-6.12 2.58-10.68 8.35-14.34 13-3 3.76-5.11 6.47-7.26 6.52a11.87 11.87 0 0 1-3.88-.7h-.17a3.36 3.36 0 0 0-3.19.45 1.71 1.71 0 0 0-.48 1.4 3.19 3.19 0 0 0 1.72 2.12 24 24 0 0 0 3.3 1.65l1.28.56h.07a7 7 0 0 0 2.92.76c2.05 0 4-2.44 4.73-3.48 8.34-11.74 12.55-15.93 18-17.9 11.46-4.15 24.14-.46 32.67 2.68l-.45 1.23c-8.34-3.07-20.71-6.69-31.77-2.68-5.1 1.85-9.32 6.08-17.38 17.43-1.06 1.49-3.22 4-5.78 4l-.05-.01Z'/%3E%3Cpath fill='%23FFCB00' d='M75.733 326.005c.193.369.276.785.24 1.2-.13 4.15-6.93 8.41-4.72 12.19.69 1.18 4.06 3 5 4.1 2.21 2.41 1.38 3.45-.16 5.9-1.69 2.68-4.36 4-3.47 6.87.58 1.85 4.92 6.31 5.31 8.2.5 2.45-2 4.61-4.21 6.47-2.21 1.86-3.51 4.17-3 6.66.6 3.28-1.15 5.39-3.63 4.94a4.89 4.89 0 0 1-4.1-4.41c-.15-2.84 1.85-6.07 4.12-8.35 2.43-2.44 3.93-3.21 4-5.34 0-2.31-5.27-6.12-5.42-8.43-.11-1.73 1.28-3.29 2.41-4.81 1.13-1.52 3.44-3.6 2.4-5.13a39.11 39.11 0 0 0-4.12-3.42c-3.88-3.28-2.25-6.68-.49-10.58 1.09-2.42 2.72-6.1 5.7-7.32a3.46 3.46 0 0 1 4.14 1.26Z'/%3E%3Cpath fill='%23791F1F' d='M67.723 383.215a4.29 4.29 0 0 1-.77-.07 5.56 5.56 0 0 1-4.64-5c-.14-2.68 1.47-6 4.31-8.84.57-.57 1.08-1 1.54-1.47 1.57-1.46 2.21-2.11 2.22-3.42 0-.93-1.36-2.5-2.57-3.88-1.42-1.62-2.76-3.15-2.84-4.5-.12-1.77 1-3.25 2.12-4.69l.42-.55c.23-.31.5-.64.79-1 1-1.14 2.15-2.57 1.59-3.4a23.67 23.67 0 0 0-2.89-2.39c-.47-.36-.87-.66-1.1-.86-4.19-3.54-2.52-7.23-.76-11.14l.13-.28c1.13-2.5 2.83-6.27 6-7.58a4.166 4.166 0 0 1 1.58-.31 4.002 4.002 0 0 1 3.44 1.85 2.81 2.81 0 0 1 .33 1.55c-.06 2-1.43 3.9-2.76 5.75-1.59 2.21-3.09 4.31-2 6.08.79.866 1.7 1.617 2.7 2.23a15.87 15.87 0 0 1 2.2 1.71c2.45 2.67 1.52 4.14.11 6.37l-.2.32a17.64 17.64 0 0 1-1.76 2.31c-1.37 1.56-2.12 2.51-1.64 4.07a18.12 18.12 0 0 0 2.42 3.62c1.38 1.8 2.67 3.5 2.91 4.64.58 2.84-2.2 5.2-4.43 7.1-1.55 1.32-3.31 3.44-2.83 6a5.41 5.41 0 0 1-.88 4.51 3.491 3.491 0 0 1-2.74 1.27Zm5.13-58.11a2.841 2.841 0 0 0-1.09.21c-2.71 1.11-4.28 4.6-5.32 6.91l-.13.28c-1.69 3.75-3 6.71.41 9.61.2.17.59.47 1 .81a19 19 0 0 1 3.28 2.75c1.11 1.64-.43 3.49-1.67 5-.27.33-.53.64-.74.92l-.42.56c-1 1.25-1.94 2.54-1.85 3.81.06.91 1.37 2.4 2.52 3.72 1.5 1.71 2.91 3.32 2.9 4.76 0 1.93-1.06 2.9-2.64 4.36-.45.41-1 .88-1.5 1.43-2 2-4.07 5.12-3.93 7.85a4.24 4.24 0 0 0 3.57 3.8 2.28 2.28 0 0 0 2.26-.74 4.21 4.21 0 0 0 .6-3.44c-.47-2.55.66-5.06 3.27-7.28 2.07-1.76 4.42-3.76 4-5.84-.17-.85-1.5-2.58-2.67-4.11a17.66 17.66 0 0 1-2.63-4c-.73-2.34.55-3.79 1.9-5.32a16.32 16.32 0 0 0 1.59-2.15l.2-.32c1.39-2.19 1.8-2.85 0-4.79a15.89 15.89 0 0 0-2-1.57 11.75 11.75 0 0 1-3.08-2.64c-1.47-2.51.35-5 2.11-7.51 1.21-1.69 2.47-3.44 2.52-5a1.53 1.53 0 0 0-.15-.85 2.72 2.72 0 0 0-2.31-1.21v-.01Z'/%3E%3Cpath fill='%23FFF' d='M83.403 301.865a3.18 3.18 0 0 0 3.63-1.7c.26-.57.73-2.69-.63-4a3.5 3.5 0 0 0-3.4-.74 3.42 3.42 0 0 0-2.15 3.49 3.58 3.58 0 0 0 2.55 2.95Z'/%3E%3Cpath fill='%23791F1F' d='M84.163 302.625a3.38 3.38 0 0 1-.94-.13 4.24 4.24 0 0 1-3-3.46 4.07 4.07 0 0 1 2.61-4.19 4.47 4.47 0 0 1 1.28-.18 4 4 0 0 1 2.76 1.08 4.41 4.41 0 0 1 .78 4.7 3.88 3.88 0 0 1-3.49 2.18Zm-.07-6.62a3.17 3.17 0 0 0-.89.13 2.78 2.78 0 0 0-1.69 2.79 2.94 2.94 0 0 0 2.08 2.35c.188.053.384.08.58.08a2.53 2.53 0 0 0 2.27-1.41c.14-.32.64-2.17-.48-3.21a2.65 2.65 0 0 0-1.87-.73Z'/%3E%3Ccircle cx='83.983' cy='298.485' r='1' fill='%23791F1F'/%3E%3Cpath fill='%23FFF' d='M65.213 301.665a3.18 3.18 0 0 0 3.63-1.7c.26-.57.73-2.69-.63-4a3.5 3.5 0 0 0-3.4-.74 3.42 3.42 0 0 0-2.15 3.49 3.58 3.58 0 0 0 2.55 2.95Z'/%3E%3Cpath fill='%23791F1F' d='M66.003 302.425a3.41 3.41 0 0 1-.94-.13 4.24 4.24 0 0 1-3-3.46 4.07 4.07 0 0 1 2.61-4.19 4.47 4.47 0 0 1 1.28-.18 4 4 0 0 1 2.76 1.08 4.41 4.41 0 0 1 .78 4.7 3.87 3.87 0 0 1-3.49 2.18Zm-.07-6.66a3.18 3.18 0 0 0-.89.13 2.78 2.78 0 0 0-1.69 2.79 2.94 2.94 0 0 0 2.05 2.32 2.1 2.1 0 0 0 .58.08 2.53 2.53 0 0 0 2.27-1.41c.14-.32.64-2.17-.48-3.21a2.65 2.65 0 0 0-1.87-.7h.03Z'/%3E%3Ccircle cx='65.263' cy='298.295' r='1' fill='%23791F1F'/%3E%3Cpath fill='%23F06303' d='M92.623 320.755c2.51-2.38 13.11-30.79 16.12-42 .38-1.42.62-3.24-.58-4.09a8.8 8.8 0 0 1-1.4-1 1.35 1.35 0 0 1 1.35-2.08 13.86 13.86 0 0 1 2.4.58l.46-2.8c.42-2.24 1.91-1.5 2-.86.25 1.09-.42 2.92-.62 4 .16-.73 1.47-4.79 2.9-4 1.17.53-.15 3.57-.6 5.22.83-1.71 2-4.67 3.26-3.7 1.52 1.05-1.41 5.81-2.62 8.09-2.06 3.88-10.69 31.58-21.93 49l-.74-6.36Z'/%3E%3Cpath fill='%23791F1F' d='m94.003 327.355-1.1-.71c8.24-12.73 15.2-31.39 18.94-41.41 1.39-3.73 2.4-6.43 3-7.49l.55-1c.77-1.4 3.12-5.66 2.27-6.24-.11-.08-.14-.08-.15-.07-.55.13-1.4 1.92-1.85 2.88l-.3.63-1.22-.46c.09-.34.22-.73.35-1.14.84-2.59.79-3.23.61-3.31-.18-.08-.1 0-.17 0-.71.33-1.62 2.65-1.83 3.59l-1.28-.26c.06-.33.16-.71.26-1.11a7.15 7.15 0 0 0 .37-2.65.31.31 0 0 0-.26-.14c-.11 0-.36.24-.5 1l-.58 3.48-.69-.17-.78-.22a7 7 0 0 0-1.5-.34.78.78 0 0 0-.83.44.55.55 0 0 0 0 .53c.305.288.64.543 1 .76l.31.2c1.77 1.26 1.06 3.92.83 4.79-2.87 10.73-13.58 39.77-16.3 42.35l-.9-.95c2-1.93 12.39-28.46 15.93-41.74.47-1.76.36-2.9-.33-3.39l-.25-.16a6.91 6.91 0 0 1-1.23-.93 1.78 1.78 0 0 1-.25-2 2.08 2.08 0 0 1 2.11-1.2 8.08 8.08 0 0 1 1.79.39l.34-2.08c.35-1.85 1.38-2.05 1.8-2.05a1.57 1.57 0 0 1 1.52 1.18 2.22 2.22 0 0 1 .66-.5 1.4 1.4 0 0 1 1.29 0c.605.312.94.978.83 1.65a1.8 1.8 0 0 1 .7-.36 1.41 1.41 0 0 1 1.23.28c1.79 1.24-.26 4.95-1.9 7.93l-.54 1c-.52 1-1.52 3.65-2.89 7.34-3.8 10.06-10.79 28.81-19.06 41.66Z'/%3E%3Cpath fill='%23B3DDC2' d='M97.583 446.385c-9 3.21-18.26 6.54-33.58 4.64a14 14 0 0 1-7.67-3.9 2.73 2.73 0 0 1-.92-1.62c-.81-3.84 1.6-6.82 5.42-9 3.82-2.18 9-3.52 14.1-4.88l22.72-6c1.48.32 2 1.43 1.91 2.08-.09.65 0 1.62 1.48 2.14a4.48 4.48 0 0 0 2 .17c2.28-.25 4-.94 6.19-1.23a15.92 15.92 0 0 1 8.13 1.17c2.93 1.3 4.39 3.31 4.13 4.67-.26 1.36-1.85 2.23-3.44 3a173.64 173.64 0 0 1-20.47 8.76Z'/%3E%3Cpath fill='%23791F1F' d='M73.783 313.835a12 12 0 0 1-5.59-1.43 10.07 10.07 0 0 1-4.68-7.76l1.74-.07a8.26 8.26 0 0 0 3.75 6.29 9.92 9.92 0 0 0 8.27.59 9 9 0 0 0 5.73-6.59l1.73.18a10.69 10.69 0 0 1-6.9 8 11.73 11.73 0 0 1-4.05.79Z'/%3E%3Cellipse cx='451.825' cy='103.609' fill='%23DEBD53' rx='3.9' ry='1.83' transform='rotate(-29.78 451.825 103.609)'/%3E%3Cpath fill='%23DEBD53' d='m447.711 98.544 1.831-1.048 2.826 4.938-1.83 1.048zm8.939-5.193 1.831-1.048 2.827 4.939-1.832 1.048zm10.03-4.183 1.83-1.048 2.826 4.94-1.831 1.047z'/%3E%3Cellipse cx='460.872' cy='98.697' fill='%23DEBD53' rx='3.9' ry='1.84' transform='rotate(-29.77 460.872 98.697)'/%3E%3Cellipse cx='470.522' cy='94.112' fill='%23DEBD53' rx='3.9' ry='1.84' transform='rotate(-29.78 470.522 94.112)'/%3E%3Cellipse cx='436.557' cy='77.626' fill='%23DEBD53' rx='3.9' ry='1.84' transform='rotate(-29.78 436.557 77.626)'/%3E%3Cpath fill='%23DEBD53' d='m436.028 78.843 1.831-1.049 2.827 4.938-1.83 1.049zm8.28-5.159 1.831-1.048 2.826 4.939-1.83 1.048zm9.338-6.514 1.832-1.047 2.822 4.94-1.832 1.048z'/%3E%3Cpath fill='%23C75B8D' d='M335.793 144.005c-15.42-28.18-34.27-19.86-34.27-19.86-9.29 2.85-14.29 13.82-14.29 13.82-2.5 6.72-5.8 9-5.8 9-6.86 4.63-16.73-.29-16.73-.29-18.71-6.21-31.41 1.76-31.41 1.76-38.84 23.28-15.59 68.83-15.59 68.83a44.06 44.06 0 0 0 1.95 4.14l1.62 3.33s1.22 2.12 3.15 4.89c0 0 26.47 41.94 66.21 20.26 0 0 13.31-6.91 17.44-26.19 0 0 .75-11 8.23-14.57a20.13 20.13 0 0 1 12.44-.43s8.31 1.59 17.37-5.36c0 0 16.91-11.09.24-39.61l-3.95-6.9-1.75-4.29-4.86-8.53Z'/%3E%3Cpath fill='%23E8D268' d='M232.923 147.755c-20.35 12.2-23.57 30.54-22.68 43.77a75 75 0 0 0 6.84 26 43.56 43.56 0 0 0 2 4.16l1.61 3.3c.07.13 1.27 2.22 3.2 5a70.72 70.72 0 0 0 18.38 18.44c10.73 7.2 27.89 13.4 48.73 2 .54-.28 13.64-7.28 17.78-26.65v-.1c0-.11.82-10.65 7.83-14a19.66 19.66 0 0 1 12-.38c.4.08 8.77 1.58 18-5.48a18.9 18.9 0 0 0 6.61-9.43c1.5-4.43 2.19-11.27-1.24-20.71a63.7 63.7 0 0 0-4.95-10.36l-4.03-6.88-1.75-4.29-4.88-8.54c-8.09-14.78-17.18-19.49-23.37-20.84a20.39 20.39 0 0 0-11.74.67c-9.45 2.93-14.47 13.72-14.68 14.18-2.4 6.43-5.53 8.65-5.56 8.67-6.44 4.35-15.94-.29-16-.34-18.9-6.26-31.57 1.48-32.1 1.81Zm-11.06 76.54-1.6-3.29a43 43 0 0 1-1.93-4.1 59.26 59.26 0 0 1-2.45-5.88 72.91 72.91 0 0 1-4.27-19.63c-1.24-18.68 6.18-33 22-42.47.14-.09 12.67-7.72 30.81-1.71.77.38 10.39 4.93 17.37.22.14-.1 3.51-2.44 6.06-9.29 0-.06 5-10.68 13.85-13.4a19.4 19.4 0 0 1 11-.58c8.57 1.84 16.3 8.84 22.49 20.12l4.85 8.47 1.75 4.29 4 6.94a62.64 62.64 0 0 1 4.85 10.15c2.69 7.4 3.13 14.09 1.26 19.7a17.92 17.92 0 0 1-6.13 8.84c-8.73 6.69-16.79 5.27-16.87 5.26a20.57 20.57 0 0 0-12.85.47c-7.61 3.64-8.57 14.24-8.63 15.09-4 18.68-16.94 25.62-17.07 25.69-16.24 8.86-32.14 8.21-47.27-1.94a69.29 69.29 0 0 1-18-18.08c-1.91-2.73-3.12-4.84-3.13-4.87h-.09Z'/%3E%3Cpath fill='%23282B26' d='m308.003 159.495 121.89-69s-5.16-6.56 3.45-9.37c0 0 6.41 2 10.77-.88a94.93 94.93 0 0 0 16-15.15l3.45.49a5.61 5.61 0 0 0 4.1 4.7s5.43 1.48 3.83 4.85c0 0 4.1-.23 2.77 5.46 0 0-.26 5.09 2.47 7.4l-1.63 2.69s-8.61-2.12-21.85 6.16c0 0-4.74 3.42-5.69 8.54 0 0-5.19 7.55-10-2l-120.68 72.19-8.88-16.08Zm-68.91 10.76s-2.37 2.79 2 2.16c0 0 9.65-1.89 16.79-8.93 0 0 3.1 1.87 10.48-.59 0 0 10.49-3.38 13.53 1.64a2.83 2.83 0 0 1-1.17 3.71s-2.35.92-3-.75c0 0-.72-1.7 1.76-2.53 0 0-3.79-3.07-12.31 1.38 0 0-7 3.81-8.5 7.31a13.16 13.16 0 0 0-8.93.21s-9.86 2.85-13.37-1.06a4.28 4.28 0 0 1-.28-6.1s1.8-2.47 3.83.34c.08-.04 1.14 1.49-.83 3.21Zm35.06 61.27s-3.61.63-.85-2.81c0 0 6.52-7.37 16.21-9.95 0 0 0-3.61 5.82-8.74 0 0 8.23-7.33 5.44-12.5a2.83 2.83 0 0 0-3.8-.87s-2 1.56-.85 2.94c0 0 1.1 1.48 3.08-.24 0 0 .73 4.82-7.43 9.91 0 0-6.81 4.06-10.61 3.62a13.17 13.17 0 0 1-4.7 7.59s-7.46 7.06-5.86 12.06a4.28 4.28 0 0 0 5.12 3.33s3-.3 1.65-3.48c-.02.02-.74-1.69-3.22-.86Zm-59.15-16.67 32-21.11 9.27 16.19-35.62 17.21s-1.71 1.33-5-5.61c-.04 0-2.21-4.4-.65-6.68Zm37.195-32.237 3.185-1.823 17.022 29.733-3.185 1.823z'/%3E%3Cpath fill='%23D74C83' d='m285.483 194.595 56.09-33.15s7.33 11-8.86 22.69l-37.28 26.61s-11.75 10.25-8.88-4.43c0 0 1.55-8.99-1.07-11.72Z'/%3E%3Cpath fill='%23E6E1C5' d='m285.113 194.545.2.21c2.51 2.62 1 11.44 1 11.52-.85 4.33-.5 7 1 7.86 2.7 1.56 8-3 8.23-3.21l37.26-26.6c16.19-11.73 9-22.9 8.92-23l-.12-.18-56.49 33.4Zm56.38-32.79a9.85 9.85 0 0 1 .69 1.53c1.27 3.5 2.49 11.9-9.61 20.67l-37.28 26.61c-.07.06-5.29 4.57-7.71 3.17a2.23 2.23 0 0 1-.95-1.23c-.46-1.26-.41-3.32.14-6.14.06-.32 1.13-6.69-.19-10.31a4.77 4.77 0 0 0-.78-1.39l55.69-32.91Z'/%3E%3Cpath fill='%23E3E6DE' d='m307.784 159.976.799-.457 8.508 14.867-.799.457zm2.398-1.346.798-.456 8.506 14.87-.799.456zm2.641-1.555.799-.457 8.508 14.868-.799.456zm2.93-1.629.798-.456 8.506 14.869-.8.457zm3.451-2.005.799-.457 8.507 14.868-.798.457zm3.711-2.147.798-.457 8.205 14.338-.798.457zm3.731-2.074.799-.458 8.202 14.34-.798.457zm4.244-2.456.799-.457 7.9 13.81-.8.457zm3.975-2.279.799-.457 7.897 13.8-.799.457zm4.251-2.407.799-.457 8.05 14.07-.798.456zm4.769-2.778.799-.457 7.748 13.54-.799.457zm5.324-2.98.798-.456 7.895 13.802-.798.456zm5.571-3.204.798-.457 7.895 13.802-.799.457zm5.563-3.217.798-.457 7.748 13.54-.798.456zm5.594-3.172.798-.457 7.748 13.54-.798.457zm6.896-3.983.798-.457 7.595 13.27-.799.458zm6.907-3.925.799-.457 7.594 13.27-.799.457zm7.156-4.13.799-.457 7.594 13.27-.799.458zm7.441-4.219.799-.456 7.288 12.742-.798.457zm7.7-4.403.798-.457 7.292 12.741-.799.457zm8.214-4.749.799-.456 7.142 12.48-.799.457zm9.031-5.135.798-.457 6.988 12.212-.798.457zm9.106-5.194 1.197-.686 7.445 13.01-1.197.686z'/%3E%3Cpath fill='%23E3E6DE' d='m257.863 190.005-10.06 6.46.25.39 10.05-6.46 173.12-98.3-.23-.4-173.13 98.31Zm1.63 2.28-10.62 6.08.23.4 10.62-6.08 171.92-98.01-.23-.4-171.92 98.01Zm1.37 2.39-10.62 6.08.22.4 10.62-6.08 171.92-98.01-.22-.4-171.92 98.01Zm-9.199 8.557 181.992-104.44.229.4L251.893 203.63zm11.929-3.777-10.62 6.08.23.4 10.62-6.08 171.46-98.81-.22-.4-171.47 98.81Zm1.37 2.39-10.62 6.07.22.4 10.62-6.07 171.43-99.5-.23-.39-171.42 99.49Z'/%3E%3Ccircle cx='441.695' cy='86.365' r='1.83' fill='%23DEBD53' transform='rotate(-29.78 441.695 86.365)'/%3E%3Ccircle cx='447.555' cy='95.713' r='1.84' fill='%23DEBD53' transform='rotate(-29.78 447.555 95.713)'/%3E%3Ccircle cx='450.415' cy='81.083' r='1.83' fill='%23DEBD53' transform='rotate(-29.78 450.415 81.083)'/%3E%3Ccircle cx='456.305' cy='90.469' r='1.84' fill='%23DEBD53' transform='rotate(-29.76 456.305 90.47)'/%3E%3Ccircle cx='459.55' cy='75.36' r='1.84' fill='%23DEBD53' transform='rotate(-29.76 459.55 75.36)'/%3E%3Ccircle cx='465.411' cy='84.693' r='1.83' fill='%23DEBD53' transform='rotate(-29.77 465.41 84.693)'/%3E%3Cellipse cx='444.702' cy='72.311' fill='%23DEBD53' rx='3.9' ry='1.83' transform='rotate(-29.77 444.702 72.31)'/%3E%3Cellipse cx='454.121' cy='66.229' fill='%23DEBD53' rx='3.9' ry='1.83' transform='rotate(-29.78 454.12 66.229)'/%3E%3Cpath fill='%23DBFAFF' d='M431.703 483.865s-115.1-3.09-97-118.24c5.88-37.34 31-62.83 67.4-71.48 34-8.09 83-6.85 114.39 8.7 38.84 19.26 63.78 60.12 57.33 103.84-5.61 38-37.46 80.38-79.26 80.38 0 0 36.86 53.88 75.67 12.79 28.44-30.09 29.29-77.85 25.77-116.51-1.43-15.66-5.88-89.71-25.47-93.18-48-8.51-86.38-30-98.19-81-9.34-40.51-8.24-109.16 33.52-130.34 66.35-33.67 204.26-6.27 221.14 77.65 9.73 48.25-40.41 141.52-100.31 130.22 0 0 27.82 101.83 1.07 167.88-9.76 24.08-23.7 49.43-44.43 66.08.34-.27 5.41 26.55 5.47 28.1 1.33 36.41-13.65 78.82-47.05 97.29-36.23 20-78.63 15.5-115.68 1.72 33.16-2 64.6-16.64 95.15-28.46 23.64-9.14 38.34-24.9 42.13-50.65.44-3-1.78-35.11-4.49-34.64-2.93.51-6.62 2.08-9.88 3.32-28.59 10.88-48.78.21-72.62-17.77-9.31-7-18.24-14.64-27.35-21.91-3.16-2.5-13.17-13.65-17.31-13.79Z'/%3E%3Ccircle cx='578.063' cy='199.075' r='1.62' fill='%23FFF'/%3E%3Ccircle cx='616.743' cy='182.675' r='1.62' fill='%23FFF'/%3E%3Cpath fill='%23DC4472' d='M661.194 199.485a17.908 17.908 0 0 1-.111 1.88c-.37 3.29-.35 12.82-5.62 16a20.79 20.79 0 0 1-3.7 1.79c.37 5.62.87 19.33-1.86 20.82-1.93 1.05-2.58 1.23-4.76.53-1.07-.34-2.77-.24-2.41-2.81a4.16 4.16 0 0 1 3.32-3.33 2.66 2.66 0 0 0 1-.34c.39-.17.41-2.7.46-3.52.1-1.83-.14-8-.19-9.81v-.64c-1.84.12-3.74 0-6 0 .35 6 .69 15.23-1.91 16.65-1.93 1.05-2.84 1.75-5 1.06-1.07-.34-2.5-.77-2.15-3.33a4.17 4.17 0 0 1 3.32-3.33 2.68 2.68 0 0 0 1-.34c.39-.17.41-2.7.45-3.52.1-1.83-.14-4.66-.19-6.49v-.68c-6.14.08-23.25 0-29.66-.43.36 5.81.78 18.94-1.88 20.39a6.46 6.46 0 0 1-4.78.72c-1.11-.14-2.29-.69-2.39-3-.1-2.31 2.32-3.14 3.32-3.33a2.67 2.67 0 0 0 1-.34c.39-.17.41-6 .45-6.84.1-1.83-.14-4.66-.18-6.49v-1.47c-2.054-.194-4.087-.43-6.1-.71.36 5.23 1 16.59-1.81 18.14-1.93 1.05-2.83 1.53-5 .83-1.07-.34-2.51-.54-2.16-3.1a4.17 4.17 0 0 1 3.32-3.33 2.67 2.67 0 0 0 1-.34c.39-.17.41-2.7.45-3.52.1-1.83-.14-4.66-.18-6.49 0-1.21-.07-2.15-.1-2.9l-.36-.07c-4.22-.78-8.19-7.09-9.64-11.3-1.05-3.06-.66-8.64-.66-11.89 0-1-6.78-.3-13.32-.79-4.86-.37-12.79-.37-16.49-4.81-2.29-2.75-2.06-8-1.77-11.05 1.36-14.08 12.09-9.17 21.57-10.26.3-5.77-.43-11.23.47-16.77a10.52 10.52 0 0 1 9.31-9 43.52 43.52 0 0 1 25.76.83c.188.094.372.197.55.31 5.26 3.33 6.78 8.25 7 12.81.2 3.78-.35 5.49-.35 9.3 0 6.84-1.18 14-.6 20.73 9.67 0 27.37.59 37 1.6a11.94 11.94 0 0 1 8.6 4.86c.129.166.25.34.36.52a13.64 13.64 0 0 1 1.62 6.6Z'/%3E%3Cellipse cx='554.633' cy='171.025' fill='%23422D2D' rx='7.96' ry='5.36'/%3E%3Cpath fill='%2349BDFF' d='M614.253 178.755s-7.85 14.91-36.1 15.3l.39 9s25.46 3.95 40.81-17.24l-5.1-7.06Zm-55.01 21.65-2.51 7.66s-1.16 3.55 2.51 3.66c0 0 2.64-.08 2-3.45l-2-7.87Z'/%3E%3Cpath fill='%23422D2D' d='m575.843 237.135-3.12-12.27h-.06c-.82-4.06-11.14-7.28-23.75-7.28s-22.81 3.18-23.74 7.21l-3.18 11-.39 1.36c-.1.18 0 .38 0 .57 0 6.09 12.33 11 27.3 11s27.12-4.94 27.12-11a1.09 1.09 0 0 0-.18-.59Z'/%3E%3Cpath fill='%2349BDFF' d='M549.003 233.145c10.49 0 19.39-2.22 22.57-5.3-3.18-3.08-12.08-5.3-22.57-5.3s-19.39 2.22-22.57 5.3c3.15 3.08 12.06 5.3 22.57 5.3Z'/%3E%3Cpath fill='%23FFF' d='M546.853 226.865h-.12c-2.88 1.94-4.87.09-5 0l-.06-.05h-.07c-3 1.79-4.48.17-4.55.1l-.05-.06h-.07a4.91 4.91 0 0 1-5.05.09l-.12.17a5.11 5.11 0 0 0 5.2 0c.3.28 1.86 1.55 4.68-.08a4.12 4.12 0 0 0 5.09 0 4.38 4.38 0 0 0 5.15-.12l-.12-.17a4.1 4.1 0 0 1-4.91.12Zm9.91 2.91h-.12c-2.88 1.94-4.87.09-5 0l-.06-.05h-.07c-3 1.78-4.49.17-4.55.1l-.05-.06h-.07a4.91 4.91 0 0 1-5.05.09l-.12.17a5.1 5.1 0 0 0 5.2 0c.3.28 1.86 1.55 4.68-.08a4.12 4.12 0 0 0 5.09 0 4.38 4.38 0 0 0 5.15-.12l-.12-.17a4.1 4.1 0 0 1-4.91.12Z'/%3E%3Ccircle cx='584.753' cy='198.615' r='1.62' fill='%23FFF'/%3E%3Ccircle cx='594.563' cy='197.225' r='1.62' fill='%23FFF'/%3E%3Ccircle cx='605.063' cy='193.185' r='1.62' fill='%23FFF'/%3E%3Ccircle cx='613.263' cy='187.645' r='1.62' fill='%23FFF'/%3E%3Ccircle cx='580.543' cy='160.295' r='5.27' fill='%23FFF'/%3E%3Ccircle cx='594.053' cy='160.295' r='5.27' fill='%23FFF'/%3E%3Ccircle cx='580.473' cy='160.305' r='3.26' fill='%23422D2D'/%3E%3Ccircle cx='594.113' cy='160.305' r='3.26' fill='%23422D2D'/%3E%3Cpath fill='%23DC4472' d='M587.643 142.435s9.39-60.22-6.87-60c0 0-10.53-2.06-9.62 20.15 0 0 .92 34.81 4.12 51.07l12.37-11.22Z'/%3E%3Cpath fill='%23FF5688' d='M579.863 86.555s-7.56.23-6.18 18.09c0 0 .69 26.33 3.21 39.39a31.1 31.1 0 0 1 7.79-3s6.17-56.54-4.82-54.48Z'/%3E%3Cpath fill='%23DC4472' d='M609.173 148.615s10.07-66.41-6.17-66.18c0 0-10.53-2.06-9.62 20.15 0 0 .92 34.81 4.12 51.07l11.67-5.04Z'/%3E%3Cpath fill='%23FF5688' d='M602.073 86.555s-7.56.23-6.18 18.09c0 0 .69 26.33 3.21 39.39 0 0 3.89 3.43 7.56 2.75-.01 0 7.77-62.78-4.59-60.23Zm-27.37 66.75s9.85 1.37 10.08-6c0 0-.34 5.84-10.76 5.5l.68.5Zm24.51-.8s-9.85 1.37-10.08-6c0 0 .34 5.84 10.76 5.5l-.68.5Zm-43.21 38.36s3.44-4.69 7.67.11c0 0 .8 4.24-3.32 4.81.04 0-4.77.8-4.35-4.92Z'/%3E%3Cpath fill='%23DC4472' d='M583.233 208.435s2.23 6.7.86 8.93c0 0 2.06-.86 1.55-6.87l-2.41-2.06Z'/%3E%3Cpath fill='%23DC4472' d='M585.643 210.005s.52 6-.17 9.27c0 0 2.23-5.67 2.06-8.76l-1.89-.51Z'/%3E%3Cpath fill='%23DC4472' d='M586.673 210.665s.52 6-.17 9.27c0 0 2.23-5.67 2.06-8.76l-1.89-.51Z'/%3E%3Cpath fill='%23DC4472' d='M587.533 211.525s.52 6-.17 9.27c0 0 2.23-5.67 2.06-8.76l-1.89-.51Z'/%3E%3Cpath fill='%23DC4472' d='M589.133 214.215s-1 4.58-.34 7.9a28.57 28.57 0 0 1 2.4-7.79l-2.06-.11Z'/%3E%3Cpath fill='%23DC4472' d='M590.393 214.905s-1 4.58-.34 7.9a28.57 28.57 0 0 1 2.4-7.79l-2.06-.11Z'/%3E%3Cpath fill='%23DC4472' d='M591.543 214.905s-1 4.58-.34 7.9a28.57 28.57 0 0 1 2.4-7.79l-2.06-.11Zm4.8 2.86s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Zm4.01.69s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M601.503 219.005s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Zm-4.01-1.12s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M598.753 217.425s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Z'/%3E%3Cpath fill='%23DC4472' d='M599.553 218.115s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Zm26.68 1.71s.11 5.6 1.72 7.66c0 0-1-4.8-.11-8.24l-1.61.58Z'/%3E%3Cpath fill='%23DC4472' d='M627.493 219.375s-.11 5.72 1 7.66c0 0-.34-6.06.11-8.35l-1.11.69Z'/%3E%3Cpath fill='%23DC4472' d='M628.293 220.055s-.11 5.72 1 7.66c0 0-.34-6.06.11-8.35l-1.11.69Z'/%3E%3Cpath fill='%23DC4472' d='M629.003 219.515s.11 6.18 1.72 8.45c0 0-1-5.3-.11-9.08l-1.61.63Z'/%3E%3Cpath fill='%23DC4472' d='M630.243 219.005s-.11 6.3 1 8.45c0 0-.34-6.68.11-9.2l-1.11.75Z'/%3E%3Cpath fill='%23DC4472' d='M631.003 219.765s-.11 6.3 1 8.45c0 0-.34-6.68.11-9.2l-1.11.75Zm-21.6-.62s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M610.543 219.715s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Zm-4.01-1.14s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M607.793 218.115s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Z'/%3E%3Cpath fill='%23DC4472' d='M608.603 218.795s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Zm5.83 1.15s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M615.473 219.255s.23 6.87 1.83 8.93a28.69 28.69 0 0 1-.23-9.5l-1.6.57Z'/%3E%3Cpath fill='%23DC4472' d='M616.003 218.455s.23 6.87 1.83 8.93a28.69 28.69 0 0 1-.23-9.5l-1.6.57Z'/%3E%3Cpath fill='%23DC4472' d='M617.073 218.795s.23 6.87 1.83 8.93a28.69 28.69 0 0 1-.23-9.5l-1.6.57Zm-5.5.58s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M612.833 218.915s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Z'/%3E%3Cpath fill='%23DC4472' d='M613.633 219.605s.34 5.72 1.49 7.67c0 0-.8-6.07-.34-8.36l-1.15.69Zm5.5.45s.11 5.61 1.72 7.67c0 0-1-4.81-.11-8.24l-1.61.57Z'/%3E%3Cpath fill='%23DC4472' d='M620.273 219.005s.11 7.21 1.72 9.27a28.55 28.55 0 0 1-.11-9.85l-1.61.58Zm-1.94.71s-.11 5.72 1 7.67c0 0-.34-6.07.11-8.36l-1.11.69Zm13.85.04s.11 6.07 1.72 8.3c0 0-1-5.2-.11-8.92l-1.61.62Z'/%3E%3Cpath fill='%23DC4472' d='M633.213 219.005s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.29l-1.6.62Z'/%3E%3Cpath fill='%23DC4472' d='M633.783 218.135s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.29l-1.6.62Z'/%3E%3Cpath fill='%23DC4472' d='M634.823 218.515s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.29l-1.6.62Zm-3.44.86s.34 6.2 1.49 8.3c0 0-.8-6.57-.34-9l-1.15.7Zm5.5.5s.11 6.07 1.72 8.3c0 0-1-5.2-.11-8.92l-1.61.62Z'/%3E%3Cpath fill='%23DC4472' d='M638.003 218.765s.11 7.81 1.72 10a33.4 33.4 0 0 1-.11-10.66l-1.61.66Zm-1.93.74s-.11 6.2 1 8.3c0 0-.34-6.57.11-9l-1.11.7Zm4.7-.1s.11 6.07 1.72 8.3c0 0-1-5.2-.11-8.92l-1.61.62Z'/%3E%3Cpath fill='%23DC4472' d='M641.803 218.665s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.33l-1.6.66Z'/%3E%3Cpath fill='%23DC4472' d='M642.373 217.795s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.29l-1.6.62Z'/%3E%3Cpath fill='%23DC4472' d='M643.403 218.165s.23 7.44 1.83 9.67a33.56 33.56 0 0 1-.23-10.29l-1.6.62Zm-3.4.84s.34 6.2 1.49 8.3c0 0-.8-6.57-.34-9l-1.15.7Zm5.46.53s.11 6.07 1.72 8.3c0 0-1-5.2-.11-8.92l-1.61.62Z'/%3E%3Cpath fill='%23DC4472' d='M646.613 218.415s.11 7.81 1.72 10a33.4 33.4 0 0 1-.11-10.66l-1.61.66Zm-1.95.75s-.11 6.2 1 8.3c0 0-.34-6.57.11-9l-1.11.7Zm6.64-2.43s.23 8.09 1.83 10.51a39.63 39.63 0 0 1-.23-11.19l-1.6.68Z'/%3E%3Cpath fill='%23DC4472' d='M651.883 215.795s.23 8.09 1.83 10.51a39.63 39.63 0 0 1-.23-11.19l-1.6.68Z'/%3E%3Cpath fill='%23DC4472' d='M652.913 216.195s.23 8.09 1.83 10.51a39.63 39.63 0 0 1-.23-11.19l-1.6.68Zm2.74-.57s.23 8.09 1.83 10.51a39.63 39.63 0 0 1-.23-11.19l-1.6.68Z'/%3E%3Cpath fill='%23DC4472' d='M654.623 215.625s.23 8.09 1.83 10.51a39.63 39.63 0 0 1-.23-11.19l-1.6.68Z'/%3E%3Cpath fill='%23DC4472' d='M654.163 217.275s-.11 6.74 1 9c0 0-.34-7.14.11-9.84l-1.11.84Zm-29.99 1.64s.11 7.21 1.72 9.27a28.55 28.55 0 0 1-.11-9.85l-1.61.58Z'/%3E%3Cpath fill='%23DC4472' d='M625.083 219.005s.11 7.21 1.72 9.27a28.56 28.56 0 0 1-.11-9.85l-1.61.58Zm-3.78.48s.11 6.41 1.72 8.47a24.92 24.92 0 0 1-.11-9l-1.61.53Z'/%3E%3Cpath fill='%23DC4472' d='M622.333 219.255s.11 6.3 1.26 8.24c0 0-.34-6.07.11-8.36l-1.37.12Z'/%3E%3Cpath fill='%23DC4472' d='M623.363 219.485s.34 6.76 1.49 8.7c0 0-.8-6.07-.34-9.39l-1.15.69Zm36.99-11.22s-1.49 14.88 3.43 18.09c0 0-6.76-2.52-5.27-15.57l1.84-2.52Z'/%3E%3Cpath fill='%23DC4472' d='M658.523 212.845s-.46 9.5 3.32 13.17c0 0-5.61-3-4.12-14l.8.83Z'/%3E%3Cpath fill='%23DC4472' d='M657.713 214.565s-.23 6.87 2.4 11.45c0 0-4.12-3.78-3.21-11.91l.81.46Z'/%3E%3Cpath fill='%23DC4472' d='M657.143 215.005s-.69 6.53 1.95 11.11c0 0-4.12-3.78-3.21-11.91l1.26.8Zm-42.65-58.61s-.17 6.87 2.23 11.34c0 0-3.26-2.75-3.43-8.59l1.2-2.75Z'/%3E%3Cpath fill='%23DC4472' d='M614.003 159.315s-.17 6.87 2.23 11.34c0 0-3.26-2.75-3.43-8.59l1.2-2.75Z'/%3E%3Cpath fill='%23DC4472' d='M613.803 163.615s-.17 6.87 2.23 11.34c0 0-3.26-2.75-3.43-8.59l1.2-2.75Z'/%3E%3Cpath fill='%23DC4472' d='M613.293 165.675s-.17 6.87 2.23 11.34c0 0-3.26-2.75-3.43-8.59l1.2-2.75Zm47.23 29.54s2.92 13.05 15.29 10.48c0 0-11 5.5-17.35-6.53l2.06-3.95Zm-62.8-50.72s1.37-2.52 1.6-9.39c0 0 .23 7.33 1.37 11.22l-2.97-1.83Z'/%3E%3Cpath fill='%23DC4472' d='M599.553 145.865s1.37-2.52 1.6-9.39c0 0 .23 7.33 1.37 11.22l-2.97-1.83Z'/%3E%3Cpath fill='%23DC4472' d='M601.153 146.785s1.37-2.52 1.6-9.39c0 0 .23 7.33 1.37 11.22l-2.97-1.83Z'/%3E%3Cpath fill='%23DC4472' d='M603.003 147.005s1.37-2.52 1.6-9.39c0 0 .23 7.33 1.37 11.22l-2.97-1.83Z'/%3E%3Cpath fill='%23DC4472' d='M604.823 147.465s1.37-2.52 1.6-9.39c0 0 .23 7.33 1.37 11.22l-2.97-1.83Z'/%3E%3Cpath fill='%23514944' d='M464.433 311.005h-.55l-.8.1h-.13l-.33.06a8.92 8.92 0 0 0-1 .28c-.73.09-4.27.79-8 6.31a2.32 2.32 0 0 0-1.67-.6 1.92 1.92 0 0 0-1.53 1.16v.19a18.19 18.19 0 0 0-.62 4.62 13.5 13.5 0 0 0 .16 2c-1.65 3.25-5.27 9.07-8.08 11.69-.34.29-6.65 5.67-12.22 11.55a73.39 73.39 0 0 0-13.66 18.84c-5 9.91-10.49 27.57-10.75 43 0 .39-.36 6-.36 12.25a100.09 100.09 0 0 0 .51 11.13v.14c0 .18 1.09 4.32 4.28 6a6.23 6.23 0 0 0 4.49.44 3.51 3.51 0 0 0 1.11 2.3 3.08 3.08 0 0 0 2.82.52h-.19c.52 0 5.05-.57 6.5-10.51a5.17 5.17 0 0 0 3.46-2.93 5.63 5.63 0 0 0 .39-2.12 14.9 14.9 0 0 0-2.34-6.94c0-.74.06-1.47.06-2.2v-.52c0-.27.28-4.32.59-8.32v1.6l.18 1.62.08.59.09.56a44.82 44.82 0 0 0 5.63 16l.08.13v-.07l.56.89.39.58v2.28c0 3-.08 6.12-.16 9.11-.07 2.69-.14 5.24-.15 7.7v.63a64.24 64.24 0 0 0 .52 9.23v.08a7.67 7.67 0 0 0 3.14 5c.8.9 4.75 4.42 18.27 6.07 5.61.59 8.54-.22 11.23-1.61h.11a6.81 6.81 0 0 0 2.71-4.28 7 7 0 0 0-1.83-5.44 12.49 12.49 0 0 0-2.18-2l-2-1.61s-1.07-1-1.21-4.13a7.08 7.08 0 0 0 2.57 3.25c.8.9 4.75 4.42 18.27 6.07 5.6.59 8.54-.22 11.23-1.61h.11a6.8 6.8 0 0 0 2.71-4.28 7 7 0 0 0-1.83-5.45 12.57 12.57 0 0 0-2.18-1.95l-2-1.61s-1.2-1.12-1.2-4.77v-2.99c0-1.77.1-3.93.28-6.58 4.48-2.94 11.19-9.67 11.19-23.64 0-.74-.02-1.504-.06-2.29v-.42a53.76 53.76 0 0 0-2.45-14.43c3.67-2 5.93-4.69 6.67-8.09.181-.845.272-1.706.27-2.57 0-6.92-5.38-13.63-5.91-14.28a5.47 5.47 0 0 1-.54-1.18 5.39 5.39 0 0 0-.88-1.67c-1.42-2.1-4.42-3-5.51-3.26a9.33 9.33 0 0 0-2.77-2.62l-.27-.12h-.3a4.52 4.52 0 0 0-3.76 2.16l-.09.17v.19a5.76 5.76 0 0 0 0 .71v.3l-.28.13c-.25-.46-.5-.92-.77-1.36a79.06 79.06 0 0 1-7.66-16 15.18 15.18 0 0 1-1-5.36 28.18 28.18 0 0 1 .62-5.16l.15-.81a26.82 26.82 0 0 0 1.27-8c0-3.8-1-8.15-4.64-11.11a9.86 9.86 0 0 0-1.8-1.09l-.17-.09a14.94 14.94 0 0 0-3.81-1.34h-.07l-1-.15h-2l.01-.07Zm-.36 2.24h2.08l.79.12h.07a12.64 12.64 0 0 1 3.22 1.14l.19.1a7.9 7.9 0 0 1 1.41.84c3 2.41 3.79 6.1 3.79 9.4a25.32 25.32 0 0 1-1.15 7.31v.08l-.18 1a30 30 0 0 0-.64 5.54 17.47 17.47 0 0 0 1.11 6.14 82 82 0 0 0 7.85 16.37c.43.7.83 1.42 1.23 2.18l.5.95 3.68-1.78-.21-.9a2.6 2.6 0 0 1-.08-.67v-.19a2.67 2.67 0 0 1 1.58-.78 9.28 9.28 0 0 1 2.12 2.07l.26.33.41.08c.91.17 3.5.95 4.5 2.41l.06.09.07.07c.218.305.386.642.5 1a7.24 7.24 0 0 0 .82 1.69c.06.07 5.45 6.62 5.45 12.93a9.865 9.865 0 0 1-.22 2.11c-.64 3-2.76 5.31-6.3 7l-.86.41.28.91a53 53 0 0 1 2.6 14.9v.39c.04.793.06 1.563.06 2.31 0 12.67-5.69 19-10.63 22l-.48.3v.56c-.22 3-.33 5.34-.33 7.29v3c0 4.59 1.68 6.21 2 6.51l2 1.63a11 11 0 0 1 1.82 1.6 5.25 5.25 0 0 1 1.39 3.25c.01.153.01.307 0 .46a4.53 4.53 0 0 1-1.72 2.75c-2 1-4.35 1.92-9.89 1.34-13.73-1.68-16.89-5.36-16.92-5.4l-.13-.15-.19-.12a5.44 5.44 0 0 1-2.26-3.65 64 64 0 0 1-.49-8.84v-4.58l-2.72.05-.08 1c-.42 4.77-.62 8.25-.62 10.92v2.99c0 4.61 1.69 6.23 2 6.52l2 1.62c.664.464 1.275 1 1.82 1.6a5.25 5.25 0 0 1 1.39 3.25c.01.153.01.307 0 .46a4.53 4.53 0 0 1-1.72 2.75c-2 1-4.35 1.92-9.89 1.34-13.73-1.68-16.89-5.36-16.92-5.4l-.13-.15-.19-.12a5.44 5.44 0 0 1-2.26-3.66 62.46 62.46 0 0 1-.49-8.85v-.62c0-2.44.08-5 .15-7.65.08-3 .16-6.11.16-9.17v-3l-.2-.3-.55-.81-.55-.87a42.58 42.58 0 0 1-5.33-15.18l-.08-.55v-.05l-.07-.48-.18-1.58-.05-.62v-.18c-.11-2.29-.16-6.08-.16-11.26v-4l-2.19-.24c-.67 3-1.13 5.48-1.43 7.57-.5 3.47-1.23 15-1.26 15.52v.08c0 1 0 1.95-.06 3v.34l.17.29c1.88 3.14 2.6 5.83 2 7.38a3.17 3.17 0 0 1-2.35 1.76h-.17l-.93.62-.06.5c-1.12 8.63-4.51 9.13-4.54 9.14h-.16a1.15 1.15 0 0 1-.89-.05c-.23-.19-.45-.82-.38-2l.09-1.6-1.85.58h-.08a4.42 4.42 0 0 1-3.79 0c-2.18-1.13-3-4.15-3.14-4.49a98.16 98.16 0 0 1-.49-10.74c0-6.35.34-12.08.35-12.17.23-14.39 5.47-32 10.51-42.05a71.23 71.23 0 0 1 13.26-18.18c5.63-5.94 12-11.38 12.09-11.43 3.1-2.87 6.92-9 8.65-12.5l.08-.19.12-.3-.06-.31a10.79 10.79 0 0 1-.17-2 16.41 16.41 0 0 1 .41-3.54c.208.193.399.403.57.63l1 1.28.83-1.39c3.66-6.1 7.08-6.46 7.12-6.47h.19l.16-.05a6.8 6.8 0 0 1 .91-.25h.36l.64-.24Z'/%3E%3Cpath fill='%2342BBA2' d='M491.183 409.955c-1.61 15.4-2.67 25.12-2.4 30.93-.3 5.31 1.62 6.74 1.62 6.74 2.07 1.75 3.17 2.37 4 3.37 4 4.94-.64 8.11-.64 8.11-2.31 1.2-5 2.08-10.58 1.49-14.64-1.79-17.67-5.81-17.67-5.81a6.59 6.59 0 0 1-2.79-4.4c-1.1-7.44 0-18.57-.21-28.92-.21-12.66-1.94-14.47-1.94-16.76l30.61 5.25Z'/%3E%3Cpath fill='%2317A786' d='M491.143 457.945c-14.64-1.79-17.67-5.81-17.67-5.81a6.59 6.59 0 0 1-2.79-4.4c-1.1-7.44 0-18.57-.21-28.92a66.54 66.54 0 0 0-1.11-12.59l-8.8-1.51c0 2.29 1.74 4.1 1.94 16.76.17 10.36-.89 21.49.21 28.92a6.59 6.59 0 0 0 2.79 4.4s3 4 17.67 5.81c5.61.59 8.27-.29 10.58-1.49a5.89 5.89 0 0 0 1-1 30.36 30.36 0 0 1-3.61-.17Z'/%3E%3Cpath fill='%2342BBA2' d='M463.493 419.705c-1.61 15.4-2.67 25.12-2.41 30.93-.3 5.32 1.62 6.74 1.62 6.74 2.07 1.75 3.17 2.37 4 3.37 4 4.93-.64 8.11-.64 8.11-2.31 1.2-5 2.09-10.58 1.49-14.64-1.79-17.67-5.82-17.67-5.82a6.58 6.58 0 0 1-2.79-4.4c-1.1-7.44 0-18.57-.21-28.93-.21-12.66-1.94-14.47-1.94-16.76l30.62 5.27Z'/%3E%3Cpath fill='%2342BBA2' d='M472.533 314.685c-3.63-2.55-7.83-3.1-10.74-2.07 0 0-6 .29-10.82 12.92-1.55 3.1-5.32 9.3-8.42 12.18 0 0-13.73 13.51-14.58 27.28 0 0-.72 34.93-.24 45 0 0 .72 16.14 11.08 25.53 0 0 23.85 6.27 45.53-1.69 0 0 17-4.1 15.81-27.46 0 0 0-18-12.85-37 0 0-.52-3.38-3.77-8.71a80.51 80.51 0 0 1-7.75-16.17c-1.78-4.84-.89-8.42-.26-12 0 .12 4.25-12.72-2.99-17.81Z'/%3E%3Cpath fill='%2317A786' d='M426.163 402.265c2.36-16.54 15.05-53.34 13.59-60.85a69.94 69.94 0 0 0-22.75 26.29c-5.09 10.08-10.39 27.78-10.63 42.53 0 0-.89 14.47.15 23.19 0 0 1.92 7.83 8.42 5.32 0 0 10.19-2.44 10-21-.04-.05.72-12.01 1.22-15.48Z'/%3E%3Cpath fill='%2342BBA2' d='M490.513 363.755s5-1.7 10 5.32c0 0 12.59 16-4.91 22.85l-20.34-20s10.82-7.51 15.25-8.17Z'/%3E%3Cpath fill='%2317A786' d='M454.153 467.375c-1.27-2.44-4.93-3.42-6.79-5.14-3.34-3.11-2.09-8.75-1.72-13 .4-4.66 2.52-8.92 2.38-13.65-.12-4.07-1.93-6.87-3.46-10.55-2.12-5.07-2.1-10.54-1.5-16 .6-5.46 2.93-10.41 4.07-15.65.68-3.11 2.07-5.91 3.55-11.82 4.14-14.47 1.77-21.27 1.18-39.58-.32-9.94 5-29.24 18.9-28.38a11.72 11.72 0 0 0-9-1s-6 .29-10.82 12.92c-1.55 3.1-5.32 9.3-8.42 12.18 0 0-34.71 29.39-14.58 27.28 0 0-.72 34.93-.24 45 0 0 .53 11.68 7.06 21v.21c.17 10.36-.89 21.49.21 28.93a6.58 6.58 0 0 0 2.79 4.4s2.93 3.88 16.87 5.71a6.06 6.06 0 0 0-.48-2.86Z'/%3E%3Cellipse cx='464.563' cy='324.695' fill='%23FFF' rx='2.88' ry='4.76'/%3E%3Cpath fill='%23514944' d='M461.373 324.695c0 2.84 1.4 5.07 3.19 5.07s3.19-2.23 3.19-5.07c0-2.84-1.4-5.07-3.19-5.07s-3.19 2.22-3.19 5.07Zm.62 0c0-2.41 1.18-4.45 2.57-4.45s2.57 2 2.57 4.45-1.18 4.45-2.57 4.45-2.56-2.04-2.56-4.45h-.01Z'/%3E%3Cellipse cx='465.443' cy='325.535' fill='%23514E42' rx='1.77' ry='3.32'/%3E%3Cellipse cx='473.273' cy='324.835' fill='%23FFF' rx='2.88' ry='4.76'/%3E%3Cpath fill='%23514944' d='M470.083 324.835c0 2.84 1.4 5.07 3.19 5.07s3.19-2.23 3.19-5.07c0-2.84-1.4-5.07-3.19-5.07s-3.19 2.24-3.19 5.07Zm.62 0c0-2.41 1.18-4.45 2.57-4.45s2.57 2 2.57 4.45-1.18 4.45-2.57 4.45-2.57-2.03-2.57-4.45Z'/%3E%3Cellipse cx='474.163' cy='325.685' fill='%23514E42' rx='1.77' ry='3.32'/%3E%3Cpath fill='%2317A786' d='M415.523 436.825s-1.48 5.91 2.36 5c0 0 5.91-.29 5.91-16.24l-8.27 11.24Z'/%3E%3Cpath fill='%2317A786' d='M423.723 419.005s7.75 10.41.07 12.48l-1.79 1.2s-3-9.23 1.72-13.68Zm68.75-41.26a35.89 35.89 0 0 1 6.2 12.41s8.12-3.25 1.33-10.34c0 0-2.21-2.96-7.53-2.07Zm-10.6-21.44a54.16 54.16 0 0 0-11.11.47l8.86 20.68s6.5 3.25 10-2.36c.04-.01-2.73-16.72-7.75-18.79Zm-26.69-34.53s-2.66-5.1-3.77-2.88c0 0-2.22 7.53 2 10.63l1.77-7.75Z'/%3E%3Cpath fill='%23514944' d='M426.603 408.005h.89a90.07 90.07 0 0 1 5.73-34.59l-.82-.34a91 91 0 0 0-5.8 34.93Zm63.91-31.89c.05.06 5.07 6.08 8.1 19.53l.86-.2c-3.09-13.68-8.08-19.66-8.29-19.91l-.67.58Z'/%3E%3Cpath fill='%23514944' d='M478.123 354.745a1.5 1.5 0 0 0 .54 1.1l1.65 20.78v.07c0 1.91 4.26 2.12 6.09 2.12s6.09-.21 6.09-2.12v.06l2.39-21c.256-.27.4-.628.4-1 0-2.58-7.14-2.71-8.57-2.71-1.43 0-8.59.12-8.59 2.7Zm4 21.94v.12-.12Zm4.62-22.87a17 17 0 0 1 6.58 1h-.05l-.09.38-2.4 21.22a11.08 11.08 0 0 1-4.33.63 11.26 11.26 0 0 1-4.32-.62l-1.71-21.55-.2-.13a17.29 17.29 0 0 1 6.47-.94l.05.01Z'/%3E%3Cpath fill='%23DC443F' d='m494.233 354.745-14.63-.15 1.75 22a.28.28 0 0 0 0 .06c0 .68 2.33 1.23 5.21 1.23s5.21-.55 5.21-1.23l2.46-21.91Z'/%3E%3Cpath fill='%2342BBA2' d='M490.073 361.765s5.1-.45 6.65 1.55c0 0 3.77 2.88.44 7.09l-1.33 6.6s-3.25 1.55-7.38-1.25c0 0-2.81-2.07.74-3.4 0 0-3.1-1.11-1.55-4a3.86 3.86 0 0 1 2.66-1.11s-3.54-.44-3.1-4a3.49 3.49 0 0 1 2.87-1.48Z'/%3E%3Cpath fill='%23C2322B' d='m486.393 354.885-6.68-.07 1.75 22a.28.28 0 0 0 0 .06c0 .65 2.1 1.18 4.78 1.23l.15-23.22Z'/%3E%3Cellipse cx='486.843' cy='354.745' fill='%23FFF' rx='7.68' ry='1.82'/%3E%3Cpath fill='%23514944' d='m488.853 337.875.06 17.15-3.1-.34v-.8c0-3.63.08-8.88.08-11.9v-1.76l-12.89-3.84.89-3 14.96 4.49Zm-1.43 24.05a2.7 2.7 0 0 0-.9 2.09v.21a4.32 4.32 0 0 0 2 3.09 3.38 3.38 0 0 0-1.18.85 2.63 2.63 0 0 0-.64 1.77v.29a3.21 3.21 0 0 0 .86 2.15c.108.082.226.153.35.21a2.25 2.25 0 0 0-.78 1.73c-.006.1-.006.2 0 .3v.05a2.36 2.36 0 0 0 .62 1.53c.79.91 2.22 1.42 4.23 1.52v-.89c-2.17-.1-3.15-.7-3.59-1.19a1.47 1.47 0 0 1-.42-1v-.09a1.58 1.58 0 0 1 1.22-1.73l-.16-.87a1.14 1.14 0 0 1-.94-.25 2.33 2.33 0 0 1-.56-1.53 1.85 1.85 0 0 1 .41-1.44 3.78 3.78 0 0 1 2.28-1v-.89c-2.45 0-2.87-2.61-2.88-2.72a1.85 1.85 0 0 1 .6-1.55c1.6-1.45 5.71-.83 5.76-.83l.14-.88c-.12-.01-4.51-.67-6.42 1.07Zm-37.56-38.6a14.29 14.29 0 0 0 1.15 5.52l.79-.4a13.64 13.64 0 0 1-1.06-5.12h-.88Z'/%3E%3Cpath fill='%2317A786' d='M468.693 445.385a42.13 42.13 0 0 0 18.31-12.7s-7.09 6.5-23.33 5l5.02 7.7Z'/%3E%3Cpath fill='%23514944' d='M461.863 437.415h-.4l-.07.4a64.09 64.09 0 0 0 0 16.3l.88-.1a75 75 0 0 1-.49-8.45 54.08 54.08 0 0 1 .43-7.21c2.53.13 17.12.55 28.66-6.74l-.47-.75c-12.4 7.77-28.4 6.56-28.54 6.55Z'/%3E%3Cpath fill='%23FF6068' d='m473.393 335.495 13.51 4.14c.09.82 0 15.07 0 15.07l1.32.15v-16.24l-14.35-4.39-.48 1.27Z'/%3E%3Cpath fill='%23FF0' d='M235.433 571.895s-3.38-37.68-80.43-48.89a17.83 17.83 0 0 0 9 14.62s-8.44 6.75 5.06 15.19c0 0-11.25 9.56 8.44 18 0-.04 22.65 11.52 57.93 1.08Zm237.34 0s3.37-37.68 80.43-48.93a17.83 17.83 0 0 1-9 14.62s8.44 6.75-5.06 15.19c0 0 11.25 9.56-8.44 18 0 0-22.7 11.56-57.93 1.12Z'/%3E%3C/g%3E%3C/svg%3E");background-position:bottom var(--size-spacing-half-again) center;background-repeat:no-repeat;background-size:366px 481px;padding-bottom:530px}@media screen and (min-width:768px){.content-migrate{background-position:var(--size-spacing-quadruple) var(--size-spacing-quadruple);min-height:calc(481px + var(--size-spacing-quadruple));padding-bottom:0;padding-left:430px}}.content-migrate p{font-size:1.25rem;margin:var(--size-spacing-triple) 0}.content-membership-thanks{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 625 821'%3E%3Cg fill='none' transform='translate(.904)'%3E%3Cpath fill='%23FFFFCF' d='M27.336 191.6s-63.8-86.76 33.89-180.69c0 0 5.52-4.18 4.58 1.47-4.32 26.23-18.65 112.72 28.29 170.32l-66.76 8.9Zm569.15 0s63.79-86.76-33.89-180.69c0 0-5.52-4.18-4.58 1.47 4.32 26.23 18.65 112.71-28.29 170.32l66.76 8.9Z'/%3E%3Cpath fill='%23000' d='M623.096 122.64s0 .07-.06.11c-.41-4-3.46-7.18-7.21-7.18a7.14 7.14 0 0 0-6.11 3.67 6.84 6.84 0 0 0-4.79-2 7.11 7.11 0 0 0-6 3.45 6.77 6.77 0 0 0-11 .12 7.12 7.12 0 0 0-6-3.47 6.89 6.89 0 0 0-4.93 2.15 7.15 7.15 0 0 0-6.17-3.79 6.9 6.9 0 0 0-5 2.21 7.09 7.09 0 0 0-5.89-3.35c-3.65 0-6.65 3-7.18 6.85-7.63 47.16-51.46 53.63-80.38 53.33-17.54-20.12-36.23-37.81-36.23-37.81-24.19-22-39.57-36.11-53.87-50.86C331.586 33.69 316.406 0 316.406 0c-3.92 7.7-14.31 29.89-60.44 79.09-14.51 15.48-37.09 37.81-58.27 57.82 0 0-16.33 16.58-33.38 37.25-26.29 1.89-84.47 2.26-93.37-52.77-.53-3.87-3.53-6.85-7.18-6.85a7.09 7.09 0 0 0-5.89 3.35 6.9 6.9 0 0 0-5-2.21 7.15 7.15 0 0 0-6.17 3.79 6.89 6.89 0 0 0-4.93-2.15 7.12 7.12 0 0 0-6 3.47 6.77 6.77 0 0 0-11-.12 7.11 7.11 0 0 0-6-3.45 6.85 6.85 0 0 0-4.79 2 7.14 7.14 0 0 0-6.11-3.67c-3.75 0-6.81 3.15-7.21 7.18 0 0 0-.07-.06-.11-.06-.04-15.33 114.51 111.16 139.31-12.91 33-19.79 70.82-13 111.74 0 0 11.1 116.08 142.18 151.19-17.94 8.37-28.2 17.14-28.2 17.14a136.11 136.11 0 0 0-15.09 15.13v-.29c-20.42 26.62-41.47 29.09-41.47 29.09a48.71 48.71 0 0 0 24.23-3l-.09.17c-12.8 12.11-23.71 14.45-23.71 14.45a51.13 51.13 0 0 0 18.29-2.85c-5.89 13.94-10.79 30.56-14.27 50.69-2 7.38-8.44 41.8 15.23 41.4 23.67-.4 26.93-18.43 23-33.67 5.14-28.32 15-65.43 45.73-88.65 0 12.44 1.66 203.5.87 216.36a167 167 0 0 0-15.53 11.17c-15.35 13.65-2.27 27.1 25.79 28.17 46.76 1.41 55-13.77 55.79-22.39.27-15.49-.7-63.44-.82-63.41.24-10.47-.2-20.55.68-26.62.77-5.35 3.54-7.62 7-7.73h.52c3.42.11 6.19 2.38 7 7.73.88 6.07.43 16.15.68 26.62-.12 0-1.09 47.92-.83 63.41.75 8.62 9 23.79 55.8 22.39 28.06-1.07 41.15-14.52 25.79-28.17-3.6-3.2-11.83-7.41-15-9.42-.8-12.87.3-205.78.34-218.22v-3.33c2.79.57 4.37.77 4.37.77a133.62 133.62 0 0 0 30.9.31c26.47.41 39.2 11.47 39.2 11.47a49.64 49.64 0 0 0-16.46-14.83c16.95 1.49 25.68 7.75 25.68 7.75a50.59 50.59 0 0 0-15.24-12c14.37-5 30.08-12.61 47.45-23.66 6.84-3.46 34.69-19.15 20.39-39.12a33.76 33.76 0 0 0-3.55-5.37c2.1-2.68 10.48-16.33 4.62-21.5-5.86-5.17-13.71-1-24.09 14.77a5.15 5.15 0 0 1-.48.59s-5.27-6.75-11.42-2c0 0-5.49 3.3-5.5 19.12-23.86 14.9-57 32.09-93.79 26.22 132.69-37 140.6-149.9 140.6-149.9 6.62-41.12.32-80.31-12.11-114.21 114.39-29.1 99.85-136.8 99.85-136.8Z'/%3E%3Cellipse cx='283.036' cy='160.51' fill='%23FFFFCF' rx='24.96' ry='33.33'/%3E%3Cellipse cx='283.026' cy='160.52' fill='%23000' rx='14.58' ry='19.47'/%3E%3Cellipse cx='341.796' cy='160.51' fill='%23FFFFCF' rx='24.96' ry='33.33'/%3E%3Cellipse cx='341.786' cy='160.52' fill='%23000' rx='14.58' ry='19.47'/%3E%3Ccircle cx='312.406' cy='203.84' r='7.18' fill='%23FF005D'/%3E%3Cpath fill='%23F09' d='M349.756 261c49.37-1.78 95.9-8.11 137.51-18.09-7.62 62.5-52.62 114-112.94 133.41-11.71-13.11-30.9-21.66-52.6-21.66-21.7 0-41.08 8.64-52.78 21.85-60.64-19.21-105.93-70.88-113.58-133.61 42.27 10.14 89.61 16.52 139.84 18.18 0 .04 28.13 1.38 54.55-.08Z'/%3E%3Cpath fill='%23A80058' d='M321.726 354.7c-21.81 0-41.08 8.64-52.78 21.85a174.37 174.37 0 0 0 105.38-.2c-11.71-13.1-30.9-21.65-52.6-21.65Z'/%3E%3Crect width='70.28' height='70.28' x='282.996' y='547.64' fill='%23F09' rx='3.75'/%3E%3Cpath fill='%23FFF' d='M333.306 594.29a2.38 2.38 0 0 0-3.25.87 13.75 13.75 0 0 1-23.83 0 2.382 2.382 0 0 0-4.12 2.39 18.51 18.51 0 0 0 32.08 0 2.38 2.38 0 0 0-.88-3.26Zm-41.56-25.73a9.74 9.74 0 1 0 19.47-.624 9.74 9.74 0 0 0-19.47.624Zm33.35 0a9.74 9.74 0 1 0 19.47-.624 9.74 9.74 0 0 0-19.47.624Z'/%3E%3C/g%3E%3C/svg%3E");background-position:bottom var(--size-spacing-half-again) center;background-repeat:no-repeat;background-size:312px 410px;padding-bottom:450px}@media screen and (min-width:768px){.content-membership-thanks{background-position:var(--size-spacing-quadruple) var(--size-spacing-quadruple);min-height:calc(410px + var(--size-spacing-quadruple));padding-bottom:0;padding-left:380px}}.content-membership-thanks a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.content-membership-thanks a:active,.content-membership-thanks a:focus,.content-membership-thanks a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.content-membership-thanks a:active,.content-membership-thanks a:focus,.content-membership-thanks a:hover{text-decoration:underline}.membership-header{margin-bottom:var(--size-spacing-double)}@media screen and (min-width:480px){.membership-header{display:flex}}@media screen and (min-width:768px){.membership-header{margin-bottom:var(--size-spacing-quadruple)}}.membership-header h2{font-size:2rem;line-height:1.1;margin-bottom:var(--size-spacing-default);margin-top:var(--size-spacing-double)}.membership-header p{font-size:1.2rem;margin:0}.membership-header--illustration{display:none}@media screen and (min-width:480px){.membership-header--illustration{display:block;flex:none;margin-right:var(--size-spacing-half-again);width:75px}}@media screen and (min-width:768px){.membership-header--illustration{margin-right:var(--size-spacing-quadruple);width:140px}}@media screen and (min-width:480px){.membership-header--content{flex:1}}.membership-options>*{margin:var(--size-spacing-double) 0}@media screen and (min-width:768px){.membership-options{display:flex}.membership-options>*{margin:0}.membership-options>*+*{margin-left:var(--size-spacing-quadruple)}}@media screen and (min-width:768px){.membership-options--plan{flex:1}}.membership-options--plan .label{margin-bottom:var(--size-spacing-half)}.membership-options--plan .btn{text-transform:uppercase}.membership-options--plan h3{font-size:2rem}.membership-options--plan p{margin-top:.25em}.membership-options--plan ul{margin-bottom:2em}@media screen and (min-width:768px){.is-active-member .membership-options--plan{padding-top:var(--size-spacing-quadruple)}.is-active-member .membership-options--plan.is-active-plan{padding-top:0}}.membership-options--plan-title{color:var(--color-brand-primary);text-transform:uppercase}.membership-options--plan-tagline{font-size:1.25rem}.membership-options--or{align-items:center;display:flex;justify-content:center}@media screen and (min-width:768px){.membership-options--or{flex-direction:column}}.membership-options--or:after,.membership-options--or:before{background:var(--color-brand-primary);content:"";flex:1;height:1px}@media screen and (min-width:768px){.membership-options--or:after,.membership-options--or:before{height:auto;width:1px}}@media screen and (min-width:768px){.membership-options--or:before{flex-basis:var(--size-spacing-triple);flex-grow:0}}.membership-options--or-bullet{background-color:var(--color-brand-primary);border-radius:50%;color:var(--color-text-light-emphasis);display:flex;font-size:1.5rem;height:3em;margin-left:auto;margin-right:auto;width:3em}.membership-options--or-bullet span{margin:auto}.membership-footer{margin-top:var(--size-spacing-half-again);text-align:center}@media screen and (min-width:768px){.membership-footer{margin-top:var(--size-spacing-quadruple)}}.membership-footer p{margin:0}.content-permalink{padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.content-permalink{padding:var(--size-spacing-triple)}}.content-permalink>*{margin:0!important;padding:0!important}.content-permalink>*+*{margin-top:var(--size-spacing-half-again)!important}@media screen and (min-width:768px){.content-permalink>*+*{margin-top:var(--size-spacing-triple)!important}}@media screen and (min-width:768px){.content-permalink>.permalink-sidebar{float:right;width:330px}}@media screen and (min-width:768px){.content-permalink>.image-comment-form,.content-permalink>.image-comments,.content-permalink>.image-content{float:left;width:calc(100% - 360px)}}@supports (display:grid){@media screen and (min-width:768px){.content-permalink{grid-gap:var(--size-spacing-triple);display:grid;grid-template-areas:"title title" "image sidebar" "comments sidebar" "post-comment sidebar";grid-template-columns:1fr 330px;grid-template-rows:repeat(3,min-content) 1fr}.content-permalink>*{margin:0!important}.content-permalink:after{content:none}.content-permalink>.image-comment-form,.content-permalink>.image-comments,.content-permalink>.image-content,.content-permalink>.permalink-sidebar{width:auto}.content-permalink>.image-title{grid-area:title}.content-permalink>.image-content{grid-area:image}.content-permalink>.permalink-sidebar{grid-area:sidebar}.content-permalink>.image-comments{grid-area:comments}.content-permalink>.image-comment-form{grid-area:post-comment}}}.permalink-sidebar{position:relative}.tools-page{background-color:var(--color-background-page)}.tools-page a{color:var(--color-text-link-primary);text-decoration-color:var(--color-text-link-primary-underline)}.tools-page a:active,.tools-page a:focus,.tools-page a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.tools-page .header{color:var(--color-page-text-secondary);font-size:.75rem;height:55px;margin:0 var(--size-spacing-double);padding-top:var(--size-spacing-half);position:relative;text-align:right}.tools-page .header a{color:var(--color-text-link-primary);font-weight:var(--number-font-weight-bold);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.tools-page .header a:active,.tools-page .header a:focus,.tools-page .header a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.tools-page .header a:active,.tools-page .header a:focus,.tools-page .header a:hover{text-decoration:underline}.tools-page .header img{height:49px;left:0;position:absolute;top:3px;width:100px}.tools-page .content{background:var(--color-background-content);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-double)}.tools-page .content-inner{display:flex;flex-direction:column;margin:0 auto}@media screen and (min-width:768px){.tools-page .content-inner{flex-direction:row;justify-content:space-between}}.tools-page .content-inner>*{min-width:0}@media screen and (min-width:768px){.tools-page .content-inner>*{flex-basis:calc(50% - 10px)}}.tools-page .content-inner>*+*{margin-top:20px}@media screen and (min-width:768px){.tools-page .content-inner>*+*{margin-top:0}}@media screen and (min-width:768px){.tools-page .content-inner .full-width{flex:1}}.tools-page .tools-fun-form{margin-top:0}.tools-page .tools-fun-form .tools-field-title .input-text{font-weight:var(--number-font-weight-bold)}.tools-page .tools-saved-it{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 616 256'%3E%3Cg fill='none'%3E%3Cpath fill='%23ED003A' d='M181.23 158.14c3.76-5.57 2-6.88 2-6.88a1.59 1.59 0 0 0-2.38-.11 11.18 11.18 0 0 0-1.84 2.44c-.86 1.42-1.84 2.95-2.67 4a38.64 38.64 0 0 1-4.66 5.17l-.42-.21c-15.16 11.83-28.86 7.38-30.19 6.91v-3.11c-12.08-9.74-24.6-7.22-24.6-7.22-17.11 0-19.12 7.22-19.12 7.22l.1 3.64-1-.57S82.1 175 66.14 162.56l-.42.21a38.62 38.62 0 0 1-4.66-5.17c-.82-1.09-1.81-2.62-2.67-4a11.18 11.18 0 0 0-1.84-2.44 1.59 1.59 0 0 0-2.38.11s-1.75 1.31 2 6.88c0 0-5.45 3.26-2.37 8.36a9.83 9.83 0 0 0 5.06 4.34s17.06 14.46 38.75 9.23c0 9.62-.05 31.32-.09 46a3.15 3.15 0 0 1-.32-.25l.23 14.44a28 28 0 0 0-4.2 3.16 5 5 0 0 0-1.84 3.64 9.35 9.35 0 0 0-.2 3.79s.31 2 3.12 3.35a18.88 18.88 0 0 0 8.05 1.53s6.37.08 10.3-1.53c2.48-.86 4.84-2.55 5.59-4.47 0 0 .74-4.35.12-6.46a2.68 2.68 0 0 0-.8-1.13c0-6-.15-15.94-.15-15.94l-.11.06c0-2.35-.1-4.59-.1-6.25 0-3 .58-3.95 2.17-3.95 2.7 0 2.53 3.89 2.53 3.89l.12 6.28s.16 10.17.22 16.22a3.06 3.06 0 0 0-.6.85c-.62 2.11.12 6.46.12 6.46.74 1.92 3.11 3.61 5.59 4.47 3.93 1.61 10.3 1.53 10.3 1.53a18.88 18.88 0 0 0 8.05-1.53c2.81-1.39 2.84-3.35 2.84-3.35a11.19 11.19 0 0 0-.5-4.15l-.11-.08a6.12 6.12 0 0 0-1.71-3.19 20.13 20.13 0 0 0-3.71-3v-.25c0-1.24-.07-7.24-.2-14.69l-.2.15c-.35-14.13-.79-34.5-1-45.21 21.09 4.39 37.48-9.51 37.48-9.51a9.83 9.83 0 0 0 5.06-4.34c3.02-5.22-2.43-8.48-2.43-8.48Z'/%3E%3Cpath fill='%23FFF' d='m118.12 171-12.09 19.27h8.51l-13.45 24.64 30.92-30.02h-8.96l9.41-13.89z'/%3E%3Cpath fill='%2317B6E1' d='m59.92 54.48-25.3-21.62-1.82 12.32L0 20.47l36.85 51.15 1.92-12.97 18.09 16.58zm116.47 0 25.3-21.62 1.82 12.32 32.8-24.71-36.85 51.15-1.92-12.97-18.09 16.58z'/%3E%3Cellipse cx='117.5' cy='104.42' fill='%23FFF' rx='68.1' ry='62'/%3E%3Cpath fill='%23FF0042' d='M119.45 31.21s-82.19-3-78.59 73.94c4.08 63 61.29 66.76 77.51 66.76 19.88 0 73.46-4.69 76.8-66.76 0 0 6.08-71.77-75.72-73.94Z'/%3E%3Cpath fill='%23FFF' d='M119.45 33.18c69.72 1.85 75.81 54.1 76.07 69.76.21-12.11-2.62-70-76.07-72 0 0-79.83-2.87-78.94 70.53.71-71.09 78.94-68.29 78.94-68.29Z'/%3E%3Cpath fill='%23D9002B' d='M195.2 99.1c-3.35 62.27-56.86 66.9-76.8 66.9-16.28 0-73.4-3.86-77.5-67 0-.58-.023-1.153-.07-1.72-.08 2.51-.1 5.1 0 7.8 4.1 63.16 61.29 67 77.57 67 19.94 0 73.45-4.71 76.79-67a54.87 54.87 0 0 0 0-6.45c.02.37.01.47.01.47Z'/%3E%3Ccircle cx='148.47' cy='44.32' r='1.58' fill='%23FFF'/%3E%3Cpath fill='%2300DCD7' d='M118.37 56.81c-67-1.18-63.78 41.51-63.78 41.51-.16 4.82-1 62.77 63.77 62.77 64.78 0 63.93-58 63.77-62.77.02.01 3.22-42.69-63.76-41.51Z'/%3E%3Cpath fill='%2317B6E1' d='M54.58 101.33C54.92 93 60 60.71 118.37 61.74c58.36-1 63.44 31.25 63.78 39.58v-3s3.19-42.69-63.78-41.51c-67-1.18-63.78 41.51-63.78 41.51-.02.5-.04 1.54-.01 3.01Z'/%3E%3Ccircle cx='117.88' cy='91.49' r='2.25' fill='red'/%3E%3Cpath fill='%23FFF' d='M102.09 80.62c-.225 6.41-5.563 11.445-11.974 11.297-6.411-.149-11.51-5.426-11.438-11.839.072-6.413 5.289-11.574 11.702-11.578a11.91 11.91 0 0 1 11.71 12.12Z'/%3E%3Cellipse cx='90.34' cy='80.65' fill='%23231F20' rx='3.72' ry='3.85'/%3E%3Cpath fill='%23FFF' d='M133.78 80.62c.225 6.41 5.563 11.445 11.974 11.297 6.411-.149 11.51-5.426 11.438-11.839-.072-6.413-5.289-11.574-11.702-11.578a11.91 11.91 0 0 0-11.71 12.12Z'/%3E%3Cellipse cx='145.54' cy='80.65' fill='%23231F20' rx='3.72' ry='3.85'/%3E%3Cpath fill='%23EB2D74' d='M125.11 114a159.49 159.49 0 0 0 31.26-4.11c-1.73 14.21-12 25.92-25.67 30.33-6.634-6.611-17.366-6.611-24 0-13.7-4.32-24.04-16.06-25.78-30.32a159.79 159.79 0 0 0 31.79 4.1s6.39.34 12.4 0Z'/%3E%3Cpath fill='%23C00058' d='M118.73 135.3a16 16 0 0 0-12 5 39.64 39.64 0 0 0 24 0 16 16 0 0 0-12-5Z'/%3E%3Cpath fill='%23FF0' d='M159.59 117s66.31-14 66.31-40 5.62-64 41.86-64c10.67 0 41-7.41 105.24-9C527 .19 563.83 0 563.83 0s51 10 51 48 6 116-51 116S482 169 429 169c-53 0-147.5 6.5-154 0-3.36-3.36-11.86-3.3-23.18-13-10.56-9-24.1-35-24.1-35s-12.08 8.46-32.37 7c-27.83-2-35.76-11-35.76-11Z'/%3E%3Cpath fill='%23FF0180' d='M263.62 123.39 263 109a39.67 39.67 0 0 0 18.49 4.12c4.9-.21 8.59-2.87 8.41-7-.19-4.5-4.37-6.32-7.1-6.91l-6.47-1.53c-6.07-1.44-15.4-6.75-15.93-19.34-.52-12.19 10-19.94 21.65-20.44a41.17 41.17 0 0 1 16.2 2l.61 14.39A28.15 28.15 0 0 0 284 70.67c-4.9.21-9.38 3.1-9.18 7.8.18 4.2 4.65 5.81 7.38 6.49l5.46 1.37c8.29 2.05 16.12 7.22 16.6 18.51.58 13.69-9.3 21.41-22.19 22a41.21 41.21 0 0 1-18.45-3.45Zm90.26-.52-12.29.52-.1-2.4a18.31 18.31 0 0 1-10.65 3.75c-12.19.52-22.21-9.27-22.73-21.46a22.45 22.45 0 0 1 44.86-1.9l.91 21.49Zm-23.82-30.62c-5.546.208-9.873 4.872-9.666 10.419.207 5.546 4.871 9.874 10.417 9.667 5.546-.207 9.875-4.87 9.669-10.416a10.05 10.05 0 0 0-10.42-9.67Zm35.09 25.13-13.7-37.56 13.29-.56 8.39 25.57 6.3-26.19 13.19-.56-10.37 38.58c-1.13 4.15-4.15 6.08-8.34 6.26-4.19.18-7.29-1.5-8.76-5.54Zm71.44-12.53s-4.14 15.59-20.53 16.28a22.45 22.45 0 0 1-1.9-44.86c11.69-.5 22 8.38 22.39 18.07.19 4.6-2 8.29-7.45 8.52l-22.68 1a9.57 9.57 0 0 0 16.28 1.61l13.89-.62ZM423.3 93.7a8.94 8.94 0 0 0-8.63-5.14 9.23 9.23 0 0 0-8.67 5.87l17.3-.73Zm61.45 2.1c.525 12.388-9.092 22.855-21.48 23.38-12.388.525-22.855-9.092-23.38-21.48-.52-12.29 8.64-22.89 20.83-23.4a18 18 0 0 1 10.42 2.56l-1.1-26 12.79-.54 1.92 45.48Zm-22.91-9.14c-5.546.208-9.873 4.872-9.666 10.419.207 5.546 4.871 9.874 10.417 9.667 5.546-.207 9.875-4.87 9.669-10.416a10.05 10.05 0 0 0-10.42-9.67ZM515 52a7.6 7.6 0 1 1 .646 15.186A7.6 7.6 0 0 1 515 52Zm-3.68 64.22-1.82-43.09 12.79-.54 1.83 43.06-12.8.57ZM527.16 53l12.84-.58.82 19.38 8.09-.34.5 11.89-8.09.34.53 12.49c.2 4.8 3.39 6.66 6.88 6.51l2.2-.09.5 11.89-2.2.09c-13.49.57-19.76-8.07-20.18-17.86L527.16 53Zm29.77 41-2.26-53.41L569.5 40l2.26 53.41-14.83.59Zm7.65 3.75a8.58 8.58 0 1 1 1.12 17.123 8.58 8.58 0 0 1-1.12-17.123Z'/%3E%3C/g%3E%3C/svg%3E") 0 0 no-repeat;background-size:contain;font-size:1.75rem;padding-top:calc(41.5625% + var(--size-spacing-double))}@media screen and (min-width:768px){.tools-page .tools-saved-it{background-size:320px 133px;padding-top:160px}}.tools-page .content-sign-in{margin-top:var(--size-spacing-double)}.tools-page .content-sign-in h1{color:var(--color-brand-secondary);font-size:1.5rem;padding:var(--size-spacing-triple)}.tools-page .tools-signin-logo{margin-top:var(--size-spacing-default);text-align:center}.tools-page .sign-in-fun-form{margin-top:0;padding-bottom:var(--size-spacing-double)}@media screen and (min-width:768px){.tools-page .sign-in-fun-form label{flex-basis:120px}.tools-page .sign-in-fun-form .field-submit{padding-left:130px}}.tools-page .email-unverified h2,.tools-page .over-upload-limit h2{color:var(--color-brand-primary);font-size:1.5rem;text-align:center}.content-relationships .body .friend,.content-relationships .body .shake{border-bottom:1px dashed var(--color-border-default);padding:0 var(--size-spacing-quadruple)}.content-relationships .body .user-follow-extended .website a{color:var(--color-brand-secondary)}.content-relationships .user-info .avatar{float:left;margin-right:var(--size-spacing-half-again)}.content-relationships .user-info .avatar--img{height:var(--size-avatar-large);width:var(--size-avatar-large)}.content-relationships .user-info .details h3{color:var(--color-page-text);font-size:1.125rem;font-weight:var(--number-font-weight-bold);padding-top:var(--size-spacing-half)}.content-search{list-style:none}.content-search .search-empty-results{margin:var(--size-spacing-double)}.sidebar-search-block{margin-bottom:var(--size-spacing-double)}.sidebar-search-block .field-help{padding-left:0}.settings-header{align-items:center;background:linear-gradient(to bottom,var(--color-base-white-transparent),var(--color-base-white-transparent) calc(100% - 6px),var(--color-border-default));display:flex;flex-direction:column;justify-content:space-between;padding:1.5rem 1.875rem 1rem;width:100%}.settings-header .avatar{display:flex}.settings-header .avatar img{display:block;height:50px;width:50px}.settings-header .avatar-media{flex:none}.settings-header h2{word-wrap:break-word;font-size:1.875rem;line-height:50px;overflow-wrap:break-word;padding-left:1.5rem;word-break:break-word}@media screen and (min-width:480px){.settings-header h2{font-size:2.25rem}}@media screen and (min-width:768px){.settings-header h2{font-size:2.625rem}.settings-header{flex-direction:row}}@media screen and (min-width:768px){.settings-header>*{flex:1;max-width:calc(50% - var(--size-spacing-double))}}.settings-header h1{font-size:3rem;margin-bottom:var(--size-spacing-half-again)}@media screen and (min-width:768px){.settings-header h1{margin-bottom:0}}.settings-navigation ul{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);display:flex;list-style:none;margin:0;padding:var(--size-spacing-half-again);padding-bottom:0}@media screen and (min-width:480px){.settings-navigation ul{padding-left:var(--size-spacing-double)}}@media screen and (min-width:480px){.settings-navigation li+li{margin-left:var(--size-spacing-half-again)}}.settings-navigation a{border-top-left-radius:var(--size-border-radius-default);border-top-right-radius:var(--size-border-radius-default);color:var(--color-text-link-primary);display:block;font-size:.875rem;font-weight:var(--number-font-weight-bold);padding:var(--size-spacing-default);padding-bottom:var(--size-spacing-half-again);text-decoration-color:var(--color-text-link-primary-underline);text-decoration:none}.settings-navigation a:active,.settings-navigation a:focus,.settings-navigation a:hover{color:var(--color-text-link-primary-hover);text-decoration-color:var(--color-text-link-primary-underline-hover)}.settings-navigation a:active,.settings-navigation a:focus,.settings-navigation a:hover{text-decoration:underline}.settings-navigation .selected a{background-color:var(--color-background-content)}.settings-body{display:flex;flex-direction:column;justify-content:space-between;padding:var(--size-spacing-half-again)}@media screen and (min-width:768px){.settings-body{flex-direction:row;padding:var(--size-spacing-quadruple)}}.settings-body>*{margin:0}@media screen and (min-width:768px){.settings-body>*{flex:1;max-width:calc(50% - var(--size-spacing-double))}}.settings-body>*+*{margin-top:var(--size-spacing-triple)}@media screen and (min-width:768px){.settings-body>*+*{margin-top:0}}.settings-subscription-sidebar h4{border-bottom:1px dashed var(--color-border-default);font-size:1.125rem;margin-bottom:var(--size-spacing-default);padding-bottom:.25em}.settings-subscription-sidebar .member-status-block,.settings-subscription-sidebar .migration-block{background-color:var(--color-bg-success-pastel);border-radius:var(--size-border-radius-large);padding:var(--size-spacing-half-again)}.settings-subscription-sidebar .member-status-block h3,.settings-subscription-sidebar .migration-block h3{color:var(--color-status-success-pastel-dark)}.settings-subscription-sidebar .member-status-block:first-child,.settings-subscription-sidebar .migration-block:first-child{margin-top:0}.settings-subscription-sidebar .member-status-block:last-child,.settings-subscription-sidebar .migration-block:last-child{margin-bottom:0}.settings-subscription-sidebar .migration-block{background-color:var(--color-bg-secondary-brand-pastel);margin-bottom:var(--size-spacing-double)}.settings-subscription-sidebar .migration-block h3{color:var(--color-brand-secondary)}.settings-subscription-sidebar .member-status-block-content,.settings-subscription-sidebar .migration-block-content{background-color:var(--color-background-content);border-radius:var(--size-border-radius-large);font-size:.875rem;margin-top:var(--size-spacing-default);padding:var(--size-spacing-double)}.settings-subscription-sidebar .transaction-list li{margin-bottom:var(--size-spacing-default)}.settings-subscription-sidebar .transaction-list li .id{color:var(--color-page-text-secondary)}.settings-subscription-sidebar .transaction-list li .amount{font-weight:var(--number-font-weight-bold)}.settings-subscription-sidebar p{margin:var(--size-spacing-double) 0}.settings-body-content .fun-form{margin-top:0}.settings-body-sidebar .profile-photo{display:flex;flex-direction:column}@media screen and (min-width:480px){.settings-body-sidebar .profile-photo{flex-direction:row}}.settings-body-sidebar .profile-photo-media{flex:none}.settings-body-sidebar .profile-photo-media .avatar--img{height:var(--size-avatar-large);width:var(--size-avatar-large)}.settings-body-sidebar .profile-photo-meta{flex:1;padding-top:var(--size-spacing-default)}@media screen and (min-width:480px){.settings-body-sidebar .profile-photo-meta{margin-left:var(--size-spacing-default);padding-top:var(--size-spacing-half)}}.settings-body-sidebar .profile-photo-meta h3{font-size:1.125rem;margin-bottom:var(--size-spacing-double)}.settings-body-sidebar .profile-photo-meta h4{font-size:.875rem;margin-bottom:var(--size-spacing-default)}.settings-body-sidebar .profile-photo-meta .settings-photo-upload{color:var(--color-page-text-secondary);font-size:.75rem}.settings-body-sidebar .info-block{background-color:var(--color-background-content-secondary);border-radius:var(--size-border-radius-large);margin-top:var(--size-spacing-quadruple);padding:var(--size-spacing-half-again)}@media screen and (min-width:480px){.settings-body-sidebar .info-block{padding:var(--size-spacing-double)}}.settings-body-sidebar .info-block h3{font-size:1rem;margin-bottom:var(--size-spacing-default)}.settings-body-sidebar .info-block p{font-size:.875rem;margin-bottom:0}.settings-body-connections{display:block}.settings-body-connections>*{max-width:none}.settings-body-connections h3{font-size:1.125rem}.settings-body-connections .apps{list-style:none;margin:var(--size-spacing-triple) 0;padding:0}.settings-body-connections .apps li{margin-bottom:var(--size-spacing-double)}.settings-body-connections .apps h4{color:var(--color-page-text-secondary);font-size:1.125rem;margin-bottom:var(--size-spacing-half)}.settings-body-connections .apps h4 .title{color:var(--color-brand-primary)}.settings-body-connections .apps h4 .by{font-size:.875rem}.settings-body-connections .apps h4 .by a{text-decoration:none}.settings-body-connections .apps p{font-size:.875rem;margin-bottom:var(--size-spacing-half)}.settings-body-connections .apps .disconnect{font-size:.75rem;text-decoration:none}#sign-in-form .field-submit{text-align:right}.forgot-password-field{padding-top:var(--size-spacing-double);text-align:right}.password-manager-icon{float:left;padding-right:var(--size-spacing-double)}.steps{display:none}.steps p{text-align:center} \ No newline at end of file diff --git a/static/js/tools.js b/static/js/tools.js index 7555bdec..259f8de9 100644 --- a/static/js/tools.js +++ b/static/js/tools.js @@ -1,29 +1,29 @@ /* JS for the tools page */ (function($) { - + $.fn.hint = function(text) { - + var default_text = text; var field = this; var original_color = $(this).css('color'); - + var setDefault = function() { if ($(field).val() == '') { $(field).css('color', '#999').val(default_text); } }; - + var resetStyles = function() { $(field).css('color', original_color); }; - + var clearField = function() { $(field).val(''); }; - + setDefault(); - + this.focus(function() { if ($(this).val() == default_text) { resetStyles(); @@ -32,35 +32,22 @@ }).blur(function() { setDefault(); }); - + // on submit, clear out the hint before submit. this.parents('form:first').submit(function() { if ($(field).val() == default_text) { clearField(); } }); - + return this; }; - - $('.picker-content .textarea-navigation .tab').click(function(e) { - let tabId = e.target.getAttribute('data-tab'); - let tab = document.getElementById(tabId); - if (tab && !$(tab).hasClass('field-textarea--selected')) { - $('.picker-content .textarea-navigation li').removeClass('selected'); - $(e.target).closest('li').addClass('selected'); - $('#description').removeClass('field-textarea--selected'); - $('#alt-text').removeClass('field-textarea--selected'); - $(tab).addClass('field-textarea--selected'); - } - }); - - + })(jQuery); $(document).ready(function() { - + $('#description-field').hint('Write a description if you\'d like!'); }); diff --git a/templates/tools/picker.html b/templates/tools/picker.html index 31d4ac0e..afacc1e8 100644 --- a/templates/tools/picker.html +++ b/templates/tools/picker.html @@ -25,14 +25,7 @@

In order to post, you have to confirm your email address first. Check your e

-
-
    -
  • Description
  • -
  • Alt Text
  • -
-
- -
+
@@ -47,21 +40,6 @@

In order to post, you have to confirm your email address first. Check your e

-
-
-
- - {% if errors.alt_text %} -
- - {{errors.alt_text }} - -
- {% end %} -
-
-
- {% if len(shakes) > 1 %}
From b9a84866e1eebefa637fed5d11e322a3c478a51e Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 23 Jul 2023 17:42:39 -0500 Subject: [PATCH 09/19] Additional upgrades * Upgrades boto to boto3 * Some improvements to tests for reusing the database connection * Disabling tornado logging by default * Relocated fakes3 Dockerfile into project --- Makefile | 21 +++++---- README.md | 32 ++++++++----- docker-compose.yml | 12 +++-- handlers/base.py | 4 -- handlers/tools.py | 13 +++++- lib/flyingcow/db.py | 2 +- lib/flyingcow/model.py | 18 ++++---- lib/s3.py | 87 ++++++++++++++++++++++++++++------- main.py | 21 ++++++--- mltshpoptions.py | 1 + models/shake.py | 29 ++++++------ models/sourcefile.py | 37 +++++++++------ models/user.py | 15 +++--- requirements.txt | 2 +- scripts/make-zip-of-images.py | 18 ++++---- settings.example.py | 5 +- setup/dev/fakes3/Dockerfile | 23 +++++++++ setup/dev/nginx.conf | 10 ++-- setup/production/nginx.conf | 19 ++------ tasks/transcode.py | 16 +++---- test.py | 8 ++++ test/FileTests.py | 24 +++++----- test/base.py | 38 +++++++++------ test/unit/base.py | 36 ++++++++++----- 24 files changed, 315 insertions(+), 176 deletions(-) create mode 100644 setup/dev/fakes3/Dockerfile diff --git a/Makefile b/Makefile index 772c4c0b..ae8887e5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: init-dev run shell test destroy migrate mysql +.PHONY: init-dev run stop custom-build build shell test destroy migrate mysql init-dev: cp settings.example.py settings.py @@ -6,26 +6,29 @@ init-dev: mkdir -p mounts/mysql mounts/logs mounts/fakes3 mounts/uploaded run: - docker compose --env-file .env up -d + docker compose up -d stop: - docker compose --env-file .env down + docker compose down + +custom-build: + @read -p "build tag (default is 'latest'): " build_tag; \ + docker build -t mltshp/mltshp-web:$${build_tag:-latest} build: docker build -t mltshp/mltshp-web:latest . shell: - docker compose --env-file .env exec mltshp bash + docker compose exec mltshp bash test: - docker compose --env-file .env exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; python3 test.py $(TEST)" + docker compose exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; python3 test.py $(TEST)" destroy: - docker compose down - rm -rf mounts + docker compose down && rm -rf mounts migrate: - docker compose --env-file .env exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; python3 migrate.py" + docker compose exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; python3 migrate.py" mysql: - docker compose --env-file .env exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; mysql -u root --host mysql mltshp" + docker compose exec mltshp su ubuntu -c "cd /srv/mltshp.com/mltshp; mysql -u root --host mysql mltshp" diff --git a/README.md b/README.md index b0545e28..be66ffa0 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,11 @@ should be okay): $ make init-dev +Check the process limits for your computer using the `ulimit -a` command. Increase +the file descriptor limit if it's not at least 1000, using `ulimit -n 1000` (or +some suitably higher value). You can add this to your shell startup script to +make it permanent. + You should be able to start the app itself using: $ make run @@ -92,8 +97,7 @@ if you have a MySQL database you use locally for development and testing and keep it versus using the `destroy` and `init-dev` commands to make a new one. To update your database, just do this: - $ make shell - docker-shell$ cd /srv/mltshp.com/mltshp; python migrate.py + $ make migrate That should do it. @@ -110,21 +114,17 @@ of the project: FAKES3_LICENSE_KEY=your-license-key-here -Some of the unit tests actually test against Twitter itself, so you'll -want to generate a custom Twitter app with your own set of keys. -Configure the `test_settings` in your `settings.py` file appropriately: - - "twitter_consumer_key" : "twitter_consumer_key_here", - "twitter_consumer_secret" : "twitter_consumer_secret_key_here", - "twitter_access_key" : "twitter_access_key_here", - "twitter_access_secret" : "twitter_access_secret_here", - Then, just run: $ make test Which will invoke a Docker process to run the unit test suite. +You can also run a specific unit test by setting a TEST environment +variable (you can find the unit test names in `test.py`): + + $ TEST=test.unit.shake_tests make test + ## Connecting to the MLTSHP shell If you ever need to access the Docker image running the application, @@ -137,6 +137,14 @@ This should place you in the /srv/mltshp.com/mltshp directory as the root user. You can use `apt-get` commands to install utilities you may need. +## Connecting to the MLTSHP MySQL database + +You can also access the MySQL shell using: + + $ make mysql + +Useful for inspecting the database for your local directly. + ## Cleanup If you ever want to _wipe your local data_ and rebuild your Docker @@ -147,7 +155,7 @@ containers, just use this command: If you just wish to rebuild the Docker container, use the Docker compose command: - $ docker compose down + $ make stop Then, run another `make run`. diff --git a/docker-compose.yml b/docker-compose.yml index ef51fdce..bdf409bf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,8 @@ -version: "2" services: mltshp: + build: . + env_file: + - .env image: mltshp/mltshp-web volumes: - .:/srv/mltshp.com/mltshp @@ -21,8 +23,12 @@ services: aliases: - mltshp.localhost fakes3: - image: ourtownrentals/fake-s3 - entrypoint: fakes3 -r /srv --license ${FAKES3_LICENSE_KEY} -p 8000 + build: ./setup/dev/fakes3 + env_file: + - .env + entrypoint: fakes3 --root /srv --license ${FAKES3_LICENSE_KEY} --port 4567 + ports: + - "4567:4567" volumes: - ./mounts/fakes3:/srv networks: diff --git a/handlers/base.py b/handlers/base.py index a8ec0181..b87cc74d 100644 --- a/handlers/base.py +++ b/handlers/base.py @@ -5,7 +5,6 @@ from tornado.options import define, options from lib.flyingcow.cache import RequestHandlerQueryCache -from lib.s3 import S3Connection import models SESSION_COOKIE = "sid" @@ -60,9 +59,6 @@ def finish(self, chunk=None): self.set_header('x-proc-time',"%s" % (proc_time)) super(BaseHandler, self).finish(chunk) - def get_s3_connection(self): - return S3Connection() - def get_current_user(self): sid = self.get_secure_cookie(SESSION_COOKIE) if sid: diff --git a/handlers/tools.py b/handlers/tools.py index 2d9616e2..3e4ad157 100644 --- a/handlers/tools.py +++ b/handlers/tools.py @@ -13,6 +13,9 @@ from .base import BaseHandler, require_membership from lib.utilities import base36encode +import logging +logger = logging.getLogger("mltshp") + class PickerPopupHandler(BaseHandler): @tornado.web.authenticated @@ -91,12 +94,15 @@ async def post(self): http = tornado.httpclient.AsyncHTTPClient() + logger.debug("Fetching %s" % (self.url)) request = HTTPRequest(self.url, header_callback=self.on_header) fut = http.fetch(request) response = await fut + logger.debug("Got response for %s" % (self.url)) self.on_response(response) def on_response(self, response): + logger.debug("Parsing response for %s" % (self.url)) url_parts = urlparse(response.request.url) file_name = os.path.basename(url_parts.path) title = self.get_argument("title", None) @@ -119,12 +125,15 @@ def on_response(self, response): sha1_file_key = Sourcefile.get_sha1_file_key(file_data=response.body) user = self.get_current_user() try: + logger.debug("Writing file %s/%s" % (options.uploaded_files, sha1_file_key)) fh = open("%s/%s" % (options.uploaded_files, sha1_file_key), 'wb') fh.write(response.body) fh.close() except Exception as e: + logger.error("Error saving file %s/%s" % (options.uploaded_files, sha1_file_key)) raise tornado.web.HTTPError(500) + logger.debug("Creating sharedfile") sf = Sharedfile.create_from_file( file_path = "%s/%s" % (options.uploaded_files, sha1_file_key), file_name = file_name, @@ -135,6 +144,7 @@ def on_response(self, response): shake_id = shake_id) sf.source_url = source_url sf.description = description + logger.debug("Saving to database") sf.save() if not options.debug: # file cleanup @@ -142,12 +152,13 @@ def on_response(self, response): os.remove("%s/%s" % (options.uploaded_files, sha1_file_key)) except: pass + logger.debug("Rendering picker-success.html") self.render("tools/picker-success.html", sf=sf) def on_header(self, header): if header.lower().startswith("content-length:"): content_length = re.search("content-length: (.*)", header, re.IGNORECASE) - if int(content_length.group(1).rstrip()) > 10000000: #this is not hte correct size to error on + if int(content_length.group(1).rstrip()) > 10000000: #this is not the correct size to error on raise tornado.web.HTTPError(413) elif header.lower().startswith("content-type:"): ct = re.search("content-type: (.*)", header, re.IGNORECASE) diff --git a/lib/flyingcow/db.py b/lib/flyingcow/db.py index d70f18f8..3767c30a 100644 --- a/lib/flyingcow/db.py +++ b/lib/flyingcow/db.py @@ -15,7 +15,7 @@ def __init__(self): self._connection = None def register(self, host='localhost', name=None, user=None, password=None, charset="utf8"): - self._connection = torndb.Connection(host, name, user, password, charset=charset) + self._connection = self._connection or torndb.Connection(host, name, user, password, charset=charset) return self._connection def connection(self): diff --git a/lib/flyingcow/model.py b/lib/flyingcow/model.py index c8ac2966..67df2347 100644 --- a/lib/flyingcow/model.py +++ b/lib/flyingcow/model.py @@ -19,7 +19,7 @@ class BaseModel(type): def __new__(cls, name, bases, attrs): if name == 'Model': return super(BaseModel, cls).__new__(cls, name, bases, attrs) - + new_class = type.__new__(cls, name, bases, attrs) for key, value in list(attrs.items()): if isinstance(value, properties.Property): @@ -31,7 +31,7 @@ class Model(object, metaclass=BaseModel): """ The Model class that gets inherited to create table-specific Models. """ - + def __init__(self, *args, **kwargs): self._id = None self.connection = db.connection() @@ -50,7 +50,7 @@ def id(self): that an instance is tied to a DB record. """ return self._id - + def saved(self): """ A way to check if object has been saved. @@ -64,19 +64,19 @@ def initialize(self, *args, **kwargs): """ A hook for subclasses to override model initialization. """ - + def add_error(self, property_name, error_message): """ Used to add any errors while validating an object. """ self.errors[property_name] = error_message - + def on_create(self): pass - + def on_update(self): pass - + def save(self): """ Builds the query to save all of the object's db attributes. @@ -114,7 +114,7 @@ def update_attribute(self, name, value): sql = "UPDATE %s SET %s = %s where id = %s" % (self._table_name(), name, "%s", "%s") self.execute(sql, value, self.id) return True - + def _populate_properties(self, include_id, **kwargs): """ Populates the properties with values, if include_id is passed in, @@ -180,7 +180,7 @@ def object_query(cls, query, *args): for result in cls.query(query, *args): results.append(cls._make_instance(result)) return results - + @classmethod def query(cls, query, *args): """ diff --git a/lib/s3.py b/lib/s3.py index 3acc9f10..f66760a0 100644 --- a/lib/s3.py +++ b/lib/s3.py @@ -1,26 +1,81 @@ -from boto.s3.connection import S3Connection as Connection +import io +import boto3 from tornado.options import options +from hashlib import md5 -def S3Connection(): + +def S3Client(): kwargs = {} if options.aws_port and options.aws_host: - kwargs['host'] = options.aws_host - kwargs['port'] = options.aws_port - # if we're using a custom AWS host/port, disable - # SSL, since fakes3 doesn't support it - kwargs['is_secure'] = False - - return Connection( - options.aws_key, - options.aws_secret, + kwargs['endpoint_url'] = "http://%s:%s" % (options.aws_host, options.aws_port) + kwargs['use_ssl'] = False + + return boto3.client('s3', + aws_access_key_id=options.aws_key, + aws_secret_access_key=options.aws_secret, **kwargs) +class S3BucketWrapper(object): + def __init__(self, bucket_name, create=False): + self.bucket_name = bucket_name + self.client = S3Client() + if create: + self.client.create_bucket(Bucket=options.aws_bucket) + + def generate_url(self, key, **kwargs): + return self.client.generate_presigned_url( + ClientMethod="get_object", + Params={ + 'Bucket': self.bucket_name, + 'Key': key, + }, + **kwargs) + + def upload_file(self, file_name, key, **kwargs): + return self.client.upload_file( + Filename=file_name, + Key=key, + Bucket=self.bucket_name, + **kwargs + ) + + def put_object(self, data, key, **kwargs): + if 'ContentMD5' not in kwargs: + md5_hash = md5() + md5_hash.update(data) + kwargs['ContentMD5'] = md5_hash.hexdigest() + if 'ContentLength' not in kwargs: + if hasattr(data, 'read'): + kwargs['ContentLength'] = data.seek(0, 2) + data.seek(0) + else: + kwargs['ContentLength'] = len(data) + return self.client.put_object( + Body=data, + Key=key, + Bucket=self.bucket_name, + **kwargs, + ) + + def get_object(self, key, **kwargs): + data = io.BytesIO() + self.client.download_fileobj( + Key=key, Fileobj=data, Bucket=self.bucket_name, **kwargs + ) + return data.getvalue() + + def download_file(self, file_name, key, **kwargs): + with open(file_name, "wb") as f: + self.client.download_fileobj( + Key=key, Fileobj=f, Bucket=self.bucket_name, **kwargs + ) + + def S3Bucket(): - # if we're testing, then just auto-create a bucket if it doesn't exist already - if options.aws_bucket.endswith("-dev") or options.aws_bucket.endswith("-testing"): - return S3Connection().create_bucket(options.aws_bucket) - else: - return S3Connection().get_bucket(options.aws_bucket) \ No newline at end of file + return S3BucketWrapper( + options.aws_bucket, + # if we're testing, then just auto-create a bucket if it doesn't exist already + create=options.aws_bucket.endswith("-dev") or options.aws_bucket.endswith("-testing")) diff --git a/main.py b/main.py index 26662f0c..ebe6e5cf 100755 --- a/main.py +++ b/main.py @@ -17,6 +17,10 @@ from settings import settings import stripe +import logging + +logger = logging.getLogger('mltshp') + AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient") @@ -35,16 +39,17 @@ def app_settings(cls): # invariant settings "login_url": "/sign-in", "static_path": os.path.join(dirname, "static"), - "template_path": os.path.join(dirname, "templates"), + "template_path": os.path.join(dirname, "templates"), "ui_modules": lib.uimodules, } def __init__(self, *args, **settings): - self.db = register_connection(host=options.database_host, - name=options.database_name, - user=options.database_user, - password=options.database_password, - charset="utf8mb4") + self.db = settings.get("db") or register_connection( + host=options.database_host, + name=options.database_name, + user=options.database_user, + password=options.database_password, + charset="utf8mb4") if options.use_query_cache: lib.flyingcow.cache.use_query_cache = True if options.stripe_secret_key: @@ -67,7 +72,9 @@ async def main(): sys.exit(0) application = MltshpApplication(routes, autoescape=None, **app_settings) - print("starting on port %s" % (options.on_port)) + if options.debug: + logger.setLevel(logging.DEBUG) + logger.info("starting on port %s" % (options.on_port)) application.listen(int(options.on_port)) await asyncio.Event().wait() diff --git a/mltshpoptions.py b/mltshpoptions.py index 349cbbe2..86cd8a5b 100644 --- a/mltshpoptions.py +++ b/mltshpoptions.py @@ -9,6 +9,7 @@ def parse_dictionary(settings): define('debug', type=bool, default=True, help="Run in debug/development mode") define('dump_settings', type=bool, default=False, help="Dump evaluated settings and exit") +define('tornado_logging', type=bool, default=True, help="Controls Tornado logging") # app settings define('app_host', default='mltshp.com', metavar="HOST", help="Base hostname for web site") diff --git a/models/shake.py b/models/shake.py index 5328f842..4ee689b3 100644 --- a/models/shake.py +++ b/models/shake.py @@ -5,7 +5,6 @@ from tornado.options import options from lib.s3 import S3Bucket -from boto.s3.key import Key from PIL import Image from lib.flyingcow import Model, Property @@ -334,22 +333,22 @@ def set_page_image(self, file_path=None, sha1_value=None): try: #save thumbnail - k = Key(bucket) - k.key = "account/%s/shake_%s_small.jpg" % (self.user_id, self.name) - k.set_metadata('Content-Type', 'image/jpeg') - k.set_metadata('Cache-Control', 'max-age=86400') - k.set_contents_from_string(thumb_cstr.getvalue()) - k.set_acl('public-read') - k.close(fast=True) + bucket.put_object( + thumb_cstr.getvalue(), + "account/%s/shake_%s_small.jpg" % (self.user_id, self.name), + ContentType="image/jpeg", + CacheControl="max-age=86400", + ACL="public-read", + ) #save small - k = Key(bucket) - k.key = "account/%s/shake_%s.jpg" % (self.user_id, self.name) - k.set_metadata('Content-Type', 'image/jpeg') - k.set_metadata('Cache-Control', 'max-age=86400') - k.set_contents_from_string(image_cstr.getvalue()) - k.set_acl('public-read') - k.close(fast=True) + bucket.put_object( + image_cstr.getvalue(), + "account/%s/shake_%s.jpg" % (self.user_id, self.name), + ContentType="image/jpeg", + CacheControl="max-age=86400", + ACL="public-read", + ) self.image = 1 self.save() diff --git a/models/sourcefile.py b/models/sourcefile.py index 2d0ef1b8..85e5b211 100644 --- a/models/sourcefile.py +++ b/models/sourcefile.py @@ -9,11 +9,13 @@ from tornado.options import options from PIL import Image from lib.s3 import S3Bucket -from boto.s3.key import Key from lib.flyingcow import Model, Property from lib.flyingcow.cache import ModelQueryCache +import logging +logger = logging.getLogger('mltshp') + class Sourcefile(ModelQueryCache, Model): width = Property() # original width dimension of source file @@ -118,6 +120,7 @@ def get_from_file(file_path, sha1_value, type='image', skip_s3=None): if existing_source_file: return existing_source_file try: + logger.debug("creating %s" % file_path) img = Image.open(file_path) original_width = img.size[0] original_height= img.size[1] @@ -126,6 +129,7 @@ def get_from_file(file_path, sha1_value, type='image', skip_s3=None): return None if img.mode != "RGB": + logger.debug("converting to RGB") img2 = img.convert("RGB") img.close() img = img2 @@ -142,36 +146,43 @@ def get_from_file(file_path, sha1_value, type='image', skip_s3=None): bucket = None if not skip_s3: + logger.debug("making S3 bucket") bucket = S3Bucket() #save original file if type != 'link': if not skip_s3: - k = Key(bucket) - k.key = "originals/%s" % sha1_value - k.set_contents_from_filename(file_path) - k.close(fast=True) + logger.debug("putting object originals/%s" % sha1_value) + bucket.upload_file( + file_path, + "originals/%s" % sha1_value, + ) img.close() #save thumbnail thumbnail_file_key = Sourcefile.get_sha1_file_key(file_data=thumb_cstr.getvalue()) if not skip_s3: - k = Key(bucket) - k.key = "thumbnails/%s" % thumbnail_file_key - k.set_contents_from_string(thumb_cstr.getvalue()) - k.close(fast=True) + thumb_bytes = thumb_cstr.getvalue() + logger.debug("putting object thumbnails/%s (length %d)" % (thumbnail_file_key, len(thumb_bytes))) + bucket.put_object( + thumb_bytes, + "thumbnails/%s" % thumbnail_file_key, + ) thumb.close() #save small small_file_key = Sourcefile.get_sha1_file_key(file_data=small_cstr.getvalue()) if not skip_s3: - k = Key(bucket) - k.key = "smalls/%s" % small_file_key - k.set_contents_from_string(small_cstr.getvalue()) - k.close(fast=True) + small_bytes = small_cstr.getvalue() + logger.debug("putting object smalls/%s (length %d)" % (small_file_key, len(small_bytes))) + bucket.put_object( + small_bytes, + "smalls/%s" % small_file_key, + ) small.close() #save source file + logger.debug("saving sourcefile") sf = Sourcefile(width=original_width, height=original_height, file_key=sha1_value, thumb_key=thumbnail_file_key, small_key=small_file_key, type=type) sf.save() return sf diff --git a/models/user.py b/models/user.py index 40fb5a17..4d3bdaa0 100644 --- a/models/user.py +++ b/models/user.py @@ -8,7 +8,6 @@ from datetime import datetime from lib.s3 import S3Bucket -from boto.s3.key import Key import postmark from tornado.options import define, options @@ -238,13 +237,13 @@ def set_profile_image(self, file_path, file_name, content_type): return False bucket = S3Bucket() - k = Key(bucket) - k.key = "account/%s/profile.jpg" % self.id - k.set_metadata('Content-Type', 'image/jpeg') - k.set_metadata('Cache-Control', 'max-age=86400') - k.set_contents_from_string(destination.getvalue()) - k.set_acl('public-read') - k.close(fast=True) + bucket.put_object( + destination.getvalue(), + "account/%s/profile.jpg" % self.id, + ContentType="image/jpeg", + CacheControl="max-age=86400", + ACL="public-read" + ) self.profile_image = 1 self.save() return True diff --git a/requirements.txt b/requirements.txt index ceeae5f0..edcb4f1d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ beautifulsoup4==4.12.2 mysqlclient==2.1.1 Pillow==10.0.0 amqplib==1.0.2 -boto==2.49.0 +boto3==1.28.9 celery==5.3.1 kombu==5.3.1 pycurl==7.45.2 diff --git a/scripts/make-zip-of-images.py b/scripts/make-zip-of-images.py index aeef207b..b929d739 100644 --- a/scripts/make-zip-of-images.py +++ b/scripts/make-zip-of-images.py @@ -7,7 +7,6 @@ import models import sys from lib.s3 import S3Bucket -from boto.s3.key import Key from tornado.options import options import json import os @@ -27,7 +26,7 @@ def main(): } return json.dumps(results) -def percent_cb(complete, total): +def percent_cb(bytes_amount): sys.stdout.write('.') sys.stdout.flush() @@ -61,7 +60,6 @@ def make_zip_file(for_user=None): else: sys.stdout.write('.') sys.stdout.flush() - file_object = s3_bucket.get_key("originals/{0}".format(source.file_key)) extension = "" if sf.content_type == 'image/gif': extension = "gif" @@ -75,18 +73,18 @@ def make_zip_file(for_user=None): print("extension blank") sys.exit() - file_object.get_contents_to_filename("/mnt/backups/users/{0}/{1}.{2}".format(user.name, sf.share_key, extension)) + file_name = "/mnt/backups/users/{0}/{1}.{2}".format(user.name, sf.share_key, extension) + s3_bucket.download_file(file_name, "originals/{0}".format(source.file_key)) #zip contents of directory and save to /users/id-name.zip - subprocess.call(["zip", "-r", "/mnt/backups/users/{0}.zip".format(user.name), "/mnt/backups/users/{0}/".format(user.name)]) + zip_file = "/mnt/backups/users/{0}.zip".format(user.name) + subprocess.call(["zip", "-r", zip_file, "/mnt/backups/users/{0}/".format(user.name)]) #upload to s3 as /bucket-name/account/id/images.zip - k = Key(s3_bucket) - k.key = "account/{0}/images.zip".format(user.id) - k.set_contents_from_filename("/mnt/backups/users/{0}.zip".format(user.name), cb=percent_cb, num_cb=10) + key = "account/{0}/images.zip".format(user.id) + s3_bucket.upload_file(zip_file, key, Callback=percent_cb, ExtraArgs={"ContentType": "application/zip"}) - happy_url = k.generate_url(expires_in=72000) - k.close(fast=True) + happy_url = s3_bucket.generate_url(key, ExpiresIn=72000) #email link to user email 8 hours pm = postmark.PMMail(api_key=options.postmark_api_key, sender="hello@mltshp.com", to=user.email, diff --git a/settings.example.py b/settings.example.py index 244ff833..a63b2b5e 100644 --- a/settings.example.py +++ b/settings.example.py @@ -7,7 +7,7 @@ "auth_secret" : "dummy-secret", "aws_bucket": "mltshp-dev", "aws_host": "fakes3", - "aws_port": 8000, + "aws_port": 4567, "aws_key": "dummy-key", "aws_secret": "dummy-secret", "cookie_secret": "some secret string", @@ -41,7 +41,7 @@ "database_host": "mysql", "aws_bucket": "mltshp-testing", "aws_host": "fakes3", - "aws_port": 8000, + "aws_port": 4567, "aws_key": "dummy-key", "aws_secret": "dummy-secret", "max_mb_per_month" : 300, @@ -49,6 +49,7 @@ "use_workers": False, "debug_workers": True, "superuser_list": "admin", + "tornado_logging": False, # these must be set for testing test/unit/externalservice_tests.py # "twitter_consumer_key" : "twitter_consumer_key_here", # "twitter_consumer_secret" : "twitter_consumer_secret_key_here", diff --git a/setup/dev/fakes3/Dockerfile b/setup/dev/fakes3/Dockerfile new file mode 100644 index 00000000..de8f6717 --- /dev/null +++ b/setup/dev/fakes3/Dockerfile @@ -0,0 +1,23 @@ +FROM ruby:2.7 as builder + +ENV FAKES3_VERSION 2.0.0 + +RUN gem install fakes3 -v ${FAKES3_VERSION} \ + && rm -rf /usr/local/bundle/cache/* + +FROM ruby:2.7-slim + +COPY --from=builder /usr/local/bundle /usr/local/bundle/ + +VOLUME /srv +RUN mkdir -p /srv \ + && chown nobody:nogroup /srv \ + && chmod 750 /srv \ + && ln -s /usr/local/bundle/bin/fakes3 /usr/bin/fakes3 +WORKDIR /srv + +EXPOSE 4567 + +USER nobody +ENTRYPOINT ["fakes3", "--port", "4567"] +CMD ["--root", "/srv"] diff --git a/setup/dev/nginx.conf b/setup/dev/nginx.conf index edacdfc8..78aac568 100644 --- a/setup/dev/nginx.conf +++ b/setup/dev/nginx.conf @@ -1,5 +1,7 @@ # MLTSHP.com dev config +worker_processes auto; + worker_rlimit_nofile 20480; error_log /srv/mltshp.com/logs/error.log; @@ -30,7 +32,7 @@ http { # Only retry if there was a communication error, not a timeout # on the Tornado server (to avoid propagating "queries of death" # to all frontends) - proxy_next_upstream error; + proxy_next_upstream error timeout; # This will load a dynamically generated "resolver n.n.n.n;" line # that will have the correct resolver IP address for the dev @@ -86,6 +88,7 @@ http { upload_pass_form_field "_xsrf"; upload_pass_form_field "title"; + upload_pass_form_field "alt_text"; upload_pass_form_field "description"; upload_cleanup 400 404 499 500-505; @@ -124,6 +127,7 @@ http { upload_aggregate_form_field "${upload_field_name}_size" "$upload_file_size"; upload_pass_form_field "title"; + upload_pass_form_field "alt_text"; upload_pass_form_field "description"; upload_pass_form_field "shake_id"; @@ -131,7 +135,7 @@ http { } location ~* ^/s3/((?:account)/\d+/[a-zA-Z0-9_-]+\.jpg) { - set $download_url http://mltshp-dev.fakes3:8000/$1; + set $download_url http://mltshp-dev.fakes3:4567/$1; proxy_hide_header Content-Disposition; proxy_hide_header Content-Type; proxy_set_header X-Rewrite-URL $download_url; @@ -144,7 +148,7 @@ http { internal; set $download_uri $1; - set $download_url http://mltshp-dev.fakes3:8000/$download_uri$is_args$args; + set $download_url http://fakes3:4567/mltshp-dev/$download_uri$is_args$args; proxy_hide_header Content-Disposition; proxy_hide_header Content-Type; proxy_set_header X-Rewrite-URL $download_url; diff --git a/setup/production/nginx.conf b/setup/production/nginx.conf index 6b1f7f2f..531fd54b 100644 --- a/setup/production/nginx.conf +++ b/setup/production/nginx.conf @@ -1,7 +1,6 @@ # MLTSHP.com production config -# FIXME: Not supported on ancient nginx -#worker_processes auto; +worker_processes auto; worker_rlimit_nofile 20480; @@ -14,12 +13,6 @@ http { include mime.types; default_type application/octet-stream; - # Accepts the origin IP address as sent - # by our Linode NodeBalancer - # FIXME: Not supported on ancient nginx - #real_ip_header X-Forwarded-For; - #set_real_ip_from 192.168.255.0/24; - upstream frontends { server 127.0.0.1:8001; server 127.0.0.1:8002; @@ -39,14 +32,10 @@ http { application/x-javascript application/xml application/atom+xml text/javascript; - # FIXME: Not supported on ancient nginx - #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE - #ssl_prefer_server_ciphers on; - # Only retry if there was a communication error, not a timeout # on the Tornado server (to avoid propagating "queries of death" # to all frontends) - proxy_next_upstream error; + proxy_next_upstream error timeout; resolver 8.8.8.8; @@ -85,7 +74,7 @@ http { location = /upload { # Pass altered request body to this location - upload_pass /internalupload; + upload_pass /internalupload; # Store files to this directory # The directory is hashed, subdirectories 0 1 2 3 4 5 6 7 8 9 should exist @@ -122,6 +111,7 @@ http { upload_pass_form_field "_xsrf"; upload_pass_form_field "title"; + upload_pass_form_field "alt_text"; upload_pass_form_field "description"; upload_cleanup 400 404 499 500-505; } @@ -155,6 +145,7 @@ http { upload_aggregate_form_field "${upload_field_name}_sha1" "$upload_file_sha1"; upload_aggregate_form_field "${upload_field_name}_size" "$upload_file_size"; upload_pass_form_field "title"; + upload_pass_form_field "alt_text"; upload_pass_form_field "description"; upload_pass_form_field "shake_id"; diff --git a/tasks/transcode.py b/tasks/transcode.py index e44d68c5..2b4d457f 100644 --- a/tasks/transcode.py +++ b/tasks/transcode.py @@ -11,7 +11,6 @@ from ffmpy import FFmpeg from PIL import Image from lib.s3 import S3Bucket -from boto.s3.key import Key logger = get_task_logger(__name__) @@ -69,12 +68,11 @@ def gif_to_video(sourcefile_id, file_key, input_file, format): # upload transcoded file to S3, then flag the sourcefile bucket = S3Bucket() - key = Key(bucket) - key.key = "%s/%s" % (format, file_key) - logger.info("uploading transcoded video: %s" % file_key) - key.set_contents_from_filename(output_file) - key.close(fast=True) + bucket.upload_file( + output_file, + "%s/%s" % (format, file_key), + ) logger.info("-- upload complete") db = db_connect() db.execute( @@ -122,11 +120,9 @@ def transcode_sharedfile(sharedfile_id): input_file = input_temp.name bucket = S3Bucket() - key = Key(bucket) - key.key = "originals/%s" % sourcefile["file_key"] + key = "originals/%s" % sourcefile["file_key"] logger.info("Downloading original GIF from S3 for sourcefile %s..." % sharedfile["source_id"]) - key.get_contents_to_filename(input_file) - key.close(fast=True) + bucket.download_file(input_file, key) # Test to see if GIF is animated or not animated = False diff --git a/test.py b/test.py index 8f480037..117df17d 100755 --- a/test.py +++ b/test.py @@ -1,9 +1,12 @@ #!/usr/bin/env python import unittest +import logging + from torndb import Connection from tornado.options import options from tornado.httpclient import AsyncHTTPClient +from tornado.options import options import MySQLdb @@ -70,6 +73,11 @@ def all(): if __name__ == '__main__': mltshpoptions.parse_dictionary(test_settings) + if not options.tornado_logging: + options.logging = None + logging.getLogger("tornado.access").disabled = True + logging.getLogger("tornado.application").disabled = True + logging.getLogger("tornado.general").disabled = True import tornado.testing db = Connection(options.database_host, 'mysql', options.database_user, options.database_password) diff --git a/test/FileTests.py b/test/FileTests.py index 54d8ab54..c46dc906 100644 --- a/test/FileTests.py +++ b/test/FileTests.py @@ -40,7 +40,7 @@ def setUp(self): self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) def test_deleting_file_sets_to_true(self): - response = self.fetch("/p/1/delete", method='POST', headers={'Cookie': "_xsrf=%s;sid=%s" % (self.xsrf, self.sid)}, body="_xsrf=%s" % self.xsrf) + response = self.post_url("/p/1/delete") sf = Sharedfile.get("id=1") self.assertEqual(sf.deleted, 1) @@ -64,7 +64,7 @@ def test_delete_button_only_works_for_owner(self): bill.save() sid = self.sign_in("bill", "asdfasdf") - response = self.fetch("/p/1/delete", method='POST', headers={'Cookie': "_xsrf=%s;sid=%s" % (self.xsrf, sid)}, body="_xsrf=%s" % self.xsrf) + response = self.post_url("/p/1/delete") sf = Sharedfile.get("id=1") self.assertEqual(sf.deleted, 0) @@ -226,25 +226,25 @@ def test_title_pulls_from_name_if_blank_or_null(self): def test_quick_edit_title(self): self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - response = self.fetch('/p/1/quick-edit-title', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="_xsrf=%s&title=%s" % (self.xsrf, url_escape("Monkey Business"))) + response = self.post_url('/p/1/quick-edit-title', arguments={"title": "Monkey Business"}) j = json_decode(response.body) self.assertEqual(j['title'], 'Monkey Business') def test_quick_edit_description(self): self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - response = self.fetch('/p/1/quick-edit-description', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="_xsrf=%s&description=%s" % (self.xsrf, url_escape('Bilbo\nbaggins'))) + response = self.post_url('/p/1/quick-edit-description', arguments={"description": "Bilbo\nbaggins"}) j = json_decode(response.body) self.assertEqual(j['description_raw'], 'Bilbo\nbaggins') def test_quick_edit_alt_text(self): self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - response = self.fetch('/p/1/quick-edit-alt-text', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="_xsrf=%s&alt_text=%s" % (self.xsrf, url_escape('A small person carrying a ring'))) + response = self.post_url('/p/1/quick-edit-alt-text', arguments={"alt_text": 'A small person carrying a ring'}) j = json_decode(response.body) self.assertEqual(j['alt_text_raw'], 'A small person carrying a ring') def test_quick_edit_source_url(self): self.upload_file(self.test_file1_path, self.test_file1_sha1, self.test_file1_content_type, 1, self.sid, self.xsrf) - response = self.fetch('/p/1/quick-edit-source-url', method='POST', headers={'Cookie':'_xsrf=%s;sid=%s' % (self.xsrf, self.sid)}, body="_xsrf=%s&source_url=%s" % (self.xsrf, url_escape('http://www.example.com/'))) + response = self.post_url('/p/1/quick-edit-source-url', arguments={"source_url": 'http://www.example.com/'}) j = json_decode(response.body) self.assertEqual(j['source_url'], 'http://www.example.com/') @@ -283,7 +283,7 @@ def test_adding_video_makes_it_show_up_in_friends_shake(self): user2.subscribe(self.user.shake()) url = 'https://vimeo.com/20379529' - response = self.fetch('/tools/save-video', method='POST', headers={"Cookie":"sid=%s;_xsrf=%s" % (self.sid, self.xsrf)}, body="url=%s&_xsrf=%s" % (url_escape(url), self.xsrf)) + response = self.post_url('/tools/save-video', arguments={"url": url}) sfs = Sharedfile.from_subscriptions(user2.id) self.assertTrue(len(sfs) > 0) self.assertEqual(sfs[0].name , url) @@ -300,7 +300,7 @@ def setUp(self): self.xsrf = self.get_xsrf().decode("ascii") self.url = 'http://notes.torrez.org/images/categories/television.png?x=1' - self.source_url = url_escape('http://notes.torrez.org/') + self.source_url = 'http://notes.torrez.org/' self.description = "This is a multi-\nline\ndescription" def test_picker_not_authed_displays_sign_in(self): @@ -312,7 +312,7 @@ def test_picker_get_displays_image_passed_in(self): self.assertIn('hidden" name="url" value="%s"' % self.url, response.body) def test_picker_authenticated_stores_image(self): - response = self.fetch('/tools/p', method='POST', headers={"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, body="_xsrf=%s&url=%s&title=boatmoatgoat" % (self.xsrf, self.url)) + response = self.post_url('/tools/p', arguments={"url": self.url, "title": "boatmoatgoat"}, raise_error=True) self.assertNotIn("ERROR", response.body) sf = Sharedfile.get("id=1") self.assertEqual(sf.name, "television.png") @@ -336,17 +336,17 @@ def test_picker_errors(self): self.assertTrue(response.error) def test_picker_stores_image_and_shakesharedfile(self): - response = self.fetch('/tools/p', method='POST', headers={"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, body="_xsrf=%s&url=%s&title=boatmoatgoat" % (self.xsrf, self.url)) + response = self.post_url('/tools/p', arguments={"url": self.url, "title": "boatmoatgoat"}) ssf = Shakesharedfile.get("sharedfile_id=1 and shake_id=%s", self.user_shake.id) self.assertTrue(ssf) def test_picker_stores_source_url(self): - response = self.fetch('/tools/p', method='POST', headers={"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, body="_xsrf=%s&url=%s&title=boatmoatgoat&source_url=%s" % (self.xsrf, self.url, self.source_url)) + response = self.post_url('/tools/p', arguments={"url": self.url, "title": "boatmoatgoat", "source_url": self.source_url}) sf = Sharedfile.get("id=1") self.assertEqual(sf.source_url, 'http://notes.torrez.org/') def test_picker_stores_description(self): - response = self.fetch('/tools/p', method='POST', headers={"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, body="_xsrf=%s&url=%s&title=boatmoatgoat&description=%s" % (self.xsrf, url_escape(self.url), url_escape(self.description))) + response = self.post_url('/tools/p', arguments={"url": self.url, "title": "boatmoatgoat", "description": self.description}) self.assertNotIn("ERROR", response.body) sf = Sharedfile.get("id=1") self.assertEqual(sf.description, self.description) diff --git a/test/base.py b/test/base.py index 0b3a236a..0500d9a8 100644 --- a/test/base.py +++ b/test/base.py @@ -1,8 +1,8 @@ from tornado.testing import AsyncHTTPTestCase, ExpectLog from tornado.options import options import http.cookies -from lib.flyingcow import db as _db from main import MltshpApplication +from lib.flyingcow import register_connection from tornado.escape import json_encode from routes import routes import urllib.request, urllib.parse, urllib.error @@ -13,30 +13,41 @@ import hashlib import binascii import uuid +import logging from models import User, Sourcefile +logger = logging.getLogger('mltshp.test') +logger.setLevel(logging.INFO) + class BaseAsyncTestCase(AsyncHTTPTestCase, ExpectLog): sid = '' + def __init__(self, *args, **kwargs): + self.db = register_connection( + host=options.database_host, + name=options.database_name, + user=options.database_user, + password=options.database_password, + charset="utf8mb4") + super(BaseAsyncTestCase, self).__init__(*args, **kwargs) + def get_app(self): app_settings = MltshpApplication.app_settings() - application = MltshpApplication(routes, autoescape=None, autoreload=False, **app_settings) - self.db = self.create_database() - return application + return MltshpApplication(routes, autoescape=None, autoreload=False, + db=self.db, **app_settings) def setUp(self): super(BaseAsyncTestCase, self).setUp() self.start_time = time.time() + if options.database_name != "mltshp_testing": + raise Exception("Invalid database name for unit tests") + self.create_database() def get_httpserver_options(self): return {'no_keep_alive':False} - def tearDown(self): - super(BaseAsyncTestCase, self).tearDown() - self.db.close() - def sign_in(self, name, password): """ Authenticates the user and sets an instance variable to the user's @@ -62,11 +73,11 @@ def get_xsrf(self): def create_database(self): # start_time = int(time.time()) - db = _db.connection() - db.execute("DROP database IF EXISTS %s" % (options.database_name)) - db.execute("CREATE database %s" % (options.database_name)) - db.execute("USE %s" % (options.database_name)) + # logger.info("Creating database from BaseAsyncTestCase...") + self.db.execute("DROP database IF EXISTS %s" % (options.database_name)) + self.db.execute("CREATE database %s" % (options.database_name)) + self.db.execute("USE %s" % (options.database_name)) f = open("setup/db-install.sql") load_query = f.read() f.close() @@ -74,10 +85,9 @@ def create_database(self): statements = load_query.split(";") for statement in statements: if statement.strip() != "": - db.execute(statement.strip()) + self.db.execute(statement.strip()) # end_time = int(time.time()) # print "Database reset took: %s" % (end_time - start_time) - return db def upload_file(self, file_path, sha1, content_type, user_id, sid, xsrf, shake_id=None): """ diff --git a/test/unit/base.py b/test/unit/base.py index 0fd76276..ed603c55 100644 --- a/test/unit/base.py +++ b/test/unit/base.py @@ -4,27 +4,39 @@ import string import random -class BaseTestCase(unittest.TestCase): +import logging + +logger = logging.getLogger('mltshp.test') +logger.setLevel(logging.INFO) + +class BaseTestCase(unittest.TestCase): + def __init__(self, *args, **kwargs): + super(BaseTestCase, self).__init__(*args, **kwargs) + self.db = register_connection( + host=options.database_host, + name=options.database_name, + user=options.database_user, + password=options.database_password, + charset="utf8mb4") + def setUp(self): super(BaseTestCase, self).setUp() - self.db = self.create_database('mltshp_testing') - - def tearDown(self): - super(BaseTestCase, self).tearDown() + if options.database_name != "mltshp_testing": + raise Exception("Invalid database name for unit tests") + self.create_database() - def create_database(self, name): - db = register_connection(options.database_host, name, options.database_user, options.database_password) - db.execute("DROP database IF EXISTS %s" % (name)) - db.execute("CREATE database %s" % (name)) - db.execute("USE %s" % (name)) + def create_database(self): + # logger.info("Creating database from BaseTestCase...") + self.db.execute("DROP database IF EXISTS %s" % (options.database_name)) + self.db.execute("CREATE database %s" % (options.database_name)) + self.db.execute("USE %s" % (options.database_name)) f = open("setup/db-install.sql") load_query = f.read() f.close() statements = load_query.split(";") for statement in statements: if statement.strip() != "": - db.execute(statement.strip()) - return db + self.db.execute(statement.strip()) def generate_string_of_len(self, length): return ''.join(random.choice(string.ascii_letters) for i in range(length)) From 4273ac11460ca0b71096361bc9f6634dbca0a29c Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 23 Jul 2023 17:57:23 -0500 Subject: [PATCH 10/19] Buildkite updates --- .buildkite/docker-compose.yml | 13 ++++++++----- .buildkite/settings.py | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.buildkite/docker-compose.yml b/.buildkite/docker-compose.yml index fcb7821b..5cb99dc6 100644 --- a/.buildkite/docker-compose.yml +++ b/.buildkite/docker-compose.yml @@ -1,7 +1,8 @@ -version: "2" services: mltshp: - image: mltshp/mltshp-web:latest + build: . + env_file: + - ../.env volumes: - ./settings.py:/srv/mltshp.com/mltshp/settings.py - ../.git:/srv/mltshp.com/mltshp/.git @@ -29,10 +30,12 @@ services: networks: - app_net fakes3: - image: ourtownrentals/fake-s3 - entrypoint: fakes3 -r /srv --license ${FAKES3_LICENSE_KEY} -p 8000 + build: ./setup/dev/fakes3 + env_file: + - ../.env + entrypoint: fakes3 -r /srv --license ${FAKES3_LICENSE_KEY} -p 4567 ports: - - "8000:8000" + - "4567:4567" networks: app_net: aliases: diff --git a/.buildkite/settings.py b/.buildkite/settings.py index cb5b9fd5..f0fe9f92 100644 --- a/.buildkite/settings.py +++ b/.buildkite/settings.py @@ -22,7 +22,7 @@ "database_host": "mysql", "aws_bucket": "mltshp-testing", "aws_host": "fakes3", - "aws_port": 8000, + "aws_port": 4567, "aws_key": "dummy-key", "aws_secret": "dummy-secret", "max_mb_per_month" : 300, From a0700cd22de4cc544133b0fe5dff222fa56cd417 Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 23 Jul 2023 18:00:32 -0500 Subject: [PATCH 11/19] Buildkite updates --- .buildkite/docker-compose.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.buildkite/docker-compose.yml b/.buildkite/docker-compose.yml index 5cb99dc6..53129cf9 100644 --- a/.buildkite/docker-compose.yml +++ b/.buildkite/docker-compose.yml @@ -1,8 +1,6 @@ services: mltshp: build: . - env_file: - - ../.env volumes: - ./settings.py:/srv/mltshp.com/mltshp/settings.py - ../.git:/srv/mltshp.com/mltshp/.git @@ -31,8 +29,6 @@ services: - app_net fakes3: build: ./setup/dev/fakes3 - env_file: - - ../.env entrypoint: fakes3 -r /srv --license ${FAKES3_LICENSE_KEY} -p 4567 ports: - "4567:4567" From 2ce06d4ca574ac1a69fba4e02e34b88589f8d74c Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 23 Jul 2023 18:01:21 -0500 Subject: [PATCH 12/19] Buildkite updates --- .buildkite/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/docker-compose.yml b/.buildkite/docker-compose.yml index 53129cf9..f9484f77 100644 --- a/.buildkite/docker-compose.yml +++ b/.buildkite/docker-compose.yml @@ -28,7 +28,7 @@ services: networks: - app_net fakes3: - build: ./setup/dev/fakes3 + build: ../setup/dev/fakes3 entrypoint: fakes3 -r /srv --license ${FAKES3_LICENSE_KEY} -p 4567 ports: - "4567:4567" From 4f7e10bc0909fd3e8e9d662c08d96b9cabbd098c Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 23 Jul 2023 18:03:16 -0500 Subject: [PATCH 13/19] Buildkite updates --- .buildkite/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/docker-compose.yml b/.buildkite/docker-compose.yml index f9484f77..bcd68deb 100644 --- a/.buildkite/docker-compose.yml +++ b/.buildkite/docker-compose.yml @@ -1,6 +1,6 @@ services: mltshp: - build: . + build: .. volumes: - ./settings.py:/srv/mltshp.com/mltshp/settings.py - ../.git:/srv/mltshp.com/mltshp/.git From 7680ca649a9b8cfb4cbad29902b0c3eca5043e09 Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 23 Jul 2023 18:04:14 -0500 Subject: [PATCH 14/19] Buildkite updates --- .buildkite/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/.buildkite/settings.py b/.buildkite/settings.py index f0fe9f92..a3181ae4 100644 --- a/.buildkite/settings.py +++ b/.buildkite/settings.py @@ -30,6 +30,7 @@ "use_workers": False, "debug_workers": True, "superuser_list": "admin", + "tornado_logging": False, # these must be set for testing test/unit/externalservice_tests.py # "twitter_consumer_key" : "twitter_consumer_key_here", # "twitter_consumer_secret" : "twitter_consumer_secret_key_here", From c964783f88802a7a913f286ab90aae79f874b655 Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 23 Jul 2023 19:51:31 -0500 Subject: [PATCH 15/19] Buildkite updates --- .buildkite/docker-compose.yml | 2 +- .buildkite/steps/test.sh | 2 +- torndb.py | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.buildkite/docker-compose.yml b/.buildkite/docker-compose.yml index bcd68deb..4a5e577f 100644 --- a/.buildkite/docker-compose.yml +++ b/.buildkite/docker-compose.yml @@ -29,7 +29,7 @@ services: - app_net fakes3: build: ../setup/dev/fakes3 - entrypoint: fakes3 -r /srv --license ${FAKES3_LICENSE_KEY} -p 4567 + entrypoint: fakes3 --root /srv --license ${FAKES3_LICENSE_KEY} --port 4567 ports: - "4567:4567" networks: diff --git a/.buildkite/steps/test.sh b/.buildkite/steps/test.sh index 266866ea..af3a2c15 100755 --- a/.buildkite/steps/test.sh +++ b/.buildkite/steps/test.sh @@ -11,7 +11,7 @@ docker pull mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} docker tag mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} mltshp/mltshp-web:latest # launch fakes3/mysql/web app -docker compose -f .buildkite/docker-compose.yml up -d +docker compose -f .buildkite/docker-compose.yml up -d --build # let's wait and allow mysql/fakes3 to spin up #wait_for localhost 3306 diff --git a/torndb.py b/torndb.py index c443cea8..ff3e4b89 100644 --- a/torndb.py +++ b/torndb.py @@ -23,9 +23,7 @@ """ -import copy import logging -import os import time import MySQLdb.constants From 79998d6dc81c2cb5c1adedc93ab24a6684c2bca6 Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Mon, 24 Jul 2023 21:48:28 -0500 Subject: [PATCH 16/19] Updates to Buildkite steps --- .buildkite/docker-compose.yml | 24 +++++++++++----------- .buildkite/steps/build-web.sh | 14 +++++++++---- .buildkite/steps/build-worker.sh | 14 +++++++++---- .buildkite/steps/deploy.sh | 27 ++++++++++++++++-------- .buildkite/steps/test.sh | 35 +++++++++++++++++--------------- 5 files changed, 69 insertions(+), 45 deletions(-) diff --git a/.buildkite/docker-compose.yml b/.buildkite/docker-compose.yml index 4a5e577f..fe6be6f7 100644 --- a/.buildkite/docker-compose.yml +++ b/.buildkite/docker-compose.yml @@ -8,25 +8,15 @@ services: ports: - "8001:80" depends_on: - - fakes3 - mysql - links: - fakes3 + links: - mysql + - fakes3 networks: app_net: aliases: - mltshp.dev - mysql: - image: mysql:latest - ports: - - "3306:3306" - environment: - MYSQL_ALLOW_EMPTY_PASSWORD: "yes" - MYSQL_DATABASE: "mltshp_testing" - MYSQL_USER: "mltshp" - networks: - - app_net fakes3: build: ../setup/dev/fakes3 entrypoint: fakes3 --root /srv --license ${FAKES3_LICENSE_KEY} --port 4567 @@ -37,6 +27,16 @@ services: aliases: - mltshp-testing.fakes3 - mltshp-dev.fakes3 + mysql: + image: mysql:latest + ports: + - "3306:3306" + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: "yes" + MYSQL_DATABASE: "mltshp_testing" + MYSQL_USER: "mltshp" + networks: + - app_net networks: app_net: diff --git a/.buildkite/steps/build-web.sh b/.buildkite/steps/build-web.sh index 6239a5df..90b77666 100755 --- a/.buildkite/steps/build-web.sh +++ b/.buildkite/steps/build-web.sh @@ -1,9 +1,15 @@ #!/bin/bash +# exit if any command fails (e); strict variable substitution (u); +# set exit code to non-zero for any failed piped commands (o pipefail) +# See also: http://redsymbol.net/articles/unofficial-bash-strict-mode/ set -euo pipefail -# pull prior image to populate layer cache -docker pull mltshp/mltshp-web:latest +echo "--- Pulling base Docker image" + docker pull mltshp/mltshp-web:latest -docker build -t mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} . -docker push mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} \ No newline at end of file +echo "+++ Building Docker image for web node" + docker build -t mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} . + +echo "--- Pushing build Docker image to Docker Hub" + docker push mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} diff --git a/.buildkite/steps/build-worker.sh b/.buildkite/steps/build-worker.sh index 8e477aca..6d987f47 100755 --- a/.buildkite/steps/build-worker.sh +++ b/.buildkite/steps/build-worker.sh @@ -1,9 +1,15 @@ #!/bin/bash +# exit if any command fails (e); strict variable substitution (u); +# set exit code to non-zero for any failed piped commands (o pipefail) +# See also: http://redsymbol.net/articles/unofficial-bash-strict-mode/ set -euo pipefail -# pull prior image to populate layer cache -docker pull mltshp/mltshp-worker:latest +echo "--- Pulling base Docker image" + docker pull mltshp/mltshp-worker:latest -docker build -t mltshp/mltshp-worker:build-${BUILDKITE_BUILD_NUMBER} -f Dockerfile.worker . -docker push mltshp/mltshp-worker:build-${BUILDKITE_BUILD_NUMBER} \ No newline at end of file +echo "+++ Building Docker image for worker node" + docker build -t mltshp/mltshp-worker:build-${BUILDKITE_BUILD_NUMBER} -f Dockerfile.worker . + +echo "--- Pushing build Docker image to Docker Hub" + docker push mltshp/mltshp-worker:build-${BUILDKITE_BUILD_NUMBER} \ No newline at end of file diff --git a/.buildkite/steps/deploy.sh b/.buildkite/steps/deploy.sh index cb556640..9a573846 100755 --- a/.buildkite/steps/deploy.sh +++ b/.buildkite/steps/deploy.sh @@ -1,6 +1,9 @@ #!/bin/bash -set -eo pipefail +# exit if any command fails (e); strict variable substitution (u); +# set exit code to non-zero for any failed piped commands (o pipefail) +# See also: http://redsymbol.net/articles/unofficial-bash-strict-mode/ +set -euo pipefail function slackpost { # Usage: slackpost @@ -18,13 +21,19 @@ function slackpost { fi } -# Grab CI images -docker pull mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} -docker tag mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} mltshp/mltshp-web:latest -docker push mltshp/mltshp-web:latest +echo "--- Pulling Docker image for web node build ${BUILDKITE_BUILD_NUMBER}" + docker pull mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} -docker pull mltshp/mltshp-worker:build-${BUILDKITE_BUILD_NUMBER} -docker tag mltshp/mltshp-worker:build-${BUILDKITE_BUILD_NUMBER} mltshp/mltshp-worker:latest -docker push mltshp/mltshp-worker:latest +echo "--- Tagging Docker image as latest and pushing to Docker Hub" + docker tag mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} mltshp/mltshp-web:latest + docker push mltshp/mltshp-web:latest -slackpost "#operations" "Build ${BUILDKITE_BUILD_NUMBER} has been pushed to Docker cloud by ${BUILDKITE_UNBLOCKER}: ${BUILDKITE_BUILD_URL}" +echo "--- Pulling Docker image for worker node build ${BUILDKITE_BUILD_NUMBER}" + docker pull mltshp/mltshp-worker:build-${BUILDKITE_BUILD_NUMBER} + +echo "--- Tagging Docker image as latest and pushing to Docker Hub" + docker tag mltshp/mltshp-worker:build-${BUILDKITE_BUILD_NUMBER} mltshp/mltshp-worker:latest + docker push mltshp/mltshp-worker:latest + +echo "--- Posting Slack alert!" + slackpost "#operations" "Build ${BUILDKITE_BUILD_NUMBER} has been pushed to Docker cloud by ${BUILDKITE_UNBLOCKER}: ${BUILDKITE_BUILD_URL}" diff --git a/.buildkite/steps/test.sh b/.buildkite/steps/test.sh index af3a2c15..5750f4a9 100755 --- a/.buildkite/steps/test.sh +++ b/.buildkite/steps/test.sh @@ -1,5 +1,8 @@ #!/bin/bash +# exit if any command fails (e); strict variable substitution (u); +# set exit code to non-zero for any failed piped commands (o pipefail) +# See also: http://redsymbol.net/articles/unofficial-bash-strict-mode/ set -euo pipefail wait_for() { @@ -7,24 +10,24 @@ wait_for() { while ! nc -z $1 $2; do echo sleeping; sleep 2; done } -docker pull mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} -docker tag mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} mltshp/mltshp-web:latest +echo "--- Pulling base Docker image" + docker pull mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} + docker tag mltshp/mltshp-web:build-${BUILDKITE_BUILD_NUMBER} mltshp/mltshp-web:latest -# launch fakes3/mysql/web app -docker compose -f .buildkite/docker-compose.yml up -d --build +echo "--- Launching Docker containers" + docker compose -f .buildkite/docker-compose.yml up -d --build -# let's wait and allow mysql/fakes3 to spin up -#wait_for localhost 3306 -#wait_for localhost 8000 -sleep 10 +echo "~~~ Waiting for containers to start" + #wait_for localhost 3306 + #wait_for localhost 8000 + sleep 10 -# run our tests against it -docker exec -t buildkite_mltshp_1 ./run-tests.sh +echo "+++ Running unit tests" + docker exec -t buildkite_mltshp_1 ./run-tests.sh -# submit coverage data -docker exec -t -e BUILDKITE -e BUILDKITE_JOB_ID -e BUILDKITE_BRANCH -e COVERALLS_REPO_TOKEN buildkite_mltshp_1 ./coveralls-report.sh +echo "--- Submitting coverage data" + docker exec -t -e BUILDKITE -e BUILDKITE_JOB_ID -e BUILDKITE_BRANCH -e COVERALLS_REPO_TOKEN buildkite_mltshp_1 ./coveralls-report.sh -# tear down containers -docker compose -f .buildkite/docker-compose.yml down - -docker container prune -f +echo "~~~ Stopping containers; cleanup" + docker compose -f .buildkite/docker-compose.yml down + docker container prune -f From b9cea5a2c45d750d325193a2ac035fc5f95a4b37 Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Mon, 24 Jul 2023 22:01:32 -0500 Subject: [PATCH 17/19] Disable output buffering for tests --- run-tests.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/run-tests.sh b/run-tests.sh index 90aebcfb..ef197abb 100755 --- a/run-tests.sh +++ b/run-tests.sh @@ -1,7 +1,11 @@ #!/bin/bash +# exit if any command fails (e); strict variable substitution (u); +# set exit code to non-zero for any failed piped commands (o pipefail) +# See also: http://redsymbol.net/articles/unofficial-bash-strict-mode/ set -euo pipefail +export PYTHONUNBUFFERED=1 pip install -r requirements-test.txt coverage run --source=handlers,models,tasks,lib test.py coverage xml From 60d82dd0559f5a8797df3b0229ae85a32d880c5b Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Sun, 17 Dec 2023 22:41:10 -0600 Subject: [PATCH 18/19] Updates for python 3 --- test/FileTests.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/FileTests.py b/test/FileTests.py index 007adcb5..6b452242 100644 --- a/test/FileTests.py +++ b/test/FileTests.py @@ -300,7 +300,7 @@ def setUp(self): self.xsrf = self.get_xsrf().decode("ascii") self.url = 'https://example.com/images/television.png?x=1' - self.source_url = url_escape('https://example.com/') + self.source_url = 'https://example.com/' self.description = "This is a multi-\nline\ndescription" self.alt_text = "This is some alt text\nit spans two lines." @@ -353,10 +353,8 @@ def test_picker_stores_description(self): self.assertEqual(sf.description, self.description) def test_picker_stores_alt_text(self): - request = HTTPRequest(self.get_url('/tools/p'), 'POST', {"Cookie":"_xsrf=%s;sid=%s" % (self.xsrf,self.sid)}, "_xsrf=%s&url=%s&title=boatmoatgoat&description=%s&alt_text=%s&skip_s3=1" % (self.xsrf, url_escape(self.url), url_escape(self.description), url_escape(self.alt_text))) - self.http_client.fetch(request, self.stop) - response = self.wait() - self.assertTrue(response.body.find("ERROR") == -1) + response = self.post_url('/tools/p', arguments={"url": self.url, "title": "boatmoatgoat", "description": self.description, "alt_text": self.alt_text, "skip_s3": "1"}) + self.assertNotIn("ERROR", response.body) sf = Sharedfile.get("id=1") self.assertEqual(sf.alt_text, self.alt_text) From f7942db6b5ccb382a4df00a4adea2865d5041be9 Mon Sep 17 00:00:00 2001 From: Brad Choate Date: Mon, 18 Dec 2023 09:43:38 -0600 Subject: [PATCH 19/19] Requirement updates --- Dockerfile | 6 +++--- requirements-test.txt | 2 +- requirements.txt | 24 ++++++++++++------------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4440df02..c6b1a5b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,9 +7,9 @@ ENV PYTHONUNBUFFERED 1 # whole layer and steps to build it should be cached. RUN apt-get -y update && apt-get install -y \ supervisor \ + python3-dev \ libmysqlclient-dev \ mysql-client \ - python3-dev \ libjpeg-dev \ libcurl4-openssl-dev \ curl \ @@ -25,9 +25,9 @@ RUN apt-get -y update && apt-get install -y \ # install nginx + upload module mkdir -p /tmp/install && \ cd /tmp/install && \ - wget http://nginx.org/download/nginx-1.25.1.tar.gz && tar zxf nginx-1.25.1.tar.gz && \ + wget http://nginx.org/download/nginx-1.25.3.tar.gz && tar zxf nginx-1.25.3.tar.gz && \ wget https://github.com/fdintino/nginx-upload-module/archive/2.3.0.tar.gz && tar zxf 2.3.0.tar.gz && \ - cd /tmp/install/nginx-1.25.1 && \ + cd /tmp/install/nginx-1.25.3 && \ ./configure \ --with-http_ssl_module \ --with-http_stub_status_module \ diff --git a/requirements-test.txt b/requirements-test.txt index 0f13cc23..a7cafe10 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,2 +1,2 @@ coverage==6.5.0 -coveralls==3.3.1 +coveralls==3.3.1 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index edcb4f1d..43e521d8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,18 +1,18 @@ +amqplib==1.0.2 beautifulsoup4==4.12.2 +boto3==1.34.2 +celery==5.3.6 +ffmpy==0.3.1 +kombu==5.3.4 +mock==5.1.0 mysqlclient==2.1.1 -Pillow==10.0.0 -amqplib==1.0.2 -boto3==1.28.9 -celery==5.3.1 -kombu==5.3.1 +Pillow==10.1.0 pycurl==7.45.2 -pyparsing==3.1.0 +pyOpenSSL==23.3.0 +pyparsing==3.1.1 python-dateutil==2.8.2 python-postmark==0.6.0 -tornado==6.3.2 -mock==5.0.2 -pyOpenSSL==23.2.0 requests==2.31.0 -stripe==5.4.0 -yoyo-migrations==8.2.0 -ffmpy==0.3.0 +stripe==7.9.0 +tornado==6.4.0 +yoyo-migrations==8.2.0 \ No newline at end of file