From 134d10248ae87bb6d046ab2d8f3aa92fc868d4d0 Mon Sep 17 00:00:00 2001 From: ljf Date: Fri, 4 Aug 2017 15:44:19 +0200 Subject: [PATCH 001/195] [enh] Add a notification if a user subscribe --- coin/members/models.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/coin/members/models.py b/coin/members/models.py index 5a2f834..25a0b8b 100644 --- a/coin/members/models.py +++ b/coin/members/models.py @@ -16,7 +16,10 @@ from django.core.validators import RegexValidator from django.core.exceptions import ValidationError from django.utils import timezone from django.utils.text import slugify +from django.core.mail import send_mail +from django.core.urlresolvers import reverse from ldapdb.models.fields import CharField, IntegerField, ListField +from registration.signals import user_registered from coin.offers.models import Offer, OfferSubscription from coin.mixins import CoinLdapSyncMixin @@ -35,6 +38,22 @@ class MemberManager(UserManager): offers = Offer.objects.manageable_by(user) return super(MemberManager, self).filter(offersubscription__offer__in=offers).distinct() +@receiver(user_registered) +def send_registration_notification(sender, user, request=None, **kwargs): + """ + Send a notification to the admin if a user subscribe + """ + relative_link = reverse('admin:members_member_change', args=[user.id]) + abs_link = request.build_absolute_uri(relative_link) + + send_mail('[COIN] Nouvelle inscription', + 'Bonjour,\n' + + '%s s\'est enregistré(e) sur COIN.\n' % user.username + + 'Lien d\'édition: %s' % abs_link, + settings.DEFAULT_FROM_EMAIL, + settings.NOTIFICATION_EMAILS, + fail_silently=False) + class Member(CoinLdapSyncMixin, AbstractUser): -- GitLab From 7e0db26aab221394d5eb4c53db0db0ed0a1de81d Mon Sep 17 00:00:00 2001 From: ljf Date: Fri, 18 Aug 2017 15:55:18 +0200 Subject: [PATCH 002/195] [enh] Link ip pools to an offer --- coin/offers/admin.py | 11 ++++-- .../migrations/0008_auto_20170818_1507.py | 32 ++++++++++++++++ .../migrations/0009_auto_20170818_1529.py | 37 +++++++++++++++++++ coin/offers/models.py | 18 +++++++++ 4 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 coin/offers/migrations/0008_auto_20170818_1507.py create mode 100644 coin/offers/migrations/0009_auto_20170818_1529.py diff --git a/coin/offers/admin.py b/coin/offers/admin.py index 461b1ae..845b431 100644 --- a/coin/offers/admin.py +++ b/coin/offers/admin.py @@ -6,7 +6,7 @@ from django.db.models import Q from polymorphic.admin import PolymorphicChildModelAdmin from coin.members.models import Member -from coin.offers.models import Offer, OfferSubscription +from coin.offers.models import Offer, OfferIPPool, OfferSubscription from coin.offers.offersubscription_filter import\ OfferSubscriptionTerminationFilter,\ OfferSubscriptionCommitmentFilter @@ -14,6 +14,11 @@ from coin.offers.forms import OfferAdminForm import autocomplete_light +class OfferIPPoolAdmin(admin.TabularInline): + model = OfferIPPool + extra = 1 + + class OfferAdmin(admin.ModelAdmin): list_display = ('get_configuration_type_display', 'name', 'reference', 'billing_period', 'period_fees', 'initial_fees') @@ -21,7 +26,7 @@ class OfferAdmin(admin.ModelAdmin): list_filter = ('configuration_type',) search_fields = ['name'] form = OfferAdminForm - + inlines = (OfferIPPoolAdmin,) # def get_readonly_fields(self, request, obj=None): # if obj: # return ['backend',] @@ -38,7 +43,7 @@ class OfferSubscriptionAdmin(admin.ModelAdmin): 'offer', 'member') search_fields = ['member__first_name', 'member__last_name', 'member__email', 'member__nickname'] - + fields = ( 'member', 'offer', diff --git a/coin/offers/migrations/0008_auto_20170818_1507.py b/coin/offers/migrations/0008_auto_20170818_1507.py new file mode 100644 index 0000000..0f5e4cb --- /dev/null +++ b/coin/offers/migrations/0008_auto_20170818_1507.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('resources', '0003_auto_20150203_1043'), + ('offers', '0007_offersubscription_comments'), + ] + + operations = [ + migrations.CreateModel( + name='OfferIPPool', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('priority', models.IntegerField()), + ('ippool', models.ForeignKey(to='resources.IPPool')), + ('offer', models.ForeignKey(to='offers.Offer')), + ], + options={ + 'ordering': ['priority'], + }, + ), + migrations.AddField( + model_name='offer', + name='ippools', + field=models.ManyToManyField(to='resources.IPPool', through='offers.OfferIPPool'), + ), + ] diff --git a/coin/offers/migrations/0009_auto_20170818_1529.py b/coin/offers/migrations/0009_auto_20170818_1529.py new file mode 100644 index 0000000..d22258e --- /dev/null +++ b/coin/offers/migrations/0009_auto_20170818_1529.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('offers', '0008_auto_20170818_1507'), + ] + + operations = [ + migrations.AlterModelOptions( + name='offerippool', + options={'ordering': ['to_assign'], 'verbose_name': "pool d'IP", 'verbose_name_plural': "pools d'IP"}, + ), + migrations.RemoveField( + model_name='offerippool', + name='priority', + ), + migrations.AddField( + model_name='offerippool', + name='to_assign', + field=models.BooleanField(default=False, verbose_name='assignation par d\xe9faut'), + ), + migrations.AlterField( + model_name='offerippool', + name='ippool', + field=models.ForeignKey(verbose_name="pool d'IP", to='resources.IPPool'), + ), + migrations.AlterField( + model_name='offerippool', + name='offer', + field=models.ForeignKey(verbose_name='offre', to='offers.Offer'), + ), + ] diff --git a/coin/offers/models.py b/coin/offers/models.py index 0a01a9d..9c1e64b 100644 --- a/coin/offers/models.py +++ b/coin/offers/models.py @@ -9,6 +9,8 @@ from django.db.models import Count, Q from django.core.validators import MinValueValidator from django.contrib.contenttypes.models import ContentType +from coin.resources.models import IPPool + class OfferManager(models.Manager): def manageable_by(self, user): @@ -60,6 +62,8 @@ class Offer(models.Model): verbose_name='n\'est pas facturable', help_text='L\'offre ne sera pas facturée par la commande charge_members') + ippools = models.ManyToManyField(IPPool, through='OfferIPPool') + objects = OfferManager() def get_configuration_type_display(self): @@ -97,6 +101,20 @@ class Offer(models.Model): verbose_name = 'offre' +class OfferIPPool(models.Model): + offer = models.ForeignKey(Offer, + verbose_name='offre') + ippool = models.ForeignKey(IPPool, + verbose_name='pool d\'IP') + to_assign = models.BooleanField(default=False, + verbose_name='assignation par défaut') + + class Meta: + verbose_name = 'pool d\'IP' + verbose_name_plural = "pools d'IP" + ordering = ['-to_assign'] + + class OfferSubscriptionQuerySet(models.QuerySet): def running(self, at_date=None): """ Only the running contracts at a given date. -- GitLab From 41a7638b86d39fe3fb6a527c0da301cb6cb8b23d Mon Sep 17 00:00:00 2001 From: ljf Date: Fri, 18 Aug 2017 20:03:10 +0200 Subject: [PATCH 003/195] [enh] Autocreate config --- coin/configuration/models.py | 31 ++++++++++++++++--- .../migrations/0010_auto_20170818_1835.py | 28 +++++++++++++++++ coin/offers/models.py | 4 +-- coin/resources/models.py | 4 +++ housing/models.py | 4 +-- vpn/models.py | 6 ++-- vps/models.py | 6 ++-- 7 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 coin/offers/migrations/0010_auto_20170818_1835.py diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 8f54268..2add343 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -17,7 +17,7 @@ technical informations of a subscription. To add a new configuration backend, you have to create a new app with a model which inherit from Configuration. -Your model can implement Meta verbose_name to have human readable name and a +Your model can implement Meta verbose_name to have human readable name and a url_namespace variable to specify the url namespace used by this model. """ @@ -35,9 +35,9 @@ class Configuration(PolymorphicModel): Génère automatiquement la liste de choix possibles de configurations en fonction des classes enfants de Configuration """ - return tuple((x().__class__.__name__,x()._meta.verbose_name) + return tuple((x().__class__.__name__,x()._meta.verbose_name) for x in Configuration.__subclasses__()) - + def model_name(self): return self.__class__.__name__ model_name.short_description = 'Nom du modèle' @@ -52,7 +52,7 @@ class Configuration(PolymorphicModel): Une url doit être nommée "details" """ from django.core.urlresolvers import reverse - return reverse('%s:details' % self.get_url_namespace(), + return reverse('%s:details' % self.get_url_namespace(), args=[str(self.id)]) def get_url_namespace(self): @@ -66,10 +66,33 @@ class Configuration(PolymorphicModel): else: return self.model_name().lower() + def save(self, **kwargs): + self.clean() + os = self.offersubscription + for offer_ip_pool in os.offer.offerippool_set.order_by('-to_assign'): + IPSubnet.objects.create( + configuration=self, + ip_pool=offer_ip_pool.ip_pool) + return super(Configuration, self).save(**kwargs) + class Meta: verbose_name = 'configuration' +@receiver(post_save, sender=OfferSubscription) +def offer_subscription_event(sender, **kwargs): + os = kwargs['instance'] + + if not hasattr(os, 'configuration'): + config_cls = None + for subconfig_cls in Configuration.__subclasses__(): + if subconfig_cls().__class__.__name__ == os.offer.configuration_type: + config_cls = subconfig_cls + break + + if config_cls is not None: + config = config_cls.objects.create(offersubscription=os) + @receiver(post_save, sender=IPSubnet) @receiver(post_delete, sender=IPSubnet) def subnet_event(sender, **kwargs): diff --git a/coin/offers/migrations/0010_auto_20170818_1835.py b/coin/offers/migrations/0010_auto_20170818_1835.py new file mode 100644 index 0000000..4bbea48 --- /dev/null +++ b/coin/offers/migrations/0010_auto_20170818_1835.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('offers', '0009_auto_20170818_1529'), + ] + + operations = [ + migrations.AlterModelOptions( + name='offerippool', + options={'ordering': ['-to_assign'], 'verbose_name': "pool d'IP", 'verbose_name_plural': "pools d'IP"}, + ), + migrations.RenameField( + model_name='offer', + old_name='ippools', + new_name='ip_pools', + ), + migrations.RenameField( + model_name='offerippool', + old_name='ippool', + new_name='ip_pool', + ), + ] diff --git a/coin/offers/models.py b/coin/offers/models.py index 9c1e64b..5fc2a99 100644 --- a/coin/offers/models.py +++ b/coin/offers/models.py @@ -62,7 +62,7 @@ class Offer(models.Model): verbose_name='n\'est pas facturable', help_text='L\'offre ne sera pas facturée par la commande charge_members') - ippools = models.ManyToManyField(IPPool, through='OfferIPPool') + ip_pools = models.ManyToManyField(IPPool, through='OfferIPPool') objects = OfferManager() @@ -104,7 +104,7 @@ class Offer(models.Model): class OfferIPPool(models.Model): offer = models.ForeignKey(Offer, verbose_name='offre') - ippool = models.ForeignKey(IPPool, + ip_pool = models.ForeignKey(IPPool, verbose_name='pool d\'IP') to_assign = models.BooleanField(default=False, verbose_name='assignation par défaut') diff --git a/coin/resources/models.py b/coin/resources/models.py index 69211d7..1e06075 100644 --- a/coin/resources/models.py +++ b/coin/resources/models.py @@ -111,6 +111,10 @@ class IPSubnet(models.Model): self.validate_inclusion() self.validate_reverse_dns() + def save(self, **kwargs): + self.clean() + return super(IPSubnet, self).save(**kwargs) + def __unicode__(self): return str(self.inet) diff --git a/housing/models.py b/housing/models.py index 532a9a5..bd8f724 100644 --- a/housing/models.py +++ b/housing/models.py @@ -14,7 +14,7 @@ from coin import validation class HousingConfiguration(Configuration): url_namespace = "housing" - activated = models.BooleanField(default=False, verbose_name='activé') + activated = models.BooleanField(default=True, verbose_name='activé') ipv4_endpoint = InetAddressField(validators=[validation.validate_v4], verbose_name="IPv4", blank=True, null=True, help_text="Adresse IPv4 utilisée par " @@ -93,7 +93,7 @@ class HousingConfiguration(Configuration): def clean(self): # If saving for the first time and IP endpoints are not specified, # generate them automatically. - if self.pk is None: + if self.ipv4_endpoint is None or self.ipv6_endpoint is None: self.generate_endpoints() self.check_endpoints() diff --git a/vpn/models.py b/vpn/models.py index 25adc2d..beebbdd 100644 --- a/vpn/models.py +++ b/vpn/models.py @@ -23,7 +23,7 @@ class VPNConfiguration(CoinLdapSyncMixin, Configuration): # 'offers.OfferSubscription', # related_name=backend_name, # validators=[ValidateBackendType(backend_name)]) - activated = models.BooleanField(default=False, verbose_name='activé') + activated = models.BooleanField(default=True, verbose_name='activé') login = models.CharField(max_length=50, unique=True, blank=True, verbose_name="identifiant", help_text="Laisser vide pour une génération automatique") @@ -141,9 +141,9 @@ class VPNConfiguration(CoinLdapSyncMixin, Configuration): ValidationError("Impossible de générer un login VPN") # Hash password if needed self.password = utils.ldap_hash(self.password) - # If saving for the first time and IP endpoints are not specified, + # If IP endpoints are not specified, # generate them automatically. - if self.pk is None: + if self.ipv4_endpoint is None or self.ipv6_endpoint is None: self.generate_endpoints() self.check_endpoints() diff --git a/vps/models.py b/vps/models.py index 82610bf..839a497 100644 --- a/vps/models.py +++ b/vps/models.py @@ -24,7 +24,7 @@ PROTOCOLE_TYPES = ( class VPSConfiguration(Configuration): url_namespace = "vps" - activated = models.BooleanField(default=False, verbose_name='activé') + activated = models.BooleanField(default=True, verbose_name='activé') ipv4_endpoint = InetAddressField(validators=[validation.validate_v4], verbose_name="IPv4", blank=True, null=True, help_text="Adresse IPv4 utilisée par " @@ -100,9 +100,9 @@ class VPSConfiguration(Configuration): raise ValidationError(error.format(self.ipv6_endpoint)) def clean(self): - # If saving for the first time and IP endpoints are not specified, + # If IP endpoints are not specified, # generate them automatically. - if self.pk is None: + if self.ipv4_endpoint is None or self.ipv6_endpoint is None: self.generate_endpoints() self.check_endpoints() -- GitLab From bed4321fa733572824ce8b37b6c48c89584aaef3 Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 19 Aug 2017 20:32:19 +0200 Subject: [PATCH 004/195] [fix] Unsaved object --- coin/configuration/models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 2add343..c162cbb 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -68,12 +68,13 @@ class Configuration(PolymorphicModel): def save(self, **kwargs): self.clean() - os = self.offersubscription + config = super(Configuration, self).save(**kwargs) + os = config.offersubscription for offer_ip_pool in os.offer.offerippool_set.order_by('-to_assign'): IPSubnet.objects.create( - configuration=self, + configuration=config, ip_pool=offer_ip_pool.ip_pool) - return super(Configuration, self).save(**kwargs) + return config class Meta: verbose_name = 'configuration' -- GitLab From e305293a8c216092b186463490db3b4e2a89a0a3 Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 19 Aug 2017 20:52:10 +0200 Subject: [PATCH 005/195] [fix] Nonetype error --- coin/configuration/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index c162cbb..0c1931a 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -69,10 +69,10 @@ class Configuration(PolymorphicModel): def save(self, **kwargs): self.clean() config = super(Configuration, self).save(**kwargs) - os = config.offersubscription + os = self.offersubscription for offer_ip_pool in os.offer.offerippool_set.order_by('-to_assign'): IPSubnet.objects.create( - configuration=config, + configuration=self, ip_pool=offer_ip_pool.ip_pool) return config -- GitLab From fb55dc4817e7f1b9860c9a0c1f56a2b35ea1aafc Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 19 Aug 2017 21:40:30 +0200 Subject: [PATCH 006/195] [fix] No resource error --- coin/configuration/models.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 0c1931a..9e47cb2 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -69,11 +69,6 @@ class Configuration(PolymorphicModel): def save(self, **kwargs): self.clean() config = super(Configuration, self).save(**kwargs) - os = self.offersubscription - for offer_ip_pool in os.offer.offerippool_set.order_by('-to_assign'): - IPSubnet.objects.create( - configuration=self, - ip_pool=offer_ip_pool.ip_pool) return config class Meta: @@ -93,6 +88,11 @@ def offer_subscription_event(sender, **kwargs): if config_cls is not None: config = config_cls.objects.create(offersubscription=os) + for offer_ip_pool in os.offer.offerippool_set.order_by('-to_assign'): + IPSubnet.objects.create( + configuration=config, + ip_pool=offer_ip_pool.ip_pool) + config.save() @receiver(post_save, sender=IPSubnet) @receiver(post_delete, sender=IPSubnet) -- GitLab From 5a1b7666da9e6bb3578f480b910e2d2060748dc3 Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 19 Aug 2017 23:06:17 +0200 Subject: [PATCH 007/195] [fix] IPV6 unique case --- vpn/models.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/vpn/models.py b/vpn/models.py index beebbdd..d43be28 100644 --- a/vpn/models.py +++ b/vpn/models.py @@ -95,9 +95,13 @@ class VPNConfiguration(CoinLdapSyncMixin, Configuration): subnets_v6 = [s for s in subnets if s.inet.version == 6] if len(subnets_v6) > 0: # With v6, we choose the second host of the subnet (cafe::1) - gen = subnets_v6[0].inet.iter_hosts() - gen.next() - self.ipv6_endpoint = gen.next() + net = subnets_v6[0].inet + if inet.prefixlen != 128: + gen = inet.iter_hosts() + gen.next() + self.ipv6_endpoint = gen.next() + else: + self.ipv6_endpoint = inet.ip updated = True return updated -- GitLab From 50512332fd3d0f632ae5bea83fcf67ec93e522a5 Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 19 Aug 2017 23:16:42 +0200 Subject: [PATCH 008/195] [fix] Import error --- coin/resources/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coin/resources/models.py b/coin/resources/models.py index 1e06075..268c5d6 100644 --- a/coin/resources/models.py +++ b/coin/resources/models.py @@ -5,7 +5,7 @@ from django.db import models from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator from netfields import CidrAddressField, NetManager -from netaddr import IPSet +from netaddr import IPSet, IPNetwork, IPAddress class IPPool(models.Model): -- GitLab From 2483d0f4231d81d98bdc07ce8b9ea9dc5cbbca15 Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 19 Aug 2017 23:17:57 +0200 Subject: [PATCH 009/195] [fix] syntax error --- vpn/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpn/models.py b/vpn/models.py index d43be28..0247778 100644 --- a/vpn/models.py +++ b/vpn/models.py @@ -95,7 +95,7 @@ class VPNConfiguration(CoinLdapSyncMixin, Configuration): subnets_v6 = [s for s in subnets if s.inet.version == 6] if len(subnets_v6) > 0: # With v6, we choose the second host of the subnet (cafe::1) - net = subnets_v6[0].inet + inet = subnets_v6[0].inet if inet.prefixlen != 128: gen = inet.iter_hosts() gen.next() -- GitLab From b2f648162a17217639c0f5f18025deba30b5779a Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 15 Apr 2018 22:36:49 +0200 Subject: [PATCH 010/195] [enh] Manage donation and membership fees --- coin/billing/admin.py | 31 +- .../membershipfee_filter.py | 0 .../migrations/0011_auto_20180414_2250.py | 19 + .../migrations/0012_auto_20180415_1502.py | 96 +++++ .../migrations/0013_auto_20180415_0413.py | 63 +++ .../migrations/0014_auto_20180415_1814.py | 54 +++ .../migrations/0015_remove_payment_invoice.py | 18 + .../migrations/0016_auto_20180415_2208.py | 19 + coin/billing/models.py | 369 ++++++++++++------ coin/billing/tests.py | 169 +++++++- coin/billing/urls.py | 4 +- coin/billing/utils.py | 2 +- coin/members/admin.py | 21 +- .../migrations/0018_auto_20180414_2250.py | 30 ++ .../migrations/0019_auto_20180415_1814.py | 21 + coin/members/models.py | 4 +- coin/members/templates/members/invoices.html | 8 +- coin/members/tests.py | 159 +------- coin/members/views.py | 3 +- 19 files changed, 779 insertions(+), 311 deletions(-) rename coin/{members => billing}/membershipfee_filter.py (100%) create mode 100644 coin/billing/migrations/0011_auto_20180414_2250.py create mode 100644 coin/billing/migrations/0012_auto_20180415_1502.py create mode 100644 coin/billing/migrations/0013_auto_20180415_0413.py create mode 100644 coin/billing/migrations/0014_auto_20180415_1814.py create mode 100644 coin/billing/migrations/0015_remove_payment_invoice.py create mode 100644 coin/billing/migrations/0016_auto_20180415_2208.py create mode 100644 coin/members/migrations/0018_auto_20180414_2250.py create mode 100644 coin/members/migrations/0019_auto_20180415_1814.py diff --git a/coin/billing/admin.py b/coin/billing/admin.py index 1d1ce8e..9fa751f 100644 --- a/coin/billing/admin.py +++ b/coin/billing/admin.py @@ -9,8 +9,11 @@ from django.contrib.admin.utils import flatten_fieldsets from django import forms from coin.filtering_queryset import LimitedAdminInlineMixin -from coin.billing.models import Invoice, InvoiceDetail, Payment, PaymentAllocation +from coin.billing.models import Invoice, InvoiceDetail, Payment, \ + PaymentAllocation, MembershipFee, Donation from coin.billing.utils import get_invoice_from_id_or_number +from coin.billing.membershipfee_filter import MembershipFeeFilter +from coin.members.admin import MemberAdmin from django.core.urlresolvers import reverse import autocomplete_light @@ -185,7 +188,7 @@ class InvoiceAdmin(admin.ModelAdmin): # TODO : Add better perm here if request.user.is_superuser: invoice = get_invoice_from_id_or_number(id) - if invoice.amount() == 0: + if invoice.amount == 0: messages.error(request, 'Une facture validée ne peut pas avoir' ' un total de 0€.') else: @@ -203,8 +206,8 @@ class InvoiceAdmin(admin.ModelAdmin): class PaymentAllocationInlineReadOnly(admin.TabularInline): model = PaymentAllocation extra = 0 - fields = ("invoice", "amount") - readonly_fields = ("invoice", "amount") + fields = ("bill", "amount") + readonly_fields = ("bill", "amount") verbose_name = None verbose_name_plural = "Alloué à" @@ -238,5 +241,25 @@ class PaymentAdmin(admin.ModelAdmin): def get_inline_instances(self, request, obj=None): return [PaymentAllocationInlineReadOnly(self.model, self.admin_site)] + +class MembershipFeeAdmin(admin.ModelAdmin): + list_display = ('member', 'end_date', '_amount') + form = autocomplete_light.modelform_factory(MembershipFee, fields='__all__') + + +class DonationAdmin(admin.ModelAdmin): + list_display = ('member', 'date', '_amount') + form = autocomplete_light.modelform_factory(MembershipFee, fields='__all__') + +class MembershipFeeInline(admin.TabularInline): + model = MembershipFee + extra = 0 + fields = ('start_date', 'end_date', '_amount') + +MemberAdmin.list_filter += ('status', MembershipFeeFilter) +MemberAdmin.inlines += [MembershipFeeInline] + admin.site.register(Invoice, InvoiceAdmin) admin.site.register(Payment, PaymentAdmin) +admin.site.register(MembershipFee, MembershipFeeAdmin) +admin.site.register(Donation, DonationAdmin) diff --git a/coin/members/membershipfee_filter.py b/coin/billing/membershipfee_filter.py similarity index 100% rename from coin/members/membershipfee_filter.py rename to coin/billing/membershipfee_filter.py diff --git a/coin/billing/migrations/0011_auto_20180414_2250.py b/coin/billing/migrations/0011_auto_20180414_2250.py new file mode 100644 index 0000000..a738194 --- /dev/null +++ b/coin/billing/migrations/0011_auto_20180414_2250.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0010_new_billing_system_data'), + ] + + operations = [ + migrations.AlterField( + model_name='payment', + name='amount', + field=models.DecimalField(null=True, verbose_name='montant', max_digits=6, decimal_places=2), + ), + ] diff --git a/coin/billing/migrations/0012_auto_20180415_1502.py b/coin/billing/migrations/0012_auto_20180415_1502.py new file mode 100644 index 0000000..c431651 --- /dev/null +++ b/coin/billing/migrations/0012_auto_20180415_1502.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import datetime +import coin.billing.models +import django.db.models.deletion +import django.core.files.storage +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('billing', '0011_auto_20180414_2250'), + ] + + operations = [ + migrations.CreateModel( + name='Bill', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('status2', models.CharField(default='open', max_length=50, verbose_name='statut', choices=[('open', '\xc0 payer'), ('closed', 'R\xe9gl\xe9e'), ('trouble', 'Litige')])), + ('date2', models.DateField(default=datetime.date.today, help_text='Cette date sera d\xe9finie \xe0 la date de validation dans le document final', null=True, verbose_name='date')), + ('pdf2', models.FileField(storage=django.core.files.storage.FileSystemStorage(location='/vagrant/apps/extra/coin2/smedia/'), upload_to=coin.billing.models.bill_pdf_filename, null=True, verbose_name='PDF', blank=True)), + ], + options={ + 'verbose_name': 'note', + }, + ), + migrations.RemoveField( + model_name='invoice', + name='id', + ), + migrations.AlterField( + model_name='invoice', + name='date', + field=models.DateField(default=datetime.date.today, help_text='Cette date sera d\xe9finie \xe0 la date de validation dans le document final', null=True, verbose_name='date'), + ), + migrations.AlterField( + model_name='invoice', + name='pdf', + field=models.FileField(storage=django.core.files.storage.FileSystemStorage(location='/vagrant/apps/extra/coin2/smedia/'), upload_to=coin.billing.models.bill_pdf_filename, null=True, verbose_name='PDF', blank=True), + ), + migrations.AlterField( + model_name='payment', + name='invoice', + field=models.ForeignKey(related_name='payments_old', verbose_name='facture associ\xe9e', blank=True, to='billing.Invoice', null=True), + ), + migrations.AlterField( + model_name='paymentallocation', + name='invoice', + field=models.ForeignKey(related_name='allocations', verbose_name='facture associ\xe9e', to='billing.Bill'), + ), + migrations.CreateModel( + name='Donation', + fields=[ + ('bill_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='billing.Bill')), + ('_amount', models.DecimalField(verbose_name='Montant', max_digits=8, decimal_places=2)), + ], + options={ + 'verbose_name': 'don', + }, + bases=('billing.bill',), + ), + migrations.CreateModel( + name='TempMembershipFee', + fields=[ + ('bill_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='billing.Bill')), + ('_amount', models.DecimalField(default=None, help_text='en \u20ac', verbose_name='montant', max_digits=5, decimal_places=2)), + ('start_date', models.DateField(verbose_name='date de d\xe9but de cotisation')), + ('end_date', models.DateField(help_text='par d\xe9faut, la cotisation dure un an', verbose_name='date de fin de cotisation', blank=True)), + ], + options={ + 'verbose_name': 'cotisation', + }, + bases=('billing.bill',), + ), + migrations.AddField( + model_name='bill', + name='member2', + field=models.ForeignKey(related_name='bills', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to=settings.AUTH_USER_MODEL, null=True, verbose_name='membre'), + ), + migrations.AddField( + model_name='invoice', + name='bill_ptr', + field=models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, default=1, serialize=False, to='billing.Bill'), + preserve_default=False, + ), + migrations.AddField( + model_name='payment', + name='bill', + field=models.ForeignKey(related_name='payments', verbose_name='facture associ\xe9e', blank=True, to='billing.Bill', null=True), + ), + ] diff --git a/coin/billing/migrations/0013_auto_20180415_0413.py b/coin/billing/migrations/0013_auto_20180415_0413.py new file mode 100644 index 0000000..327c3b7 --- /dev/null +++ b/coin/billing/migrations/0013_auto_20180415_0413.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + +def forwards(apps, schema_editor): + + Payment = apps.get_model('billing', 'Payment') + Invoice = apps.get_model('billing', 'Invoice') + MembershipFee = apps.get_model('members', 'MembershipFee') + TempMembershipFee = apps.get_model('billing', 'TempMembershipFee') + PaymentAllocation = apps.get_model('billing', 'PaymentAllocation') + + # Update payment + for payment in Payment.objects.all(): + payment.bill = payment.invoice + payment.save() + + # Update invoice data + for invoice in Invoice.objects.all(): + invoice.member2 = invoice.member + invoice.status2 = invoice.status + invoice.date2 = invoice.date + invoice.pdf2 = invoice.pdf + invoice.save() + + # Update balance for all members + for fee in MembershipFee.objects.all(): + + temp_fee = TempMembershipFee() + temp_fee._amount = fee.amount + temp_fee.start_date = fee.start_date + temp_fee.end_date = fee.end_date + temp_fee.status2 = 'closed' + temp_fee.date2 = temp_fee.start_date + temp_fee.member2 = fee.member + temp_fee.save() + + payment = Payment() + payment.member = fee.member + payment.payment_mean = fee.payment_method + payment.amount = fee.amount + payment.date = fee.payment_date + payment.label = fee.reference + payment.bill = temp_fee + payment.save() + + allocation = PaymentAllocation() + allocation.invoice = temp_fee + allocation.payment = payment + allocation.amount = fee.amount + allocation.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0012_auto_20180415_1502'), + ] + + operations = [ + migrations.RunPython(forwards), + ] diff --git a/coin/billing/migrations/0014_auto_20180415_1814.py b/coin/billing/migrations/0014_auto_20180415_1814.py new file mode 100644 index 0000000..01c1895 --- /dev/null +++ b/coin/billing/migrations/0014_auto_20180415_1814.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0013_auto_20180415_0413'), + ] + + operations = [ + migrations.RenameModel( + old_name='TempMembershipFee', + new_name='MembershipFee', + ), + migrations.RenameField( + model_name='bill', + old_name='date2', + new_name='date', + ), + migrations.RenameField( + model_name='bill', + old_name='member2', + new_name='member', + ), + migrations.RenameField( + model_name='bill', + old_name='pdf2', + new_name='pdf', + ), + migrations.RenameField( + model_name='bill', + old_name='status2', + new_name='status', + ), + migrations.RemoveField( + model_name='invoice', + name='date', + ), + migrations.RemoveField( + model_name='invoice', + name='member', + ), + migrations.RemoveField( + model_name='invoice', + name='pdf', + ), + migrations.RemoveField( + model_name='invoice', + name='status', + ), + ] diff --git a/coin/billing/migrations/0015_remove_payment_invoice.py b/coin/billing/migrations/0015_remove_payment_invoice.py new file mode 100644 index 0000000..e35de26 --- /dev/null +++ b/coin/billing/migrations/0015_remove_payment_invoice.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0014_auto_20180415_1814'), + ] + + operations = [ + migrations.RemoveField( + model_name='payment', + name='invoice', + ), + ] diff --git a/coin/billing/migrations/0016_auto_20180415_2208.py b/coin/billing/migrations/0016_auto_20180415_2208.py new file mode 100644 index 0000000..7f61091 --- /dev/null +++ b/coin/billing/migrations/0016_auto_20180415_2208.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0015_remove_payment_invoice'), + ] + + operations = [ + migrations.RenameField( + model_name='paymentallocation', + old_name='invoice', + new_name='bill', + ), + ] diff --git a/coin/billing/models.py b/coin/billing/models.py index 0d946c0..a392e97 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -5,6 +5,7 @@ import datetime import logging import uuid import re +import abc from decimal import Decimal from dateutil.relativedelta import relativedelta @@ -29,13 +30,124 @@ from coin.isp_database.models import ISPInfo accounting_log = logging.getLogger("coin.billing") -def invoice_pdf_filename(instance, filename): +def bill_pdf_filename(instance, filename): """Nom et chemin du fichier pdf à stocker pour les factures""" member_id = instance.member.id if instance.member else 0 return 'invoices/%d_%s_%s.pdf' % (member_id, instance.number, uuid.uuid4()) +def invoice_pdf_filename(instance, filename): + return bill_pdf_filename(instance, filename) + +class Bill(models.Model): + + BILL_STATUS_CHOICES = ( + ('open', 'À payer'), + ('closed', 'Réglée'), + ('trouble', 'Litige') + ) + + status = models.CharField(max_length=50, choices=BILL_STATUS_CHOICES, + default='open', + verbose_name='statut') + date = models.DateField( + default=datetime.date.today, null=True, verbose_name='date', + help_text='Cette date sera définie à la date de validation dans le document final') + member = models.ForeignKey(Member, null=True, blank=True, default=None, + related_name='bills', + verbose_name='membre', + on_delete=models.SET_NULL) + pdf = models.FileField(storage=private_files_storage, + upload_to=bill_pdf_filename, + null=True, blank=True, + verbose_name='PDF') + @property + def amount(self): + """ Return bill amount """ + return self.cast.amount + amount.fget.short_description = 'Montant' + + def amount_paid(self): + """ + Calcul le montant déjà payé à partir des allocations de paiements + """ + return sum([a.amount for a in self.allocations.all()]) + amount_paid.short_description = 'Montant payé' + + def amount_remaining_to_pay(self): + """ + Calcul le montant restant à payer + """ + return self.amount - self.amount_paid() + amount_remaining_to_pay.short_description = 'Reste à payer' + + def has_owner(self, username): + """ + Check if passed username (ex gmajax) is owner of the invoice + """ + return (self.member and self.member.username == username) + + def generate_pdf(self): + """ + Make and store a pdf file for the invoice + """ + context = {"invoice": self} + context.update(branding(None)) + pdf_file = render_as_pdf('billing/invoice_pdf.html', context) + self.pdf.save('%s.pdf' % self.number, pdf_file) + + def pdf_exists(self): + return (bool(self.pdf) + and private_files_storage.exists(self.pdf.name)) + +# def get_absolute_url(self): +# return reverse('billing:invoice', args=[self.number]) + + def __unicode__(self): + return '%s - %s - %i€' % (self.member, self.date, self.amount) + + @property + def reference(self): + if hasattr(self, 'membershipfee'): + return 'Cotisation' + elif hasattr(self, 'donation'): + return 'Don' + elif hasattr(self, 'invoice'): + return self.invoice.number + + def log_change(self, created): + if created: + accounting_log.info( + "Creating draft bill DRAFT-{} (Member: {}).".format( + self.pk, self.member)) + else: + if not self.validated: + accounting_log.info( + "Updating draft bill DRAFT-{} (Member: {}).".format( + self.pk, self.member)) + else: + accounting_log.info( + "Updating bill {} (Member: {}, Total amount: {}, Amount paid: {}).".format( + self.pk, self.member, + self.amount, self.amount_paid())) + + + @property + def cast(bill): + if hasattr(bill, 'membershipfee'): + return bill.membershipfee + elif hasattr(bill, 'donation'): + return bill.donation + elif hasattr(bill, 'invoice'): + return bill.invoice + @staticmethod + def get_member_validated_bills(member): + related_fields = ['membershipfee', 'donation', 'invoice'] + return [i.cast for i in member.bills.order_by("date") if i.cast.validated] + + class Meta: + verbose_name = 'note' @python_2_unicode_compatible class InvoiceNumber: @@ -117,13 +229,8 @@ class InvoiceQuerySet(models.QuerySet): InvoiceNumber.RE_INVOICE_NUMBER)) -class Invoice(models.Model): +class Invoice(Bill): - INVOICES_STATUS_CHOICES = ( - ('open', 'À payer'), - ('closed', 'Réglée'), - ('trouble', 'Litige') - ) validated = models.BooleanField(default=False, verbose_name='validée', help_text='Once validated, a PDF is generated' @@ -131,24 +238,10 @@ class Invoice(models.Model): number = models.CharField(max_length=25, unique=True, verbose_name='numéro') - status = models.CharField(max_length=50, choices=INVOICES_STATUS_CHOICES, - default='open', - verbose_name='statut') - date = models.DateField( - default=datetime.date.today, null=True, verbose_name='date', - help_text='Cette date sera définie à la date de validation dans la facture finale') date_due = models.DateField( null=True, blank=True, verbose_name="date d'échéance de paiement", help_text='Le délai de paiement sera fixé à {} jours à la validation si laissé vide'.format(settings.PAYMENT_DELAY)) - member = models.ForeignKey(Member, null=True, blank=True, default=None, - related_name='invoices', - verbose_name='membre', - on_delete=models.SET_NULL) - pdf = models.FileField(storage=private_files_storage, - upload_to=invoice_pdf_filename, - null=True, blank=True, - verbose_name='PDF') date_last_reminder_email = models.DateTimeField(null=True, blank=True, verbose_name="Date du dernier email de relance envoyé") @@ -161,6 +254,7 @@ class Invoice(models.Model): self.number = 'DRAFT-{}'.format(self.pk) self.save() + @property def amount(self): """ Calcul le montant de la facture @@ -170,7 +264,7 @@ class Invoice(models.Model): for detail in self.details.all(): total += detail.total() return total.quantize(Decimal('0.01')) - amount.short_description = 'Montant' + amount.fget.short_description = 'Montant' def amount_before_tax(self): total = Decimal('0.0') @@ -179,34 +273,6 @@ class Invoice(models.Model): return total.quantize(Decimal('0.01')) amount_before_tax.short_description = 'Montant HT' - def amount_paid(self): - """ - Calcul le montant déjà payé à partir des allocations de paiements - """ - return sum([a.amount for a in self.allocations.all()]) - amount_paid.short_description = 'Montant payé' - - def amount_remaining_to_pay(self): - """ - Calcul le montant restant à payer - """ - return self.amount() - self.amount_paid() - amount_remaining_to_pay.short_description = 'Reste à payer' - - def has_owner(self, username): - """ - Check if passed username (ex gmajax) is owner of the invoice - """ - return (self.member and self.member.username == username) - - def generate_pdf(self): - """ - Make and store a pdf file for the invoice - """ - context = {"invoice": self} - context.update(branding(None)) - pdf_file = render_as_pdf('billing/invoice_pdf.html', context) - self.pdf.save('%s.pdf' % self.number, pdf_file) @transaction.atomic def validate(self): @@ -239,12 +305,9 @@ class Invoice(models.Model): and bool(self.pdf) and private_files_storage.exists(self.pdf.name)) - def get_absolute_url(self): - return reverse('billing:invoice', args=[self.number]) - def __unicode__(self): return '#{} {:0.2f}€ {}'.format( - self.number, self.amount(), self.date_due) + self.number, self.amount, self.date_due) def reminder_needed(self): @@ -306,6 +369,18 @@ class Invoice(models.Model): self.save() return True + def log_change(self, created): + + if created: + accounting_log.info("Creating draft invoice %s (Member: %s)." + % ('DRAFT-{}'.format(self.pk), self.member)) + else: + if not self.validated: + accounting_log.info("Updating draft invoice %s (Member: %s)." + % (self.number, self.member)) + else: + accounting_log.info("Updating invoice %s (Member: %s, Total amount: %s, Amount paid: %s)." + % (self.number, self.member, self.amount, self.amount_paid() )) class Meta: verbose_name = 'facture' @@ -353,6 +428,69 @@ class InvoiceDetail(models.Model): verbose_name = 'détail de facture' +class Donation(Bill): + _amount = models.DecimalField(max_digits=8, decimal_places=2, + verbose_name='Montant') + + @property + def amount(self): + return self._amount + amount.fget.short_description = 'Montant' + + @property + def validated(self): + return True + + def save(self, *args, **kwargs): + + super(Donation, self).save(*args, **kwargs) + + def clean(self): + # Only if no amount already allocated... + if not self.member or self.member.balance < self.amount: + raise ValidationError("Le solde n'est pas suffisant pour payer ce don. \ + Merci de commencer par enregistrer un paiement pour ce membre.") + class Meta: + verbose_name = 'don' + +class MembershipFee(Bill): + _amount = models.DecimalField(null=False, max_digits=5, decimal_places=2, + default=settings.MEMBER_DEFAULT_COTISATION, + verbose_name='montant', help_text='en €') + start_date = models.DateField( + null=False, + blank=False, + verbose_name='date de début de cotisation') + end_date = models.DateField( + null=False, + blank=True, + verbose_name='date de fin de cotisation', + help_text='par défaut, la cotisation dure un an') + + @property + def amount(self): + return self._amount + amount.fget.short_description = 'Montant' + @property + def validated(self): + return True + + def save(self, *args, **kwargs): + + super(MembershipFee, self).save(*args, **kwargs) + + + def clean(self): + if self.start_date is not None and self.end_date is None: + self.end_date = self.start_date + datetime.timedelta(364) + # Only if no amount already allocated... + if not self.member or self.member.balance < self.amount: + raise ValidationError("Le solde n'est pas suffisant pour payer cette cotisation. \ + Merci de commencer par enregistrer un paiement pour ce membre.") + + class Meta: + verbose_name = 'cotisation' + class Payment(models.Model): PAYMENT_MEAN_CHOICES = ( @@ -374,7 +512,7 @@ class Payment(models.Model): amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, verbose_name='montant') date = models.DateField(default=datetime.date.today) - invoice = models.ForeignKey(Invoice, verbose_name='facture associée', null=True, + bill = models.ForeignKey(Bill, verbose_name='facture associée', null=True, blank=True, related_name='payments') label = models.CharField(max_length=500, @@ -387,9 +525,9 @@ class Payment(models.Model): if self.amount_already_allocated() == 0: # If there's a linked invoice and no member defined - if self.invoice and not self.member: + if self.bill and not self.member: # Automatically set member to invoice's member - self.member = self.invoice.member + self.member = self.bill.member super(Payment, self).save(*args, **kwargs) @@ -401,7 +539,7 @@ class Payment(models.Model): # If there's a linked invoice and this payment would pay more than # the remaining amount needed to pay the invoice... - if self.invoice and self.amount > self.invoice.amount_remaining_to_pay(): + if self.bill and self.amount > self.bill.amount_remaining_to_pay(): raise ValidationError("This payment would pay more than the invoice's remaining to pay") def amount_already_allocated(self): @@ -411,31 +549,31 @@ class Payment(models.Model): return self.amount - self.amount_already_allocated() @transaction.atomic - def allocate_to_invoice(self, invoice): + def allocate_to_bill(self, bill): # FIXME - Add asserts about remaining amount > 0, unpaid amount > 0, # ... amount_can_pay = self.amount_not_allocated() - amount_to_pay = invoice.amount_remaining_to_pay() + amount_to_pay = bill.amount_remaining_to_pay() amount_to_allocate = min(amount_can_pay, amount_to_pay) accounting_log.info( - "Allocating {} from payment {} to invoice {}".format( - amount_to_allocate, self.date, invoice.number)) + "Allocating {} from payment {} to bill {} {}".format( + amount_to_allocate, self.date, bill.reference, bill.pk)) - PaymentAllocation.objects.create(invoice=invoice, + PaymentAllocation.objects.create(bill=bill, payment=self, amount=amount_to_allocate) # Close invoice if relevant - if (invoice.amount_remaining_to_pay() <= 0) and (invoice.status == "open"): + if (bill.amount_remaining_to_pay() <= 0) and (bill.status == "open"): accounting_log.info( - "Invoice {} has been paid and is now closed".format( - invoice.number)) - invoice.status = "closed" + "Bill {} {} has been paid and is now closed".format( + bill.reference, bill.pk)) + bill.status = "closed" - invoice.save() + bill.save() self.save() def __unicode__(self): @@ -456,7 +594,7 @@ class Payment(models.Model): # There can be for example an allocation of 3.14€ from P to I. class PaymentAllocation(models.Model): - invoice = models.ForeignKey(Invoice, verbose_name='facture associée', + bill = models.ForeignKey(Bill, verbose_name='facture associée', null=False, blank=False, related_name='allocations') payment = models.ForeignKey(Payment, verbose_name='facture associée', @@ -466,21 +604,21 @@ class PaymentAllocation(models.Model): verbose_name='montant') -def get_active_payment_and_invoices(member): +def get_active_payment_and_bills(member): # Fetch relevant and active payments / invoices # and sort then by chronological order : olders first, newers last. - this_member_invoices = [i for i in member.invoices.filter(validated=True).order_by("date")] + this_member_bills = Bill.get_member_validated_bills(member) this_member_payments = [p for p in member.payments.order_by("date")] # TODO / FIXME ^^^ maybe also consider only 'opened' invoices (i.e. not # conflict / trouble invoices) active_payments = [p for p in this_member_payments if p.amount_not_allocated() > 0] - active_invoices = [p for p in this_member_invoices if p.amount_remaining_to_pay() > 0] + active_bills = [p for p in this_member_bills if p.amount_remaining_to_pay() > 0] - return active_payments, active_invoices + return active_payments, active_bills def update_accounting_for_member(member): @@ -495,12 +633,12 @@ def update_accounting_for_member(member): accounting_log.info( "Member {} current balance is {} ...".format(member, member.balance)) - reconcile_invoices_and_payments(member) + reconcile_bills_and_payments(member) - this_member_invoices = [i for i in member.invoices.filter(validated=True).order_by("date")] + this_member_bills = Bill.get_member_validated_bills(member) this_member_payments = [p for p in member.payments.order_by("date")] - member.balance = compute_balance(this_member_invoices, + member.balance = compute_balance(this_member_bills, this_member_payments) member.save() @@ -508,22 +646,22 @@ def update_accounting_for_member(member): member, member.balance)) -def reconcile_invoices_and_payments(member): +def reconcile_bills_and_payments(member): """ Rapproche des factures et des paiements qui sont actifs (paiement non alloué ou factures non entièrement payées) automatiquement. """ - active_payments, active_invoices = get_active_payment_and_invoices(member) + active_payments, active_bills = get_active_payment_and_bills(member) if active_payments == []: accounting_log.info( "(No active payment for {}.".format(member) - + " No invoice/payment reconciliation needed.).") + + " No bill/payment reconciliation needed.).") return - elif active_invoices == []: + elif active_bills == []: accounting_log.info( - "(No active invoice for {}. No invoice/payment ".format(member) + + "(No active bill for {}. No bill/payment ".format(member) + "reconciliation needed.).") return @@ -531,32 +669,32 @@ def reconcile_invoices_and_payments(member): "Initiating reconciliation between invoice and payments for {}".format( member)) - while active_payments != [] and active_invoices != []: + while active_payments != [] and active_bills != []: - # Only consider the oldest active payment and the oldest active invoice + # Only consider the oldest active payment and the oldest active bill p = active_payments[0] - # If this payment is to be allocated for a specific invoice... - if p.invoice: + # If this payment is to be allocated for a specific bill... + if p.bill: # Assert that the invoice is still 'active' - assert p.invoice in active_invoices - i = p.invoice + assert p.bill in active_bills + i = p.bill accounting_log.info( - "Payment is to be allocated specifically to invoice {}".format( - i.number)) + "Payment is to be allocated specifically to bill {}".format( + i.pk)) else: - i = active_invoices[0] + i = active_bills[0] # TODO : should add an assert that the ammount not allocated / remaining to # pay is lower before and after calling the allocate_to_invoice - p.allocate_to_invoice(i) + p.allocate_to_bill(i) - active_payments, active_invoices = get_active_payment_and_invoices(member) + active_payments, active_bills = get_active_payment_and_bills(member) if active_payments == []: accounting_log.info("No more active payment. Nothing to reconcile anymore.") - elif active_invoices == []: + elif active_bills == []: accounting_log.info("No more active invoice. Nothing to reconcile anymore.") return @@ -594,35 +732,34 @@ def payment_changed(sender, instance, created, **kwargs): update_accounting_for_member(instance.member) -@receiver(post_save, sender=Invoice) +@receiver(post_save, sender=Bill) @disable_for_loaddata -def invoice_changed(sender, instance, created, **kwargs): +def bill_changed(sender, instance, created, **kwargs): - if created: - accounting_log.info( - "Creating draft invoice DRAFT-{} (Member: {}).".format( - instance.pk, instance.member)) - else: - if not instance.validated: - accounting_log.info( - "Updating draft invoice DRAFT-{} (Member: {}).".format( - instance.number, instance.member)) - else: - accounting_log.info( - "Updating invoice {} (Member: {}, Total amount: {}, Amount paid: {}).".format( - instance.number, instance.member, - instance.amount(), instance.amount_paid())) + instance.log_change(created) + +@receiver(post_save, sender=MembershipFee) +@disable_for_loaddata +def fee_changed(sender, instance, created, **kwargs): + if created and instance.member is not None: + update_accounting_for_member(instance.member) + +@receiver(post_save, sender=Donation) +@disable_for_loaddata +def fee_changed(sender, instance, created, **kwargs): + if created and instance.member is not None: + update_accounting_for_member(instance.member) @receiver(post_delete, sender=PaymentAllocation) def paymentallocation_deleted(sender, instance, **kwargs): - invoice = instance.invoice + bill = instance.bill # Reopen invoice if relevant - if (invoice.amount_remaining_to_pay() > 0) and (invoice.status == "closed"): - accounting_log.info("Reopening invoice {} ...".format(invoice.number)) - invoice.status = "open" - invoice.save() + if (bill.amount_remaining_to_pay() > 0) and (bill.status == "closed"): + accounting_log.info("Reopening bill {} ...".format(bill.number)) + bill.status = "open" + bill.save() @receiver(post_delete, sender=Payment) @@ -637,10 +774,10 @@ def payment_deleted(sender, instance, **kwargs): if member is None: return - this_member_invoices = [i for i in member.invoices.filter(validated=True).order_by("date")] + this_member_bills = Bill.get_member_validated_bills(member) this_member_payments = [p for p in member.payments.order_by("date")] - member.balance = compute_balance(this_member_invoices, + member.balance = compute_balance(this_member_bills, this_member_payments) member.save() diff --git a/coin/billing/tests.py b/coin/billing/tests.py index 487707d..7403311 100644 --- a/coin/billing/tests.py +++ b/coin/billing/tests.py @@ -9,7 +9,7 @@ from django.test import TestCase, Client, override_settings from freezegun import freeze_time from coin.members.tests import MemberTestsUtils from coin.members.models import Member, LdapUser -from coin.billing.models import Invoice, InvoiceQuerySet, InvoiceDetail, Payment +from coin.billing.models import Invoice, InvoiceQuerySet, InvoiceDetail, Payment, MembershipFee from coin.offers.models import Offer, OfferSubscription from coin.billing.create_subscriptions_invoices import create_member_invoice_for_a_period from coin.billing.create_subscriptions_invoices import create_all_members_invoices_for_a_period @@ -134,7 +134,7 @@ class BillingInvoiceCreationTests(TestCase): period_to=datetime.date(2014, 8, 31), tax=10) - self.assertEqual(invoice.amount(), 111) + self.assertEqual(invoice.amount, 111) def test_invoice_partial_payment(self): invoice = Invoice(member=self.member) @@ -151,7 +151,7 @@ class BillingInvoiceCreationTests(TestCase): self.assertEqual(invoice.status, 'open') p1 = Payment.objects.create(member=self.member, - invoice=invoice, + bill=invoice, payment_mean='cash', amount=10) p1.save() @@ -160,7 +160,7 @@ class BillingInvoiceCreationTests(TestCase): self.assertEqual(invoice.status, 'open') p2 = Payment.objects.create(member=self.member, - invoice=invoice, + bill=invoice, payment_mean='cash', amount=90) p2.save() @@ -366,7 +366,7 @@ class PaymentInvoiceAutoReconciliationTests(TestCase): member=johndoe) InvoiceDetail.objects.create(label="superservice", amount="15.0", - invoice=invoice) + bill=invoice) invoice.validate() # Second facture @@ -374,7 +374,7 @@ class PaymentInvoiceAutoReconciliationTests(TestCase): member=johndoe) InvoiceDetail.objects.create(label="superservice", amount="42", - invoice=invoice2) + bill=invoice2) invoice2.validate() # Payment @@ -387,3 +387,160 @@ class PaymentInvoiceAutoReconciliationTests(TestCase): johndoe.delete() +class MembershipFeeTests(TestCase): + def test_mandatory_start_date(self): + member = Member(first_name='foo', last_name='foo', password='foo', email='foo') + member.save() + + # If there is no start_date clean_fields() should raise an + # error but not clean(). + membershipfee = MembershipFee(member=member) + self.assertRaises(ValidationError, membershipfee.clean_fields) + self.assertIsNone(membershipfee.clean()) + + # If there is a start_date, everything is fine. + membershipfee = MembershipFee(member=member, start_date=date.today()) + self.assertIsNone(membershipfee.clean_fields()) + self.assertIsNone(membershipfee.clean()) + + member.delete() + + def test_member_end_date_of_memberhip(self): + """ + Test que end_date_of_membership d'un membre envoi bien la date de fin d'adhésion + """ + # Créer un membre + first_name = 'Tin' + last_name = 'Tin' + username = MemberTestsUtils.get_random_username() + member = Member(first_name=first_name, + last_name=last_name, username=username) + member.save() + + start_date = date.today() + end_date = start_date + relativedelta(years=+1) + + # Créé une cotisation + membershipfee = MembershipFee(member=member, amount=20, + start_date=start_date, + end_date=end_date) + membershipfee.save() + + self.assertEqual(member.end_date_of_membership(), end_date) + + def test_member_is_paid_up(self): + """ + Test l'état "a jour de cotisation" d'un adhérent. + """ + # Créé un membre + first_name = 'Capitain' + last_name = 'Haddock' + username = MemberTestsUtils.get_random_username() + member = Member(first_name=first_name, + last_name=last_name, username=username) + member.save() + + start_date = date.today() + end_date = start_date + relativedelta(years=+1) + + # Test qu'un membre sans cotisation n'est pas à jour + self.assertEqual(member.is_paid_up(), False) + + # Créé une cotisation passée + membershipfee = MembershipFee(member=member, amount=20, + start_date=date.today() + + relativedelta(years=-1), + end_date=date.today() + relativedelta(days=-10)) + membershipfee.save() + # La cotisation s'étant terminée il y a 10 jours, il ne devrait pas + # être à jour de cotistion + self.assertEqual(member.is_paid_up(), False) + + # Créé une cotisation actuelle + membershipfee = MembershipFee(member=member, amount=20, + start_date=date.today() + + relativedelta(days=-10), + end_date=date.today() + relativedelta(days=+10)) + membershipfee.save() + # La cotisation se terminant dans 10 jour, il devrait être à jour + # de cotisation + self.assertEqual(member.is_paid_up(), True) + + +class MemberTestCallForMembershipCommand(TestCase): + + def setUp(self): + # Créé un membre + self.username = MemberTestsUtils.get_random_username() + self.member = Member(first_name='Richard', last_name='Stallman', + username=self.username) + self.member.save() + + + def tearDown(self): + # Supprime le membre + self.member.delete() + MembershipFee.objects.all().delete() + + def create_membership_fee(self, end_date): + # Créé une cotisation passée se terminant dans un mois + membershipfee = MembershipFee(member=self.member, amount=20, + start_date=end_date + relativedelta(years=-1), + end_date=end_date) + membershipfee.save() + + def create_membership_fee(self, end_date): + # Créé une cotisation se terminant à la date indiquée + membershipfee = MembershipFee(member=self.member, amount=20, + start_date=end_date + relativedelta(years=-1), + end_date=end_date) + membershipfee.save() + return membershipfee + + def do_test_email_sent(self, expected_emails = 1, reset_date_last_call = True): + # Vide la outbox + mail.outbox = [] + # Call command + management.call_command('call_for_membership_fees', stdout=StringIO()) + # Test + self.assertEqual(len(mail.outbox), expected_emails) + # Comme on utilise le même membre, on reset la date de dernier envoi + if reset_date_last_call: + self.member.date_last_call_for_membership_fees_email = None + self.member.save() + + def do_test_for_a_end_date(self, end_date, expected_emails=1, reset_date_last_call = True): + # Supprimer toutes les cotisations (au cas ou) + MembershipFee.objects.all().delete() + # Créé la cotisation + membershipfee = self.create_membership_fee(end_date) + self.do_test_email_sent(expected_emails, reset_date_last_call) + membershipfee.delete() + + def test_call_email_sent_at_expected_dates(self): + # 1 mois avant la fin, à la fin et chaque mois après la fin pendant 3 mois + self.do_test_for_a_end_date(date.today() + relativedelta(months=+1)) + self.do_test_for_a_end_date(date.today()) + self.do_test_for_a_end_date(date.today() + relativedelta(months=-1)) + self.do_test_for_a_end_date(date.today() + relativedelta(months=-2)) + self.do_test_for_a_end_date(date.today() + relativedelta(months=-3)) + + def test_call_email_not_sent_if_active_membership_fee(self): + # Créé une cotisation se terminant dans un mois + membershipfee = self.create_membership_fee(date.today() + relativedelta(months=+1)) + # Un mail devrait être envoyé (ne pas vider date_last_call_for_membership_fees_email) + self.do_test_email_sent(1, False) + # Créé une cotisation enchainant et se terminant dans un an + membershipfee = self.create_membership_fee(date.today() + relativedelta(months=+1, years=+1)) + # Pas de mail envoyé + self.do_test_email_sent(0) + + def test_date_last_call_for_membership_fees_email(self): + # Créé une cotisation se terminant dans un mois + membershipfee = self.create_membership_fee(date.today() + relativedelta(months=+1)) + # Un mail envoyé (ne pas vider date_last_call_for_membership_fees_email) + self.do_test_email_sent(1, False) + # Tente un deuxième envoi, qui devrait être à 0 + self.do_test_email_sent(0) + + diff --git a/coin/billing/urls.py b/coin/billing/urls.py index cf32e5e..3520ccf 100644 --- a/coin/billing/urls.py +++ b/coin/billing/urls.py @@ -7,7 +7,7 @@ from coin.billing import views urlpatterns = patterns( '', - url(r'^invoice/(?P.+)/pdf$', views.invoice_pdf, name="invoice_pdf"), - url(r'^invoice/(?P.+)$', views.invoice, name="invoice"), + url(r'^bill/(?P.+)/pdf$', views.invoice_pdf, name="bill_pdf"), + url(r'^bill/(?P.+)$', views.invoice, name="bill"), # url(r'^invoice/(?P.+)/validate$', views.invoice_validate, name="invoice_validate"), ) diff --git a/coin/billing/utils.py b/coin/billing/utils.py index 86664f9..675f9a4 100644 --- a/coin/billing/utils.py +++ b/coin/billing/utils.py @@ -23,4 +23,4 @@ def assert_user_can_view_the_invoice(request, invoice): """ if not invoice.has_owner(request.user.username)\ and not request.user.is_superuser: - raise PermissionDenied \ No newline at end of file + raise PermissionDenied diff --git a/coin/members/admin.py b/coin/members/admin.py index 1556e36..13ed8ce 100644 --- a/coin/members/admin.py +++ b/coin/members/admin.py @@ -15,25 +15,16 @@ from django.core.urlresolvers import reverse from django.utils.html import format_html from coin.members.models import ( - Member, CryptoKey, LdapUser, MembershipFee, Offer, OfferSubscription, RowLevelPermission) -from coin.members.membershipfee_filter import MembershipFeeFilter + Member, CryptoKey, LdapUser, Offer, OfferSubscription, RowLevelPermission) from coin.members.forms import AdminMemberChangeForm, MemberCreationForm from coin.utils import delete_selected import autocomplete_light - class CryptoKeyInline(admin.StackedInline): model = CryptoKey extra = 0 -class MembershipFeeInline(admin.TabularInline): - model = MembershipFee - extra = 0 - fields = ('start_date', 'end_date', 'amount', 'payment_method', - 'reference', 'payment_date') - - class OfferSubscriptionInline(admin.TabularInline): model = OfferSubscription extra = 0 @@ -88,7 +79,7 @@ class MemberAdmin(UserAdmin): 'nickname', 'organization_name', 'email', 'end_date_of_membership') list_display_links = ('id', 'username', 'first_name', 'last_name') - list_filter = ('status', MembershipFeeFilter) + list_filter = () search_fields = ['username', 'first_name', 'last_name', 'email', 'nickname'] ordering = ('status', 'username') actions = [delete_selected, 'set_as_member', 'set_as_non_member', @@ -143,7 +134,7 @@ class MemberAdmin(UserAdmin): save_on_top = True - inlines = [CryptoKeyInline, MembershipFeeInline, OfferSubscriptionInline] + inlines = [CryptoKeyInline, OfferSubscriptionInline] def get_queryset(self, request): qs = super(MemberAdmin, self).get_queryset(request) @@ -258,11 +249,6 @@ class MemberAdmin(UserAdmin): bulk_send_call_for_membership_fee_email.short_description = 'Envoyer le courriel de relance de cotisation' -class MembershipFeeAdmin(admin.ModelAdmin): - list_display = ('member', 'end_date', 'amount', 'payment_method', - 'payment_date') - form = autocomplete_light.modelform_factory(MembershipFee, fields='__all__') - class RowLevelPermissionAdmin(admin.ModelAdmin): def get_changeform_initial_data(self, request): return {'content_type': ContentType.objects.get_for_model(OfferSubscription)} @@ -270,7 +256,6 @@ class RowLevelPermissionAdmin(admin.ModelAdmin): admin.site.register(Member, MemberAdmin) -admin.site.register(MembershipFee, MembershipFeeAdmin) # admin.site.unregister(Group) # admin.site.register(LdapUser, LdapUserAdmin) admin.site.register(RowLevelPermission, RowLevelPermissionAdmin) diff --git a/coin/members/migrations/0018_auto_20180414_2250.py b/coin/members/migrations/0018_auto_20180414_2250.py new file mode 100644 index 0000000..11e4411 --- /dev/null +++ b/coin/members/migrations/0018_auto_20180414_2250.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import coin.members.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0017_merge'), + ] + + operations = [ + migrations.AlterModelOptions( + name='rowlevelpermission', + options={'verbose_name': 'permission fine', 'verbose_name_plural': 'permissions fines'}, + ), + migrations.AlterModelManagers( + name='member', + managers=[ + ('objects', coin.members.models.MemberManager()), + ], + ), + migrations.AlterField( + model_name='member', + name='balance', + field=models.DecimalField(default=0, verbose_name='account balance', max_digits=6, decimal_places=2), + ), + ] diff --git a/coin/members/migrations/0019_auto_20180415_1814.py b/coin/members/migrations/0019_auto_20180415_1814.py new file mode 100644 index 0000000..2f3699f --- /dev/null +++ b/coin/members/migrations/0019_auto_20180415_1814.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0018_auto_20180414_2250'), + ] + + operations = [ + migrations.RemoveField( + model_name='membershipfee', + name='member', + ), + migrations.DeleteModel( + name='MembershipFee', + ), + ] diff --git a/coin/members/models.py b/coin/members/models.py index 5a2f834..5b9048a 100644 --- a/coin/members/models.py +++ b/coin/members/models.py @@ -139,7 +139,9 @@ class Member(CoinLdapSyncMixin, AbstractUser): # Renvoie la date de fin de la dernière cotisation du membre def end_date_of_membership(self): - aggregate = self.membership_fees.aggregate(end=Max('end_date')) + # Avoid import loop + from coin.billing.models import MembershipFee + aggregate = MembershipFee.objects.filter(member=self).aggregate(end=Max('end_date')) return aggregate['end'] end_date_of_membership.short_description = "Date de fin d'adhésion" diff --git a/coin/members/templates/members/invoices.html b/coin/members/templates/members/invoices.html index 8855551..2f62710 100644 --- a/coin/members/templates/members/invoices.html +++ b/coin/members/templates/members/invoices.html @@ -6,12 +6,12 @@

Balance : {{ balance|floatformat }} €

{% endif %} -

Mes factures

+

Mes factures et reçus

- + @@ -21,11 +21,11 @@ {% for invoice in invoices %} - + 0 %} class="unpaid"{% endif %}>{{ invoice.amount_remaining_to_pay }} - + {% empty %} diff --git a/coin/members/tests.py b/coin/members/tests.py index 2bbeacf..2d1c707 100644 --- a/coin/members/tests.py +++ b/coin/members/tests.py @@ -16,7 +16,7 @@ from django.contrib.auth.models import User from django.core import mail, management from django.core.exceptions import ValidationError -from coin.members.models import Member, MembershipFee, LdapUser +from coin.members.models import Member, LdapUser from coin.validation import chatroom_url_validator @@ -298,67 +298,6 @@ class MemberTests(TestCase): member.delete() - def test_member_end_date_of_memberhip(self): - """ - Test que end_date_of_membership d'un membre envoi bien la date de fin d'adhésion - """ - # Créer un membre - first_name = 'Tin' - last_name = 'Tin' - username = MemberTestsUtils.get_random_username() - member = Member(first_name=first_name, - last_name=last_name, username=username) - member.save() - - start_date = date.today() - end_date = start_date + relativedelta(years=+1) - - # Créé une cotisation - membershipfee = MembershipFee(member=member, amount=20, - start_date=start_date, - end_date=end_date) - membershipfee.save() - - self.assertEqual(member.end_date_of_membership(), end_date) - - def test_member_is_paid_up(self): - """ - Test l'état "a jour de cotisation" d'un adhérent. - """ - # Créé un membre - first_name = 'Capitain' - last_name = 'Haddock' - username = MemberTestsUtils.get_random_username() - member = Member(first_name=first_name, - last_name=last_name, username=username) - member.save() - - start_date = date.today() - end_date = start_date + relativedelta(years=+1) - - # Test qu'un membre sans cotisation n'est pas à jour - self.assertEqual(member.is_paid_up(), False) - - # Créé une cotisation passée - membershipfee = MembershipFee(member=member, amount=20, - start_date=date.today() + - relativedelta(years=-1), - end_date=date.today() + relativedelta(days=-10)) - membershipfee.save() - # La cotisation s'étant terminée il y a 10 jours, il ne devrait pas - # être à jour de cotistion - self.assertEqual(member.is_paid_up(), False) - - # Créé une cotisation actuelle - membershipfee = MembershipFee(member=member, amount=20, - start_date=date.today() + - relativedelta(days=-10), - end_date=date.today() + relativedelta(days=+10)) - membershipfee.save() - # La cotisation se terminant dans 10 jour, il devrait être à jour - # de cotisation - self.assertEqual(member.is_paid_up(), True) - def test_member_cant_be_created_without_names(self): """ Test qu'un membre ne peut pas être créé sans "noms" @@ -374,7 +313,6 @@ class MemberTests(TestCase): member.save() - class MemberAdminTests(TestCase): def setUp(self): @@ -417,83 +355,6 @@ class MemberAdminTests(TestCase): member.delete() -class MemberTestCallForMembershipCommand(TestCase): - - def setUp(self): - # Créé un membre - self.username = MemberTestsUtils.get_random_username() - self.member = Member(first_name='Richard', last_name='Stallman', - username=self.username) - self.member.save() - - - def tearDown(self): - # Supprime le membre - self.member.delete() - MembershipFee.objects.all().delete() - - def create_membership_fee(self, end_date): - # Créé une cotisation passée se terminant dans un mois - membershipfee = MembershipFee(member=self.member, amount=20, - start_date=end_date + relativedelta(years=-1), - end_date=end_date) - membershipfee.save() - - def create_membership_fee(self, end_date): - # Créé une cotisation se terminant à la date indiquée - membershipfee = MembershipFee(member=self.member, amount=20, - start_date=end_date + relativedelta(years=-1), - end_date=end_date) - membershipfee.save() - return membershipfee - - def do_test_email_sent(self, expected_emails = 1, reset_date_last_call = True): - # Vide la outbox - mail.outbox = [] - # Call command - management.call_command('call_for_membership_fees', stdout=StringIO()) - # Test - self.assertEqual(len(mail.outbox), expected_emails) - # Comme on utilise le même membre, on reset la date de dernier envoi - if reset_date_last_call: - self.member.date_last_call_for_membership_fees_email = None - self.member.save() - - def do_test_for_a_end_date(self, end_date, expected_emails=1, reset_date_last_call = True): - # Supprimer toutes les cotisations (au cas ou) - MembershipFee.objects.all().delete() - # Créé la cotisation - membershipfee = self.create_membership_fee(end_date) - self.do_test_email_sent(expected_emails, reset_date_last_call) - membershipfee.delete() - - def test_call_email_sent_at_expected_dates(self): - # 1 mois avant la fin, à la fin et chaque mois après la fin pendant 3 mois - self.do_test_for_a_end_date(date.today() + relativedelta(months=+1)) - self.do_test_for_a_end_date(date.today()) - self.do_test_for_a_end_date(date.today() + relativedelta(months=-1)) - self.do_test_for_a_end_date(date.today() + relativedelta(months=-2)) - self.do_test_for_a_end_date(date.today() + relativedelta(months=-3)) - - def test_call_email_not_sent_if_active_membership_fee(self): - # Créé une cotisation se terminant dans un mois - membershipfee = self.create_membership_fee(date.today() + relativedelta(months=+1)) - # Un mail devrait être envoyé (ne pas vider date_last_call_for_membership_fees_email) - self.do_test_email_sent(1, False) - # Créé une cotisation enchainant et se terminant dans un an - membershipfee = self.create_membership_fee(date.today() + relativedelta(months=+1, years=+1)) - # Pas de mail envoyé - self.do_test_email_sent(0) - - def test_date_last_call_for_membership_fees_email(self): - # Créé une cotisation se terminant dans un mois - membershipfee = self.create_membership_fee(date.today() + relativedelta(months=+1)) - # Un mail envoyé (ne pas vider date_last_call_for_membership_fees_email) - self.do_test_email_sent(1, False) - # Tente un deuxième envoi, qui devrait être à 0 - self.do_test_email_sent(0) - - class MemberTestsUtils(object): @staticmethod @@ -510,21 +371,3 @@ class TestValidators(TestCase): with self.assertRaises(ValidationError): chatroom_url_validator('http://#faimaison@irc.geeknode.org') - -class MembershipFeeTests(TestCase): - def test_mandatory_start_date(self): - member = Member(first_name='foo', last_name='foo', password='foo', email='foo') - member.save() - - # If there is no start_date clean_fields() should raise an - # error but not clean(). - membershipfee = MembershipFee(member=member) - self.assertRaises(ValidationError, membershipfee.clean_fields) - self.assertIsNone(membershipfee.clean()) - - # If there is a start_date, everything is fine. - membershipfee = MembershipFee(member=member, start_date=date.today()) - self.assertIsNone(membershipfee.clean_fields()) - self.assertIsNone(membershipfee.clean()) - - member.delete() diff --git a/coin/members/views.py b/coin/members/views.py index 154964c..a02cbec 100644 --- a/coin/members/views.py +++ b/coin/members/views.py @@ -6,6 +6,7 @@ from django.shortcuts import render from django.contrib.auth.decorators import login_required from django.conf import settings from forms import PersonMemberChangeForm, OrganizationMemberChangeForm +from coin.billing.models import Bill @login_required def index(request): @@ -53,7 +54,7 @@ def subscriptions(request): @login_required def invoices(request): balance = request.user.balance - invoices = request.user.invoices.filter(validated=True).order_by('-date') + invoices = Bill.get_member_validated_bills(request.user) payments = request.user.payments.filter().order_by('-date') return render(request, 'members/invoices.html', -- GitLab From 0f12ca87c48699b07a481549333fda5309b51281 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 14 Apr 2018 22:33:55 +0200 Subject: [PATCH 011/195] Use more digits for payment amounts and member balance (otherwise can't handle payment more than 999) --- coin/billing/models.py | 2 +- coin/members/models.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/coin/billing/models.py b/coin/billing/models.py index a392e97..75ccff2 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -509,7 +509,7 @@ class Payment(models.Model): default='transfer', choices=PAYMENT_MEAN_CHOICES, verbose_name='moyen de paiement') - amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, + amount = models.DecimalField(max_digits=6, decimal_places=2, null=True, verbose_name='montant') date = models.DateField(default=datetime.date.today) bill = models.ForeignKey(Bill, verbose_name='facture associée', null=True, diff --git a/coin/members/models.py b/coin/members/models.py index 5b9048a..379c319 100644 --- a/coin/members/models.py +++ b/coin/members/models.py @@ -95,10 +95,11 @@ class Member(CoinLdapSyncMixin, AbstractUser): date_last_call_for_membership_fees_email = models.DateTimeField(null=True, blank=True, verbose_name="Date du dernier email de relance de cotisation envoyé") + send_membership_fees_email = models.BooleanField( default=True, verbose_name='relance de cotisation', help_text='Précise si l\'utilisateur doit recevoir des mails de relance pour la cotisation. Certains membres n\'ont pas à recevoir de relance (prélèvement automatique, membres d\'honneurs, etc.)') - balance = models.DecimalField(max_digits=5, decimal_places=2, default=0, + balance = models.DecimalField(max_digits=6, decimal_places=2, default=0, verbose_name='account balance') objects = MemberManager() -- GitLab From 3981ea8913dbec355b2a647c236afbc46ce906c7 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 16 Apr 2018 00:43:48 +0200 Subject: [PATCH 012/195] [fix] Replace invoice by bill --- coin/billing/models.py | 15 +++++++-------- .../admin/billing/invoice/change_form.html | 2 +- coin/billing/templates/billing/invoice.html | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/coin/billing/models.py b/coin/billing/models.py index 75ccff2..e74a424 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -443,13 +443,12 @@ class Donation(Bill): def save(self, *args, **kwargs): - super(Donation, self).save(*args, **kwargs) - - def clean(self): # Only if no amount already allocated... - if not self.member or self.member.balance < self.amount: + if self.pk is None and (not self.member or self.member.balance < self.amount): raise ValidationError("Le solde n'est pas suffisant pour payer ce don. \ Merci de commencer par enregistrer un paiement pour ce membre.") + super(Donation, self).save(*args, **kwargs) + class Meta: verbose_name = 'don' @@ -476,6 +475,10 @@ class MembershipFee(Bill): return True def save(self, *args, **kwargs): + # Only if no amount already allocated... + if self.pk is None and (not self.member or self.member.balance < self.amount): + raise ValidationError("Le solde n'est pas suffisant pour payer cette cotisation. \ + Merci de commencer par enregistrer un paiement pour ce membre.") super(MembershipFee, self).save(*args, **kwargs) @@ -483,10 +486,6 @@ class MembershipFee(Bill): def clean(self): if self.start_date is not None and self.end_date is None: self.end_date = self.start_date + datetime.timedelta(364) - # Only if no amount already allocated... - if not self.member or self.member.balance < self.amount: - raise ValidationError("Le solde n'est pas suffisant pour payer cette cotisation. \ - Merci de commencer par enregistrer un paiement pour ce membre.") class Meta: verbose_name = 'cotisation' diff --git a/coin/billing/templates/admin/billing/invoice/change_form.html b/coin/billing/templates/admin/billing/invoice/change_form.html index 281786e..15f2c64 100644 --- a/coin/billing/templates/admin/billing/invoice/change_form.html +++ b/coin/billing/templates/admin/billing/invoice/change_form.html @@ -4,7 +4,7 @@ {% if not original.validated %}
  • Valider la facture
  • {% elif original.validated %} -
  • Télécharger le PDF
  • +
  • Télécharger le PDF
  • {% endif %} {{ block.super }} {% endblock %} diff --git a/coin/billing/templates/billing/invoice.html b/coin/billing/templates/billing/invoice.html index 0f77044..10f5469 100644 --- a/coin/billing/templates/billing/invoice.html +++ b/coin/billing/templates/billing/invoice.html @@ -7,7 +7,7 @@

    Émise le {{ invoice.date }}

    - {% if invoice.validated %} Télécharger en PDF{% endif %} + {% if invoice.validated %} Télécharger en PDF{% endif %}
    -- GitLab From a30d233a2f67eda7de49bbefa8fb08fdb94d09ee Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 16 Apr 2018 01:15:29 +0200 Subject: [PATCH 013/195] [fix] Increase maximum amount --- coin/billing/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/coin/billing/models.py b/coin/billing/models.py index e74a424..f70726d 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -390,7 +390,7 @@ class Invoice(Bill): class InvoiceDetail(models.Model): label = models.CharField(max_length=100) - amount = models.DecimalField(max_digits=5, decimal_places=2, + amount = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='montant') quantity = models.DecimalField(null=True, verbose_name='quantité', default=1.0, decimal_places=2, max_digits=4) @@ -453,7 +453,7 @@ class Donation(Bill): verbose_name = 'don' class MembershipFee(Bill): - _amount = models.DecimalField(null=False, max_digits=5, decimal_places=2, + _amount = models.DecimalField(null=False, max_digits=8, decimal_places=2, default=settings.MEMBER_DEFAULT_COTISATION, verbose_name='montant', help_text='en €') start_date = models.DateField( @@ -508,7 +508,7 @@ class Payment(models.Model): default='transfer', choices=PAYMENT_MEAN_CHOICES, verbose_name='moyen de paiement') - amount = models.DecimalField(max_digits=6, decimal_places=2, null=True, + amount = models.DecimalField(max_digits=8, decimal_places=2, null=True, verbose_name='montant') date = models.DateField(default=datetime.date.today) bill = models.ForeignKey(Bill, verbose_name='facture associée', null=True, @@ -599,7 +599,7 @@ class PaymentAllocation(models.Model): payment = models.ForeignKey(Payment, verbose_name='facture associée', null=False, blank=False, related_name='allocations') - amount = models.DecimalField(max_digits=5, decimal_places=2, null=True, + amount = models.DecimalField(max_digits=8, decimal_places=2, null=True, verbose_name='montant') -- GitLab From 3d1e78a7a3377259ecc343c3f10f5913e91d829f Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 16 Apr 2018 01:55:22 +0200 Subject: [PATCH 014/195] [fix] Filter by membership fee --- coin/billing/membershipfee_filter.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/coin/billing/membershipfee_filter.py b/coin/billing/membershipfee_filter.py index 51c2d6d..8c3adae 100644 --- a/coin/billing/membershipfee_filter.py +++ b/coin/billing/membershipfee_filter.py @@ -33,10 +33,6 @@ class MembershipFeeFilter(SimpleListFilter): `self.value()`. """ if self.value() == 'paidup': - return queryset.filter( - membership_fees__start_date__lte=datetime.date.today, - membership_fees__end_date__gte=datetime.date.today) + return queryset.filter(id__in=[i.id for i in queryset.all() if i.is_paid_up()]) if self.value() == 'late': - return queryset.filter(status='member').exclude( - membership_fees__start_date__lte=datetime.date.today, - membership_fees__end_date__gte=datetime.date.today) + return queryset.filter(id__in=[i.id for i in queryset.all() if not i.is_paid_up()]) -- GitLab From c89f04082be1e0cab6dc90c27dc445539353f6ac Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 2 Aug 2017 13:09:44 +0000 Subject: [PATCH 015/195] Test tweaking of the CSS / template for ARN --- coin/static/css/local.css | 15 ++- coin/static/img/logo_ARN.svg | 233 +++++++++++++++++++++++++++++++++++ coin/templates/base.html | 9 +- 3 files changed, 253 insertions(+), 4 deletions(-) create mode 100644 coin/static/img/logo_ARN.svg diff --git a/coin/static/css/local.css b/coin/static/css/local.css index 2f1632e..b528dbd 100644 --- a/coin/static/css/local.css +++ b/coin/static/css/local.css @@ -41,8 +41,14 @@ h1 a, h1:after { /* Barre de navigation */ .side-nav { - background-color: #FAFAFA; + /* background-color: #FAFAFA; */ padding:5px; + border-right: 1px solid #dddddd; + margin-top: 5em; +} + +.side-nav li { + font-size: 1.1rem; } @@ -112,7 +118,8 @@ h2:before { } h2 { - color: #FF6600; + color: #0099aa; + font-weight: bold; /*border-bottom: 2px solid #0086A9;*/ } @@ -136,6 +143,10 @@ h2 { /* Tables */ +table { + border: none; +} + table.full-width { width:100%; } diff --git a/coin/static/img/logo_ARN.svg b/coin/static/img/logo_ARN.svg new file mode 100644 index 0000000..e082eff --- /dev/null +++ b/coin/static/img/logo_ARN.svg @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/coin/templates/base.html b/coin/templates/base.html index 2125e87..7dff650 100644 --- a/coin/templates/base.html +++ b/coin/templates/base.html @@ -4,7 +4,7 @@ - {% block title %}COIN - {{ branding.shortname|capfirst }} SI{% endblock %} + {% block title %}ARN - Espace adhéhrent{% endblock %} @@ -31,7 +31,12 @@
    -

    COIN est un Outil pour un Internet Neutre

    +
    + +
    +
    + Espace adhérent +
    -- GitLab From d1d82cbc68052b1808419fcec96dcd6e450100dd Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Apr 2018 04:04:14 +0200 Subject: [PATCH 016/195] Tweak invoice and payment howto for ARN --- arn/templates/billing/invoice.html | 77 ++++++++ arn/templates/billing/invoice_pdf.html | 234 +++++++++++++++++++++++ arn/templates/billing/payment_howto.html | 25 +++ coin/members/views.py | 3 +- coin/settings_base.py | 2 +- 5 files changed, 339 insertions(+), 2 deletions(-) create mode 100644 arn/templates/billing/invoice.html create mode 100644 arn/templates/billing/invoice_pdf.html create mode 100644 arn/templates/billing/payment_howto.html diff --git a/arn/templates/billing/invoice.html b/arn/templates/billing/invoice.html new file mode 100644 index 0000000..0f77044 --- /dev/null +++ b/arn/templates/billing/invoice.html @@ -0,0 +1,77 @@ +{% extends "base.html" %} + +{% block content %} +
    +
    +

    Facture N°{{ invoice.number }}

    +

    Émise le {{ invoice.date }}

    +
    +
    + {% if invoice.validated %} Télécharger en PDF{% endif %} +
    +
    + +
    NuméroRéférence Date Montant Reste à payer
    {{ invoice.number }}{{ invoice.reference }} {{ invoice.date }} {{ invoice.amount }} {% if invoice.validated %} PDF{% endif %}{% if invoice.validated %} PDF{% endif %}
    Aucune facture.
    + + + + + + + + + + {% for detail in invoice.details.all %} + + + + + + + {% endfor %} + + + + + +
    QuantitéPUTotal TTC
    {{ detail.label }} + {% if detail.period_from and detail.period_to %}
    Pour la période du {{ detail.period_from }} au {{ detail.period_to }}{% endif %}
    {{ detail.quantity }}{{ detail.amount }}€{{ detail.total }}€
    Total TTC{{ invoice.amount }}€
    + +

    + Facture à payer avant le {{ invoice.date_due }}. +

    + +

    Règlement

    + +{% if invoice.payments.exists %} + + + + + + + + + + {% for payment in invoice.payments.all %} + + + + + + {% endfor %} + + + + + +
    Type de paiementDateMontant
    {{ payment.get_payment_mean_display }}{{ payment.date }}-{{ payment.amount }}€
    Reste à payer{{ invoice.amount_remaining_to_pay }}€
    +{% endif %} + +{% if invoice.amount_remaining_to_pay > 0 %} +
    + {% include "billing/payment_howto.html" %} +
    +{% endif %} + +{% endblock %} diff --git a/arn/templates/billing/invoice_pdf.html b/arn/templates/billing/invoice_pdf.html new file mode 100644 index 0000000..02a3488 --- /dev/null +++ b/arn/templates/billing/invoice_pdf.html @@ -0,0 +1,234 @@ +{% load static isptags %} + + + Facture N°{{ invoice.number }} + + + + + + + + + + + + + + + + + +
    +

    + {% multiline_isp_addr branding %} +

    +

    + {{ branding.email }}
    + {{ branding.website }}
    + {{ branding.phone_number }}
    + SIRET : {{ branding.registeredoffice.siret }} +

    +
    +

    + Facturé à
    + {% with member=invoice.member %} + {{ member.last_name }} {{ member.first_name }}
    + {% if member.organization_name != "" %}{{ member.organization_name }}
    {% endif %} + {% if member.address %}{{member.address}}
    {% endif %} + {% if member.postal_code and member.city %} + {{ member.postal_code }} {{ member.city }} + {% endif %} + {% endwith %} +

    +
    + + + + + + + + + + + + + {% for detail in invoice.details.all %} + + + + + + + + {% endfor %} + + + + + + + + + + + + + +
    QuantitéPU (HT)TVATotal
    +

    + {{ detail.label }} + {% if detail.offersubscription %} +
    + {{ detail.offersubscription.offer.name }} + {% if detail.offersubscription.offer.reference %} ({{ detail.offersubscription.get_subscription_reference }}){% endif %} + + {% endif %} +

    + {% if detail.period_from and detail.period_to %} +

    Pour la période du {{ detail.period_from }} au {{ detail.period_to }}

    + {% endif %} +
    {{ detail.quantity }}{{ detail.amount }}€{{ detail.tax }}%{{ detail.total }}€
    Total HT{{ invoice.amount_before_tax }}€
    Total TTC{{ invoice.amount }}€
    + +

    À payer avant le {{ invoice.date_due }}.

    + +
    + {% include "billing/payment_howto.html" %} +
    + +
    +

    /

    +

    {{ branding.shortname|upper }} est une association de droit local alsacien-mosellan à but non lucratif.

    +
    + + diff --git a/arn/templates/billing/payment_howto.html b/arn/templates/billing/payment_howto.html new file mode 100644 index 0000000..ded3e44 --- /dev/null +++ b/arn/templates/billing/payment_howto.html @@ -0,0 +1,25 @@ +{% load isptags %} + +

    + Merci de payer par virement bancaire
    +
    + Titulaire du compte : {% firstof branding.shortname branding.name %}
    + IBAN : {{ branding.bankinfo.iban|pretty_iban }}
    + {% if branding.bankinfo.bic %} + BIC : {{ branding.bankinfo.bic }}
    + {% endif %} + +
    + + {% if invoice %} + Prière de faire figurer la reference suivante sur vos virement :
    + {% with member=invoice.member %} + ID {{ member.pk }} et ou {{ member.username }}
    + {% endwith %} + {% endif %} + + {% if member %} + Prière de faire figurer la reference suivante sur vos virement :
    + ID {{ member.pk }} et ou {{ member.username }}
    + {% endif %} +

    diff --git a/coin/members/views.py b/coin/members/views.py index 154964c..c7fd65b 100644 --- a/coin/members/views.py +++ b/coin/members/views.py @@ -60,7 +60,8 @@ def invoices(request): {'balance' : balance, 'handle_balance' : settings.HANDLE_BALANCE, 'invoices': invoices, - 'payments': payments}) + 'payments': payments, + 'member': request.user}) @login_required diff --git a/coin/settings_base.py b/coin/settings_base.py index b0c44ab..0181f3e 100644 --- a/coin/settings_base.py +++ b/coin/settings_base.py @@ -146,7 +146,7 @@ TEMPLATE_DIRS = ( os.path.join(PROJECT_PATH, 'templates/'), ) -EXTRA_TEMPLATE_DIRS = tuple() +EXTRA_TEMPLATE_DIRS = ('./arn/templates',) INSTALLED_APPS = ( 'debug_toolbar', # always installed, but enabled only if DEBUG=True -- GitLab From 7ae3337f7429a63df299e375fa0597622a866c2e Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 19 Aug 2017 23:05:03 +0200 Subject: [PATCH 017/195] [fix] Specific ARN exclude some ips --- coin/resources/models.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/coin/resources/models.py b/coin/resources/models.py index 69211d7..c671f1b 100644 --- a/coin/resources/models.py +++ b/coin/resources/models.py @@ -65,6 +65,13 @@ class IPSubnet(models.Model): pool = IPSet([self.ip_pool.inet]) used = IPSet((s.inet for s in self.ip_pool.ipsubnet_set.all())) free = pool.difference(used) + free = free.difference(IPSet([ + IPNetwork('89.234.141.0/31'), + IPAddress('2a00:5881:8100:0100::0'), + IPAddress('2a00:5881:8100:0100::1'), + IPNetwork('2a00:5881:8118:0000::/56'), + IPNetwork('2a00:5881:8118:0100::/56'), + ])) # Generator for efficiency (we don't build the whole list) available = (p for p in free.iter_cidrs() if p.prefixlen <= self.ip_pool.default_subnetsize) # TODO: for IPv4, get rid of the network and broadcast -- GitLab From cad0cc87315dc5801a845c14aa028d372509e8ab Mon Sep 17 00:00:00 2001 From: root Date: Mon, 16 Apr 2018 02:09:46 +0200 Subject: [PATCH 018/195] [fix] typo --- coin/templates/base.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coin/templates/base.html b/coin/templates/base.html index 7dff650..5306d98 100644 --- a/coin/templates/base.html +++ b/coin/templates/base.html @@ -4,7 +4,7 @@ - {% block title %}ARN - Espace adhéhrent{% endblock %} + {% block title %}ARN - Espace adhérent{% endblock %} -- GitLab From 621c26d1734f9035b44272a090bc491845bbeef1 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 16 Apr 2018 01:04:15 +0200 Subject: [PATCH 019/195] [enh] Temporary credit note feature --- coin/billing/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/coin/billing/models.py b/coin/billing/models.py index 0d946c0..b27209c 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -359,6 +359,7 @@ class Payment(models.Model): ('cash', 'Espèces'), ('check', 'Chèque'), ('transfer', 'Virement'), + ('creditnote', 'Avoir'), ('other', 'Autre') ) -- GitLab From af7f3c8bab1d763a1cc24417d9c3ffc09a8227b8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Apr 2018 17:58:58 +0200 Subject: [PATCH 020/195] Iteration on the invoice template --- arn/templates/billing/invoice_pdf.html | 9 +++++---- arn/templates/billing/payment_howto.html | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arn/templates/billing/invoice_pdf.html b/arn/templates/billing/invoice_pdf.html index 02a3488..b6187cd 100644 --- a/arn/templates/billing/invoice_pdf.html +++ b/arn/templates/billing/invoice_pdf.html @@ -48,8 +48,8 @@ } #header .logo { - height: 35pt; - margin: 0 auto 20pt; + height: 50pt; + margin: 0; } #header .header-left { @@ -137,7 +137,7 @@ -

    Facture N°{{ invoice.number }}

    +

    Facture N°{{ invoice.number }}

    Date : {{ invoice.date }}

    @@ -220,7 +220,8 @@ -

    À payer avant le {{ invoice.date_due }}.

    +

    TVA non applicable - article 293 B du CGI

    +

    À payer sans escompte avant le {{ invoice.date_due }}.

    {% include "billing/payment_howto.html" %} diff --git a/arn/templates/billing/payment_howto.html b/arn/templates/billing/payment_howto.html index ded3e44..dbbaa51 100644 --- a/arn/templates/billing/payment_howto.html +++ b/arn/templates/billing/payment_howto.html @@ -12,14 +12,14 @@
    {% if invoice %} - Prière de faire figurer la reference suivante sur vos virement :
    + Prière de faire figurer l'une de vos reference adherent sur vos virement :
    {% with member=invoice.member %} ID {{ member.pk }} et ou {{ member.username }}
    {% endwith %} {% endif %} {% if member %} - Prière de faire figurer la reference suivante sur vos virement :
    + Prière de faire figurer l'une de vos reference adherent sur vos virement :
    ID {{ member.pk }} et ou {{ member.username }}
    {% endif %}

    -- GitLab From 002d01e688b85ed105fa2333f12cb336e957cb52 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 15 Apr 2018 17:36:43 +0200 Subject: [PATCH 021/195] Add a --antidate option to charge_subscriptions action --- coin/billing/create_subscriptions_invoices.py | 13 ++++--- .../commands/charge_subscriptions.py | 34 +++++++++++++++++-- coin/billing/models.py | 6 ++-- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/coin/billing/create_subscriptions_invoices.py b/coin/billing/create_subscriptions_invoices.py index 36f5a32..4f070dd 100644 --- a/coin/billing/create_subscriptions_invoices.py +++ b/coin/billing/create_subscriptions_invoices.py @@ -13,7 +13,7 @@ from coin.members.models import Member from coin.billing.models import Invoice, InvoiceDetail from django.conf import settings -def create_all_members_invoices_for_a_period(date=None): +def create_all_members_invoices_for_a_period(date=None, antidate=False): """ Pour chaque membre ayant au moins un abonnement actif, génère les factures en prenant la date comme premier mois de la période de facturation @@ -27,14 +27,14 @@ def create_all_members_invoices_for_a_period(date=None): invoices = [] for member in members: - invoice = create_member_invoice_for_a_period(member, date) + invoice = create_member_invoice_for_a_period(member, date, antidate) if invoice is not None: invoices.append(invoice) return invoices @transaction.atomic -def create_member_invoice_for_a_period(member, date): +def create_member_invoice_for_a_period(member, date, antidate): """ Créé si nécessaire une facture pour un membre en prenant la date passée en paramètre comme premier mois de période. Renvoi la facture générée @@ -157,7 +157,12 @@ def create_member_invoice_for_a_period(member, date): if invoice.details.count() > 0: invoice.save() transaction.savepoint_commit(sid) - invoice.validate() # Valide la facture et génère le PDF + # Valide la facture et génère le PDF + if antidate: + invoice.date_due = None # (reset the due date, will automatically be redefined when validating) + invoice.validate(period_to) + else: + invoice.validate() return invoice else: transaction.savepoint_rollback(sid) diff --git a/coin/billing/management/commands/charge_subscriptions.py b/coin/billing/management/commands/charge_subscriptions.py index e248f65..a2327d3 100644 --- a/coin/billing/management/commands/charge_subscriptions.py +++ b/coin/billing/management/commands/charge_subscriptions.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- import datetime + +from argparse import RawTextHelpFormatter from django.core.management.base import BaseCommand, CommandError from django.conf import settings @@ -8,13 +10,39 @@ from coin.billing.create_subscriptions_invoices import create_all_members_invoic class Command(BaseCommand): - args = '[date=2011-07-04]' + help = 'Create invoices for members subscriptions for date specified (or today if no date passed)' + def create_parser(self, *args, **kwargs): + parser = super(Command, self).create_parser(*args, **kwargs) + parser.formatter_class = RawTextHelpFormatter + return parser + + def add_arguments(self, parser): + + parser.add_argument( + 'date', + type=str, + help="The date for the period for which to charge subscription (e.g. 2011-07-04)" + ) + + parser.add_argument( + '--antidate', + action='store_true', + dest='antidate', + default=False, + help="'Antidate' invoices, in the sense that invoices won't be validated with today's date but using the date of the end of the service. Meant to be use to charge subscription from a few months in the past..." + ) + + + def handle(self, *args, **options): verbosity = int(options['verbosity']) + antidate = options['antidate'] + date = options["date"] + try: - date = datetime.datetime.strptime(args[0], '%Y-%m-%d').date() + date = datetime.datetime.strptime(date, '%Y-%m-%d').date() except IndexError: date = datetime.date.today() except ValueError: @@ -25,7 +53,7 @@ class Command(BaseCommand): self.stdout.write( 'Create invoices for all members for the date : %s' % date) with respect_language(settings.LANGUAGE_CODE): - invoices = create_all_members_invoices_for_a_period(date) + invoices = create_all_members_invoices_for_a_period(date, antidate) if len(invoices) > 0 or verbosity >= 2: self.stdout.write( diff --git a/coin/billing/models.py b/coin/billing/models.py index b27209c..24c62b5 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -209,12 +209,14 @@ class Invoice(models.Model): self.pdf.save('%s.pdf' % self.number, pdf_file) @transaction.atomic - def validate(self): + def validate(self, custom_date=None): """ Switch invoice to validate mode. This set to False the draft field and generate the pdf """ - self.date = datetime.date.today() + + self.date = custom_date or datetime.date.today() + if not self.date_due: self.date_due = self.date + datetime.timedelta(days=settings.PAYMENT_DELAY) old_number = self.number -- GitLab From d0835194fce7ee3f36895ad4fe19c1a79bb07579 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 16 Apr 2018 00:47:06 +0200 Subject: [PATCH 022/195] [fix] Replace invoice by bill --- arn/templates/billing/invoice.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arn/templates/billing/invoice.html b/arn/templates/billing/invoice.html index 0f77044..10f5469 100644 --- a/arn/templates/billing/invoice.html +++ b/arn/templates/billing/invoice.html @@ -7,7 +7,7 @@

    Émise le {{ invoice.date }}

    - {% if invoice.validated %} Télécharger en PDF{% endif %} + {% if invoice.validated %} Télécharger en PDF{% endif %}
    -- GitLab From 5d3eeabea16a4428cabcafa8819c213618bc5413 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 19 Aug 2017 20:32:07 +0000 Subject: [PATCH 023/195] Working logs for IP allocations --- coin/configuration/models.py | 57 +++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 8f54268..350547c 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +import logging + from django.db import models from polymorphic import PolymorphicModel from coin.offers.models import OfferSubscription @@ -17,7 +19,7 @@ technical informations of a subscription. To add a new configuration backend, you have to create a new app with a model which inherit from Configuration. -Your model can implement Meta verbose_name to have human readable name and a +Your model can implement Meta verbose_name to have human readable name and a url_namespace variable to specify the url namespace used by this model. """ @@ -35,9 +37,9 @@ class Configuration(PolymorphicModel): Génère automatiquement la liste de choix possibles de configurations en fonction des classes enfants de Configuration """ - return tuple((x().__class__.__name__,x()._meta.verbose_name) + return tuple((x().__class__.__name__,x()._meta.verbose_name) for x in Configuration.__subclasses__()) - + def model_name(self): return self.__class__.__name__ model_name.short_description = 'Nom du modèle' @@ -52,7 +54,7 @@ class Configuration(PolymorphicModel): Une url doit être nommée "details" """ from django.core.urlresolvers import reverse - return reverse('%s:details' % self.get_url_namespace(), + return reverse('%s:details' % self.get_url_namespace(), args=[str(self.id)]) def get_url_namespace(self): @@ -70,8 +72,38 @@ class Configuration(PolymorphicModel): verbose_name = 'configuration' +@receiver(post_save, sender=OfferSubscription) +def offer_subscription_event(sender, **kwargs): + os = kwargs['instance'] + + if not hasattr(os, 'configuration'): + config_cls = None + for subconfig_cls in Configuration.__subclasses__(): + if subconfig_cls().__class__.__name__ == os.offer.configuration_type: + config_cls = subconfig_cls + break + + if config_cls is not None: + config = config_cls.objects.create(offersubscription=os) + for offer_ip_pool in os.offer.offerippool_set.order_by('-to_assign'): + IPSubnet.objects.create( + configuration=config, + ip_pool=offer_ip_pool.ip_pool) + config.save() + + @receiver(post_save, sender=IPSubnet) +def subnet_event_save(sender, **kwargs): + kwargs["signal_type"] = "save" + subnet_event(sender, **kwargs) + + @receiver(post_delete, sender=IPSubnet) +def subnet_event_delete(sender, **kwargs): + kwargs["signal_type"] = "delete" + subnet_event(sender, **kwargs) + +subnet_log = logging.getLogger("coin.subnets") def subnet_event(sender, **kwargs): """Fires when a subnet is created, modified or deleted. We tell the configuration backend to do whatever it needs to do with it. @@ -105,5 +137,22 @@ def subnet_event(sender, **kwargs): config = subnet.configuration if hasattr(config, 'subnet_event'): config.subnet_event() + + offer = config.offersubscription.offer + member = config.offersubscription.member + ip = subnet.inet + + if kwargs['signal_type'] == "save": + msg = "Allocating IP %s to member %s (%s - %s %s) for offer %s" + elif kwargs['signal_type'] == "delete": + msg = "Deallocating IP %s from member %s (%s - %s %s) (was offer %s)" + else: + # Does not happens + msg = "" + + subnet_log.info(msg % (ip, str(member.pk), + member.username, member.first_name, member.last_name, + offer.name)) + except ObjectDoesNotExist: pass -- GitLab From 1647052c09cd08ec44770ddc364ac981dcd914f2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 19 Aug 2017 20:44:30 +0000 Subject: [PATCH 024/195] Also display subscription ref in log --- coin/configuration/models.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 350547c..351d134 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -138,21 +138,22 @@ def subnet_event(sender, **kwargs): if hasattr(config, 'subnet_event'): config.subnet_event() - offer = config.offersubscription.offer + offer = config.offersubscription.offer.name + subref = config.offersubscription.get_subscription_reference() member = config.offersubscription.member ip = subnet.inet if kwargs['signal_type'] == "save": - msg = "Allocating IP %s to member %s (%s - %s %s) for offer %s" + msg = "Allocating IP %s to member %s (%s - %s %s) (for offer %s, %s)" elif kwargs['signal_type'] == "delete": - msg = "Deallocating IP %s from member %s (%s - %s %s) (was offer %s)" + msg = "Deallocating IP %s from member %s (%s - %s %s) (was offer %s, %s)" else: # Does not happens msg = "" subnet_log.info(msg % (ip, str(member.pk), member.username, member.first_name, member.last_name, - offer.name)) + offer, subref)) except ObjectDoesNotExist: pass -- GitLab From 82a11d844bd99acc8b24ec7c4773fd47ba84fadb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Aug 2018 18:45:22 +0200 Subject: [PATCH 025/195] Be able to get bill type and child object easily --- coin/billing/models.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/coin/billing/models.py b/coin/billing/models.py index f70726d..d1a1d56 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -42,6 +42,12 @@ def invoice_pdf_filename(instance, filename): class Bill(models.Model): + CHILD_CLASS_NAMES = ( + 'Invoice', + 'MembershipFee', + 'Donation', + ) + BILL_STATUS_CHOICES = ( ('open', 'À payer'), ('closed', 'Réglée'), @@ -62,6 +68,23 @@ class Bill(models.Model): upload_to=bill_pdf_filename, null=True, blank=True, verbose_name='PDF') + + + def as_child(self): + for child_class_name in self.CHILD_CLASS_NAMES: + try: + return self.__getattribute__(child_class_name.lower()) + except eval(child_class_name).DoesNotExist: + pass + return self + + @property + def type(self): + for child_class_name in self.CHILD_CLASS_NAMES: + if hasattr(self, child_class_name.lower()): + return child_class_name + return self.__class__.__name__ + @property def amount(self): """ Return bill amount """ -- GitLab From 72691e154608ece851e564c6205423c161700e58 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Aug 2018 18:50:38 +0200 Subject: [PATCH 026/195] Use a more generic 'bill' object in invoice templates --- arn/templates/billing/invoice_pdf.html | 235 ++++++++++++++++++ coin/billing/models.py | 2 +- .../templates/billing/invoice_pdf.html | 16 +- 3 files changed, 244 insertions(+), 9 deletions(-) create mode 100644 arn/templates/billing/invoice_pdf.html diff --git a/arn/templates/billing/invoice_pdf.html b/arn/templates/billing/invoice_pdf.html new file mode 100644 index 0000000..e2d6a2a --- /dev/null +++ b/arn/templates/billing/invoice_pdf.html @@ -0,0 +1,235 @@ +{% load static isptags %} + + + Facture N°{{ bill.number }} + + + + + + + + + + + + + + + + + +
    +

    + {% multiline_isp_addr branding %} +

    +

    + {{ branding.email }}
    + {{ branding.website }}
    + {{ branding.phone_number }}
    + SIRET : {{ branding.registeredoffice.siret }} +

    +
    +

    + Facturé à
    + {% with member=bill.member %} + {{ member.last_name }} {{ member.first_name }}
    + {% if member.organization_name != "" %}{{ member.organization_name }}
    {% endif %} + {% if member.address %}{{member.address}}
    {% endif %} + {% if member.postal_code and member.city %} + {{ member.postal_code }} {{ member.city }} + {% endif %} + {% endwith %} +

    +
    + + + + + + + + + + + + + {% for detail in bill.details.all %} + + + + + + + + {% endfor %} + + + + + + + + + + + + + +
    QuantitéPU (HT)TVATotal
    +

    + {{ detail.label }} + {% if detail.offersubscription %} +
    + {{ detail.offersubscription.offer.name }} + {% if detail.offersubscription.offer.reference %} ({{ detail.offersubscription.get_subscription_reference }}){% endif %} + + {% endif %} +

    + {% if detail.period_from and detail.period_to %} +

    Pour la période du {{ detail.period_from }} au {{ detail.period_to }}

    + {% endif %} +
    {{ detail.quantity }}{{ detail.amount }}€{{ detail.tax }}%{{ detail.total }}€
    Total HT{{ bill.amount_before_tax }}€
    Total TTC{{ bill.amount }}€
    + +

    TVA non applicable - article 293 B du CGI

    +

    À payer sans escompte avant le {{ bill.date_due }}.

    + +
    + {% include "billing/payment_howto.html" %} +
    + +
    +

    /

    +

    {{ branding.shortname|upper }} est une association de droit local alsacien-mosellan à but non lucratif.

    +
    + + diff --git a/coin/billing/models.py b/coin/billing/models.py index d1a1d56..13cc082 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -115,7 +115,7 @@ class Bill(models.Model): """ Make and store a pdf file for the invoice """ - context = {"invoice": self} + context = {"bill": self} context.update(branding(None)) pdf_file = render_as_pdf('billing/invoice_pdf.html', context) self.pdf.save('%s.pdf' % self.number, pdf_file) diff --git a/coin/billing/templates/billing/invoice_pdf.html b/coin/billing/templates/billing/invoice_pdf.html index b075a27..ade4d81 100644 --- a/coin/billing/templates/billing/invoice_pdf.html +++ b/coin/billing/templates/billing/invoice_pdf.html @@ -1,7 +1,7 @@ {% load static isptags %} - Facture N°{{ invoice.number }} + Facture N°{{ bill.number }} + + + +
    + +

    Reçu de don

    +

    Le {{ bill.date }}

    +
    + + + + + + +
    +

    + {% multiline_isp_addr branding %} +

    +

    + {{ branding.email }}
    + {{ branding.website }}
    + {{ branding.phone_number }} +

    +
    +

    + Pour :
    + {% with member=bill.member %} + {{ member.last_name }} {{ member.first_name }}
    + {% if member.organization_name != "" %}{{ member.organization_name }}
    {% endif %} + {% if member.address %}{{member.address}}
    {% endif %} + {% if member.postal_code and member.city %} + {{ member.postal_code }} {{ member.city }} + {% endif %} + {% endwith %} +

    +
    + +
    +
    +
    + +

    + En date du {{ bill.date }}, l'association {{ branding.shortname|upper }} certifie avoir reçu un don d'un montant de {{ bill.amount }}€ de la part de {{ bill.member.first_name }} {{ bill.member.last_name }}. +

    + +
    +
    +
    + +

    + N.B. : ce reçu n'a pas valeur de reçu fiscal et ne peut pas être utilisé pour prétendre à une réduction d'impôts. +

    + +
    + +

    /

    +

    {{ branding.shortname|upper }}, association loi de 1901 à but non lucratif - SIRET : {{ branding.registeredoffice.siret }}

    +
    + + diff --git a/coin/billing/templates/billing/membershipfee_pdf.html b/coin/billing/templates/billing/membershipfee_pdf.html new file mode 100644 index 0000000..f0cb62f --- /dev/null +++ b/coin/billing/templates/billing/membershipfee_pdf.html @@ -0,0 +1,173 @@ +{% load static isptags %} + + + Reçu de cotisation + + + + + +
    + +

    Reçu de cotisation

    +

    Le {{ bill.date }}

    +
    + + + + + + +
    +

    + {% multiline_isp_addr branding %} +

    +

    + {{ branding.email }}
    + {{ branding.website }}
    + {{ branding.phone_number }} +

    +
    +

    + Pour :
    + {% with member=bill.member %} + {{ member.last_name }} {{ member.first_name }}
    + {% if member.organization_name != "" %}{{ member.organization_name }}
    {% endif %} + {% if member.address %}{{member.address}}
    {% endif %} + {% if member.postal_code and member.city %} + {{ member.postal_code }} {{ member.city }} + {% endif %} + {% endwith %} +

    +
    + +
    +
    +
    + +

    + En date du {{ bill.date }}, l'association {{ branding.shortname|upper }} certifie avoir reçu et accepté une cotisation d'un montant de {{ bill.amount }}€ de la part de {{ bill.member.first_name }} {{ bill.member.last_name }}. Cette cotisation lui confère le statut de membre pour la période du {{ bill.start_date }} au {{ bill.end_date }}. +

    + +
    + +

    /

    +

    {{ branding.shortname|upper }}, association loi de 1901 à but non lucratif - SIRET : {{ branding.registeredoffice.siret }}

    +
    + + -- GitLab From 406255e9bd79e867891552a2b455198905be9f8b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 20 Aug 2018 01:43:18 +0200 Subject: [PATCH 033/195] Factorize common parts in invoice/membershipfee/donation templates --- arn/templates/billing/bill_pdf.html | 190 ++++++++++++++++++ coin/billing/models.py | 13 ++ coin/billing/templates/billing/bill_pdf.html | 175 ++++++++++++++++ .../templates/billing/donation_pdf.html | 170 +--------------- .../templates/billing/invoice_pdf.html | 190 ++---------------- .../templates/billing/membershipfee_pdf.html | 168 +--------------- 6 files changed, 399 insertions(+), 507 deletions(-) create mode 100644 arn/templates/billing/bill_pdf.html create mode 100644 coin/billing/templates/billing/bill_pdf.html diff --git a/arn/templates/billing/bill_pdf.html b/arn/templates/billing/bill_pdf.html new file mode 100644 index 0000000..fceebfa --- /dev/null +++ b/arn/templates/billing/bill_pdf.html @@ -0,0 +1,190 @@ +{% load static isptags %} + + + {{ bill.pdf_title }} + + + + + + {% block header %} + + + + + + + {% endblock %} + + {% block coordinates %} + + + + + +
    +

    + {% multiline_isp_addr branding %} +

    +

    + {{ branding.email }}
    + {{ branding.website }}
    + {{ branding.phone_number }}
    + SIRET : {{ branding.registeredoffice.siret }} +

    +
    +

    + À l'intention de :
    + {% with member=bill.member %} + {{ member.last_name }} {{ member.first_name }}
    + {% if member.organization_name != "" %}{{ member.organization_name }}
    {% endif %} + {% if member.address %}{{member.address}}
    {% endif %} + {% if member.postal_code and member.city %} + {{ member.postal_code }} {{ member.city }} + {% endif %} + {% endwith %} +

    +
    + {% endblock %} + + {% block content %} + {% endblock %} + + {% block footer %} +
    +

    /

    +

    {{ branding.shortname|upper }} est une association de droit local alsacien-mosellan à but non lucratif.

    +
    + {% endblock %} + + + diff --git a/coin/billing/models.py b/coin/billing/models.py index a32635f..e0faf59 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -325,6 +325,10 @@ class Invoice(Bill): and bool(self.pdf) and private_files_storage.exists(self.pdf.name)) + @property + def pdf_title(self): + return "Facture N°"+self.number + def __unicode__(self): return '#{} {:0.2f}€ {}'.format( self.number, self.amount, self.date_due) @@ -475,6 +479,11 @@ class Donation(Bill): raise ValidationError("Le solde n'est pas suffisant pour payer ce don. \ Merci de commencer par enregistrer un paiement pour ce membre.") + @property + def pdf_title(self): + return "Reçu de don" + + class Meta: verbose_name = 'don' @@ -517,6 +526,10 @@ class MembershipFee(Bill): if self.start_date is not None and self.end_date is None: self.end_date = self.start_date + datetime.timedelta(364) + @property + def pdf_title(self): + return "Reçu de cotisation" + class Meta: verbose_name = 'cotisation' diff --git a/coin/billing/templates/billing/bill_pdf.html b/coin/billing/templates/billing/bill_pdf.html new file mode 100644 index 0000000..6e1cf33 --- /dev/null +++ b/coin/billing/templates/billing/bill_pdf.html @@ -0,0 +1,175 @@ +{% load static isptags %} + + + {{ bill.pdf_title }} + + + + + + {% block header %} +
    + +

    {{ bill.pdf_title }}

    +

    Le {{ bill.date }}

    +
    + {% endblock %} + + {% block coordinates %} + + + + + +
    +

    + {% multiline_isp_addr branding %} +

    +

    + {{ branding.email }}
    + {{ branding.website }}
    + {{ branding.phone_number }} +

    +
    +

    + À l'intention de :
    + {% with member=bill.member %} + {{ member.last_name }} {{ member.first_name }}
    + {% if member.organization_name != "" %}{{ member.organization_name }}
    {% endif %} + {% if member.address %}{{member.address}}
    {% endif %} + {% if member.postal_code and member.city %} + {{ member.postal_code }} {{ member.city }} + {% endif %} + {% endwith %} +

    +
    + {% endblock %} + + {% block content %} + {% endblock %} + + {% block footer %} +
    + +

    /

    +

    {{ branding.shortname|upper }}, association loi de 1901 à but non lucratif - SIRET : {{ branding.registeredoffice.siret }}

    +
    + {% endblock %} + + + diff --git a/coin/billing/templates/billing/donation_pdf.html b/coin/billing/templates/billing/donation_pdf.html index e5ddd84..47c73b1 100644 --- a/coin/billing/templates/billing/donation_pdf.html +++ b/coin/billing/templates/billing/donation_pdf.html @@ -1,161 +1,6 @@ -{% load static isptags %} - - - Reçu de don - - - - - -
    - -

    Reçu de don

    -

    Le {{ bill.date }}

    -
    - - - - - - -
    -

    - {% multiline_isp_addr branding %} -

    -

    - {{ branding.email }}
    - {{ branding.website }}
    - {{ branding.phone_number }} -

    -
    -

    - Pour :
    - {% with member=bill.member %} - {{ member.last_name }} {{ member.first_name }}
    - {% if member.organization_name != "" %}{{ member.organization_name }}
    {% endif %} - {% if member.address %}{{member.address}}
    {% endif %} - {% if member.postal_code and member.city %} - {{ member.postal_code }} {{ member.city }} - {% endif %} - {% endwith %} -

    -
    +{% extends "billing/bill_pdf.html" %} +{% block content %}


    @@ -167,15 +12,8 @@


    - +

    N.B. : ce reçu n'a pas valeur de reçu fiscal et ne peut pas être utilisé pour prétendre à une réduction d'impôts.

    - -
    - -

    /

    -

    {{ branding.shortname|upper }}, association loi de 1901 à but non lucratif - SIRET : {{ branding.registeredoffice.siret }}

    -
    - - +{% endblock %} diff --git a/coin/billing/templates/billing/invoice_pdf.html b/coin/billing/templates/billing/invoice_pdf.html index ade4d81..5f70835 100644 --- a/coin/billing/templates/billing/invoice_pdf.html +++ b/coin/billing/templates/billing/invoice_pdf.html @@ -1,162 +1,7 @@ -{% load static isptags %} - - - Facture N°{{ bill.number }} +{% extends "billing/bill_pdf.html" %} - - - - -
    - -

    Facture N°{{ bill.number }}

    -

    Le {{ bill.date }}

    -
    - - - - - - -
    -

    - {% multiline_isp_addr branding %} -

    -

    - {{ branding.email }}
    - {{ branding.website }}
    - {{ branding.phone_number }} -

    -
    -

    - Facturé à :
    - {% with member=bill.member %} - {{ member.last_name }} {{ member.first_name }}
    - {% if member.organization_name != "" %}{{ member.organization_name }}
    {% endif %} - {% if member.address %}{{member.address}}
    {% endif %} - {% if member.postal_code and member.city %} - {{ member.postal_code }} {{ member.city }} - {% endif %} - {% endwith %} -

    -
    - - +{% block content %} +
    @@ -189,7 +34,7 @@ {% endfor %} - + @@ -200,21 +45,14 @@ - - -
    {{ detail.total }}€
    Total HTTotal TTC {{ bill.amount }}€
    -

    - Facture à payer avant le {{ bill.date_due }}. -

    - -
    - {% include "billing/payment_howto.html" %} -
    -
    - -

    /

    -

    {{ branding.shortname|upper }}, association loi de 1901 à but non lucratif - SIRET : {{ branding.registeredoffice.siret }}

    -
    - - + + +

    +Facture à payer avant le {{ bill.date_due }}. +

    + +
    +{% include "billing/payment_howto.html" %} +
    +{% endblock %} diff --git a/coin/billing/templates/billing/membershipfee_pdf.html b/coin/billing/templates/billing/membershipfee_pdf.html index f0cb62f..acb70b7 100644 --- a/coin/billing/templates/billing/membershipfee_pdf.html +++ b/coin/billing/templates/billing/membershipfee_pdf.html @@ -1,161 +1,6 @@ -{% load static isptags %} - - - Reçu de cotisation - - - - - -
    - -

    Reçu de cotisation

    -

    Le {{ bill.date }}

    -
    - - - - - - -
    -

    - {% multiline_isp_addr branding %} -

    -

    - {{ branding.email }}
    - {{ branding.website }}
    - {{ branding.phone_number }} -

    -
    -

    - Pour :
    - {% with member=bill.member %} - {{ member.last_name }} {{ member.first_name }}
    - {% if member.organization_name != "" %}{{ member.organization_name }}
    {% endif %} - {% if member.address %}{{member.address}}
    {% endif %} - {% if member.postal_code and member.city %} - {{ member.postal_code }} {{ member.city }} - {% endif %} - {% endwith %} -

    -
    +{% extends "billing/bill_pdf.html" %} +{% block content %}


    @@ -163,11 +8,4 @@

    En date du {{ bill.date }}, l'association {{ branding.shortname|upper }} certifie avoir reçu et accepté une cotisation d'un montant de {{ bill.amount }}€ de la part de {{ bill.member.first_name }} {{ bill.member.last_name }}. Cette cotisation lui confère le statut de membre pour la période du {{ bill.start_date }} au {{ bill.end_date }}.

    - -
    - -

    /

    -

    {{ branding.shortname|upper }}, association loi de 1901 à but non lucratif - SIRET : {{ branding.registeredoffice.siret }}

    -
    - - +{% endblock %} -- GitLab From 0cbca5119a8665d5e4eb7d1057c2907fded5e518 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 20 Aug 2018 14:36:18 +0200 Subject: [PATCH 034/195] Fix membership fees not being automatically applied --- coin/billing/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coin/billing/models.py b/coin/billing/models.py index e0faf59..d77940c 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -782,13 +782,13 @@ def bill_changed(sender, instance, created, **kwargs): @receiver(post_save, sender=MembershipFee) @disable_for_loaddata -def fee_changed(sender, instance, created, **kwargs): +def membershipfee_changed(sender, instance, created, **kwargs): if created and instance.member is not None: update_accounting_for_member(instance.member) @receiver(post_save, sender=Donation) @disable_for_loaddata -def fee_changed(sender, instance, created, **kwargs): +def donation_changed(sender, instance, created, **kwargs): if created and instance.member is not None: update_accounting_for_member(instance.member) -- GitLab From cc00929646d62050580e6c71283ef0d262d1e00b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 20 Aug 2018 19:41:46 +0200 Subject: [PATCH 035/195] Allow comment to be blank --- coin/configuration/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coin/configuration/forms.py b/coin/configuration/forms.py index 753e7aa..02f3742 100644 --- a/coin/configuration/forms.py +++ b/coin/configuration/forms.py @@ -10,7 +10,7 @@ from coin.configuration.models import Configuration class ConfigurationForm(ModelForm): - comment = forms.CharField(widget=forms.Textarea) + comment = forms.CharField(widget=forms.Textarea, blank=True) class Meta: model = Configuration -- GitLab From 1d5ae72f403ebecb3bf6f8abbe9512829d4b938e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 21 Aug 2018 18:00:10 +0200 Subject: [PATCH 036/195] Allow to customize the ip allocation message --- README.md | 1 + coin/configuration/models.py | 11 +++++------ coin/settings_base.py | 4 ++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f24d84e..77c1af1 100644 --- a/README.md +++ b/README.md @@ -349,6 +349,7 @@ List of available settings in your `settings_local.py` file. - `HANDLE_BALANCE`: Allows to handle money balances for members (False default) - `INVOICES_INCLUDE_CONFIG_COMMENTS`: Add comment related to a subscription configuration when generating invoices - `MEMBER_CAN_EDIT_VPN_CONF`: Allow members to edit some part of their vpn configuration +- `IP_ALLOCATION_MESSAGE`: Template string that will be used to log IP allocation in the corresponding coin.subnets logging system - `DEBUG` : Enable debug for development **do not use in production** : display stracktraces and enable [django-debug-toolbar](https://django-debug-toolbar.readthedocs.io). diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 351d134..ae1ab59 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -9,6 +9,7 @@ from coin.offers.models import OfferSubscription from django.db.models.signals import post_save, post_delete from django.core.exceptions import ObjectDoesNotExist from django.dispatch import receiver +from django.conf import settings from coin.resources.models import IPSubnet @@ -139,21 +140,19 @@ def subnet_event(sender, **kwargs): config.subnet_event() offer = config.offersubscription.offer.name - subref = config.offersubscription.get_subscription_reference() + ref = config.offersubscription.get_subscription_reference() member = config.offersubscription.member ip = subnet.inet if kwargs['signal_type'] == "save": - msg = "Allocating IP %s to member %s (%s - %s %s) (for offer %s, %s)" + msg = "[Allocating IP] " + settings.IP_ALLOCATION_MESSAGE elif kwargs['signal_type'] == "delete": - msg = "Deallocating IP %s from member %s (%s - %s %s) (was offer %s, %s)" + msg = "[Deallocating IP] " + settings.IP_ALLOCATION_MESSAGE else: # Does not happens msg = "" - subnet_log.info(msg % (ip, str(member.pk), - member.username, member.first_name, member.last_name, - offer, subref)) + subnet_log.info(msg.format(ip=ip, member=member, offer=offer, ref=ref)) except ObjectDoesNotExist: pass diff --git a/coin/settings_base.py b/coin/settings_base.py index b0c44ab..5b8ca89 100644 --- a/coin/settings_base.py +++ b/coin/settings_base.py @@ -302,3 +302,7 @@ HANDLE_BALANCE = False # Add subscription comments in invoice items INVOICES_INCLUDE_CONFIG_COMMENTS = True + +# String template used for the IP allocation log (c.f. coin.subnet loggers +# This will get prefixed by [Allocating IP] or [Desallocating IP] +IP_ALLOCATION_MESSAGE = "{ip} to {member.pk} ({member.username} - {member.first_name} {member.last_name}) (for offer {offer}, {ref})" -- GitLab From 1f01953a16ca8bef255b998abd1e703deb8a75d9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 21 Aug 2018 18:04:36 +0200 Subject: [PATCH 037/195] Log as debug because for some weird reason, during tests info messages are not logged x_x --- coin/configuration/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index ae1ab59..dcf9dde 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -152,7 +152,7 @@ def subnet_event(sender, **kwargs): # Does not happens msg = "" - subnet_log.info(msg.format(ip=ip, member=member, offer=offer, ref=ref)) + subnet_log.debug(msg.format(ip=ip, member=member, offer=offer, ref=ref)) except ObjectDoesNotExist: pass -- GitLab From 19c21f4b32debf599ec86d8b906d25cf439b0567 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 22 Aug 2018 19:07:51 +0200 Subject: [PATCH 038/195] [enh] Use utils.send_templated_email --- coin/members/models.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/coin/members/models.py b/coin/members/models.py index 25a0b8b..982d454 100644 --- a/coin/members/models.py +++ b/coin/members/models.py @@ -44,15 +44,14 @@ def send_registration_notification(sender, user, request=None, **kwargs): Send a notification to the admin if a user subscribe """ relative_link = reverse('admin:members_member_change', args=[user.id]) - abs_link = request.build_absolute_uri(relative_link) - - send_mail('[COIN] Nouvelle inscription', - 'Bonjour,\n' + - '%s s\'est enregistré(e) sur COIN.\n' % user.username + - 'Lien d\'édition: %s' % abs_link, - settings.DEFAULT_FROM_EMAIL, - settings.NOTIFICATION_EMAILS, - fail_silently=False) + edit_link = request.build_absolute_uri(relative_link) + + utils.send_templated_email( + to=settings.NOTIFICATION_EMAILS, + subject_template='members/emails/new_member_subject.txt', + body_template='members/emails/new_member_email.html', + context={'member': self, 'edit_link': edit_link}, + **kwargs) class Member(CoinLdapSyncMixin, AbstractUser): -- GitLab From 7774ebb161a1ad3a5c3928ce81d8b0dd84047323 Mon Sep 17 00:00:00 2001 From: ljf Date: Wed, 22 Aug 2018 23:44:58 +0200 Subject: [PATCH 039/195] [enh] Document settings --- README.md | 1 + coin/members/models.py | 14 +++++++------- coin/settings_base.py | 3 +++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f24d84e..766a1e2 100644 --- a/README.md +++ b/README.md @@ -351,6 +351,7 @@ List of available settings in your `settings_local.py` file. - `MEMBER_CAN_EDIT_VPN_CONF`: Allow members to edit some part of their vpn configuration - `DEBUG` : Enable debug for development **do not use in production** : display stracktraces and enable [django-debug-toolbar](https://django-debug-toolbar.readthedocs.io). +- `NOTIFICATION_EMAILS` : Emails on which to send notifications. Accounting logs --------------- diff --git a/coin/members/models.py b/coin/members/models.py index 982d454..28c9af6 100644 --- a/coin/members/models.py +++ b/coin/members/models.py @@ -45,13 +45,13 @@ def send_registration_notification(sender, user, request=None, **kwargs): """ relative_link = reverse('admin:members_member_change', args=[user.id]) edit_link = request.build_absolute_uri(relative_link) - - utils.send_templated_email( - to=settings.NOTIFICATION_EMAILS, - subject_template='members/emails/new_member_subject.txt', - body_template='members/emails/new_member_email.html', - context={'member': self, 'edit_link': edit_link}, - **kwargs) + if settings.NOTIFICATION_EMAILS is not None: + utils.send_templated_email( + to=settings.NOTIFICATION_EMAILS, + subject_template='members/emails/new_member_subject.txt', + body_template='members/emails/new_member_email.html', + context={'member': self, 'edit_link': edit_link}, + **kwargs) class Member(CoinLdapSyncMixin, AbstractUser): diff --git a/coin/settings_base.py b/coin/settings_base.py index b0c44ab..3d2224c 100644 --- a/coin/settings_base.py +++ b/coin/settings_base.py @@ -16,6 +16,9 @@ ADMINS = ( # ('Your Name', 'your_email@example.com'), ) +# Email on which to send emails +NOTIFICATION_EMAILS = None + MANAGERS = ADMINS DATABASES = { -- GitLab From 13c44a8579e79fb3b669808ea88b1e8eadeece55 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 18 Nov 2018 16:07:41 +0100 Subject: [PATCH 040/195] [fix] Missing templates --- .../templates/members/emails/new_member_email.html | 9 +++++++++ .../members/emails/new_member_email_subject.txt | 1 + 2 files changed, 10 insertions(+) create mode 100644 coin/members/templates/members/emails/new_member_email.html create mode 100644 coin/members/templates/members/emails/new_member_email_subject.txt diff --git a/coin/members/templates/members/emails/new_member_email.html b/coin/members/templates/members/emails/new_member_email.html new file mode 100644 index 0000000..e5d2eec --- /dev/null +++ b/coin/members/templates/members/emails/new_member_email.html @@ -0,0 +1,9 @@ +Bonjour,
    + +

    {{ member }} s'est enregistré⋅e sur l'espace adhérent +(COIN).

    + +

    Lien d'édition: {{ edit_link }}

    + +Bisou, +Votre canard dévoué diff --git a/coin/members/templates/members/emails/new_member_email_subject.txt b/coin/members/templates/members/emails/new_member_email_subject.txt new file mode 100644 index 0000000..a40bc7b --- /dev/null +++ b/coin/members/templates/members/emails/new_member_email_subject.txt @@ -0,0 +1 @@ +[COIN] Nouveau compte créé -- GitLab From a9253dfa774f0f44f2b163a549650ed7a8dd1208 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 25 Aug 2019 15:44:00 +0200 Subject: [PATCH 041/195] Why is the prod with a merge not commited ? --- coin/billing/admin.py | 8 ++++++-- coin/billing/create_subscriptions_invoices.py | 2 +- coin/billing/models.py | 8 ++++++-- .../templates/members/registration/registration_form.html | 7 ++++++- housing/models.py | 4 ++-- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/coin/billing/admin.py b/coin/billing/admin.py index c388ff1..15c02bb 100644 --- a/coin/billing/admin.py +++ b/coin/billing/admin.py @@ -100,7 +100,7 @@ class PaymentAllocatedReadOnly(admin.TabularInline): class PaymentInlineAdd(admin.StackedInline): model = Payment extra = 0 - fields = (('date', 'payment_mean', 'amount'),) + fields = (('date', 'payment_mean', 'amount', 'label'),) can_delete = False verbose_name_plural = "Ajouter des paiements" @@ -246,7 +246,9 @@ class PaymentAdmin(admin.ModelAdmin): fields = (('member'), ('amount', 'payment_mean', 'date', 'label'), ('amount_already_allocated')) - readonly_fields = ('amount_already_allocated', 'label') + readonly_fields = ('amount_already_allocated',) + list_filter = ['payment_mean'] + search_fields = ['member__username', 'member__first_name', 'member__last_name', 'member__email', 'member__nickname'] form = autocomplete_light.modelform_factory(Payment, fields='__all__') def get_readonly_fields(self, request, obj=None): @@ -303,6 +305,8 @@ class PaymentAdmin(admin.ModelAdmin): class MembershipFeeAdmin(admin.ModelAdmin): list_display = ('member', 'end_date', '_amount') + search_fields = ['member__username', 'member__first_name', 'member__last_name', 'member__email', 'member__nickname'] + list_filter = ['date'] form = autocomplete_light.modelform_factory(MembershipFee, fields='__all__') diff --git a/coin/billing/create_subscriptions_invoices.py b/coin/billing/create_subscriptions_invoices.py index 4f070dd..d760cd3 100644 --- a/coin/billing/create_subscriptions_invoices.py +++ b/coin/billing/create_subscriptions_invoices.py @@ -158,8 +158,8 @@ def create_member_invoice_for_a_period(member, date, antidate): invoice.save() transaction.savepoint_commit(sid) # Valide la facture et génère le PDF + invoice.date_due = None # (reset the due date, will automatically be redefined when validating, to date+PAYMENT_DELAY) if antidate: - invoice.date_due = None # (reset the due date, will automatically be redefined when validating) invoice.validate(period_to) else: invoice.validate() diff --git a/coin/billing/models.py b/coin/billing/models.py index 111ede6..f6a0000 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -316,7 +316,7 @@ class Invoice(Bill): "Draft invoice {} validated as invoice {}. ".format( old_number, self.number) + "(Total amount : {} ; Member : {})".format( - self.amount(), self.member)) + self.amount, self.member)) assert self.pdf_exists() if self.member is not None: update_accounting_for_member(self.member) @@ -415,7 +415,7 @@ class Invoice(Bill): class InvoiceDetail(models.Model): - label = models.CharField(max_length=100) + label = models.CharField(max_length=255) amount = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='montant') quantity = models.DecimalField(null=True, verbose_name='quantité', @@ -513,6 +513,10 @@ class MembershipFee(Bill): return True def save(self, *args, **kwargs): + # Only if no amount already allocated... + #if self.pk is None and (not self.member or self.member.balance < self.amount): + # raise ValidationError("Le solde n'est pas suffisant pour payer cette cotisation. \ + # Merci de commencer par enregistrer un paiement pour ce membre.") super(MembershipFee, self).save(*args, **kwargs) diff --git a/coin/members/templates/members/registration/registration_form.html b/coin/members/templates/members/registration/registration_form.html index c61905f..474e5b9 100644 --- a/coin/members/templates/members/registration/registration_form.html +++ b/coin/members/templates/members/registration/registration_form.html @@ -21,8 +21,10 @@ {% csrf_token %}
    {{ form.as_p }} + J'ai lu les statuts de l'association
    - +

    1+1:

    + @@ -47,6 +49,9 @@ $(document).ready(function () { } return false; }; + $('#status').on('change', function() { + $('#submit').prop( "disabled", !$('#submit').prop( "disabled")); + }); $('#id_type').on('change', hideFields); if (!$('#id_organization_name').val()) { $('#id_type').val('natural_person'); diff --git a/housing/models.py b/housing/models.py index 63db46a..e1e7ad3 100644 --- a/housing/models.py +++ b/housing/models.py @@ -29,7 +29,7 @@ class HousingConfiguration(Configuration): verbose_name="IPv6", blank=True, null=True, help_text="Adresse IPv6 utilisée par " "défaut sur le Housing") - vlan = models.IntegerField(verbose_name="vlan id", null=True) + vlan = models.IntegerField(verbose_name="vlan id",blank=True, null=True) objects = NetManager() def get_absolute_url(self): @@ -104,7 +104,7 @@ class HousingConfiguration(Configuration): self.check_endpoints() def __unicode__(self): - return 'Housing ' #+ self.login + return 'Housing ' + str(self.vlan) class Meta: verbose_name = 'Housing' -- GitLab From e8b4498c1afed84130a64e53e569712881418d53 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 25 Aug 2019 15:47:03 +0200 Subject: [PATCH 042/195] Things on arn prod, need to be sorted --- coin/billing/membershipfee_filter.py | 2 +- coin/billing/migrations/0017_merge.py | 15 ++++++ .../migrations/0018_auto_20181118_2001.py | 46 +++++++++++++++++++ .../migrations/0019_auto_20190623_1256.py | 19 ++++++++ coin/billing/views.py | 3 -- coin/configuration/forms.py | 2 +- coin/members/migrations/0020_merge.py | 15 ++++++ .../migrations/0021_auto_20181118_2001.py | 40 ++++++++++++++++ .../migrations/0022_auto_20190623_1256.py | 19 ++++++++ coin/members/templates/members/detail.html | 2 +- .../members/emails/new_member_subject.txt | 1 + coin/members/templates/members/invoices.html | 1 + coin/settings_base.py | 4 ++ coin/templates/base.html | 5 +- coin/utils.py | 1 + housing/migrations/0002_auto_20181118_2001.py | 24 ++++++++++ vpn/migrations/0004_auto_20181118_2001.py | 19 ++++++++ vps/migrations/0004_auto_20181118_2001.py | 19 ++++++++ 18 files changed, 229 insertions(+), 8 deletions(-) create mode 100644 coin/billing/migrations/0017_merge.py create mode 100644 coin/billing/migrations/0018_auto_20181118_2001.py create mode 100644 coin/billing/migrations/0019_auto_20190623_1256.py create mode 100644 coin/members/migrations/0020_merge.py create mode 100644 coin/members/migrations/0021_auto_20181118_2001.py create mode 100644 coin/members/migrations/0022_auto_20190623_1256.py create mode 100644 coin/members/templates/members/emails/new_member_subject.txt create mode 100644 housing/migrations/0002_auto_20181118_2001.py create mode 100644 vpn/migrations/0004_auto_20181118_2001.py create mode 100644 vps/migrations/0004_auto_20181118_2001.py diff --git a/coin/billing/membershipfee_filter.py b/coin/billing/membershipfee_filter.py index 8c3adae..b3f5126 100644 --- a/coin/billing/membershipfee_filter.py +++ b/coin/billing/membershipfee_filter.py @@ -11,7 +11,7 @@ class MembershipFeeFilter(SimpleListFilter): title = 'Cotisations' # Parameter for the filter that will be used in the URL query. - parameter_name = 'fee' + parameter_name = 'fee_billing' def lookups(self, request, model_admin): """ diff --git a/coin/billing/migrations/0017_merge.py b/coin/billing/migrations/0017_merge.py new file mode 100644 index 0000000..d7ffbcf --- /dev/null +++ b/coin/billing/migrations/0017_merge.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0016_auto_20180415_2208'), + ('billing', '0011_auto_20180819_0221'), + ] + + operations = [ + ] diff --git a/coin/billing/migrations/0018_auto_20181118_2001.py b/coin/billing/migrations/0018_auto_20181118_2001.py new file mode 100644 index 0000000..d8ff559 --- /dev/null +++ b/coin/billing/migrations/0018_auto_20181118_2001.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.core.files.storage +import coin.billing.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0017_merge'), + ] + + operations = [ + migrations.AlterField( + model_name='bill', + name='pdf', + field=models.FileField(storage=django.core.files.storage.FileSystemStorage(location='/var/www/adherents.arn-fai.net/smedia/'), upload_to=coin.billing.models.bill_pdf_filename, null=True, verbose_name='PDF', blank=True), + ), + migrations.AlterField( + model_name='invoicedetail', + name='amount', + field=models.DecimalField(verbose_name='montant', max_digits=8, decimal_places=2), + ), + migrations.AlterField( + model_name='membershipfee', + name='_amount', + field=models.DecimalField(default=None, help_text='en \u20ac', verbose_name='montant', max_digits=8, decimal_places=2), + ), + migrations.AlterField( + model_name='payment', + name='amount', + field=models.DecimalField(null=True, verbose_name='montant', max_digits=8, decimal_places=2), + ), + migrations.AlterField( + model_name='payment', + name='payment_mean', + field=models.CharField(default='transfer', max_length=100, null=True, verbose_name='moyen de paiement', choices=[('cash', 'Esp\xe8ces'), ('check', 'Ch\xe8que'), ('transfer', 'Virement'), ('creditnote', 'Avoir'), ('other', 'Autre')]), + ), + migrations.AlterField( + model_name='paymentallocation', + name='amount', + field=models.DecimalField(null=True, verbose_name='montant', max_digits=8, decimal_places=2), + ), + ] diff --git a/coin/billing/migrations/0019_auto_20190623_1256.py b/coin/billing/migrations/0019_auto_20190623_1256.py new file mode 100644 index 0000000..5fab37f --- /dev/null +++ b/coin/billing/migrations/0019_auto_20190623_1256.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0018_auto_20181118_2001'), + ] + + operations = [ + migrations.AlterField( + model_name='invoicedetail', + name='label', + field=models.CharField(max_length=255), + ), + ] diff --git a/coin/billing/views.py b/coin/billing/views.py index d3b5afc..534c595 100644 --- a/coin/billing/views.py +++ b/coin/billing/views.py @@ -11,9 +11,6 @@ from sendfile import sendfile from coin.billing.models import Invoice from coin.members.models import Member from coin.html2pdf import render_as_pdf -from coin.billing.utils import get_invoice_from_id_or_number, assert_user_can_view_the_invoice - -from sendfile import sendfile from coin.billing.create_subscriptions_invoices import create_all_members_invoices_for_a_period from coin.billing.utils import get_bill_from_id_or_number, assert_user_can_view_the_bill diff --git a/coin/configuration/forms.py b/coin/configuration/forms.py index 02f3742..753e7aa 100644 --- a/coin/configuration/forms.py +++ b/coin/configuration/forms.py @@ -10,7 +10,7 @@ from coin.configuration.models import Configuration class ConfigurationForm(ModelForm): - comment = forms.CharField(widget=forms.Textarea, blank=True) + comment = forms.CharField(widget=forms.Textarea) class Meta: model = Configuration diff --git a/coin/members/migrations/0020_merge.py b/coin/members/migrations/0020_merge.py new file mode 100644 index 0000000..cbbef91 --- /dev/null +++ b/coin/members/migrations/0020_merge.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0019_auto_20180415_1814'), + ('members', '0018_auto_20180819_0211'), + ] + + operations = [ + ] diff --git a/coin/members/migrations/0021_auto_20181118_2001.py b/coin/members/migrations/0021_auto_20181118_2001.py new file mode 100644 index 0000000..3961481 --- /dev/null +++ b/coin/members/migrations/0021_auto_20181118_2001.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0020_merge'), + ] + + operations = [ + migrations.CreateModel( + name='MembershipFee', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('amount', models.DecimalField(default=20, help_text='en \u20ac', verbose_name='montant', max_digits=5, decimal_places=2)), + ('start_date', models.DateField(verbose_name='date de d\xe9but de cotisation')), + ('end_date', models.DateField(help_text='par d\xe9faut, la cotisation dure un an', verbose_name='date de fin de cotisation', blank=True)), + ('payment_method', models.CharField(blank=True, max_length=100, null=True, verbose_name='moyen de paiement', choices=[('cash', 'Esp\xe8ces'), ('check', 'Ch\xe8que'), ('transfer', 'Virement'), ('other', 'Autre')])), + ('reference', models.CharField(help_text='num\xe9ro de ch\xe8que, r\xe9f\xe9rence de virement, commentaire...', max_length=125, null=True, verbose_name='r\xe9f\xe9rence du paiement', blank=True)), + ('payment_date', models.DateField(null=True, verbose_name='date du paiement', blank=True)), + ], + options={ + 'verbose_name': 'cotisation', + }, + ), + migrations.AlterField( + model_name='member', + name='balance', + field=models.DecimalField(default=0, verbose_name='solde', max_digits=6, decimal_places=2), + ), + migrations.AddField( + model_name='membershipfee', + name='member', + field=models.ForeignKey(related_name='membership_fees', verbose_name='membre', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/coin/members/migrations/0022_auto_20190623_1256.py b/coin/members/migrations/0022_auto_20190623_1256.py new file mode 100644 index 0000000..9e96d53 --- /dev/null +++ b/coin/members/migrations/0022_auto_20190623_1256.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0021_auto_20181118_2001'), + ] + + operations = [ + migrations.AlterField( + model_name='membershipfee', + name='amount', + field=models.DecimalField(default=15, help_text='en \u20ac', verbose_name='montant', max_digits=5, decimal_places=2), + ), + ] diff --git a/coin/members/templates/members/detail.html b/coin/members/templates/members/detail.html index 83ae3fb..890209c 100644 --- a/coin/members/templates/members/detail.html +++ b/coin/members/templates/members/detail.html @@ -87,7 +87,7 @@ Je n'ai encore jamais cotisé. {% endif %}

    - +

    Note: nous importons les comptes tous les 1 à 2 mois, inutile donc de nous contacter si votre adhésion n'est pas validée malgré un paiement de moins de 2 mois.

    {% if membership_info_url %} Renouveler ma cotisation diff --git a/coin/members/templates/members/emails/new_member_subject.txt b/coin/members/templates/members/emails/new_member_subject.txt new file mode 100644 index 0000000..a40bc7b --- /dev/null +++ b/coin/members/templates/members/emails/new_member_subject.txt @@ -0,0 +1 @@ +[COIN] Nouveau compte créé diff --git a/coin/members/templates/members/invoices.html b/coin/members/templates/members/invoices.html index 2b5acbc..0b1022f 100644 --- a/coin/members/templates/members/invoices.html +++ b/coin/members/templates/members/invoices.html @@ -6,6 +6,7 @@ {% if handle_balance %}

    Solde : {{ balance|floatformat }} €

    +

    Note: nous importons les comptes tous les 1 à 2 mois, inutile donc de nous contacter si votre solde est négatif ou votre adhésion non validée malgré un paiement de moins de 2 mois.

    {% endif %}

    Mes factures et reçus

    diff --git a/coin/settings_base.py b/coin/settings_base.py index b2a1a17..c28d8f5 100644 --- a/coin/settings_base.py +++ b/coin/settings_base.py @@ -222,6 +222,10 @@ LOGGING = { "coin.billing": { 'handlers': ['console'], 'level': 'INFO', + }, + "coin.subnets": { + 'handlers': ['console'], + 'level': 'INFO', } } } diff --git a/coin/templates/base.html b/coin/templates/base.html index ad01577..e90cf4f 100644 --- a/coin/templates/base.html +++ b/coin/templates/base.html @@ -15,6 +15,7 @@ + {% hijack_notification %} @@ -32,8 +33,8 @@
    -
    -
    +
    +
    diff --git a/vps/admin.py b/vps/admin.py index aef0c4e..c5ccfcc 100644 --- a/vps/admin.py +++ b/vps/admin.py @@ -28,46 +28,17 @@ class VPSConfigurationInline(admin.StackedInline): class VPSConfigurationAdmin(ConfigurationAdminFormMixin, PolymorphicChildModelAdmin): base_model = VPSConfiguration - list_display = ('offersubscription', 'activated', + list_display = ('offersubscription', 'ipv4_endpoint', 'ipv6_endpoint', 'comment') - list_filter = ('activated',) search_fields = ('comment', # TODO: searching on member directly doesn't work 'offersubscription__member__first_name', 'offersubscription__member__last_name', 'offersubscription__member__email') actions = (delete_selected, "generate_endpoints", "generate_endpoints_v4", - "generate_endpoints_v6", "activate", "deactivate") + "generate_endpoints_v6") inline = VPSConfigurationInline - def get_readonly_fields(self, request, obj=None): - if obj: - return [] - else: - return [] - - def set_activation(self, request, queryset, value): - count = 0 - # We must update each object individually, because we want to run - # the save() method to update the backend. - for vps in queryset: - if vps.activated != value: - vps.activated = value - vps.full_clean() - vps.save() - count += 1 - action = "activated" if value else "deactivated" - msg = "{} VPS subscription(s) {}.".format(count, action) - self.message_user(request, msg) - - def activate(self, request, queryset): - self.set_activation(request, queryset, True) - activate.short_description = "Activate selected VPSs" - - def deactivate(self, request, queryset): - self.set_activation(request, queryset, False) - deactivate.short_description = "Deactivate selected VPSs" - def generate_endpoints_generic(self, request, queryset, v4=True, v6=True): count = 0 for vps in queryset: diff --git a/vps/models.py b/vps/models.py index edf1831..ac1acde 100644 --- a/vps/models.py +++ b/vps/models.py @@ -32,7 +32,6 @@ PROTOCOLE_TYPES = ( class VPSConfiguration(Configuration): url_namespace = "vps" - activated = models.BooleanField(default=False, verbose_name='activé') ipv4_endpoint = InetAddressField(validators=[validation.validate_v4], verbose_name="IPv4", blank=True, null=True, help_text="Adresse IPv4 utilisée par " diff --git a/vps/templates/vps/vps.html b/vps/templates/vps/vps.html index 61c6597..7fb643c 100644 --- a/vps/templates/vps/vps.html +++ b/vps/templates/vps/vps.html @@ -56,14 +56,6 @@ {% endif %} - - - {% if form %} - - {% endif %} - Ce VPS est {{ object.activated|yesno:"activé,désactivé" }} - -
    -- GitLab From d637c98711b9105842b965b592fb8f23e196bee8 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 1 Jul 2020 19:30:02 +0200 Subject: [PATCH 064/195] [enh] Add automatically membershipfee --- .../commands/create_membership_fees.py | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 coin/members/management/commands/create_membership_fees.py diff --git a/coin/members/management/commands/create_membership_fees.py b/coin/members/management/commands/create_membership_fees.py new file mode 100644 index 0000000..bafe2a2 --- /dev/null +++ b/coin/members/management/commands/create_membership_fees.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import datetime +from dateutil.relativedelta import relativedelta +from django.core.management.base import BaseCommand, CommandError +from django.db.models import Q +from django.conf import settings + +from coin.utils import respect_language +from coin.members.models import Member +from coin.billing.models import MembershipFee, Payment + +class Command(BaseCommand): + args = '[date=2011-07-04]' + help = """Create membership fees if a member has an active service + """ + + def handle(self, *args, **options): + + # Member with a service + self.stdout.write("Member with a service") + members = Member.objects.exclude(status='not_member').exclude(offersubscription=None).filter(offersubscription__resign_date=None).distinct('username') + + membership_fees = MembershipFee.objects.order_by('member__username', '-end_date').distinct('member__username').filter(member__in=members) + + for member in members: + membership_fee = membership_fees.filter(member=member) + start_date = member.offersubscription_set.order_by('subscription_date')[0].subscription_date + if membership_fee and start_date < membership_fee[0].end_date: + start_date = membership_fee[0].end_date + + while start_date <= datetime.date.today(): + self.stdout.write("Create MembershipFee for {username} {date}".format(username=member.username,date=start_date)) + end_date = start_date + relativedelta(years=1) + fee = MembershipFee(member=member, _amount=settings.DEFAULT_MEMBERSHIP_FEE, + start_date=start_date, + end_date=start_date + relativedelta(years=1)) + fee.save() + start_date = end_date + member.status='member' + member.save() + + + # Member whithout services + self.stdout.write("\n\nMember without a service") + members = Member.objects.exclude(status='not_member').filter(offersubscription=None, balance__gte=settings.DEFAULT_MEMBERSHIP_FEE).distinct('username') + for member in members: + membership_fee = membership_fees.filter(member=member) + start_date = None + past_year = datetime.date.today() + relativedelta(years=-1) + if not (membership_fee and membership_fee[0].end_date > datetime.date.today()): + continue + if membership_fee and past_year < membership_fee[0].end_date: + start_date = membership_fee[0].end_date + + last_payment = Payment.objects.filter(member=member, amount__gte=settings.DEFAULT_MEMBERSHIP_FEE, date__gte=past_year).order_by('-date') + if last_payment: + start_date = last_payment[0].date + + + if start_date: + self.stdout.write("Create MembershipFee for {username} {date}".format(username=member.username,date=start_date)) + fee = MembershipFee(member=member, _amount=settings.DEFAULT_MEMBERSHIP_FEE, + start_date=start_date, + end_date=start_date + relativedelta(years=1)) + fee.save() + member.status='member' + member.save() + + -- GitLab From 5191b9ebdd71bf24561971b6659998bbf41f12a6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 1 Jul 2020 20:26:58 +0200 Subject: [PATCH 065/195] The right setting is DEFAULT_MEMBERSHIP_FEE --- coin/billing/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coin/billing/models.py b/coin/billing/models.py index a36b192..335bcfc 100644 --- a/coin/billing/models.py +++ b/coin/billing/models.py @@ -497,7 +497,7 @@ class Donation(Bill): class MembershipFee(Bill): _amount = models.DecimalField(null=False, max_digits=8, decimal_places=2, - default=settings.MEMBER_DEFAULT_COTISATION, + default=settings.DEFAULT_MEMBERSHIP_FEE, verbose_name='montant', help_text='en €') start_date = models.DateField( null=False, -- GitLab From 7c7f594d12e5a216e346b3bff1ea938f1f0887db Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 1 Sep 2019 23:21:01 +0200 Subject: [PATCH 066/195] Fixes following rebase with now the new IPConfiguration class --- coin/configuration/admin.py | 5 ++++- coin/configuration/forms.py | 4 ++-- coin/configuration/models.py | 26 ++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/coin/configuration/admin.py b/coin/configuration/admin.py index 59d1028..d1acee6 100644 --- a/coin/configuration/admin.py +++ b/coin/configuration/admin.py @@ -73,6 +73,10 @@ class ChildConfigurationAdmin(PolymorphicChildModelAdmin): 'provisioned', 'status']})] + if hasattr(obj, "ipv4_endpoint") and hasattr(obj, "ipv6_endpoint"): + fields.append(["Adresses IP (endpoints)", + {"fields": tuple(["ipv4_endpoint", "ipv6_endpoint"])}]) + if hasattr(obj, "provisioning_infos") and obj.provisioning_infos: fields.append(["Informations pour le provisionnement", {"fields": tuple(obj.provisioning_infos)}]) @@ -144,5 +148,4 @@ class ChildConfigurationAdmin(PolymorphicChildModelAdmin): return HttpResponseRedirect(reverse('admin:configuration_configuration_change', args=(id,))) - admin.site.register(Configuration, ParentConfigurationAdmin) diff --git a/coin/configuration/forms.py b/coin/configuration/forms.py index addabec..ca96f05 100644 --- a/coin/configuration/forms.py +++ b/coin/configuration/forms.py @@ -26,7 +26,7 @@ class ConfigurationForm(ModelForm): super(ConfigurationForm, self).__init__(*args, **kwargs) if self.instance: queryset = OfferSubscription.objects.filter( - Q(offer__configuration_type=self.instance.model_name) & ( + Q(offer__configuration_type=self.instance.configuration_type_name) & ( Q(configuration=None) | Q(configuration=self.instance.pk))) if 'offersubscription' in self.fields: self.fields['offersubscription'].queryset = queryset @@ -37,7 +37,7 @@ class ConfigurationForm(ModelForm): offer which use the same configuration type than the edited configuration. """ offersubscription = self.cleaned_data['offersubscription'] - if offersubscription.offer.configuration_type != self.instance.model_name(): + if offersubscription.offer.configuration_type != self.instance.configuration_type_name(): raise ValidationError('Administrative subscription must refer an offer having a "{}" configuration type.'.format(self.instance.model_name())) return offersubscription diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 2871077..563ba95 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -222,6 +222,32 @@ class Configuration(PolymorphicModel): verbose_name = 'configuration' +class IPConfiguration(Configuration): + ipv4_endpoint = InetAddressField(validators=[validation.validate_v4], + verbose_name="IPv4", blank=True, null=True, + help_text="Adresse IPv4 utilisée comme endpoint") + ipv6_endpoint = InetAddressField(validators=[validation.validate_v6], + verbose_name="IPv6", blank=True, null=True, + help_text="Adresse IPv6 utilisée comme endpoint") + objects = NetManager() + + def convert_to_dict_for_hook(self): + d = super(IPConfiguration, self).convert_to_dict_for_hook() + if self.ipv4_endpoint: + d["ipv4"] = str(self.ipv4_endpoint) + if self.ipv6_endpoint: + d["ipv6"] = str(self.ipv6_endpoint) + return d + + # This method is part of the general configuration interface. + def subnet_event(self): + self.check_endpoints(delete=True) + # We potentially changed the endpoints, so we need to save. Also, + # saving will update the subnets in the LDAP backend. + self.full_clean() + self.save() + + @receiver(post_save, sender=OfferSubscription) def offer_subscription_event(sender, **kwargs): os = kwargs['instance'] -- GitLab From 7a81f4c484398444bdbb035b3ecf1dbefd32bcc3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 30 Jun 2020 19:26:13 +0200 Subject: [PATCH 067/195] Add a comment to create toy data for development --- coin/configuration/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/fill_with_toy_data.py | 59 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 coin/configuration/management/__init__.py create mode 100644 coin/configuration/management/commands/__init__.py create mode 100644 coin/configuration/management/commands/fill_with_toy_data.py diff --git a/coin/configuration/management/__init__.py b/coin/configuration/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/coin/configuration/management/commands/__init__.py b/coin/configuration/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/coin/configuration/management/commands/fill_with_toy_data.py b/coin/configuration/management/commands/fill_with_toy_data.py new file mode 100644 index 0000000..9db04c3 --- /dev/null +++ b/coin/configuration/management/commands/fill_with_toy_data.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from argparse import RawTextHelpFormatter +from django.core.management.base import BaseCommand, CommandError +from django.contrib.auth.hashers import make_password + +from coin.resources.models import IPPool +from coin.offers.models import Offer, OfferSubscription, OfferIPPool +from coin.configuration.models import Configuration +from coin.members.models import Member + +################################################################################ + + +class Command(BaseCommand): + + help = __doc__ + + def create_parser(self, *args, **kwargs): + parser = super(Command, self).create_parser(*args, **kwargs) + parser.formatter_class = RawTextHelpFormatter + return parser + + def handle(self, *args, **options): + + Member.objects.all().delete() + IPPool.objects.all().delete() + Offer.objects.all().delete() + OfferIPPool.objects.all().delete() + + admin = Member.objects.create_superuser(username="admin", first_name="Syssa", last_name="Mine", email="admin@yolo.test", password="Yunohost") + member1 = Member.objects.create(username="sasha", first_name="Sasha", last_name="Yolo", email="sasha@yolo.test") + member2 = Member.objects.create(username="camille", first_name="Camille", last_name="Yolo", email="camille@yolo.test") + admin.save() + member1.save() + member2.save() + + pool1 = IPPool.objects.create(name="Pool d'IP pour VPS", default_subnetsize=32, inet="10.20.0.0/24") + pool2 = IPPool.objects.create(name="Pool d'IP pour VPN", default_subnetsize=32, inet="10.30.0.0/24") + pool1.save() + pool2.save() + + vps_de_test = Offer.objects.create(name="VPS de test", reference="vps-test", configuration_type="VPS", period_fees=0, initial_fees=0, non_billable=True) + vps_small = Offer.objects.create(name="VPS 1core 1G", reference="vps-1core-1G", configuration_type="VPS", period_fees=8, initial_fees=0, non_billable=False) + vps_big = Offer.objects.create(name="VPS 2core 2G", reference="vps-2core-2G", configuration_type="VPS", period_fees=15, initial_fees=0, non_billable=False) + vps_de_test.save() + vps_small.save() + vps_big.save() + OfferIPPool.objects.create(ip_pool=pool1, to_assign=True, offer=vps_de_test).save() + OfferIPPool.objects.create(ip_pool=pool1, to_assign=True, offer=vps_small).save() + OfferIPPool.objects.create(ip_pool=pool1, to_assign=True, offer=vps_big).save() + + vpn_de_test = Offer.objects.create(name="VPN de test", reference="vpn-test", configuration_type="VPN", period_fees=0, initial_fees=0, non_billable=True) + vpn_standard = Offer.objects.create(name="VPN standard", reference="vpn-standard", configuration_type="VPN", period_fees=4, initial_fees=0, non_billable=False) + vpn_de_test.save() + vpn_standard.save() + OfferIPPool.objects.create(ip_pool=pool2, to_assign=True, offer=vpn_de_test).save() + OfferIPPool.objects.create(ip_pool=pool2, to_assign=True, offer=vpn_standard).save() -- GitLab From aee62b95fd9a333f28d1c5fde3fb40f9048e09dc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 2 Jul 2020 15:40:58 +0200 Subject: [PATCH 068/195] Fix small issue with old mixin class --- coin/configuration/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coin/configuration/admin.py b/coin/configuration/admin.py index d1acee6..a0de73c 100644 --- a/coin/configuration/admin.py +++ b/coin/configuration/admin.py @@ -104,7 +104,7 @@ class ChildConfigurationAdmin(PolymorphicChildModelAdmin): """ Custom admin urls """ - urls = super(ConfigurationAdminFormMixin, self).get_urls() + urls = super(ChildConfigurationAdmin, self).get_urls() return urls + [ url(r'^provision/(?P.+)$', self.admin_site.admin_view(self.provision_view), -- GitLab From acd99c6284e157367c74fa929bde005b317fdfc6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 2 Jul 2020 15:42:53 +0200 Subject: [PATCH 069/195] Forgot to fix that conflict.. --- vpn/models.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/vpn/models.py b/vpn/models.py index 5721eff..b197e58 100644 --- a/vpn/models.py +++ b/vpn/models.py @@ -154,27 +154,3 @@ class VPNConfiguration(Configuration): class Meta: verbose_name = 'VPN' verbose_name_plural = 'VPN' -<<<<<<< HEAD - - -class LdapVPNConfig(ldapdb.models.Model): - base_dn = settings.VPN_CONF_BASE_DN - object_classes = ['person', 'organizationalPerson', 'inetOrgPerson', - 'top', 'radiusprofile'] - - login = CharField(db_column='cn', primary_key=True, max_length=255) - sn = CharField(db_column='sn', max_length=255) - password = CharField(db_column='userPassword', max_length=255) - active = CharField(db_column='dialupAccess', max_length=3) - ipv4_endpoint = CharField(db_column='radiusFramedIPAddress', max_length=16) - ipv6_endpoint = CharField(db_column='postalAddress', max_length=40) - ranges_v4 = ListField(db_column='radiusFramedRoute') - ranges_v6 = ListField(db_column='registeredAddress') - - def __unicode__(self): - return self.login - - class Meta: - managed = False # Indique à South de ne pas gérer le model LdapUser -======= ->>>>>>> provisioning-and-state-for-configurations -- GitLab From ca7c55e271c67ded572253da92da133ee658c182 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 2 Jul 2020 15:46:03 +0200 Subject: [PATCH 070/195] Missing InetAddressField import --- coin/configuration/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 563ba95..a475737 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -7,6 +7,8 @@ import StringIO as io import logging +from netfields import InetAddressField + from django.db import models from polymorphic import PolymorphicModel from coin.offers.models import OfferSubscription -- GitLab From 47b6490e6bbd097baa8003de491183ecd2f27498 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 2 Jul 2020 16:16:41 +0200 Subject: [PATCH 071/195] This warning is now fixed I believe --- housing/models.py | 6 ------ vpn/models.py | 7 ------- vps/models.py | 7 ------- 3 files changed, 20 deletions(-) diff --git a/housing/models.py b/housing/models.py index 2530e66..2cb056e 100644 --- a/housing/models.py +++ b/housing/models.py @@ -11,12 +11,6 @@ from coin.configuration.models import IPConfiguration # from coin.offers.backends import ValidateBackendType from coin import validation -"""BIG FAT WARNING - -Ce code requiert une sévère factorisation avec vpn/models.py et vps/models.py - -""" - class HousingConfiguration(IPConfiguration): url_namespace = "housing" diff --git a/vpn/models.py b/vpn/models.py index b762ff8..bb87adb 100644 --- a/vpn/models.py +++ b/vpn/models.py @@ -14,13 +14,6 @@ from coin.configuration.models import IPConfiguration from coin import utils from coin import validation -"""BIG FAT WARNING - -Ce code requiert une sévère factorisation avec housing/models.py et -vps/models.py - -""" - class VPNConfiguration(CoinLdapSyncMixin, IPConfiguration): url_namespace = "vpn" diff --git a/vps/models.py b/vps/models.py index a2fe4a6..7a6d258 100644 --- a/vps/models.py +++ b/vps/models.py @@ -12,13 +12,6 @@ from coin.configuration.models import IPConfiguration from coin import validation -"""BIG FAT WARNING - -Ce code requiert une sévère factorisation avec vpn/models.py et -housing/models.py - -""" - FINGERPRINT_TYPES = ( ('ED25519', 'ED25519'), ('RSA', 'RSA'), -- GitLab From 3fb054f92be761e24a06a10de1ae14a2587c197f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 2 Jul 2020 16:20:01 +0200 Subject: [PATCH 072/195] Fix / uniformize ModelForm usage --- coin/configuration/forms.py | 5 ++--- coin/isp_database/admin.py | 4 ++-- coin/offers/forms.py | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/coin/configuration/forms.py b/coin/configuration/forms.py index ca96f05..1d5c77c 100644 --- a/coin/configuration/forms.py +++ b/coin/configuration/forms.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.forms import ModelForm, ValidationError from django.db.models import Q from django import forms @@ -9,7 +8,7 @@ from coin.offers.models import OfferSubscription from coin.configuration.models import Configuration -class ConfigurationForm(ModelForm): +class ConfigurationForm(forms.ModelForm): class Meta: model = Configuration widgets = { @@ -38,6 +37,6 @@ class ConfigurationForm(ModelForm): """ offersubscription = self.cleaned_data['offersubscription'] if offersubscription.offer.configuration_type != self.instance.configuration_type_name(): - raise ValidationError('Administrative subscription must refer an offer having a "{}" configuration type.'.format(self.instance.model_name())) + raise forms.ValidationError('Administrative subscription must refer an offer having a "{}" configuration type.'.format(self.instance.model_name())) return offersubscription diff --git a/coin/isp_database/admin.py b/coin/isp_database/admin.py index 5b8e040..0305598 100644 --- a/coin/isp_database/admin.py +++ b/coin/isp_database/admin.py @@ -2,13 +2,13 @@ from __future__ import unicode_literals from django.contrib import admin -from django.forms import ModelForm +from django import forms from localflavor.fr.forms import FRPhoneNumberField from coin.isp_database.models import ISPInfo, RegisteredOffice, OtherWebsite, ChatRoom, CoveredArea, BankInfo -class ISPAdminForm(ModelForm): +class ISPAdminForm(forms.ModelForm): class Meta: model = ISPInfo diff --git a/coin/offers/forms.py b/coin/offers/forms.py index dd329c5..6f18803 100644 --- a/coin/offers/forms.py +++ b/coin/offers/forms.py @@ -4,7 +4,7 @@ from django.forms.widgets import Select from coin.offers.models import Offer, OfferSubscriptionRequest from coin.configuration.models import Configuration -class OfferAdminForm(ModelForm): +class OfferAdminForm(forms.ModelForm): class Meta: model = Offer widgets = { -- GitLab From b216829248756c3a9b661dd8eda0312efe28cc8d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 3 Jul 2020 16:33:29 +0200 Subject: [PATCH 073/195] Integrate subcription requests into vpn, vps --- coin/configuration/admin.py | 1 + coin/configuration/models.py | 45 ++++++++++++- .../templates/members/subscriptions.html | 8 +-- vpn/admin.py | 8 ++- vpn/models.py | 12 +++- vps/admin.py | 30 ++++++++- vps/models.py | 65 ++++++++++++++++++- vps/views.py | 1 + 8 files changed, 155 insertions(+), 15 deletions(-) diff --git a/coin/configuration/admin.py b/coin/configuration/admin.py index a0de73c..0603aad 100644 --- a/coin/configuration/admin.py +++ b/coin/configuration/admin.py @@ -148,4 +148,5 @@ class ChildConfigurationAdmin(PolymorphicChildModelAdmin): return HttpResponseRedirect(reverse('admin:configuration_configuration_change', args=(id,))) + admin.site.register(Configuration, ParentConfigurationAdmin) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index d4c0f65..2a7bd96 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -7,7 +7,7 @@ import StringIO as io import logging -from netfields import InetAddressField +from netfields import InetAddressField, NetManager from django.db import models from polymorphic import PolymorphicModel @@ -21,8 +21,6 @@ from django.core.exceptions import ValidationError from coin.hooks import HookManager from coin.resources.models import IPSubnet -from coin.utils import get_descendant_classes -from netfields import InetAddressField, NetManager from coin import validation """ @@ -227,6 +225,47 @@ class Configuration(PolymorphicModel): config = super(Configuration, self).save(**kwargs) return config + # + # Standard hook to fetch states + # + + def _has_hook_state(self): + return HookManager.is_defined(self.configuration_type_name, "GET_STATE") + + def _get_state(self, key=None): + if self._has_hook_state(): + # FIXME : gotta see what info to feed exactly + state = HookManager.run(self.configuration_type_name, "GET_STATE", self) + if key is None: + return state + else: + value = state.get(key, None) + return value if value else "N/A" + else: + return "Inconnu (pas de hook provisionné)" + + def is_provisioned(self): + return self._get_state("provisioned") + is_provisioned.short_description = 'Provisionné' + + def state_display(self): + return self._get_state("display") + state_display.short_description = 'État' + + # + # Standard hook to provision + # + + def _has_hook_provision(self): + return HookManager.is_defined(self.configuration_type_name, "PROVISION") + + def provision(self): + if self._has_hook_provision(): + # FIXME : gotta see what info to feed exactly + return HookManager.run(self.configuration_type_name, "PROVISION", self) + else: + raise Exception("No hook is defined to provision %ss" % self.configuration_type_name) + class Meta: verbose_name = 'configuration' diff --git a/coin/members/templates/members/subscriptions.html b/coin/members/templates/members/subscriptions.html index 57b2fd7..9fa2428 100644 --- a/coin/members/templates/members/subscriptions.html +++ b/coin/members/templates/members/subscriptions.html @@ -11,10 +11,10 @@ Type Offre - Date de souscription - Date de résiliation + Souscris le + Résilié le Commentaire - Configuration + @@ -26,7 +26,7 @@ {{ subscription.subscription_date }} {{ subscription.resign_date|default_if_none:"" }} {{ subscription.configuration.comment }} - {% if subscription.configuration and subscription.configuration.url_namespace %} Configuration{% endif %} + {% if subscription.configuration and subscription.configuration.url_namespace %} Infos / gérer{% endif %} {% endfor %} diff --git a/vpn/admin.py b/vpn/admin.py index f0322a9..74029e1 100644 --- a/vpn/admin.py +++ b/vpn/admin.py @@ -4,9 +4,10 @@ from __future__ import unicode_literals from django.contrib import admin from coin.configuration.admin import ChildConfigurationAdmin +from coin.offers.admin import ChildOfferSubscriptionRequestAdmin from coin.utils import delete_selected -from .models import VPNConfiguration +from .models import VPNConfiguration, VPNSubscriptionRequest class VPNConfigurationInline(admin.StackedInline): @@ -59,4 +60,9 @@ class VPNConfigurationAdmin(ChildConfigurationAdmin): self.generate_endpoints_generic(request, queryset, v4=False) generate_endpoints_v6.short_description = "Attribuer des adresses IPv6" +class VPNSubscriptionRequestAdmin(ChildOfferSubscriptionRequestAdmin): + + base_model = VPNSubscriptionRequest + + admin.site.register(VPNConfiguration, VPNConfigurationAdmin) diff --git a/vpn/models.py b/vpn/models.py index bb87adb..46d3be2 100644 --- a/vpn/models.py +++ b/vpn/models.py @@ -10,9 +10,8 @@ from netfields import InetAddressField, NetManager from coin.mixins import CoinLdapSyncMixin from coin.configuration.models import IPConfiguration -# from coin.offers.backends import ValidateBackendType +from coin.offers.models import OfferSubscriptionRequest from coin import utils -from coin import validation class VPNConfiguration(CoinLdapSyncMixin, IPConfiguration): @@ -90,3 +89,12 @@ class VPNConfiguration(CoinLdapSyncMixin, IPConfiguration): class Meta: verbose_name = 'VPN' verbose_name_plural = 'VPN' + + +class VPNSubscriptionRequest(OfferSubscriptionRequest): + + configuration_type = "VPNConfiguration" + + class Meta: + verbose_name = 'demande de VPN' + verbose_name_plural = 'demandes de VPN' diff --git a/vps/admin.py b/vps/admin.py index e306fa6..3f23bdb 100644 --- a/vps/admin.py +++ b/vps/admin.py @@ -4,9 +4,10 @@ from __future__ import unicode_literals from django.contrib import admin from coin.configuration.admin import ChildConfigurationAdmin +from coin.offers.admin import ChildOfferSubscriptionRequestAdmin from coin.utils import delete_selected -from .models import VPSConfiguration, FingerPrint, Console +from .models import VPSConfiguration, FingerPrint, Console, VPSSubscriptionRequest, VPSOperatingSystem class ConsoleInline(admin.TabularInline): @@ -60,5 +61,30 @@ class VPSConfigurationAdmin(ChildConfigurationAdmin): self.generate_endpoints_generic(request, queryset, v4=False) generate_endpoints_v6.short_description = "Generate IPv6 endpoints" -VPSConfigurationAdmin.inlines = VPSConfigurationAdmin.inlines + (FingerPrintInline, ConsoleInline ) +VPSConfigurationAdmin.inlines = VPSConfigurationAdmin.inlines + (FingerPrintInline, ConsoleInline) + + +class VPSOperatingSystemAdmin(admin.ModelAdmin): + + base_model = VPSOperatingSystem + list_display = ('display_name',) + + +class VPSSubscriptionRequestAdmin(ChildOfferSubscriptionRequestAdmin): + + base_model = VPSSubscriptionRequest + + def get_fieldsets(self, request, obj=None): + fieldsets = super(VPSSubscriptionRequestAdmin, self).get_fieldsets(request, obj) + fieldsets.insert(1, ("Informations pour le provisionnement", {"fields": ("operating_system", "ssh_key")})) + return fieldsets + + def get_readonly_fields(self, request, obj=None): + readonly_fields = super(VPSSubscriptionRequestAdmin, self).get_readonly_fields(request, obj) + if obj and obj.state != "pending": + readonly_fields += ["operating_system", "ssh_key"] + return readonly_fields + + admin.site.register(VPSConfiguration, VPSConfigurationAdmin) +admin.site.register(VPSOperatingSystem, VPSOperatingSystemAdmin) diff --git a/vps/models.py b/vps/models.py index 7a6d258..921c06b 100644 --- a/vps/models.py +++ b/vps/models.py @@ -8,8 +8,7 @@ from django.core.urlresolvers import reverse from netfields import InetAddressField, NetManager from coin.configuration.models import IPConfiguration -# from coin.offers.backends import ValidateBackendType -from coin import validation +from coin.offers.models import OfferSubscriptionRequest FINGERPRINT_TYPES = ( @@ -22,10 +21,62 @@ PROTOCOLE_TYPES = ( ('VNC', 'VNC'), ) +import struct +import base64 +from django.core.exceptions import ValidationError +def PublicSSHKeyValidator(ssh_pubkey): + if not isinstance(ssh_pubkey, basestring): + raise ValidationError("Une clef SSH devrait être une chaine de caractères?") + try: + # From https://stackoverflow.com/questions/2494450/ssh-rsa-public-key-validation-using-a-regular-expression + type_, key_string, comment = ssh_pubkey.split() + data = base64.decodestring(key_string) + int_len = 4 + str_len = struct.unpack('>I', data[:int_len])[0] # this should return 7 + data[int_len:int_len+str_len] == type_ + except: + raise ValidationError("Il semblerait qu'il y ai une erreur de syntaxe dans cette clef SSH") + + +class VPSOperatingSystem(models.Model): + + reference = models.CharField(max_length=64, + blank=False, null=False, + verbose_name="Nom technique (utilisé par les scripts)") + + display_name = models.CharField(max_length=128, verbose_name="Nom affiché aux êtres humains", + blank=False, null=False) + + def __unicode__(self): + return self.display_name + + class Meta: + verbose_name = 'Système d\'exploitation' + verbose_name_plural = 'Systèmes d\'exploitation' + + +class VPSProvisioningParameters(models.Model): + + operating_system = models.ForeignKey(VPSOperatingSystem, blank=True, null=True) + + ssh_key = models.TextField(max_length=1024, verbose_name="Clef SSH", + blank=True, null=True, + validators=[PublicSSHKeyValidator]) + + class Meta: + abstract = True + +class VPSConfiguration(IPConfiguration, VPSProvisioningParameters): -class VPSConfiguration(IPConfiguration): url_namespace = "vps" + # Subclasses can fill this list with stuff like "operating_system" or "ssh_key" or "password" + provisioning_infos = ["operating_system", "ssh_key"] + + @staticmethod + def request_via(): + return VPSSubscriptionRequest + def get_absolute_url(self): return reverse('vps:details', args=[str(self.pk)]) @@ -58,3 +109,11 @@ class Console(models.Model): class Meta: verbose_name = 'Console' + +class VPSSubscriptionRequest(OfferSubscriptionRequest, VPSProvisioningParameters): + + offer_type = "VPS" + + class Meta: + verbose_name = 'demande de VPS' + verbose_name_plural = 'demandes de VPS' diff --git a/vps/views.py b/vps/views.py index 7560acd..252f799 100644 --- a/vps/views.py +++ b/vps/views.py @@ -11,6 +11,7 @@ from django.utils.decorators import method_decorator from coin.members.models import Member from .models import VPSConfiguration +from .forms import VPSSubscriptionRequestStep2Form # We need to load this even if we don't actually use it ... but otherwise the code ain't loaded at all in django for some reason :| class VPSView(SuccessMessageMixin, UpdateView): -- GitLab From 8c3415d90df2d7b780223e3a8124e979baa6cbc9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 3 Jul 2020 16:59:04 +0200 Subject: [PATCH 074/195] Fix get_configurations_choices_list because now there's an intermediate class IPConfiguration in between --- coin/configuration/models.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 2a7bd96..5c04ab9 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -75,8 +75,21 @@ class Configuration(PolymorphicModel): Génère automatiquement la liste de choix possibles de configurations en fonction des classes enfants de Configuration """ - return tuple((x().__class__.__name__, x()._meta.verbose_name) - for x in Configuration.__subclasses__()) + def get_subclasses(class_): + subclasses = class_.__subclasses__() + # Is it a leaf class ? + + if not subclasses: + out = [(class_.__name__, class_._meta.verbose_name)] + # Otherwise we recursively get the subclasses of this class + else: + out = [] + for c in subclasses: + out += get_subclasses(c) + + return tuple(out) + + return get_subclasses(Configuration) def convert_to_dict_for_hook(self): # FIXME : check assertions here -- GitLab From 65f8162b2f0e6cb7988deb13abc7906d97f8eafc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 3 Jul 2020 16:59:46 +0200 Subject: [PATCH 075/195] Make sure to have a tuple to make django happy --- coin/offers/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coin/offers/admin.py b/coin/offers/admin.py index eb2ff5d..24c81e5 100644 --- a/coin/offers/admin.py +++ b/coin/offers/admin.py @@ -125,7 +125,7 @@ class ParentOfferSubscriptionRequestAdmin(PolymorphicParentModelAdmin): list_display = ('__unicode__', 'enhanced_state', 'request_date') - list_display_links = ('__unicode__') + list_display_links = ('__unicode__',) list_filter = ("state",) -- GitLab From 3533fd269dbf0ea9567228ffcd17aef10dc42b77 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 3 Jul 2020 17:17:31 +0200 Subject: [PATCH 076/195] Add missing custom form for VPS subscription request --- vps/forms.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 vps/forms.py diff --git a/vps/forms.py b/vps/forms.py new file mode 100644 index 0000000..810e782 --- /dev/null +++ b/vps/forms.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- + +from django import forms + +from coin.offers.forms import OfferSubscriptionRequestStep2Form +from .models import VPSOperatingSystem, VPSSubscriptionRequest, PublicSSHKeyValidator + + +class VPSSubscriptionRequestStep2Form(OfferSubscriptionRequestStep2Form): + + offer_type = "VPS" + subscriptionrequest_class = VPSSubscriptionRequest + + operating_system = forms.ModelChoiceField(queryset=VPSOperatingSystem.objects.all(), label="Système d'exploitation", required=True) + ssh_key = forms.CharField(label="Clef SSH", max_length=1024, widget=forms.Textarea(attrs={'style': 'height: 10em;'}), required=True, validators=[PublicSSHKeyValidator]) + + def __init__(self, *args, **kwargs): + super(VPSSubscriptionRequestStep2Form, self).__init__(*args, **kwargs) + # Put the comment + agree tos checkbox at the end of the form + member_comments = self.fields.pop("member_comments") + agree_tos = self.fields.pop("agree_tos") + self.fields["member_comments"] = member_comments + self.fields["agree_tos"] = agree_tos + + def create_offersubscriptionrequest(self, request): + subscriptionrequest = super(VPSSubscriptionRequestStep2Form, self).create_offersubscriptionrequest(request) + subscriptionrequest.operating_system = self.cleaned_data["operating_system"] + subscriptionrequest.ssh_key = self.cleaned_data["ssh_key"] + return subscriptionrequest -- GitLab From d42730db3ad85eb1b62ff348e6b3863936d066c9 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 2 Sep 2019 00:59:20 +0200 Subject: [PATCH 077/195] [fix] Error 500 on offer/config creation --- coin/configuration/models.py | 11 +++++------ coin/offers/admin.py | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 5c04ab9..9bc3aa2 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -233,11 +233,6 @@ class Configuration(PolymorphicModel): else: return self.model_name().lower() - def save(self, **kwargs): - self.fill_empty_fields() - config = super(Configuration, self).save(**kwargs) - return config - # # Standard hook to fetch states # @@ -371,8 +366,12 @@ class IPConfiguration(Configuration): if self.ipv4_endpoint is None or self.ipv6_endpoint is None: self.generate_endpoints() - def clean(self): + def save(self, **kwargs): self.fill_empty_fields() + config = super(IPConfiguration, self).save(**kwargs) + return config + + def clean(self): self.check_endpoints() def convert_to_dict_for_hook(self): diff --git a/coin/offers/admin.py b/coin/offers/admin.py index 24c81e5..2b853cf 100644 --- a/coin/offers/admin.py +++ b/coin/offers/admin.py @@ -113,7 +113,7 @@ class OfferSubscriptionAdmin(admin.ModelAdmin): if config_cls is not None: config = config_cls.objects.create(offersubscription=obj) - for offer_ip_pool in obj.offer.offerippool_set.order_by('-to_assign'): + for offer_ip_pool in obj.offer.offer_ip_pools.order_by('-to_assign'): IPSubnet.objects.create( configuration=config, ip_pool=offer_ip_pool.ip_pool) -- GitLab From a61dbc1b389bd5470159e6a191bb8ba0fda7ee9e Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 2 Sep 2019 02:06:01 +0200 Subject: [PATCH 078/195] [enh] Generate subnet in all cases unitl there is no pools associated --- coin/configuration/models.py | 8 +++++++- coin/offers/admin.py | 4 ---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 9bc3aa2..9764eea 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -361,14 +361,20 @@ class IPConfiguration(Configuration): raise ValidationError(error.format(self.ipv6_endpoint)) def fill_empty_fields(self): + if not self.ip_subnet.all(): + for offer_ip_pool in self.offersubscription.offer.offer_ip_pools.order_by('-to_assign'): + IPSubnet.objects.create( + configuration=self, + ip_pool=offer_ip_pool.ip_pool) # If IP endpoints are not specified, # generate them automatically. if self.ipv4_endpoint is None or self.ipv6_endpoint is None: self.generate_endpoints() + super(IPConfiguration, self).save() def save(self, **kwargs): - self.fill_empty_fields() config = super(IPConfiguration, self).save(**kwargs) + self.fill_empty_fields() return config def clean(self): diff --git a/coin/offers/admin.py b/coin/offers/admin.py index 2b853cf..df5bd67 100644 --- a/coin/offers/admin.py +++ b/coin/offers/admin.py @@ -113,10 +113,6 @@ class OfferSubscriptionAdmin(admin.ModelAdmin): if config_cls is not None: config = config_cls.objects.create(offersubscription=obj) - for offer_ip_pool in obj.offer.offer_ip_pools.order_by('-to_assign'): - IPSubnet.objects.create( - configuration=config, - ip_pool=offer_ip_pool.ip_pool) config.save() class ParentOfferSubscriptionRequestAdmin(PolymorphicParentModelAdmin): -- GitLab From 508da7a2d6fa78a7325eb8c17c281f350f43c0d3 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 2 Sep 2019 02:12:48 +0200 Subject: [PATCH 079/195] [fix] Avoid 500 if clean trigger a validation error --- coin/resources/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coin/resources/models.py b/coin/resources/models.py index a17ebe3..8953e89 100644 --- a/coin/resources/models.py +++ b/coin/resources/models.py @@ -124,7 +124,8 @@ class IPSubnet(models.Model): self.validate_reverse_dns() def save(self, **kwargs): - self.clean() + if not self.inet: + self.allocate() return super(IPSubnet, self).save(**kwargs) def __unicode__(self): -- GitLab From 2110090c7b1c6551e65e9215c3825d1cfa940f04 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 2 Sep 2019 02:14:26 +0200 Subject: [PATCH 080/195] Apply suggestion to vpn/models.py --- vpn/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpn/models.py b/vpn/models.py index 46d3be2..0a2370d 100644 --- a/vpn/models.py +++ b/vpn/models.py @@ -78,7 +78,7 @@ class VPNConfiguration(CoinLdapSyncMixin, IPConfiguration): IPConfiguration.fill_empty_fields(self) def clean(self): - IPConfiguration.clean(self) + super(VPNConfiguration, self).clean() # We may have failed. if not self.login: ValidationError("Impossible de générer un login VPN") -- GitLab From 8fe028d9e3d9dfb8b14e27503027018fbc4ad558 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 2 Sep 2019 02:15:08 +0200 Subject: [PATCH 081/195] Apply suggestion to vpn/models.py --- vpn/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpn/models.py b/vpn/models.py index 0a2370d..a2f5c9a 100644 --- a/vpn/models.py +++ b/vpn/models.py @@ -75,7 +75,7 @@ class VPNConfiguration(CoinLdapSyncMixin, IPConfiguration): break # Hash password if needed self.password = utils.ldap_hash(self.password) - IPConfiguration.fill_empty_fields(self) + super(VPNConfiguration, self).fill_empty_fields() def clean(self): super(VPNConfiguration, self).clean() -- GitLab From 5cd725262ac28a100a9ca2204d06e03ce7bf357c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 3 Jul 2020 21:57:26 +0200 Subject: [PATCH 082/195] Fix/clean initialization of configuration when accepting the subscription request --- coin/configuration/models.py | 17 ++------------- coin/offers/admin.py | 11 +--------- coin/offers/models.py | 40 ++++++++++++++++++++++++++++-------- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 9764eea..6044743 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -19,6 +19,7 @@ from django.conf import settings from django.utils.safestring import mark_safe from django.core.exceptions import ValidationError +from coin.utils import get_descendant_classes from coin.hooks import HookManager from coin.resources.models import IPSubnet from coin import validation @@ -75,21 +76,7 @@ class Configuration(PolymorphicModel): Génère automatiquement la liste de choix possibles de configurations en fonction des classes enfants de Configuration """ - def get_subclasses(class_): - subclasses = class_.__subclasses__() - # Is it a leaf class ? - - if not subclasses: - out = [(class_.__name__, class_._meta.verbose_name)] - # Otherwise we recursively get the subclasses of this class - else: - out = [] - for c in subclasses: - out += get_subclasses(c) - - return tuple(out) - - return get_subclasses(Configuration) + return tuple([(c.__name__, c._meta.verbose_name) for c in get_descendant_classes(Configuration)]) def convert_to_dict_for_hook(self): # FIXME : check assertions here diff --git a/coin/offers/admin.py b/coin/offers/admin.py index df5bd67..deeaa62 100644 --- a/coin/offers/admin.py +++ b/coin/offers/admin.py @@ -19,7 +19,6 @@ from coin.offers.offersubscription_filter import\ OfferSubscriptionCommitmentFilter from coin.offers.forms import OfferAdminForm from coin.configuration.admin import ChildConfigurationAdmin -from coin.utils import get_descendant_classes from coin.configuration.models import Configuration from coin.resources.models import IPSubnet @@ -104,16 +103,8 @@ class OfferSubscriptionAdmin(admin.ModelAdmin): if request.GET.get('_popup', 0): return - if not hasattr(obj, 'configuration'): - config_cls = None - for subconfig_cls in get_descendant_classes(Configuration): - if subconfig_cls().__class__.__name__ == obj.offer.configuration_type: - config_cls = subconfig_cls - break + obj.create_config_if_it_does_not_exists_yet() - if config_cls is not None: - config = config_cls.objects.create(offersubscription=obj) - config.save() class ParentOfferSubscriptionRequestAdmin(PolymorphicParentModelAdmin): diff --git a/coin/offers/models.py b/coin/offers/models.py index ec4dd2b..3874d42 100644 --- a/coin/offers/models.py +++ b/coin/offers/models.py @@ -11,6 +11,7 @@ from django.db.models import Count, Q from django.core.validators import MinValueValidator from django.contrib.contenttypes.models import ContentType +from coin.utils import get_descendant_classes from coin.resources.models import IPPool @@ -80,9 +81,9 @@ class Offer(models.Model): This translates for example "VPNConfiguration" into "VPN" ... """ from coin.configuration.models import Configuration - for item in Configuration.get_configurations_choices_list(): - if item and item[0] == configuration_type: - return item[1] + for subconfig_class in get_descendant_classes(Configuration): + if subconfig_class.__name__ == configuration_type: + return subconfig_class._meta.verbose_name return configuration_type @staticmethod @@ -91,11 +92,21 @@ class Offer(models.Model): This translates for example "VPN" into "VPNConfiguration" ... """ from coin.configuration.models import Configuration - for item in Configuration.get_configurations_choices_list(): - if item and item[1] == offer_type: - return item[0] + for subconfig_class in get_descendant_classes(Configuration): + if subconfig_class._meta.verbose_name == offer_type: + return subconfig_class.__name__ return offer_type + def _get_configuration_class(self): + """ + This translate an offer into its corresponding configuration class (e.g. VPNConfiguration) + """ + from coin.configuration.models import Configuration + for subconfig_class in get_descendant_classes(Configuration): + if subconfig_class.__name__ == self.configuration_type: + return subconfig_class + return None + def display_price(self): """Displays the price of an offer in a human-readable manner (for instance "30€ / month") @@ -197,6 +208,17 @@ class OfferSubscription(models.Model): return settings.SUBSCRIPTION_REFERENCE.format(subscription=self) get_subscription_reference.short_description = 'Référence' + def create_config_if_it_does_not_exists_yet(self): + if not hasattr(self, 'configuration'): + config_class = self.offer._get_configuration_class() + if config_class is not None: + config = config_class.objects.create(offersubscription=self) + config.offersubscription = self + config.save() + return True + + return False + def __unicode__(self): return '%s - %s - %s' % (self.member, self.offer.name, self.subscription_date) @@ -273,13 +295,15 @@ class OfferSubscriptionRequest(Request): def accept(self): - self.subscription = OfferSubscription.objects.create(offer=self.offer, + self.offersubscription = OfferSubscription.objects.create(offer=self.offer, member=self.member, comments=self.admin_comments) - self.subscription.save() + self.offersubscription.save() super(OfferSubscriptionRequest, self).accept() + self.offersubscription.create_config_if_it_does_not_exists_yet() + @property def configuration_type(self): return Offer._get_configuration_type(self.offer_type) -- GitLab From 6ee3869d27bd41b3f7275f637d671eea9cfd364e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 3 Jul 2020 22:03:15 +0200 Subject: [PATCH 083/195] Cleanup of stuff that seems unused / not necessary ... --- coin/configuration/admin.py | 2 +- coin/configuration/models.py | 20 -------------------- housing/admin.py | 25 +------------------------ vpn/admin.py | 25 +------------------------ vps/admin.py | 27 ++------------------------- vps/models.py | 4 ---- 6 files changed, 5 insertions(+), 98 deletions(-) diff --git a/coin/configuration/admin.py b/coin/configuration/admin.py index 0603aad..c011fe1 100644 --- a/coin/configuration/admin.py +++ b/coin/configuration/admin.py @@ -63,7 +63,7 @@ class ChildConfigurationAdmin(PolymorphicChildModelAdmin): base_form = ConfigurationForm # For each child (admin object for configurations), this will display # an inline form to assign IP addresses. - inlines = (IPSubnetInline, ) + #inlines = (IPSubnetInline, ) change_form_template = "admin/configuration/configuration/change_form.html" diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 6044743..b1855d6 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -379,26 +379,6 @@ class IPConfiguration(Configuration): abstract = True -@receiver(post_save, sender=OfferSubscription) -def offer_subscription_event(sender, **kwargs): - os = kwargs['instance'] - - if not hasattr(os, 'configuration'): - config_cls = None - for subconfig_cls in Configuration.__subclasses__(): - if subconfig_cls().__class__.__name__ == os.offer.configuration_type: - config_cls = subconfig_cls - break - - if config_cls is not None: - config = config_cls.objects.create(offersubscription=os) - for offer_ip_pool in os.offer.offerippool_set.order_by('-to_assign'): - IPSubnet.objects.create( - configuration=config, - ip_pool=offer_ip_pool.ip_pool) - config.save() - - @receiver(post_save, sender=IPSubnet) def subnet_event_save(sender, **kwargs): kwargs["signal_type"] = "save" diff --git a/housing/admin.py b/housing/admin.py index 9451d93..00aca9c 100644 --- a/housing/admin.py +++ b/housing/admin.py @@ -25,30 +25,7 @@ class HousingConfigurationAdmin(ChildConfigurationAdmin): 'offersubscription__member__first_name', 'offersubscription__member__last_name', 'offersubscription__member__email') - actions = (delete_selected, "generate_endpoints", "generate_endpoints_v4", - "generate_endpoints_v6") + actions = (delete_selected,) inline = HousingConfigurationInline - def generate_endpoints_generic(self, request, queryset, v4=True, v6=True): - count = 0 - for housing in queryset: - if housing.generate_endpoints(v4, v6): - housing.full_clean() - housing.save() - count += 1 - msg = "{} Housing subscription(s) updated.".format(count) - self.message_user(request, msg) - - def generate_endpoints(self, request, queryset): - self.generate_endpoints_generic(request, queryset) - generate_endpoints.short_description = "Generate IPv4 and IPv6 endpoints" - - def generate_endpoints_v4(self, request, queryset): - self.generate_endpoints_generic(request, queryset, v6=False) - generate_endpoints_v4.short_description = "Generate IPv4 endpoints" - - def generate_endpoints_v6(self, request, queryset): - self.generate_endpoints_generic(request, queryset, v4=False) - generate_endpoints_v6.short_description = "Generate IPv6 endpoints" - admin.site.register(HousingConfiguration, HousingConfigurationAdmin) diff --git a/vpn/admin.py b/vpn/admin.py index 74029e1..7e8d93d 100644 --- a/vpn/admin.py +++ b/vpn/admin.py @@ -26,8 +26,7 @@ class VPNConfigurationAdmin(ChildConfigurationAdmin): 'offersubscription__member__first_name', 'offersubscription__member__last_name', 'offersubscription__member__email') - actions = (delete_selected, "generate_endpoints", "generate_endpoints_v4", - "generate_endpoints_v6") + actions = (delete_selected,) exclude = ("password",) inline = VPNConfigurationInline @@ -38,28 +37,6 @@ class VPNConfigurationAdmin(ChildConfigurationAdmin): readonly_fields.append('login') return readonly_fields - def generate_endpoints_generic(self, request, queryset, v4=True, v6=True): - count = 0 - for vpn in queryset: - if vpn.generate_endpoints(v4, v6): - vpn.full_clean() - vpn.save() - count += 1 - msg = "{} VPN subscription(s) updated.".format(count) - self.message_user(request, msg) - - def generate_endpoints(self, request, queryset): - self.generate_endpoints_generic(request, queryset) - generate_endpoints.short_description = "Attribuer des adresses IPv4 et IPv6" - - def generate_endpoints_v4(self, request, queryset): - self.generate_endpoints_generic(request, queryset, v6=False) - generate_endpoints_v4.short_description = "Attribuer des adresses IPv4" - - def generate_endpoints_v6(self, request, queryset): - self.generate_endpoints_generic(request, queryset, v4=False) - generate_endpoints_v6.short_description = "Attribuer des adresses IPv6" - class VPNSubscriptionRequestAdmin(ChildOfferSubscriptionRequestAdmin): base_model = VPNSubscriptionRequest diff --git a/vps/admin.py b/vps/admin.py index 3f23bdb..2fc74b0 100644 --- a/vps/admin.py +++ b/vps/admin.py @@ -35,33 +35,10 @@ class VPSConfigurationAdmin(ChildConfigurationAdmin): 'offersubscription__member__first_name', 'offersubscription__member__last_name', 'offersubscription__member__email') - actions = (delete_selected, "generate_endpoints", "generate_endpoints_v4", - "generate_endpoints_v6") + actions = (delete_selected,) inline = VPSConfigurationInline - def generate_endpoints_generic(self, request, queryset, v4=True, v6=True): - count = 0 - for vps in queryset: - if vps.generate_endpoints(v4, v6): - vps.full_clean() - vps.save() - count += 1 - msg = "{} VPS subscription(s) updated.".format(count) - self.message_user(request, msg) - - def generate_endpoints(self, request, queryset): - self.generate_endpoints_generic(request, queryset) - generate_endpoints.short_description = "Generate IPv4 and IPv6 endpoints" - - def generate_endpoints_v4(self, request, queryset): - self.generate_endpoints_generic(request, queryset, v6=False) - generate_endpoints_v4.short_description = "Generate IPv4 endpoints" - - def generate_endpoints_v6(self, request, queryset): - self.generate_endpoints_generic(request, queryset, v4=False) - generate_endpoints_v6.short_description = "Generate IPv6 endpoints" - -VPSConfigurationAdmin.inlines = VPSConfigurationAdmin.inlines + (FingerPrintInline, ConsoleInline) +VPSConfigurationAdmin.inlines = tuple(VPSConfigurationAdmin.inlines) + (FingerPrintInline, ConsoleInline) class VPSOperatingSystemAdmin(admin.ModelAdmin): diff --git a/vps/models.py b/vps/models.py index 921c06b..f6b5e2c 100644 --- a/vps/models.py +++ b/vps/models.py @@ -73,10 +73,6 @@ class VPSConfiguration(IPConfiguration, VPSProvisioningParameters): # Subclasses can fill this list with stuff like "operating_system" or "ssh_key" or "password" provisioning_infos = ["operating_system", "ssh_key"] - @staticmethod - def request_via(): - return VPSSubscriptionRequest - def get_absolute_url(self): return reverse('vps:details', args=[str(self.pk)]) -- GitLab From 0e8e49003a49c3c9376c5201044afc0775d2c66a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 3 Jul 2020 23:20:17 +0200 Subject: [PATCH 084/195] Save provisioning infos when accepting an offer subscription request --- coin/offers/models.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/coin/offers/models.py b/coin/offers/models.py index 3874d42..a4f1c7d 100644 --- a/coin/offers/models.py +++ b/coin/offers/models.py @@ -304,6 +304,12 @@ class OfferSubscriptionRequest(Request): self.offersubscription.create_config_if_it_does_not_exists_yet() + # If there are some provisioning_infos to transmit to the created configuration + config = self.offersubscription.configuration + for info in config.provisioning_infos: + setattr(config, info, getattr(self, info)) + config.save() + @property def configuration_type(self): return Offer._get_configuration_type(self.offer_type) -- GitLab From 9722cd8c996abb6ce20344d7337424cea69e28d2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 3 Jul 2020 23:21:01 +0200 Subject: [PATCH 085/195] Fix an issue where the offersubscription field was empty when looking at a configuration in the admin --- coin/configuration/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coin/configuration/forms.py b/coin/configuration/forms.py index 1d5c77c..9cdf8f9 100644 --- a/coin/configuration/forms.py +++ b/coin/configuration/forms.py @@ -23,7 +23,7 @@ class ConfigurationForm(forms.ModelForm): and that haven't already a configuration associated with """ super(ConfigurationForm, self).__init__(*args, **kwargs) - if self.instance: + if self.instance and not hasattr(self.instance, "offersubscription"): queryset = OfferSubscription.objects.filter( Q(offer__configuration_type=self.instance.configuration_type_name) & ( Q(configuration=None) | Q(configuration=self.instance.pk))) -- GitLab From 43db228eeaacf4ee041b742aced532d834ccd54a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 4 Jul 2020 18:24:04 +0200 Subject: [PATCH 086/195] Improve inline admin for configurations --- coin/configuration/admin.py | 40 +++++++++++++++++++++++++++++++++++++ housing/admin.py | 5 ++--- vpn/admin.py | 5 ++--- vps/admin.py | 5 ++--- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/coin/configuration/admin.py b/coin/configuration/admin.py index c011fe1..2ffd430 100644 --- a/coin/configuration/admin.py +++ b/coin/configuration/admin.py @@ -148,5 +148,45 @@ class ChildConfigurationAdmin(PolymorphicChildModelAdmin): return HttpResponseRedirect(reverse('admin:configuration_configuration_change', args=(id,))) +class ChildConfigurationAdminInline(admin.StackedInline): + + def get_fieldsets(self, request, obj=None): + + fields = [('', {'fields': ('comment', + 'provisioned', + 'status')})] + + conf = obj.configuration + + if hasattr(conf, "ipv4_endpoint") and hasattr(conf, "ipv6_endpoint"): + fields.append(["Adresses IP (endpoints)", + {"fields": tuple(["ipv4_endpoint", "ipv6_endpoint"])}]) + + if hasattr(conf, "provisioning_infos") and conf.provisioning_infos: + fields.append(["Informations pour le provisionnement", + {"fields": tuple(conf.provisioning_infos)}]) + + return fields + + def get_readonly_fields(self, request, obj=None): + + readonly_fields = [] + if self.model.provision_is_managed_via_hook(): + readonly_fields.append("provisioned") + + if self.model.state_is_managed_via_hook(): + readonly_fields.append("status") + + conf = obj.configuration + + # If this config is provisioned, we shouldn't change the subscription or the provisioning infos + if conf and conf.provisioned != "no": + readonly_fields.append("offersubscription") + if hasattr(conf, "provisioning_infos") and conf.provisioning_infos: + readonly_fields.extend(conf.provisioning_infos) + + return readonly_fields + + admin.site.register(Configuration, ParentConfigurationAdmin) diff --git a/housing/admin.py b/housing/admin.py index 00aca9c..09f23bd 100644 --- a/housing/admin.py +++ b/housing/admin.py @@ -3,16 +3,15 @@ from __future__ import unicode_literals from django.contrib import admin -from coin.configuration.admin import ChildConfigurationAdmin +from coin.configuration.admin import ChildConfigurationAdmin, ChildConfigurationAdminInline from coin.utils import delete_selected from .models import HousingConfiguration -class HousingConfigurationInline(admin.StackedInline): +class HousingConfigurationInline(ChildConfigurationAdminInline): model = HousingConfiguration - # fk_name = 'offersubscription' readonly_fields = ['configuration_ptr'] diff --git a/vpn/admin.py b/vpn/admin.py index 7e8d93d..96748eb 100644 --- a/vpn/admin.py +++ b/vpn/admin.py @@ -3,16 +3,15 @@ from __future__ import unicode_literals from django.contrib import admin -from coin.configuration.admin import ChildConfigurationAdmin +from coin.configuration.admin import ChildConfigurationAdmin, ChildConfigurationAdminInline from coin.offers.admin import ChildOfferSubscriptionRequestAdmin from coin.utils import delete_selected from .models import VPNConfiguration, VPNSubscriptionRequest -class VPNConfigurationInline(admin.StackedInline): +class VPNConfigurationInline(ChildConfigurationAdminInline): model = VPNConfiguration - # fk_name = 'offersubscription' exclude = ('password',) readonly_fields = ['configuration_ptr', 'login'] diff --git a/vps/admin.py b/vps/admin.py index 2fc74b0..d74b9b8 100644 --- a/vps/admin.py +++ b/vps/admin.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.contrib import admin -from coin.configuration.admin import ChildConfigurationAdmin +from coin.configuration.admin import ChildConfigurationAdmin, ChildConfigurationAdminInline from coin.offers.admin import ChildOfferSubscriptionRequestAdmin from coin.utils import delete_selected @@ -20,9 +20,8 @@ class FingerPrintInline(admin.TabularInline): extra = 0 -class VPSConfigurationInline(admin.StackedInline): +class VPSConfigurationInline(ChildConfigurationAdminInline): model = VPSConfiguration - # fk_name = 'offersubscription' readonly_fields = ['configuration_ptr'] -- GitLab From 1679e2148c84e6d5576a9a251a2cd7ec39db4eda Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 4 Jul 2020 18:24:22 +0200 Subject: [PATCH 087/195] Remove unused stuff about trafic graph --- vpn/templates/vpn/vpn.html | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/vpn/templates/vpn/vpn.html b/vpn/templates/vpn/vpn.html index a66b4a1..452d203 100644 --- a/vpn/templates/vpn/vpn.html +++ b/vpn/templates/vpn/vpn.html @@ -106,18 +106,6 @@ {% endif %}
    -
    -

    Graphe de trafic sur - :

    - Graphe de trafic {{ object.login }} -
    - {% endblock %} {% block extra_js %} @@ -140,16 +128,5 @@ return false; }); - - // Graphe de conso data - $('#graph_period').change(function(){ - $('#graph .pending_request').html(''); - base_url = "{% url 'vpn:get_graph' vpn_id=object.pk %}/"; - $('#graph_trafic').attr('src', base_url + $(this).val()); - }); - $('#graph_trafic').load(function() { - $('#graph .pending_request').html(''); - }); - {% endblock extra_js %} -- GitLab From e298a413da12bf26de2c1aa74b53f4418983b5e3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 4 Jul 2020 21:43:58 +0200 Subject: [PATCH 088/195] Yolocustomisation de l'index de l'admin --- coin/admin.py | 85 +++++++++++++++++++++++++++++++++ coin/static/css/admin-local.css | 42 ++++++++++++++++ coin/templates/admin/index.html | 61 +++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 coin/admin.py create mode 100644 coin/templates/admin/index.html diff --git a/coin/admin.py b/coin/admin.py new file mode 100644 index 0000000..2fff950 --- /dev/null +++ b/coin/admin.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.contrib import admin + +admin_cards = [ + + {"id": "members", + "icon": "users", + "title": "Membres", + "models": [ + ("Membres", "members/member"), + ("Groupes", "auth/group"), + ("Permissions", "members/rowlevelpermission"), + ] + }, + + {"id": "services", + "icon": "shopping-cart", + "title": "Services", + "models": [ + ("Offres", "offers/offer"), + ("Demandes", "offers/offersubscriptionrequest"), + ("Abonnements", "offers/offersubscription"), + ] + }, + + {"id": "configs", + "icon": "gears", + "title": "Configurations", + "models": [ + ("Tunnel VPN", "vpn/vpnconfiguration"), + ("Serveur VPS", "vps/vpsconfiguration"), + ("Housing", "housing/housingconfiguration"), + ] + }, + + {"id": "billing", + "icon": "euro", + "title": "Facturation", + "models": [ + ("Cotisations", "billing/membershipfee"), + ("Dons", "billing/donation"), + ("Paiements", "billing/payment"), + ("Factures", "billing/invoice"), + ] + }, + + {"id": "hardware", + "icon": "cubes", + "title": "Matos", + "models": [ + ("Lieux de stockage", "hardware_provisioning/storage"), + ("Type d'objet", "hardware_provisioning/itemtype"), + ("Objets", "hardware_provisioning/item"), + ] + }, + + {"id": "infra", + "icon": "database", + "title": "Infra", + "models": [ + ("Pool d'IP", "resources/ippool"), + ("Subnets", "resources/ipsubnet"), + ("OS pour VPS", "vps/vpsoperatingsystem"), + ] + }, + + {"id": "meta", + "icon": "institution", + "title": "Meta", + "models": [ + ("Infos du FAI", "isp_database/ispinfo"), + ] + }, +] + +original_index = admin.site.index + +def new_index(request, extra_context=None): + extra_context = extra_context or {} + extra_context["admin_cards"] = admin_cards + return original_index(request, extra_context) + +admin.site.index = new_index diff --git a/coin/static/css/admin-local.css b/coin/static/css/admin-local.css index 691e940..343d3a9 100644 --- a/coin/static/css/admin-local.css +++ b/coin/static/css/admin-local.css @@ -17,3 +17,45 @@ form .inline-group .inline-related h3 .inline_label { /* TabularStacked .inline-related.dynamic-maillinglistsubscription_set .related-widget-wrapper-link.add-related { display: none; } + +/* Cards in admin index */ + +.app-card { + width: 31%; + border: 1px solid lightgray; + border-radius: 3px; + height: 15em; + display: inline-grid; + margin: 3px; + font-weight: bold; + position: relative; + text-align: center; +} + +.app-card h3 { + font-size: 1.9em; + margin: 0; + margin-top: 0.4em; + font-weight: normal; +} + +.app-card h3 .fa { + display: block; + font-size: 2em; +} + +.app-card ul { + position: absolute; + bottom: 0; + left: 0; + padding: 0; + margin: 0; + width: 100%; +} + +.app-card ul li { + line-height: 1.4em; + font-size: 1.2em; + list-style-type: none; + border-top: 1px solid #eee; +} diff --git a/coin/templates/admin/index.html b/coin/templates/admin/index.html new file mode 100644 index 0000000..def35bb --- /dev/null +++ b/coin/templates/admin/index.html @@ -0,0 +1,61 @@ +{% extends "admin/base_site.html" %} +{% load i18n admin_static %} + +{% block extrastyle %}{{ block.super }}{% endblock %} + +{% block coltype %}colMS{% endblock %} + +{% block bodyclass %}{{ block.super }} dashboard{% endblock %} + +{% block breadcrumbs %}{% endblock %} + +{% block content %} +
    + +{% for card in admin_cards %} +
    +

    {{card.title}}

    +
      + {% for model, uri in card.models %} +
    • {{model}}
    • + {% endfor %} +
    +
    +{% endfor %} + +
    + +{% endblock %} + + +{% block sidebar %} + +{% endblock %} -- GitLab From 71f7db2fb60a862969df0881716bb852fad7b545 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 4 Jul 2020 21:55:41 +0200 Subject: [PATCH 089/195] Misc tweaks for more consistent admin views --- coin/offers/admin.py | 5 ++--- vpn/admin.py | 2 +- vps/admin.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/coin/offers/admin.py b/coin/offers/admin.py index deeaa62..9ddcfe8 100644 --- a/coin/offers/admin.py +++ b/coin/offers/admin.py @@ -28,9 +28,8 @@ class OfferIPPoolAdmin(admin.TabularInline): class OfferAdmin(admin.ModelAdmin): - list_display = ('get_configuration_type_display', 'name', 'reference', 'billing_period', 'period_fees', + list_display = ('name', 'get_configuration_type_display', 'reference', 'billing_period', 'period_fees', 'initial_fees') - list_display_links = ('name',) list_filter = ('configuration_type',) search_fields = ['name'] form = OfferAdminForm @@ -45,7 +44,7 @@ class OfferAdmin(admin.ModelAdmin): class OfferSubscriptionAdmin(admin.ModelAdmin): list_display = ('get_subscription_reference', 'member', 'offer', 'subscription_date', 'commitment', 'resign_date') - list_display_links = ('member','offer') + list_display_links = ('get_subscription_reference',) list_filter = ( OfferSubscriptionTerminationFilter, OfferSubscriptionCommitmentFilter, 'offer', 'member') diff --git a/vpn/admin.py b/vpn/admin.py index 96748eb..a9e87d2 100644 --- a/vpn/admin.py +++ b/vpn/admin.py @@ -18,7 +18,7 @@ class VPNConfigurationInline(ChildConfigurationAdminInline): class VPNConfigurationAdmin(ChildConfigurationAdmin): base_model = VPNConfiguration - list_display = ('offersubscription', 'login', + list_display = ('__unicode__', 'offersubscription', 'ipv4_endpoint', 'ipv6_endpoint', 'comment') search_fields = ('login', 'comment', # TODO: searching on member directly doesn't work diff --git a/vps/admin.py b/vps/admin.py index d74b9b8..2afd4cb 100644 --- a/vps/admin.py +++ b/vps/admin.py @@ -27,7 +27,7 @@ class VPSConfigurationInline(ChildConfigurationAdminInline): class VPSConfigurationAdmin(ChildConfigurationAdmin): base_model = VPSConfiguration - list_display = ('offersubscription', + list_display = ('__unicode__', 'offersubscription', 'ipv4_endpoint', 'ipv6_endpoint', 'comment') search_fields = ('comment', # TODO: searching on member directly doesn't work -- GitLab From c4bebc55be6612883e291ed111aaf5841d45d9af Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 5 Jul 2020 19:27:16 +0200 Subject: [PATCH 090/195] Send an email when a subscription request is accepted / refused --- coin/offers/models.py | 22 ++++++++++++++++--- .../emails/subscription_request_accepted.html | 7 ++++++ .../emails/subscription_request_accepted.txt | 1 + .../emails/subscription_request_refused.html | 5 +++++ .../emails/subscription_request_refused.txt | 1 + 5 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 coin/offers/templates/offers/emails/subscription_request_accepted.html create mode 100644 coin/offers/templates/offers/emails/subscription_request_accepted.txt create mode 100644 coin/offers/templates/offers/emails/subscription_request_refused.html create mode 100644 coin/offers/templates/offers/emails/subscription_request_refused.txt diff --git a/coin/offers/models.py b/coin/offers/models.py index a4f1c7d..9a27ea8 100644 --- a/coin/offers/models.py +++ b/coin/offers/models.py @@ -11,7 +11,7 @@ from django.db.models import Count, Q from django.core.validators import MinValueValidator from django.contrib.contenttypes.models import ContentType -from coin.utils import get_descendant_classes +from coin.utils import get_descendant_classes, send_templated_email from coin.resources.models import IPPool @@ -262,8 +262,8 @@ class Request(PolymorphicModel): help_text="Commentaires libres") admin_comments = models.TextField(blank=True, - verbose_name='commentaires des admins', - help_text="Commentaires des admins") + verbose_name='commentaires de l\'équipe bénévole', + help_text="Commentaires de l\'équipe bénévole") def accept(self): self.state = "accepted" @@ -310,6 +310,22 @@ class OfferSubscriptionRequest(Request): setattr(config, info, getattr(self, info)) config.save() + send_templated_email( + to=self.member.email, + subject_template='offers/emails/subscription_request_accepted.txt', + body_template='offers/emails/subscription_request_accepted.html', + context={ 'request': self }) + + def refuse(self): + + super(OfferSubscriptionRequest, self).refuse() + + send_templated_email( + to=self.member.email, + subject_template='offers/emails/subscription_request_refused.txt', + body_template='offers/emails/subscription_request_refused.html', + context={ 'request': self }) + @property def configuration_type(self): return Offer._get_configuration_type(self.offer_type) diff --git a/coin/offers/templates/offers/emails/subscription_request_accepted.html b/coin/offers/templates/offers/emails/subscription_request_accepted.html new file mode 100644 index 0000000..49b0d9f --- /dev/null +++ b/coin/offers/templates/offers/emails/subscription_request_accepted.html @@ -0,0 +1,7 @@ +Un bénévole viens de valider votre demande pour un {{ request.offer.name }}. + +Commentaire de l'équipe bénévole : + +{{ request.admin_comments }} + +Vous pouvez consulter votre espace adhérent pour plus d'informations sur l'état et la configuration de votre service. diff --git a/coin/offers/templates/offers/emails/subscription_request_accepted.txt b/coin/offers/templates/offers/emails/subscription_request_accepted.txt new file mode 100644 index 0000000..326812d --- /dev/null +++ b/coin/offers/templates/offers/emails/subscription_request_accepted.txt @@ -0,0 +1 @@ +Votre demande pour un {{ request.offer.name }} a été accepté ! diff --git a/coin/offers/templates/offers/emails/subscription_request_refused.html b/coin/offers/templates/offers/emails/subscription_request_refused.html new file mode 100644 index 0000000..8bdb4ce --- /dev/null +++ b/coin/offers/templates/offers/emails/subscription_request_refused.html @@ -0,0 +1,5 @@ +Votre demande pour un {{ request.offer.name }} a été refusée. + +Commentaire de l'équipe bénévole : + +{{ request.admin_comments }} diff --git a/coin/offers/templates/offers/emails/subscription_request_refused.txt b/coin/offers/templates/offers/emails/subscription_request_refused.txt new file mode 100644 index 0000000..1b22b18 --- /dev/null +++ b/coin/offers/templates/offers/emails/subscription_request_refused.txt @@ -0,0 +1 @@ +Votre demande pour un {{ request.offer.name }} a été refusée. -- GitLab From f7a2599a967ba713862dc4d663db26bcdf467502 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 5 Jul 2020 19:31:26 +0200 Subject: [PATCH 091/195] Do not display button 'accept and provision' in subscription request admin if there's no provision hook defined --- coin/offers/models.py | 5 +++-- .../admin/offersubscriptionrequest/change_form.html | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/coin/offers/models.py b/coin/offers/models.py index 9a27ea8..db5f7e1 100644 --- a/coin/offers/models.py +++ b/coin/offers/models.py @@ -97,7 +97,8 @@ class Offer(models.Model): return subconfig_class.__name__ return offer_type - def _get_configuration_class(self): + @property + def configuration_class(self): """ This translate an offer into its corresponding configuration class (e.g. VPNConfiguration) """ @@ -210,7 +211,7 @@ class OfferSubscription(models.Model): def create_config_if_it_does_not_exists_yet(self): if not hasattr(self, 'configuration'): - config_class = self.offer._get_configuration_class() + config_class = self.offer.configuration_class if config_class is not None: config = config_class.objects.create(offersubscription=self) config.offersubscription = self diff --git a/coin/offers/templates/admin/offersubscriptionrequest/change_form.html b/coin/offers/templates/admin/offersubscriptionrequest/change_form.html index ec3afdb..d928045 100644 --- a/coin/offers/templates/admin/offersubscriptionrequest/change_form.html +++ b/coin/offers/templates/admin/offersubscriptionrequest/change_form.html @@ -1,9 +1,12 @@ {% extends "admin/change_form.html" %} +{% load configuration %} {% load url from future %} {% block object-tools-items %} {% if original.state == "pending" %}
  • Accepter
  • + {% if original.offer.configuration_class|provision_is_managed_via_hook %}
  • Accepter et provisionner
  • + {% endif %}
  • Refuser
  • {% endif %} {{ block.super }} -- GitLab From 1dedb394ab3babb0d7534c688315ea4d5bb9ae2e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 5 Jul 2020 19:31:35 +0200 Subject: [PATCH 092/195] CSS tweak for admin index --- coin/static/css/admin-local.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/coin/static/css/admin-local.css b/coin/static/css/admin-local.css index 343d3a9..b65c674 100644 --- a/coin/static/css/admin-local.css +++ b/coin/static/css/admin-local.css @@ -20,6 +20,10 @@ form .inline-group .inline-related h3 .inline_label { /* TabularStacked /* Cards in admin index */ +.dashboard #content { + width: auto; +} + .app-card { width: 31%; border: 1px solid lightgray; -- GitLab From f85ac5e9f7cdbf019e2b9fa15eeefcb4047b4857 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 6 Jul 2020 01:31:21 +0200 Subject: [PATCH 093/195] Fix validation of SSH in case the comment contains spaces --- vps/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vps/models.py b/vps/models.py index f6b5e2c..a61b29c 100644 --- a/vps/models.py +++ b/vps/models.py @@ -29,7 +29,7 @@ def PublicSSHKeyValidator(ssh_pubkey): raise ValidationError("Une clef SSH devrait être une chaine de caractères?") try: # From https://stackoverflow.com/questions/2494450/ssh-rsa-public-key-validation-using-a-regular-expression - type_, key_string, comment = ssh_pubkey.split() + type_, key_string, comment = ssh_pubkey.strip().split(None, 2) data = base64.decodestring(key_string) int_len = 4 str_len = struct.unpack('>I', data[:int_len])[0] # this should return 7 -- GitLab From 96a0e7d04c025dfc0f73332955348b0defa90a58 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 6 Jul 2020 01:46:53 +0200 Subject: [PATCH 094/195] Fix mail sending when accepting/refusing offer subscription request --- coin/offers/models.py | 8 ++++---- .../offers/emails/subscription_request_accepted.html | 7 ------- .../offers/emails/subscription_request_accepted.txt | 8 +++++++- .../emails/subscription_request_accepted_subject.txt | 1 + .../offers/emails/subscription_request_refused.html | 5 ----- .../offers/emails/subscription_request_refused.txt | 4 ++++ .../emails/subscription_request_refused_subject.txt | 1 + 7 files changed, 17 insertions(+), 17 deletions(-) delete mode 100644 coin/offers/templates/offers/emails/subscription_request_accepted.html create mode 100644 coin/offers/templates/offers/emails/subscription_request_accepted_subject.txt delete mode 100644 coin/offers/templates/offers/emails/subscription_request_refused.html create mode 100644 coin/offers/templates/offers/emails/subscription_request_refused_subject.txt diff --git a/coin/offers/models.py b/coin/offers/models.py index db5f7e1..372f13e 100644 --- a/coin/offers/models.py +++ b/coin/offers/models.py @@ -313,8 +313,8 @@ class OfferSubscriptionRequest(Request): send_templated_email( to=self.member.email, - subject_template='offers/emails/subscription_request_accepted.txt', - body_template='offers/emails/subscription_request_accepted.html', + subject_template='offers/emails/subscription_request_accepted_subject.txt', + body_template='offers/emails/subscription_request_accepted.txt', context={ 'request': self }) def refuse(self): @@ -323,8 +323,8 @@ class OfferSubscriptionRequest(Request): send_templated_email( to=self.member.email, - subject_template='offers/emails/subscription_request_refused.txt', - body_template='offers/emails/subscription_request_refused.html', + subject_template='offers/emails/subscription_request_refused_subject.txt', + body_template='offers/emails/subscription_request_refused.txt', context={ 'request': self }) @property diff --git a/coin/offers/templates/offers/emails/subscription_request_accepted.html b/coin/offers/templates/offers/emails/subscription_request_accepted.html deleted file mode 100644 index 49b0d9f..0000000 --- a/coin/offers/templates/offers/emails/subscription_request_accepted.html +++ /dev/null @@ -1,7 +0,0 @@ -Un bénévole viens de valider votre demande pour un {{ request.offer.name }}. - -Commentaire de l'équipe bénévole : - -{{ request.admin_comments }} - -Vous pouvez consulter votre espace adhérent pour plus d'informations sur l'état et la configuration de votre service. diff --git a/coin/offers/templates/offers/emails/subscription_request_accepted.txt b/coin/offers/templates/offers/emails/subscription_request_accepted.txt index 326812d..49b0d9f 100644 --- a/coin/offers/templates/offers/emails/subscription_request_accepted.txt +++ b/coin/offers/templates/offers/emails/subscription_request_accepted.txt @@ -1 +1,7 @@ -Votre demande pour un {{ request.offer.name }} a été accepté ! +Un bénévole viens de valider votre demande pour un {{ request.offer.name }}. + +Commentaire de l'équipe bénévole : + +{{ request.admin_comments }} + +Vous pouvez consulter votre espace adhérent pour plus d'informations sur l'état et la configuration de votre service. diff --git a/coin/offers/templates/offers/emails/subscription_request_accepted_subject.txt b/coin/offers/templates/offers/emails/subscription_request_accepted_subject.txt new file mode 100644 index 0000000..326812d --- /dev/null +++ b/coin/offers/templates/offers/emails/subscription_request_accepted_subject.txt @@ -0,0 +1 @@ +Votre demande pour un {{ request.offer.name }} a été accepté ! diff --git a/coin/offers/templates/offers/emails/subscription_request_refused.html b/coin/offers/templates/offers/emails/subscription_request_refused.html deleted file mode 100644 index 8bdb4ce..0000000 --- a/coin/offers/templates/offers/emails/subscription_request_refused.html +++ /dev/null @@ -1,5 +0,0 @@ -Votre demande pour un {{ request.offer.name }} a été refusée. - -Commentaire de l'équipe bénévole : - -{{ request.admin_comments }} diff --git a/coin/offers/templates/offers/emails/subscription_request_refused.txt b/coin/offers/templates/offers/emails/subscription_request_refused.txt index 1b22b18..8bdb4ce 100644 --- a/coin/offers/templates/offers/emails/subscription_request_refused.txt +++ b/coin/offers/templates/offers/emails/subscription_request_refused.txt @@ -1 +1,5 @@ Votre demande pour un {{ request.offer.name }} a été refusée. + +Commentaire de l'équipe bénévole : + +{{ request.admin_comments }} diff --git a/coin/offers/templates/offers/emails/subscription_request_refused_subject.txt b/coin/offers/templates/offers/emails/subscription_request_refused_subject.txt new file mode 100644 index 0000000..1b22b18 --- /dev/null +++ b/coin/offers/templates/offers/emails/subscription_request_refused_subject.txt @@ -0,0 +1 @@ +Votre demande pour un {{ request.offer.name }} a été refusée. -- GitLab From b3719259ea91361242f34b03d115698a5647a9ee Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 6 Jul 2020 03:26:23 +0200 Subject: [PATCH 095/195] Rework VPS/VPN user page + misc CSS tweaks --- coin/members/templates/members/detail.html | 2 +- .../templates/members/subscriptions.html | 5 +- coin/static/css/local.css | 27 ++- vpn/templates/vpn/vpn.html | 160 +++++------------- vps/templates/vps/vps.html | 141 ++++++--------- 5 files changed, 111 insertions(+), 224 deletions(-) diff --git a/coin/members/templates/members/detail.html b/coin/members/templates/members/detail.html index 890209c..7d0013d 100644 --- a/coin/members/templates/members/detail.html +++ b/coin/members/templates/members/detail.html @@ -87,7 +87,7 @@ Je n'ai encore jamais cotisé. {% endif %}

    -

    Note: nous importons les comptes tous les 1 à 2 mois, inutile donc de nous contacter si votre adhésion n'est pas validée malgré un paiement de moins de 2 mois.

    +

    Note: nous importons les comptes tous les 1 à 2 mois, inutile donc de nous contacter si votre adhésion n'est pas validée malgré un paiement de moins de 2 mois.

    {% if membership_info_url %} Renouveler ma cotisation diff --git a/coin/members/templates/members/subscriptions.html b/coin/members/templates/members/subscriptions.html index 9fa2428..f88e01d 100644 --- a/coin/members/templates/members/subscriptions.html +++ b/coin/members/templates/members/subscriptions.html @@ -9,7 +9,6 @@ - Type Offre Souscris le Résilié le @@ -21,19 +20,17 @@ {% for subscription in subscriptions %} {{ subscription.configuration.get_state_icon_display }} - {{ subscription.offer.get_configuration_type_display }} {{ subscription.offer.name }} {{ subscription.subscription_date }} {{ subscription.resign_date|default_if_none:"" }} {{ subscription.configuration.comment }} - {% if subscription.configuration and subscription.configuration.url_namespace %} Infos / gérer{% endif %} + {% if subscription.configuration and subscription.configuration.url_namespace %} Infos / gérer{% endif %} {% endfor %} {% for subscription in old_subscriptions %} {{ subscription.configuration.get_state_icon_display }} - {{ subscription.offer.get_configuration_type_display }} {{ subscription.offer.name }} {{ subscription.subscription_date }} {{ subscription.resign_date|default_if_none:"" }} diff --git a/coin/static/css/local.css b/coin/static/css/local.css index 4adfc20..41012ec 100644 --- a/coin/static/css/local.css +++ b/coin/static/css/local.css @@ -133,15 +133,23 @@ h2 { /* panels */ -.panel {} +.panel { + border: none; + background: none; + padding: 0; + margin-bottom: 1em; +} + .panel > h2, .panel > h3, .panel > h4, .panel > h5 { - border-bottom: 1px solid #d8d8d8; - margin-bottom: 1.25rem; - padding-bottom: 0.625rem; + border-bottom: 1px solid lightgrey; + margin-bottom: 0.5em; + padding: 0.3em; + padding-left: 0.5em; } + .panel.callout > h2, .panel.callout > h3, .panel.callout > h4, @@ -149,6 +157,12 @@ h2 { border-color: #B5F0FF; } +.panel tr td:first-child { + width: 33%; + font-weight: bold; + text-align: right; + color: #666; +} /* Tables */ table { @@ -190,11 +204,6 @@ table.no-background tr { #personnal-info tr:last-child td { border-bottom: none; } -#personnal-info tr td:first-child { - text-align: right; - color: #666; - font-weight: bold; -} #personnal-info .email td { /* email address can be reallllly long word */ overflow-wrap: break-word; diff --git a/vpn/templates/vpn/vpn.html b/vpn/templates/vpn/vpn.html index 452d203..6280a7c 100644 --- a/vpn/templates/vpn/vpn.html +++ b/vpn/templates/vpn/vpn.html @@ -1,132 +1,52 @@ {% extends "base.html" %} - {% load subnets %} -{% block title %}Configuration du VPN {{ object.offersubscription.get_subscription_reference }} - {{ block.super }}{% endblock %} {% block content %}
    -

    Configuration du VPN

    - {% if form %} -
    {% csrf_token %} -

    Quand vous aurez terminé vos modifications, cliquez sur

    - {% endif %} - {% for message in messages %} -
    - {{ message }} -
    - {% endfor %} - - {% if form %} - {% if form.non_field_errors or form.ipv4_endpoint.errors or form.ipv6_endpoint.errors %} -
    - {{ form.non_field_errors }} - {{ form.ipv4_endpoint.errors }} - {{ form.ipv6_endpoint.errors }} -
    - {% endif %} - {% endif %} -
    -
    -

    Authentification

    - - - - - - {% if object.SECRETS_TRANSMISSION_METHOD == object.METHOD_GEN_PASSWORD_AND_FORGET %} - - - - {% endif %} - {% if object.SECRETS_TRANSMISSION_METHOD == object.METHOD_CRYPTO_LINK %} - - - - - - - {% endif %} - - {% if form %} - - - {% else %} - - - - {% endif %} - -
    Identifiant{{object.login}}
    - - Générer un nouveau mot de passe -
    Matériel cryptographique - {% if object.crypto_link %} - Télecharger (lien supprimé après ouverture) - {% else %} - Ton matériel cryptographique n'a pas encore été généré. - {% endif %} -
    {{ form.comment.label_tag }}{{ form.comment }}Commentaire{{ object.comment }}
    -
    +

    {{ object.get_state_icon_display }} {{ object }}

    + +
    +

    État

    + + + + + + + + + + + + + + + + + + + + + +
    Offre{{ object.offersubscription.offer }}
    Commentaire{{ object.comment }}
    IPv4{{ object.ipv4_endpoint }}
    IPv6{{ object.ipv6_endpoint }}
    Sous-réseaux + {% for subnet in object.ip_subnet.all %}{{ subnet|prettify }}
    {% endfor %} +
    -
    -
    -

    Adresses IP

    - - {% if form %} - - - {{ form.ipv4_endpoint }} - - - - {{ form.ipv6_endpoint }} - +
    +

    Authentification

    +
    {{ form.ipv4_endpoint.label_tag }}
    {{ form.ipv6_endpoint.label_tag }}
    + + + - - - - - - - + Ton matériel cryptographique n'a pas encore été généré. {% endif %} - - - - -
    Matériel cryptographique + {% if object.crypto_link %} + Télecharger (lien supprimé après ouverture) {% else %} -
    IPv4{{ object.ipv4_endpoint }}
    IPv6{{ object.ipv6_endpoint }}
    Sous-réseaux - {% for subnet in object.ip_subnet.all %}{{ subnet|prettify }}
    {% endfor %} -
    -
    + + +
    - {% if form %} -

    - - {% endif %}
    {% endblock %} - -{% block extra_js %} - -{% endblock extra_js %} diff --git a/vps/templates/vps/vps.html b/vps/templates/vps/vps.html index 7fb643c..05790d9 100644 --- a/vps/templates/vps/vps.html +++ b/vps/templates/vps/vps.html @@ -1,101 +1,62 @@ {% extends "base.html" %} - {% load subnets %} - {% block content %}
    -

    Configuration du VPS

    - {% if form %} -
    {% csrf_token %} -

    Quand vous aurez terminé vos modifications, cliquez sur

    - {% endif %} - {% for message in messages %} -
    - {{ message }} -
    - {% endfor %} - - {% if form %} - {% if form.non_field_errors or form.ipv4_endpoint.errors or form.ipv6_endpoint.errors %} -
    - {{ form.non_field_errors }} - {{ form.ipv4_endpoint.errors }} - {{ form.ipv6_endpoint.errors }} -
    - {% endif %} - {% endif %} -
    -
    -

    Authentification

    - - {% if object.fingerprint_set.all %} - - - - - {% endif %} - {% if object.console %} - - - - - {% endif %} - - {% if form %} - - - {% else %} - - - - {% endif %} - -
    Empreinte(s){% for fingerprint in object.fingerprint_set.all %} - {{ fingerprint.length }} {{ fingerprint.fingerprint }} ({{ fingerprint.algo }}){% if not forloop.last %}
    {% endif %} - {% endfor %}
    Console {{object.console.protocol }} - {{ object.console.domain }}:{{ object.console.port }}
    +

    {{ object.get_state_icon_display }} {{ object }}

    - Voir le mot de passe (lien supprimé après ouverture) -
    {{ form.comment.label_tag }}{{ form.comment }}Commentaire{{ object.comment }}
    -
    +
    +

    État

    + + + + + + + + + + + + + + + + + + + + + +
    Offre{{ object.offersubscription.offer }}
    Commentaire{{ object.comment }}
    IPv4{{ object.ipv4_endpoint }}
    IPv6{{ object.ipv6_endpoint }}
    Sous-réseaux + {% for subnet in object.ip_subnet.all %}{{ subnet|prettify }}
    {% endfor %} +
    -
    -
    -

    Adresses IP

    - - {% if form %} - - - {{ form.ipv4_endpoint }} - - - - {{ form.ipv6_endpoint }} - - {% else %} - - - - - - - - - {% endif %} - - - - -
    {{ form.ipv4_endpoint.label_tag }}
    {{ form.ipv6_endpoint.label_tag }}
    IPv4{{ object.ipv4_endpoint }}
    IPv6{{ object.ipv6_endpoint }}
    Sous-réseaux - {% for subnet in object.ip_subnet.all %}{{ subnet|prettify }}
    {% endfor %} -
    -
    +
    +

    Accès

    + + {% if object.fingerprint_set.all %} + + + + + {% endif %} + {% if object.console %} + + + + + {% endif %} +
    Empreinte(s) + {% for fingerprint in object.fingerprint_set.all %} + {{ fingerprint.length }} {{ fingerprint.fingerprint }} ({{ fingerprint.algo }}){% if not forloop.last %}
    {% endif %} + {% endfor %} +
    Console {{object.console.protocol}} + {{ object.console.domain }}:{{ object.console.port }}
    + Voir le mot de passe (lien supprimé après ouverture) +
    - {% if form %} -

    - - {% endif %} +
    {% endblock %} -- GitLab From f0a81254c6f095742bf44b83d2f979b408e151e6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 6 Jul 2020 17:02:05 +0200 Subject: [PATCH 096/195] Bring back the inline subnet thing in configuration view --- coin/configuration/admin.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/coin/configuration/admin.py b/coin/configuration/admin.py index 2ffd430..ef25308 100644 --- a/coin/configuration/admin.py +++ b/coin/configuration/admin.py @@ -22,6 +22,7 @@ filter offersubscription select input to avoid selecting wrong subscription. class IPSubnetInline(admin.TabularInline): model = IPSubnet extra = 0 + fields = ["inet", "ip_pool"] class ParentConfigurationAdmin(PolymorphicParentModelAdmin): @@ -63,7 +64,7 @@ class ChildConfigurationAdmin(PolymorphicChildModelAdmin): base_form = ConfigurationForm # For each child (admin object for configurations), this will display # an inline form to assign IP addresses. - #inlines = (IPSubnetInline, ) + inlines = (IPSubnetInline, ) change_form_template = "admin/configuration/configuration/change_form.html" @@ -73,14 +74,14 @@ class ChildConfigurationAdmin(PolymorphicChildModelAdmin): 'provisioned', 'status']})] - if hasattr(obj, "ipv4_endpoint") and hasattr(obj, "ipv6_endpoint"): - fields.append(["Adresses IP (endpoints)", - {"fields": tuple(["ipv4_endpoint", "ipv6_endpoint"])}]) - if hasattr(obj, "provisioning_infos") and obj.provisioning_infos: fields.append(["Informations pour le provisionnement", {"fields": tuple(obj.provisioning_infos)}]) + if hasattr(obj, "ipv4_endpoint") and hasattr(obj, "ipv6_endpoint"): + fields.append(["Adresses IP (endpoints)", + {"fields": tuple(["ipv4_endpoint", "ipv6_endpoint"])}]) + return fields def get_readonly_fields(self, request, obj=None): -- GitLab From 4c0d183e173f7eee8a57bc95959cbf0265936720 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 6 Jul 2020 17:11:38 +0200 Subject: [PATCH 097/195] Fix validation ... configuration type must match model name --- coin/configuration/forms.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coin/configuration/forms.py b/coin/configuration/forms.py index 9cdf8f9..8c6e02f 100644 --- a/coin/configuration/forms.py +++ b/coin/configuration/forms.py @@ -25,7 +25,7 @@ class ConfigurationForm(forms.ModelForm): super(ConfigurationForm, self).__init__(*args, **kwargs) if self.instance and not hasattr(self.instance, "offersubscription"): queryset = OfferSubscription.objects.filter( - Q(offer__configuration_type=self.instance.configuration_type_name) & ( + Q(offer__configuration_type=self.instance.model_name) & ( Q(configuration=None) | Q(configuration=self.instance.pk))) if 'offersubscription' in self.fields: self.fields['offersubscription'].queryset = queryset @@ -36,7 +36,7 @@ class ConfigurationForm(forms.ModelForm): offer which use the same configuration type than the edited configuration. """ offersubscription = self.cleaned_data['offersubscription'] - if offersubscription.offer.configuration_type != self.instance.configuration_type_name(): + if offersubscription.offer.configuration_type != self.instance.model_name(): raise forms.ValidationError('Administrative subscription must refer an offer having a "{}" configuration type.'.format(self.instance.model_name())) return offersubscription -- GitLab From f89eb3639b52e3420f3c52ecd48c0cb7654d3c72 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 6 Jul 2020 17:53:54 +0200 Subject: [PATCH 098/195] More consistent prettifying of IP endpoints / subnets --- coin/resources/templatetags/subnets.py | 3 ++- vpn/templates/vpn/vpn.html | 6 +++--- vps/templates/vps/vps.html | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/coin/resources/templatetags/subnets.py b/coin/resources/templatetags/subnets.py index 689889d..cfb6e6c 100644 --- a/coin/resources/templatetags/subnets.py +++ b/coin/resources/templatetags/subnets.py @@ -11,12 +11,13 @@ register = template.Library() def prettify(subnet): """Prettify an IPv4 subnet by remove the subnet length when it is equal to /32 """ - # Support IPSubnet objects, who have a IPNetwork as attribute if hasattr(subnet, "inet") and isinstance(subnet.inet, IPNetwork): subnet = subnet.inet if isinstance(subnet, IPNetwork): if subnet.version == 4 and subnet.prefixlen == 32: return str(subnet.ip) + elif subnet.version == 6 and subnet.prefixlen == 128: + return str(subnet.ip) else: return str(subnet) return subnet diff --git a/vpn/templates/vpn/vpn.html b/vpn/templates/vpn/vpn.html index 6280a7c..19c4c22 100644 --- a/vpn/templates/vpn/vpn.html +++ b/vpn/templates/vpn/vpn.html @@ -17,16 +17,16 @@ IPv4 - {{ object.ipv4_endpoint }} + {{ object.ipv4_endpoint|prettify }} IPv6 - {{ object.ipv6_endpoint }} + {{ object.ipv6_endpoint|prettify }} Sous-réseaux - {% for subnet in object.ip_subnet.all %}{{ subnet|prettify }}
    {% endfor %} + {% for subnet in object.ip_subnet.all %}{{ subnet }}
    {% endfor %} diff --git a/vps/templates/vps/vps.html b/vps/templates/vps/vps.html index 05790d9..2616846 100644 --- a/vps/templates/vps/vps.html +++ b/vps/templates/vps/vps.html @@ -17,16 +17,16 @@ IPv4 - {{ object.ipv4_endpoint }} + {{ object.ipv4_endpoint|prettify }} IPv6 - {{ object.ipv6_endpoint }} + {{ object.ipv6_endpoint|prettify }} Sous-réseaux - {% for subnet in object.ip_subnet.all %}{{ subnet|prettify }}
    {% endfor %} + {% for subnet in object.ip_subnet.all %}{{ subnet }}
    {% endfor %} -- GitLab From cf7f63f23e9084d8976d971396928bb10c44bbc6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 6 Jul 2020 18:33:35 +0200 Subject: [PATCH 099/195] Zzzzz these must be .html in the end -_- --- ...epted.txt => subscription_request_accepted.html} | 13 +++++++++++-- ...efused.txt => subscription_request_refused.html} | 8 +++++++- 2 files changed, 18 insertions(+), 3 deletions(-) rename coin/offers/templates/offers/emails/{subscription_request_accepted.txt => subscription_request_accepted.html} (65%) rename coin/offers/templates/offers/emails/{subscription_request_refused.txt => subscription_request_refused.html} (59%) diff --git a/coin/offers/templates/offers/emails/subscription_request_accepted.txt b/coin/offers/templates/offers/emails/subscription_request_accepted.html similarity index 65% rename from coin/offers/templates/offers/emails/subscription_request_accepted.txt rename to coin/offers/templates/offers/emails/subscription_request_accepted.html index 49b0d9f..8e862ba 100644 --- a/coin/offers/templates/offers/emails/subscription_request_accepted.txt +++ b/coin/offers/templates/offers/emails/subscription_request_accepted.html @@ -1,7 +1,16 @@ +

    Un bénévole viens de valider votre demande pour un {{ request.offer.name }}. +

    -Commentaire de l'équipe bénévole : - +{% if request.admin_comments %} +

    +Commentaires de l'équipe bénévole : +

    +

    {{ request.admin_comments }} +

    +{% endif %} +

    Vous pouvez consulter votre espace adhérent pour plus d'informations sur l'état et la configuration de votre service. +

    diff --git a/coin/offers/templates/offers/emails/subscription_request_refused.txt b/coin/offers/templates/offers/emails/subscription_request_refused.html similarity index 59% rename from coin/offers/templates/offers/emails/subscription_request_refused.txt rename to coin/offers/templates/offers/emails/subscription_request_refused.html index 8bdb4ce..fd71b0d 100644 --- a/coin/offers/templates/offers/emails/subscription_request_refused.txt +++ b/coin/offers/templates/offers/emails/subscription_request_refused.html @@ -1,5 +1,11 @@ +

    Votre demande pour un {{ request.offer.name }} a été refusée. +

    -Commentaire de l'équipe bénévole : +

    +Commentaires de l'équipe bénévole : +

    +

    {{ request.admin_comments }} +

    -- GitLab From b1f03279a103ec6d43ce483a40808f83e7e3b01e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 6 Jul 2020 18:43:54 +0200 Subject: [PATCH 100/195] Fix deletion of subscription / configurations using turbo black magic --- coin/configuration/models.py | 4 ++++ coin/offers/models.py | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index b1855d6..51996c0 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -261,6 +261,10 @@ class Configuration(PolymorphicModel): else: raise Exception("No hook is defined to provision %ss" % self.configuration_type_name) + def bulk_related_objects(self, objs, *args, **kwargs): + # Fix delete screen. Workaround for https://github.com/chrisglass/django_polymorphic/issues/34 + return super(Configuration, self).bulk_related_objects(objs, *args, **kwargs).non_polymorphic() + class Meta: verbose_name = 'configuration' diff --git a/coin/offers/models.py b/coin/offers/models.py index 372f13e..66bf40f 100644 --- a/coin/offers/models.py +++ b/coin/offers/models.py @@ -224,6 +224,10 @@ class OfferSubscription(models.Model): return '%s - %s - %s' % (self.member, self.offer.name, self.subscription_date) + def bulk_related_objects(self, objs, *args, **kwargs): + # Fix delete screen. Workaround for https://github.com/chrisglass/django_polymorphic/issues/34 + return super(OfferSubscription, self).bulk_related_objects(objs, *args, **kwargs).non_polymorphic() + class Meta: verbose_name = 'abonnement' @@ -279,6 +283,10 @@ class Request(PolymorphicModel): def __unicode__(self): return 'Demande de %s' % self.member + def bulk_related_objects(self, objs, *args, **kwargs): + # Fix delete screen. Workaround for https://github.com/chrisglass/django_polymorphic/issues/34 + return super(OfferSubscription, self).bulk_related_objects(objs, *args, **kwargs).non_polymorphic() + class Meta: verbose_name = 'demande' -- GitLab From 81d4143d7514c312165dd42d2755b0af7637134a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 6 Jul 2020 20:20:04 +0200 Subject: [PATCH 101/195] Add external account configuration class --- external_account/__init__.py | 1 + external_account/admin.py | 30 ++++++++++++++ external_account/apps.py | 14 +++++++ external_account/migrations/__init__.py | 0 external_account/models.py | 40 +++++++++++++++++++ .../external_account/external_account.html | 35 ++++++++++++++++ external_account/urls.py | 13 ++++++ external_account/views.py | 31 ++++++++++++++ 8 files changed, 164 insertions(+) create mode 100644 external_account/__init__.py create mode 100644 external_account/admin.py create mode 100644 external_account/apps.py create mode 100644 external_account/migrations/__init__.py create mode 100644 external_account/models.py create mode 100644 external_account/templates/external_account/external_account.html create mode 100644 external_account/urls.py create mode 100644 external_account/views.py diff --git a/external_account/__init__.py b/external_account/__init__.py new file mode 100644 index 0000000..18a954a --- /dev/null +++ b/external_account/__init__.py @@ -0,0 +1 @@ +default_app_config = 'external_account.apps.ExternalAccountConfig' diff --git a/external_account/admin.py b/external_account/admin.py new file mode 100644 index 0000000..920168e --- /dev/null +++ b/external_account/admin.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.contrib import admin + +from coin.configuration.admin import ChildConfigurationAdmin, ChildConfigurationAdminInline +from coin.offers.admin import ChildOfferSubscriptionRequestAdmin +from coin.utils import delete_selected + +from .models import ExternalAccountConfiguration, ExternalAccountSubscriptionRequest + + +class ExternalAccountConfigurationInline(ChildConfigurationAdminInline): + model = ExternalAccountConfiguration + readonly_fields = ['configuration_ptr'] + + +class ExternalAccountConfigurationAdmin(ChildConfigurationAdmin): + base_model = ExternalAccountConfiguration + list_display = ('__unicode__', 'offersubscription', 'comment') + actions = (delete_selected,) + inline = ExternalAccountConfigurationInline + + +class ExternalAccountSubscriptionRequestAdmin(ChildOfferSubscriptionRequestAdmin): + + base_model = ExternalAccountSubscriptionRequest + + +admin.site.register(ExternalAccountConfiguration, ExternalAccountConfigurationAdmin) diff --git a/external_account/apps.py b/external_account/apps.py new file mode 100644 index 0000000..1693f49 --- /dev/null +++ b/external_account/apps.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.apps import AppConfig +import coin.apps + +from . import urls + + +class ExternalAccountConfig(AppConfig, coin.apps.AppURLs): + name = 'external_account' + verbose_name = "Compte externe" + + exported_urlpatterns = [('external_account', urls.urlpatterns)] diff --git a/external_account/migrations/__init__.py b/external_account/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/external_account/models.py b/external_account/models.py new file mode 100644 index 0000000..54c8c48 --- /dev/null +++ b/external_account/models.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models +from django.core.exceptions import ValidationError +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.core.urlresolvers import reverse +from netfields import InetAddressField, NetManager + +from coin.configuration.models import Configuration +from coin.offers.models import OfferSubscriptionRequest +from coin import utils + + +class ExternalAccountConfiguration(Configuration): + + url_namespace = "external_account" + + login = models.CharField(max_length=50, unique=True, blank=True, + verbose_name="identifiant") + + def get_absolute_url(self): + return reverse('external_account:details', args=[str(self.pk)]) + + def __unicode__(self): + return self.offersubscription.offer + " pour " + self.offersubscription.member + + class Meta: + verbose_name = 'Compte externe' + verbose_name_plural = 'Comptes externe' + + +class ExternalAccountSubscriptionRequest(OfferSubscriptionRequest): + + configuration_type = "ExternalAccountConfiguration" + + class Meta: + verbose_name = 'demande de compte externe' + verbose_name_plural = 'demandes de compte externe' diff --git a/external_account/templates/external_account/external_account.html b/external_account/templates/external_account/external_account.html new file mode 100644 index 0000000..5bd067b --- /dev/null +++ b/external_account/templates/external_account/external_account.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} +{% load subnets %} +{% block content %} +
    +

    {{ object.get_state_icon_display }} {{ object }}

    + +
    +

    État

    + + + + + + + + + +
    Offre{{ object.offersubscription.offer }}
    Commentaire{{ object.comment }}
    +
    + +
    +

    Accès

    + + + + + +
    Identifiant + {% object.login %} +
    +
    + +
    + +{% endblock %} diff --git a/external_account/urls.py b/external_account/urls.py new file mode 100644 index 0000000..f5656c5 --- /dev/null +++ b/external_account/urls.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.conf.urls import patterns, url + +from .views import ExternalAccountView + +urlpatterns = patterns( + '', + # This is part of the generic configuration interface (the "name" is + # the same as the "backend_name" of the model). + url(r'^(?P\d+)$', ExternalAccountView.as_view(template_name="external_account/external_account.html"), name="details"), +) diff --git a/external_account/views.py b/external_account/views.py new file mode 100644 index 0000000..ca738ca --- /dev/null +++ b/external_account/views.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.shortcuts import get_object_or_404 +from django.views.generic.edit import UpdateView +from django.conf import settings +from django.contrib.messages.views import SuccessMessageMixin +from django.contrib.auth.decorators import login_required +from django.utils.decorators import method_decorator + +from coin.members.models import Member + +from .models import ExternalAccountConfiguration + + +class ExternalAccountView(SuccessMessageMixin, UpdateView): + model = ExternalAccountConfiguration + fields = ['comment'] + success_message = "Configuration enregistrée avec succès !" + + @method_decorator(login_required) + def dispatch(self, *args, **kwargs): + return super(ExternalAccountView, self).dispatch(*args, **kwargs) + + def get_object(self): + if self.request.user.is_superuser: + return get_object_or_404(ExternalAccountConfiguration, pk=self.kwargs.get("pk")) + # For normal users, ensure the ExternalAccount belongs to them. + return get_object_or_404(ExternalAccountConfiguration, pk=self.kwargs.get("pk"), + offersubscription__member=self.request.user) + -- GitLab From 564d62f80608a52f11d8a35a95e672048c03de10 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 6 Jul 2020 21:30:39 +0200 Subject: [PATCH 102/195] Make sure the configuration list choice is right... --- coin/offers/forms.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/coin/offers/forms.py b/coin/offers/forms.py index 6f18803..06b9f2c 100644 --- a/coin/offers/forms.py +++ b/coin/offers/forms.py @@ -4,11 +4,19 @@ from django.forms.widgets import Select from coin.offers.models import Offer, OfferSubscriptionRequest from coin.configuration.models import Configuration +class ConfigurationTypeSelect(forms.Select): + + def render(self, *args, **kwargs): + # We do this so that get_configurations_choices_list is called during + # runtime once we're sure all Configuration subclasses are loaded... + self.choices = [('', '---------'),] + list(Configuration.get_configurations_choices_list()) + return super(ConfigurationTypeSelect, self).render(*args, **kwargs) + class OfferAdminForm(forms.ModelForm): class Meta: model = Offer widgets = { - 'configuration_type': Select(choices=(('', '---------'),) + Configuration.get_configurations_choices_list()) + 'configuration_type': ConfigurationTypeSelect() } exclude = ('', ) -- GitLab From 6f161bebe9041a27d8a57e211f063ffc386f7d6d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 7 Jul 2020 01:09:30 +0200 Subject: [PATCH 103/195] Use the appropriate class when creating subscription requests --- coin/offers/forms.py | 9 ++++----- coin/offers/models.py | 13 +++++++++++++ external_account/models.py | 2 +- vpn/models.py | 2 +- vps/models.py | 2 +- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/coin/offers/forms.py b/coin/offers/forms.py index 06b9f2c..0f05b24 100644 --- a/coin/offers/forms.py +++ b/coin/offers/forms.py @@ -34,13 +34,12 @@ class OfferSubscriptionRequestStep2Form(forms.Form): member_comments = forms.CharField(label="Commentaires pour l'équipe de bénévole", max_length=1000, widget=forms.Textarea(attrs={'style': 'height: 10em;'}), required=False) agree_tos = forms.BooleanField(label="Vous acceptez les conditions d'abonnement et d'utilisation de cette offre", required=True) - subscriptionrequest_class = OfferSubscriptionRequest - def set_offer_choices(self, offer_type): self.fields['offer'].queryset = OfferSubscriptionRequest.requestable_offers(offer_type=offer_type) def create_offersubscriptionrequest(self, request): - return self.subscriptionrequest_class.objects.create(offer=self.cleaned_data["offer"], - member=request.user, - member_comments=self.cleaned_data["member_comments"]) + request_class = self.cleaned_data["offer"].subscriptionrequest_class + return request_class.objects.create(offer=self.cleaned_data["offer"], + member=request.user, + member_comments=self.cleaned_data["member_comments"]) diff --git a/coin/offers/models.py b/coin/offers/models.py index 66bf40f..d2619ae 100644 --- a/coin/offers/models.py +++ b/coin/offers/models.py @@ -108,6 +108,19 @@ class Offer(models.Model): return subconfig_class return None + + @property + def subscriptionrequest_class(self): + """ + This translate an offer into its corresponding subscription request class (e.g. VPNSubscriptionRequest) + """ + conf_class = self.configuration_class + for subconfig_class in get_descendant_classes(OfferSubscriptionRequest): + if subconfig_class.configuration_class == conf_class: + return subconfig_class + return None + + def display_price(self): """Displays the price of an offer in a human-readable manner (for instance "30€ / month") diff --git a/external_account/models.py b/external_account/models.py index 54c8c48..c0b6dcb 100644 --- a/external_account/models.py +++ b/external_account/models.py @@ -33,7 +33,7 @@ class ExternalAccountConfiguration(Configuration): class ExternalAccountSubscriptionRequest(OfferSubscriptionRequest): - configuration_type = "ExternalAccountConfiguration" + configuration_class = ExternalAccountConfiguration class Meta: verbose_name = 'demande de compte externe' diff --git a/vpn/models.py b/vpn/models.py index a2f5c9a..c84806c 100644 --- a/vpn/models.py +++ b/vpn/models.py @@ -93,7 +93,7 @@ class VPNConfiguration(CoinLdapSyncMixin, IPConfiguration): class VPNSubscriptionRequest(OfferSubscriptionRequest): - configuration_type = "VPNConfiguration" + configuration_class = VPNConfiguration class Meta: verbose_name = 'demande de VPN' diff --git a/vps/models.py b/vps/models.py index a61b29c..b297ee1 100644 --- a/vps/models.py +++ b/vps/models.py @@ -108,7 +108,7 @@ class Console(models.Model): class VPSSubscriptionRequest(OfferSubscriptionRequest, VPSProvisioningParameters): - offer_type = "VPS" + configuration_class = VPSConfiguration class Meta: verbose_name = 'demande de VPS' -- GitLab From f7db570958b27ccee37f0889f0850064b258d989 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 7 Jul 2020 01:10:16 +0200 Subject: [PATCH 104/195] Misc cosmetic fixes + add external accounts to admin index --- coin/admin.py | 1 + external_account/models.py | 2 +- .../templates/external_account/external_account.html | 6 ++---- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/coin/admin.py b/coin/admin.py index 2fff950..6f856dc 100644 --- a/coin/admin.py +++ b/coin/admin.py @@ -29,6 +29,7 @@ admin_cards = [ "icon": "gears", "title": "Configurations", "models": [ + ("Comptes externes", "external_account/externalaccountconfiguration"), ("Tunnel VPN", "vpn/vpnconfiguration"), ("Serveur VPS", "vps/vpsconfiguration"), ("Housing", "housing/housingconfiguration"), diff --git a/external_account/models.py b/external_account/models.py index c0b6dcb..7e31d59 100644 --- a/external_account/models.py +++ b/external_account/models.py @@ -24,7 +24,7 @@ class ExternalAccountConfiguration(Configuration): return reverse('external_account:details', args=[str(self.pk)]) def __unicode__(self): - return self.offersubscription.offer + " pour " + self.offersubscription.member + return "%s pour %s" % (self.offersubscription.offer, self.offersubscription.member) class Meta: verbose_name = 'Compte externe' diff --git a/external_account/templates/external_account/external_account.html b/external_account/templates/external_account/external_account.html index 5bd067b..978562b 100644 --- a/external_account/templates/external_account/external_account.html +++ b/external_account/templates/external_account/external_account.html @@ -2,7 +2,7 @@ {% load subnets %} {% block content %}
    -

    {{ object.get_state_icon_display }} {{ object }}

    +

    {{ object.get_state_icon_display }} {{ object.offersubscription.offer.name }}

    État

    @@ -23,9 +23,7 @@ - +
    Identifiant - {% object.login %} - {{ object.login }}
    -- GitLab From 5fefddf595be3819510fc6984ee56f14f96a4f6f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 7 Jul 2020 01:28:28 +0200 Subject: [PATCH 105/195] Add state icon to list views of vps / vpn / external account --- coin/configuration/models.py | 1 + external_account/admin.py | 3 ++- vpn/admin.py | 2 +- vps/admin.py | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 51996c0..d832b21 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -188,6 +188,7 @@ class Configuration(PolymorphicModel): text = "Status: %s\nDernière mise à jour: %s" % (text, str(self.last_status_update)) return mark_safe(''.format(color=color, text=text)) + get_state_icon_display.short_description = 'État' def __str__(self): return str(self.offersubscription) diff --git a/external_account/admin.py b/external_account/admin.py index 920168e..f149a63 100644 --- a/external_account/admin.py +++ b/external_account/admin.py @@ -17,7 +17,8 @@ class ExternalAccountConfigurationInline(ChildConfigurationAdminInline): class ExternalAccountConfigurationAdmin(ChildConfigurationAdmin): base_model = ExternalAccountConfiguration - list_display = ('__unicode__', 'offersubscription', 'comment') + list_display = ('__unicode__', 'offersubscription', 'get_state_icon_display', 'comment') + actions = (delete_selected,) inline = ExternalAccountConfigurationInline diff --git a/vpn/admin.py b/vpn/admin.py index a9e87d2..3b5b380 100644 --- a/vpn/admin.py +++ b/vpn/admin.py @@ -18,7 +18,7 @@ class VPNConfigurationInline(ChildConfigurationAdminInline): class VPNConfigurationAdmin(ChildConfigurationAdmin): base_model = VPNConfiguration - list_display = ('__unicode__', 'offersubscription', + list_display = ('__unicode__', 'offersubscription', 'get_state_icon_display', 'ipv4_endpoint', 'ipv6_endpoint', 'comment') search_fields = ('login', 'comment', # TODO: searching on member directly doesn't work diff --git a/vps/admin.py b/vps/admin.py index 2afd4cb..09f37f3 100644 --- a/vps/admin.py +++ b/vps/admin.py @@ -27,7 +27,7 @@ class VPSConfigurationInline(ChildConfigurationAdminInline): class VPSConfigurationAdmin(ChildConfigurationAdmin): base_model = VPSConfiguration - list_display = ('__unicode__', 'offersubscription', + list_display = ('__unicode__', 'offersubscription', 'get_state_icon_display', 'ipv4_endpoint', 'ipv6_endpoint', 'comment') search_fields = ('comment', # TODO: searching on member directly doesn't work -- GitLab From 0a211adc0833a0ab9554df9d78812996f2c4118b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 7 Jul 2020 01:45:37 +0200 Subject: [PATCH 106/195] Add optional infos in offer, to be displayed in member space --- coin/offers/models.py | 4 ++++ .../templates/external_account/external_account.html | 8 ++++++++ vpn/templates/vpn/vpn.html | 9 +++++++++ vps/templates/vps/vps.html | 8 ++++++++ 4 files changed, 29 insertions(+) diff --git a/coin/offers/models.py b/coin/offers/models.py index d2619ae..404a4be 100644 --- a/coin/offers/models.py +++ b/coin/offers/models.py @@ -67,6 +67,10 @@ class Offer(models.Model): requestable = models.BooleanField(default=False, verbose_name='Permettre aux membres de créer des demandes pour cette offre depuis leur espace') + infos = models.TextField(blank=True, + verbose_name='infos complémentaires', + help_text="Informations complémentaires pour les membres, affichées dans leur espace") + ip_pools = models.ManyToManyField(IPPool, related_name='offers', through='OfferIPPool') objects = OfferManager() diff --git a/external_account/templates/external_account/external_account.html b/external_account/templates/external_account/external_account.html index 978562b..62a3b3e 100644 --- a/external_account/templates/external_account/external_account.html +++ b/external_account/templates/external_account/external_account.html @@ -28,6 +28,14 @@
    + {% if object.offersubscription.offer.infos %} +
    +

    Informations complémentaires de l'équipe bénévole

    +
    + {{ object.offersubscription.offer.infos|safe }} +
    +
    + {% endif %}
    {% endblock %} diff --git a/vpn/templates/vpn/vpn.html b/vpn/templates/vpn/vpn.html index 19c4c22..11da765 100644 --- a/vpn/templates/vpn/vpn.html +++ b/vpn/templates/vpn/vpn.html @@ -47,6 +47,15 @@
    + + {% if object.offersubscription.offer.infos %} +
    +

    Informations complémentaires de l'équipe bénévole

    +
    + {{ object.offersubscription.offer.infos|safe }} +
    +
    + {% endif %}
    {% endblock %} diff --git a/vps/templates/vps/vps.html b/vps/templates/vps/vps.html index 2616846..5be2808 100644 --- a/vps/templates/vps/vps.html +++ b/vps/templates/vps/vps.html @@ -57,6 +57,14 @@ + {% if object.offersubscription.offer.infos %} +
    +

    Informations complémentaires de l'équipe bénévole

    +
    + {{ object.offersubscription.offer.infos|safe }} +
    +
    + {% endif %} {% endblock %} -- GitLab From dcadea92dbcf62e149ea090c24764b92e25810aa Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 7 Jul 2020 02:14:41 +0200 Subject: [PATCH 107/195] Send an email to admins when a subscription request is made by a member --- .../members/emails/new_member_email.html | 2 +- coin/offers/forms.py | 24 ++++++++++++++++--- .../emails/subscription_request_new.html | 12 ++++++++++ .../subscription_request_new_subject.txt | 1 + coin/offers/views.py | 3 +-- coin/settings_base.py | 1 + 6 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 coin/offers/templates/offers/emails/subscription_request_new.html create mode 100644 coin/offers/templates/offers/emails/subscription_request_new_subject.txt diff --git a/coin/members/templates/members/emails/new_member_email.html b/coin/members/templates/members/emails/new_member_email.html index e5d2eec..9ce1e6a 100644 --- a/coin/members/templates/members/emails/new_member_email.html +++ b/coin/members/templates/members/emails/new_member_email.html @@ -6,4 +6,4 @@ Bonjour,

    Lien d'édition: {{ edit_link }}

    Bisou, -Votre canard dévoué +Votre cigogne dévouée diff --git a/coin/offers/forms.py b/coin/offers/forms.py index 0f05b24..f80ad43 100644 --- a/coin/offers/forms.py +++ b/coin/offers/forms.py @@ -1,8 +1,12 @@ # -*- coding: utf-8 -*- from django import forms from django.forms.widgets import Select +from django.core.urlresolvers import reverse +from django.conf import settings + from coin.offers.models import Offer, OfferSubscriptionRequest from coin.configuration.models import Configuration +from coin.utils import send_templated_email class ConfigurationTypeSelect(forms.Select): @@ -40,6 +44,20 @@ class OfferSubscriptionRequestStep2Form(forms.Form): def create_offersubscriptionrequest(self, request): request_class = self.cleaned_data["offer"].subscriptionrequest_class - return request_class.objects.create(offer=self.cleaned_data["offer"], - member=request.user, - member_comments=self.cleaned_data["member_comments"]) + subscription_request = request_class.objects.create(offer=self.cleaned_data["offer"], + member=request.user, + member_comments=self.cleaned_data["member_comments"]) + subscription_request.save() + + + if settings.REQUESTS_NOTIFICATION_EMAILS: + + relative_link = reverse('admin:offers_offersubscriptionrequest_change', args=[subscription_request.id]) + admin_link = request.build_absolute_uri(relative_link) + + send_templated_email( + to=settings.REQUESTS_NOTIFICATION_EMAILS, + subject_template='offers/emails/subscription_request_new_subject.txt', + body_template='offers/emails/subscription_request_new.txt', + context={'request': subscription_request, 'admin_link': admin_link}, + ) diff --git a/coin/offers/templates/offers/emails/subscription_request_new.html b/coin/offers/templates/offers/emails/subscription_request_new.html new file mode 100644 index 0000000..389143a --- /dev/null +++ b/coin/offers/templates/offers/emails/subscription_request_new.html @@ -0,0 +1,12 @@ +Bonjour,
    + +

    +{{ request.member }} viens de faire une demande pour un {{ request.offer.name }} +

    + +

    +Lien pour gérer la requête: {{ admin_link }} +

    + +Bisou, +Votre cigogne dévouée diff --git a/coin/offers/templates/offers/emails/subscription_request_new_subject.txt b/coin/offers/templates/offers/emails/subscription_request_new_subject.txt new file mode 100644 index 0000000..70f48a9 --- /dev/null +++ b/coin/offers/templates/offers/emails/subscription_request_new_subject.txt @@ -0,0 +1 @@ +Nouvelle demande de {{ request.member }} pour un {{ request.offer.name }} diff --git a/coin/offers/views.py b/coin/offers/views.py index c897b9c..d29daa8 100644 --- a/coin/offers/views.py +++ b/coin/offers/views.py @@ -80,8 +80,7 @@ def offersubscriptionrequest_step2(request, offer_type): form = FormForThisOfferType(request.POST) form.set_offer_choices(offer_type) if form.is_valid(): - subscriptionrequest = form.create_offersubscriptionrequest(request) - subscriptionrequest.save() + form.create_offersubscriptionrequest(request) return HttpResponseRedirect(reverse('members:subscriptionrequest_step3')) else: form = FormForThisOfferType() diff --git a/coin/settings_base.py b/coin/settings_base.py index 3d46df5..d2c2ca5 100644 --- a/coin/settings_base.py +++ b/coin/settings_base.py @@ -18,6 +18,7 @@ ADMINS = ( # Email on which to send emails SUBSCRIPTIONS_NOTIFICATION_EMAILS = [] +REQUESTS_NOTIFICATION_EMAILS = [] MANAGERS = ADMINS -- GitLab From a05de883f51f558a539f031568e8455cbdd25933 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Jul 2020 17:18:20 +0200 Subject: [PATCH 108/195] Gotta return the subscription_request so that child classes can properly tweak the behavior --- coin/offers/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coin/offers/forms.py b/coin/offers/forms.py index f80ad43..0a56316 100644 --- a/coin/offers/forms.py +++ b/coin/offers/forms.py @@ -49,7 +49,6 @@ class OfferSubscriptionRequestStep2Form(forms.Form): member_comments=self.cleaned_data["member_comments"]) subscription_request.save() - if settings.REQUESTS_NOTIFICATION_EMAILS: relative_link = reverse('admin:offers_offersubscriptionrequest_change', args=[subscription_request.id]) @@ -61,3 +60,5 @@ class OfferSubscriptionRequestStep2Form(forms.Form): body_template='offers/emails/subscription_request_new.txt', context={'request': subscription_request, 'admin_link': admin_link}, ) + + return subscription_request -- GitLab From 72b233a60e39d7f7bebdf5155dcd3c8bbce6fa4e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Jul 2020 18:12:53 +0200 Subject: [PATCH 109/195] Fix admin view for external account (only show IPSubnet inline for child of IPConfiguration, + show login field for external account) --- coin/configuration/admin.py | 12 ++++++++---- external_account/admin.py | 5 +++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/coin/configuration/admin.py b/coin/configuration/admin.py index ef25308..31f160f 100644 --- a/coin/configuration/admin.py +++ b/coin/configuration/admin.py @@ -9,7 +9,7 @@ from django.core.urlresolvers import reverse from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin from coin.resources.models import IPSubnet -from coin.configuration.models import Configuration +from coin.configuration.models import Configuration, IPConfiguration from coin.configuration.forms import ConfigurationForm """ @@ -62,12 +62,16 @@ class ParentConfigurationAdmin(PolymorphicParentModelAdmin): class ChildConfigurationAdmin(PolymorphicChildModelAdmin): base_form = ConfigurationForm - # For each child (admin object for configurations), this will display - # an inline form to assign IP addresses. - inlines = (IPSubnetInline, ) change_form_template = "admin/configuration/configuration/change_form.html" + def get_inlines(self, request, obj=None): + + if isinstance(obj, IPConfiguration): + return tuple(IPSubnetInline, ) + else: + return tuple() + def get_fieldsets(self, request, obj=None): fields = [('', {'fields': ['offersubscription', 'comment', diff --git a/external_account/admin.py b/external_account/admin.py index f149a63..8b1f8c5 100644 --- a/external_account/admin.py +++ b/external_account/admin.py @@ -22,6 +22,11 @@ class ExternalAccountConfigurationAdmin(ChildConfigurationAdmin): actions = (delete_selected,) inline = ExternalAccountConfigurationInline + def get_fieldsets(self, request, obj=None): + fields = super(ExternalAccountConfigurationAdmin, self).get_fieldsets(request, obj) + fields[0][1]["fields"].append("login") + return fields + class ExternalAccountSubscriptionRequestAdmin(ChildOfferSubscriptionRequestAdmin): -- GitLab From 210650b593fa971b0d078e4e5622f443db89d6a0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Jul 2020 18:38:52 +0200 Subject: [PATCH 110/195] Also display login in inline admin for external accounts --- coin/configuration/admin.py | 8 ++++---- external_account/admin.py | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/coin/configuration/admin.py b/coin/configuration/admin.py index 31f160f..26e8820 100644 --- a/coin/configuration/admin.py +++ b/coin/configuration/admin.py @@ -157,19 +157,19 @@ class ChildConfigurationAdminInline(admin.StackedInline): def get_fieldsets(self, request, obj=None): - fields = [('', {'fields': ('comment', + fields = [('', {'fields': ['comment', 'provisioned', - 'status')})] + 'status']})] conf = obj.configuration if hasattr(conf, "ipv4_endpoint") and hasattr(conf, "ipv6_endpoint"): fields.append(["Adresses IP (endpoints)", - {"fields": tuple(["ipv4_endpoint", "ipv6_endpoint"])}]) + {"fields": ["ipv4_endpoint", "ipv6_endpoint"]}]) if hasattr(conf, "provisioning_infos") and conf.provisioning_infos: fields.append(["Informations pour le provisionnement", - {"fields": tuple(conf.provisioning_infos)}]) + {"fields": conf.provisioning_infos}]) return fields diff --git a/external_account/admin.py b/external_account/admin.py index 8b1f8c5..f783087 100644 --- a/external_account/admin.py +++ b/external_account/admin.py @@ -14,6 +14,11 @@ class ExternalAccountConfigurationInline(ChildConfigurationAdminInline): model = ExternalAccountConfiguration readonly_fields = ['configuration_ptr'] + def get_fieldsets(self, request, obj=None): + fields = super(ExternalAccountConfigurationInline, self).get_fieldsets(request, obj) + fields[0][1]["fields"].append("login") + return fields + class ExternalAccountConfigurationAdmin(ChildConfigurationAdmin): base_model = ExternalAccountConfiguration -- GitLab From af8dd620719dc413e6179eff0af5139ceae60f86 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Jul 2020 19:24:13 +0200 Subject: [PATCH 111/195] Update view for housing --- housing/templates/housing/housing.html | 73 +++++++++++++++----------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/housing/templates/housing/housing.html b/housing/templates/housing/housing.html index 1f917f1..b8676be 100644 --- a/housing/templates/housing/housing.html +++ b/housing/templates/housing/housing.html @@ -1,40 +1,49 @@ {% extends "base.html" %} - {% load subnets %} - {% block content %}
    -

    Configuration du Housing

    - {% for message in messages %} -
    - {{ message }} -
    - {% endfor %} -
    -
    -

    Adresses IP

    - - - - - - - - - - - - - - - - - -
    VLAN ID{{ object.vlan }}
    IPv4{{ object.ipv4_endpoint }}
    IPv6{{ object.ipv6_endpoint }}
    Sous-réseaux - {% for subnet in object.ip_subnet.all %}{{ subnet|prettify }}
    {% endfor %} -
    -
    +

    {{ object.get_state_icon_display }} {{ object }}

    + +
    +

    État

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Offre{{ object.offersubscription.offer }}
    Commentaire{{ object.comment }}
    VLAN ID{{ object.vlan }}
    IPv4{{ object.ipv4_endpoint|prettify }}
    IPv6{{ object.ipv6_endpoint|prettify }}
    Sous-réseaux + {% for subnet in object.ip_subnet.all %}{{ subnet }}
    {% endfor %} +
    +
    + + {% if object.offersubscription.offer.infos %} +
    +

    Informations complémentaires de l'équipe bénévole

    +
    + {{ object.offersubscription.offer.infos|safe }} +
    + {% endif %}
    {% endblock %} -- GitLab From 97bddfce730d71aea2d1a5ede9b59e507460f550 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Jul 2020 20:42:45 +0200 Subject: [PATCH 112/195] Fix update_configuration_state argument --- .../management/commands/update_configuration_states.py | 3 ++- coin/configuration/models.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/coin/configuration/management/commands/update_configuration_states.py b/coin/configuration/management/commands/update_configuration_states.py index bf97716..e57ccdb 100644 --- a/coin/configuration/management/commands/update_configuration_states.py +++ b/coin/configuration/management/commands/update_configuration_states.py @@ -35,6 +35,7 @@ from argparse import RawTextHelpFormatter from django.core.management.base import BaseCommand, CommandError from coin.configuration.models import Configuration +from coin.utils import get_descendant_classes ################################################################################ @@ -61,7 +62,7 @@ class Command(BaseCommand): if not options["conf_type"] != "": raise CommandError("You must provide a configuration type") - conf_classes = {c()._meta.verbose_name: c().__class__ for c in Configuration.__subclasses__()} + conf_classes = {c.url_namespace: c for c in get_descendant_classes(Configuration)} if options["conf_type"] not in conf_classes: raise CommandError("Unknown conf type %s ... known conf types are : %s" % (options["conf_type"], ', '.join(conf_classes.keys()))) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index d832b21..44c5035 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -158,7 +158,7 @@ class Configuration(PolymorphicModel): writer.writerow([c[info] for info in info_order]) conf_list_csv = conf_list_csv.getvalue() - success, out, err = HookManager.run(self._meta.verbose_name, "FETCH_ALL_STATES", conf_list=conf_list, conf_list_csv=conf_list_csv) + success, out, err = HookManager.run(self.url_namespace, "FETCH_ALL_STATES", conf_list=conf_list, conf_list_csv=conf_list_csv) if not success: raise Exception("Some errors happened during the execution of FETCH_ALL_STATES for %s : %s" % (self._meta.verbose_name, err)) -- GitLab From 59ecfe35e333581c6e1277ad7aa7344e41e07914 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Jul 2020 20:49:43 +0200 Subject: [PATCH 113/195] Fix some encoding issues in hook mechanism --- coin/configuration/models.py | 6 +++--- coin/hooks.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 44c5035..878bfe5 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -81,9 +81,9 @@ class Configuration(PolymorphicModel): def convert_to_dict_for_hook(self): # FIXME : check assertions here return {"id": self.pk, - "offer_name": str(self.offersubscription.offer.name), - "offer_ref": str(self.offersubscription.offer.reference), - "username": str(self.offersubscription.member.username), + "offer_name": self.offersubscription.offer.name.encode('utf-8'), + "offer_ref": self.offersubscription.offer.reference.encode('utf-8'), + "username": self.offersubscription.member.username.encode('utf-8'), } def provision(self): diff --git a/coin/hooks.py b/coin/hooks.py index 62c5a8f..57af661 100644 --- a/coin/hooks.py +++ b/coin/hooks.py @@ -106,7 +106,7 @@ class HookManager(): to_run = HookManager._format_hook_bash(hook, **kwargs) if "stdin" in hook: - stdin = hook["stdin"].format(**kwargs) + stdin = hook["stdin"].encode('utf-8').format(**kwargs) else: stdin = None -- GitLab From 010c3b40ac004d55735b21352f581d8b5106e1c4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Jul 2020 20:56:08 +0200 Subject: [PATCH 114/195] Ugh fix again some hook namespace consistency --- coin/configuration/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 878bfe5..270bb41 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -64,11 +64,11 @@ class Configuration(PolymorphicModel): @classmethod def provision_is_managed_via_hook(self): - return HookManager.is_defined(self._meta.verbose_name, "PROVISION") + return HookManager.is_defined(self.url_namespace, "PROVISION") @classmethod def state_is_managed_via_hook(self): - return HookManager.is_defined(self._meta.verbose_name, "FETCH_ALL_STATES") + return HookManager.is_defined(self.url_namespace, "FETCH_ALL_STATES") @staticmethod def get_configurations_choices_list(): @@ -147,7 +147,7 @@ class Configuration(PolymorphicModel): {"id":7, "provisioned": "ongoing", "status": "booting", "status_color": "orange"} ] """ - assert self.state_is_managed_via_hook(), "There is no hook defined for %s" % self._meta.verbose_name + assert self.state_is_managed_via_hook(), "There is no hook defined for %s" % self.url_namespace confs_to_update = self.objects.all() conf_list = [c.convert_to_dict_for_hook() for c in confs_to_update] @@ -161,7 +161,7 @@ class Configuration(PolymorphicModel): success, out, err = HookManager.run(self.url_namespace, "FETCH_ALL_STATES", conf_list=conf_list, conf_list_csv=conf_list_csv) if not success: - raise Exception("Some errors happened during the execution of FETCH_ALL_STATES for %s : %s" % (self._meta.verbose_name, err)) + raise Exception("Some errors happened during the execution of FETCH_ALL_STATES for %s : %s" % (self.url_namespace, err)) assert isinstance(out, list), "Was expecting to get a list as output of FETCH_ALL_STATES" for state_infos in out: -- GitLab From be27468307b810c15e2c0ae57cee3327db738748 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Jul 2020 21:18:19 +0200 Subject: [PATCH 115/195] Bored about this code ~.~ --- README.md | 11 ----------- coin/settings_base.py | 3 --- vpn/models.py | 37 +++---------------------------------- 3 files changed, 3 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index ce1b7f2..16e90d3 100644 --- a/README.md +++ b/README.md @@ -420,17 +420,6 @@ See also [using optional apps](#using-optional-apps). - `{email}`: the mail address of the list - `{short_name}`: the list name -#### vpn - -- `VPN_SECRETS_TRANSMISSION_METHOD` : how are VPN secrets transmited to - subscriber ? Two values are currently supported : - - `gen-password-and-forget` (default, used by Illyse) : generate a - password, push it to LDAP (which holds VPN auth), displays it to user and - forget it. - - `crypto-link` (used by ARN) : credentials are generated by an admin - outside coin, and put on an encrypted burn-after-reading web page, whom - URL is filled-in coin. - Accounting logs --------------- diff --git a/coin/settings_base.py b/coin/settings_base.py index d2c2ca5..805efd2 100644 --- a/coin/settings_base.py +++ b/coin/settings_base.py @@ -331,9 +331,6 @@ MEMBER_CAN_EDIT_VPS_CONF = True # Allow user to edit their VPN Info MEMBER_CAN_EDIT_VPN_CONF = True -# vpn app settings : how do we transmit the VPN secrets to subscriber ? -VPN_SECRETS_TRANSMISSION_METHOD = 'gen-password-and-forget' - # Account registration # Allow visitor to join the association by register on COIN REGISTRATION_OPEN = False diff --git a/vpn/models.py b/vpn/models.py index c84806c..d7dcd1a 100644 --- a/vpn/models.py +++ b/vpn/models.py @@ -8,18 +8,15 @@ from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import reverse from netfields import InetAddressField, NetManager -from coin.mixins import CoinLdapSyncMixin from coin.configuration.models import IPConfiguration from coin.offers.models import OfferSubscriptionRequest from coin import utils -class VPNConfiguration(CoinLdapSyncMixin, IPConfiguration): +class VPNConfiguration(IPConfiguration): + url_namespace = "vpn" - # administrative_subscription = models.OneToOneField( - # 'offers.OfferSubscription', - # related_name=backend_name, - # validators=[ValidateBackendType(backend_name)]) + login = models.CharField(max_length=50, unique=True, blank=True, verbose_name="identifiant", help_text="Laisser vide pour une génération automatique") @@ -28,37 +25,9 @@ class VPNConfiguration(CoinLdapSyncMixin, IPConfiguration): crypto_link = models.URLField(verbose_name="Matériel cryptographique", blank=True, null=True, help_text="Lien à usage unique (détruit après ouverture)") - METHOD_CRYPTO_LINK = 'crypto-link' - METHOD_GEN_PASSWORD_AND_FORGET = 'gen-password-and-forget' - - SECRETS_TRANSMISSION_METHOD = settings.VPN_SECRETS_TRANSMISSION_METHOD - - if SECRETS_TRANSMISSION_METHOD not in ( - METHOD_CRYPTO_LINK, METHOD_GEN_PASSWORD_AND_FORGET): - raise ImproperlyConfigured( - 'Invalid value for VPN_SECRETS_TRANSMISSION_METHOD setting: {}'.format( - SECRETS_TRANSMISSION_METHOD)) - def get_absolute_url(self): return reverse('vpn:details', args=[str(self.pk)]) - def sync_to_ldap(self, creation, *args, **kwargs): - if creation: - config = LdapVPNConfig() - else: - config = LdapVPNConfig.objects.get(pk=self.login) - config.login = config.sn = self.login - config.password = self.password - config.active = 'yes' if self.activated else 'no' - config.ipv4_endpoint = utils.str_or_none(self.ipv4_endpoint) - config.ipv6_endpoint = utils.str_or_none(self.ipv6_endpoint) - config.ranges_v4 = [str(s) for s in self.get_subnets(4)] - config.ranges_v6 = [str(s) for s in self.get_subnets(6)] - config.save() - - def delete_from_ldap(self): - LdapVPNConfig.objects.get(pk=self.login).delete() - def fill_empty_fields(self): # Generate VPN login, of the form "login-vpnX". The resulting # login should not contain any ".", because graphite uses "." as a -- GitLab From d0428fd06e1933afbb223567c56ae9a5455ddc9d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Jul 2020 21:56:54 +0200 Subject: [PATCH 116/195] Properly handle specific configuration fields to be displayed in admin --- coin/configuration/admin.py | 19 ++++++++++++++++--- external_account/admin.py | 10 ++-------- vpn/admin.py | 9 ++------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/coin/configuration/admin.py b/coin/configuration/admin.py index 26e8820..8dc881e 100644 --- a/coin/configuration/admin.py +++ b/coin/configuration/admin.py @@ -61,10 +61,13 @@ class ParentConfigurationAdmin(PolymorphicParentModelAdmin): class ChildConfigurationAdmin(PolymorphicChildModelAdmin): + base_form = ConfigurationForm change_form_template = "admin/configuration/configuration/change_form.html" + specific_fields = [] + def get_inlines(self, request, obj=None): if isinstance(obj, IPConfiguration): @@ -78,6 +81,10 @@ class ChildConfigurationAdmin(PolymorphicChildModelAdmin): 'provisioned', 'status']})] + if self.specific_fields: + fields.append(["Configuration spécifique", + {"fields": tuple(self.specific_fields)}]) + if hasattr(obj, "provisioning_infos") and obj.provisioning_infos: fields.append(["Informations pour le provisionnement", {"fields": tuple(obj.provisioning_infos)}]) @@ -155,6 +162,8 @@ class ChildConfigurationAdmin(PolymorphicChildModelAdmin): class ChildConfigurationAdminInline(admin.StackedInline): + specific_fields = [] + def get_fieldsets(self, request, obj=None): fields = [('', {'fields': ['comment', @@ -163,14 +172,18 @@ class ChildConfigurationAdminInline(admin.StackedInline): conf = obj.configuration - if hasattr(conf, "ipv4_endpoint") and hasattr(conf, "ipv6_endpoint"): - fields.append(["Adresses IP (endpoints)", - {"fields": ["ipv4_endpoint", "ipv6_endpoint"]}]) + if self.specific_fields: + fields.append(["Configuration spécifique", + {"fields": tuple(self.specific_fields)}]) if hasattr(conf, "provisioning_infos") and conf.provisioning_infos: fields.append(["Informations pour le provisionnement", {"fields": conf.provisioning_infos}]) + if hasattr(conf, "ipv4_endpoint") and hasattr(conf, "ipv6_endpoint"): + fields.append(["Adresses IP (endpoints)", + {"fields": ["ipv4_endpoint", "ipv6_endpoint"]}]) + return fields def get_readonly_fields(self, request, obj=None): diff --git a/external_account/admin.py b/external_account/admin.py index f783087..6eef6d7 100644 --- a/external_account/admin.py +++ b/external_account/admin.py @@ -14,10 +14,7 @@ class ExternalAccountConfigurationInline(ChildConfigurationAdminInline): model = ExternalAccountConfiguration readonly_fields = ['configuration_ptr'] - def get_fieldsets(self, request, obj=None): - fields = super(ExternalAccountConfigurationInline, self).get_fieldsets(request, obj) - fields[0][1]["fields"].append("login") - return fields + specific_fields = ["login"] class ExternalAccountConfigurationAdmin(ChildConfigurationAdmin): @@ -27,10 +24,7 @@ class ExternalAccountConfigurationAdmin(ChildConfigurationAdmin): actions = (delete_selected,) inline = ExternalAccountConfigurationInline - def get_fieldsets(self, request, obj=None): - fields = super(ExternalAccountConfigurationAdmin, self).get_fieldsets(request, obj) - fields[0][1]["fields"].append("login") - return fields + specific_fields = ["login"] class ExternalAccountSubscriptionRequestAdmin(ChildOfferSubscriptionRequestAdmin): diff --git a/vpn/admin.py b/vpn/admin.py index 3b5b380..7b307c4 100644 --- a/vpn/admin.py +++ b/vpn/admin.py @@ -14,6 +14,7 @@ class VPNConfigurationInline(ChildConfigurationAdminInline): model = VPNConfiguration exclude = ('password',) readonly_fields = ['configuration_ptr', 'login'] + specific_fields = ["login", "crypto_link"] class VPNConfigurationAdmin(ChildConfigurationAdmin): @@ -28,13 +29,7 @@ class VPNConfigurationAdmin(ChildConfigurationAdmin): actions = (delete_selected,) exclude = ("password",) inline = VPNConfigurationInline - - def get_readonly_fields(self, request, obj=None): - readonly_fields = super(VPNConfigurationAdmin, self).get_readonly_fields(request, obj) - if obj: - readonly_fields = list(readonly_fields) - readonly_fields.append('login') - return readonly_fields + specific_fields = ["login", "crypto_link"] class VPNSubscriptionRequestAdmin(ChildOfferSubscriptionRequestAdmin): -- GitLab From 053eab6f5179f0e7f339e8014e5548cd73890b9b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Jul 2020 21:57:20 +0200 Subject: [PATCH 117/195] External accounts: Also feed the login field to hook --- external_account/models.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/external_account/models.py b/external_account/models.py index 7e31d59..f2f8f19 100644 --- a/external_account/models.py +++ b/external_account/models.py @@ -26,6 +26,11 @@ class ExternalAccountConfiguration(Configuration): def __unicode__(self): return "%s pour %s" % (self.offersubscription.offer, self.offersubscription.member) + def convert_to_dict_for_hook(self): + d = super(ExternalAccountConfiguration, self).convert_to_dict_for_hook() + d["login"] = login + return d + class Meta: verbose_name = 'Compte externe' verbose_name_plural = 'Comptes externe' -- GitLab From ef3d2b6c2c51596c436e5f07ee9e9a0fd731a28a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Jul 2020 23:25:56 +0200 Subject: [PATCH 118/195] Misc fixes following tests... --- coin/configuration/models.py | 7 +++---- external_account/models.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/coin/configuration/models.py b/coin/configuration/models.py index 270bb41..5a93ef0 100644 --- a/coin/configuration/models.py +++ b/coin/configuration/models.py @@ -152,12 +152,12 @@ class Configuration(PolymorphicModel): conf_list = [c.convert_to_dict_for_hook() for c in confs_to_update] conf_list_csv = io.StringIO() - info_order = ["id", "offer_name", "offer_ref", "username"] + info_keys = conf_list[0].keys() writer = csv.writer(conf_list_csv, delimiter=str(';'), quotechar=str('"'), quoting=csv.QUOTE_MINIMAL) + writer.writerow([key for key in info_keys]) for c in conf_list: - writer.writerow([c[info] for info in info_order]) + writer.writerow([c[info] for info in info_keys]) conf_list_csv = conf_list_csv.getvalue() - success, out, err = HookManager.run(self.url_namespace, "FETCH_ALL_STATES", conf_list=conf_list, conf_list_csv=conf_list_csv) if not success: @@ -168,7 +168,6 @@ class Configuration(PolymorphicModel): assert isinstance(state_infos, dict) and "id" in state_infos, "Was expecting to get a list of dict as output of FETCH_ALL_STATES with at least 'id' in it" conf_to_update = Configuration.objects.get(pk=state_infos.pop("id")) conf_to_update.update_state(**state_infos) - print conf_to_update.__dict__ def get_state_icon_display(self): diff --git a/external_account/models.py b/external_account/models.py index f2f8f19..e4cc39f 100644 --- a/external_account/models.py +++ b/external_account/models.py @@ -28,7 +28,7 @@ class ExternalAccountConfiguration(Configuration): def convert_to_dict_for_hook(self): d = super(ExternalAccountConfiguration, self).convert_to_dict_for_hook() - d["login"] = login + d["login"] = self.login return d class Meta: -- GitLab From 590d20be681fb2e9de7265e04da3412c1486549b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 26 Jul 2020 15:05:43 +0200 Subject: [PATCH 119/195] Drop foundation, replace with bootstrap 4 + crispy module --- coin/settings_base.py | 4 +- coin/static/css/bootstrap.min.css | 7 + coin/static/css/foundation.css | 5006 ----------------- coin/static/css/foundation.min.css | 1 - coin/static/css/normalize.css | 410 -- coin/static/js/foundation.min.js | 10 - coin/static/js/foundation/foundation.abide.js | 256 - .../js/foundation/foundation.accordion.js | 49 - coin/static/js/foundation/foundation.alert.js | 37 - .../js/foundation/foundation.clearing.js | 485 -- .../js/foundation/foundation.dropdown.js | 208 - .../js/foundation/foundation.equalizer.js | 64 - .../js/foundation/foundation.interchange.js | 326 -- .../js/foundation/foundation.joyride.js | 848 --- coin/static/js/foundation/foundation.js | 587 -- .../js/foundation/foundation.magellan.js | 171 - .../js/foundation/foundation.offcanvas.js | 39 - coin/static/js/foundation/foundation.orbit.js | 464 -- .../static/js/foundation/foundation.reveal.js | 399 -- coin/static/js/foundation/foundation.tab.js | 58 - .../js/foundation/foundation.tooltip.js | 215 - .../static/js/foundation/foundation.topbar.js | 387 -- coin/static/js/foundation/jquery.cookie.js | 8 - coin/static/js/vendor/bootstrap.js | 7 + coin/static/js/vendor/fastclick.js | 9 - coin/static/js/vendor/jquery.cookie.js | 8 - coin/static/js/vendor/jquery.js | 28 +- coin/static/js/vendor/modernizr.js | 8 - coin/static/js/vendor/placeholder.js | 2 - coin/templates/base.html | 13 +- requirements.txt | 1 + 31 files changed, 23 insertions(+), 10092 deletions(-) create mode 100644 coin/static/css/bootstrap.min.css delete mode 100644 coin/static/css/foundation.css delete mode 100644 coin/static/css/foundation.min.css delete mode 100644 coin/static/css/normalize.css delete mode 100644 coin/static/js/foundation.min.js delete mode 100644 coin/static/js/foundation/foundation.abide.js delete mode 100644 coin/static/js/foundation/foundation.accordion.js delete mode 100644 coin/static/js/foundation/foundation.alert.js delete mode 100644 coin/static/js/foundation/foundation.clearing.js delete mode 100644 coin/static/js/foundation/foundation.dropdown.js delete mode 100644 coin/static/js/foundation/foundation.equalizer.js delete mode 100644 coin/static/js/foundation/foundation.interchange.js delete mode 100644 coin/static/js/foundation/foundation.joyride.js delete mode 100644 coin/static/js/foundation/foundation.js delete mode 100644 coin/static/js/foundation/foundation.magellan.js delete mode 100644 coin/static/js/foundation/foundation.offcanvas.js delete mode 100644 coin/static/js/foundation/foundation.orbit.js delete mode 100644 coin/static/js/foundation/foundation.reveal.js delete mode 100644 coin/static/js/foundation/foundation.tab.js delete mode 100644 coin/static/js/foundation/foundation.tooltip.js delete mode 100644 coin/static/js/foundation/foundation.topbar.js delete mode 100644 coin/static/js/foundation/jquery.cookie.js create mode 100644 coin/static/js/vendor/bootstrap.js delete mode 100644 coin/static/js/vendor/fastclick.js delete mode 100644 coin/static/js/vendor/jquery.cookie.js delete mode 100644 coin/static/js/vendor/modernizr.js delete mode 100644 coin/static/js/vendor/placeholder.js diff --git a/coin/settings_base.py b/coin/settings_base.py index d2c2ca5..be90bd0 100644 --- a/coin/settings_base.py +++ b/coin/settings_base.py @@ -172,7 +172,7 @@ INSTALLED_APPS = ( 'compat', 'hijack', 'django_extensions', - + 'crispy_forms', 'coin', 'coin.members', 'coin.offers', @@ -183,6 +183,8 @@ INSTALLED_APPS = ( 'coin.isp_database', ) +CRISPY_TEMPLATE_PACK = 'bootstrap4' + EXTRA_INSTALLED_APPS = tuple() # A sample logging configuration. The only tangible logging diff --git a/coin/static/css/bootstrap.min.css b/coin/static/css/bootstrap.min.css new file mode 100644 index 0000000..92e3fe8 --- /dev/null +++ b/coin/static/css/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:calc(1rem + .4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-sm .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-md .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-lg .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-xl .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #dee2e6;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:0s .6s opacity}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/coin/static/css/foundation.css b/coin/static/css/foundation.css deleted file mode 100644 index 87b694c..0000000 --- a/coin/static/css/foundation.css +++ /dev/null @@ -1,5006 +0,0 @@ -meta.foundation-version { - font-family: "/5.1.0/"; } - -meta.foundation-mq-small { - font-family: "/only screen and (max-width: 40em)/"; - width: 0em; } - -meta.foundation-mq-medium { - font-family: "/only screen and (min-width:40.063em)/"; - width: 40.063em; } - -meta.foundation-mq-large { - font-family: "/only screen and (min-width:64.063em)/"; - width: 64.063em; } - -meta.foundation-mq-xlarge { - font-family: "/only screen and (min-width:90.063em)/"; - width: 90.063em; } - -meta.foundation-mq-xxlarge { - font-family: "/only screen and (min-width:120.063em)/"; - width: 120.063em; } - -meta.foundation-data-attribute-namespace { - font-family: false; } - -html, body { - height: 100%; } - -*, -*:before, -*:after { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } - -html, -body { - font-size: 100%; } - -body { - background: white; - color: #222222; - padding: 0; - margin: 0; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-weight: normal; - font-style: normal; - line-height: 1; - position: relative; - cursor: default; } - -a:hover { - cursor: pointer; } - -img, -object, -embed { - max-width: 100%; - height: auto; } - -object, -embed { - height: 100%; } - -img { - -ms-interpolation-mode: bicubic; } - -#map_canvas img, -#map_canvas embed, -#map_canvas object, -.map_canvas img, -.map_canvas embed, -.map_canvas object { - max-width: none !important; } - -.left { - float: left !important; } - -.right { - float: right !important; } - -.clearfix { - *zoom: 1; } - .clearfix:before, .clearfix:after { - content: " "; - display: table; } - .clearfix:after { - clear: both; } - -.hide { - display: none; } - -.antialiased { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; } - -img { - display: inline-block; - vertical-align: middle; } - -textarea { - height: auto; - min-height: 50px; } - -select { - width: 100%; } - -.row { - width: 100%; - margin-left: auto; - margin-right: auto; - margin-top: 0; - margin-bottom: 0; - max-width: 62.5em; - *zoom: 1; } - .row:before, .row:after { - content: " "; - display: table; } - .row:after { - clear: both; } - .row.collapse > .column, - .row.collapse > .columns { - padding-left: 0; - padding-right: 0; - float: left; } - .row.collapse .row { - margin-left: 0; - margin-right: 0; } - .row .row { - width: auto; - margin-left: -0.9375em; - margin-right: -0.9375em; - margin-top: 0; - margin-bottom: 0; - max-width: none; - *zoom: 1; } - .row .row:before, .row .row:after { - content: " "; - display: table; } - .row .row:after { - clear: both; } - .row .row.collapse { - width: auto; - margin: 0; - max-width: none; - *zoom: 1; } - .row .row.collapse:before, .row .row.collapse:after { - content: " "; - display: table; } - .row .row.collapse:after { - clear: both; } - -.column, -.columns { - padding-left: 0.9375em; - padding-right: 0.9375em; - width: 100%; - float: left; } - -@media only screen { - .column.small-centered, - .columns.small-centered { - margin-left: auto; - margin-right: auto; - float: none; } - - .column.small-uncentered, - .columns.small-uncentered { - margin-left: 0; - margin-right: 0; - float: left; } - - .column.small-uncentered.opposite, - .columns.small-uncentered.opposite { - float: right; } - - .small-push-0 { - left: 0%; - right: auto; } - - .small-pull-0 { - right: 0%; - left: auto; } - - .small-push-1 { - left: 8.33333%; - right: auto; } - - .small-pull-1 { - right: 8.33333%; - left: auto; } - - .small-push-2 { - left: 16.66667%; - right: auto; } - - .small-pull-2 { - right: 16.66667%; - left: auto; } - - .small-push-3 { - left: 25%; - right: auto; } - - .small-pull-3 { - right: 25%; - left: auto; } - - .small-push-4 { - left: 33.33333%; - right: auto; } - - .small-pull-4 { - right: 33.33333%; - left: auto; } - - .small-push-5 { - left: 41.66667%; - right: auto; } - - .small-pull-5 { - right: 41.66667%; - left: auto; } - - .small-push-6 { - left: 50%; - right: auto; } - - .small-pull-6 { - right: 50%; - left: auto; } - - .small-push-7 { - left: 58.33333%; - right: auto; } - - .small-pull-7 { - right: 58.33333%; - left: auto; } - - .small-push-8 { - left: 66.66667%; - right: auto; } - - .small-pull-8 { - right: 66.66667%; - left: auto; } - - .small-push-9 { - left: 75%; - right: auto; } - - .small-pull-9 { - right: 75%; - left: auto; } - - .small-push-10 { - left: 83.33333%; - right: auto; } - - .small-pull-10 { - right: 83.33333%; - left: auto; } - - .small-push-11 { - left: 91.66667%; - right: auto; } - - .small-pull-11 { - right: 91.66667%; - left: auto; } - - .column, - .columns { - position: relative; - padding-left: 0.9375em; - padding-right: 0.9375em; - float: left; } - - .small-1 { - width: 8.33333%; } - - .small-2 { - width: 16.66667%; } - - .small-3 { - width: 25%; } - - .small-4 { - width: 33.33333%; } - - .small-5 { - width: 41.66667%; } - - .small-6 { - width: 50%; } - - .small-7 { - width: 58.33333%; } - - .small-8 { - width: 66.66667%; } - - .small-9 { - width: 75%; } - - .small-10 { - width: 83.33333%; } - - .small-11 { - width: 91.66667%; } - - .small-12 { - width: 100%; } - - [class*="column"] + [class*="column"]:last-child { - float: right; } - - [class*="column"] + [class*="column"].end { - float: left; } - - .small-offset-0 { - margin-left: 0% !important; } - - .small-offset-1 { - margin-left: 8.33333% !important; } - - .small-offset-2 { - margin-left: 16.66667% !important; } - - .small-offset-3 { - margin-left: 25% !important; } - - .small-offset-4 { - margin-left: 33.33333% !important; } - - .small-offset-5 { - margin-left: 41.66667% !important; } - - .small-offset-6 { - margin-left: 50% !important; } - - .small-offset-7 { - margin-left: 58.33333% !important; } - - .small-offset-8 { - margin-left: 66.66667% !important; } - - .small-offset-9 { - margin-left: 75% !important; } - - .small-offset-10 { - margin-left: 83.33333% !important; } - - .small-offset-11 { - margin-left: 91.66667% !important; } - - .small-reset-order, - .small-reset-order { - margin-left: 0; - margin-right: 0; - left: auto; - right: auto; - float: left; } } -@media only screen and (min-width: 40.063em) { - .column.medium-centered, - .columns.medium-centered { - margin-left: auto; - margin-right: auto; - float: none; } - - .column.medium-uncentered, - .columns.medium-uncentered { - margin-left: 0; - margin-right: 0; - float: left; } - - .column.medium-uncentered.opposite, - .columns.medium-uncentered.opposite { - float: right; } - - .medium-push-0 { - left: 0%; - right: auto; } - - .medium-pull-0 { - right: 0%; - left: auto; } - - .medium-push-1 { - left: 8.33333%; - right: auto; } - - .medium-pull-1 { - right: 8.33333%; - left: auto; } - - .medium-push-2 { - left: 16.66667%; - right: auto; } - - .medium-pull-2 { - right: 16.66667%; - left: auto; } - - .medium-push-3 { - left: 25%; - right: auto; } - - .medium-pull-3 { - right: 25%; - left: auto; } - - .medium-push-4 { - left: 33.33333%; - right: auto; } - - .medium-pull-4 { - right: 33.33333%; - left: auto; } - - .medium-push-5 { - left: 41.66667%; - right: auto; } - - .medium-pull-5 { - right: 41.66667%; - left: auto; } - - .medium-push-6 { - left: 50%; - right: auto; } - - .medium-pull-6 { - right: 50%; - left: auto; } - - .medium-push-7 { - left: 58.33333%; - right: auto; } - - .medium-pull-7 { - right: 58.33333%; - left: auto; } - - .medium-push-8 { - left: 66.66667%; - right: auto; } - - .medium-pull-8 { - right: 66.66667%; - left: auto; } - - .medium-push-9 { - left: 75%; - right: auto; } - - .medium-pull-9 { - right: 75%; - left: auto; } - - .medium-push-10 { - left: 83.33333%; - right: auto; } - - .medium-pull-10 { - right: 83.33333%; - left: auto; } - - .medium-push-11 { - left: 91.66667%; - right: auto; } - - .medium-pull-11 { - right: 91.66667%; - left: auto; } - - .column, - .columns { - position: relative; - padding-left: 0.9375em; - padding-right: 0.9375em; - float: left; } - - .medium-1 { - width: 8.33333%; } - - .medium-2 { - width: 16.66667%; } - - .medium-3 { - width: 25%; } - - .medium-4 { - width: 33.33333%; } - - .medium-5 { - width: 41.66667%; } - - .medium-6 { - width: 50%; } - - .medium-7 { - width: 58.33333%; } - - .medium-8 { - width: 66.66667%; } - - .medium-9 { - width: 75%; } - - .medium-10 { - width: 83.33333%; } - - .medium-11 { - width: 91.66667%; } - - .medium-12 { - width: 100%; } - - [class*="column"] + [class*="column"]:last-child { - float: right; } - - [class*="column"] + [class*="column"].end { - float: left; } - - .medium-offset-0 { - margin-left: 0% !important; } - - .medium-offset-1 { - margin-left: 8.33333% !important; } - - .medium-offset-2 { - margin-left: 16.66667% !important; } - - .medium-offset-3 { - margin-left: 25% !important; } - - .medium-offset-4 { - margin-left: 33.33333% !important; } - - .medium-offset-5 { - margin-left: 41.66667% !important; } - - .medium-offset-6 { - margin-left: 50% !important; } - - .medium-offset-7 { - margin-left: 58.33333% !important; } - - .medium-offset-8 { - margin-left: 66.66667% !important; } - - .medium-offset-9 { - margin-left: 75% !important; } - - .medium-offset-10 { - margin-left: 83.33333% !important; } - - .medium-offset-11 { - margin-left: 91.66667% !important; } - - .medium-reset-order, - .medium-reset-order { - margin-left: 0; - margin-right: 0; - left: auto; - right: auto; - float: left; } - - .push-0 { - left: 0%; - right: auto; } - - .pull-0 { - right: 0%; - left: auto; } - - .push-1 { - left: 8.33333%; - right: auto; } - - .pull-1 { - right: 8.33333%; - left: auto; } - - .push-2 { - left: 16.66667%; - right: auto; } - - .pull-2 { - right: 16.66667%; - left: auto; } - - .push-3 { - left: 25%; - right: auto; } - - .pull-3 { - right: 25%; - left: auto; } - - .push-4 { - left: 33.33333%; - right: auto; } - - .pull-4 { - right: 33.33333%; - left: auto; } - - .push-5 { - left: 41.66667%; - right: auto; } - - .pull-5 { - right: 41.66667%; - left: auto; } - - .push-6 { - left: 50%; - right: auto; } - - .pull-6 { - right: 50%; - left: auto; } - - .push-7 { - left: 58.33333%; - right: auto; } - - .pull-7 { - right: 58.33333%; - left: auto; } - - .push-8 { - left: 66.66667%; - right: auto; } - - .pull-8 { - right: 66.66667%; - left: auto; } - - .push-9 { - left: 75%; - right: auto; } - - .pull-9 { - right: 75%; - left: auto; } - - .push-10 { - left: 83.33333%; - right: auto; } - - .pull-10 { - right: 83.33333%; - left: auto; } - - .push-11 { - left: 91.66667%; - right: auto; } - - .pull-11 { - right: 91.66667%; - left: auto; } } -@media only screen and (min-width: 64.063em) { - .column.large-centered, - .columns.large-centered { - margin-left: auto; - margin-right: auto; - float: none; } - - .column.large-uncentered, - .columns.large-uncentered { - margin-left: 0; - margin-right: 0; - float: left; } - - .column.large-uncentered.opposite, - .columns.large-uncentered.opposite { - float: right; } - - .large-push-0 { - left: 0%; - right: auto; } - - .large-pull-0 { - right: 0%; - left: auto; } - - .large-push-1 { - left: 8.33333%; - right: auto; } - - .large-pull-1 { - right: 8.33333%; - left: auto; } - - .large-push-2 { - left: 16.66667%; - right: auto; } - - .large-pull-2 { - right: 16.66667%; - left: auto; } - - .large-push-3 { - left: 25%; - right: auto; } - - .large-pull-3 { - right: 25%; - left: auto; } - - .large-push-4 { - left: 33.33333%; - right: auto; } - - .large-pull-4 { - right: 33.33333%; - left: auto; } - - .large-push-5 { - left: 41.66667%; - right: auto; } - - .large-pull-5 { - right: 41.66667%; - left: auto; } - - .large-push-6 { - left: 50%; - right: auto; } - - .large-pull-6 { - right: 50%; - left: auto; } - - .large-push-7 { - left: 58.33333%; - right: auto; } - - .large-pull-7 { - right: 58.33333%; - left: auto; } - - .large-push-8 { - left: 66.66667%; - right: auto; } - - .large-pull-8 { - right: 66.66667%; - left: auto; } - - .large-push-9 { - left: 75%; - right: auto; } - - .large-pull-9 { - right: 75%; - left: auto; } - - .large-push-10 { - left: 83.33333%; - right: auto; } - - .large-pull-10 { - right: 83.33333%; - left: auto; } - - .large-push-11 { - left: 91.66667%; - right: auto; } - - .large-pull-11 { - right: 91.66667%; - left: auto; } - - .column, - .columns { - position: relative; - padding-left: 0.9375em; - padding-right: 0.9375em; - float: left; } - - .large-1 { - width: 8.33333%; } - - .large-2 { - width: 16.66667%; } - - .large-3 { - width: 25%; } - - .large-4 { - width: 33.33333%; } - - .large-5 { - width: 41.66667%; } - - .large-6 { - width: 50%; } - - .large-7 { - width: 58.33333%; } - - .large-8 { - width: 66.66667%; } - - .large-9 { - width: 75%; } - - .large-10 { - width: 83.33333%; } - - .large-11 { - width: 91.66667%; } - - .large-12 { - width: 100%; } - - [class*="column"] + [class*="column"]:last-child { - float: right; } - - [class*="column"] + [class*="column"].end { - float: left; } - - .large-offset-0 { - margin-left: 0% !important; } - - .large-offset-1 { - margin-left: 8.33333% !important; } - - .large-offset-2 { - margin-left: 16.66667% !important; } - - .large-offset-3 { - margin-left: 25% !important; } - - .large-offset-4 { - margin-left: 33.33333% !important; } - - .large-offset-5 { - margin-left: 41.66667% !important; } - - .large-offset-6 { - margin-left: 50% !important; } - - .large-offset-7 { - margin-left: 58.33333% !important; } - - .large-offset-8 { - margin-left: 66.66667% !important; } - - .large-offset-9 { - margin-left: 75% !important; } - - .large-offset-10 { - margin-left: 83.33333% !important; } - - .large-offset-11 { - margin-left: 91.66667% !important; } - - .large-reset-order, - .large-reset-order { - margin-left: 0; - margin-right: 0; - left: auto; - right: auto; - float: left; } - - .push-0 { - left: 0%; - right: auto; } - - .pull-0 { - right: 0%; - left: auto; } - - .push-1 { - left: 8.33333%; - right: auto; } - - .pull-1 { - right: 8.33333%; - left: auto; } - - .push-2 { - left: 16.66667%; - right: auto; } - - .pull-2 { - right: 16.66667%; - left: auto; } - - .push-3 { - left: 25%; - right: auto; } - - .pull-3 { - right: 25%; - left: auto; } - - .push-4 { - left: 33.33333%; - right: auto; } - - .pull-4 { - right: 33.33333%; - left: auto; } - - .push-5 { - left: 41.66667%; - right: auto; } - - .pull-5 { - right: 41.66667%; - left: auto; } - - .push-6 { - left: 50%; - right: auto; } - - .pull-6 { - right: 50%; - left: auto; } - - .push-7 { - left: 58.33333%; - right: auto; } - - .pull-7 { - right: 58.33333%; - left: auto; } - - .push-8 { - left: 66.66667%; - right: auto; } - - .pull-8 { - right: 66.66667%; - left: auto; } - - .push-9 { - left: 75%; - right: auto; } - - .pull-9 { - right: 75%; - left: auto; } - - .push-10 { - left: 83.33333%; - right: auto; } - - .pull-10 { - right: 83.33333%; - left: auto; } - - .push-11 { - left: 91.66667%; - right: auto; } - - .pull-11 { - right: 91.66667%; - left: auto; } } -meta.foundation-mq-topbar { - font-family: "/only screen and (min-width:40.063em)/"; - width: 58.75em; } - -/* Wrapped around .top-bar to contain to grid width */ -.contain-to-grid { - width: 100%; - background: #333333; } - .contain-to-grid .top-bar { - margin-bottom: 0; } - -.fixed { - width: 100%; - left: 0; - position: fixed; - top: 0; - z-index: 99; } - .fixed.expanded:not(.top-bar) { - overflow-y: auto; - height: auto; - width: 100%; - max-height: 100%; } - .fixed.expanded:not(.top-bar) .title-area { - position: fixed; - width: 100%; - z-index: 99; } - .fixed.expanded:not(.top-bar) .top-bar-section { - z-index: 98; - margin-top: 45px; } - -.top-bar { - overflow: hidden; - height: 45px; - line-height: 45px; - position: relative; - background: #333333; - margin-bottom: 0; } - .top-bar ul { - margin-bottom: 0; - list-style: none; } - .top-bar .row { - max-width: none; } - .top-bar form, - .top-bar input { - margin-bottom: 0; } - .top-bar input { - height: auto; - padding-top: .35rem; - padding-bottom: .35rem; - font-size: 0.75rem; } - .top-bar .button { - padding-top: .45rem; - padding-bottom: .35rem; - margin-bottom: 0; - font-size: 0.75rem; } - .top-bar .title-area { - position: relative; - margin: 0; } - .top-bar .name { - height: 45px; - margin: 0; - font-size: 16px; } - .top-bar .name h1 { - line-height: 45px; - font-size: 1.0625rem; - margin: 0; } - .top-bar .name h1 a { - font-weight: normal; - color: white; - width: 50%; - display: block; - padding: 0 15px; } - .top-bar .toggle-topbar { - position: absolute; - right: 0; - top: 0; } - .top-bar .toggle-topbar a { - color: white; - text-transform: uppercase; - font-size: 0.8125rem; - font-weight: bold; - position: relative; - display: block; - padding: 0 15px; - height: 45px; - line-height: 45px; } - .top-bar .toggle-topbar.menu-icon { - right: 15px; - top: 50%; - margin-top: -16px; - padding-left: 40px; } - .top-bar .toggle-topbar.menu-icon a { - height: 34px; - line-height: 33px; - padding: 0; - padding-right: 25px; - color: white; - position: relative; } - .top-bar .toggle-topbar.menu-icon a::after { - content: ""; - position: absolute; - right: 0; - display: block; - width: 16px; - top: 0; - height: 0; - -webkit-box-shadow: 0 10px 0 1px white, 0 16px 0 1px white, 0 22px 0 1px white; - box-shadow: 0 10px 0 1px white, 0 16px 0 1px white, 0 22px 0 1px white; } - .top-bar.expanded { - height: auto; - background: transparent; } - .top-bar.expanded .title-area { - background: #333333; } - .top-bar.expanded .toggle-topbar a { - color: #888888; } - .top-bar.expanded .toggle-topbar a span { - -webkit-box-shadow: 0 10px 0 1px #888888, 0 16px 0 1px #888888, 0 22px 0 1px #888888; - box-shadow: 0 10px 0 1px #888888, 0 16px 0 1px #888888, 0 22px 0 1px #888888; } - -.top-bar-section { - left: 0; - position: relative; - width: auto; - -webkit-transition: left 300ms ease-out; - -moz-transition: left 300ms ease-out; - transition: left 300ms ease-out; } - .top-bar-section ul { - width: 100%; - height: auto; - display: block; - background: #333333; - font-size: 16px; - margin: 0; } - .top-bar-section .divider, - .top-bar-section [role="separator"] { - border-top: solid 1px #1a1a1a; - clear: both; - height: 1px; - width: 100%; } - .top-bar-section ul li > a { - display: block; - width: 100%; - color: white; - padding: 12px 0 12px 0; - padding-left: 15px; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-size: 0.8125rem; - font-weight: normal; - background: #333333; } - .top-bar-section ul li > a.button { - background: #0086a9; - font-size: 0.8125rem; - padding-right: 15px; - padding-left: 15px; } - .top-bar-section ul li > a.button:hover { - background: #00627b; } - .top-bar-section ul li > a.button.secondary { - background: #ff6600; } - .top-bar-section ul li > a.button.secondary:hover { - background: #e35b00; } - .top-bar-section ul li > a.button.success { - background: #20ba44; } - .top-bar-section ul li > a.button.success:hover { - background: #199336; } - .top-bar-section ul li > a.button.alert { - background: #c60f13; } - .top-bar-section ul li > a.button.alert:hover { - background: #a20c10; } - .top-bar-section ul li:hover > a { - background: #272727; - color: white; } - .top-bar-section ul li.active > a { - background: #0086a9; - color: white; } - .top-bar-section ul li.active > a:hover { - background: #007391; - color: white; } - .top-bar-section .has-form { - padding: 15px; } - .top-bar-section .has-dropdown { - position: relative; } - .top-bar-section .has-dropdown > a:after { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 5px; - border-color: transparent transparent transparent rgba(255, 255, 255, 0.4); - border-left-style: solid; - margin-right: 15px; - margin-top: -4.5px; - position: absolute; - top: 50%; - right: 0; } - .top-bar-section .has-dropdown.moved { - position: static; } - .top-bar-section .has-dropdown.moved > .dropdown { - display: block; } - .top-bar-section .dropdown { - position: absolute; - left: 100%; - top: 0; - display: none; - z-index: 99; } - .top-bar-section .dropdown li { - width: 100%; - height: auto; } - .top-bar-section .dropdown li a { - font-weight: normal; - padding: 8px 15px; } - .top-bar-section .dropdown li a.parent-link { - font-weight: normal; } - .top-bar-section .dropdown li.title h5 { - margin-bottom: 0; } - .top-bar-section .dropdown li.title h5 a { - color: white; - line-height: 22.5px; - display: block; } - .top-bar-section .dropdown li.has-form { - padding: 8px 15px; } - .top-bar-section .dropdown li .button { - top: auto; } - .top-bar-section .dropdown label { - padding: 8px 15px 2px; - margin-bottom: 0; - text-transform: uppercase; - color: #777777; - font-weight: bold; - font-size: 0.625rem; } - -.js-generated { - display: block; } - -@media only screen and (min-width: 40.063em) { - .top-bar { - background: #333333; - *zoom: 1; - overflow: visible; } - .top-bar:before, .top-bar:after { - content: " "; - display: table; } - .top-bar:after { - clear: both; } - .top-bar .toggle-topbar { - display: none; } - .top-bar .title-area { - float: left; } - .top-bar .name h1 a { - width: auto; } - .top-bar input, - .top-bar .button { - font-size: 0.875rem; - position: relative; - top: 7px; } - .top-bar.expanded { - background: #333333; } - - .contain-to-grid .top-bar { - max-width: 62.5em; - margin: 0 auto; - margin-bottom: 0; } - - .top-bar-section { - -webkit-transition: none 0 0; - -moz-transition: none 0 0; - transition: none 0 0; - left: 0 !important; } - .top-bar-section ul { - width: auto; - height: auto !important; - display: inline; } - .top-bar-section ul li { - float: left; } - .top-bar-section ul li .js-generated { - display: none; } - .top-bar-section li.hover > a:not(.button) { - background: #272727; - color: white; } - .top-bar-section li:not(.has-form) a:not(.button) { - padding: 0 15px; - line-height: 45px; - background: #333333; } - .top-bar-section li:not(.has-form) a:not(.button):hover { - background: #272727; } - .top-bar-section li.active:not(.has-form) a:not(.button) { - padding: 0 15px; - line-height: 45px; - color: white; - background: #0086a9; } - .top-bar-section li.active:not(.has-form) a:not(.button):hover { - background: #007391; } - .top-bar-section .has-dropdown > a { - padding-right: 35px !important; } - .top-bar-section .has-dropdown > a:after { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 5px; - border-color: rgba(255, 255, 255, 0.4) transparent transparent transparent; - border-top-style: solid; - margin-top: -2.5px; - top: 22.5px; } - .top-bar-section .has-dropdown.moved { - position: relative; } - .top-bar-section .has-dropdown.moved > .dropdown { - display: none; } - .top-bar-section .has-dropdown.hover > .dropdown, .top-bar-section .has-dropdown.not-click:hover > .dropdown { - display: block; } - .top-bar-section .has-dropdown .dropdown li.has-dropdown > a:after { - border: none; - content: "\00bb"; - top: 1rem; - margin-top: -2px; - right: 5px; - line-height: 1.2; } - .top-bar-section .dropdown { - left: 0; - top: auto; - background: transparent; - min-width: 100%; } - .top-bar-section .dropdown li a { - color: white; - line-height: 1; - white-space: nowrap; - padding: 12px 15px; - background: #333333; } - .top-bar-section .dropdown li label { - white-space: nowrap; - background: #333333; } - .top-bar-section .dropdown li .dropdown { - left: 100%; - top: 0; } - .top-bar-section > ul > .divider, .top-bar-section > ul > [role="separator"] { - border-bottom: none; - border-top: none; - border-right: solid 1px #4e4e4e; - clear: none; - height: 45px; - width: 0; } - .top-bar-section .has-form { - background: #333333; - padding: 0 15px; - height: 45px; } - .top-bar-section .right li .dropdown { - left: auto; - right: 0; } - .top-bar-section .right li .dropdown li .dropdown { - right: 100%; } - .top-bar-section .left li .dropdown { - right: auto; - left: 0; } - .top-bar-section .left li .dropdown li .dropdown { - left: 100%; } - - .no-js .top-bar-section ul li:hover > a { - background: #272727; - color: white; } - .no-js .top-bar-section ul li:active > a { - background: #0086a9; - color: white; } - .no-js .top-bar-section .has-dropdown:hover > .dropdown { - display: block; } } -.breadcrumbs { - display: block; - padding: 0.5625rem 0.875rem 0.5625rem; - overflow: hidden; - margin-left: 0; - list-style: none; - border-style: solid; - border-width: 1px; - background-color: #ffba8c; - border-color: #ffa265; - -webkit-border-radius: 3px; - border-radius: 3px; } - .breadcrumbs > * { - margin: 0; - float: left; - font-size: 0.6875rem; - text-transform: uppercase; } - .breadcrumbs > *:hover a, .breadcrumbs > *:focus a { - text-decoration: underline; } - .breadcrumbs > * a, - .breadcrumbs > * span { - text-transform: uppercase; - color: #0086a9; } - .breadcrumbs > *.current { - cursor: default; - color: #333333; } - .breadcrumbs > *.current a { - cursor: default; - color: #333333; } - .breadcrumbs > *.current:hover, .breadcrumbs > *.current:hover a, .breadcrumbs > *.current:focus, .breadcrumbs > *.current:focus a { - text-decoration: none; } - .breadcrumbs > *.unavailable { - color: #999999; } - .breadcrumbs > *.unavailable a { - color: #999999; } - .breadcrumbs > *.unavailable:hover, .breadcrumbs > *.unavailable:hover a, .breadcrumbs > *.unavailable:focus, - .breadcrumbs > *.unavailable a:focus { - text-decoration: none; - color: #999999; - cursor: default; } - .breadcrumbs > *:before { - content: "/"; - color: #aaaaaa; - margin: 0 0.75rem; - position: relative; - top: 1px; } - .breadcrumbs > *:first-child:before { - content: " "; - margin: 0; } - -.alert-box { - border-style: solid; - border-width: 1px; - display: block; - font-weight: normal; - margin-bottom: 1.25rem; - position: relative; - padding: 0.875rem 1.5rem 0.875rem 0.875rem; - font-size: 0.8125rem; - background-color: #0086a9; - border-color: #007391; - color: white; } - .alert-box .close { - font-size: 1.375rem; - padding: 9px 6px 4px; - line-height: 0; - position: absolute; - top: 50%; - margin-top: -0.6875rem; - right: 0.25rem; - color: #333333; - opacity: 0.3; } - .alert-box .close:hover, .alert-box .close:focus { - opacity: 0.5; } - .alert-box.radius { - -webkit-border-radius: 3px; - border-radius: 3px; } - .alert-box.round { - -webkit-border-radius: 1000px; - border-radius: 1000px; } - .alert-box.success { - background-color: #20ba44; - border-color: #1ca03a; - color: white; } - .alert-box.alert { - background-color: #c60f13; - border-color: #aa0d10; - color: white; } - .alert-box.secondary { - background-color: #ff6600; - border-color: #db5800; - color: white; } - .alert-box.warning { - background-color: #f08a24; - border-color: #de770f; - color: white; } - .alert-box.info { - background-color: #a0d3e8; - border-color: #74bfdd; - color: #572300; } - -.inline-list { - margin: 0 auto 1.0625rem auto; - margin-left: -1.375rem; - margin-right: 0; - padding: 0; - list-style: none; - overflow: hidden; } - .inline-list > li { - list-style: none; - float: left; - margin-left: 1.375rem; - display: block; } - .inline-list > li > * { - display: block; } - -button, .button { - border-style: solid; - border-width: 0px; - cursor: pointer; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-weight: normal; - line-height: normal; - margin: 0 0 1.25rem; - position: relative; - text-decoration: none; - text-align: center; - display: inline-block; - padding-top: 1rem; - padding-right: 2rem; - padding-bottom: 1.0625rem; - padding-left: 2rem; - font-size: 1rem; - /* @else { font-size: $padding - rem-calc(2); } */ - background-color: #0086a9; - border-color: #006b87; - color: white; - -webkit-transition: background-color 300ms ease-out; - -moz-transition: background-color 300ms ease-out; - transition: background-color 300ms ease-out; - padding-top: 1.0625rem; - padding-bottom: 1rem; - -webkit-appearance: none; - border: none; - font-weight: normal !important; } - button:hover, button:focus, .button:hover, .button:focus { - background-color: #006b87; } - button:hover, button:focus, .button:hover, .button:focus { - color: white; } - button.secondary, .button.secondary { - background-color: #ff6600; - border-color: #cc5200; - color: white; } - button.secondary:hover, button.secondary:focus, .button.secondary:hover, .button.secondary:focus { - background-color: #cc5200; } - button.secondary:hover, button.secondary:focus, .button.secondary:hover, .button.secondary:focus { - color: white; } - button.success, .button.success { - background-color: #20ba44; - border-color: #1a9536; - color: white; } - button.success:hover, button.success:focus, .button.success:hover, .button.success:focus { - background-color: #1a9536; } - button.success:hover, button.success:focus, .button.success:hover, .button.success:focus { - color: white; } - button.alert, .button.alert { - background-color: #c60f13; - border-color: #9e0c0f; - color: white; } - button.alert:hover, button.alert:focus, .button.alert:hover, .button.alert:focus { - background-color: #9e0c0f; } - button.alert:hover, button.alert:focus, .button.alert:hover, .button.alert:focus { - color: white; } - button.large, .button.large { - padding-top: 1.125rem; - padding-right: 2.25rem; - padding-bottom: 1.1875rem; - padding-left: 2.25rem; - font-size: 1.25rem; - /* @else { font-size: $padding - rem-calc(2); } */ } - button.small, .button.small { - padding-top: 0.875rem; - padding-right: 1.75rem; - padding-bottom: 0.9375rem; - padding-left: 1.75rem; - font-size: 0.8125rem; - /* @else { font-size: $padding - rem-calc(2); } */ } - button.tiny, .button.tiny { - padding-top: 0.625rem; - padding-right: 1.25rem; - padding-bottom: 0.6875rem; - padding-left: 1.25rem; - font-size: 0.6875rem; - /* @else { font-size: $padding - rem-calc(2); } */ } - button.expand, .button.expand { - padding-right: 0; - padding-left: 0; - width: 100%; } - button.left-align, .button.left-align { - text-align: left; - text-indent: 0.75rem; } - button.right-align, .button.right-align { - text-align: right; - padding-right: 0.75rem; } - button.radius, .button.radius { - -webkit-border-radius: 3px; - border-radius: 3px; } - button.round, .button.round { - -webkit-border-radius: 1000px; - border-radius: 1000px; } - button.disabled, button[disabled], .button.disabled, .button[disabled] { - background-color: #0086a9; - border-color: #006b87; - color: white; - cursor: default; - opacity: 0.7; - -webkit-box-shadow: none; - box-shadow: none; } - button.disabled:hover, button.disabled:focus, button[disabled]:hover, button[disabled]:focus, .button.disabled:hover, .button.disabled:focus, .button[disabled]:hover, .button[disabled]:focus { - background-color: #006b87; } - button.disabled:hover, button.disabled:focus, button[disabled]:hover, button[disabled]:focus, .button.disabled:hover, .button.disabled:focus, .button[disabled]:hover, .button[disabled]:focus { - color: white; } - button.disabled:hover, button.disabled:focus, button[disabled]:hover, button[disabled]:focus, .button.disabled:hover, .button.disabled:focus, .button[disabled]:hover, .button[disabled]:focus { - background-color: #0086a9; } - button.disabled.secondary, button[disabled].secondary, .button.disabled.secondary, .button[disabled].secondary { - background-color: #ff6600; - border-color: #cc5200; - color: white; - cursor: default; - opacity: 0.7; - -webkit-box-shadow: none; - box-shadow: none; } - button.disabled.secondary:hover, button.disabled.secondary:focus, button[disabled].secondary:hover, button[disabled].secondary:focus, .button.disabled.secondary:hover, .button.disabled.secondary:focus, .button[disabled].secondary:hover, .button[disabled].secondary:focus { - background-color: #cc5200; } - button.disabled.secondary:hover, button.disabled.secondary:focus, button[disabled].secondary:hover, button[disabled].secondary:focus, .button.disabled.secondary:hover, .button.disabled.secondary:focus, .button[disabled].secondary:hover, .button[disabled].secondary:focus { - color: white; } - button.disabled.secondary:hover, button.disabled.secondary:focus, button[disabled].secondary:hover, button[disabled].secondary:focus, .button.disabled.secondary:hover, .button.disabled.secondary:focus, .button[disabled].secondary:hover, .button[disabled].secondary:focus { - background-color: #ff6600; } - button.disabled.success, button[disabled].success, .button.disabled.success, .button[disabled].success { - background-color: #20ba44; - border-color: #1a9536; - color: white; - cursor: default; - opacity: 0.7; - -webkit-box-shadow: none; - box-shadow: none; } - button.disabled.success:hover, button.disabled.success:focus, button[disabled].success:hover, button[disabled].success:focus, .button.disabled.success:hover, .button.disabled.success:focus, .button[disabled].success:hover, .button[disabled].success:focus { - background-color: #1a9536; } - button.disabled.success:hover, button.disabled.success:focus, button[disabled].success:hover, button[disabled].success:focus, .button.disabled.success:hover, .button.disabled.success:focus, .button[disabled].success:hover, .button[disabled].success:focus { - color: white; } - button.disabled.success:hover, button.disabled.success:focus, button[disabled].success:hover, button[disabled].success:focus, .button.disabled.success:hover, .button.disabled.success:focus, .button[disabled].success:hover, .button[disabled].success:focus { - background-color: #20ba44; } - button.disabled.alert, button[disabled].alert, .button.disabled.alert, .button[disabled].alert { - background-color: #c60f13; - border-color: #9e0c0f; - color: white; - cursor: default; - opacity: 0.7; - -webkit-box-shadow: none; - box-shadow: none; } - button.disabled.alert:hover, button.disabled.alert:focus, button[disabled].alert:hover, button[disabled].alert:focus, .button.disabled.alert:hover, .button.disabled.alert:focus, .button[disabled].alert:hover, .button[disabled].alert:focus { - background-color: #9e0c0f; } - button.disabled.alert:hover, button.disabled.alert:focus, button[disabled].alert:hover, button[disabled].alert:focus, .button.disabled.alert:hover, .button.disabled.alert:focus, .button[disabled].alert:hover, .button[disabled].alert:focus { - color: white; } - button.disabled.alert:hover, button.disabled.alert:focus, button[disabled].alert:hover, button[disabled].alert:focus, .button.disabled.alert:hover, .button.disabled.alert:focus, .button[disabled].alert:hover, .button[disabled].alert:focus { - background-color: #c60f13; } - -@media only screen and (min-width: 40.063em) { - button, .button { - display: inline-block; } } -.button-group { - list-style: none; - margin: 0; - left: 0; - *zoom: 1; } - .button-group:before, .button-group:after { - content: " "; - display: table; } - .button-group:after { - clear: both; } - .button-group li { - margin: 0; - float: left; } - .button-group li > button, .button-group li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group li:first-child button, .button-group li:first-child .button { - border-left: 0; } - .button-group li:first-child { - margin-left: 0; } - .button-group.radius > * > button, .button-group.radius > * .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.radius > *:first-child button, .button-group.radius > *:first-child .button { - border-left: 0; } - .button-group.radius > *:first-child, .button-group.radius > *:first-child > a, .button-group.radius > *:first-child > button, .button-group.radius > *:first-child > .button { - -moz-border-radius-bottomleft: 3px; - -moz-border-radius-topleft: 3px; - -webkit-border-bottom-left-radius: 3px; - -webkit-border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - border-top-left-radius: 3px; } - .button-group.radius > *:last-child, .button-group.radius > *:last-child > a, .button-group.radius > *:last-child > button, .button-group.radius > *:last-child > .button { - -moz-border-radius-bottomright: 3px; - -moz-border-radius-topright: 3px; - -webkit-border-bottom-right-radius: 3px; - -webkit-border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - border-top-right-radius: 3px; } - .button-group.round > * > button, .button-group.round > * .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.round > *:first-child button, .button-group.round > *:first-child .button { - border-left: 0; } - .button-group.round > *:first-child, .button-group.round > *:first-child > a, .button-group.round > *:first-child > button, .button-group.round > *:first-child > .button { - -moz-border-radius-bottomleft: 1000px; - -moz-border-radius-topleft: 1000px; - -webkit-border-bottom-left-radius: 1000px; - -webkit-border-top-left-radius: 1000px; - border-bottom-left-radius: 1000px; - border-top-left-radius: 1000px; } - .button-group.round > *:last-child, .button-group.round > *:last-child > a, .button-group.round > *:last-child > button, .button-group.round > *:last-child > .button { - -moz-border-radius-bottomright: 1000px; - -moz-border-radius-topright: 1000px; - -webkit-border-bottom-right-radius: 1000px; - -webkit-border-top-right-radius: 1000px; - border-bottom-right-radius: 1000px; - border-top-right-radius: 1000px; } - .button-group.even-2 li { - width: 50%; } - .button-group.even-2 li > button, .button-group.even-2 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-2 li:first-child button, .button-group.even-2 li:first-child .button { - border-left: 0; } - .button-group.even-2 li button, .button-group.even-2 li .button { - width: 100%; } - .button-group.even-3 li { - width: 33.33333%; } - .button-group.even-3 li > button, .button-group.even-3 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-3 li:first-child button, .button-group.even-3 li:first-child .button { - border-left: 0; } - .button-group.even-3 li button, .button-group.even-3 li .button { - width: 100%; } - .button-group.even-4 li { - width: 25%; } - .button-group.even-4 li > button, .button-group.even-4 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-4 li:first-child button, .button-group.even-4 li:first-child .button { - border-left: 0; } - .button-group.even-4 li button, .button-group.even-4 li .button { - width: 100%; } - .button-group.even-5 li { - width: 20%; } - .button-group.even-5 li > button, .button-group.even-5 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-5 li:first-child button, .button-group.even-5 li:first-child .button { - border-left: 0; } - .button-group.even-5 li button, .button-group.even-5 li .button { - width: 100%; } - .button-group.even-6 li { - width: 16.66667%; } - .button-group.even-6 li > button, .button-group.even-6 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-6 li:first-child button, .button-group.even-6 li:first-child .button { - border-left: 0; } - .button-group.even-6 li button, .button-group.even-6 li .button { - width: 100%; } - .button-group.even-7 li { - width: 14.28571%; } - .button-group.even-7 li > button, .button-group.even-7 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-7 li:first-child button, .button-group.even-7 li:first-child .button { - border-left: 0; } - .button-group.even-7 li button, .button-group.even-7 li .button { - width: 100%; } - .button-group.even-8 li { - width: 12.5%; } - .button-group.even-8 li > button, .button-group.even-8 li .button { - border-left: 1px solid; - border-color: rgba(255, 255, 255, 0.5); } - .button-group.even-8 li:first-child button, .button-group.even-8 li:first-child .button { - border-left: 0; } - .button-group.even-8 li button, .button-group.even-8 li .button { - width: 100%; } - -.button-bar { - *zoom: 1; } - .button-bar:before, .button-bar:after { - content: " "; - display: table; } - .button-bar:after { - clear: both; } - .button-bar .button-group { - float: left; - margin-right: 0.625rem; } - .button-bar .button-group div { - overflow: hidden; } - -/* Panels */ -.panel { - border-style: solid; - border-width: 1px; - border-color: #d8d8d8; - margin-bottom: 1.25rem; - padding: 1.25rem; - background: #f2f2f2; } - .panel > :first-child { - margin-top: 0; } - .panel > :last-child { - margin-bottom: 0; } - .panel h1, .panel h2, .panel h3, .panel h4, .panel h5, .panel h6, .panel p { - color: #333333; } - .panel h1, .panel h2, .panel h3, .panel h4, .panel h5, .panel h6 { - line-height: 1; - margin-bottom: 0.625rem; } - .panel h1.subheader, .panel h2.subheader, .panel h3.subheader, .panel h4.subheader, .panel h5.subheader, .panel h6.subheader { - line-height: 1.4; } - .panel.callout { - border-style: solid; - border-width: 1px; - border-color: #b5f0ff; - margin-bottom: 1.25rem; - padding: 1.25rem; - background: #ebfbff; } - .panel.callout > :first-child { - margin-top: 0; } - .panel.callout > :last-child { - margin-bottom: 0; } - .panel.callout h1, .panel.callout h2, .panel.callout h3, .panel.callout h4, .panel.callout h5, .panel.callout h6, .panel.callout p { - color: #333333; } - .panel.callout h1, .panel.callout h2, .panel.callout h3, .panel.callout h4, .panel.callout h5, .panel.callout h6 { - line-height: 1; - margin-bottom: 0.625rem; } - .panel.callout h1.subheader, .panel.callout h2.subheader, .panel.callout h3.subheader, .panel.callout h4.subheader, .panel.callout h5.subheader, .panel.callout h6.subheader { - line-height: 1.4; } - .panel.callout a { - color: #0086a9; } - .panel.radius { - -webkit-border-radius: 3px; - border-radius: 3px; } - -.dropdown.button, button.dropdown { - position: relative; - padding-right: 3.5625rem; } - .dropdown.button:before, button.dropdown:before { - position: absolute; - content: ""; - width: 0; - height: 0; - display: block; - border-style: solid; - border-color: white transparent transparent transparent; - top: 50%; } - .dropdown.button:before, button.dropdown:before { - border-width: 0.375rem; - right: 1.40625rem; - margin-top: -0.15625rem; } - .dropdown.button:before, button.dropdown:before { - border-color: white transparent transparent transparent; } - .dropdown.button.tiny, button.dropdown.tiny { - padding-right: 2.625rem; } - .dropdown.button.tiny:before, button.dropdown.tiny:before { - border-width: 0.375rem; - right: 1.125rem; - margin-top: -0.125rem; } - .dropdown.button.tiny:before, button.dropdown.tiny:before { - border-color: white transparent transparent transparent; } - .dropdown.button.small, button.dropdown.small { - padding-right: 3.0625rem; } - .dropdown.button.small:before, button.dropdown.small:before { - border-width: 0.4375rem; - right: 1.3125rem; - margin-top: -0.15625rem; } - .dropdown.button.small:before, button.dropdown.small:before { - border-color: white transparent transparent transparent; } - .dropdown.button.large, button.dropdown.large { - padding-right: 3.625rem; } - .dropdown.button.large:before, button.dropdown.large:before { - border-width: 0.3125rem; - right: 1.71875rem; - margin-top: -0.15625rem; } - .dropdown.button.large:before, button.dropdown.large:before { - border-color: white transparent transparent transparent; } - .dropdown.button.secondary:before, button.dropdown.secondary:before { - border-color: #333333 transparent transparent transparent; } - -/* Image Thumbnails */ -.th { - line-height: 0; - display: inline-block; - border: solid 4px white; - max-width: 100%; - -webkit-box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); - -webkit-transition: all 200ms ease-out; - -moz-transition: all 200ms ease-out; - transition: all 200ms ease-out; } - .th:hover, .th:focus { - -webkit-box-shadow: 0 0 6px 1px rgba(0, 134, 169, 0.5); - box-shadow: 0 0 6px 1px rgba(0, 134, 169, 0.5); } - .th.radius { - -webkit-border-radius: 3px; - border-radius: 3px; } - -/* Pricing Tables */ -.pricing-table { - border: solid 1px #dddddd; - margin-left: 0; - margin-bottom: 1.25rem; } - .pricing-table * { - list-style: none; - line-height: 1; } - .pricing-table .title { - background-color: #333333; - padding: 0.9375rem 1.25rem; - text-align: center; - color: #eeeeee; - font-weight: normal; - font-size: 1rem; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } - .pricing-table .price { - background-color: #f6f6f6; - padding: 0.9375rem 1.25rem; - text-align: center; - color: #333333; - font-weight: normal; - font-size: 2rem; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } - .pricing-table .description { - background-color: white; - padding: 0.9375rem; - text-align: center; - color: #777777; - font-size: 0.75rem; - font-weight: normal; - line-height: 1.4; - border-bottom: dotted 1px #dddddd; } - .pricing-table .bullet-item { - background-color: white; - padding: 0.9375rem; - text-align: center; - color: #333333; - font-size: 0.875rem; - font-weight: normal; - border-bottom: dotted 1px #dddddd; } - .pricing-table .cta-button { - background-color: white; - text-align: center; - padding: 1.25rem 1.25rem 0; } - -@-webkit-keyframes rotate { - from { - -webkit-transform: rotate(0deg); } - - to { - -webkit-transform: rotate(360deg); } } - -@-moz-keyframes rotate { - from { - -moz-transform: rotate(0deg); } - - to { - -moz-transform: rotate(360deg); } } - -@-o-keyframes rotate { - from { - -o-transform: rotate(0deg); } - - to { - -o-transform: rotate(360deg); } } - -@keyframes rotate { - from { - transform: rotate(0deg); } - - to { - transform: rotate(360deg); } } - -/* Orbit Graceful Loading */ -.slideshow-wrapper { - position: relative; } - .slideshow-wrapper ul { - list-style-type: none; - margin: 0; } - .slideshow-wrapper ul li, - .slideshow-wrapper ul li .orbit-caption { - display: none; } - .slideshow-wrapper ul li:first-child { - display: block; } - .slideshow-wrapper .orbit-container { - background-color: transparent; } - .slideshow-wrapper .orbit-container li { - display: block; } - .slideshow-wrapper .orbit-container li .orbit-caption { - display: block; } - -.preloader { - display: block; - width: 40px; - height: 40px; - position: absolute; - top: 50%; - left: 50%; - margin-top: -20px; - margin-left: -20px; - border: solid 3px; - border-color: #555555 white; - -webkit-border-radius: 1000px; - border-radius: 1000px; - -webkit-animation-name: rotate; - -webkit-animation-duration: 1.5s; - -webkit-animation-iteration-count: infinite; - -webkit-animation-timing-function: linear; - -moz-animation-name: rotate; - -moz-animation-duration: 1.5s; - -moz-animation-iteration-count: infinite; - -moz-animation-timing-function: linear; - -o-animation-name: rotate; - -o-animation-duration: 1.5s; - -o-animation-iteration-count: infinite; - -o-animation-timing-function: linear; - animation-name: rotate; - animation-duration: 1.5s; - animation-iteration-count: infinite; - animation-timing-function: linear; } - -.orbit-container { - overflow: hidden; - width: 100%; - position: relative; - background: none; } - .orbit-container .orbit-slides-container { - list-style: none; - margin: 0; - padding: 0; - position: relative; - -webkit-transform: translateZ(0); } - .orbit-container .orbit-slides-container img { - display: block; - max-width: 100%; } - .orbit-container .orbit-slides-container > * { - position: absolute; - top: 0; - width: 100%; - margin-left: 100%; } - .orbit-container .orbit-slides-container > *:first-child { - margin-left: 0%; } - .orbit-container .orbit-slides-container > * .orbit-caption { - position: absolute; - bottom: 0; - background-color: rgba(51, 51, 51, 0.8); - color: white; - width: 100%; - padding: 0.625rem 0.875rem; - font-size: 0.875rem; } - .orbit-container .orbit-slide-number { - position: absolute; - top: 10px; - left: 10px; - font-size: 12px; - color: white; - background: rgba(0, 0, 0, 0); - z-index: 10; } - .orbit-container .orbit-slide-number span { - font-weight: 700; - padding: 0.3125rem; } - .orbit-container .orbit-timer { - position: absolute; - top: 12px; - right: 10px; - height: 6px; - width: 100px; - z-index: 10; } - .orbit-container .orbit-timer .orbit-progress { - height: 3px; - background-color: rgba(255, 255, 255, 0.3); - display: block; - width: 0%; - position: relative; - right: 20px; - top: 5px; } - .orbit-container .orbit-timer > span { - display: none; - position: absolute; - top: 0px; - right: 0; - width: 11px; - height: 14px; - border: solid 4px white; - border-top: none; - border-bottom: none; } - .orbit-container .orbit-timer.paused > span { - right: -4px; - top: 0px; - width: 11px; - height: 14px; - border: inset 8px; - border-right-style: solid; - border-color: transparent transparent transparent white; } - .orbit-container .orbit-timer.paused > span.dark { - border-color: transparent transparent transparent #333333; } - .orbit-container:hover .orbit-timer > span { - display: block; } - .orbit-container .orbit-prev, - .orbit-container .orbit-next { - position: absolute; - top: 45%; - margin-top: -25px; - width: 36px; - height: 60px; - line-height: 50px; - color: white; - background-color: none; - text-indent: -9999px !important; - z-index: 10; } - .orbit-container .orbit-prev:hover, - .orbit-container .orbit-next:hover { - background-color: rgba(0, 0, 0, 0.3); } - .orbit-container .orbit-prev > span, - .orbit-container .orbit-next > span { - position: absolute; - top: 50%; - margin-top: -10px; - display: block; - width: 0; - height: 0; - border: inset 10px; } - .orbit-container .orbit-prev { - left: 0; } - .orbit-container .orbit-prev > span { - border-right-style: solid; - border-color: transparent; - border-right-color: white; } - .orbit-container .orbit-prev:hover > span { - border-right-color: white; } - .orbit-container .orbit-next { - right: 0; } - .orbit-container .orbit-next > span { - border-color: transparent; - border-left-style: solid; - border-left-color: white; - left: 50%; - margin-left: -4px; } - .orbit-container .orbit-next:hover > span { - border-left-color: white; } - -.orbit-bullets-container { - text-align: center; } - -.orbit-bullets { - margin: 0 auto 30px auto; - overflow: hidden; - position: relative; - top: 10px; - float: none; - text-align: center; - display: block; } - .orbit-bullets li { - display: inline-block; - width: 0.5625rem; - height: 0.5625rem; - background: #cccccc; - float: none; - margin-right: 6px; - -webkit-border-radius: 1000px; - border-radius: 1000px; } - .orbit-bullets li.active { - background: #999999; } - .orbit-bullets li:last-child { - margin-right: 0; } - -.touch .orbit-container .orbit-prev, -.touch .orbit-container .orbit-next { - display: none; } -.touch .orbit-bullets { - display: none; } - -@media only screen and (min-width: 40.063em) { - .touch .orbit-container .orbit-prev, - .touch .orbit-container .orbit-next { - display: inherit; } - .touch .orbit-bullets { - display: block; } } -@media only screen and (max-width: 40em) { - .orbit-stack-on-small .orbit-slides-container { - height: auto !important; } - .orbit-stack-on-small .orbit-slides-container > * { - position: relative; - margin-left: 0% !important; } - .orbit-stack-on-small .orbit-timer, - .orbit-stack-on-small .orbit-next, - .orbit-stack-on-small .orbit-prev, - .orbit-stack-on-small .orbit-bullets { - display: none; } } -[data-magellan-expedition] { - background: white; - z-index: 50; - min-width: 100%; - padding: 10px; } - [data-magellan-expedition] .sub-nav { - margin-bottom: 0; } - [data-magellan-expedition] .sub-nav dd { - margin-bottom: 0; } - [data-magellan-expedition] .sub-nav a { - line-height: 1.8em; } - -.tabs { - *zoom: 1; - margin-bottom: 0 !important; } - .tabs:before, .tabs:after { - content: " "; - display: table; } - .tabs:after { - clear: both; } - .tabs dd { - position: relative; - margin-bottom: 0 !important; - top: 1px; - float: left; } - .tabs dd > a { - display: block; - background: #efefef; - color: #222222; - padding-top: 1rem; - padding-right: 2rem; - padding-bottom: 1.0625rem; - padding-left: 2rem; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-size: 1rem; } - .tabs dd > a:hover { - background: #e1e1e1; } - .tabs dd.active a { - background: white; } - .tabs.radius dd:first-child a { - -moz-border-radius-bottomleft: 3px; - -moz-border-radius-topleft: 3px; - -webkit-border-bottom-left-radius: 3px; - -webkit-border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - border-top-left-radius: 3px; } - .tabs.radius dd:last-child a { - -moz-border-radius-bottomright: 3px; - -moz-border-radius-topright: 3px; - -webkit-border-bottom-right-radius: 3px; - -webkit-border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - border-top-right-radius: 3px; } - .tabs.vertical dd { - position: inherit; - float: none; - display: block; - top: auto; } - -.tabs-content { - *zoom: 1; - margin-bottom: 1.5rem; - width: 100%; } - .tabs-content:before, .tabs-content:after { - content: " "; - display: table; } - .tabs-content:after { - clear: both; } - .tabs-content > .content { - display: none; - float: left; - padding: 0.9375em 0; - width: 100%; } - .tabs-content > .content.active { - display: block; } - .tabs-content > .content.contained { - padding: 0.9375em; } - .tabs-content.vertical { - display: block; } - .tabs-content.vertical > .content { - padding: 0 0.9375em; } - -@media only screen and (min-width: 40.063em) { - .tabs.vertical { - width: 20%; - float: left; - margin-bottom: 1.25rem; } - - .tabs-content.vertical { - width: 80%; - float: left; - margin-left: -1px; } } -.side-nav { - display: block; - margin: 0; - padding: 0.875rem 0; - list-style-type: none; - list-style-position: inside; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } - .side-nav li { - margin: 0 0 0.4375rem 0; - font-size: 0.875rem; } - .side-nav li a:not(.button) { - display: block; - color: #0086a9; } - .side-nav li a:not(.button):hover, .side-nav li a:not(.button):focus { - color: #10ceff; } - .side-nav li.active > a:first-child:not(.button) { - color: #10ceff; - font-weight: normal; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } - .side-nav li.divider { - border-top: 1px solid; - height: 0; - padding: 0; - list-style: none; - border-top-color: white; } - -.accordion { - *zoom: 1; - margin-bottom: 0; } - .accordion:before, .accordion:after { - content: " "; - display: table; } - .accordion:after { - clear: both; } - .accordion dd { - display: block; - margin-bottom: 0 !important; } - .accordion dd.active a { - background: #e8e8e8; } - .accordion dd > a { - background: #efefef; - color: #222222; - padding: 1rem; - display: block; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-size: 1rem; } - .accordion dd > a:hover { - background: #e3e3e3; } - .accordion .content { - display: none; - padding: 0.9375em; } - .accordion .content.active { - display: block; - background: white; } - -.text-left { - text-align: left !important; } - -.text-right { - text-align: right !important; } - -.text-center { - text-align: center !important; } - -.text-justify { - text-align: justify !important; } - -@media only screen and (max-width: 40em) { - .small-only-text-left { - text-align: left !important; } - - .small-only-text-right { - text-align: right !important; } - - .small-only-text-center { - text-align: center !important; } - - .small-only-text-justify { - text-align: justify !important; } } -@media only screen { - .small-text-left { - text-align: left !important; } - - .small-text-right { - text-align: right !important; } - - .small-text-center { - text-align: center !important; } - - .small-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 40.063em) and (max-width: 64em) { - .medium-only-text-left { - text-align: left !important; } - - .medium-only-text-right { - text-align: right !important; } - - .medium-only-text-center { - text-align: center !important; } - - .medium-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 40.063em) { - .medium-text-left { - text-align: left !important; } - - .medium-text-right { - text-align: right !important; } - - .medium-text-center { - text-align: center !important; } - - .medium-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 64.063em) and (max-width: 90em) { - .large-only-text-left { - text-align: left !important; } - - .large-only-text-right { - text-align: right !important; } - - .large-only-text-center { - text-align: center !important; } - - .large-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 64.063em) { - .large-text-left { - text-align: left !important; } - - .large-text-right { - text-align: right !important; } - - .large-text-center { - text-align: center !important; } - - .large-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 90.063em) and (max-width: 120em) { - .xlarge-only-text-left { - text-align: left !important; } - - .xlarge-only-text-right { - text-align: right !important; } - - .xlarge-only-text-center { - text-align: center !important; } - - .xlarge-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 90.063em) { - .xlarge-text-left { - text-align: left !important; } - - .xlarge-text-right { - text-align: right !important; } - - .xlarge-text-center { - text-align: center !important; } - - .xlarge-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 120.063em) and (max-width: 99999999em) { - .xxlarge-only-text-left { - text-align: left !important; } - - .xxlarge-only-text-right { - text-align: right !important; } - - .xxlarge-only-text-center { - text-align: center !important; } - - .xxlarge-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 120.063em) { - .xxlarge-text-left { - text-align: left !important; } - - .xxlarge-text-right { - text-align: right !important; } - - .xxlarge-text-center { - text-align: center !important; } - - .xxlarge-text-justify { - text-align: justify !important; } } -/* Typography resets */ -div, -dl, -dt, -dd, -ul, -ol, -li, -h1, -h2, -h3, -h4, -h5, -h6, -pre, -form, -p, -blockquote, -th, -td { - margin: 0; - padding: 0; } - -/* Default Link Styles */ -a { - color: #0086a9; - text-decoration: none; - line-height: inherit; } - a:hover, a:focus { - color: #007391; } - a img { - border: none; } - -/* Default paragraph styles */ -p { - font-family: inherit; - font-weight: normal; - font-size: 1rem; - line-height: 1.6; - margin-bottom: 1.25rem; - text-rendering: optimizeLegibility; } - p.lead { - font-size: 1.21875rem; - line-height: 1.6; } - p aside { - font-size: 0.875rem; - line-height: 1.35; - font-style: italic; } - -/* Default header styles */ -h1, h2, h3, h4, h5, h6 { - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-weight: normal; - font-style: normal; - color: #222222; - text-rendering: optimizeLegibility; - margin-top: 0.2rem; - margin-bottom: 0.5rem; - line-height: 1.4; } - h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { - font-size: 60%; - color: #6f6f6f; - line-height: 0; } - -h1 { - font-size: 2.125rem; } - -h2 { - font-size: 1.6875rem; } - -h3 { - font-size: 1.375rem; } - -h4 { - font-size: 1.125rem; } - -h5 { - font-size: 1.125rem; } - -h6 { - font-size: 1rem; } - -.subheader { - line-height: 1.4; - color: #6f6f6f; - font-weight: normal; - margin-top: 0.2rem; - margin-bottom: 0.5rem; } - -hr { - border: solid #dddddd; - border-width: 1px 0 0; - clear: both; - margin: 1.25rem 0 1.1875rem; - height: 0; } - -/* Helpful Typography Defaults */ -em, -i { - font-style: italic; - line-height: inherit; } - -strong, -b { - font-weight: bold; - line-height: inherit; } - -small { - font-size: 60%; - line-height: inherit; } - -code { - font-family: Consolas, "Liberation Mono", Courier, monospace; - font-weight: bold; - color: #910b0e; } - -/* Lists */ -ul, -ol, -dl { - font-size: 1rem; - line-height: 1.6; - margin-bottom: 1.25rem; - list-style-position: outside; - font-family: inherit; } - -ul { - margin-left: 1.1rem; } - ul.no-bullet { - margin-left: 0; } - ul.no-bullet li ul, - ul.no-bullet li ol { - margin-left: 1.25rem; - margin-bottom: 0; - list-style: none; } - -/* Unordered Lists */ -ul li ul, -ul li ol { - margin-left: 1.25rem; - margin-bottom: 0; } -ul.square li ul, ul.circle li ul, ul.disc li ul { - list-style: inherit; } -ul.square { - list-style-type: square; - margin-left: 1.1rem; } -ul.circle { - list-style-type: circle; - margin-left: 1.1rem; } -ul.disc { - list-style-type: disc; - margin-left: 1.1rem; } -ul.no-bullet { - list-style: none; } - -/* Ordered Lists */ -ol { - margin-left: 1.4rem; } - ol li ul, - ol li ol { - margin-left: 1.25rem; - margin-bottom: 0; } - -/* Definition Lists */ -dl dt { - margin-bottom: 0.3rem; - font-weight: bold; } -dl dd { - margin-bottom: 0.75rem; } - -/* Abbreviations */ -abbr, -acronym { - text-transform: uppercase; - font-size: 90%; - color: #222222; - border-bottom: 1px dotted #dddddd; - cursor: help; } - -abbr { - text-transform: none; } - -/* Blockquotes */ -blockquote { - margin: 0 0 1.25rem; - padding: 0.5625rem 1.25rem 0 1.1875rem; - border-left: 1px solid #dddddd; } - blockquote cite { - display: block; - font-size: 0.8125rem; - color: #555555; } - blockquote cite:before { - content: "\2014 \0020"; } - blockquote cite a, - blockquote cite a:visited { - color: #555555; } - -blockquote, -blockquote p { - line-height: 1.6; - color: #6f6f6f; } - -/* Microformats */ -.vcard { - display: inline-block; - margin: 0 0 1.25rem 0; - border: 1px solid #dddddd; - padding: 0.625rem 0.75rem; } - .vcard li { - margin: 0; - display: block; } - .vcard .fn { - font-weight: bold; - font-size: 0.9375rem; } - -.vevent .summary { - font-weight: bold; } -.vevent abbr { - cursor: default; - text-decoration: none; - font-weight: bold; - border: none; - padding: 0 0.0625rem; } - -@media only screen and (min-width: 40.063em) { - h1, h2, h3, h4, h5, h6 { - line-height: 1.4; } - - h1 { - font-size: 2.75rem; } - - h2 { - font-size: 2.3125rem; } - - h3 { - font-size: 1.6875rem; } - - h4 { - font-size: 1.4375rem; } } -/* - * Print styles. - * - * Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ - * Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com) -*/ -.print-only { - display: none !important; } - -@media print { - * { - background: transparent !important; - color: black !important; - /* Black prints faster: h5bp.com/s */ - box-shadow: none !important; - text-shadow: none !important; } - - a, - a:visited { - text-decoration: underline; } - - a[href]:after { - content: " (" attr(href) ")"; } - - abbr[title]:after { - content: " (" attr(title) ")"; } - - .ir a:after, - a[href^="javascript:"]:after, - a[href^="#"]:after { - content: ""; } - - pre, - blockquote { - border: 1px solid #999999; - page-break-inside: avoid; } - - thead { - display: table-header-group; - /* h5bp.com/t */ } - - tr, - img { - page-break-inside: avoid; } - - img { - max-width: 100% !important; } - - @page { - margin: 0.5cm; } - - p, - h2, - h3 { - orphans: 3; - widows: 3; } - - h2, - h3 { - page-break-after: avoid; } - - .hide-on-print { - display: none !important; } - - .print-only { - display: block !important; } - - .hide-for-print { - display: none !important; } - - .show-for-print { - display: inherit !important; } } -.split.button { - position: relative; - padding-right: 5.0625rem; } - .split.button span { - display: block; - height: 100%; - position: absolute; - right: 0; - top: 0; - border-left: solid 1px; } - .split.button span:before { - position: absolute; - content: ""; - width: 0; - height: 0; - display: block; - border-style: inset; - top: 50%; - left: 50%; } - .split.button span:active { - background-color: rgba(0, 0, 0, 0.1); } - .split.button span { - border-left-color: rgba(255, 255, 255, 0.5); } - .split.button span { - width: 3.09375rem; } - .split.button span:before { - border-top-style: solid; - border-width: 0.375rem; - top: 48%; - margin-left: -0.375rem; } - .split.button span:before { - border-color: white transparent transparent transparent; } - .split.button.secondary span { - border-left-color: rgba(255, 255, 255, 0.5); } - .split.button.secondary span:before { - border-color: white transparent transparent transparent; } - .split.button.alert span { - border-left-color: rgba(255, 255, 255, 0.5); } - .split.button.success span { - border-left-color: rgba(255, 255, 255, 0.5); } - .split.button.tiny { - padding-right: 3.75rem; } - .split.button.tiny span { - width: 2.25rem; } - .split.button.tiny span:before { - border-top-style: solid; - border-width: 0.375rem; - top: 48%; - margin-left: -0.375rem; } - .split.button.small { - padding-right: 4.375rem; } - .split.button.small span { - width: 2.625rem; } - .split.button.small span:before { - border-top-style: solid; - border-width: 0.4375rem; - top: 48%; - margin-left: -0.375rem; } - .split.button.large { - padding-right: 5.5rem; } - .split.button.large span { - width: 3.4375rem; } - .split.button.large span:before { - border-top-style: solid; - border-width: 0.3125rem; - top: 48%; - margin-left: -0.375rem; } - .split.button.expand { - padding-left: 2rem; } - .split.button.secondary span:before { - border-color: #333333 transparent transparent transparent; } - .split.button.radius span { - -moz-border-radius-bottomright: 3px; - -moz-border-radius-topright: 3px; - -webkit-border-bottom-right-radius: 3px; - -webkit-border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - border-top-right-radius: 3px; } - .split.button.round span { - -moz-border-radius-bottomright: 1000px; - -moz-border-radius-topright: 1000px; - -webkit-border-bottom-right-radius: 1000px; - -webkit-border-top-right-radius: 1000px; - border-bottom-right-radius: 1000px; - border-top-right-radius: 1000px; } - -.reveal-modal-bg { - position: fixed; - height: 100%; - width: 100%; - background: black; - background: rgba(0, 0, 0, 0.45); - z-index: 98; - display: none; - top: 0; - left: 0; } - -dialog, .reveal-modal { - visibility: hidden; - display: none; - position: absolute; - left: 50%; - z-index: 99; - height: auto; - margin-left: -40%; - width: 80%; - background-color: white; - padding: 1.25rem; - border: solid 1px #666666; - -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); - box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); - top: 6.25rem; } - dialog .column, - dialog .columns, .reveal-modal .column, - .reveal-modal .columns { - min-width: 0; } - dialog > :first-child, .reveal-modal > :first-child { - margin-top: 0; } - dialog > :last-child, .reveal-modal > :last-child { - margin-bottom: 0; } - dialog .close-reveal-modal, .reveal-modal .close-reveal-modal { - font-size: 1.375rem; - line-height: 1; - position: absolute; - top: 0.5rem; - right: 0.6875rem; - color: #aaaaaa; - font-weight: bold; - cursor: pointer; } - -dialog[open] { - display: block; - visibility: visible; } - -@media only screen and (min-width: 40.063em) { - dialog, .reveal-modal { - padding: 1.875rem; - top: 6.25rem; } - dialog.tiny, .reveal-modal.tiny { - margin-left: -15%; - width: 30%; } - dialog.small, .reveal-modal.small { - margin-left: -20%; - width: 40%; } - dialog.medium, .reveal-modal.medium { - margin-left: -30%; - width: 60%; } - dialog.large, .reveal-modal.large { - margin-left: -35%; - width: 70%; } - dialog.xlarge, .reveal-modal.xlarge { - margin-left: -47.5%; - width: 95%; } } -@media print { - dialog, .reveal-modal { - background: white !important; } } -/* Tooltips */ -.has-tip { - border-bottom: dotted 1px #cccccc; - cursor: help; - font-weight: bold; - color: #333333; } - .has-tip:hover, .has-tip:focus { - border-bottom: dotted 1px #003c4c; - color: #0086a9; } - .has-tip.tip-left, .has-tip.tip-right { - float: none !important; } - -.tooltip { - display: none; - position: absolute; - z-index: 999; - font-weight: normal; - font-size: 0.875rem; - line-height: 1.3; - padding: 0.75rem; - max-width: 85%; - left: 50%; - width: 100%; - color: white; - background: #333333; } - .tooltip > .nub { - display: block; - left: 5px; - position: absolute; - width: 0; - height: 0; - border: solid 5px; - border-color: transparent transparent #333333 transparent; - top: -10px; } - .tooltip.radius { - -webkit-border-radius: 3px; - border-radius: 3px; } - .tooltip.round { - -webkit-border-radius: 1000px; - border-radius: 1000px; } - .tooltip.round > .nub { - left: 2rem; } - .tooltip.opened { - color: #0086a9 !important; - border-bottom: dotted 1px #003c4c !important; } - -.tap-to-close { - display: block; - font-size: 0.625rem; - color: #777777; - font-weight: normal; } - -@media only screen and (min-width: 40.063em) { - .tooltip > .nub { - border-color: transparent transparent #333333 transparent; - top: -10px; } - .tooltip.tip-top > .nub { - border-color: #333333 transparent transparent transparent; - top: auto; - bottom: -10px; } - .tooltip.tip-left, .tooltip.tip-right { - float: none !important; } - .tooltip.tip-left > .nub { - border-color: transparent transparent transparent #333333; - right: -10px; - left: auto; - top: 50%; - margin-top: -5px; } - .tooltip.tip-right > .nub { - border-color: transparent #333333 transparent transparent; - right: auto; - left: -10px; - top: 50%; - margin-top: -5px; } } -/* Clearing Styles */ -.clearing-thumbs, [data-clearing] { - *zoom: 1; - margin-bottom: 0; - margin-left: 0; - list-style: none; } - .clearing-thumbs:before, .clearing-thumbs:after, [data-clearing]:before, [data-clearing]:after { - content: " "; - display: table; } - .clearing-thumbs:after, [data-clearing]:after { - clear: both; } - .clearing-thumbs li, [data-clearing] li { - float: left; - margin-right: 10px; } - .clearing-thumbs[class*="block-grid-"] li, [data-clearing][class*="block-grid-"] li { - margin-right: 0; } - -.clearing-blackout { - background: #333333; - position: fixed; - width: 100%; - height: 100%; - top: 0; - left: 0; - z-index: 998; } - .clearing-blackout .clearing-close { - display: block; } - -.clearing-container { - position: relative; - z-index: 998; - height: 100%; - overflow: hidden; - margin: 0; } - -.clearing-touch-label { - position: absolute; - top: 50%; - left: 50%; - color: #aaa; - font-size: 0.6em; } - -.visible-img { - height: 95%; - position: relative; } - .visible-img img { - position: absolute; - left: 50%; - top: 50%; - margin-left: -50%; - max-height: 100%; - max-width: 100%; } - -.clearing-caption { - color: #cccccc; - font-size: 0.875em; - line-height: 1.3; - margin-bottom: 0; - text-align: center; - bottom: 0; - background: #333333; - width: 100%; - padding: 10px 30px 20px; - position: absolute; - left: 0; } - -.clearing-close { - z-index: 999; - padding-left: 20px; - padding-top: 10px; - font-size: 30px; - line-height: 1; - color: #cccccc; - display: none; } - .clearing-close:hover, .clearing-close:focus { - color: #ccc; } - -.clearing-assembled .clearing-container { - height: 100%; } - .clearing-assembled .clearing-container .carousel > ul { - display: none; } - -.clearing-feature li { - display: none; } - .clearing-feature li.clearing-featured-img { - display: block; } - -@media only screen and (min-width: 40.063em) { - .clearing-main-prev, - .clearing-main-next { - position: absolute; - height: 100%; - width: 40px; - top: 0; } - .clearing-main-prev > span, - .clearing-main-next > span { - position: absolute; - top: 50%; - display: block; - width: 0; - height: 0; - border: solid 12px; } - .clearing-main-prev > span:hover, - .clearing-main-next > span:hover { - opacity: 0.8; } - - .clearing-main-prev { - left: 0; } - .clearing-main-prev > span { - left: 5px; - border-color: transparent; - border-right-color: #cccccc; } - - .clearing-main-next { - right: 0; } - .clearing-main-next > span { - border-color: transparent; - border-left-color: #cccccc; } - - .clearing-main-prev.disabled, - .clearing-main-next.disabled { - opacity: 0.3; } - - .clearing-assembled .clearing-container .carousel { - background: rgba(51, 51, 51, 0.8); - height: 120px; - margin-top: 10px; - text-align: center; } - .clearing-assembled .clearing-container .carousel > ul { - display: inline-block; - z-index: 999; - height: 100%; - position: relative; - float: none; } - .clearing-assembled .clearing-container .carousel > ul li { - display: block; - width: 120px; - min-height: inherit; - float: left; - overflow: hidden; - margin-right: 0; - padding: 0; - position: relative; - cursor: pointer; - opacity: 0.4; } - .clearing-assembled .clearing-container .carousel > ul li.fix-height img { - height: 100%; - max-width: none; } - .clearing-assembled .clearing-container .carousel > ul li a.th { - border: none; - -webkit-box-shadow: none; - box-shadow: none; - display: block; } - .clearing-assembled .clearing-container .carousel > ul li img { - cursor: pointer !important; - width: 100% !important; } - .clearing-assembled .clearing-container .carousel > ul li.visible { - opacity: 1; } - .clearing-assembled .clearing-container .carousel > ul li:hover { - opacity: 0.8; } - .clearing-assembled .clearing-container .visible-img { - background: #333333; - overflow: hidden; - height: 85%; } - - .clearing-close { - position: absolute; - top: 10px; - right: 20px; - padding-left: 0; - padding-top: 0; } } -/* Progress Bar */ -.progress { - background-color: #f6f6f6; - height: 1.5625rem; - border: 1px solid white; - padding: 0.125rem; - margin-bottom: 0.625rem; } - .progress .meter { - background: #0086a9; - height: 100%; - display: block; } - .progress.secondary .meter { - background: #ff6600; - height: 100%; - display: block; } - .progress.success .meter { - background: #20ba44; - height: 100%; - display: block; } - .progress.alert .meter { - background: #c60f13; - height: 100%; - display: block; } - .progress.radius { - -webkit-border-radius: 3px; - border-radius: 3px; } - .progress.radius .meter { - -webkit-border-radius: 2px; - border-radius: 2px; } - .progress.round { - -webkit-border-radius: 1000px; - border-radius: 1000px; } - .progress.round .meter { - -webkit-border-radius: 999px; - border-radius: 999px; } - -.sub-nav { - display: block; - width: auto; - overflow: hidden; - margin: -0.25rem 0 1.125rem; - padding-top: 0.25rem; - margin-right: 0; - margin-left: -0.75rem; } - .sub-nav dt { - text-transform: uppercase; } - .sub-nav dt, - .sub-nav dd, - .sub-nav li { - float: left; - display: inline; - margin-left: 1rem; - margin-bottom: 0.625rem; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - font-weight: normal; - font-size: 0.875rem; - color: #999999; } - .sub-nav dt a, - .sub-nav dd a, - .sub-nav li a { - text-decoration: none; - color: #999999; - padding: 0.1875rem 1rem; } - .sub-nav dt a:hover, - .sub-nav dd a:hover, - .sub-nav li a:hover { - color: #737373; } - .sub-nav dt.active a, - .sub-nav dd.active a, - .sub-nav li.active a { - -webkit-border-radius: 3px; - border-radius: 3px; - font-weight: normal; - background: #0086a9; - padding: 0.1875rem 1rem; - cursor: default; - color: white; } - .sub-nav dt.active a:hover, - .sub-nav dd.active a:hover, - .sub-nav li.active a:hover { - background: #007391; } - -/* Foundation Joyride */ -.joyride-list { - display: none; } - -/* Default styles for the container */ -.joyride-tip-guide { - display: none; - position: absolute; - background: #333333; - color: white; - z-index: 101; - top: 0; - left: 2.5%; - font-family: inherit; - font-weight: normal; - width: 95%; } - -.lt-ie9 .joyride-tip-guide { - max-width: 800px; - left: 50%; - margin-left: -400px; } - -.joyride-content-wrapper { - width: 100%; - padding: 1.125rem 1.25rem 1.5rem; } - .joyride-content-wrapper .button { - margin-bottom: 0 !important; } - -/* Add a little css triangle pip, older browser just miss out on the fanciness of it */ -.joyride-tip-guide .joyride-nub { - display: block; - position: absolute; - left: 22px; - width: 0; - height: 0; - border: 10px solid #333333; } - .joyride-tip-guide .joyride-nub.top { - border-top-style: solid; - border-color: #333333; - border-top-color: transparent !important; - border-left-color: transparent !important; - border-right-color: transparent !important; - top: -20px; } - .joyride-tip-guide .joyride-nub.bottom { - border-bottom-style: solid; - border-color: #333333 !important; - border-bottom-color: transparent !important; - border-left-color: transparent !important; - border-right-color: transparent !important; - bottom: -20px; } - .joyride-tip-guide .joyride-nub.right { - right: -20px; } - .joyride-tip-guide .joyride-nub.left { - left: -20px; } - -/* Typography */ -.joyride-tip-guide h1, -.joyride-tip-guide h2, -.joyride-tip-guide h3, -.joyride-tip-guide h4, -.joyride-tip-guide h5, -.joyride-tip-guide h6 { - line-height: 1.25; - margin: 0; - font-weight: bold; - color: white; } - -.joyride-tip-guide p { - margin: 0 0 1.125rem 0; - font-size: 0.875rem; - line-height: 1.3; } - -.joyride-timer-indicator-wrap { - width: 50px; - height: 3px; - border: solid 1px #555555; - position: absolute; - right: 1.0625rem; - bottom: 1rem; } - -.joyride-timer-indicator { - display: block; - width: 0; - height: inherit; - background: #666666; } - -.joyride-close-tip { - position: absolute; - right: 12px; - top: 10px; - color: #777777 !important; - text-decoration: none; - font-size: 24px; - font-weight: normal; - line-height: 0.5 !important; } - .joyride-close-tip:hover, .joyride-close-tip:focus { - color: #eeeeee !important; } - -.joyride-modal-bg { - position: fixed; - height: 100%; - width: 100%; - background: transparent; - background: rgba(0, 0, 0, 0.5); - z-index: 100; - display: none; - top: 0; - left: 0; - cursor: pointer; } - -.joyride-expose-wrapper { - background-color: #ffffff; - position: absolute; - border-radius: 3px; - z-index: 102; - -moz-box-shadow: 0 0 30px white; - -webkit-box-shadow: 0 0 15px white; - box-shadow: 0 0 15px white; } - -.joyride-expose-cover { - background: transparent; - border-radius: 3px; - position: absolute; - z-index: 9999; - top: 0; - left: 0; } - -/* Styles for screens that are at least 768px; */ -@media only screen and (min-width: 40.063em) { - .joyride-tip-guide { - width: 300px; - left: inherit; } - .joyride-tip-guide .joyride-nub.bottom { - border-color: #333333 !important; - border-bottom-color: transparent !important; - border-left-color: transparent !important; - border-right-color: transparent !important; - bottom: -20px; } - .joyride-tip-guide .joyride-nub.right { - border-color: #333333 !important; - border-top-color: transparent !important; - border-right-color: transparent !important; - border-bottom-color: transparent !important; - top: 22px; - left: auto; - right: -20px; } - .joyride-tip-guide .joyride-nub.left { - border-color: #333333 !important; - border-top-color: transparent !important; - border-left-color: transparent !important; - border-bottom-color: transparent !important; - top: 22px; - left: -20px; - right: auto; } } -.label { - font-weight: normal; - font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; - text-align: center; - text-decoration: none; - line-height: 1; - white-space: nowrap; - display: inline-block; - position: relative; - margin-bottom: inherit; - padding: 0.25rem 0.5rem 0.375rem; - font-size: 0.6875rem; - background-color: #0086a9; - color: white; } - .label.radius { - -webkit-border-radius: 3px; - border-radius: 3px; } - .label.round { - -webkit-border-radius: 1000px; - border-radius: 1000px; } - .label.alert { - background-color: #c60f13; - color: white; } - .label.success { - background-color: #20ba44; - color: white; } - .label.secondary { - background-color: #ff6600; - color: white; } - -.text-left { - text-align: left !important; } - -.text-right { - text-align: right !important; } - -.text-center { - text-align: center !important; } - -.text-justify { - text-align: justify !important; } - -@media only screen and (max-width: 40em) { - .small-only-text-left { - text-align: left !important; } - - .small-only-text-right { - text-align: right !important; } - - .small-only-text-center { - text-align: center !important; } - - .small-only-text-justify { - text-align: justify !important; } } -@media only screen { - .small-text-left { - text-align: left !important; } - - .small-text-right { - text-align: right !important; } - - .small-text-center { - text-align: center !important; } - - .small-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 40.063em) and (max-width: 64em) { - .medium-only-text-left { - text-align: left !important; } - - .medium-only-text-right { - text-align: right !important; } - - .medium-only-text-center { - text-align: center !important; } - - .medium-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 40.063em) { - .medium-text-left { - text-align: left !important; } - - .medium-text-right { - text-align: right !important; } - - .medium-text-center { - text-align: center !important; } - - .medium-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 64.063em) and (max-width: 90em) { - .large-only-text-left { - text-align: left !important; } - - .large-only-text-right { - text-align: right !important; } - - .large-only-text-center { - text-align: center !important; } - - .large-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 64.063em) { - .large-text-left { - text-align: left !important; } - - .large-text-right { - text-align: right !important; } - - .large-text-center { - text-align: center !important; } - - .large-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 90.063em) and (max-width: 120em) { - .xlarge-only-text-left { - text-align: left !important; } - - .xlarge-only-text-right { - text-align: right !important; } - - .xlarge-only-text-center { - text-align: center !important; } - - .xlarge-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 90.063em) { - .xlarge-text-left { - text-align: left !important; } - - .xlarge-text-right { - text-align: right !important; } - - .xlarge-text-center { - text-align: center !important; } - - .xlarge-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 120.063em) and (max-width: 99999999em) { - .xxlarge-only-text-left { - text-align: left !important; } - - .xxlarge-only-text-right { - text-align: right !important; } - - .xxlarge-only-text-center { - text-align: center !important; } - - .xxlarge-only-text-justify { - text-align: justify !important; } } -@media only screen and (min-width: 120.063em) { - .xxlarge-text-left { - text-align: left !important; } - - .xxlarge-text-right { - text-align: right !important; } - - .xxlarge-text-center { - text-align: center !important; } - - .xxlarge-text-justify { - text-align: justify !important; } } -.off-canvas-wrap { - -webkit-backface-visibility: hidden; - position: relative; - width: 100%; - overflow-x: hidden; } - .off-canvas-wrap.move-right, .off-canvas-wrap.move-left { - height: 100%; } - -.inner-wrap { - -webkit-backface-visibility: hidden; - position: relative; - width: 100%; - *zoom: 1; - -webkit-transition: -webkit-transform 500ms ease; - -moz-transition: -moz-transform 500ms ease; - -ms-transition: -ms-transform 500ms ease; - -o-transition: -o-transform 500ms ease; - transition: transform 500ms ease; } - .inner-wrap:before, .inner-wrap:after { - content: " "; - display: table; } - .inner-wrap:after { - clear: both; } - -nav.tab-bar { - -webkit-backface-visibility: hidden; - background: #333333; - color: white; - height: 2.8125rem; - line-height: 2.8125rem; - position: relative; } - nav.tab-bar h1, nav.tab-bar h2, nav.tab-bar h3, nav.tab-bar h4, nav.tab-bar h5, nav.tab-bar h6 { - color: white; - font-weight: bold; - line-height: 2.8125rem; - margin: 0; } - nav.tab-bar h1, nav.tab-bar h2, nav.tab-bar h3, nav.tab-bar h4 { - font-size: 1.125rem; } - -section.left-small { - width: 2.8125rem; - height: 2.8125rem; - position: absolute; - top: 0; - border-right: solid 1px #1a1a1a; - box-shadow: 1px 0 0 #4e4e4e; - left: 0; } - -section.right-small { - width: 2.8125rem; - height: 2.8125rem; - position: absolute; - top: 0; - border-left: solid 1px #4e4e4e; - box-shadow: -1px 0 0 #1a1a1a; - right: 0; } - -section.tab-bar-section { - padding: 0 0.625rem; - position: absolute; - text-align: center; - height: 2.8125rem; - top: 0; } - @media only screen and (min-width: 40.063em) { - section.tab-bar-section { - text-align: left; } } - section.tab-bar-section.left { - left: 0; - right: 2.8125rem; } - section.tab-bar-section.right { - left: 2.8125rem; - right: 0; } - section.tab-bar-section.middle { - left: 2.8125rem; - right: 2.8125rem; } - -a.menu-icon { - text-indent: 2.1875rem; - width: 2.8125rem; - height: 2.8125rem; - display: block; - line-height: 2.0625rem; - padding: 0; - color: white; - position: relative; } - a.menu-icon span { - position: absolute; - display: block; - width: 1rem; - height: 0; - left: 0.8125rem; - top: 0.3125rem; - -webkit-box-shadow: 1px 10px 1px 1px white, 1px 16px 1px 1px white, 1px 22px 1px 1px white; - box-shadow: 0 10px 0 1px white, 0 16px 0 1px white, 0 22px 0 1px white; } - a.menu-icon:hover span { - -webkit-box-shadow: 1px 10px 1px 1px #b3b3b3, 1px 16px 1px 1px #b3b3b3, 1px 22px 1px 1px #b3b3b3; - box-shadow: 0 10px 0 1px #b3b3b3, 0 16px 0 1px #b3b3b3, 0 22px 0 1px #b3b3b3; } - -.left-off-canvas-menu { - -webkit-backface-visibility: hidden; - width: 250px; - top: 0; - bottom: 0; - position: absolute; - overflow-y: auto; - background: #333333; - z-index: 1001; - box-sizing: content-box; - -webkit-transform: translate3d(-100%, 0, 0); - -moz-transform: translate3d(-100%, 0, 0); - -ms-transform: translate3d(-100%, 0, 0); - -o-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - left: 0; } - .left-off-canvas-menu * { - -webkit-backface-visibility: hidden; } - -.right-off-canvas-menu { - -webkit-backface-visibility: hidden; - width: 250px; - top: 0; - bottom: 0; - position: absolute; - overflow-y: auto; - background: #333333; - z-index: 1001; - box-sizing: content-box; - -webkit-transform: translate3d(100%, 0, 0); - -moz-transform: translate3d(100%, 0, 0); - -ms-transform: translate3d(100%, 0, 0); - -o-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - right: 0; } - -ul.off-canvas-list { - list-style-type: none; - padding: 0; - margin: 0; } - ul.off-canvas-list li label { - padding: 0.3rem 0.9375rem; - color: #999999; - text-transform: uppercase; - font-weight: bold; - background: #444444; - border-top: 1px solid #5e5e5e; - border-bottom: none; - margin: 0; } - ul.off-canvas-list li a { - display: block; - padding: 0.66667rem; - color: rgba(255, 255, 255, 0.7); - border-bottom: 1px solid #262626; } - -.move-right > .inner-wrap { - -webkit-transform: translate3d(250px, 0, 0); - -moz-transform: translate3d(250px, 0, 0); - -ms-transform: translate3d(250px, 0, 0); - -o-transform: translate3d(250px, 0, 0); - transform: translate3d(250px, 0, 0); } -.move-right a.exit-off-canvas { - -webkit-backface-visibility: hidden; - transition: background 300ms ease; - cursor: pointer; - box-shadow: -4px 0 4px rgba(0, 0, 0, 0.5), 4px 0 4px rgba(0, 0, 0, 0.5); - display: block; - position: absolute; - background: rgba(255, 255, 255, 0.2); - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: 1002; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } - @media only screen and (min-width: 40.063em) { - .move-right a.exit-off-canvas:hover { - background: rgba(255, 255, 255, 0.05); } } - -.move-left > .inner-wrap { - -webkit-transform: translate3d(-250px, 0, 0); - -moz-transform: translate3d(-250px, 0, 0); - -ms-transform: translate3d(-250px, 0, 0); - -o-transform: translate3d(-250px, 0, 0); - transform: translate3d(-250px, 0, 0); } -.move-left a.exit-off-canvas { - -webkit-backface-visibility: hidden; - transition: background 300ms ease; - cursor: pointer; - box-shadow: -4px 0 4px rgba(0, 0, 0, 0.5), 4px 0 4px rgba(0, 0, 0, 0.5); - display: block; - position: absolute; - background: rgba(255, 255, 255, 0.2); - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: 1002; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } - @media only screen and (min-width: 40.063em) { - .move-left a.exit-off-canvas:hover { - background: rgba(255, 255, 255, 0.05); } } - -.csstransforms.no-csstransforms3d .left-off-canvas-menu { - -webkit-transform: translate(-100%, 0); - -moz-transform: translate(-100%, 0); - -ms-transform: translate(-100%, 0); - -o-transform: translate(-100%, 0); - transform: translate(-100%, 0); } -.csstransforms.no-csstransforms3d .right-off-canvas-menu { - -webkit-transform: translate(100%, 0); - -moz-transform: translate(100%, 0); - -ms-transform: translate(100%, 0); - -o-transform: translate(100%, 0); - transform: translate(100%, 0); } -.csstransforms.no-csstransforms3d .move-left > .inner-wrap { - -webkit-transform: translate(-250px, 0); - -moz-transform: translate(-250px, 0); - -ms-transform: translate(-250px, 0); - -o-transform: translate(-250px, 0); - transform: translate(-250px, 0); } -.csstransforms.no-csstransforms3d .move-right > .inner-wrap { - -webkit-transform: translate(250px, 0); - -moz-transform: translate(250px, 0); - -ms-transform: translate(250px, 0); - -o-transform: translate(250px, 0); - transform: translate(250px, 0); } - -.no-csstransforms .left-off-canvas-menu { - left: -250px; } -.no-csstransforms .right-off-canvas-menu { - right: -250px; } -.no-csstransforms .move-left > .inner-wrap { - right: 250px; } -.no-csstransforms .move-right > .inner-wrap { - left: 250px; } - -@media only screen and (max-width: 40em) { - .f-dropdown { - max-width: 100%; - left: 0; } } -/* Foundation Dropdowns */ -.f-dropdown { - position: absolute; - left: -9999px; - list-style: none; - margin-left: 0; - width: 100%; - max-height: none; - height: auto; - background: white; - border: solid 1px #cccccc; - font-size: 16px; - z-index: 99; - margin-top: 2px; - max-width: 200px; } - .f-dropdown > *:first-child { - margin-top: 0; } - .f-dropdown > *:last-child { - margin-bottom: 0; } - .f-dropdown:before { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 6px; - border-color: transparent transparent white transparent; - border-bottom-style: solid; - position: absolute; - top: -12px; - left: 10px; - z-index: 99; } - .f-dropdown:after { - content: ""; - display: block; - width: 0; - height: 0; - border: inset 7px; - border-color: transparent transparent #cccccc transparent; - border-bottom-style: solid; - position: absolute; - top: -14px; - left: 9px; - z-index: 98; } - .f-dropdown.right:before { - left: auto; - right: 10px; } - .f-dropdown.right:after { - left: auto; - right: 9px; } - .f-dropdown li { - font-size: 0.875rem; - cursor: pointer; - line-height: 1.125rem; - margin: 0; } - .f-dropdown li:hover, .f-dropdown li:focus { - background: #eeeeee; } - .f-dropdown li a { - display: block; - padding: 0.5rem; - color: #555555; } - .f-dropdown.content { - position: absolute; - left: -9999px; - list-style: none; - margin-left: 0; - padding: 1.25rem; - width: 100%; - height: auto; - max-height: none; - background: white; - border: solid 1px #cccccc; - font-size: 16px; - z-index: 99; - max-width: 200px; } - .f-dropdown.content > *:first-child { - margin-top: 0; } - .f-dropdown.content > *:last-child { - margin-bottom: 0; } - .f-dropdown.tiny { - max-width: 200px; } - .f-dropdown.small { - max-width: 300px; } - .f-dropdown.medium { - max-width: 500px; } - .f-dropdown.large { - max-width: 800px; } - -table { - background: white; - margin-bottom: 1.25rem; - border: solid 1px #dddddd; } - table thead, - table tfoot { - background: whitesmoke; } - table thead tr th, - table thead tr td, - table tfoot tr th, - table tfoot tr td { - padding: 0.5rem 0.625rem 0.625rem; - font-size: 0.875rem; - font-weight: bold; - color: #222222; - text-align: left; } - table tr th, - table tr td { - padding: 0.5625rem 0.625rem; - font-size: 0.875rem; - color: #222222; } - table tr.even, table tr.alt, table tr:nth-of-type(even) { - background: #f9f9f9; } - table thead tr th, - table tfoot tr th, - table tbody tr td, - table tr td, - table tfoot tr td { - display: table-cell; - line-height: 1.125rem; } - -/* Standard Forms */ -form { - margin: 0 0 1rem; } - -/* Using forms within rows, we need to set some defaults */ -form .row .row { - margin: 0 -0.5rem; } - form .row .row .column, - form .row .row .columns { - padding: 0 0.5rem; } - form .row .row.collapse { - margin: 0; } - form .row .row.collapse .column, - form .row .row.collapse .columns { - padding: 0; } - form .row .row.collapse input { - -moz-border-radius-bottomright: 0; - -moz-border-radius-topright: 0; - -webkit-border-bottom-right-radius: 0; - -webkit-border-top-right-radius: 0; } -form .row input.column, -form .row input.columns, -form .row textarea.column, -form .row textarea.columns { - padding-left: 0.5rem; } - -/* Label Styles */ -label { - font-size: 0.875rem; - color: #4d4d4d; - cursor: pointer; - display: block; - font-weight: normal; - line-height: 1.5; - margin-bottom: 0; - /* Styles for required inputs */ } - label.right { - float: none; - text-align: right; } - label.inline { - margin: 0 0 1rem 0; - padding: 0.625rem 0; } - label small { - text-transform: capitalize; - color: #676767; } - -select { - -webkit-appearance: none !important; - background: #fafafa url("data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iM3B4IiB2aWV3Qm94PSIwIDAgNiAzIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2IDMiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwb2x5Z29uIHBvaW50cz0iNS45OTIsMCAyLjk5MiwzIC0wLjAwOCwwICIvPjwvc3ZnPg==") no-repeat; - background-position-x: 97%; - background-position-y: center; - border: 1px solid #cccccc; - padding: 0.5rem; - font-size: 0.875rem; - -webkit-border-radius: 0; - border-radius: 0; } - select.radius { - -webkit-border-radius: 3px; - border-radius: 3px; } - select:hover { - background: #f3f3f3 url("data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iM3B4IiB2aWV3Qm94PSIwIDAgNiAzIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2IDMiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwb2x5Z29uIHBvaW50cz0iNS45OTIsMCAyLjk5MiwzIC0wLjAwOCwwICIvPjwvc3ZnPg==") no-repeat; - background-position-x: 97%; - background-position-y: center; - border-color: #999999; } - -select::-ms-expand { - display: none; } - -@-moz-document url-prefix() { - select { - background: #fafafa; } - - select:hover { - background: #f3f3f3; } } - -/* Attach elements to the beginning or end of an input */ -.prefix, -.postfix { - display: block; - position: relative; - z-index: 2; - text-align: center; - width: 100%; - padding-top: 0; - padding-bottom: 0; - border-style: solid; - border-width: 1px; - overflow: hidden; - font-size: 0.875rem; - height: 2.3125rem; - line-height: 2.3125rem; } - -/* Adjust padding, alignment and radius if pre/post element is a button */ -.postfix.button { - padding-left: 0; - padding-right: 0; - padding-top: 0; - padding-bottom: 0; - text-align: center; - line-height: 2.125rem; - border: none; } - -.prefix.button { - padding-left: 0; - padding-right: 0; - padding-top: 0; - padding-bottom: 0; - text-align: center; - line-height: 2.125rem; - border: none; } - -.prefix.button.radius { - -webkit-border-radius: 0; - border-radius: 0; - -moz-border-radius-bottomleft: 3px; - -moz-border-radius-topleft: 3px; - -webkit-border-bottom-left-radius: 3px; - -webkit-border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - border-top-left-radius: 3px; } - -.postfix.button.radius { - -webkit-border-radius: 0; - border-radius: 0; - -moz-border-radius-bottomright: 3px; - -moz-border-radius-topright: 3px; - -webkit-border-bottom-right-radius: 3px; - -webkit-border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - border-top-right-radius: 3px; } - -.prefix.button.round { - -webkit-border-radius: 0; - border-radius: 0; - -moz-border-radius-bottomleft: 1000px; - -moz-border-radius-topleft: 1000px; - -webkit-border-bottom-left-radius: 1000px; - -webkit-border-top-left-radius: 1000px; - border-bottom-left-radius: 1000px; - border-top-left-radius: 1000px; } - -.postfix.button.round { - -webkit-border-radius: 0; - border-radius: 0; - -moz-border-radius-bottomright: 1000px; - -moz-border-radius-topright: 1000px; - -webkit-border-bottom-right-radius: 1000px; - -webkit-border-top-right-radius: 1000px; - border-bottom-right-radius: 1000px; - border-top-right-radius: 1000px; } - -/* Separate prefix and postfix styles when on span or label so buttons keep their own */ -span.prefix, label.prefix { - background: #f2f2f2; - border-right: none; - color: #333333; - border-color: #cccccc; } - span.prefix.radius, label.prefix.radius { - -webkit-border-radius: 0; - border-radius: 0; - -moz-border-radius-bottomleft: 3px; - -moz-border-radius-topleft: 3px; - -webkit-border-bottom-left-radius: 3px; - -webkit-border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - border-top-left-radius: 3px; } - -span.postfix, label.postfix { - background: #f2f2f2; - border-left: none; - color: #333333; - border-color: #cccccc; } - span.postfix.radius, label.postfix.radius { - -webkit-border-radius: 0; - border-radius: 0; - -moz-border-radius-bottomright: 3px; - -moz-border-radius-topright: 3px; - -webkit-border-bottom-right-radius: 3px; - -webkit-border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - border-top-right-radius: 3px; } - -/* We use this to get basic styling on all basic form elements */ -input[type="text"], -input[type="password"], -input[type="date"], -input[type="datetime"], -input[type="datetime-local"], -input[type="month"], -input[type="week"], -input[type="email"], -input[type="number"], -input[type="search"], -input[type="tel"], -input[type="time"], -input[type="url"], -textarea { - -webkit-appearance: none; - background-color: white; - font-family: inherit; - border: 1px solid #cccccc; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - color: rgba(0, 0, 0, 0.75); - display: block; - font-size: 0.875rem; - margin: 0 0 1rem 0; - padding: 0.5rem; - height: 2.3125rem; - width: 100%; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - -webkit-transition: -webkit-box-shadow 0.45s, border-color 0.45s ease-in-out; - -moz-transition: -moz-box-shadow 0.45s, border-color 0.45s ease-in-out; - transition: box-shadow 0.45s, border-color 0.45s ease-in-out; } - input[type="text"]:focus, - input[type="password"]:focus, - input[type="date"]:focus, - input[type="datetime"]:focus, - input[type="datetime-local"]:focus, - input[type="month"]:focus, - input[type="week"]:focus, - input[type="email"]:focus, - input[type="number"]:focus, - input[type="search"]:focus, - input[type="tel"]:focus, - input[type="time"]:focus, - input[type="url"]:focus, - textarea:focus { - -webkit-box-shadow: 0 0 5px #999999; - -moz-box-shadow: 0 0 5px #999999; - box-shadow: 0 0 5px #999999; - border-color: #999999; } - input[type="text"]:focus, - input[type="password"]:focus, - input[type="date"]:focus, - input[type="datetime"]:focus, - input[type="datetime-local"]:focus, - input[type="month"]:focus, - input[type="week"]:focus, - input[type="email"]:focus, - input[type="number"]:focus, - input[type="search"]:focus, - input[type="tel"]:focus, - input[type="time"]:focus, - input[type="url"]:focus, - textarea:focus { - background: #fafafa; - border-color: #999999; - outline: none; } - input[type="text"][disabled], - input[type="password"][disabled], - input[type="date"][disabled], - input[type="datetime"][disabled], - input[type="datetime-local"][disabled], - input[type="month"][disabled], - input[type="week"][disabled], - input[type="email"][disabled], - input[type="number"][disabled], - input[type="search"][disabled], - input[type="tel"][disabled], - input[type="time"][disabled], - input[type="url"][disabled], - textarea[disabled] { - background-color: #dddddd; } - input[type="text"].radius, - input[type="password"].radius, - input[type="date"].radius, - input[type="datetime"].radius, - input[type="datetime-local"].radius, - input[type="month"].radius, - input[type="week"].radius, - input[type="email"].radius, - input[type="number"].radius, - input[type="search"].radius, - input[type="tel"].radius, - input[type="time"].radius, - input[type="url"].radius, - textarea.radius { - -webkit-border-radius: 3px; - border-radius: 3px; } - -/* Add height value for select elements to match text input height */ -select { - height: 2.3125rem; } - -/* Adjust margin for form elements below */ -input[type="file"], -input[type="checkbox"], -input[type="radio"], -select { - margin: 0 0 1rem 0; } - -input[type="checkbox"] + label, -input[type="radio"] + label { - display: inline-block; - margin-left: 0.5rem; - margin-right: 1rem; - margin-bottom: 0; - vertical-align: baseline; } - -/* Normalize file input width */ -input[type="file"] { - width: 100%; } - -/* We add basic fieldset styling */ -fieldset { - border: solid 1px #dddddd; - padding: 1.25rem; - margin: 1.125rem 0; } - fieldset legend { - font-weight: bold; - background: white; - padding: 0 0.1875rem; - margin: 0; - margin-left: -0.1875rem; } - -/* Error Handling */ -[data-abide] .error small.error, [data-abide] span.error, [data-abide] small.error { - display: block; - padding: 0.375rem 0.5625rem 0.5625rem; - margin-top: -1px; - margin-bottom: 1rem; - font-size: 0.75rem; - font-weight: normal; - font-style: italic; - background: #c60f13; - color: white; } -[data-abide] span.error, [data-abide] small.error { - display: none; } - -span.error, small.error { - display: block; - padding: 0.375rem 0.5625rem 0.5625rem; - margin-top: -1px; - margin-bottom: 1rem; - font-size: 0.75rem; - font-weight: normal; - font-style: italic; - background: #c60f13; - color: white; } - -.error input, -.error textarea, -.error select { - margin-bottom: 0; } -.error input[type="checkbox"], -.error input[type="radio"] { - margin-bottom: 1rem; } -.error label, -.error label.error { - color: #c60f13; } -.error small.error { - display: block; - padding: 0.375rem 0.5625rem 0.5625rem; - margin-top: -1px; - margin-bottom: 1rem; - font-size: 0.75rem; - font-weight: normal; - font-style: italic; - background: #c60f13; - color: white; } -.error > label > small { - color: #676767; - background: transparent; - padding: 0; - text-transform: capitalize; - font-style: normal; - font-size: 60%; - margin: 0; - display: inline; } -.error span.error-message { - display: block; } - -input.error, -textarea.error { - margin-bottom: 0; } - -label.error { - color: #c60f13; } - -[class*="block-grid-"] { - display: block; - padding: 0; - margin: 0 -0.625rem; - *zoom: 1; } - [class*="block-grid-"]:before, [class*="block-grid-"]:after { - content: " "; - display: table; } - [class*="block-grid-"]:after { - clear: both; } - [class*="block-grid-"] > li { - display: block; - height: auto; - float: left; - padding: 0 0.625rem 1.25rem; } - -@media only screen { - .small-block-grid-1 > li { - width: 100%; - list-style: none; } - .small-block-grid-1 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-1 > li:nth-of-type(1n+1) { - clear: both; } - - .small-block-grid-2 > li { - width: 50%; - list-style: none; } - .small-block-grid-2 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-2 > li:nth-of-type(2n+1) { - clear: both; } - - .small-block-grid-3 > li { - width: 33.33333%; - list-style: none; } - .small-block-grid-3 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-3 > li:nth-of-type(3n+1) { - clear: both; } - - .small-block-grid-4 > li { - width: 25%; - list-style: none; } - .small-block-grid-4 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-4 > li:nth-of-type(4n+1) { - clear: both; } - - .small-block-grid-5 > li { - width: 20%; - list-style: none; } - .small-block-grid-5 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-5 > li:nth-of-type(5n+1) { - clear: both; } - - .small-block-grid-6 > li { - width: 16.66667%; - list-style: none; } - .small-block-grid-6 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-6 > li:nth-of-type(6n+1) { - clear: both; } - - .small-block-grid-7 > li { - width: 14.28571%; - list-style: none; } - .small-block-grid-7 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-7 > li:nth-of-type(7n+1) { - clear: both; } - - .small-block-grid-8 > li { - width: 12.5%; - list-style: none; } - .small-block-grid-8 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-8 > li:nth-of-type(8n+1) { - clear: both; } - - .small-block-grid-9 > li { - width: 11.11111%; - list-style: none; } - .small-block-grid-9 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-9 > li:nth-of-type(9n+1) { - clear: both; } - - .small-block-grid-10 > li { - width: 10%; - list-style: none; } - .small-block-grid-10 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-10 > li:nth-of-type(10n+1) { - clear: both; } - - .small-block-grid-11 > li { - width: 9.09091%; - list-style: none; } - .small-block-grid-11 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-11 > li:nth-of-type(11n+1) { - clear: both; } - - .small-block-grid-12 > li { - width: 8.33333%; - list-style: none; } - .small-block-grid-12 > li:nth-of-type(n) { - clear: none; } - .small-block-grid-12 > li:nth-of-type(12n+1) { - clear: both; } } -@media only screen and (min-width: 40.063em) { - .medium-block-grid-1 > li { - width: 100%; - list-style: none; } - .medium-block-grid-1 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-1 > li:nth-of-type(1n+1) { - clear: both; } - - .medium-block-grid-2 > li { - width: 50%; - list-style: none; } - .medium-block-grid-2 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-2 > li:nth-of-type(2n+1) { - clear: both; } - - .medium-block-grid-3 > li { - width: 33.33333%; - list-style: none; } - .medium-block-grid-3 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-3 > li:nth-of-type(3n+1) { - clear: both; } - - .medium-block-grid-4 > li { - width: 25%; - list-style: none; } - .medium-block-grid-4 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-4 > li:nth-of-type(4n+1) { - clear: both; } - - .medium-block-grid-5 > li { - width: 20%; - list-style: none; } - .medium-block-grid-5 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-5 > li:nth-of-type(5n+1) { - clear: both; } - - .medium-block-grid-6 > li { - width: 16.66667%; - list-style: none; } - .medium-block-grid-6 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-6 > li:nth-of-type(6n+1) { - clear: both; } - - .medium-block-grid-7 > li { - width: 14.28571%; - list-style: none; } - .medium-block-grid-7 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-7 > li:nth-of-type(7n+1) { - clear: both; } - - .medium-block-grid-8 > li { - width: 12.5%; - list-style: none; } - .medium-block-grid-8 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-8 > li:nth-of-type(8n+1) { - clear: both; } - - .medium-block-grid-9 > li { - width: 11.11111%; - list-style: none; } - .medium-block-grid-9 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-9 > li:nth-of-type(9n+1) { - clear: both; } - - .medium-block-grid-10 > li { - width: 10%; - list-style: none; } - .medium-block-grid-10 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-10 > li:nth-of-type(10n+1) { - clear: both; } - - .medium-block-grid-11 > li { - width: 9.09091%; - list-style: none; } - .medium-block-grid-11 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-11 > li:nth-of-type(11n+1) { - clear: both; } - - .medium-block-grid-12 > li { - width: 8.33333%; - list-style: none; } - .medium-block-grid-12 > li:nth-of-type(n) { - clear: none; } - .medium-block-grid-12 > li:nth-of-type(12n+1) { - clear: both; } } -@media only screen and (min-width: 64.063em) { - .large-block-grid-1 > li { - width: 100%; - list-style: none; } - .large-block-grid-1 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-1 > li:nth-of-type(1n+1) { - clear: both; } - - .large-block-grid-2 > li { - width: 50%; - list-style: none; } - .large-block-grid-2 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-2 > li:nth-of-type(2n+1) { - clear: both; } - - .large-block-grid-3 > li { - width: 33.33333%; - list-style: none; } - .large-block-grid-3 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-3 > li:nth-of-type(3n+1) { - clear: both; } - - .large-block-grid-4 > li { - width: 25%; - list-style: none; } - .large-block-grid-4 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-4 > li:nth-of-type(4n+1) { - clear: both; } - - .large-block-grid-5 > li { - width: 20%; - list-style: none; } - .large-block-grid-5 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-5 > li:nth-of-type(5n+1) { - clear: both; } - - .large-block-grid-6 > li { - width: 16.66667%; - list-style: none; } - .large-block-grid-6 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-6 > li:nth-of-type(6n+1) { - clear: both; } - - .large-block-grid-7 > li { - width: 14.28571%; - list-style: none; } - .large-block-grid-7 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-7 > li:nth-of-type(7n+1) { - clear: both; } - - .large-block-grid-8 > li { - width: 12.5%; - list-style: none; } - .large-block-grid-8 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-8 > li:nth-of-type(8n+1) { - clear: both; } - - .large-block-grid-9 > li { - width: 11.11111%; - list-style: none; } - .large-block-grid-9 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-9 > li:nth-of-type(9n+1) { - clear: both; } - - .large-block-grid-10 > li { - width: 10%; - list-style: none; } - .large-block-grid-10 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-10 > li:nth-of-type(10n+1) { - clear: both; } - - .large-block-grid-11 > li { - width: 9.09091%; - list-style: none; } - .large-block-grid-11 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-11 > li:nth-of-type(11n+1) { - clear: both; } - - .large-block-grid-12 > li { - width: 8.33333%; - list-style: none; } - .large-block-grid-12 > li:nth-of-type(n) { - clear: none; } - .large-block-grid-12 > li:nth-of-type(12n+1) { - clear: both; } } -.keystroke, -kbd { - background-color: #ededed; - border-color: #dddddd; - color: #222222; - border-style: solid; - border-width: 1px; - margin: 0; - font-family: "Consolas", "Menlo", "Courier", monospace; - font-size: 0.875rem; - padding: 0.125rem 0.25rem 0; - -webkit-border-radius: 3px; - border-radius: 3px; } - -/* Foundation Visibility HTML Classes */ -.show-for-small, -.show-for-small-only, -.show-for-medium-down, -.show-for-large-down, -.hide-for-medium, -.hide-for-medium-up, -.hide-for-medium-only, -.hide-for-large, -.hide-for-large-up, -.hide-for-large-only, -.hide-for-xlarge, -.hide-for-xlarge-up, -.hide-for-xlarge-only, -.hide-for-xxlarge-up, -.hide-for-xxlarge-only { - display: inherit !important; } - -.hide-for-small, -.hide-for-small-only, -.hide-for-medium-down, -.show-for-medium, -.show-for-medium-up, -.show-for-medium-only, -.hide-for-large-down, -.show-for-large, -.show-for-large-up, -.show-for-large-only, -.show-for-xlarge, -.show-for-xlarge-up, -.show-for-xlarge-only, -.show-for-xxlarge-up, -.show-for-xxlarge-only { - display: none !important; } - -/* Specific visibility for tables */ -table.show-for-small, table.show-for-small-only, table.show-for-medium-down, table.show-for-large-down, table.hide-for-medium, table.hide-for-medium-up, table.hide-for-medium-only, table.hide-for-large, table.hide-for-large-up, table.hide-for-large-only, table.hide-for-xlarge, table.hide-for-xlarge-up, table.hide-for-xlarge-only, table.hide-for-xxlarge-up, table.hide-for-xxlarge-only { - display: table; } - -thead.show-for-small, thead.show-for-small-only, thead.show-for-medium-down, thead.show-for-large-down, thead.hide-for-medium, thead.hide-for-medium-up, thead.hide-for-medium-only, thead.hide-for-large, thead.hide-for-large-up, thead.hide-for-large-only, thead.hide-for-xlarge, thead.hide-for-xlarge-up, thead.hide-for-xlarge-only, thead.hide-for-xxlarge-up, thead.hide-for-xxlarge-only { - display: table-header-group !important; } - -tbody.show-for-small, tbody.show-for-small-only, tbody.show-for-medium-down, tbody.show-for-large-down, tbody.hide-for-medium, tbody.hide-for-medium-up, tbody.hide-for-medium-only, tbody.hide-for-large, tbody.hide-for-large-up, tbody.hide-for-large-only, tbody.hide-for-xlarge, tbody.hide-for-xlarge-up, tbody.hide-for-xlarge-only, tbody.hide-for-xxlarge-up, tbody.hide-for-xxlarge-only { - display: table-row-group !important; } - -tr.show-for-small, tr.show-for-small-only, tr.show-for-medium-down, tr.show-for-large-down, tr.hide-for-medium, tr.hide-for-medium-up, tr.hide-for-medium-only, tr.hide-for-large, tr.hide-for-large-up, tr.hide-for-large-only, tr.hide-for-xlarge, tr.hide-for-xlarge-up, tr.hide-for-xlarge-only, tr.hide-for-xxlarge-up, tr.hide-for-xxlarge-only { - display: table-row !important; } - -td.show-for-small, td.show-for-small-only, td.show-for-medium-down, td.show-for-large-down, td.hide-for-medium, td.hide-for-medium-up, td.hide-for-large, td.hide-for-large-up, td.hide-for-xlarge, td.hide-for-xlarge-up, td.hide-for-xxlarge-up, -th.show-for-small, -th.show-for-small-only, -th.show-for-medium-down, -th.show-for-large-down, -th.hide-for-medium, -th.hide-for-medium-up, -th.hide-for-large, -th.hide-for-large-up, -th.hide-for-xlarge, -th.hide-for-xlarge-up, -th.hide-for-xxlarge-up { - display: table-cell !important; } - -/* Medium Displays: 641px and up */ -@media only screen and (min-width: 40.063em) { - .hide-for-small, - .hide-for-small-only, - .show-for-medium, - .show-for-medium-down, - .show-for-medium-up, - .show-for-medium-only, - .hide-for-large, - .hide-for-large-up, - .hide-for-large-only, - .hide-for-xlarge, - .hide-for-xlarge-up, - .hide-for-xlarge-only, - .hide-for-xxlarge-up, - .hide-for-xxlarge-only { - display: inherit !important; } - - .show-for-small, - .show-for-small-only, - .hide-for-medium, - .hide-for-medium-down, - .hide-for-medium-up, - .hide-for-medium-only, - .hide-for-large-down, - .show-for-large, - .show-for-large-up, - .show-for-large-only, - .show-for-xlarge, - .show-for-xlarge-up, - .show-for-xlarge-only, - .show-for-xxlarge-up, - .show-for-xxlarge-only { - display: none !important; } - - /* Specific visibility for tables */ - table.hide-for-small, table.hide-for-small-only, table.show-for-medium, table.show-for-medium-down, table.show-for-medium-up, table.show-for-medium-only, table.hide-for-large, table.hide-for-large-up, table.hide-for-large-only, table.hide-for-xlarge, table.hide-for-xlarge-up, table.hide-for-xlarge-only, table.hide-for-xxlarge-up, table.hide-for-xxlarge-only { - display: table; } - - thead.hide-for-small, thead.hide-for-small-only, thead.show-for-medium, thead.show-for-medium-down, thead.show-for-medium-up, thead.show-for-medium-only, thead.hide-for-large, thead.hide-for-large-up, thead.hide-for-large-only, thead.hide-for-xlarge, thead.hide-for-xlarge-up, thead.hide-for-xlarge-only, thead.hide-for-xxlarge-up, thead.hide-for-xxlarge-only { - display: table-header-group !important; } - - tbody.hide-for-small, tbody.hide-for-small-only, tbody.show-for-medium, tbody.show-for-medium-down, tbody.show-for-medium-up, tbody.show-for-medium-only, tbody.hide-for-large, tbody.hide-for-large-up, tbody.hide-for-large-only, tbody.hide-for-xlarge, tbody.hide-for-xlarge-up, tbody.hide-for-xlarge-only, tbody.hide-for-xxlarge-up, tbody.hide-for-xxlarge-only { - display: table-row-group !important; } - - tr.hide-for-small, tr.hide-for-small-only, tr.show-for-medium, tr.show-for-medium-down, tr.show-for-medium-up, tr.show-for-medium-only, tr.hide-for-large, tr.hide-for-large-up, tr.hide-for-large-only, tr.hide-for-xlarge, tr.hide-for-xlarge-up, tr.hide-for-xlarge-only, tr.hide-for-xxlarge-up, tr.hide-for-xxlarge-only { - display: table-row !important; } - - td.hide-for-small, td.hide-for-small-only, td.show-for-medium, td.show-for-medium-down, td.show-for-medium-up, td.show-for-medium-only, td.hide-for-large, td.hide-for-large-up, td.hide-for-large-only, td.hide-for-xlarge, td.hide-for-xlarge-up, td.hide-for-xlarge-only, td.hide-for-xxlarge-up, td.hide-for-xxlarge-only, - th.hide-for-small, - th.hide-for-small-only, - th.show-for-medium, - th.show-for-medium-down, - th.show-for-medium-up, - th.show-for-medium-only, - th.hide-for-large, - th.hide-for-large-up, - th.hide-for-large-only, - th.hide-for-xlarge, - th.hide-for-xlarge-up, - th.hide-for-xlarge-only, - th.hide-for-xxlarge-up, - th.hide-for-xxlarge-only { - display: table-cell !important; } } -/* Large Displays: 1024px and up */ -@media only screen and (min-width: 64.063em) { - .hide-for-small, - .hide-for-small-only, - .hide-for-medium, - .hide-for-medium-down, - .hide-for-medium-only, - .show-for-medium-up, - .show-for-large, - .show-for-large-up, - .show-for-large-only, - .hide-for-xlarge, - .hide-for-xlarge-up, - .hide-for-xlarge-only, - .hide-for-xxlarge-up, - .hide-for-xxlarge-only { - display: inherit !important; } - - .show-for-small-only, - .show-for-medium, - .show-for-medium-down, - .show-for-medium-only, - .hide-for-large, - .hide-for-large-up, - .hide-for-large-only, - .show-for-xlarge, - .show-for-xlarge-up, - .show-for-xlarge-only, - .show-for-xxlarge-up, - .show-for-xxlarge-only { - display: none !important; } - - /* Specific visibility for tables */ - table.hide-for-small, table.hide-for-small-only, table.hide-for-medium, table.hide-for-medium-down, table.hide-for-medium-only, table.show-for-medium-up, table.show-for-large, table.show-for-large-up, table.show-for-large-only, table.hide-for-xlarge, table.hide-for-xlarge-up, table.hide-for-xlarge-only, table.hide-for-xxlarge-up, table.hide-for-xxlarge-only { - display: table; } - - thead.hide-for-small, thead.hide-for-small-only, thead.hide-for-medium, thead.hide-for-medium-down, thead.hide-for-medium-only, thead.show-for-medium-up, thead.show-for-large, thead.show-for-large-up, thead.show-for-large-only, thead.hide-for-xlarge, thead.hide-for-xlarge-up, thead.hide-for-xlarge-only, thead.hide-for-xxlarge-up, thead.hide-for-xxlarge-only { - display: table-header-group !important; } - - tbody.hide-for-small, tbody.hide-for-small-only, tbody.hide-for-medium, tbody.hide-for-medium-down, tbody.hide-for-medium-only, tbody.show-for-medium-up, tbody.show-for-large, tbody.show-for-large-up, tbody.show-for-large-only, tbody.hide-for-xlarge, tbody.hide-for-xlarge-up, tbody.hide-for-xlarge-only, tbody.hide-for-xxlarge-up, tbody.hide-for-xxlarge-only { - display: table-row-group !important; } - - tr.hide-for-small, tr.hide-for-small-only, tr.hide-for-medium, tr.hide-for-medium-down, tr.hide-for-medium-only, tr.show-for-medium-up, tr.show-for-large, tr.show-for-large-up, tr.show-for-large-only, tr.hide-for-xlarge, tr.hide-for-xlarge-up, tr.hide-for-xlarge-only, tr.hide-for-xxlarge-up, tr.hide-for-xxlarge-only { - display: table-row !important; } - - td.hide-for-small, td.hide-for-small-only, td.hide-for-medium, td.hide-for-medium-down, td.hide-for-medium-only, td.show-for-medium-up, td.show-for-large, td.show-for-large-up, td.show-for-large-only, td.hide-for-xlarge, td.hide-for-xlarge-up, td.hide-for-xlarge-only, td.hide-for-xxlarge-up, td.hide-for-xxlarge-only, - th.hide-for-small, - th.hide-for-small-only, - th.hide-for-medium, - th.hide-for-medium-down, - th.hide-for-medium-only, - th.show-for-medium-up, - th.show-for-large, - th.show-for-large-up, - th.show-for-large-only, - th.hide-for-xlarge, - th.hide-for-xlarge-up, - th.hide-for-xlarge-only, - th.hide-for-xxlarge-up, - th.hide-for-xxlarge-only { - display: table-cell !important; } } -/* X-Large Displays: 1441 and up */ -@media only screen and (min-width: 90.063em) { - .hide-for-small, - .hide-for-small-only, - .hide-for-medium, - .hide-for-medium-down, - .hide-for-medium-only, - .show-for-medium-up, - .show-for-large-up, - .hide-for-large-only, - .show-for-xlarge, - .show-for-xlarge-up, - .show-for-xlarge-only, - .hide-for-xxlarge-up, - .hide-for-xxlarge-only { - display: inherit !important; } - - .show-for-small-only, - .show-for-medium, - .show-for-medium-down, - .show-for-medium-only, - .show-for-large, - .show-for-large-only, - .show-for-large-down, - .hide-for-xlarge, - .hide-for-xlarge-up, - .hide-for-xlarge-only, - .show-for-xxlarge-up, - .show-for-xxlarge-only { - display: none !important; } - - /* Specific visibility for tables */ - table.hide-for-small, table.hide-for-small-only, table.hide-for-medium, table.hide-for-medium-down, table.hide-for-medium-only, table.show-for-medium-up, table.show-for-large-up, table.hide-for-large-only, table.show-for-xlarge, table.show-for-xlarge-up, table.show-for-xlarge-only, table.hide-for-xxlarge-up, table.hide-for-xxlarge-only { - display: table; } - - thead.hide-for-small, thead.hide-for-small-only, thead.hide-for-medium, thead.hide-for-medium-down, thead.hide-for-medium-only, thead.show-for-medium-up, thead.show-for-large-up, thead.hide-for-large-only, thead.show-for-xlarge, thead.show-for-xlarge-up, thead.show-for-xlarge-only, thead.hide-for-xxlarge-up, thead.hide-for-xxlarge-only { - display: table-header-group !important; } - - tbody.hide-for-small, tbody.hide-for-small-only, tbody.hide-for-medium, tbody.hide-for-medium-down, tbody.hide-for-medium-only, tbody.show-for-medium-up, tbody.show-for-large-up, tbody.hide-for-large-only, tbody.show-for-xlarge, tbody.show-for-xlarge-up, tbody.show-for-xlarge-only, tbody.hide-for-xxlarge-up, tbody.hide-for-xxlarge-only { - display: table-row-group !important; } - - tr.hide-for-small, tr.hide-for-small-only, tr.hide-for-medium, tr.hide-for-medium-down, tr.hide-for-medium-only, tr.show-for-medium-up, tr.show-for-large-up, tr.hide-for-large-only, tr.show-for-xlarge, tr.show-for-xlarge-up, tr.show-for-xlarge-only, tr.hide-for-xxlarge-up, tr.hide-for-xxlarge-only { - display: table-row !important; } - - td.hide-for-small, td.hide-for-small-only, td.hide-for-medium, td.hide-for-medium-down, td.hide-for-medium-only, td.show-for-medium-up, td.show-for-large-up, td.hide-for-large-only, td.show-for-xlarge, td.show-for-xlarge-up, td.show-for-xlarge-only, td.hide-for-xxlarge-up, td.hide-for-xxlarge-only, - th.hide-for-small, - th.hide-for-small-only, - th.hide-for-medium, - th.hide-for-medium-down, - th.hide-for-medium-only, - th.show-for-medium-up, - th.show-for-large-up, - th.hide-for-large-only, - th.show-for-xlarge, - th.show-for-xlarge-up, - th.show-for-xlarge-only, - th.hide-for-xxlarge-up, - th.hide-for-xxlarge-only { - display: table-cell !important; } } -/* XX-Large Displays: 1920 and up */ -@media only screen and (min-width: 120.063em) { - .hide-for-small, - .hide-for-small-only, - .hide-for-medium, - .hide-for-medium-down, - .hide-for-medium-only, - .show-for-medium-up, - .show-for-large-up, - .hide-for-large-only, - .hide-for-xlarge-only, - .show-for-xlarge-up, - .show-for-xxlarge-up, - .show-for-xxlarge-only { - display: inherit !important; } - - .show-for-small-only, - .show-for-medium, - .show-for-medium-down, - .show-for-medium-only, - .show-for-large, - .show-for-large-only, - .show-for-large-down, - .hide-for-xlarge, - .show-for-xlarge-only, - .hide-for-xxlarge-up, - .hide-for-xxlarge-only { - display: none !important; } - - /* Specific visibility for tables */ - table.hide-for-small, table.hide-for-small-only, table.hide-for-medium, table.hide-for-medium-down, table.hide-for-medium-only, table.show-for-medium-up, table.show-for-large-up, table.hide-for-xlarge-only, table.show-for-xlarge-up, table.show-for-xxlarge-up, table.show-for-xxlarge-only { - display: table; } - - thead.hide-for-small, thead.hide-for-small-only, thead.hide-for-medium, thead.hide-for-medium-down, thead.hide-for-medium-only, thead.show-for-medium-up, thead.show-for-large-up, thead.hide-for-xlarge-only, thead.show-for-xlarge-up, thead.show-for-xxlarge-up, thead.show-for-xxlarge-only { - display: table-header-group !important; } - - tbody.hide-for-small, tbody.hide-for-small-only, tbody.hide-for-medium, tbody.hide-for-medium-down, tbody.hide-for-medium-only, tbody.show-for-medium-up, tbody.show-for-large-up, tbody.hide-for-xlarge-only, tbody.show-for-xlarge-up, tbody.show-for-xxlarge-up, tbody.show-for-xxlarge-only { - display: table-row-group !important; } - - tr.hide-for-small, tr.hide-for-small-only, tr.hide-for-medium, tr.hide-for-medium-down, tr.hide-for-medium-only, tr.show-for-medium-up, tr.show-for-large-up, tr.hide-for-xlarge-only, tr.show-for-xlarge-up, tr.show-for-xxlarge-up, tr.show-for-xxlarge-only { - display: table-row !important; } - - td.hide-for-small, td.hide-for-small-only, td.hide-for-medium, td.hide-for-medium-down, td.hide-for-medium-only, td.show-for-medium-up, td.show-for-large-up, td.hide-for-xlarge-only, td.show-for-xlarge-up, td.show-for-xxlarge-up, td.show-for-xxlarge-only, - th.hide-for-small, - th.hide-for-small-only, - th.hide-for-medium, - th.hide-for-medium-down, - th.hide-for-medium-only, - th.show-for-medium-up, - th.show-for-large-up, - th.hide-for-xlarge-only, - th.show-for-xlarge-up, - th.show-for-xxlarge-up, - th.show-for-xxlarge-only { - display: table-cell !important; } } -/* Orientation targeting */ -.show-for-landscape, -.hide-for-portrait { - display: inherit !important; } - -.hide-for-landscape, -.show-for-portrait { - display: none !important; } - -/* Specific visibility for tables */ -table.hide-for-landscape, table.show-for-portrait { - display: table; } - -thead.hide-for-landscape, thead.show-for-portrait { - display: table-header-group !important; } - -tbody.hide-for-landscape, tbody.show-for-portrait { - display: table-row-group !important; } - -tr.hide-for-landscape, tr.show-for-portrait { - display: table-row !important; } - -td.hide-for-landscape, td.show-for-portrait, -th.hide-for-landscape, -th.show-for-portrait { - display: table-cell !important; } - -@media only screen and (orientation: landscape) { - .show-for-landscape, - .hide-for-portrait { - display: inherit !important; } - - .hide-for-landscape, - .show-for-portrait { - display: none !important; } - - /* Specific visibility for tables */ - table.show-for-landscape, table.hide-for-portrait { - display: table; } - - thead.show-for-landscape, thead.hide-for-portrait { - display: table-header-group !important; } - - tbody.show-for-landscape, tbody.hide-for-portrait { - display: table-row-group !important; } - - tr.show-for-landscape, tr.hide-for-portrait { - display: table-row !important; } - - td.show-for-landscape, td.hide-for-portrait, - th.show-for-landscape, - th.hide-for-portrait { - display: table-cell !important; } } -@media only screen and (orientation: portrait) { - .show-for-portrait, - .hide-for-landscape { - display: inherit !important; } - - .hide-for-portrait, - .show-for-landscape { - display: none !important; } - - /* Specific visibility for tables */ - table.show-for-portrait, table.hide-for-landscape { - display: table; } - - thead.show-for-portrait, thead.hide-for-landscape { - display: table-header-group !important; } - - tbody.show-for-portrait, tbody.hide-for-landscape { - display: table-row-group !important; } - - tr.show-for-portrait, tr.hide-for-landscape { - display: table-row !important; } - - td.show-for-portrait, td.hide-for-landscape, - th.show-for-portrait, - th.hide-for-landscape { - display: table-cell !important; } } -/* Touch-enabled device targeting */ -.show-for-touch { - display: none !important; } - -.hide-for-touch { - display: inherit !important; } - -.touch .show-for-touch { - display: inherit !important; } - -.touch .hide-for-touch { - display: none !important; } - -/* Specific visibility for tables */ -table.hide-for-touch { - display: table; } - -.touch table.show-for-touch { - display: table; } - -thead.hide-for-touch { - display: table-header-group !important; } - -.touch thead.show-for-touch { - display: table-header-group !important; } - -tbody.hide-for-touch { - display: table-row-group !important; } - -.touch tbody.show-for-touch { - display: table-row-group !important; } - -tr.hide-for-touch { - display: table-row !important; } - -.touch tr.show-for-touch { - display: table-row !important; } - -td.hide-for-touch { - display: table-cell !important; } - -.touch td.show-for-touch { - display: table-cell !important; } - -th.hide-for-touch { - display: table-cell !important; } - -.touch th.show-for-touch { - display: table-cell !important; } diff --git a/coin/static/css/foundation.min.css b/coin/static/css/foundation.min.css deleted file mode 100644 index 9a0defd..0000000 --- a/coin/static/css/foundation.min.css +++ /dev/null @@ -1 +0,0 @@ -meta.foundation-version{font-family:"/5.1.0/"}meta.foundation-mq-small{font-family:"/only screen and (max-width: 40em)/";width:0em}meta.foundation-mq-medium{font-family:"/only screen and (min-width:40.063em)/";width:40.063em}meta.foundation-mq-large{font-family:"/only screen and (min-width:64.063em)/";width:64.063em}meta.foundation-mq-xlarge{font-family:"/only screen and (min-width:90.063em)/";width:90.063em}meta.foundation-mq-xxlarge{font-family:"/only screen and (min-width:120.063em)/";width:120.063em}meta.foundation-data-attribute-namespace{font-family:false}html,body{height:100%}*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}html,body{font-size:100%}body{background:#fff;color:#222;padding:0;margin:0;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-weight:normal;font-style:normal;line-height:1;position:relative;cursor:default}a:hover{cursor:pointer}img,object,embed{max-width:100%;height:auto}object,embed{height:100%}img{-ms-interpolation-mode:bicubic}#map_canvas img,#map_canvas embed,#map_canvas object,.map_canvas img,.map_canvas embed,.map_canvas object{max-width:none !important}.left{float:left !important}.right{float:right !important}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{content:" ";display:table}.clearfix:after{clear:both}.hide{display:none}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}img{display:inline-block;vertical-align:middle}textarea{height:auto;min-height:50px}select{width:100%}.row{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1}.row:before,.row:after{content:" ";display:table}.row:after{clear:both}.row.collapse>.column,.row.collapse>.columns{padding-left:0;padding-right:0;float:left}.row.collapse .row{margin-left:0;margin-right:0}.row .row{width:auto;margin-left:-0.9375em;margin-right:-0.9375em;margin-top:0;margin-bottom:0;max-width:none;*zoom:1}.row .row:before,.row .row:after{content:" ";display:table}.row .row:after{clear:both}.row .row.collapse{width:auto;margin:0;max-width:none;*zoom:1}.row .row.collapse:before,.row .row.collapse:after{content:" ";display:table}.row .row.collapse:after{clear:both}.column,.columns{padding-left:0.9375em;padding-right:0.9375em;width:100%;float:left}@media only screen{.column.small-centered,.columns.small-centered{margin-left:auto;margin-right:auto;float:none}.column.small-uncentered,.columns.small-uncentered{margin-left:0;margin-right:0;float:left}.column.small-uncentered.opposite,.columns.small-uncentered.opposite{float:right}.small-push-0{left:0%;right:auto}.small-pull-0{right:0%;left:auto}.small-push-1{left:8.33333%;right:auto}.small-pull-1{right:8.33333%;left:auto}.small-push-2{left:16.66667%;right:auto}.small-pull-2{right:16.66667%;left:auto}.small-push-3{left:25%;right:auto}.small-pull-3{right:25%;left:auto}.small-push-4{left:33.33333%;right:auto}.small-pull-4{right:33.33333%;left:auto}.small-push-5{left:41.66667%;right:auto}.small-pull-5{right:41.66667%;left:auto}.small-push-6{left:50%;right:auto}.small-pull-6{right:50%;left:auto}.small-push-7{left:58.33333%;right:auto}.small-pull-7{right:58.33333%;left:auto}.small-push-8{left:66.66667%;right:auto}.small-pull-8{right:66.66667%;left:auto}.small-push-9{left:75%;right:auto}.small-pull-9{right:75%;left:auto}.small-push-10{left:83.33333%;right:auto}.small-pull-10{right:83.33333%;left:auto}.small-push-11{left:91.66667%;right:auto}.small-pull-11{right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375em;padding-right:0.9375em;float:left}.small-1{width:8.33333%}.small-2{width:16.66667%}.small-3{width:25%}.small-4{width:33.33333%}.small-5{width:41.66667%}.small-6{width:50%}.small-7{width:58.33333%}.small-8{width:66.66667%}.small-9{width:75%}.small-10{width:83.33333%}.small-11{width:91.66667%}.small-12{width:100%}[class*="column"]+[class*="column"]:last-child{float:right}[class*="column"]+[class*="column"].end{float:left}.small-offset-0{margin-left:0% !important}.small-offset-1{margin-left:8.33333% !important}.small-offset-2{margin-left:16.66667% !important}.small-offset-3{margin-left:25% !important}.small-offset-4{margin-left:33.33333% !important}.small-offset-5{margin-left:41.66667% !important}.small-offset-6{margin-left:50% !important}.small-offset-7{margin-left:58.33333% !important}.small-offset-8{margin-left:66.66667% !important}.small-offset-9{margin-left:75% !important}.small-offset-10{margin-left:83.33333% !important}.small-offset-11{margin-left:91.66667% !important}.small-reset-order,.small-reset-order{margin-left:0;margin-right:0;left:auto;right:auto;float:left}}@media only screen and (min-width: 40.063em){.column.medium-centered,.columns.medium-centered{margin-left:auto;margin-right:auto;float:none}.column.medium-uncentered,.columns.medium-uncentered{margin-left:0;margin-right:0;float:left}.column.medium-uncentered.opposite,.columns.medium-uncentered.opposite{float:right}.medium-push-0{left:0%;right:auto}.medium-pull-0{right:0%;left:auto}.medium-push-1{left:8.33333%;right:auto}.medium-pull-1{right:8.33333%;left:auto}.medium-push-2{left:16.66667%;right:auto}.medium-pull-2{right:16.66667%;left:auto}.medium-push-3{left:25%;right:auto}.medium-pull-3{right:25%;left:auto}.medium-push-4{left:33.33333%;right:auto}.medium-pull-4{right:33.33333%;left:auto}.medium-push-5{left:41.66667%;right:auto}.medium-pull-5{right:41.66667%;left:auto}.medium-push-6{left:50%;right:auto}.medium-pull-6{right:50%;left:auto}.medium-push-7{left:58.33333%;right:auto}.medium-pull-7{right:58.33333%;left:auto}.medium-push-8{left:66.66667%;right:auto}.medium-pull-8{right:66.66667%;left:auto}.medium-push-9{left:75%;right:auto}.medium-pull-9{right:75%;left:auto}.medium-push-10{left:83.33333%;right:auto}.medium-pull-10{right:83.33333%;left:auto}.medium-push-11{left:91.66667%;right:auto}.medium-pull-11{right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375em;padding-right:0.9375em;float:left}.medium-1{width:8.33333%}.medium-2{width:16.66667%}.medium-3{width:25%}.medium-4{width:33.33333%}.medium-5{width:41.66667%}.medium-6{width:50%}.medium-7{width:58.33333%}.medium-8{width:66.66667%}.medium-9{width:75%}.medium-10{width:83.33333%}.medium-11{width:91.66667%}.medium-12{width:100%}[class*="column"]+[class*="column"]:last-child{float:right}[class*="column"]+[class*="column"].end{float:left}.medium-offset-0{margin-left:0% !important}.medium-offset-1{margin-left:8.33333% !important}.medium-offset-2{margin-left:16.66667% !important}.medium-offset-3{margin-left:25% !important}.medium-offset-4{margin-left:33.33333% !important}.medium-offset-5{margin-left:41.66667% !important}.medium-offset-6{margin-left:50% !important}.medium-offset-7{margin-left:58.33333% !important}.medium-offset-8{margin-left:66.66667% !important}.medium-offset-9{margin-left:75% !important}.medium-offset-10{margin-left:83.33333% !important}.medium-offset-11{margin-left:91.66667% !important}.medium-reset-order,.medium-reset-order{margin-left:0;margin-right:0;left:auto;right:auto;float:left}.push-0{left:0%;right:auto}.pull-0{right:0%;left:auto}.push-1{left:8.33333%;right:auto}.pull-1{right:8.33333%;left:auto}.push-2{left:16.66667%;right:auto}.pull-2{right:16.66667%;left:auto}.push-3{left:25%;right:auto}.pull-3{right:25%;left:auto}.push-4{left:33.33333%;right:auto}.pull-4{right:33.33333%;left:auto}.push-5{left:41.66667%;right:auto}.pull-5{right:41.66667%;left:auto}.push-6{left:50%;right:auto}.pull-6{right:50%;left:auto}.push-7{left:58.33333%;right:auto}.pull-7{right:58.33333%;left:auto}.push-8{left:66.66667%;right:auto}.pull-8{right:66.66667%;left:auto}.push-9{left:75%;right:auto}.pull-9{right:75%;left:auto}.push-10{left:83.33333%;right:auto}.pull-10{right:83.33333%;left:auto}.push-11{left:91.66667%;right:auto}.pull-11{right:91.66667%;left:auto}}@media only screen and (min-width: 64.063em){.column.large-centered,.columns.large-centered{margin-left:auto;margin-right:auto;float:none}.column.large-uncentered,.columns.large-uncentered{margin-left:0;margin-right:0;float:left}.column.large-uncentered.opposite,.columns.large-uncentered.opposite{float:right}.large-push-0{left:0%;right:auto}.large-pull-0{right:0%;left:auto}.large-push-1{left:8.33333%;right:auto}.large-pull-1{right:8.33333%;left:auto}.large-push-2{left:16.66667%;right:auto}.large-pull-2{right:16.66667%;left:auto}.large-push-3{left:25%;right:auto}.large-pull-3{right:25%;left:auto}.large-push-4{left:33.33333%;right:auto}.large-pull-4{right:33.33333%;left:auto}.large-push-5{left:41.66667%;right:auto}.large-pull-5{right:41.66667%;left:auto}.large-push-6{left:50%;right:auto}.large-pull-6{right:50%;left:auto}.large-push-7{left:58.33333%;right:auto}.large-pull-7{right:58.33333%;left:auto}.large-push-8{left:66.66667%;right:auto}.large-pull-8{right:66.66667%;left:auto}.large-push-9{left:75%;right:auto}.large-pull-9{right:75%;left:auto}.large-push-10{left:83.33333%;right:auto}.large-pull-10{right:83.33333%;left:auto}.large-push-11{left:91.66667%;right:auto}.large-pull-11{right:91.66667%;left:auto}.column,.columns{position:relative;padding-left:0.9375em;padding-right:0.9375em;float:left}.large-1{width:8.33333%}.large-2{width:16.66667%}.large-3{width:25%}.large-4{width:33.33333%}.large-5{width:41.66667%}.large-6{width:50%}.large-7{width:58.33333%}.large-8{width:66.66667%}.large-9{width:75%}.large-10{width:83.33333%}.large-11{width:91.66667%}.large-12{width:100%}[class*="column"]+[class*="column"]:last-child{float:right}[class*="column"]+[class*="column"].end{float:left}.large-offset-0{margin-left:0% !important}.large-offset-1{margin-left:8.33333% !important}.large-offset-2{margin-left:16.66667% !important}.large-offset-3{margin-left:25% !important}.large-offset-4{margin-left:33.33333% !important}.large-offset-5{margin-left:41.66667% !important}.large-offset-6{margin-left:50% !important}.large-offset-7{margin-left:58.33333% !important}.large-offset-8{margin-left:66.66667% !important}.large-offset-9{margin-left:75% !important}.large-offset-10{margin-left:83.33333% !important}.large-offset-11{margin-left:91.66667% !important}.large-reset-order,.large-reset-order{margin-left:0;margin-right:0;left:auto;right:auto;float:left}.push-0{left:0%;right:auto}.pull-0{right:0%;left:auto}.push-1{left:8.33333%;right:auto}.pull-1{right:8.33333%;left:auto}.push-2{left:16.66667%;right:auto}.pull-2{right:16.66667%;left:auto}.push-3{left:25%;right:auto}.pull-3{right:25%;left:auto}.push-4{left:33.33333%;right:auto}.pull-4{right:33.33333%;left:auto}.push-5{left:41.66667%;right:auto}.pull-5{right:41.66667%;left:auto}.push-6{left:50%;right:auto}.pull-6{right:50%;left:auto}.push-7{left:58.33333%;right:auto}.pull-7{right:58.33333%;left:auto}.push-8{left:66.66667%;right:auto}.pull-8{right:66.66667%;left:auto}.push-9{left:75%;right:auto}.pull-9{right:75%;left:auto}.push-10{left:83.33333%;right:auto}.pull-10{right:83.33333%;left:auto}.push-11{left:91.66667%;right:auto}.pull-11{right:91.66667%;left:auto}}meta.foundation-mq-topbar{font-family:"/only screen and (min-width:40.063em)/";width:58.75em}.contain-to-grid{width:100%;background:#333}.contain-to-grid .top-bar{margin-bottom:0}.fixed{width:100%;left:0;position:fixed;top:0;z-index:99}.fixed.expanded:not(.top-bar){overflow-y:auto;height:auto;width:100%;max-height:100%}.fixed.expanded:not(.top-bar) .title-area{position:fixed;width:100%;z-index:99}.fixed.expanded:not(.top-bar) .top-bar-section{z-index:98;margin-top:45px}.top-bar{overflow:hidden;height:45px;line-height:45px;position:relative;background:#333;margin-bottom:0}.top-bar ul{margin-bottom:0;list-style:none}.top-bar .row{max-width:none}.top-bar form,.top-bar input{margin-bottom:0}.top-bar input{height:auto;padding-top:.35rem;padding-bottom:.35rem;font-size:0.75rem}.top-bar .button{padding-top:.45rem;padding-bottom:.35rem;margin-bottom:0;font-size:0.75rem}.top-bar .title-area{position:relative;margin:0}.top-bar .name{height:45px;margin:0;font-size:16px}.top-bar .name h1{line-height:45px;font-size:1.0625rem;margin:0}.top-bar .name h1 a{font-weight:normal;color:#fff;width:50%;display:block;padding:0 15px}.top-bar .toggle-topbar{position:absolute;right:0;top:0}.top-bar .toggle-topbar a{color:#fff;text-transform:uppercase;font-size:0.8125rem;font-weight:bold;position:relative;display:block;padding:0 15px;height:45px;line-height:45px}.top-bar .toggle-topbar.menu-icon{right:15px;top:50%;margin-top:-16px;padding-left:40px}.top-bar .toggle-topbar.menu-icon a{height:34px;line-height:33px;padding:0;padding-right:25px;color:#fff;position:relative}.top-bar .toggle-topbar.menu-icon a::after{content:"";position:absolute;right:0;display:block;width:16px;top:0;height:0;-webkit-box-shadow:0 10px 0 1px #fff,0 16px 0 1px #fff,0 22px 0 1px #fff;box-shadow:0 10px 0 1px #fff,0 16px 0 1px #fff,0 22px 0 1px #fff}.top-bar.expanded{height:auto;background:transparent}.top-bar.expanded .title-area{background:#333}.top-bar.expanded .toggle-topbar a{color:#888}.top-bar.expanded .toggle-topbar a span{-webkit-box-shadow:0 10px 0 1px #888,0 16px 0 1px #888,0 22px 0 1px #888;box-shadow:0 10px 0 1px #888,0 16px 0 1px #888,0 22px 0 1px #888}.top-bar-section{left:0;position:relative;width:auto;-webkit-transition:left 300ms ease-out;-moz-transition:left 300ms ease-out;transition:left 300ms ease-out}.top-bar-section ul{width:100%;height:auto;display:block;background:#333;font-size:16px;margin:0}.top-bar-section .divider,.top-bar-section [role="separator"]{border-top:solid 1px #1a1a1a;clear:both;height:1px;width:100%}.top-bar-section ul li>a{display:block;width:100%;color:#fff;padding:12px 0 12px 0;padding-left:15px;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-size:0.8125rem;font-weight:normal;background:#333}.top-bar-section ul li>a.button{background:#0086a9;font-size:0.8125rem;padding-right:15px;padding-left:15px}.top-bar-section ul li>a.button:hover{background:#00627b}.top-bar-section ul li>a.button.secondary{background:#f60}.top-bar-section ul li>a.button.secondary:hover{background:#e35b00}.top-bar-section ul li>a.button.success{background:#20ba44}.top-bar-section ul li>a.button.success:hover{background:#199336}.top-bar-section ul li>a.button.alert{background:#c60f13}.top-bar-section ul li>a.button.alert:hover{background:#a20c10}.top-bar-section ul li:hover>a{background:#272727;color:#fff}.top-bar-section ul li.active>a{background:#0086a9;color:#fff}.top-bar-section ul li.active>a:hover{background:#007391;color:#fff}.top-bar-section .has-form{padding:15px}.top-bar-section .has-dropdown{position:relative}.top-bar-section .has-dropdown>a:after{content:"";display:block;width:0;height:0;border:inset 5px;border-color:transparent transparent transparent rgba(255,255,255,0.4);border-left-style:solid;margin-right:15px;margin-top:-4.5px;position:absolute;top:50%;right:0}.top-bar-section .has-dropdown.moved{position:static}.top-bar-section .has-dropdown.moved>.dropdown{display:block}.top-bar-section .dropdown{position:absolute;left:100%;top:0;display:none;z-index:99}.top-bar-section .dropdown li{width:100%;height:auto}.top-bar-section .dropdown li a{font-weight:normal;padding:8px 15px}.top-bar-section .dropdown li a.parent-link{font-weight:normal}.top-bar-section .dropdown li.title h5{margin-bottom:0}.top-bar-section .dropdown li.title h5 a{color:#fff;line-height:22.5px;display:block}.top-bar-section .dropdown li.has-form{padding:8px 15px}.top-bar-section .dropdown li .button{top:auto}.top-bar-section .dropdown label{padding:8px 15px 2px;margin-bottom:0;text-transform:uppercase;color:#777;font-weight:bold;font-size:0.625rem}.js-generated{display:block}@media only screen and (min-width: 40.063em){.top-bar{background:#333;*zoom:1;overflow:visible}.top-bar:before,.top-bar:after{content:" ";display:table}.top-bar:after{clear:both}.top-bar .toggle-topbar{display:none}.top-bar .title-area{float:left}.top-bar .name h1 a{width:auto}.top-bar input,.top-bar .button{font-size:0.875rem;position:relative;top:7px}.top-bar.expanded{background:#333}.contain-to-grid .top-bar{max-width:62.5em;margin:0 auto;margin-bottom:0}.top-bar-section{-webkit-transition:none 0 0;-moz-transition:none 0 0;transition:none 0 0;left:0 !important}.top-bar-section ul{width:auto;height:auto !important;display:inline}.top-bar-section ul li{float:left}.top-bar-section ul li .js-generated{display:none}.top-bar-section li.hover>a:not(.button){background:#272727;color:#fff}.top-bar-section li:not(.has-form) a:not(.button){padding:0 15px;line-height:45px;background:#333}.top-bar-section li:not(.has-form) a:not(.button):hover{background:#272727}.top-bar-section li.active:not(.has-form) a:not(.button){padding:0 15px;line-height:45px;color:#fff;background:#0086a9}.top-bar-section li.active:not(.has-form) a:not(.button):hover{background:#007391}.top-bar-section .has-dropdown>a{padding-right:35px !important}.top-bar-section .has-dropdown>a:after{content:"";display:block;width:0;height:0;border:inset 5px;border-color:rgba(255,255,255,0.4) transparent transparent transparent;border-top-style:solid;margin-top:-2.5px;top:22.5px}.top-bar-section .has-dropdown.moved{position:relative}.top-bar-section .has-dropdown.moved>.dropdown{display:none}.top-bar-section .has-dropdown.hover>.dropdown,.top-bar-section .has-dropdown.not-click:hover>.dropdown{display:block}.top-bar-section .has-dropdown .dropdown li.has-dropdown>a:after{border:none;content:"\00bb";top:1rem;margin-top:-2px;right:5px;line-height:1.2}.top-bar-section .dropdown{left:0;top:auto;background:transparent;min-width:100%}.top-bar-section .dropdown li a{color:#fff;line-height:1;white-space:nowrap;padding:12px 15px;background:#333}.top-bar-section .dropdown li label{white-space:nowrap;background:#333}.top-bar-section .dropdown li .dropdown{left:100%;top:0}.top-bar-section>ul>.divider,.top-bar-section>ul>[role="separator"]{border-bottom:none;border-top:none;border-right:solid 1px #4e4e4e;clear:none;height:45px;width:0}.top-bar-section .has-form{background:#333;padding:0 15px;height:45px}.top-bar-section .right li .dropdown{left:auto;right:0}.top-bar-section .right li .dropdown li .dropdown{right:100%}.top-bar-section .left li .dropdown{right:auto;left:0}.top-bar-section .left li .dropdown li .dropdown{left:100%}.no-js .top-bar-section ul li:hover>a{background:#272727;color:#fff}.no-js .top-bar-section ul li:active>a{background:#0086a9;color:#fff}.no-js .top-bar-section .has-dropdown:hover>.dropdown{display:block}}.breadcrumbs{display:block;padding:0.5625rem 0.875rem 0.5625rem;overflow:hidden;margin-left:0;list-style:none;border-style:solid;border-width:1px;background-color:#ffba8c;border-color:#ffa265;-webkit-border-radius:3px;border-radius:3px}.breadcrumbs>*{margin:0;float:left;font-size:0.6875rem;text-transform:uppercase}.breadcrumbs>*:hover a,.breadcrumbs>*:focus a{text-decoration:underline}.breadcrumbs>* a,.breadcrumbs>* span{text-transform:uppercase;color:#0086a9}.breadcrumbs>*.current{cursor:default;color:#333}.breadcrumbs>*.current a{cursor:default;color:#333}.breadcrumbs>*.current:hover,.breadcrumbs>*.current:hover a,.breadcrumbs>*.current:focus,.breadcrumbs>*.current:focus a{text-decoration:none}.breadcrumbs>*.unavailable{color:#999}.breadcrumbs>*.unavailable a{color:#999}.breadcrumbs>*.unavailable:hover,.breadcrumbs>*.unavailable:hover a,.breadcrumbs>*.unavailable:focus,.breadcrumbs>*.unavailable a:focus{text-decoration:none;color:#999;cursor:default}.breadcrumbs>*:before{content:"/";color:#aaa;margin:0 0.75rem;position:relative;top:1px}.breadcrumbs>*:first-child:before{content:" ";margin:0}.alert-box{border-style:solid;border-width:1px;display:block;font-weight:normal;margin-bottom:1.25rem;position:relative;padding:0.875rem 1.5rem 0.875rem 0.875rem;font-size:0.8125rem;background-color:#0086a9;border-color:#007391;color:#fff}.alert-box .close{font-size:1.375rem;padding:9px 6px 4px;line-height:0;position:absolute;top:50%;margin-top:-0.6875rem;right:0.25rem;color:#333;opacity:0.3}.alert-box .close:hover,.alert-box .close:focus{opacity:0.5}.alert-box.radius{-webkit-border-radius:3px;border-radius:3px}.alert-box.round{-webkit-border-radius:1000px;border-radius:1000px}.alert-box.success{background-color:#20ba44;border-color:#1ca03a;color:#fff}.alert-box.alert{background-color:#c60f13;border-color:#aa0d10;color:#fff}.alert-box.secondary{background-color:#f60;border-color:#db5800;color:#fff}.alert-box.warning{background-color:#f08a24;border-color:#de770f;color:#fff}.alert-box.info{background-color:#a0d3e8;border-color:#74bfdd;color:#572300}.inline-list{margin:0 auto 1.0625rem auto;margin-left:-1.375rem;margin-right:0;padding:0;list-style:none;overflow:hidden}.inline-list>li{list-style:none;float:left;margin-left:1.375rem;display:block}.inline-list>li>*{display:block}button,.button{border-style:solid;border-width:0px;cursor:pointer;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-weight:normal;line-height:normal;margin:0 0 1.25rem;position:relative;text-decoration:none;text-align:center;display:inline-block;padding-top:1rem;padding-right:2rem;padding-bottom:1.0625rem;padding-left:2rem;font-size:1rem;background-color:#0086a9;border-color:#006b87;color:#fff;-webkit-transition:background-color 300ms ease-out;-moz-transition:background-color 300ms ease-out;transition:background-color 300ms ease-out;padding-top:1.0625rem;padding-bottom:1rem;-webkit-appearance:none;border:none;font-weight:normal !important}button:hover,button:focus,.button:hover,.button:focus{background-color:#006b87}button:hover,button:focus,.button:hover,.button:focus{color:#fff}button.secondary,.button.secondary{background-color:#f60;border-color:#cc5200;color:#fff}button.secondary:hover,button.secondary:focus,.button.secondary:hover,.button.secondary:focus{background-color:#cc5200}button.secondary:hover,button.secondary:focus,.button.secondary:hover,.button.secondary:focus{color:#fff}button.success,.button.success{background-color:#20ba44;border-color:#1a9536;color:#fff}button.success:hover,button.success:focus,.button.success:hover,.button.success:focus{background-color:#1a9536}button.success:hover,button.success:focus,.button.success:hover,.button.success:focus{color:#fff}button.alert,.button.alert{background-color:#c60f13;border-color:#9e0c0f;color:#fff}button.alert:hover,button.alert:focus,.button.alert:hover,.button.alert:focus{background-color:#9e0c0f}button.alert:hover,button.alert:focus,.button.alert:hover,.button.alert:focus{color:#fff}button.large,.button.large{padding-top:1.125rem;padding-right:2.25rem;padding-bottom:1.1875rem;padding-left:2.25rem;font-size:1.25rem}button.small,.button.small{padding-top:0.875rem;padding-right:1.75rem;padding-bottom:0.9375rem;padding-left:1.75rem;font-size:0.8125rem}button.tiny,.button.tiny{padding-top:0.625rem;padding-right:1.25rem;padding-bottom:0.6875rem;padding-left:1.25rem;font-size:0.6875rem}button.expand,.button.expand{padding-right:0;padding-left:0;width:100%}button.left-align,.button.left-align{text-align:left;text-indent:0.75rem}button.right-align,.button.right-align{text-align:right;padding-right:0.75rem}button.radius,.button.radius{-webkit-border-radius:3px;border-radius:3px}button.round,.button.round{-webkit-border-radius:1000px;border-radius:1000px}button.disabled,button[disabled],.button.disabled,.button[disabled]{background-color:#0086a9;border-color:#006b87;color:#fff;cursor:default;opacity:0.7;-webkit-box-shadow:none;box-shadow:none}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{background-color:#006b87}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{color:#fff}button.disabled:hover,button.disabled:focus,button[disabled]:hover,button[disabled]:focus,.button.disabled:hover,.button.disabled:focus,.button[disabled]:hover,.button[disabled]:focus{background-color:#0086a9}button.disabled.secondary,button[disabled].secondary,.button.disabled.secondary,.button[disabled].secondary{background-color:#f60;border-color:#cc5200;color:#fff;cursor:default;opacity:0.7;-webkit-box-shadow:none;box-shadow:none}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{background-color:#cc5200}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{color:#fff}button.disabled.secondary:hover,button.disabled.secondary:focus,button[disabled].secondary:hover,button[disabled].secondary:focus,.button.disabled.secondary:hover,.button.disabled.secondary:focus,.button[disabled].secondary:hover,.button[disabled].secondary:focus{background-color:#f60}button.disabled.success,button[disabled].success,.button.disabled.success,.button[disabled].success{background-color:#20ba44;border-color:#1a9536;color:#fff;cursor:default;opacity:0.7;-webkit-box-shadow:none;box-shadow:none}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{background-color:#1a9536}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{color:#fff}button.disabled.success:hover,button.disabled.success:focus,button[disabled].success:hover,button[disabled].success:focus,.button.disabled.success:hover,.button.disabled.success:focus,.button[disabled].success:hover,.button[disabled].success:focus{background-color:#20ba44}button.disabled.alert,button[disabled].alert,.button.disabled.alert,.button[disabled].alert{background-color:#c60f13;border-color:#9e0c0f;color:#fff;cursor:default;opacity:0.7;-webkit-box-shadow:none;box-shadow:none}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{background-color:#9e0c0f}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{color:#fff}button.disabled.alert:hover,button.disabled.alert:focus,button[disabled].alert:hover,button[disabled].alert:focus,.button.disabled.alert:hover,.button.disabled.alert:focus,.button[disabled].alert:hover,.button[disabled].alert:focus{background-color:#c60f13}@media only screen and (min-width: 40.063em){button,.button{display:inline-block}}.button-group{list-style:none;margin:0;left:0;*zoom:1}.button-group:before,.button-group:after{content:" ";display:table}.button-group:after{clear:both}.button-group li{margin:0;float:left}.button-group li>button,.button-group li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group li:first-child button,.button-group li:first-child .button{border-left:0}.button-group li:first-child{margin-left:0}.button-group.radius>*>button,.button-group.radius>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.radius>*:first-child button,.button-group.radius>*:first-child .button{border-left:0}.button-group.radius>*:first-child,.button-group.radius>*:first-child>a,.button-group.radius>*:first-child>button,.button-group.radius>*:first-child>.button{-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.button-group.radius>*:last-child,.button-group.radius>*:last-child>a,.button-group.radius>*:last-child>button,.button-group.radius>*:last-child>.button{-moz-border-radius-bottomright:3px;-moz-border-radius-topright:3px;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.button-group.round>*>button,.button-group.round>* .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.round>*:first-child button,.button-group.round>*:first-child .button{border-left:0}.button-group.round>*:first-child,.button-group.round>*:first-child>a,.button-group.round>*:first-child>button,.button-group.round>*:first-child>.button{-moz-border-radius-bottomleft:1000px;-moz-border-radius-topleft:1000px;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.button-group.round>*:last-child,.button-group.round>*:last-child>a,.button-group.round>*:last-child>button,.button-group.round>*:last-child>.button{-moz-border-radius-bottomright:1000px;-moz-border-radius-topright:1000px;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}.button-group.even-2 li{width:50%}.button-group.even-2 li>button,.button-group.even-2 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-2 li:first-child button,.button-group.even-2 li:first-child .button{border-left:0}.button-group.even-2 li button,.button-group.even-2 li .button{width:100%}.button-group.even-3 li{width:33.33333%}.button-group.even-3 li>button,.button-group.even-3 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-3 li:first-child button,.button-group.even-3 li:first-child .button{border-left:0}.button-group.even-3 li button,.button-group.even-3 li .button{width:100%}.button-group.even-4 li{width:25%}.button-group.even-4 li>button,.button-group.even-4 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-4 li:first-child button,.button-group.even-4 li:first-child .button{border-left:0}.button-group.even-4 li button,.button-group.even-4 li .button{width:100%}.button-group.even-5 li{width:20%}.button-group.even-5 li>button,.button-group.even-5 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-5 li:first-child button,.button-group.even-5 li:first-child .button{border-left:0}.button-group.even-5 li button,.button-group.even-5 li .button{width:100%}.button-group.even-6 li{width:16.66667%}.button-group.even-6 li>button,.button-group.even-6 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-6 li:first-child button,.button-group.even-6 li:first-child .button{border-left:0}.button-group.even-6 li button,.button-group.even-6 li .button{width:100%}.button-group.even-7 li{width:14.28571%}.button-group.even-7 li>button,.button-group.even-7 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-7 li:first-child button,.button-group.even-7 li:first-child .button{border-left:0}.button-group.even-7 li button,.button-group.even-7 li .button{width:100%}.button-group.even-8 li{width:12.5%}.button-group.even-8 li>button,.button-group.even-8 li .button{border-left:1px solid;border-color:rgba(255,255,255,0.5)}.button-group.even-8 li:first-child button,.button-group.even-8 li:first-child .button{border-left:0}.button-group.even-8 li button,.button-group.even-8 li .button{width:100%}.button-bar{*zoom:1}.button-bar:before,.button-bar:after{content:" ";display:table}.button-bar:after{clear:both}.button-bar .button-group{float:left;margin-right:0.625rem}.button-bar .button-group div{overflow:hidden}.panel{border-style:solid;border-width:1px;border-color:#d8d8d8;margin-bottom:1.25rem;padding:1.25rem;background:#f2f2f2}.panel>:first-child{margin-top:0}.panel>:last-child{margin-bottom:0}.panel h1,.panel h2,.panel h3,.panel h4,.panel h5,.panel h6,.panel p{color:#333}.panel h1,.panel h2,.panel h3,.panel h4,.panel h5,.panel h6{line-height:1;margin-bottom:0.625rem}.panel h1.subheader,.panel h2.subheader,.panel h3.subheader,.panel h4.subheader,.panel h5.subheader,.panel h6.subheader{line-height:1.4}.panel.callout{border-style:solid;border-width:1px;border-color:#b5f0ff;margin-bottom:1.25rem;padding:1.25rem;background:#ebfbff}.panel.callout>:first-child{margin-top:0}.panel.callout>:last-child{margin-bottom:0}.panel.callout h1,.panel.callout h2,.panel.callout h3,.panel.callout h4,.panel.callout h5,.panel.callout h6,.panel.callout p{color:#333}.panel.callout h1,.panel.callout h2,.panel.callout h3,.panel.callout h4,.panel.callout h5,.panel.callout h6{line-height:1;margin-bottom:0.625rem}.panel.callout h1.subheader,.panel.callout h2.subheader,.panel.callout h3.subheader,.panel.callout h4.subheader,.panel.callout h5.subheader,.panel.callout h6.subheader{line-height:1.4}.panel.callout a{color:#0086a9}.panel.radius{-webkit-border-radius:3px;border-radius:3px}.dropdown.button,button.dropdown{position:relative;padding-right:3.5625rem}.dropdown.button:before,button.dropdown:before{position:absolute;content:"";width:0;height:0;display:block;border-style:solid;border-color:#fff transparent transparent transparent;top:50%}.dropdown.button:before,button.dropdown:before{border-width:0.375rem;right:1.40625rem;margin-top:-0.15625rem}.dropdown.button:before,button.dropdown:before{border-color:#fff transparent transparent transparent}.dropdown.button.tiny,button.dropdown.tiny{padding-right:2.625rem}.dropdown.button.tiny:before,button.dropdown.tiny:before{border-width:0.375rem;right:1.125rem;margin-top:-0.125rem}.dropdown.button.tiny:before,button.dropdown.tiny:before{border-color:#fff transparent transparent transparent}.dropdown.button.small,button.dropdown.small{padding-right:3.0625rem}.dropdown.button.small:before,button.dropdown.small:before{border-width:0.4375rem;right:1.3125rem;margin-top:-0.15625rem}.dropdown.button.small:before,button.dropdown.small:before{border-color:#fff transparent transparent transparent}.dropdown.button.large,button.dropdown.large{padding-right:3.625rem}.dropdown.button.large:before,button.dropdown.large:before{border-width:0.3125rem;right:1.71875rem;margin-top:-0.15625rem}.dropdown.button.large:before,button.dropdown.large:before{border-color:#fff transparent transparent transparent}.dropdown.button.secondary:before,button.dropdown.secondary:before{border-color:#333 transparent transparent transparent}.th{line-height:0;display:inline-block;border:solid 4px #fff;max-width:100%;-webkit-box-shadow:0 0 0 1px rgba(0,0,0,0.2);box-shadow:0 0 0 1px rgba(0,0,0,0.2);-webkit-transition:all 200ms ease-out;-moz-transition:all 200ms ease-out;transition:all 200ms ease-out}.th:hover,.th:focus{-webkit-box-shadow:0 0 6px 1px rgba(0,134,169,0.5);box-shadow:0 0 6px 1px rgba(0,134,169,0.5)}.th.radius{-webkit-border-radius:3px;border-radius:3px}.pricing-table{border:solid 1px #ddd;margin-left:0;margin-bottom:1.25rem}.pricing-table *{list-style:none;line-height:1}.pricing-table .title{background-color:#333;padding:0.9375rem 1.25rem;text-align:center;color:#eee;font-weight:normal;font-size:1rem;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif}.pricing-table .price{background-color:#f6f6f6;padding:0.9375rem 1.25rem;text-align:center;color:#333;font-weight:normal;font-size:2rem;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif}.pricing-table .description{background-color:#fff;padding:0.9375rem;text-align:center;color:#777;font-size:0.75rem;font-weight:normal;line-height:1.4;border-bottom:dotted 1px #ddd}.pricing-table .bullet-item{background-color:#fff;padding:0.9375rem;text-align:center;color:#333;font-size:0.875rem;font-weight:normal;border-bottom:dotted 1px #ddd}.pricing-table .cta-button{background-color:#fff;text-align:center;padding:1.25rem 1.25rem 0}@-webkit-keyframes rotate{from{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}@-moz-keyframes rotate{from{-moz-transform:rotate(0deg)}to{-moz-transform:rotate(360deg)}}@-o-keyframes rotate{from{-o-transform:rotate(0deg)}to{-o-transform:rotate(360deg)}}@keyframes rotate{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}.slideshow-wrapper{position:relative}.slideshow-wrapper ul{list-style-type:none;margin:0}.slideshow-wrapper ul li,.slideshow-wrapper ul li .orbit-caption{display:none}.slideshow-wrapper ul li:first-child{display:block}.slideshow-wrapper .orbit-container{background-color:transparent}.slideshow-wrapper .orbit-container li{display:block}.slideshow-wrapper .orbit-container li .orbit-caption{display:block}.preloader{display:block;width:40px;height:40px;position:absolute;top:50%;left:50%;margin-top:-20px;margin-left:-20px;border:solid 3px;border-color:#555 #fff;-webkit-border-radius:1000px;border-radius:1000px;-webkit-animation-name:rotate;-webkit-animation-duration:1.5s;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear;-moz-animation-name:rotate;-moz-animation-duration:1.5s;-moz-animation-iteration-count:infinite;-moz-animation-timing-function:linear;-o-animation-name:rotate;-o-animation-duration:1.5s;-o-animation-iteration-count:infinite;-o-animation-timing-function:linear;animation-name:rotate;animation-duration:1.5s;animation-iteration-count:infinite;animation-timing-function:linear}.orbit-container{overflow:hidden;width:100%;position:relative;background:none}.orbit-container .orbit-slides-container{list-style:none;margin:0;padding:0;position:relative;-webkit-transform:translateZ(0)}.orbit-container .orbit-slides-container img{display:block;max-width:100%}.orbit-container .orbit-slides-container>*{position:absolute;top:0;width:100%;margin-left:100%}.orbit-container .orbit-slides-container>*:first-child{margin-left:0%}.orbit-container .orbit-slides-container>* .orbit-caption{position:absolute;bottom:0;background-color:rgba(51,51,51,0.8);color:#fff;width:100%;padding:0.625rem 0.875rem;font-size:0.875rem}.orbit-container .orbit-slide-number{position:absolute;top:10px;left:10px;font-size:12px;color:#fff;background:rgba(0,0,0,0);z-index:10}.orbit-container .orbit-slide-number span{font-weight:700;padding:0.3125rem}.orbit-container .orbit-timer{position:absolute;top:12px;right:10px;height:6px;width:100px;z-index:10}.orbit-container .orbit-timer .orbit-progress{height:3px;background-color:rgba(255,255,255,0.3);display:block;width:0%;position:relative;right:20px;top:5px}.orbit-container .orbit-timer>span{display:none;position:absolute;top:0px;right:0;width:11px;height:14px;border:solid 4px #fff;border-top:none;border-bottom:none}.orbit-container .orbit-timer.paused>span{right:-4px;top:0px;width:11px;height:14px;border:inset 8px;border-right-style:solid;border-color:transparent transparent transparent #fff}.orbit-container .orbit-timer.paused>span.dark{border-color:transparent transparent transparent #333}.orbit-container:hover .orbit-timer>span{display:block}.orbit-container .orbit-prev,.orbit-container .orbit-next{position:absolute;top:45%;margin-top:-25px;width:36px;height:60px;line-height:50px;color:white;background-color:none;text-indent:-9999px !important;z-index:10}.orbit-container .orbit-prev:hover,.orbit-container .orbit-next:hover{background-color:rgba(0,0,0,0.3)}.orbit-container .orbit-prev>span,.orbit-container .orbit-next>span{position:absolute;top:50%;margin-top:-10px;display:block;width:0;height:0;border:inset 10px}.orbit-container .orbit-prev{left:0}.orbit-container .orbit-prev>span{border-right-style:solid;border-color:transparent;border-right-color:#fff}.orbit-container .orbit-prev:hover>span{border-right-color:#fff}.orbit-container .orbit-next{right:0}.orbit-container .orbit-next>span{border-color:transparent;border-left-style:solid;border-left-color:#fff;left:50%;margin-left:-4px}.orbit-container .orbit-next:hover>span{border-left-color:#fff}.orbit-bullets-container{text-align:center}.orbit-bullets{margin:0 auto 30px auto;overflow:hidden;position:relative;top:10px;float:none;text-align:center;display:block}.orbit-bullets li{display:inline-block;width:0.5625rem;height:0.5625rem;background:#ccc;float:none;margin-right:6px;-webkit-border-radius:1000px;border-radius:1000px}.orbit-bullets li.active{background:#999}.orbit-bullets li:last-child{margin-right:0}.touch .orbit-container .orbit-prev,.touch .orbit-container .orbit-next{display:none}.touch .orbit-bullets{display:none}@media only screen and (min-width: 40.063em){.touch .orbit-container .orbit-prev,.touch .orbit-container .orbit-next{display:inherit}.touch .orbit-bullets{display:block}}@media only screen and (max-width: 40em){.orbit-stack-on-small .orbit-slides-container{height:auto !important}.orbit-stack-on-small .orbit-slides-container>*{position:relative;margin-left:0% !important}.orbit-stack-on-small .orbit-timer,.orbit-stack-on-small .orbit-next,.orbit-stack-on-small .orbit-prev,.orbit-stack-on-small .orbit-bullets{display:none}}[data-magellan-expedition]{background:#fff;z-index:50;min-width:100%;padding:10px}[data-magellan-expedition] .sub-nav{margin-bottom:0}[data-magellan-expedition] .sub-nav dd{margin-bottom:0}[data-magellan-expedition] .sub-nav a{line-height:1.8em}.tabs{*zoom:1;margin-bottom:0 !important}.tabs:before,.tabs:after{content:" ";display:table}.tabs:after{clear:both}.tabs dd{position:relative;margin-bottom:0 !important;top:1px;float:left}.tabs dd>a{display:block;background:#efefef;color:#222;padding-top:1rem;padding-right:2rem;padding-bottom:1.0625rem;padding-left:2rem;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-size:1rem}.tabs dd>a:hover{background:#e1e1e1}.tabs dd.active a{background:#fff}.tabs.radius dd:first-child a{-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.tabs.radius dd:last-child a{-moz-border-radius-bottomright:3px;-moz-border-radius-topright:3px;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.tabs.vertical dd{position:inherit;float:none;display:block;top:auto}.tabs-content{*zoom:1;margin-bottom:1.5rem;width:100%}.tabs-content:before,.tabs-content:after{content:" ";display:table}.tabs-content:after{clear:both}.tabs-content>.content{display:none;float:left;padding:0.9375em 0;width:100%}.tabs-content>.content.active{display:block}.tabs-content>.content.contained{padding:0.9375em}.tabs-content.vertical{display:block}.tabs-content.vertical>.content{padding:0 0.9375em}@media only screen and (min-width: 40.063em){.tabs.vertical{width:20%;float:left;margin-bottom:1.25rem}.tabs-content.vertical{width:80%;float:left;margin-left:-1px}}.side-nav{display:block;margin:0;padding:0.875rem 0;list-style-type:none;list-style-position:inside;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif}.side-nav li{margin:0 0 0.4375rem 0;font-size:0.875rem}.side-nav li a:not(.button){display:block;color:#0086a9}.side-nav li a:not(.button):hover,.side-nav li a:not(.button):focus{color:#10ceff}.side-nav li.active>a:first-child:not(.button){color:#10ceff;font-weight:normal;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif}.side-nav li.divider{border-top:1px solid;height:0;padding:0;list-style:none;border-top-color:#fff}.accordion{*zoom:1;margin-bottom:0}.accordion:before,.accordion:after{content:" ";display:table}.accordion:after{clear:both}.accordion dd{display:block;margin-bottom:0 !important}.accordion dd.active a{background:#e8e8e8}.accordion dd>a{background:#efefef;color:#222;padding:1rem;display:block;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-size:1rem}.accordion dd>a:hover{background:#e3e3e3}.accordion .content{display:none;padding:0.9375em}.accordion .content.active{display:block;background:#fff}.text-left{text-align:left !important}.text-right{text-align:right !important}.text-center{text-align:center !important}.text-justify{text-align:justify !important}@media only screen and (max-width: 40em){.small-only-text-left{text-align:left !important}.small-only-text-right{text-align:right !important}.small-only-text-center{text-align:center !important}.small-only-text-justify{text-align:justify !important}}@media only screen{.small-text-left{text-align:left !important}.small-text-right{text-align:right !important}.small-text-center{text-align:center !important}.small-text-justify{text-align:justify !important}}@media only screen and (min-width: 40.063em) and (max-width: 64em){.medium-only-text-left{text-align:left !important}.medium-only-text-right{text-align:right !important}.medium-only-text-center{text-align:center !important}.medium-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 40.063em){.medium-text-left{text-align:left !important}.medium-text-right{text-align:right !important}.medium-text-center{text-align:center !important}.medium-text-justify{text-align:justify !important}}@media only screen and (min-width: 64.063em) and (max-width: 90em){.large-only-text-left{text-align:left !important}.large-only-text-right{text-align:right !important}.large-only-text-center{text-align:center !important}.large-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 64.063em){.large-text-left{text-align:left !important}.large-text-right{text-align:right !important}.large-text-center{text-align:center !important}.large-text-justify{text-align:justify !important}}@media only screen and (min-width: 90.063em) and (max-width: 120em){.xlarge-only-text-left{text-align:left !important}.xlarge-only-text-right{text-align:right !important}.xlarge-only-text-center{text-align:center !important}.xlarge-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 90.063em){.xlarge-text-left{text-align:left !important}.xlarge-text-right{text-align:right !important}.xlarge-text-center{text-align:center !important}.xlarge-text-justify{text-align:justify !important}}@media only screen and (min-width: 120.063em) and (max-width: 99999999em){.xxlarge-only-text-left{text-align:left !important}.xxlarge-only-text-right{text-align:right !important}.xxlarge-only-text-center{text-align:center !important}.xxlarge-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 120.063em){.xxlarge-text-left{text-align:left !important}.xxlarge-text-right{text-align:right !important}.xxlarge-text-center{text-align:center !important}.xxlarge-text-justify{text-align:justify !important}}div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}a{color:#0086a9;text-decoration:none;line-height:inherit}a:hover,a:focus{color:#007391}a img{border:none}p{font-family:inherit;font-weight:normal;font-size:1rem;line-height:1.6;margin-bottom:1.25rem;text-rendering:optimizeLegibility}p.lead{font-size:1.21875rem;line-height:1.6}p aside{font-size:0.875rem;line-height:1.35;font-style:italic}h1,h2,h3,h4,h5,h6{font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-weight:normal;font-style:normal;color:#222;text-rendering:optimizeLegibility;margin-top:0.2rem;margin-bottom:0.5rem;line-height:1.4}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-size:60%;color:#6f6f6f;line-height:0}h1{font-size:2.125rem}h2{font-size:1.6875rem}h3{font-size:1.375rem}h4{font-size:1.125rem}h5{font-size:1.125rem}h6{font-size:1rem}.subheader{line-height:1.4;color:#6f6f6f;font-weight:normal;margin-top:0.2rem;margin-bottom:0.5rem}hr{border:solid #ddd;border-width:1px 0 0;clear:both;margin:1.25rem 0 1.1875rem;height:0}em,i{font-style:italic;line-height:inherit}strong,b{font-weight:bold;line-height:inherit}small{font-size:60%;line-height:inherit}code{font-family:Consolas,"Liberation Mono",Courier,monospace;font-weight:bold;color:#910b0e}ul,ol,dl{font-size:1rem;line-height:1.6;margin-bottom:1.25rem;list-style-position:outside;font-family:inherit}ul{margin-left:1.1rem}ul.no-bullet{margin-left:0}ul.no-bullet li ul,ul.no-bullet li ol{margin-left:1.25rem;margin-bottom:0;list-style:none}ul li ul,ul li ol{margin-left:1.25rem;margin-bottom:0}ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}ul.square{list-style-type:square;margin-left:1.1rem}ul.circle{list-style-type:circle;margin-left:1.1rem}ul.disc{list-style-type:disc;margin-left:1.1rem}ul.no-bullet{list-style:none}ol{margin-left:1.4rem}ol li ul,ol li ol{margin-left:1.25rem;margin-bottom:0}dl dt{margin-bottom:0.3rem;font-weight:bold}dl dd{margin-bottom:0.75rem}abbr,acronym{text-transform:uppercase;font-size:90%;color:#222;border-bottom:1px dotted #ddd;cursor:help}abbr{text-transform:none}blockquote{margin:0 0 1.25rem;padding:0.5625rem 1.25rem 0 1.1875rem;border-left:1px solid #ddd}blockquote cite{display:block;font-size:0.8125rem;color:#555}blockquote cite:before{content:"\2014 \0020"}blockquote cite a,blockquote cite a:visited{color:#555}blockquote,blockquote p{line-height:1.6;color:#6f6f6f}.vcard{display:inline-block;margin:0 0 1.25rem 0;border:1px solid #ddd;padding:0.625rem 0.75rem}.vcard li{margin:0;display:block}.vcard .fn{font-weight:bold;font-size:0.9375rem}.vevent .summary{font-weight:bold}.vevent abbr{cursor:default;text-decoration:none;font-weight:bold;border:none;padding:0 0.0625rem}@media only screen and (min-width: 40.063em){h1,h2,h3,h4,h5,h6{line-height:1.4}h1{font-size:2.75rem}h2{font-size:2.3125rem}h3{font-size:1.6875rem}h4{font-size:1.4375rem}}.print-only{display:none !important}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.hide-on-print{display:none !important}.print-only{display:block !important}.hide-for-print{display:none !important}.show-for-print{display:inherit !important}}.split.button{position:relative;padding-right:5.0625rem}.split.button span{display:block;height:100%;position:absolute;right:0;top:0;border-left:solid 1px}.split.button span:before{position:absolute;content:"";width:0;height:0;display:block;border-style:inset;top:50%;left:50%}.split.button span:active{background-color:rgba(0,0,0,0.1)}.split.button span{border-left-color:rgba(255,255,255,0.5)}.split.button span{width:3.09375rem}.split.button span:before{border-top-style:solid;border-width:0.375rem;top:48%;margin-left:-0.375rem}.split.button span:before{border-color:#fff transparent transparent transparent}.split.button.secondary span{border-left-color:rgba(255,255,255,0.5)}.split.button.secondary span:before{border-color:#fff transparent transparent transparent}.split.button.alert span{border-left-color:rgba(255,255,255,0.5)}.split.button.success span{border-left-color:rgba(255,255,255,0.5)}.split.button.tiny{padding-right:3.75rem}.split.button.tiny span{width:2.25rem}.split.button.tiny span:before{border-top-style:solid;border-width:0.375rem;top:48%;margin-left:-0.375rem}.split.button.small{padding-right:4.375rem}.split.button.small span{width:2.625rem}.split.button.small span:before{border-top-style:solid;border-width:0.4375rem;top:48%;margin-left:-0.375rem}.split.button.large{padding-right:5.5rem}.split.button.large span{width:3.4375rem}.split.button.large span:before{border-top-style:solid;border-width:0.3125rem;top:48%;margin-left:-0.375rem}.split.button.expand{padding-left:2rem}.split.button.secondary span:before{border-color:#333 transparent transparent transparent}.split.button.radius span{-moz-border-radius-bottomright:3px;-moz-border-radius-topright:3px;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.split.button.round span{-moz-border-radius-bottomright:1000px;-moz-border-radius-topright:1000px;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}.reveal-modal-bg{position:fixed;height:100%;width:100%;background:#000;background:rgba(0,0,0,0.45);z-index:98;display:none;top:0;left:0}dialog,.reveal-modal{visibility:hidden;display:none;position:absolute;left:50%;z-index:99;height:auto;margin-left:-40%;width:80%;background-color:#fff;padding:1.25rem;border:solid 1px #666;-webkit-box-shadow:0 0 10px rgba(0,0,0,0.4);box-shadow:0 0 10px rgba(0,0,0,0.4);top:6.25rem}dialog .column,dialog .columns,.reveal-modal .column,.reveal-modal .columns{min-width:0}dialog>:first-child,.reveal-modal>:first-child{margin-top:0}dialog>:last-child,.reveal-modal>:last-child{margin-bottom:0}dialog .close-reveal-modal,.reveal-modal .close-reveal-modal{font-size:1.375rem;line-height:1;position:absolute;top:0.5rem;right:0.6875rem;color:#aaa;font-weight:bold;cursor:pointer}dialog[open]{display:block;visibility:visible}@media only screen and (min-width: 40.063em){dialog,.reveal-modal{padding:1.875rem;top:6.25rem}dialog.tiny,.reveal-modal.tiny{margin-left:-15%;width:30%}dialog.small,.reveal-modal.small{margin-left:-20%;width:40%}dialog.medium,.reveal-modal.medium{margin-left:-30%;width:60%}dialog.large,.reveal-modal.large{margin-left:-35%;width:70%}dialog.xlarge,.reveal-modal.xlarge{margin-left:-47.5%;width:95%}}@media print{dialog,.reveal-modal{background:#fff !important}}.has-tip{border-bottom:dotted 1px #ccc;cursor:help;font-weight:bold;color:#333}.has-tip:hover,.has-tip:focus{border-bottom:dotted 1px #003c4c;color:#0086a9}.has-tip.tip-left,.has-tip.tip-right{float:none !important}.tooltip{display:none;position:absolute;z-index:999;font-weight:normal;font-size:0.875rem;line-height:1.3;padding:0.75rem;max-width:85%;left:50%;width:100%;color:#fff;background:#333}.tooltip>.nub{display:block;left:5px;position:absolute;width:0;height:0;border:solid 5px;border-color:transparent transparent #333 transparent;top:-10px}.tooltip.radius{-webkit-border-radius:3px;border-radius:3px}.tooltip.round{-webkit-border-radius:1000px;border-radius:1000px}.tooltip.round>.nub{left:2rem}.tooltip.opened{color:#0086a9 !important;border-bottom:dotted 1px #003c4c !important}.tap-to-close{display:block;font-size:0.625rem;color:#777;font-weight:normal}@media only screen and (min-width: 40.063em){.tooltip>.nub{border-color:transparent transparent #333 transparent;top:-10px}.tooltip.tip-top>.nub{border-color:#333 transparent transparent transparent;top:auto;bottom:-10px}.tooltip.tip-left,.tooltip.tip-right{float:none !important}.tooltip.tip-left>.nub{border-color:transparent transparent transparent #333;right:-10px;left:auto;top:50%;margin-top:-5px}.tooltip.tip-right>.nub{border-color:transparent #333 transparent transparent;right:auto;left:-10px;top:50%;margin-top:-5px}}.clearing-thumbs,[data-clearing]{*zoom:1;margin-bottom:0;margin-left:0;list-style:none}.clearing-thumbs:before,.clearing-thumbs:after,[data-clearing]:before,[data-clearing]:after{content:" ";display:table}.clearing-thumbs:after,[data-clearing]:after{clear:both}.clearing-thumbs li,[data-clearing] li{float:left;margin-right:10px}.clearing-thumbs[class*="block-grid-"] li,[data-clearing][class*="block-grid-"] li{margin-right:0}.clearing-blackout{background:#333;position:fixed;width:100%;height:100%;top:0;left:0;z-index:998}.clearing-blackout .clearing-close{display:block}.clearing-container{position:relative;z-index:998;height:100%;overflow:hidden;margin:0}.clearing-touch-label{position:absolute;top:50%;left:50%;color:#aaa;font-size:0.6em}.visible-img{height:95%;position:relative}.visible-img img{position:absolute;left:50%;top:50%;margin-left:-50%;max-height:100%;max-width:100%}.clearing-caption{color:#ccc;font-size:0.875em;line-height:1.3;margin-bottom:0;text-align:center;bottom:0;background:#333;width:100%;padding:10px 30px 20px;position:absolute;left:0}.clearing-close{z-index:999;padding-left:20px;padding-top:10px;font-size:30px;line-height:1;color:#ccc;display:none}.clearing-close:hover,.clearing-close:focus{color:#ccc}.clearing-assembled .clearing-container{height:100%}.clearing-assembled .clearing-container .carousel>ul{display:none}.clearing-feature li{display:none}.clearing-feature li.clearing-featured-img{display:block}@media only screen and (min-width: 40.063em){.clearing-main-prev,.clearing-main-next{position:absolute;height:100%;width:40px;top:0}.clearing-main-prev>span,.clearing-main-next>span{position:absolute;top:50%;display:block;width:0;height:0;border:solid 12px}.clearing-main-prev>span:hover,.clearing-main-next>span:hover{opacity:0.8}.clearing-main-prev{left:0}.clearing-main-prev>span{left:5px;border-color:transparent;border-right-color:#ccc}.clearing-main-next{right:0}.clearing-main-next>span{border-color:transparent;border-left-color:#ccc}.clearing-main-prev.disabled,.clearing-main-next.disabled{opacity:0.3}.clearing-assembled .clearing-container .carousel{background:rgba(51,51,51,0.8);height:120px;margin-top:10px;text-align:center}.clearing-assembled .clearing-container .carousel>ul{display:inline-block;z-index:999;height:100%;position:relative;float:none}.clearing-assembled .clearing-container .carousel>ul li{display:block;width:120px;min-height:inherit;float:left;overflow:hidden;margin-right:0;padding:0;position:relative;cursor:pointer;opacity:0.4}.clearing-assembled .clearing-container .carousel>ul li.fix-height img{height:100%;max-width:none}.clearing-assembled .clearing-container .carousel>ul li a.th{border:none;-webkit-box-shadow:none;box-shadow:none;display:block}.clearing-assembled .clearing-container .carousel>ul li img{cursor:pointer !important;width:100% !important}.clearing-assembled .clearing-container .carousel>ul li.visible{opacity:1}.clearing-assembled .clearing-container .carousel>ul li:hover{opacity:0.8}.clearing-assembled .clearing-container .visible-img{background:#333;overflow:hidden;height:85%}.clearing-close{position:absolute;top:10px;right:20px;padding-left:0;padding-top:0}}.progress{background-color:#f6f6f6;height:1.5625rem;border:1px solid #fff;padding:0.125rem;margin-bottom:0.625rem}.progress .meter{background:#0086a9;height:100%;display:block}.progress.secondary .meter{background:#f60;height:100%;display:block}.progress.success .meter{background:#20ba44;height:100%;display:block}.progress.alert .meter{background:#c60f13;height:100%;display:block}.progress.radius{-webkit-border-radius:3px;border-radius:3px}.progress.radius .meter{-webkit-border-radius:2px;border-radius:2px}.progress.round{-webkit-border-radius:1000px;border-radius:1000px}.progress.round .meter{-webkit-border-radius:999px;border-radius:999px}.sub-nav{display:block;width:auto;overflow:hidden;margin:-0.25rem 0 1.125rem;padding-top:0.25rem;margin-right:0;margin-left:-0.75rem}.sub-nav dt{text-transform:uppercase}.sub-nav dt,.sub-nav dd,.sub-nav li{float:left;display:inline;margin-left:1rem;margin-bottom:0.625rem;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;font-weight:normal;font-size:0.875rem;color:#999}.sub-nav dt a,.sub-nav dd a,.sub-nav li a{text-decoration:none;color:#999;padding:0.1875rem 1rem}.sub-nav dt a:hover,.sub-nav dd a:hover,.sub-nav li a:hover{color:#737373}.sub-nav dt.active a,.sub-nav dd.active a,.sub-nav li.active a{-webkit-border-radius:3px;border-radius:3px;font-weight:normal;background:#0086a9;padding:0.1875rem 1rem;cursor:default;color:#fff}.sub-nav dt.active a:hover,.sub-nav dd.active a:hover,.sub-nav li.active a:hover{background:#007391}.joyride-list{display:none}.joyride-tip-guide{display:none;position:absolute;background:#333;color:#fff;z-index:101;top:0;left:2.5%;font-family:inherit;font-weight:normal;width:95%}.lt-ie9 .joyride-tip-guide{max-width:800px;left:50%;margin-left:-400px}.joyride-content-wrapper{width:100%;padding:1.125rem 1.25rem 1.5rem}.joyride-content-wrapper .button{margin-bottom:0 !important}.joyride-tip-guide .joyride-nub{display:block;position:absolute;left:22px;width:0;height:0;border:10px solid #333}.joyride-tip-guide .joyride-nub.top{border-top-style:solid;border-color:#333;border-top-color:transparent !important;border-left-color:transparent !important;border-right-color:transparent !important;top:-20px}.joyride-tip-guide .joyride-nub.bottom{border-bottom-style:solid;border-color:#333 !important;border-bottom-color:transparent !important;border-left-color:transparent !important;border-right-color:transparent !important;bottom:-20px}.joyride-tip-guide .joyride-nub.right{right:-20px}.joyride-tip-guide .joyride-nub.left{left:-20px}.joyride-tip-guide h1,.joyride-tip-guide h2,.joyride-tip-guide h3,.joyride-tip-guide h4,.joyride-tip-guide h5,.joyride-tip-guide h6{line-height:1.25;margin:0;font-weight:bold;color:#fff}.joyride-tip-guide p{margin:0 0 1.125rem 0;font-size:0.875rem;line-height:1.3}.joyride-timer-indicator-wrap{width:50px;height:3px;border:solid 1px #555;position:absolute;right:1.0625rem;bottom:1rem}.joyride-timer-indicator{display:block;width:0;height:inherit;background:#666}.joyride-close-tip{position:absolute;right:12px;top:10px;color:#777 !important;text-decoration:none;font-size:24px;font-weight:normal;line-height:0.5 !important}.joyride-close-tip:hover,.joyride-close-tip:focus{color:#eee !important}.joyride-modal-bg{position:fixed;height:100%;width:100%;background:transparent;background:rgba(0,0,0,0.5);z-index:100;display:none;top:0;left:0;cursor:pointer}.joyride-expose-wrapper{background-color:#ffffff;position:absolute;border-radius:3px;z-index:102;-moz-box-shadow:0 0 30px #fff;-webkit-box-shadow:0 0 15px #fff;box-shadow:0 0 15px #fff}.joyride-expose-cover{background:transparent;border-radius:3px;position:absolute;z-index:9999;top:0;left:0}@media only screen and (min-width: 40.063em){.joyride-tip-guide{width:300px;left:inherit}.joyride-tip-guide .joyride-nub.bottom{border-color:#333 !important;border-bottom-color:transparent !important;border-left-color:transparent !important;border-right-color:transparent !important;bottom:-20px}.joyride-tip-guide .joyride-nub.right{border-color:#333 !important;border-top-color:transparent !important;border-right-color:transparent !important;border-bottom-color:transparent !important;top:22px;left:auto;right:-20px}.joyride-tip-guide .joyride-nub.left{border-color:#333 !important;border-top-color:transparent !important;border-left-color:transparent !important;border-bottom-color:transparent !important;top:22px;left:-20px;right:auto}}.label{font-weight:normal;font-family:"Helvetica Neue","Helvetica",Helvetica,Arial,sans-serif;text-align:center;text-decoration:none;line-height:1;white-space:nowrap;display:inline-block;position:relative;margin-bottom:inherit;padding:0.25rem 0.5rem 0.375rem;font-size:0.6875rem;background-color:#0086a9;color:#fff}.label.radius{-webkit-border-radius:3px;border-radius:3px}.label.round{-webkit-border-radius:1000px;border-radius:1000px}.label.alert{background-color:#c60f13;color:#fff}.label.success{background-color:#20ba44;color:#fff}.label.secondary{background-color:#f60;color:#fff}.text-left{text-align:left !important}.text-right{text-align:right !important}.text-center{text-align:center !important}.text-justify{text-align:justify !important}@media only screen and (max-width: 40em){.small-only-text-left{text-align:left !important}.small-only-text-right{text-align:right !important}.small-only-text-center{text-align:center !important}.small-only-text-justify{text-align:justify !important}}@media only screen{.small-text-left{text-align:left !important}.small-text-right{text-align:right !important}.small-text-center{text-align:center !important}.small-text-justify{text-align:justify !important}}@media only screen and (min-width: 40.063em) and (max-width: 64em){.medium-only-text-left{text-align:left !important}.medium-only-text-right{text-align:right !important}.medium-only-text-center{text-align:center !important}.medium-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 40.063em){.medium-text-left{text-align:left !important}.medium-text-right{text-align:right !important}.medium-text-center{text-align:center !important}.medium-text-justify{text-align:justify !important}}@media only screen and (min-width: 64.063em) and (max-width: 90em){.large-only-text-left{text-align:left !important}.large-only-text-right{text-align:right !important}.large-only-text-center{text-align:center !important}.large-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 64.063em){.large-text-left{text-align:left !important}.large-text-right{text-align:right !important}.large-text-center{text-align:center !important}.large-text-justify{text-align:justify !important}}@media only screen and (min-width: 90.063em) and (max-width: 120em){.xlarge-only-text-left{text-align:left !important}.xlarge-only-text-right{text-align:right !important}.xlarge-only-text-center{text-align:center !important}.xlarge-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 90.063em){.xlarge-text-left{text-align:left !important}.xlarge-text-right{text-align:right !important}.xlarge-text-center{text-align:center !important}.xlarge-text-justify{text-align:justify !important}}@media only screen and (min-width: 120.063em) and (max-width: 99999999em){.xxlarge-only-text-left{text-align:left !important}.xxlarge-only-text-right{text-align:right !important}.xxlarge-only-text-center{text-align:center !important}.xxlarge-only-text-justify{text-align:justify !important}}@media only screen and (min-width: 120.063em){.xxlarge-text-left{text-align:left !important}.xxlarge-text-right{text-align:right !important}.xxlarge-text-center{text-align:center !important}.xxlarge-text-justify{text-align:justify !important}}.off-canvas-wrap{-webkit-backface-visibility:hidden;position:relative;width:100%;overflow-x:hidden}.off-canvas-wrap.move-right,.off-canvas-wrap.move-left{height:100%}.inner-wrap{-webkit-backface-visibility:hidden;position:relative;width:100%;*zoom:1;-webkit-transition:-webkit-transform 500ms ease;-moz-transition:-moz-transform 500ms ease;-ms-transition:-ms-transform 500ms ease;-o-transition:-o-transform 500ms ease;transition:transform 500ms ease}.inner-wrap:before,.inner-wrap:after{content:" ";display:table}.inner-wrap:after{clear:both}nav.tab-bar{-webkit-backface-visibility:hidden;background:#333;color:#fff;height:2.8125rem;line-height:2.8125rem;position:relative}nav.tab-bar h1,nav.tab-bar h2,nav.tab-bar h3,nav.tab-bar h4,nav.tab-bar h5,nav.tab-bar h6{color:#fff;font-weight:bold;line-height:2.8125rem;margin:0}nav.tab-bar h1,nav.tab-bar h2,nav.tab-bar h3,nav.tab-bar h4{font-size:1.125rem}section.left-small{width:2.8125rem;height:2.8125rem;position:absolute;top:0;border-right:solid 1px #1a1a1a;box-shadow:1px 0 0 #4e4e4e;left:0}section.right-small{width:2.8125rem;height:2.8125rem;position:absolute;top:0;border-left:solid 1px #4e4e4e;box-shadow:-1px 0 0 #1a1a1a;right:0}section.tab-bar-section{padding:0 0.625rem;position:absolute;text-align:center;height:2.8125rem;top:0}@media only screen and (min-width: 40.063em){section.tab-bar-section{text-align:left}}section.tab-bar-section.left{left:0;right:2.8125rem}section.tab-bar-section.right{left:2.8125rem;right:0}section.tab-bar-section.middle{left:2.8125rem;right:2.8125rem}a.menu-icon{text-indent:2.1875rem;width:2.8125rem;height:2.8125rem;display:block;line-height:2.0625rem;padding:0;color:#fff;position:relative}a.menu-icon span{position:absolute;display:block;width:1rem;height:0;left:0.8125rem;top:0.3125rem;-webkit-box-shadow:1px 10px 1px 1px #fff,1px 16px 1px 1px #fff,1px 22px 1px 1px #fff;box-shadow:0 10px 0 1px #fff,0 16px 0 1px #fff,0 22px 0 1px #fff}a.menu-icon:hover span{-webkit-box-shadow:1px 10px 1px 1px #b3b3b3,1px 16px 1px 1px #b3b3b3,1px 22px 1px 1px #b3b3b3;box-shadow:0 10px 0 1px #b3b3b3,0 16px 0 1px #b3b3b3,0 22px 0 1px #b3b3b3}.left-off-canvas-menu{-webkit-backface-visibility:hidden;width:250px;top:0;bottom:0;position:absolute;overflow-y:auto;background:#333;z-index:1001;box-sizing:content-box;-webkit-transform:translate3d(-100%, 0, 0);-moz-transform:translate3d(-100%, 0, 0);-ms-transform:translate3d(-100%, 0, 0);-o-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.left-off-canvas-menu *{-webkit-backface-visibility:hidden}.right-off-canvas-menu{-webkit-backface-visibility:hidden;width:250px;top:0;bottom:0;position:absolute;overflow-y:auto;background:#333;z-index:1001;box-sizing:content-box;-webkit-transform:translate3d(100%, 0, 0);-moz-transform:translate3d(100%, 0, 0);-ms-transform:translate3d(100%, 0, 0);-o-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);right:0}ul.off-canvas-list{list-style-type:none;padding:0;margin:0}ul.off-canvas-list li label{padding:0.3rem 0.9375rem;color:#999;text-transform:uppercase;font-weight:bold;background:#444;border-top:1px solid #5e5e5e;border-bottom:none;margin:0}ul.off-canvas-list li a{display:block;padding:0.66667rem;color:rgba(255,255,255,0.7);border-bottom:1px solid #262626}.move-right>.inner-wrap{-webkit-transform:translate3d(250px, 0, 0);-moz-transform:translate3d(250px, 0, 0);-ms-transform:translate3d(250px, 0, 0);-o-transform:translate3d(250px, 0, 0);transform:translate3d(250px, 0, 0)}.move-right a.exit-off-canvas{-webkit-backface-visibility:hidden;transition:background 300ms ease;cursor:pointer;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);display:block;position:absolute;background:rgba(255,255,255,0.2);top:0;bottom:0;left:0;right:0;z-index:1002;-webkit-tap-highlight-color:rgba(0,0,0,0)}@media only screen and (min-width: 40.063em){.move-right a.exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.move-left>.inner-wrap{-webkit-transform:translate3d(-250px, 0, 0);-moz-transform:translate3d(-250px, 0, 0);-ms-transform:translate3d(-250px, 0, 0);-o-transform:translate3d(-250px, 0, 0);transform:translate3d(-250px, 0, 0)}.move-left a.exit-off-canvas{-webkit-backface-visibility:hidden;transition:background 300ms ease;cursor:pointer;box-shadow:-4px 0 4px rgba(0,0,0,0.5),4px 0 4px rgba(0,0,0,0.5);display:block;position:absolute;background:rgba(255,255,255,0.2);top:0;bottom:0;left:0;right:0;z-index:1002;-webkit-tap-highlight-color:rgba(0,0,0,0)}@media only screen and (min-width: 40.063em){.move-left a.exit-off-canvas:hover{background:rgba(255,255,255,0.05)}}.csstransforms.no-csstransforms3d .left-off-canvas-menu{-webkit-transform:translate(-100%, 0);-moz-transform:translate(-100%, 0);-ms-transform:translate(-100%, 0);-o-transform:translate(-100%, 0);transform:translate(-100%, 0)}.csstransforms.no-csstransforms3d .right-off-canvas-menu{-webkit-transform:translate(100%, 0);-moz-transform:translate(100%, 0);-ms-transform:translate(100%, 0);-o-transform:translate(100%, 0);transform:translate(100%, 0)}.csstransforms.no-csstransforms3d .move-left>.inner-wrap{-webkit-transform:translate(-250px, 0);-moz-transform:translate(-250px, 0);-ms-transform:translate(-250px, 0);-o-transform:translate(-250px, 0);transform:translate(-250px, 0)}.csstransforms.no-csstransforms3d .move-right>.inner-wrap{-webkit-transform:translate(250px, 0);-moz-transform:translate(250px, 0);-ms-transform:translate(250px, 0);-o-transform:translate(250px, 0);transform:translate(250px, 0)}.no-csstransforms .left-off-canvas-menu{left:-250px}.no-csstransforms .right-off-canvas-menu{right:-250px}.no-csstransforms .move-left>.inner-wrap{right:250px}.no-csstransforms .move-right>.inner-wrap{left:250px}@media only screen and (max-width: 40em){.f-dropdown{max-width:100%;left:0}}.f-dropdown{position:absolute;left:-9999px;list-style:none;margin-left:0;width:100%;max-height:none;height:auto;background:#fff;border:solid 1px #ccc;font-size:16px;z-index:99;margin-top:2px;max-width:200px}.f-dropdown>*:first-child{margin-top:0}.f-dropdown>*:last-child{margin-bottom:0}.f-dropdown:before{content:"";display:block;width:0;height:0;border:inset 6px;border-color:transparent transparent #fff transparent;border-bottom-style:solid;position:absolute;top:-12px;left:10px;z-index:99}.f-dropdown:after{content:"";display:block;width:0;height:0;border:inset 7px;border-color:transparent transparent #ccc transparent;border-bottom-style:solid;position:absolute;top:-14px;left:9px;z-index:98}.f-dropdown.right:before{left:auto;right:10px}.f-dropdown.right:after{left:auto;right:9px}.f-dropdown li{font-size:0.875rem;cursor:pointer;line-height:1.125rem;margin:0}.f-dropdown li:hover,.f-dropdown li:focus{background:#eee}.f-dropdown li a{display:block;padding:0.5rem;color:#555}.f-dropdown.content{position:absolute;left:-9999px;list-style:none;margin-left:0;padding:1.25rem;width:100%;height:auto;max-height:none;background:#fff;border:solid 1px #ccc;font-size:16px;z-index:99;max-width:200px}.f-dropdown.content>*:first-child{margin-top:0}.f-dropdown.content>*:last-child{margin-bottom:0}.f-dropdown.tiny{max-width:200px}.f-dropdown.small{max-width:300px}.f-dropdown.medium{max-width:500px}.f-dropdown.large{max-width:800px}table{background:#fff;margin-bottom:1.25rem;border:solid 1px #ddd}table thead,table tfoot{background:#f5f5f5}table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:0.5rem 0.625rem 0.625rem;font-size:0.875rem;font-weight:bold;color:#222;text-align:left}table tr th,table tr td{padding:0.5625rem 0.625rem;font-size:0.875rem;color:#222}table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f9f9f9}table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.125rem}form{margin:0 0 1rem}form .row .row{margin:0 -0.5rem}form .row .row .column,form .row .row .columns{padding:0 0.5rem}form .row .row.collapse{margin:0}form .row .row.collapse .column,form .row .row.collapse .columns{padding:0}form .row .row.collapse input{-moz-border-radius-bottomright:0;-moz-border-radius-topright:0;-webkit-border-bottom-right-radius:0;-webkit-border-top-right-radius:0}form .row input.column,form .row input.columns,form .row textarea.column,form .row textarea.columns{padding-left:0.5rem}label{font-size:0.875rem;color:#4d4d4d;cursor:pointer;display:block;font-weight:normal;line-height:1.5;margin-bottom:0}label.right{float:none;text-align:right}label.inline{margin:0 0 1rem 0;padding:0.625rem 0}label small{text-transform:capitalize;color:#676767}select{-webkit-appearance:none !important;background:#fafafa url("data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iM3B4IiB2aWV3Qm94PSIwIDAgNiAzIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2IDMiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwb2x5Z29uIHBvaW50cz0iNS45OTIsMCAyLjk5MiwzIC0wLjAwOCwwICIvPjwvc3ZnPg==") no-repeat;background-position-x:97%;background-position-y:center;border:1px solid #ccc;padding:0.5rem;font-size:0.875rem;-webkit-border-radius:0;border-radius:0}select.radius{-webkit-border-radius:3px;border-radius:3px}select:hover{background:#f3f3f3 url("data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iM3B4IiB2aWV3Qm94PSIwIDAgNiAzIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2IDMiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwb2x5Z29uIHBvaW50cz0iNS45OTIsMCAyLjk5MiwzIC0wLjAwOCwwICIvPjwvc3ZnPg==") no-repeat;background-position-x:97%;background-position-y:center;border-color:#999}select::-ms-expand{display:none}@-moz-document url-prefix(){select{background:#fafafa}select:hover{background:#f3f3f3}}.prefix,.postfix{display:block;position:relative;z-index:2;text-align:center;width:100%;padding-top:0;padding-bottom:0;border-style:solid;border-width:1px;overflow:hidden;font-size:0.875rem;height:2.3125rem;line-height:2.3125rem}.postfix.button{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0;text-align:center;line-height:2.125rem;border:none}.prefix.button{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0;text-align:center;line-height:2.125rem;border:none}.prefix.button.radius{-webkit-border-radius:0;border-radius:0;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}.postfix.button.radius{-webkit-border-radius:0;border-radius:0;-moz-border-radius-bottomright:3px;-moz-border-radius-topright:3px;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}.prefix.button.round{-webkit-border-radius:0;border-radius:0;-moz-border-radius-bottomleft:1000px;-moz-border-radius-topleft:1000px;-webkit-border-bottom-left-radius:1000px;-webkit-border-top-left-radius:1000px;border-bottom-left-radius:1000px;border-top-left-radius:1000px}.postfix.button.round{-webkit-border-radius:0;border-radius:0;-moz-border-radius-bottomright:1000px;-moz-border-radius-topright:1000px;-webkit-border-bottom-right-radius:1000px;-webkit-border-top-right-radius:1000px;border-bottom-right-radius:1000px;border-top-right-radius:1000px}span.prefix,label.prefix{background:#f2f2f2;border-right:none;color:#333;border-color:#ccc}span.prefix.radius,label.prefix.radius{-webkit-border-radius:0;border-radius:0;-moz-border-radius-bottomleft:3px;-moz-border-radius-topleft:3px;-webkit-border-bottom-left-radius:3px;-webkit-border-top-left-radius:3px;border-bottom-left-radius:3px;border-top-left-radius:3px}span.postfix,label.postfix{background:#f2f2f2;border-left:none;color:#333;border-color:#ccc}span.postfix.radius,label.postfix.radius{-webkit-border-radius:0;border-radius:0;-moz-border-radius-bottomright:3px;-moz-border-radius-topright:3px;-webkit-border-bottom-right-radius:3px;-webkit-border-top-right-radius:3px;border-bottom-right-radius:3px;border-top-right-radius:3px}input[type="text"],input[type="password"],input[type="date"],input[type="datetime"],input[type="datetime-local"],input[type="month"],input[type="week"],input[type="email"],input[type="number"],input[type="search"],input[type="tel"],input[type="time"],input[type="url"],textarea{-webkit-appearance:none;background-color:#fff;font-family:inherit;border:1px solid #ccc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);color:rgba(0,0,0,0.75);display:block;font-size:0.875rem;margin:0 0 1rem 0;padding:0.5rem;height:2.3125rem;width:100%;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-transition:-webkit-box-shadow 0.45s,border-color 0.45s ease-in-out;-moz-transition:-moz-box-shadow 0.45s,border-color 0.45s ease-in-out;transition:box-shadow 0.45s,border-color 0.45s ease-in-out}input[type="text"]:focus,input[type="password"]:focus,input[type="date"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="month"]:focus,input[type="week"]:focus,input[type="email"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="time"]:focus,input[type="url"]:focus,textarea:focus{-webkit-box-shadow:0 0 5px #999;-moz-box-shadow:0 0 5px #999;box-shadow:0 0 5px #999;border-color:#999}input[type="text"]:focus,input[type="password"]:focus,input[type="date"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="month"]:focus,input[type="week"]:focus,input[type="email"]:focus,input[type="number"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="time"]:focus,input[type="url"]:focus,textarea:focus{background:#fafafa;border-color:#999;outline:none}input[type="text"][disabled],input[type="password"][disabled],input[type="date"][disabled],input[type="datetime"][disabled],input[type="datetime-local"][disabled],input[type="month"][disabled],input[type="week"][disabled],input[type="email"][disabled],input[type="number"][disabled],input[type="search"][disabled],input[type="tel"][disabled],input[type="time"][disabled],input[type="url"][disabled],textarea[disabled]{background-color:#ddd}input[type="text"].radius,input[type="password"].radius,input[type="date"].radius,input[type="datetime"].radius,input[type="datetime-local"].radius,input[type="month"].radius,input[type="week"].radius,input[type="email"].radius,input[type="number"].radius,input[type="search"].radius,input[type="tel"].radius,input[type="time"].radius,input[type="url"].radius,textarea.radius{-webkit-border-radius:3px;border-radius:3px}select{height:2.3125rem}input[type="file"],input[type="checkbox"],input[type="radio"],select{margin:0 0 1rem 0}input[type="checkbox"]+label,input[type="radio"]+label{display:inline-block;margin-left:0.5rem;margin-right:1rem;margin-bottom:0;vertical-align:baseline}input[type="file"]{width:100%}fieldset{border:solid 1px #ddd;padding:1.25rem;margin:1.125rem 0}fieldset legend{font-weight:bold;background:#fff;padding:0 0.1875rem;margin:0;margin-left:-0.1875rem}[data-abide] .error small.error,[data-abide] span.error,[data-abide] small.error{display:block;padding:0.375rem 0.5625rem 0.5625rem;margin-top:-1px;margin-bottom:1rem;font-size:0.75rem;font-weight:normal;font-style:italic;background:#c60f13;color:#fff}[data-abide] span.error,[data-abide] small.error{display:none}span.error,small.error{display:block;padding:0.375rem 0.5625rem 0.5625rem;margin-top:-1px;margin-bottom:1rem;font-size:0.75rem;font-weight:normal;font-style:italic;background:#c60f13;color:#fff}.error input,.error textarea,.error select{margin-bottom:0}.error input[type="checkbox"],.error input[type="radio"]{margin-bottom:1rem}.error label,.error label.error{color:#c60f13}.error small.error{display:block;padding:0.375rem 0.5625rem 0.5625rem;margin-top:-1px;margin-bottom:1rem;font-size:0.75rem;font-weight:normal;font-style:italic;background:#c60f13;color:#fff}.error>label>small{color:#676767;background:transparent;padding:0;text-transform:capitalize;font-style:normal;font-size:60%;margin:0;display:inline}.error span.error-message{display:block}input.error,textarea.error{margin-bottom:0}label.error{color:#c60f13}[class*="block-grid-"]{display:block;padding:0;margin:0 -0.625rem;*zoom:1}[class*="block-grid-"]:before,[class*="block-grid-"]:after{content:" ";display:table}[class*="block-grid-"]:after{clear:both}[class*="block-grid-"]>li{display:block;height:auto;float:left;padding:0 0.625rem 1.25rem}@media only screen{.small-block-grid-1>li{width:100%;list-style:none}.small-block-grid-1>li:nth-of-type(n){clear:none}.small-block-grid-1>li:nth-of-type(1n+1){clear:both}.small-block-grid-2>li{width:50%;list-style:none}.small-block-grid-2>li:nth-of-type(n){clear:none}.small-block-grid-2>li:nth-of-type(2n+1){clear:both}.small-block-grid-3>li{width:33.33333%;list-style:none}.small-block-grid-3>li:nth-of-type(n){clear:none}.small-block-grid-3>li:nth-of-type(3n+1){clear:both}.small-block-grid-4>li{width:25%;list-style:none}.small-block-grid-4>li:nth-of-type(n){clear:none}.small-block-grid-4>li:nth-of-type(4n+1){clear:both}.small-block-grid-5>li{width:20%;list-style:none}.small-block-grid-5>li:nth-of-type(n){clear:none}.small-block-grid-5>li:nth-of-type(5n+1){clear:both}.small-block-grid-6>li{width:16.66667%;list-style:none}.small-block-grid-6>li:nth-of-type(n){clear:none}.small-block-grid-6>li:nth-of-type(6n+1){clear:both}.small-block-grid-7>li{width:14.28571%;list-style:none}.small-block-grid-7>li:nth-of-type(n){clear:none}.small-block-grid-7>li:nth-of-type(7n+1){clear:both}.small-block-grid-8>li{width:12.5%;list-style:none}.small-block-grid-8>li:nth-of-type(n){clear:none}.small-block-grid-8>li:nth-of-type(8n+1){clear:both}.small-block-grid-9>li{width:11.11111%;list-style:none}.small-block-grid-9>li:nth-of-type(n){clear:none}.small-block-grid-9>li:nth-of-type(9n+1){clear:both}.small-block-grid-10>li{width:10%;list-style:none}.small-block-grid-10>li:nth-of-type(n){clear:none}.small-block-grid-10>li:nth-of-type(10n+1){clear:both}.small-block-grid-11>li{width:9.09091%;list-style:none}.small-block-grid-11>li:nth-of-type(n){clear:none}.small-block-grid-11>li:nth-of-type(11n+1){clear:both}.small-block-grid-12>li{width:8.33333%;list-style:none}.small-block-grid-12>li:nth-of-type(n){clear:none}.small-block-grid-12>li:nth-of-type(12n+1){clear:both}}@media only screen and (min-width: 40.063em){.medium-block-grid-1>li{width:100%;list-style:none}.medium-block-grid-1>li:nth-of-type(n){clear:none}.medium-block-grid-1>li:nth-of-type(1n+1){clear:both}.medium-block-grid-2>li{width:50%;list-style:none}.medium-block-grid-2>li:nth-of-type(n){clear:none}.medium-block-grid-2>li:nth-of-type(2n+1){clear:both}.medium-block-grid-3>li{width:33.33333%;list-style:none}.medium-block-grid-3>li:nth-of-type(n){clear:none}.medium-block-grid-3>li:nth-of-type(3n+1){clear:both}.medium-block-grid-4>li{width:25%;list-style:none}.medium-block-grid-4>li:nth-of-type(n){clear:none}.medium-block-grid-4>li:nth-of-type(4n+1){clear:both}.medium-block-grid-5>li{width:20%;list-style:none}.medium-block-grid-5>li:nth-of-type(n){clear:none}.medium-block-grid-5>li:nth-of-type(5n+1){clear:both}.medium-block-grid-6>li{width:16.66667%;list-style:none}.medium-block-grid-6>li:nth-of-type(n){clear:none}.medium-block-grid-6>li:nth-of-type(6n+1){clear:both}.medium-block-grid-7>li{width:14.28571%;list-style:none}.medium-block-grid-7>li:nth-of-type(n){clear:none}.medium-block-grid-7>li:nth-of-type(7n+1){clear:both}.medium-block-grid-8>li{width:12.5%;list-style:none}.medium-block-grid-8>li:nth-of-type(n){clear:none}.medium-block-grid-8>li:nth-of-type(8n+1){clear:both}.medium-block-grid-9>li{width:11.11111%;list-style:none}.medium-block-grid-9>li:nth-of-type(n){clear:none}.medium-block-grid-9>li:nth-of-type(9n+1){clear:both}.medium-block-grid-10>li{width:10%;list-style:none}.medium-block-grid-10>li:nth-of-type(n){clear:none}.medium-block-grid-10>li:nth-of-type(10n+1){clear:both}.medium-block-grid-11>li{width:9.09091%;list-style:none}.medium-block-grid-11>li:nth-of-type(n){clear:none}.medium-block-grid-11>li:nth-of-type(11n+1){clear:both}.medium-block-grid-12>li{width:8.33333%;list-style:none}.medium-block-grid-12>li:nth-of-type(n){clear:none}.medium-block-grid-12>li:nth-of-type(12n+1){clear:both}}@media only screen and (min-width: 64.063em){.large-block-grid-1>li{width:100%;list-style:none}.large-block-grid-1>li:nth-of-type(n){clear:none}.large-block-grid-1>li:nth-of-type(1n+1){clear:both}.large-block-grid-2>li{width:50%;list-style:none}.large-block-grid-2>li:nth-of-type(n){clear:none}.large-block-grid-2>li:nth-of-type(2n+1){clear:both}.large-block-grid-3>li{width:33.33333%;list-style:none}.large-block-grid-3>li:nth-of-type(n){clear:none}.large-block-grid-3>li:nth-of-type(3n+1){clear:both}.large-block-grid-4>li{width:25%;list-style:none}.large-block-grid-4>li:nth-of-type(n){clear:none}.large-block-grid-4>li:nth-of-type(4n+1){clear:both}.large-block-grid-5>li{width:20%;list-style:none}.large-block-grid-5>li:nth-of-type(n){clear:none}.large-block-grid-5>li:nth-of-type(5n+1){clear:both}.large-block-grid-6>li{width:16.66667%;list-style:none}.large-block-grid-6>li:nth-of-type(n){clear:none}.large-block-grid-6>li:nth-of-type(6n+1){clear:both}.large-block-grid-7>li{width:14.28571%;list-style:none}.large-block-grid-7>li:nth-of-type(n){clear:none}.large-block-grid-7>li:nth-of-type(7n+1){clear:both}.large-block-grid-8>li{width:12.5%;list-style:none}.large-block-grid-8>li:nth-of-type(n){clear:none}.large-block-grid-8>li:nth-of-type(8n+1){clear:both}.large-block-grid-9>li{width:11.11111%;list-style:none}.large-block-grid-9>li:nth-of-type(n){clear:none}.large-block-grid-9>li:nth-of-type(9n+1){clear:both}.large-block-grid-10>li{width:10%;list-style:none}.large-block-grid-10>li:nth-of-type(n){clear:none}.large-block-grid-10>li:nth-of-type(10n+1){clear:both}.large-block-grid-11>li{width:9.09091%;list-style:none}.large-block-grid-11>li:nth-of-type(n){clear:none}.large-block-grid-11>li:nth-of-type(11n+1){clear:both}.large-block-grid-12>li{width:8.33333%;list-style:none}.large-block-grid-12>li:nth-of-type(n){clear:none}.large-block-grid-12>li:nth-of-type(12n+1){clear:both}}.keystroke,kbd{background-color:#ededed;border-color:#ddd;color:#222;border-style:solid;border-width:1px;margin:0;font-family:"Consolas","Menlo","Courier",monospace;font-size:0.875rem;padding:0.125rem 0.25rem 0;-webkit-border-radius:3px;border-radius:3px}.show-for-small,.show-for-small-only,.show-for-medium-down,.show-for-large-down,.hide-for-medium,.hide-for-medium-up,.hide-for-medium-only,.hide-for-large,.hide-for-large-up,.hide-for-large-only,.hide-for-xlarge,.hide-for-xlarge-up,.hide-for-xlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge-only{display:inherit !important}.hide-for-small,.hide-for-small-only,.hide-for-medium-down,.show-for-medium,.show-for-medium-up,.show-for-medium-only,.hide-for-large-down,.show-for-large,.show-for-large-up,.show-for-large-only,.show-for-xlarge,.show-for-xlarge-up,.show-for-xlarge-only,.show-for-xxlarge-up,.show-for-xxlarge-only{display:none !important}table.show-for-small,table.show-for-small-only,table.show-for-medium-down,table.show-for-large-down,table.hide-for-medium,table.hide-for-medium-up,table.hide-for-medium-only,table.hide-for-large,table.hide-for-large-up,table.hide-for-large-only,table.hide-for-xlarge,table.hide-for-xlarge-up,table.hide-for-xlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge-only{display:table}thead.show-for-small,thead.show-for-small-only,thead.show-for-medium-down,thead.show-for-large-down,thead.hide-for-medium,thead.hide-for-medium-up,thead.hide-for-medium-only,thead.hide-for-large,thead.hide-for-large-up,thead.hide-for-large-only,thead.hide-for-xlarge,thead.hide-for-xlarge-up,thead.hide-for-xlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge-only{display:table-header-group !important}tbody.show-for-small,tbody.show-for-small-only,tbody.show-for-medium-down,tbody.show-for-large-down,tbody.hide-for-medium,tbody.hide-for-medium-up,tbody.hide-for-medium-only,tbody.hide-for-large,tbody.hide-for-large-up,tbody.hide-for-large-only,tbody.hide-for-xlarge,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge-only{display:table-row-group !important}tr.show-for-small,tr.show-for-small-only,tr.show-for-medium-down,tr.show-for-large-down,tr.hide-for-medium,tr.hide-for-medium-up,tr.hide-for-medium-only,tr.hide-for-large,tr.hide-for-large-up,tr.hide-for-large-only,tr.hide-for-xlarge,tr.hide-for-xlarge-up,tr.hide-for-xlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge-only{display:table-row !important}td.show-for-small,td.show-for-small-only,td.show-for-medium-down,td.show-for-large-down,td.hide-for-medium,td.hide-for-medium-up,td.hide-for-large,td.hide-for-large-up,td.hide-for-xlarge,td.hide-for-xlarge-up,td.hide-for-xxlarge-up,th.show-for-small,th.show-for-small-only,th.show-for-medium-down,th.show-for-large-down,th.hide-for-medium,th.hide-for-medium-up,th.hide-for-large,th.hide-for-large-up,th.hide-for-xlarge,th.hide-for-xlarge-up,th.hide-for-xxlarge-up{display:table-cell !important}@media only screen and (min-width: 40.063em){.hide-for-small,.hide-for-small-only,.show-for-medium,.show-for-medium-down,.show-for-medium-up,.show-for-medium-only,.hide-for-large,.hide-for-large-up,.hide-for-large-only,.hide-for-xlarge,.hide-for-xlarge-up,.hide-for-xlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge-only{display:inherit !important}.show-for-small,.show-for-small-only,.hide-for-medium,.hide-for-medium-down,.hide-for-medium-up,.hide-for-medium-only,.hide-for-large-down,.show-for-large,.show-for-large-up,.show-for-large-only,.show-for-xlarge,.show-for-xlarge-up,.show-for-xlarge-only,.show-for-xxlarge-up,.show-for-xxlarge-only{display:none !important}table.hide-for-small,table.hide-for-small-only,table.show-for-medium,table.show-for-medium-down,table.show-for-medium-up,table.show-for-medium-only,table.hide-for-large,table.hide-for-large-up,table.hide-for-large-only,table.hide-for-xlarge,table.hide-for-xlarge-up,table.hide-for-xlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge-only{display:table}thead.hide-for-small,thead.hide-for-small-only,thead.show-for-medium,thead.show-for-medium-down,thead.show-for-medium-up,thead.show-for-medium-only,thead.hide-for-large,thead.hide-for-large-up,thead.hide-for-large-only,thead.hide-for-xlarge,thead.hide-for-xlarge-up,thead.hide-for-xlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge-only{display:table-header-group !important}tbody.hide-for-small,tbody.hide-for-small-only,tbody.show-for-medium,tbody.show-for-medium-down,tbody.show-for-medium-up,tbody.show-for-medium-only,tbody.hide-for-large,tbody.hide-for-large-up,tbody.hide-for-large-only,tbody.hide-for-xlarge,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge-only{display:table-row-group !important}tr.hide-for-small,tr.hide-for-small-only,tr.show-for-medium,tr.show-for-medium-down,tr.show-for-medium-up,tr.show-for-medium-only,tr.hide-for-large,tr.hide-for-large-up,tr.hide-for-large-only,tr.hide-for-xlarge,tr.hide-for-xlarge-up,tr.hide-for-xlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge-only{display:table-row !important}td.hide-for-small,td.hide-for-small-only,td.show-for-medium,td.show-for-medium-down,td.show-for-medium-up,td.show-for-medium-only,td.hide-for-large,td.hide-for-large-up,td.hide-for-large-only,td.hide-for-xlarge,td.hide-for-xlarge-up,td.hide-for-xlarge-only,td.hide-for-xxlarge-up,td.hide-for-xxlarge-only,th.hide-for-small,th.hide-for-small-only,th.show-for-medium,th.show-for-medium-down,th.show-for-medium-up,th.show-for-medium-only,th.hide-for-large,th.hide-for-large-up,th.hide-for-large-only,th.hide-for-xlarge,th.hide-for-xlarge-up,th.hide-for-xlarge-only,th.hide-for-xxlarge-up,th.hide-for-xxlarge-only{display:table-cell !important}}@media only screen and (min-width: 64.063em){.hide-for-small,.hide-for-small-only,.hide-for-medium,.hide-for-medium-down,.hide-for-medium-only,.show-for-medium-up,.show-for-large,.show-for-large-up,.show-for-large-only,.hide-for-xlarge,.hide-for-xlarge-up,.hide-for-xlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge-only{display:inherit !important}.show-for-small-only,.show-for-medium,.show-for-medium-down,.show-for-medium-only,.hide-for-large,.hide-for-large-up,.hide-for-large-only,.show-for-xlarge,.show-for-xlarge-up,.show-for-xlarge-only,.show-for-xxlarge-up,.show-for-xxlarge-only{display:none !important}table.hide-for-small,table.hide-for-small-only,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-medium-only,table.show-for-medium-up,table.show-for-large,table.show-for-large-up,table.show-for-large-only,table.hide-for-xlarge,table.hide-for-xlarge-up,table.hide-for-xlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge-only{display:table}thead.hide-for-small,thead.hide-for-small-only,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.show-for-large,thead.show-for-large-up,thead.show-for-large-only,thead.hide-for-xlarge,thead.hide-for-xlarge-up,thead.hide-for-xlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge-only{display:table-header-group !important}tbody.hide-for-small,tbody.hide-for-small-only,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.show-for-large,tbody.show-for-large-up,tbody.show-for-large-only,tbody.hide-for-xlarge,tbody.hide-for-xlarge-up,tbody.hide-for-xlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge-only{display:table-row-group !important}tr.hide-for-small,tr.hide-for-small-only,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.show-for-large,tr.show-for-large-up,tr.show-for-large-only,tr.hide-for-xlarge,tr.hide-for-xlarge-up,tr.hide-for-xlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge-only{display:table-row !important}td.hide-for-small,td.hide-for-small-only,td.hide-for-medium,td.hide-for-medium-down,td.hide-for-medium-only,td.show-for-medium-up,td.show-for-large,td.show-for-large-up,td.show-for-large-only,td.hide-for-xlarge,td.hide-for-xlarge-up,td.hide-for-xlarge-only,td.hide-for-xxlarge-up,td.hide-for-xxlarge-only,th.hide-for-small,th.hide-for-small-only,th.hide-for-medium,th.hide-for-medium-down,th.hide-for-medium-only,th.show-for-medium-up,th.show-for-large,th.show-for-large-up,th.show-for-large-only,th.hide-for-xlarge,th.hide-for-xlarge-up,th.hide-for-xlarge-only,th.hide-for-xxlarge-up,th.hide-for-xxlarge-only{display:table-cell !important}}@media only screen and (min-width: 90.063em){.hide-for-small,.hide-for-small-only,.hide-for-medium,.hide-for-medium-down,.hide-for-medium-only,.show-for-medium-up,.show-for-large-up,.hide-for-large-only,.show-for-xlarge,.show-for-xlarge-up,.show-for-xlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge-only{display:inherit !important}.show-for-small-only,.show-for-medium,.show-for-medium-down,.show-for-medium-only,.show-for-large,.show-for-large-only,.show-for-large-down,.hide-for-xlarge,.hide-for-xlarge-up,.hide-for-xlarge-only,.show-for-xxlarge-up,.show-for-xxlarge-only{display:none !important}table.hide-for-small,table.hide-for-small-only,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-medium-only,table.show-for-medium-up,table.show-for-large-up,table.hide-for-large-only,table.show-for-xlarge,table.show-for-xlarge-up,table.show-for-xlarge-only,table.hide-for-xxlarge-up,table.hide-for-xxlarge-only{display:table}thead.hide-for-small,thead.hide-for-small-only,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.show-for-large-up,thead.hide-for-large-only,thead.show-for-xlarge,thead.show-for-xlarge-up,thead.show-for-xlarge-only,thead.hide-for-xxlarge-up,thead.hide-for-xxlarge-only{display:table-header-group !important}tbody.hide-for-small,tbody.hide-for-small-only,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.show-for-large-up,tbody.hide-for-large-only,tbody.show-for-xlarge,tbody.show-for-xlarge-up,tbody.show-for-xlarge-only,tbody.hide-for-xxlarge-up,tbody.hide-for-xxlarge-only{display:table-row-group !important}tr.hide-for-small,tr.hide-for-small-only,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.show-for-large-up,tr.hide-for-large-only,tr.show-for-xlarge,tr.show-for-xlarge-up,tr.show-for-xlarge-only,tr.hide-for-xxlarge-up,tr.hide-for-xxlarge-only{display:table-row !important}td.hide-for-small,td.hide-for-small-only,td.hide-for-medium,td.hide-for-medium-down,td.hide-for-medium-only,td.show-for-medium-up,td.show-for-large-up,td.hide-for-large-only,td.show-for-xlarge,td.show-for-xlarge-up,td.show-for-xlarge-only,td.hide-for-xxlarge-up,td.hide-for-xxlarge-only,th.hide-for-small,th.hide-for-small-only,th.hide-for-medium,th.hide-for-medium-down,th.hide-for-medium-only,th.show-for-medium-up,th.show-for-large-up,th.hide-for-large-only,th.show-for-xlarge,th.show-for-xlarge-up,th.show-for-xlarge-only,th.hide-for-xxlarge-up,th.hide-for-xxlarge-only{display:table-cell !important}}@media only screen and (min-width: 120.063em){.hide-for-small,.hide-for-small-only,.hide-for-medium,.hide-for-medium-down,.hide-for-medium-only,.show-for-medium-up,.show-for-large-up,.hide-for-large-only,.hide-for-xlarge-only,.show-for-xlarge-up,.show-for-xxlarge-up,.show-for-xxlarge-only{display:inherit !important}.show-for-small-only,.show-for-medium,.show-for-medium-down,.show-for-medium-only,.show-for-large,.show-for-large-only,.show-for-large-down,.hide-for-xlarge,.show-for-xlarge-only,.hide-for-xxlarge-up,.hide-for-xxlarge-only{display:none !important}table.hide-for-small,table.hide-for-small-only,table.hide-for-medium,table.hide-for-medium-down,table.hide-for-medium-only,table.show-for-medium-up,table.show-for-large-up,table.hide-for-xlarge-only,table.show-for-xlarge-up,table.show-for-xxlarge-up,table.show-for-xxlarge-only{display:table}thead.hide-for-small,thead.hide-for-small-only,thead.hide-for-medium,thead.hide-for-medium-down,thead.hide-for-medium-only,thead.show-for-medium-up,thead.show-for-large-up,thead.hide-for-xlarge-only,thead.show-for-xlarge-up,thead.show-for-xxlarge-up,thead.show-for-xxlarge-only{display:table-header-group !important}tbody.hide-for-small,tbody.hide-for-small-only,tbody.hide-for-medium,tbody.hide-for-medium-down,tbody.hide-for-medium-only,tbody.show-for-medium-up,tbody.show-for-large-up,tbody.hide-for-xlarge-only,tbody.show-for-xlarge-up,tbody.show-for-xxlarge-up,tbody.show-for-xxlarge-only{display:table-row-group !important}tr.hide-for-small,tr.hide-for-small-only,tr.hide-for-medium,tr.hide-for-medium-down,tr.hide-for-medium-only,tr.show-for-medium-up,tr.show-for-large-up,tr.hide-for-xlarge-only,tr.show-for-xlarge-up,tr.show-for-xxlarge-up,tr.show-for-xxlarge-only{display:table-row !important}td.hide-for-small,td.hide-for-small-only,td.hide-for-medium,td.hide-for-medium-down,td.hide-for-medium-only,td.show-for-medium-up,td.show-for-large-up,td.hide-for-xlarge-only,td.show-for-xlarge-up,td.show-for-xxlarge-up,td.show-for-xxlarge-only,th.hide-for-small,th.hide-for-small-only,th.hide-for-medium,th.hide-for-medium-down,th.hide-for-medium-only,th.show-for-medium-up,th.show-for-large-up,th.hide-for-xlarge-only,th.show-for-xlarge-up,th.show-for-xxlarge-up,th.show-for-xxlarge-only{display:table-cell !important}}.show-for-landscape,.hide-for-portrait{display:inherit !important}.hide-for-landscape,.show-for-portrait{display:none !important}table.hide-for-landscape,table.show-for-portrait{display:table}thead.hide-for-landscape,thead.show-for-portrait{display:table-header-group !important}tbody.hide-for-landscape,tbody.show-for-portrait{display:table-row-group !important}tr.hide-for-landscape,tr.show-for-portrait{display:table-row !important}td.hide-for-landscape,td.show-for-portrait,th.hide-for-landscape,th.show-for-portrait{display:table-cell !important}@media only screen and (orientation: landscape){.show-for-landscape,.hide-for-portrait{display:inherit !important}.hide-for-landscape,.show-for-portrait{display:none !important}table.show-for-landscape,table.hide-for-portrait{display:table}thead.show-for-landscape,thead.hide-for-portrait{display:table-header-group !important}tbody.show-for-landscape,tbody.hide-for-portrait{display:table-row-group !important}tr.show-for-landscape,tr.hide-for-portrait{display:table-row !important}td.show-for-landscape,td.hide-for-portrait,th.show-for-landscape,th.hide-for-portrait{display:table-cell !important}}@media only screen and (orientation: portrait){.show-for-portrait,.hide-for-landscape{display:inherit !important}.hide-for-portrait,.show-for-landscape{display:none !important}table.show-for-portrait,table.hide-for-landscape{display:table}thead.show-for-portrait,thead.hide-for-landscape{display:table-header-group !important}tbody.show-for-portrait,tbody.hide-for-landscape{display:table-row-group !important}tr.show-for-portrait,tr.hide-for-landscape{display:table-row !important}td.show-for-portrait,td.hide-for-landscape,th.show-for-portrait,th.hide-for-landscape{display:table-cell !important}}.show-for-touch{display:none !important}.hide-for-touch{display:inherit !important}.touch .show-for-touch{display:inherit !important}.touch .hide-for-touch{display:none !important}table.hide-for-touch{display:table}.touch table.show-for-touch{display:table}thead.hide-for-touch{display:table-header-group !important}.touch thead.show-for-touch{display:table-header-group !important}tbody.hide-for-touch{display:table-row-group !important}.touch tbody.show-for-touch{display:table-row-group !important}tr.hide-for-touch{display:table-row !important}.touch tr.show-for-touch{display:table-row !important}td.hide-for-touch{display:table-cell !important}.touch td.show-for-touch{display:table-cell !important}th.hide-for-touch{display:table-cell !important}.touch th.show-for-touch{display:table-cell !important} diff --git a/coin/static/css/normalize.css b/coin/static/css/normalize.css deleted file mode 100644 index 332bc56..0000000 --- a/coin/static/css/normalize.css +++ /dev/null @@ -1,410 +0,0 @@ -/*! normalize.css v2.1.2 | MIT License | git.io/normalize */ - -/* ========================================================================== - HTML5 display definitions - ========================================================================== */ - -/** - * Correct `block` display not defined in IE 8/9. - */ - -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -nav, -section, -summary { - display: block; -} - -/** - * Correct `inline-block` display not defined in IE 8/9. - */ - -audio, -canvas, -video { - display: inline-block; -} - -/** - * Prevent modern browsers from displaying `audio` without controls. - * Remove excess height in iOS 5 devices. - */ - -audio:not([controls]) { - display: none; - height: 0; -} - -/** - * Address `[hidden]` styling not present in IE 8/9. - * Hide the `template` element in IE, Safari, and Firefox < 22. - */ - -[hidden], -template { - display: none; -} - -script { - display: none !important; -} - -/* ========================================================================== - Base - ========================================================================== */ - -/** - * 1. Set default font family to sans-serif. - * 2. Prevent iOS text size adjust after orientation change, without disabling - * user zoom. - */ - -html { - font-family: sans-serif; /* 1 */ - -ms-text-size-adjust: 100%; /* 2 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} - -/** - * Remove default margin. - */ - -body { - margin: 0; -} - -/* ========================================================================== - Links - ========================================================================== */ - -/** - * Remove the gray background color from active links in IE 10. - */ - -a { - background: transparent; -} - -/** - * Address `outline` inconsistency between Chrome and other browsers. - */ - -a:focus { - outline: thin dotted; -} - -/** - * Improve readability when focused and also mouse hovered in all browsers. - */ - -a:active, -a:hover { - outline: 0; -} - -/* ========================================================================== - Typography - ========================================================================== */ - -/** - * Address variable `h1` font-size and margin within `section` and `article` - * contexts in Firefox 4+, Safari 5, and Chrome. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/** - * Address styling not present in IE 8/9, Safari 5, and Chrome. - */ - -abbr[title] { - border-bottom: 1px dotted; -} - -/** - * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. - */ - -b, -strong { - font-weight: bold; -} - -/** - * Address styling not present in Safari 5 and Chrome. - */ - -dfn { - font-style: italic; -} - -/** - * Address differences between Firefox and other browsers. - */ - -hr { - -moz-box-sizing: content-box; - box-sizing: content-box; - height: 0; -} - -/** - * Address styling not present in IE 8/9. - */ - -mark { - background: #ff0; - color: #000; -} - -/** - * Correct font family set oddly in Safari 5 and Chrome. - */ - -code, -kbd, -pre, -samp { - font-family: monospace, serif; - font-size: 1em; -} - -/** - * Improve readability of pre-formatted text in all browsers. - */ - -pre { - white-space: pre-wrap; -} - -/** - * Set consistent quote types. - */ - -q { - quotes: "\201C" "\201D" "\2018" "\2019"; -} - -/** - * Address inconsistent and variable font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` affecting `line-height` in all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sup { - top: -0.5em; -} - -sub { - bottom: -0.25em; -} - -/* ========================================================================== - Embedded content - ========================================================================== */ - -/** - * Remove border when inside `a` element in IE 8/9. - */ - -img { - border: 0; -} - -/** - * Correct overflow displayed oddly in IE 9. - */ - -svg:not(:root) { - overflow: hidden; -} - -/* ========================================================================== - Figures - ========================================================================== */ - -/** - * Address margin not present in IE 8/9 and Safari 5. - */ - -figure { - margin: 0; -} - -/* ========================================================================== - Forms - ========================================================================== */ - -/** - * Define consistent border, margin, and padding. - */ - -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} - -/** - * 1. Correct `color` not being inherited in IE 8/9. - * 2. Remove padding so people aren't caught out if they zero out fieldsets. - */ - -legend { - border: 0; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * 1. Correct font family not being inherited in all browsers. - * 2. Correct font size not being inherited in all browsers. - * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. - */ - -button, -input, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 2 */ - margin: 0; /* 3 */ -} - -/** - * Address Firefox 4+ setting `line-height` on `input` using `!important` in - * the UA stylesheet. - */ - -button, -input { - line-height: normal; -} - -/** - * Address inconsistent `text-transform` inheritance for `button` and `select`. - * All other form control elements do not inherit `text-transform` values. - * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. - * Correct `select` style inheritance in Firefox 4+ and Opera. - */ - -button, -select { - text-transform: none; -} - -/** - * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` - * and `video` controls. - * 2. Correct inability to style clickable `input` types in iOS. - * 3. Improve usability and consistency of cursor style between image-type - * `input` and others. - */ - -button, -html input[type="button"], /* 1 */ -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; /* 2 */ - cursor: pointer; /* 3 */ -} - -/** - * Re-set default cursor for disabled elements. - */ - -button[disabled], -html input[disabled] { - cursor: default; -} - -/** - * 1. Address box sizing set to `content-box` in IE 8/9. - * 2. Remove excess padding in IE 8/9. - */ - -input[type="checkbox"], -input[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. - * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome - * (include `-moz` to future-proof). - */ - -input[type="search"] { - -webkit-appearance: textfield; /* 1 */ - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; /* 2 */ - box-sizing: content-box; -} - -/** - * Remove inner padding and search cancel button in Safari 5 and Chrome - * on OS X. - */ - -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * Remove inner padding and border in Firefox 4+. - */ - -button::-moz-focus-inner, -input::-moz-focus-inner { - border: 0; - padding: 0; -} - -/** - * 1. Remove default vertical scrollbar in IE 8/9. - * 2. Improve readability and alignment in all browsers. - */ - -textarea { - overflow: auto; /* 1 */ - vertical-align: top; /* 2 */ -} - -/* ========================================================================== - Tables - ========================================================================== */ - -/** - * Remove most spacing between table cells. - */ - -table { - border-collapse: collapse; - border-spacing: 0; -} diff --git a/coin/static/js/foundation.min.js b/coin/static/js/foundation.min.js deleted file mode 100644 index 4c0a673..0000000 --- a/coin/static/js/foundation.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Foundation Responsive Library - * http://foundation.zurb.com - * Copyright 2014, ZURB - * Free to use under the MIT license. - * http://www.opensource.org/licenses/mit-license.php -*/ -(function(e,t,n,r){"use strict";function l(e){if(typeof e=="string"||e instanceof String)e=e.replace(/^['\\/"]+|(;\s?})+|['\\/"]+$/g,"");return e}var i=function(t){var n=t.length;while(n--)e("head").has("."+t[n]).length===0&&e("head").append('')};i(["foundation-mq-small","foundation-mq-medium","foundation-mq-large","foundation-mq-xlarge","foundation-mq-xxlarge","foundation-data-attribute-namespace"]),e(function(){typeof FastClick!="undefined"&&typeof n.body!="undefined"&&FastClick.attach(n.body)});var s=function(t,r){if(typeof t=="string"){if(r){var i;return r.jquery?i=r[0]:i=r,e(i.querySelectorAll(t))}return e(n.querySelectorAll(t))}return e(t,r)},o=function(e){var t=[];return e||t.push("data"),this.namespace.length>0&&t.push(this.namespace),t.push(this.name),t.join("-")},i=function(t){var n=t.length;while(n--)e("head").has("."+t[n]).length===0&&e("head").append('')},u=function(e){var t=e.split("-"),n=t.length,r=[];while(n--)n!==0?r.push(t[n]):this.namespace.length>0?r.push(this.namespace,t[n]):r.push(t[n]);return r.reverse().join("-")},a=function(t,n){var r=this,i=!s(this).data(this.attr_name(!0));if(typeof t=="string")return this[t].call(this,n);s(this.scope).is("["+this.attr_name()+"]")?(s(this.scope).data(this.attr_name(!0)+"-init",e.extend({},this.settings,n||t,this.data_options(s(this.scope)))),i&&this.events(this.scope)):s("["+this.attr_name()+"]",this.scope).each(function(){var i=!s(this).data(r.attr_name(!0)+"-init");s(this).data(r.attr_name(!0)+"-init",e.extend({},r.settings,n||t,r.data_options(s(this)))),i&&r.events(this)})},f=function(e,t){function n(){t(e[0])}function r(){this.one("load",n);if(/MSIE (\d+\.\d+);/.test(navigator.userAgent)){var e=this.attr("src"),t=e.match(/\?/)?"&":"?";t+="random="+(new Date).getTime(),this.attr("src",e+t)}}if(!e.attr("src")){n();return}e[0].complete||e[0].readyState===4?n():r.call(e)};t.matchMedia=t.matchMedia||function(e,t){var n,r=e.documentElement,i=r.firstElementChild||r.firstChild,s=e.createElement("body"),o=e.createElement("div");return o.id="mq-test-1",o.style.cssText="position:absolute;top:-100em",s.style.background="none",s.appendChild(o),function(e){return o.innerHTML='­',r.insertBefore(s,i),n=o.offsetWidth===42,r.removeChild(s),{matches:n,media:e}}}(n),function(e){function u(){n&&(s(u),jQuery.fx.tick())}var n,r=0,i=["webkit","moz"],s=t.requestAnimationFrame,o=t.cancelAnimationFrame;for(;r").appendTo("head")[0].sheet,global:{namespace:""},init:function(e,t,n,r,i){var o,u=[e,n,r,i],a=[];this.rtl=/rtl/i.test(s("html").attr("dir")),this.scope=e||this.scope,this.set_namespace();if(t&&typeof t=="string"&&!/reflow/i.test(t))this.libs.hasOwnProperty(t)&&a.push(this.init_lib(t,u));else for(var f in this.libs)a.push(this.init_lib(f,t));return e},init_lib:function(e,t){return this.libs.hasOwnProperty(e)?(this.patch(this.libs[e]),t&&t.hasOwnProperty(e)?this.libs[e].init.apply(this.libs[e],[this.scope,t[e]]):(t=t instanceof Array?t:Array(t),this.libs[e].init.apply(this.libs[e],t))):function(){}},patch:function(e){e.scope=this.scope,e.namespace=this.global.namespace,e.rtl=this.rtl,e.data_options=this.utils.data_options,e.attr_name=o,e.add_namespace=u,e.bindings=a,e.S=this.utils.S},inherit:function(e,t){var n=t.split(" "),r=n.length;while(r--)this.utils.hasOwnProperty(n[r])&&(e[n[r]]=this.utils[n[r]])},set_namespace:function(){var t=e(".foundation-data-attribute-namespace").css("font-family");if(/false/i.test(t))return;this.global.namespace=t},libs:{},utils:{S:s,throttle:function(e,t){var n=null;return function(){var r=this,i=arguments;clearTimeout(n),n=setTimeout(function(){e.apply(r,i)},t)}},debounce:function(e,t,n){var r,i;return function(){var s=this,o=arguments,u=function(){r=null,n||(i=e.apply(s,o))},a=n&&!r;return clearTimeout(r),r=setTimeout(u,t),a&&(i=e.apply(s,o)),i}},data_options:function(t){function a(e){return!isNaN(e-0)&&e!==null&&e!==""&&e!==!1&&e!==!0}function f(t){return typeof t=="string"?e.trim(t):t}var n={},r,i,s,o=function(e){var t=Foundation.global.namespace;return t.length>0?e.data(t+"-options"):e.data("options")},u=o(t);if(typeof u=="object")return u;s=(u||":").split(";"),r=s.length;while(r--)i=s[r].split(":"),/true/i.test(i[1])&&(i[1]=!0),/false/i.test(i[1])&&(i[1]=!1),a(i[1])&&(i[1]=parseInt(i[1],10)),i.length===2&&i[0].length>0&&(n[f(i[0])]=f(i[1]));return n},register_media:function(t,n){Foundation.media_queries[t]===r&&(e("head").append(''),Foundation.media_queries[t]=l(e("."+n).css("font-family")))},add_custom_rule:function(e,t){if(t===r)Foundation.stylesheet.insertRule(e,Foundation.stylesheet.cssRules.length);else{var n=Foundation.media_queries[t];n!==r&&Foundation.stylesheet.insertRule("@media "+Foundation.media_queries[t]+"{ "+e+" }")}},image_loaded:function(e,t){var n=this,r=e.length;e.each(function(){f(n.S(this),function(){r-=1,r==0&&t(e)})})},random_str:function(e){var t="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");e||(e=Math.floor(Math.random()*t.length));var n="";while(e--)n+=t[Math.floor(Math.random()*t.length)];return n}}},e.fn.foundation=function(){var e=Array.prototype.slice.call(arguments,0);return this.each(function(){return Foundation.init.apply(Foundation,[this].concat(e)),this})}})(jQuery,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.interchange={name:"interchange",version:"5.1.1",cache:{},images_loaded:!1,nodes_loaded:!1,settings:{load_attr:"interchange",named_queries:{"default":"only screen",small:Foundation.media_queries.small,medium:Foundation.media_queries.medium,large:Foundation.media_queries.large,xlarge:Foundation.media_queries.xlarge,xxlarge:Foundation.media_queries.xxlarge,landscape:"only screen and (orientation: landscape)",portrait:"only screen and (orientation: portrait)",retina:"only screen and (-webkit-min-device-pixel-ratio: 2),only screen and (min--moz-device-pixel-ratio: 2),only screen and (-o-min-device-pixel-ratio: 2/1),only screen and (min-device-pixel-ratio: 2),only screen and (min-resolution: 192dpi),only screen and (min-resolution: 2dppx)"},directives:{replace:function(t,n,r){if(/IMG/.test(t[0].nodeName)){var i=t[0].src;if((new RegExp(n,"i")).test(i))return;return t[0].src=n,r(t[0].src)}var s=t.data(this.data_attr+"-last-path");if(s==n)return;return e.get(n,function(e){t.html(e),t.data(this.data_attr+"-last-path",n),r()})}}},init:function(t,n,r){Foundation.inherit(this,"throttle random_str"),this.data_attr=this.set_data_attr(),e.extend(!0,this.settings,n,r),this.bindings(n,r),this.load("images"),this.load("nodes")},events:function(){var n=this;return e(t).off(".interchange").on("resize.fndtn.interchange",n.throttle(function(){n.resize()},50)),this},resize:function(){var t=this.cache;if(!this.images_loaded||!this.nodes_loaded){setTimeout(e.proxy(this.resize,this),50);return}for(var n in t)if(t.hasOwnProperty(n)){var r=this.results(n,t[n]);r&&this.settings.directives[r.scenario[1]].call(this,r.el,r.scenario[0],function(){if(arguments[0]instanceof Array)var e=arguments[0];else var e=Array.prototype.slice.call(arguments,0);r.el.trigger(r.scenario[1],e)})}},results:function(e,t){var n=t.length;if(n>0){var r=this.S("["+this.add_namespace("data-uuid")+'="'+e+'"]');while(n--){var i,s=t[n][2];this.settings.named_queries.hasOwnProperty(s)?i=matchMedia(this.settings.named_queries[s]):i=matchMedia(s);if(i.matches)return{el:r,scenario:t[n]}}}return!1},load:function(e,t){return(typeof this["cached_"+e]=="undefined"||t)&&this["update_"+e](),this["cached_"+e]},update_images:function(){var e=this.S("img["+this.data_attr+"]"),t=e.length,n=t,r=0,i=this.data_attr;this.cache={},this.cached_images=[],this.images_loaded=t===0;while(n--){r++;if(e[n]){var s=e[n].getAttribute(i)||"";s.length>0&&this.cached_images.push(e[n])}r===t&&(this.images_loaded=!0,this.enhance("images"))}return this},update_nodes:function(){var e=this.S("["+this.data_attr+"]").not("img"),t=e.length,n=t,r=0,i=this.data_attr;this.cached_nodes=[],this.nodes_loaded=t===0;while(n--){r++;var s=e[n].getAttribute(i)||"";s.length>0&&this.cached_nodes.push(e[n]),r===t&&(this.nodes_loaded=!0,this.enhance("nodes"))}return this},enhance:function(n){var r=this["cached_"+n].length;while(r--)this.object(e(this["cached_"+n][r]));return e(t).trigger("resize")},parse_params:function(e,t,n){return[this.trim(e),this.convert_directive(t),this.trim(n)]},convert_directive:function(e){var t=this.trim(e);return t.length>0?t:"replace"},object:function(e){var t=this.parse_data_attr(e),n=[],r=t.length;if(r>0)while(r--){var i=t[r].split(/\((.*?)(\))$/);if(i.length>1){var s=i[0].split(","),o=this.parse_params(s[0],s[1],i[1]);n.push(o)}}return this.store(e,n)},uuid:function(e){function r(){return n.random_str(6)}var t=e||"-",n=this;return r()+r()+t+r()+t+r()+t+r()+t+r()+r()+r()},store:function(e,t){var n=this.uuid(),r=e.data(this.add_namespace("uuid",!0));return this.cache[r]?this.cache[r]:(e.attr(this.add_namespace("data-uuid"),n),this.cache[n]=t)},trim:function(t){return typeof t=="string"?e.trim(t):t},set_data_attr:function(e){return e?this.namespace.length>0?this.namespace+"-"+this.settings.load_attr:this.settings.load_attr:this.namespace.length>0?"data-"+this.namespace+"-"+this.settings.load_attr:"data-"+this.settings.load_attr},parse_data_attr:function(e){var t=e.attr(this.attr_name()).split(/\[(.*?)\]/),n=t.length,r=[];while(n--)t[n].replace(/[\W\d]+/,"").length>4&&r.push(t[n]);return r},reflow:function(){this.load("images",!0),this.load("nodes",!0)}}}(jQuery,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.equalizer={name:"equalizer",version:"5.1.1",settings:{use_tallest:!0,before_height_change:e.noop,after_height_change:e.noop},init:function(e,t,n){this.bindings(t,n),this.reflow()},events:function(){this.S(t).off(".equalizer").on("resize.fndtn.equalizer",function(e){this.reflow()}.bind(this))},equalize:function(t){var n=!1,r=t.find("["+this.attr_name()+"-watch]"),i=r.first().offset().top,s=t.data(this.attr_name(!0)+"-init");if(r.length===0)return;s.before_height_change(),t.trigger("before-height-change"),r.height("inherit"),r.each(function(){var t=e(this);t.offset().top!==i&&(n=!0)});if(n)return;var o=r.map(function(){return e(this).outerHeight()});if(s.use_tallest){var u=Math.max.apply(null,o);r.height(u)}else{var a=Math.min.apply(null,o);r.height(a)}s.after_height_change(),t.trigger("after-height-change")},reflow:function(){var t=this;this.S("["+this.attr_name()+"]",this.scope).each(function(){t.equalize(e(this))})}}}(jQuery,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.abide={name:"abide",version:"5.1.1",settings:{live_validate:!0,focus_on_invalid:!0,error_labels:!0,timeout:1e3,patterns:{alpha:/^[a-zA-Z]+$/,alpha_numeric:/^[a-zA-Z0-9]+$/,integer:/^\d+$/,number:/-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?/,password:/(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/,card:/^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/,cvv:/^([0-9]){3,4}$/,email:/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,url:/(https?|ftp|file|ssh):\/\/(((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?/,domain:/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$/,datetime:/([0-2][0-9]{3})\-([0-1][0-9])\-([0-3][0-9])T([0-5][0-9])\:([0-5][0-9])\:([0-5][0-9])(Z|([\-\+]([0-1][0-9])\:00))/,date:/(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))/,time:/(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9]){2}/,dateISO:/\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/,month_day_year:/(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d/,color:/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/}},timer:null,init:function(e,t,n){this.bindings(t,n)},events:function(t){var n=this,r=n.S(t).attr("novalidate","novalidate"),i=r.data(this.attr_name(!0)+"-init");this.invalid_attr=this.add_namespace("data-invalid"),r.off(".abide").on("submit.fndtn.abide validate.fndtn.abide",function(e){var t=/ajax/i.test(n.S(this).attr(n.attr_name()));return n.validate(n.S(this).find("input, textarea, select").get(),e,t)}).on("reset",function(){return n.reset(e(this))}).find("input, textarea, select").off(".abide").on("blur.fndtn.abide change.fndtn.abide",function(e){n.validate([this],e)}).on("keydown.fndtn.abide",function(t){var r=e(this).closest("form").data("abide-init");r.live_validate===!0&&(clearTimeout(n.timer),n.timer=setTimeout(function(){n.validate([this],t)}.bind(this),r.timeout))})},reset:function(t){t.removeAttr(this.invalid_attr),e(this.invalid_attr,t).removeAttr(this.invalid_attr),e(".error",t).not("small").removeClass("error")},validate:function(e,t,n){var r=this.parse_patterns(e),i=r.length,s=this.S(e[0]).closest("form"),o=/submit/.test(t.type);for(var u=0;u0?[e,this.settings.patterns[r],n]:r.length>0?[e,new RegExp(r),n]:this.settings.patterns.hasOwnProperty(t)?[e,this.settings.patterns[t],n]:(r=/.*/,[e,r,n])},check_validation_and_apply_styles:function(t){var n=t.length,r=[];while(n--){var i=t[n][0],s=t[n][2],o=i.value,u=this.S(i).parent(),a=i.getAttribute(this.add_namespace("data-equalto")),f=i.type==="radio",l=i.type==="checkbox",c=this.S('label[for="'+i.getAttribute("id")+'"]'),h=s?i.value.length>0:!0,p;u.is("label")?p=u.parent():p=u,f&&s?r.push(this.valid_radio(i,s)):l&&s?r.push(this.valid_checkbox(i,s)):a&&s?r.push(this.valid_equal(i,s,p)):t[n][1].test(o)&&h||!s&&i.value.length<1?(this.S(i).removeAttr(this.invalid_attr),p.removeClass("error"),c.length>0&&this.settings.error_labels&&c.removeClass("error"),r.push(!0),e(i).triggerHandler("valid")):(this.S(i).attr(this.invalid_attr,""),p.addClass("error"),c.length>0&&this.settings.error_labels&&c.addClass("error"),r.push(!1),e(i).triggerHandler("invalid"))}return r},valid_checkbox:function(e,t){var e=this.S(e),n=e.is(":checked")||!t;return n?e.removeAttr(this.invalid_attr).parent().removeClass("error"):e.attr(this.invalid_attr,"").parent().addClass("error"),n},valid_radio:function(e,t){var r=e.getAttribute("name"),i=n.getElementsByName(r),s=i.length,o=!1;for(var u=0;u0&&(i(t.target).is("["+r.attr_name()+"-content]")||e.contains(n.first()[0],t.target))){t.stopPropagation();return}r.close.call(r,i("["+r.attr_name()+"-content]"))}).on("opened.fndtn.dropdown","["+r.attr_name()+"-content]",function(){r.settings.opened.call(this)}).on("closed.fndtn.dropdown","["+r.attr_name()+"-content]",function(){r.settings.closed.call(this)}),i(t).off(".dropdown").on("resize.fndtn.dropdown",r.throttle(function(){r.resize.call(r)},50)).trigger("resize")},close:function(e){var t=this;e.each(function(){t.S(this).hasClass(t.settings.active_class)&&(t.S(this).css(Foundation.rtl?"right":"left","-99999px").removeClass(t.settings.active_class),t.S(this).trigger("closed"))})},closeall:function(){var t=this;e.each(t.S("["+this.attr_name()+"-content]"),function(){t.close.call(t,t.S(this))})},open:function(e,t){this.css(e.addClass(this.settings.active_class),t),e.trigger("opened")},data_attr:function(){return this.namespace.length>0?this.namespace+"-"+this.name:this.name},toggle:function(e){var t=this.S("#"+e.data(this.data_attr()));if(t.length===0)return;this.close.call(this,this.S("["+this.attr_name()+"-content]").not(t)),t.hasClass(this.settings.active_class)?this.close.call(this,t):(this.close.call(this,this.S("["+this.attr_name()+"-content]")),this.open.call(this,t,e))},resize:function(){var e=this.S("["+this.attr_name()+"-content].open"),t=this.S("["+this.attr_name()+"='"+e.attr("id")+"']");e.length&&t.length&&this.css(e,t)},css:function(e,n){var r=e.offsetParent(),i=n.offset();i.top-=r.offset().top,i.left-=r.offset().left;if(this.small())e.css({position:"absolute",width:"95%","max-width":"none",top:i.top+n.outerHeight()}),e.css(Foundation.rtl?"right":"left","2.5%");else{if(!Foundation.rtl&&this.S(t).width()>e.outerWidth()+n.offset().left){var s=i.left;e.hasClass("right")&&e.removeClass("right")}else{e.hasClass("right")||e.addClass("right");var s=i.left-(e.outerWidth()-n.outerWidth())}e.attr("style","").css({position:"absolute",top:i.top+n.outerHeight(),left:s})}return e},small:function(){return matchMedia(Foundation.media_queries.small).matches&&!matchMedia(Foundation.media_queries.medium).matches},off:function(){this.S(this.scope).off(".fndtn.dropdown"),this.S("html, body").off(".fndtn.dropdown"),this.S(t).off(".fndtn.dropdown"),this.S("[data-dropdown-content]").off(".fndtn.dropdown"),this.settings.init=!1},reflow:function(){}}}(jQuery,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.alert={name:"alert",version:"5.1.1",settings:{animation:"fadeOut",speed:300,callback:function(){}},init:function(e,t,n){this.bindings(t,n)},events:function(){var t=this,n=this.S;e(this.scope).off(".alert").on("click.fndtn.alert","["+this.attr_name()+"] a.close",function(e){var r=n(this).closest("["+t.attr_name()+"]"),i=r.data(t.attr_name(!0)+"-init")||t.settings;e.preventDefault(),r[i.animation](i.speed,function(){n(this).trigger("closed").remove(),i.callback()})})},reflow:function(){}}}(jQuery,this,this.document),function(e,t,n,r){"use strict";Foundation.libs["magellan-expedition"]={name:"magellan-expedition",version:"5.1.1",settings:{active_class:"active",threshold:0,destination_threshold:20,throttle_delay:30},init:function(e,t,n){Foundation.inherit(this,"throttle"),this.bindings(t,n)},events:function(){var n=this,r=n.S,i=n.settings;n.set_expedition_position(),r(n.scope).off(".magellan").on("click.fndtn.magellan","["+n.add_namespace("data-magellan-arrival")+'] a[href^="#"]',function(r){r.preventDefault();var i=e(this).closest("["+n.attr_name()+"]"),s=i.data("magellan-expedition-init"),o=this.hash.split("#").join(""),u=e("a[name="+o+"]");u.length===0&&(u=e("#"+o));var a=u.offset().top;i.css("position")==="fixed"&&(a-=i.outerHeight()),e("html, body").stop().animate({scrollTop:a},700,"swing",function(){t.location.hash="#"+o})}).on("scroll.fndtn.magellan",n.throttle(this.check_for_arrivals.bind(this),i.throttle_delay)).on("resize.fndtn.magellan",n.throttle(this.set_expedition_position.bind(this),i.throttle_delay))},check_for_arrivals:function(){var e=this;e.update_arrivals(),e.update_expedition_positions()},set_expedition_position:function(){var t=this;e("["+this.attr_name()+"=fixed]",t.scope).each(function(n,r){var i=e(this),s=i.attr("styles"),o;i.attr("style",""),o=i.offset().top,i.data(t.data_attr("magellan-top-offset"),o),i.attr("style",s)})},update_expedition_positions:function(){var n=this,r=e(t).scrollTop();e("["+this.attr_name()+"=fixed]",n.scope).each(function(){var t=e(this),i=t.data("magellan-top-offset");if(r>=i){var s=t.prev("["+n.add_namespace("data-magellan-expedition-clone")+"]");s.length===0&&(s=t.clone(),s.removeAttr(n.attr_name()),s.attr(n.add_namespace("data-magellan-expedition-clone"),""),t.before(s)),t.css({position:"fixed",top:0})}else t.prev("["+n.add_namespace("data-magellan-expedition-clone")+"]").remove(),t.attr("style","")})},update_arrivals:function(){var n=this,r=e(t).scrollTop();e("["+this.attr_name()+"]",n.scope).each(function(){var t=e(this),i=i=t.data(n.attr_name(!0)+"-init"),s=n.offsets(t,r),o=t.find("["+n.add_namespace("data-magellan-arrival")+"]"),u=!1;s.each(function(e,r){if(r.viewport_offset>=r.top_offset){var s=t.find("["+n.add_namespace("data-magellan-arrival")+"]");return s.not(r.arrival).removeClass(i.active_class),r.arrival.addClass(i.active_class),u=!0,!0}}),u||o.removeClass(i.active_class)})},offsets:function(t,n){var r=this,i=t.data(r.attr_name(!0)+"-init"),s=n+i.destination_threshold;return t.find("["+r.add_namespace("data-magellan-arrival")+"]").map(function(t,n){var i=e(this).data(r.data_attr("magellan-arrival")),o=e("["+r.add_namespace("data-magellan-destination")+"="+i+"]");if(o.length>0){var u=o.offset().top;return{destination:o,arrival:e(this),top_offset:u,viewport_offset:s}}}).sort(function(e,t){return e.top_offsett.top_offset?1:0})},data_attr:function(e){return this.namespace.length>0?this.namespace+"-"+e:e},off:function(){this.S(this.scope).off(".magellan"),this.S(t).off(".magellan")},reflow:function(){var t=this;e("["+t.add_namespace("data-magellan-expedition-clone")+"]",t.scope).remove()}}}(jQuery,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.reveal={name:"reveal",version:"5.1.1",locked:!1,settings:{animation:"fadeAndPop",animation_speed:250,close_on_background_click:!0,close_on_esc:!0,dismiss_modal_class:"close-reveal-modal",bg_class:"reveal-modal-bg",open:function(){},opened:function(){},close:function(){},closed:function(){},bg:e(".reveal-modal-bg"),css:{open:{opacity:0,visibility:"visible",display:"block"},close:{opacity:1,visibility:"hidden",display:"none"}}},init:function(t,n,r){e.extend(!0,this.settings,n,r),this.bindings(n,r)},events:function(e){var t=this,r=t.S;return r(this.scope).off(".reveal").on("click.fndtn.reveal","["+this.add_namespace("data-reveal-id")+"]",function(e){e.preventDefault();if(!t.locked){var n=r(this),i=n.data(t.data_attr("reveal-ajax"));t.locked=!0;if(typeof i=="undefined")t.open.call(t,n);else{var s=i===!0?n.attr("href"):i;t.open.call(t,n,{url:s})}}}),r(n).on("click.fndtn.reveal",this.close_targets(),function(e){e.preventDefault();if(!t.locked){var n=r("["+t.attr_name()+"].open").data(t.attr_name(!0)+"-init"),i=r(e.target)[0]===r("."+n.bg_class)[0];if(i&&!n.close_on_background_click)return;t.locked=!0,t.close.call(t,i?r("["+t.attr_name()+"].open"):r(this).closest("["+t.attr_name()+"]"))}}),r("["+t.attr_name()+"]",this.scope).length>0?r(this.scope).on("open.fndtn.reveal",this.settings.open).on("opened.fndtn.reveal",this.settings.opened).on("opened.fndtn.reveal",this.open_video).on("close.fndtn.reveal",this.settings.close).on("closed.fndtn.reveal",this.settings.closed).on("closed.fndtn.reveal",this.close_video):r(this.scope).on("open.fndtn.reveal","["+t.attr_name()+"]",this.settings.open).on("opened.fndtn.reveal","["+t.attr_name()+"]",this.settings.opened).on("opened.fndtn.reveal","["+t.attr_name()+"]",this.open_video).on("close.fndtn.reveal","["+t.attr_name()+"]",this.settings.close).on("closed.fndtn.reveal","["+t.attr_name()+"]",this.settings.closed).on("closed.fndtn.reveal","["+t.attr_name()+"]",this.close_video),!0},key_up_on:function(e){var t=this;return t.S("body").off("keyup.fndtn.reveal").on("keyup.fndtn.reveal",function(e){var n=t.S("["+t.attr_name()+"].open"),r=n.data(t.attr_name(!0)+"-init");r&&e.which===27&&r.close_on_esc&&!t.locked&&t.close.call(t,n)}),!0},key_up_off:function(e){return this.S("body").off("keyup.fndtn.reveal"),!0},open:function(t,n){var r=this;if(t)if(typeof t.selector!="undefined")var i=r.S("#"+t.data(r.data_attr("reveal-id")));else{var i=r.S(this.scope);n=t}else var i=r.S(this.scope);var s=i.data(r.attr_name(!0)+"-init");if(!i.hasClass("open")){var o=r.S("["+r.attr_name()+"].open");typeof i.data("css-top")=="undefined"&&i.data("css-top",parseInt(i.css("top"),10)).data("offset",this.cache_offset(i)),this.key_up_on(i),i.trigger("open"),o.length<1&&this.toggle_bg(i),typeof n=="string"&&(n={url:n});if(typeof n=="undefined"||!n.url){if(o.length>0){var u=o.data(r.attr_name(!0)+"-init");this.hide(o,u.css.close)}this.show(i,s.css.open)}else{var a=typeof n.success!="undefined"?n.success:null;e.extend(n,{success:function(t,n,u){e.isFunction(a)&&a(t,n,u),i.html(t),r.S(i).foundation("section","reflow");if(o.length>0){var f=o.data(r.attr_name(!0));r.hide(o,f.css.close)}r.show(i,s.css.open)}}),e.ajax(n)}}},close:function(e){var e=e&&e.length?e:this.S(this.scope),t=this.S("["+this.attr_name()+"].open"),n=e.data(this.attr_name(!0)+"-init");t.length>0&&(this.locked=!0,this.key_up_off(e),e.trigger("close"),this.toggle_bg(e),this.hide(t,n.css.close,n))},close_targets:function(){var e="."+this.settings.dismiss_modal_class;return this.settings.close_on_background_click?e+", ."+this.settings.bg_class:e},toggle_bg:function(t){var n=t.data(this.attr_name(!0));this.S("."+this.settings.bg_class).length===0&&(this.settings.bg=e("
    ",{"class":this.settings.bg_class}).appendTo("body")),this.settings.bg.filter(":visible").length>0?this.hide(this.settings.bg):this.show(this.settings.bg)},show:function(n,r){if(r){var i=n.data(this.attr_name(!0)+"-init");if(n.parent("body").length===0){var s=n.wrap('
    ').parent(),o=this.settings.rootElement||"body";n.on("closed.fndtn.reveal.wrapped",function(){n.detach().appendTo(s),n.unwrap().unbind("closed.fndtn.reveal.wrapped")}),n.detach().appendTo(o)}if(/pop/i.test(i.animation)){r.top=e(t).scrollTop()-n.data("offset")+"px";var u={top:e(t).scrollTop()+n.data("css-top")+"px",opacity:1};return setTimeout(function(){return n.css(r).animate(u,i.animation_speed,"linear",function(){this.locked=!1,n.trigger("opened")}.bind(this)).addClass("open")}.bind(this),i.animation_speed/2)}if(/fade/i.test(i.animation)){var u={opacity:1};return setTimeout(function(){return n.css(r).animate(u,i.animation_speed,"linear",function(){this.locked=!1,n.trigger("opened")}.bind(this)).addClass("open")}.bind(this),i.animation_speed/2)}return n.css(r).show().css({opacity:1}).addClass("open").trigger("opened")}var i=this.settings;return/fade/i.test(i.animation)?n.fadeIn(i.animation_speed/2):(this.locked=!1,n.show())},hide:function(n,r){if(r){var i=n.data(this.attr_name(!0)+"-init");if(/pop/i.test(i.animation)){var s={top:-e(t).scrollTop()-n.data("offset")+"px",opacity:0};return setTimeout(function(){return n.animate(s,i.animation_speed,"linear",function(){this.locked=!1,n.css(r).trigger("closed")}.bind(this)).removeClass("open")}.bind(this),i.animation_speed/2)}if(/fade/i.test(i.animation)){var s={opacity:0};return setTimeout(function(){return n.animate(s,i.animation_speed,"linear",function(){this.locked=!1,n.css(r).trigger("closed")}.bind(this)).removeClass("open")}.bind(this),i.animation_speed/2)}return n.hide().css(r).removeClass("open").trigger("closed")}var i=this.settings;return/fade/i.test(i.animation)?n.fadeOut(i.animation_speed/2):n.hide()},close_video:function(t){var n=e(".flex-video",t.target),r=e("iframe",n);r.length>0&&(r.attr("data-src",r[0].src),r.attr("src","about:blank"),n.hide())},open_video:function(t){var n=e(".flex-video",t.target),i=n.find("iframe");if(i.length>0){var s=i.attr("data-src");if(typeof s=="string")i[0].src=i.attr("data-src");else{var o=i[0].src;i[0].src=r,i[0].src=o}n.show()}},data_attr:function(e){return this.namespace.length>0?this.namespace+"-"+e:e},cache_offset:function(e){var t=e.show().height()+parseInt(e.css("top"),10);return e.hide(),t},off:function(){e(this.scope).off(".fndtn.reveal")},reflow:function(){}}}(jQuery,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.tooltip={name:"tooltip",version:"5.1.1",settings:{additional_inheritable_classes:[],tooltip_class:".tooltip",append_to:"body",touch_close_text:"Tap To Close",disable_for_touch:!1,hover_delay:200,tip_template:function(e,t){return''+t+''}},cache:{},init:function(e,t,n){Foundation.inherit(this,"random_str"),this.bindings(t,n)},events:function(){var t=this,r=t.S;Modernizr.touch?r(n).off(".tooltip").on("click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip","["+this.attr_name()+"]:not(a)",function(n){var i=e.extend({},t.settings,t.data_options(r(this)));i.disable_for_touch||(n.preventDefault(),r(i.tooltip_class).hide(),t.showOrCreateTip(r(this)))}).on("click.fndtn.tooltip touchstart.fndtn.tooltip touchend.fndtn.tooltip",this.settings.tooltip_class,function(e){e.preventDefault(),r(this).fadeOut -(150)}):r(n).off(".tooltip").on("mouseenter.fndtn.tooltip mouseleave.fndtn.tooltip","["+this.attr_name()+"]",function(e){var n=r(this);if(/enter|over/i.test(e.type))this.timer=setTimeout(function(){var e=t.showOrCreateTip(n)}.bind(this),t.settings.hover_delay);else if(e.type==="mouseout"||e.type==="mouseleave")clearTimeout(this.timer),t.hide(n)})},showOrCreateTip:function(e){var t=this.getTip(e);return t&&t.length>0?this.show(e):this.create(e)},getTip:function(e){var t=this.selector(e),n=null;return t&&(n=this.S('span[data-selector="'+t+'"]'+this.settings.tooltip_class)),typeof n=="object"?n:!1},selector:function(e){var t=e.attr("id"),n=e.attr(this.attr_name())||e.attr("data-selector");return(t&&t.length<1||!t)&&typeof n!="string"&&(n="tooltip"+this.random_str(6),e.attr("data-selector",n)),t&&t.length>0?t:n},create:function(t){var n=e(this.settings.tip_template(this.selector(t),e("
    ").html(t.attr("title")).html())),r=this.inheritable_classes(t);n.addClass(r).appendTo(this.settings.append_to),Modernizr.touch&&n.append(''+this.settings.touch_close_text+""),t.removeAttr("title").attr("title",""),this.show(t)},reposition:function(e,t,n){var r,i,s,o,u,a;t.css("visibility","hidden").show(),r=e.data("width"),i=t.children(".nub"),s=i.outerHeight(),o=i.outerHeight(),this.small()?t.css({width:"100%"}):t.css({width:r?r:"auto"}),a=function(e,t,n,r,i,s){return e.css({top:t?t:"auto",bottom:r?r:"auto",left:i?i:"auto",right:n?n:"auto"}).end()},a(t,e.offset().top+e.outerHeight()+10,"auto","auto",e.offset().left);if(this.small())a(t,e.offset().top+e.outerHeight()+10,"auto","auto",12.5,this.S(this.scope).width()),t.addClass("tip-override"),a(i,-s,"auto","auto",e.offset().left+10);else{var f=e.offset().left;Foundation.rtl&&(f=e.offset().left+e.outerWidth()-t.outerWidth()),a(t,e.offset().top+e.outerHeight()+10,"auto","auto",f),t.removeClass("tip-override"),i.removeAttr("style"),n&&n.indexOf("tip-top")>-1?a(t,e.offset().top-t.outerHeight()-10,"auto","auto",f).removeClass("tip-override"):n&&n.indexOf("tip-left")>-1?a(t,e.offset().top+e.outerHeight()/2-t.outerHeight()/2,"auto","auto",e.offset().left-t.outerWidth()-s).removeClass("tip-override"):n&&n.indexOf("tip-right")>-1&&a(t,e.offset().top+e.outerHeight()/2-t.outerHeight()/2,"auto","auto",e.offset().left+e.outerWidth()+s).removeClass("tip-override")}t.css("visibility","visible").hide()},small:function(){return matchMedia(Foundation.media_queries.small).matches},inheritable_classes:function(t){var n=["tip-top","tip-left","tip-bottom","tip-right","radius","round"].concat(this.settings.additional_inheritable_classes),r=t.attr("class"),i=r?e.map(r.split(" "),function(t,r){if(e.inArray(t,n)!==-1)return t}).join(" "):"";return e.trim(i)},show:function(e){var t=this.getTip(e);return this.reposition(e,t,e.attr("class")),t.fadeIn(150)},hide:function(e){var t=this.getTip(e);return t.fadeOut(150)},reload:function(){var t=e(this);return t.data("fndtn-tooltips")?t.foundationTooltips("destroy").foundationTooltips("init"):t.foundationTooltips("init")},off:function(){this.S(this.scope).off(".fndtn.tooltip"),this.S(this.settings.tooltip_class).each(function(t){e("["+this.attr_name()+"]").get(t).attr("title",e(this).text())}).remove()},reflow:function(){}}}(jQuery,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.tab={name:"tab",version:"5.1.1",settings:{active_class:"active",callback:function(){}},init:function(e,t,n){this.bindings(t,n)},events:function(){var e=this,t=this.S;t(this.scope).off(".tab").on("click.fndtn.tab","["+this.attr_name()+"] > dd > a",function(n){n.preventDefault(),n.stopPropagation();var r=t(this).parent(),i=r.closest("["+e.attr_name()+"]"),s=t("#"+this.href.split("#")[1]),o=r.siblings(),u=i.data(e.attr_name(!0)+"-init");t(this).data(e.data_attr("tab-content"))&&(s=t("#"+t(this).data(e.data_attr("tab-content")).split("#")[1])),r.addClass(u.active_class).triggerHandler("opened"),o.removeClass(u.active_class),s.siblings().removeClass(u.active_class).end().addClass(u.active_class),u.callback(r),i.triggerHandler("toggled",[r])})},data_attr:function(e){return this.namespace.length>0?this.namespace+"-"+e:e},off:function(){},reflow:function(){}}}(jQuery,this,this.document),function(e,t,n,r){"use strict";Foundation.libs.clearing={name:"clearing",version:"5.1.1",settings:{templates:{viewing:'×'},close_selectors:".clearing-close",touch_label:"← Swipe to Advance →",init:!1,locked:!1},init:function(e,t,n){var r=this;Foundation.inherit(this,"throttle image_loaded"),this.bindings(t,n),r.S(this.scope).is("["+this.attr_name()+"]")?this.assemble(r.S("li",this.scope)):r.S("["+this.attr_name()+"]",this.scope).each(function(){r.assemble(r.S("li",this))})},events:function(e){var n=this,r=n.S;r(this.scope).off(".clearing").on("click.fndtn.clearing","ul["+this.attr_name()+"] li",function(e,t,i){var t=t||r(this),i=i||t,s=t.next("li"),o=t.closest("["+n.attr_name()+"]").data(n.attr_name(!0)+"-init"),u=r(e.target);e.preventDefault(),o||(n.init(),o=t.closest("["+n.attr_name()+"]").data(n.attr_name(!0)+"-init")),i.hasClass("visible")&&t[0]===i[0]&&s.length>0&&n.is_open(t)&&(i=s,u=r("img",i)),n.open(u,t,i),n.update_paddles(i)}).on("click.fndtn.clearing",".clearing-main-next",function(e){n.nav(e,"next")}).on("click.fndtn.clearing",".clearing-main-prev",function(e){n.nav(e,"prev")}).on("click.fndtn.clearing",this.settings.close_selectors,function(e){Foundation.libs.clearing.close(e,this)}).on("keydown.fndtn.clearing",function(e){n.keydown(e)}),r(t).off(".clearing").on("resize.fndtn.clearing",function(){n.resize()}),this.swipe_events(e)},swipe_events:function(e){var t=this,n=t.S;n(this.scope).on("touchstart.fndtn.clearing",".visible-img",function(e){e.touches||(e=e.originalEvent);var t={start_page_x:e.touches[0].pageX,start_page_y:e.touches[0].pageY,start_time:(new Date).getTime(),delta_x:0,is_scrolling:r};n(this).data("swipe-transition",t),e.stopPropagation()}).on("touchmove.fndtn.clearing",".visible-img",function(e){e.touches||(e=e.originalEvent);if(e.touches.length>1||e.scale&&e.scale!==1)return;var r=n(this).data("swipe-transition");typeof r=="undefined"&&(r={}),r.delta_x=e.touches[0].pageX-r.start_page_x,typeof r.is_scrolling=="undefined"&&(r.is_scrolling=!!(r.is_scrolling||Math.abs(r.delta_x)
    ');var r=this.S("#foundationClearingHolder"),i=n.data(this.attr_name(!0)+"-init"),s=n.detach(),o={grid:'",viewing:i.templates.viewing},u='
    '+o.viewing+o.grid+"
    ",a=this.settings.touch_label;Modernizr.touch&&(u=e(u).find(".clearing-touch-label").html(a).end()),r.after(u).remove()},open:function(e,t,n){var r=this,i=n.closest(".clearing-assembled"),s=r.S("div",i).first(),o=r.S(".visible-img",s),u=r.S("img",o).not(e),a=r.S(".clearing-touch-label",s);this.locked()||(u.attr("src",this.load(e)).css("visibility","hidden"),this.image_loaded(u,function(){u.css("visibility","visible"),i.addClass("clearing-blackout"),s.addClass("clearing-container"),o.show(),this.fix_height(n).caption(r.S(".clearing-caption",o),e).center_and_label(u,a).shift(t,n,function(){n.siblings().removeClass("visible"),n.addClass("visible")})}.bind(this)))},close:function(t,n){t.preventDefault();var r=function(e){return/blackout/.test(e.selector)?e:e.closest(".clearing-blackout")}(e(n)),i,s;return n===t.target&&r&&(i=e("div",r).first(),s=e(".visible-img",i),this.settings.prev_index=0,e("ul["+this.attr_name()+"]",r).attr("style","").closest(".clearing-blackout").removeClass("clearing-blackout"),i.removeClass("clearing-container"),s.hide()),!1},is_open:function(e){return e.parent().prop("style").length>0},keydown:function(t){var n=e("ul["+this.attr_name()+"]",".clearing-blackout"),r=this.rtl?37:39,i=this.rtl?39:37,s=27;t.which===r&&this.go(n,"next"),t.which===i&&this.go(n,"prev"),t.which===s&&this.S("a.clearing-close").trigger("click")},nav:function(t,n){var r=e("ul["+this.attr_name()+"]",".clearing-blackout");t.preventDefault(),this.go(r,n)},resize:function(){var t=e("img",".clearing-blackout .visible-img"),n=e(".clearing-touch-label",".clearing-blackout");t.length&&this.center_and_label(t,n)},fix_height:function(e){var t=e.parent().children(),n=this;return t.each(function(){var e=n.S(this),t=e.find("img");e.height()>t.outerHeight()&&e.addClass("fix-height")}).closest("ul").width(t.length*100+"%"),this},update_paddles:function(e){var t=e.closest(".carousel").siblings(".visible-img");e.next().length>0?this.S(".clearing-main-next",t).removeClass("disabled"):this.S(".clearing-main-next",t).addClass("disabled"),e.prev().length>0?this.S(".clearing-main-prev",t).removeClass("disabled"):this.S(".clearing-main-prev",t).addClass("disabled")},center_and_label:function(e,t){return this.rtl?(e.css({marginRight:-(e.outerWidth()/2),marginTop:-(e.outerHeight()/2),left:"auto",right:"50%"}),t.css({marginRight:-(t.outerWidth()/2),marginTop:-(e.outerHeight()/2)-t.outerHeight()-10,left:"auto",right:"50%"})):(e.css({marginLeft:-(e.outerWidth()/2),marginTop:-(e.outerHeight()/2)}),t.css({marginLeft:-(t.outerWidth()/2),marginTop:-(e.outerHeight()/2)-t.outerHeight()-10})),this},load:function(e){if(e[0].nodeName==="A")var t=e.attr("href");else var t=e.parent().attr("href");return this.preload(e),t?t:e.attr("src")},preload:function(e){this.img(e.closest("li").next()).img(e.closest("li").prev())},img:function(e){if(e.length){var t=new Image,n=this.S("a",e);n.length?t.src=n.attr("href"):t.src=this.S("img",e).attr("src")}return this},caption:function(e,t){var n=t.data("caption");return n?e.html(n).show():e.text("").hide(),this},go:function(e,t){var n=this.S(".visible",e),r=n[t]();r.length&&this.S("img",r).trigger("click",[n,r])},shift:function(e,t,n){var r=t.parent(),i=this.settings.prev_index||t.index(),s=this.direction(r,e,t),o=this.rtl?"right":"left",u=parseInt(r.css("left"),10),a=t.outerWidth(),f,l={};t.index()!==i&&!/skip/.test(s)?/left/.test(s)?(this.lock(),l[o]=u+a,r.animate(l,300,this.unlock())):/right/.test(s)&&(this.lock(),l[o]=u-a,r.animate(l,300,this.unlock())):/skip/.test(s)&&(f=t.index()-this.settings.up_count,this.lock(),f>0?(l[o]=-(f*a),r.animate(l,300,this.unlock())):(l[o]=0,r.animate(l,300,this.unlock()))),n()},direction:function(e,t,n){var r=this.S("li",e),i=r.outerWidth()+r.outerWidth()/4,s=Math.floor(this.S(".clearing-container").outerWidth()/i)-1,o=r.index(n),u;return this.settings.up_count=s,this.adjacent(this.settings.prev_index,o)?o>s&&o>this.settings.prev_index?u="right":o>s-1&&o<=this.settings.prev_index?u="left":u=!1:u="skip",this.settings.prev_index=o,u},adjacent:function(e,t){for(var n=t+1;n>=t-1;n--)if(n===e)return!0;return!1},lock:function(){this.settings.locked=!0},unlock:function(){this.settings.locked=!1},locked:function(){return this.settings.locked},off:function(){this.S(this.scope).off(".fndtn.clearing"),this.S(t).off(".fndtn.clearing")},reflow:function(){this.init()}}}(jQuery,this,this.document),!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)}(function(e){function t(e){return u.raw?e:encodeURIComponent(e)}function n(e){return u.raw?e:decodeURIComponent(e)}function r(e){return t(u.json?JSON.stringify(e):String(e))}function i(e){0===e.indexOf('"')&&(e=e.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{e=decodeURIComponent(e.replace(o," "))}catch(t){return}try{return u.json?JSON.parse(e):e}catch(t){}}function s(t,n){var r=u.raw?t:i(t);return e.isFunction(n)?n(r):r}var o=/\+/g,u=e.cookie=function(i,o,l){if(void 0!==o&&!e.isFunction(o)){if(l=e.extend({},u.defaults,l),"number"==typeof l.expires){var p=l.expires,v=l.expires=new Date;v.setDate(v.getDate()+p)}return document.cookie=[t(i),"=",r(o),l.expires?"; expires="+l.expires.toUTCString():"",l.path?"; path="+l.path:"",l.domain?"; domain="+l.domain:"",l.secure?"; secure":""].join("")}for(var m=i?void 0:{},g=document.cookie?document.cookie.split("; "):[],y=0,w=g.length;w>y;y++){var E=g[y].split("="),S=n(E.shift()),x=E.join("=");if(i&&i===S){m=s(x,o);break}i||void 0===(x=s(x))||(m[S]=x)}return m};u.defaults={},e.removeCookie=function(t,n){return void 0!==e.cookie(t)?(e.cookie(t,"",e.extend({},n,{expires:-1})),!0):!1}}),function(e,t,n,r){"use strict";var i=i||!1;Foundation.libs.joyride={name:"joyride",version:"5.1.1",defaults:{expose:!1,modal:!0,tip_location:"bottom",nub_position:"auto",scroll_speed:1500,scroll_animation:"linear",timer:0,start_timer_on_click:!0,start_offset:0,next_button:!0,tip_animation:"fade",pause_after:[],exposed:[],tip_animation_fade_speed:300,cookie_monster:!1,cookie_name:"joyride",cookie_domain:!1,cookie_expires:365,tip_container:"body",tip_location_patterns:{top:["bottom"],bottom:[],left:["right","top","bottom"],right:["left","top","bottom"]},post_ride_callback:function(){},post_step_callback:function(){},pre_step_callback:function(){},pre_ride_callback:function(){},post_expose_callback:function(){},template:{link:'×',timer:'
    ',tip:'
    ',wrapper:'
    ',button:'',modal:'
    ',expose:'
    ',expose_cover:'
    '},expose_add_class:""},init:function(e,t,n){Foundation.inherit(this,"throttle random_str"),this.settings=this.defaults,this.bindings(t,n)},events:function(){var n=this;e(this.scope).off(".joyride").on("click.fndtn.joyride",".joyride-next-tip, .joyride-modal-bg",function(e){e.preventDefault(),this.settings.$li.next().length<1?this.end():this.settings.timer>0?(clearTimeout(this.settings.automate),this.hide(),this.show(),this.startTimer()):(this.hide(),this.show())}.bind(this)).on("click.fndtn.joyride",".joyride-close-tip",function(e){e.preventDefault(),this.end()}.bind(this)),e(t).off(".joyride").on("resize.fndtn.joyride",n.throttle(function(){if(e("["+n.attr_name()+"]").length>0&&n.settings.$next_tip){if(n.settings.exposed.length>0){var t=e(n.settings.exposed);t.each(function(){var t=e(this);n.un_expose(t),n.expose(t)})}n.is_phone()?n.pos_phone():n.pos_default(!1,!0)}},100))},start:function(){var t=this,n=e("["+this.attr_name()+"]",this.scope),r=["timer","scrollSpeed","startOffset","tipAnimationFadeSpeed","cookieExpires"],i=r.length;if(!n.length>0)return;this.settings.init||this.events(),this.settings=n.data(this.attr_name(!0)+"-init"),this.settings.$content_el=n,this.settings.$body=e(this.settings.tip_container),this.settings.body_offset=e(this.settings.tip_container).position(),this.settings.$tip_content=this.settings.$content_el.find("> li"),this.settings.paused=!1,this.settings.attempts=0,typeof e.cookie!="function"&&(this.settings.cookie_monster=!1);if(!this.settings.cookie_monster||this.settings.cookie_monster&&!e.cookie(this.settings.cookie_name))this.settings.$tip_content.each(function(n){var s=e(this);this.settings=e.extend({},t.defaults,t.data_options(s));var o=i;while(o--)t.settings[r[o]]=parseInt(t.settings[r[o]],10);t.create({$li:s,index:n})}),!this.settings.start_timer_on_click&&this.settings.timer>0?(this.show("init"),this.startTimer()):this.show("init")},resume:function(){this.set_li(),this.show()},tip_template:function(t){var n,r;return t.tip_class=t.tip_class||"",n=e(this.settings.template.tip).addClass(t.tip_class),r=e.trim(e(t.li).html())+this.button_text(t.button_text)+this.settings.template.link+this.timer_instance(t.index),n.append(e(this.settings.template.wrapper)),n.first().attr(this.add_namespace("data-index"),t.index),e(".joyride-content-wrapper",n).append(r),n[0]},timer_instance:function(t){var n;return t===0&&this.settings.start_timer_on_click&&this.settings.timer>0||this.settings.timer===0?n="":n=e(this.settings.template.timer)[0].outerHTML,n},button_text:function(t){return this.settings.next_button?(t=e.trim(t)||"Next",t=e(this.settings.template.button).append(t)[0].outerHTML):t="",t},create:function(t){console.log(t.$li);var n=t.$li.attr(this.add_namespace("data-button"))||t.$li.attr(this.add_namespace("data-text")),r=t.$li.attr("class"),i=e(this.tip_template({tip_class:r,index:t.index,button_text:n,li:t.$li}));e(this.settings.tip_container).append(i)},show:function(t){var n=null;this.settings.$li===r||e.inArray(this.settings.$li.index(),this.settings.pause_after)===-1?(this.settings.paused?this.settings.paused=!1:this.set_li(t),this.settings.attempts=0,this.settings.$li.length&&this.settings.$target.length>0?(t&&(this.settings.pre_ride_callback(this.settings.$li.index(),this.settings.$next_tip),this.settings.modal&&this.show_modal()),this.settings.pre_step_callback(this.settings.$li.index(),this.settings.$next_tip),this.settings.modal&&this.settings.expose&&this.expose(),this.settings.tip_settings=e.extend({},this.settings,this.data_options(this.settings.$li)),this.settings.timer=parseInt(this.settings.timer,10),this.settings.tip_settings.tip_location_pattern=this.settings.tip_location_patterns[this.settings.tip_settings.tip_location],/body/i.test(this.settings.$target.selector)||this.scroll_to(),this.is_phone()?this.pos_phone(!0):this.pos_default(!0),n=this.settings.$next_tip.find(".joyride-timer-indicator"),/pop/i.test(this.settings.tip_animation)?(n.width(0),this.settings.timer>0?(this.settings.$next_tip.show(),setTimeout(function(){n.animate({width:n.parent().width()},this.settings.timer,"linear")}.bind(this),this.settings.tip_animation_fade_speed)):this.settings.$next_tip.show()):/fade/i.test(this.settings.tip_animation)&&(n.width(0),this.settings.timer>0?(this.settings.$next_tip.fadeIn(this.settings.tip_animation_fade_speed).show(),setTimeout(function(){n.animate({width:n.parent().width()},this.settings.timer,"linear")}.bind(this),this.settings.tip_animation_fadeSpeed)):this.settings.$next_tip.fadeIn(this.settings.tip_animation_fade_speed)),this.settings.$current_tip=this.settings.$next_tip):this.settings.$li&&this.settings.$target.length<1?this.show():this.end()):this.settings.paused=!0},is_phone:function(){return matchMedia(Foundation.media_queries.small).matches&&!matchMedia(Foundation.media_queries.medium).matches},hide:function(){this.settings.modal&&this.settings.expose&&this.un_expose(),this.settings.modal||e(".joyride-modal-bg").hide(),this.settings.$current_tip.css("visibility","hidden"),setTimeout(e.proxy(function(){this.hide(),this.css("visibility","visible")},this.settings.$current_tip),0),this.settings.post_step_callback(this.settings.$li.index(),this.settings.$current_tip)},set_li:function(e){e?(this.settings.$li=this.settings.$tip_content.eq(this.settings.start_offset),this.set_next_tip(),this.settings.$current_tip=this.settings.$next_tip):(this.settings.$li=this.settings.$li.next(),this.set_next_tip()),this.set_target()},set_next_tip:function(){this.settings.$next_tip=e(".joyride-tip-guide").eq(this.settings.$li.index()),this.settings.$next_tip.data("closed","")},set_target:function(){console.log(this.add_namespace("data-class"));var t=this.settings.$li.attr(this.add_namespace("data-class")),r=this.settings.$li.attr(this.add_namespace("data-id")),i=function(){return r?e(n.getElementById(r)):t?e("."+t).first():e("body")};console.log(t,r),this.settings.$target=i()},scroll_to:function(){var n,r;n=e(t).height()/2,r=Math.ceil(this.settings.$target.offset().top-n+this.settings.$next_tip.outerHeight()),r!=0&&e("html, body").animate({scrollTop:r},this.settings.scroll_speed,"swing")},paused:function(){return e.inArray(this.settings.$li.index()+1,this.settings.pause_after)===-1},restart:function(){this.hide(),this.settings.$li=r,this.show("init")},pos_default:function(n,r){var i=Math.ceil(e(t).height()/2),s=this.settings.$next_tip.offset(),o=this.settings.$next_tip.find(".joyride-nub"),u=Math.ceil(o.outerWidth()/2),a=Math.ceil(o.outerHeight()/2),f=n||!1;f&&(this.settings.$next_tip.css("visibility","hidden"),this.settings.$next_tip.show()),typeof r=="undefined"&&(r=!1),/body/i.test(this.settings.$target.selector)?this.settings.$li.length&&this.pos_modal(o):(this.bottom()?(this.rtl?this.settings.$next_tip.css({top:this.settings.$target.offset().top+a+this.settings.$target.outerHeight(),left:this.settings.$target.offset().left+this.settings.$target.outerWidth()-this.settings.$next_tip.outerWidth()}):this.settings.$next_tip.css({top:this.settings.$target.offset().top+a+this.settings.$target.outerHeight(),left:this.settings.$target.offset().left}),this.nub_position(o,this.settings.tip_settings.nub_position,"top")):this.top()?(this.rtl?this.settings.$next_tip.css({top:this.settings.$target.offset().top-this.settings.$next_tip.outerHeight()-a,left:this.settings.$target.offset().left+this.settings.$target.outerWidth()-this.settings.$next_tip.outerWidth()}):this.settings.$next_tip.css({top:this.settings.$target.offset().top-this.settings.$next_tip.outerHeight()-a,left:this.settings.$target.offset().left}),this.nub_position(o,this.settings.tip_settings.nub_position,"bottom")):this.right()?(this.settings.$next_tip.css({top:this.settings.$target.offset().top,left:this.outerWidth(this.settings.$target)+this.settings.$target.offset().left+u}),this.nub_position(o,this.settings.tip_settings.nub_position,"left")):this.left()&&(this.settings.$next_tip.css({top:this.settings.$target.offset().top,left:this.settings.$target.offset().left-this.outerWidth(this.settings.$next_tip)-u}),this.nub_position(o,this.settings.tip_settings.nub_position,"right")),!this.visible(this.corners(this.settings.$next_tip))&&this.settings.attempts0&&arguments[0]instanceof e)i=arguments[0];else{if(!this.settings.$target||!!/body/i.test(this.settings.$target.selector))return!1;i=this.settings.$target}if(i.length<1)return t.console&&console.error("element not valid",i),!1;n=e(this.settings.template.expose),this.settings.$body.append(n),n.css({top:i.offset().top,left:i.offset().left,width:i.outerWidth(!0),height:i.outerHeight(!0)}),r=e(this.settings.template.expose_cover),s={zIndex:i.css("z-index"),position:i.css("position")},o=i.attr("class")==null?"":i.attr("class"),i.css("z-index",parseInt(n.css("z-index"))+1),s.position=="static"&&i.css("position","relative"),i.data("expose-css",s),i.data("orig-class",o),i.attr("class",o+" "+this.settings.expose_add_class),r.css({top:i.offset().top,left:i.offset().left,width:i.outerWidth(!0),height:i.outerHeight(!0)}),this.settings.modal&&this.show_modal(),this.settings.$body.append(r),n.addClass(u),r.addClass(u),i.data("expose",u),this.settings.post_expose_callback(this.settings.$li.index(),this.settings.$next_tip,i),this.add_exposed(i)},un_expose:function(){var n,r,i,s,o,u=!1;if(arguments.length>0&&arguments[0]instanceof e)r=arguments[0];else{if(!this.settings.$target||!!/body/i.test(this.settings.$target.selector))return!1;r=this.settings.$target}if(r.length<1)return t.console&&console.error("element not valid",r),!1;n=r.data("expose"),i=e("."+n),arguments.length>1&&(u=arguments[1]),u===!0?e(".joyride-expose-wrapper,.joyride-expose-cover").remove():i.remove(),s=r.data("expose-css"),s.zIndex=="auto"?r.css("z-index",""):r.css("z-index",s.zIndex),s.position!=r.css("position")&&(s.position=="static"?r.css("position",""):r.css("position",s.position)),o=r.data("orig-class"),r.attr("class",o),r.removeData("orig-classes"),r.removeData("expose"),r.removeData("expose-z-index"),this.remove_exposed(r)},add_exposed:function(t){this.settings.exposed=this.settings.exposed||[],t instanceof e||typeof t=="object"?this.settings.exposed.push(t[0]):typeof t=="string"&&this.settings.exposed.push(t)},remove_exposed:function(t){var n,r;t instanceof e?n=t[0]:typeof t=="string"&&(n=t),this.settings.exposed=this.settings.exposed||[],r=this.settings.exposed.length;while(r--)if(this.settings.exposed[r]==n){this.settings.exposed.splice(r,1);return}},center:function(){var n=e(t);return this.settings.$next_tip.css({top:(n.height()-this.settings.$next_tip.outerHeight())/2+n.scrollTop(),left:(n.width()-this.settings.$next_tip.outerWidth())/2+n.scrollLeft()}),!0},bottom:function(){return/bottom/i.test(this.settings.tip_settings.tip_location)},top:function(){return/top/i.test(this.settings.tip_settings.tip_location)},right:function(){return/right/i.test(this.settings.tip_settings.tip_location)},left:function(){return/left/i.test(this.settings.tip_settings.tip_location)},corners:function(n){var r=e(t),i=r.height()/2,s=Math.ceil(this.settings.$target.offset().top-i+this.settings.$next_tip.outerHeight()),o=r.width()+r.scrollLeft(),u=r.height()+s,a=r.height()+r.scrollTop(),f=r.scrollTop();return sa&&(a=u),[n.offset().topn.offset().left]},visible:function(e){var t=e.length;while(t--)if(e[t])return!1;return!0},nub_position:function(e,t,n){t==="auto"?e.addClass(n):e.addClass(t)},startTimer:function(){this.settings.$li.length?this.settings.automate=setTimeout(function(){this.hide(),this.show(),this.startTimer()}.bind(this),this.settings.timer):clearTimeout(this.settings.automate)},end:function(){this.settings.cookie_monster&&e.cookie(this.settings.cookie_name,"ridden",{expires:this.settings.cookie_expires,domain:this.settings.cookie_domain}),this.settings.timer>0&&clearTimeout(this.settings.automate),this.settings.modal&&this.settings.expose&&this.un_expose(),this.settings.$next_tip.data("closed",!0),e(".joyride-modal-bg").hide(),this.settings.$current_tip.hide(),this.settings.post_step_callback(this.settings.$li.index(),this.settings.$current_tip),this.settings.post_ride_callback(this.settings.$li.index(),this.settings.$current_tip),e(".joyride-tip-guide").remove()},off:function(){e(this.scope).off(".joyride"),e(t).off(".joyride"),e(".joyride-close-tip, .joyride-next-tip, .joyride-modal-bg").off(".joyride"),e(".joyride-tip-guide, .joyride-modal-bg").remove(),clearTimeout(this.settings.automate),this.settings={}},reflow:function(){}}}(jQuery,this,this.document),function(e,t,n,r){"use strict";var i=function(){},s=function(i,s){if(i.hasClass(s.slides_container_class))return this;var f=this,l,c=i,h,p,d,v=0,m,g,y=!1,b=!1;f.slides=function(){return c.children(s.slide_selector)},f.slides().first().addClass(s.active_slide_class),f.update_slide_number=function(t){s.slide_number&&(h.find("span:first").text(parseInt(t)+1),h.find("span:last").text(f.slides().length)),s.bullets&&(p.children().removeClass(s.bullets_active