From 537c023258b4123527e22b5709c8f8b162eff42d Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Wed, 17 Jun 2026 22:37:32 +0200 Subject: [PATCH 001/137] Update project dependencies, settings, and environment configuration --- .idea/vcs.xml | 6 +++ clubmanager/settings.py | 83 ++++++++++++++++++++++------------------- pyproject.toml | 2 + uv.lock | 29 +++++++++++++- 4 files changed, 81 insertions(+), 39 deletions(-) create mode 100644 .idea/vcs.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/clubmanager/settings.py b/clubmanager/settings.py index af184fd..fb77a37 100644 --- a/clubmanager/settings.py +++ b/clubmanager/settings.py @@ -12,6 +12,9 @@ https://docs.djangoproject.com/en/6.0/ref/settings/ from pathlib import Path +from decouple import Csv, config +from dj_database_url import parse as db_url + # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -20,64 +23,63 @@ BASE_DIR = Path(__file__).resolve().parent.parent # See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-dp#5%d=#&us!*zt&v(ve48!$wb4$%j$4lk8_jx#k8p!i2w^sut' +SECRET_KEY = config("DJANGO_SECRET_KEY") # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = config("DJANGO_DEBUG", default=False, cast=bool) -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = config("DJANGO_ALLOWED_HOSTS", cast=Csv(), default="") +INTERNAL_IPS = config("DJANGO_INTERNAL_IPS", cast=Csv(), default="127.0.0.1") + +CSRF_TRUSTED_ORIGINS = config("DJANGO_CSRF_TRUSTED_ORIGINS", cast=Csv(), default="") # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'clubmanager.urls' +ROOT_URLCONF = "clubmanager.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [BASE_DIR / 'templates'] - , - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [BASE_DIR / "templates"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'clubmanager.wsgi.application' +WSGI_APPLICATION = "clubmanager.wsgi.application" # Database # https://docs.djangoproject.com/en/6.0/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', - } + "default": config("DJANGO_DATABASE_URL", default="sqlite:///db.sqlite3", cast=db_url), } @@ -86,16 +88,16 @@ DATABASES = { AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -103,9 +105,9 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization # https://docs.djangoproject.com/en/6.0/topics/i18n/ -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = "en-us" -TIME_ZONE = 'UTC' +TIME_ZONE = config("DJANGO_TIME_ZONE", default="Europe/Brussels", cast=str) USE_I18N = True @@ -115,4 +117,9 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/6.0/howto/static-files/ -STATIC_URL = 'static/' +STATIC_URL = "static/" +STATIC_ROOT = BASE_DIR / "staticfiles" +STATICFILES_DIRS = [BASE_DIR / "static"] + +MEDIA_URL = "media/" +MEDIA_ROOT = BASE_DIR / "media" diff --git a/pyproject.toml b/pyproject.toml index af2bc8b..f8070a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,9 @@ name = "clubmanager" version = "0.1.0" requires-python = ">=3.14" dependencies = [ + "dj-database-url>=3.1.2", "django>=6.0.6", + "python-decouple>=3.8", ] [dependency-groups] diff --git a/uv.lock b/uv.lock index 8a55f2c..a17813d 100644 --- a/uv.lock +++ b/uv.lock @@ -16,7 +16,9 @@ name = "clubmanager" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "dj-database-url" }, { name = "django" }, + { name = "python-decouple" }, ] [package.dev-dependencies] @@ -25,11 +27,27 @@ dev = [ ] [package.metadata] -requires-dist = [{ name = "django", specifier = ">=6.0.6" }] +requires-dist = [ + { name = "dj-database-url", specifier = ">=3.1.2" }, + { name = "django", specifier = ">=6.0.6" }, + { name = "python-decouple", specifier = ">=3.8" }, +] [package.metadata.requires-dev] dev = [{ name = "ruff", specifier = ">=0.15.17" }] +[[package]] +name = "dj-database-url" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/03/f6/00b625e9d371b980aa261011d0dc906a16444cb688f94215e0dc86996eb5/dj_database_url-3.1.2.tar.gz", hash = "sha256:63c20e4bbaa51690dfd4c8d189521f6bf6bc9da9fcdb23d95d2ee8ee87f9ec62", size = 11490, upload-time = "2026-02-19T15:30:23.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/a9/57c66006373381f1d3e5bd94216f1d371228a89f443d3030e010f73dd198/dj_database_url-3.1.2-py3-none-any.whl", hash = "sha256:544e015fee3efa5127a1eb1cca465f4ace578265b3671fe61d0ed7dbafb5ec8a", size = 8953, upload-time = "2026-02-19T15:30:39.37Z" }, +] + [[package]] name = "django" version = "6.0.6" @@ -44,6 +62,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/50/23f9dc45483419a3cc2085b498b25adfbf10642b2941c73e6d2dfaffc9ab/django-6.0.6-py3-none-any.whl", hash = "sha256:25148b1194c47c2e685e5f5e9c5d59c78b075dfd282cb9618861ba6c1708f4d2", size = 8373354, upload-time = "2026-06-03T13:02:41.72Z" }, ] +[[package]] +name = "python-decouple" +version = "3.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e1/97/373dcd5844ec0ea5893e13c39a2c67e7537987ad8de3842fe078db4582fa/python-decouple-3.8.tar.gz", hash = "sha256:ba6e2657d4f376ecc46f77a3a615e058d93ba5e465c01bbe57289bfb7cce680f", size = 9612, upload-time = "2023-03-01T19:38:38.143Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/d4/9193206c4563ec771faf2ccf54815ca7918529fe81f6adb22ee6d0e06622/python_decouple-3.8-py3-none-any.whl", hash = "sha256:d0d45340815b25f4de59c974b855bb38d03151d81b037d9e3f463b0c9f8cbd66", size = 9947, upload-time = "2023-03-01T19:38:36.015Z" }, +] + [[package]] name = "ruff" version = "0.15.17" From 2ead824c5dca6591973228ab7b63ee7a4bf785d7 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Thu, 2 Jul 2026 00:08:51 +0200 Subject: [PATCH 002/137] Add accounts app: custom User, Member, families & guardianship Introduce the foundational accounts app: - Custom email-as-username User (AbstractBaseUser + PermissionsMixin) set as AUTH_USER_MODEL, decoupled from membership so children can be members without a login. - Member model holding personal/roster data (names, contact email, phone + emergency phone via django-phonenumber-field, license number, DOB) with an optional link to a User. - Family household grouping and directional Guardianship (guardian -> child) with uniqueness and no-self-guardian constraints. - Custom UserAdmin plus Member/Family admin with inlines and autocomplete. - Settings: register apps, AUTH_USER_MODEL, phonenumber defaults (BE/E164). - Add CLAUDE.md and a tracked static/ directory. Co-Authored-By: Claude Opus 4.8 --- CLAUDE.md | 52 +++++++++++ accounts/__init__.py | 0 accounts/admin.py | 61 ++++++++++++ accounts/apps.py | 6 ++ accounts/forms.py | 16 ++++ accounts/managers.py | 32 +++++++ accounts/migrations/0001_initial.py | 97 +++++++++++++++++++ accounts/migrations/__init__.py | 0 accounts/models.py | 140 ++++++++++++++++++++++++++++ accounts/tests.py | 87 +++++++++++++++++ clubmanager/settings.py | 10 ++ pyproject.toml | 1 + static/.gitkeep | 0 uv.lock | 28 ++++++ 14 files changed, 530 insertions(+) create mode 100644 CLAUDE.md create mode 100644 accounts/__init__.py create mode 100644 accounts/admin.py create mode 100644 accounts/apps.py create mode 100644 accounts/forms.py create mode 100644 accounts/managers.py create mode 100644 accounts/migrations/0001_initial.py create mode 100644 accounts/migrations/__init__.py create mode 100644 accounts/models.py create mode 100644 accounts/tests.py create mode 100644 static/.gitkeep diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..9f88e61 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,52 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +ClubManager is a **single-club** sport club management app + public website, built on **Django 6.0** (Python 3.14+). It is deliberately *not* multi-tenant — there is no `club_id` tenancy; the app manages one club. + +The repo is currently an early **skeleton**: a stock `django-admin startproject` layout with only Django's built-in apps installed. None of the domain apps exist yet — see "Planned architecture" below for the intended shape (encoded in `pyproject.toml`, not yet on disk). Verify against the actual tree before assuming a module exists. + +## Commands + +Dependencies and the virtualenv are managed with **uv** (`pyproject.toml` at repo root, `uv.lock` committed). Run Django/tools through `uv run` so the project venv is used. + +```bash +uv sync # install deps (incl. dev group) into .venv +uv run python manage.py runserver # dev server +uv run python manage.py migrate # apply migrations +uv run python manage.py makemigrations +uv run python manage.py createsuperuser +uv run python manage.py shell + +uv run python manage.py test # run all tests (Django test runner) +uv run python manage.py test # one app +uv run python manage.py test .tests. # one TestCase +uv run python manage.py test .tests.. # one test + +uv run ruff check . # lint +uv run ruff check --fix . # lint + autofix +uv run ruff format . # format +``` + +## Configuration + +Settings live in a single `clubmanager/settings.py` and read from the environment via **python-decouple** (`config(...)`), with a local `.env` file for dev. Key vars: `DJANGO_SECRET_KEY` (required), `DJANGO_DEBUG`, `DJANGO_ALLOWED_HOSTS`, `DJANGO_CSRF_TRUSTED_ORIGINS`, `DJANGO_DATABASE_URL`, `DJANGO_TIME_ZONE`. + +The database is configured through a single `DJANGO_DATABASE_URL` (parsed by **dj-database-url**), defaulting to `sqlite:///db.sqlite3` for dev; production is intended to point at PostgreSQL via that URL. Don't hardcode DB settings — go through the env var. + +## Planned architecture + +`pyproject.toml`'s isort `known-first-party` list is the intended app decomposition — treat it as the roadmap when adding domain code: +`accounts`, `club`, `members`, `teams`, `events`, `news`, `pages`, `home`, `search`. + +Domain notes (drive modeling decisions): +- **Season** is the central organizing concept. Team rosters, events, and attendance are season-scoped — model them with a FK to a season, not as global state. +- A **Member** can play on one or more **Teams**, each with a position + jersey number, always tied to a specific season. +- Three access tiers, implemented via Django groups/permissions: public site / members + parents / coaches + team managers. + +## Conventions + +- Ruff config anticipates a Wagtail-style codebase (`DJ` Django rules; `RUF012`/`RUF005` ignored for framework idioms; `line-length = 250`). Migrations are excluded from linting — don't hand-edit them to satisfy ruff. +- Settings files are exempt from `F403/F405/E501` (star imports allowed) under `clubmanager/settings/*` — note the config expects a settings *package*, though the current code is a single `settings.py`. If you split settings, match that path. diff --git a/accounts/__init__.py b/accounts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/accounts/admin.py b/accounts/admin.py new file mode 100644 index 0000000..a31abb8 --- /dev/null +++ b/accounts/admin.py @@ -0,0 +1,61 @@ +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin as BaseUserAdmin + +from .forms import UserChangeForm, UserCreationForm +from .models import Family, Guardianship, Member, User + + +@admin.register(User) +class UserAdmin(BaseUserAdmin): + add_form = UserCreationForm + form = UserChangeForm + model = User + + list_display = ("email", "is_staff", "is_superuser", "is_active") + list_filter = ("is_staff", "is_superuser", "is_active", "groups") + search_fields = ("email",) + ordering = ("email",) + + fieldsets = ( + (None, {"fields": ("email", "password")}), + ("Permissions", {"fields": ("is_active", "is_staff", "is_superuser", "groups", "user_permissions")}), + ("Important dates", {"fields": ("last_login", "date_joined")}), + ) + add_fieldsets = ( + (None, { + "classes": ("wide",), + "fields": ("email", "usable_password", "password1", "password2"), + }), + ) + + +class GuardianshipInline(admin.TabularInline): + """Guardians of a member (edit from the child's page).""" + + model = Guardianship + fk_name = "child" + extra = 0 + autocomplete_fields = ("guardian",) + + +class MemberInline(admin.TabularInline): + model = Member + extra = 0 + fields = ("first_name", "last_name", "date_of_birth", "license_number") + show_change_link = True + + +@admin.register(Member) +class MemberAdmin(admin.ModelAdmin): + list_display = ("last_name", "first_name", "date_of_birth", "license_number", "family", "user") + list_filter = ("family",) + search_fields = ("first_name", "last_name", "email", "license_number") + autocomplete_fields = ("user", "family") + inlines = (GuardianshipInline,) + + +@admin.register(Family) +class FamilyAdmin(admin.ModelAdmin): + list_display = ("name", "address") + search_fields = ("name",) + inlines = (MemberInline,) diff --git a/accounts/apps.py b/accounts/apps.py new file mode 100644 index 0000000..0cb51e6 --- /dev/null +++ b/accounts/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AccountsConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "accounts" diff --git a/accounts/forms.py b/accounts/forms.py new file mode 100644 index 0000000..6bada4f --- /dev/null +++ b/accounts/forms.py @@ -0,0 +1,16 @@ +from django.contrib.auth.forms import BaseUserCreationForm +from django.contrib.auth.forms import UserChangeForm as BaseUserChangeForm + +from .models import User + + +class UserCreationForm(BaseUserCreationForm): + class Meta(BaseUserCreationForm.Meta): + model = User + fields = ("email",) + + +class UserChangeForm(BaseUserChangeForm): + class Meta(BaseUserChangeForm.Meta): + model = User + fields = "__all__" diff --git a/accounts/managers.py b/accounts/managers.py new file mode 100644 index 0000000..9f49068 --- /dev/null +++ b/accounts/managers.py @@ -0,0 +1,32 @@ +from django.contrib.auth.base_user import BaseUserManager + + +class UserManager(BaseUserManager): + """Manager for the email-based custom User model.""" + + use_in_migrations = True + + def _create_user(self, email, password, **extra_fields): + if not email: + raise ValueError("Users must have an email address.") + email = self.normalize_email(email) + user = self.model(email=email, **extra_fields) + user.set_password(password) + user.save(using=self._db) + return user + + def create_user(self, email, password=None, **extra_fields): + extra_fields.setdefault("is_staff", False) + extra_fields.setdefault("is_superuser", False) + return self._create_user(email, password, **extra_fields) + + def create_superuser(self, email, password=None, **extra_fields): + extra_fields.setdefault("is_staff", True) + extra_fields.setdefault("is_superuser", True) + + if extra_fields.get("is_staff") is not True: + raise ValueError("Superuser must have is_staff=True.") + if extra_fields.get("is_superuser") is not True: + raise ValueError("Superuser must have is_superuser=True.") + + return self._create_user(email, password, **extra_fields) diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py new file mode 100644 index 0000000..1b18bf5 --- /dev/null +++ b/accounts/migrations/0001_initial.py @@ -0,0 +1,97 @@ +# Generated by Django 6.0.6 on 2026-07-01 22:04 + +import accounts.managers +import django.db.models.deletion +import django.utils.timezone +import phonenumber_field.modelfields +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='Family', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='e.g. "The Smiths"', max_length=150)), + ('address', models.CharField(blank=True, max_length=255)), + ], + options={ + 'verbose_name_plural': 'families', + 'ordering': ['name'], + }, + ), + migrations.CreateModel( + name='Guardianship', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('relationship', models.CharField(choices=[('parent', 'Parent'), ('guardian', 'Guardian'), ('other', 'Other')], default='parent', max_length=20)), + ], + ), + migrations.CreateModel( + name='User', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('email', models.EmailField(max_length=254, unique=True)), + ('is_staff', models.BooleanField(default=False, help_text='Whether the user can log into the admin site.')), + ('is_active', models.BooleanField(default=True)), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'abstract': False, + }, + managers=[ + ('objects', accounts.managers.UserManager()), + ], + ), + migrations.CreateModel( + name='Member', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('first_name', models.CharField(max_length=150)), + ('last_name', models.CharField(max_length=150)), + ('email', models.EmailField(blank=True, max_length=254)), + ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, region=None)), + ('emergency_phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, region=None)), + ('license_number', models.CharField(blank=True, max_length=50)), + ('date_of_birth', models.DateField()), + ('family', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='accounts.family')), + ('guardians', models.ManyToManyField(related_name='dependents', through='accounts.Guardianship', through_fields=('child', 'guardian'), to='accounts.member')), + ('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'ordering': ['last_name', 'first_name'], + }, + ), + migrations.AddField( + model_name='guardianship', + name='child', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='child_links', to='accounts.member'), + ), + migrations.AddField( + model_name='guardianship', + name='guardian', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='guardian_links', to='accounts.member'), + ), + migrations.AddConstraint( + model_name='guardianship', + constraint=models.UniqueConstraint(fields=('guardian', 'child'), name='unique_guardianship'), + ), + migrations.AddConstraint( + model_name='guardianship', + constraint=models.CheckConstraint(condition=models.Q(('guardian', models.F('child')), _negated=True), name='guardian_not_self'), + ), + ] diff --git a/accounts/migrations/__init__.py b/accounts/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/accounts/models.py b/accounts/models.py new file mode 100644 index 0000000..7580b4e --- /dev/null +++ b/accounts/models.py @@ -0,0 +1,140 @@ +from django.conf import settings +from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin +from django.db import models +from django.utils import timezone +from phonenumber_field.modelfields import PhoneNumberField + +from .managers import UserManager + + +class User(AbstractBaseUser, PermissionsMixin): + """Login identity. Only people who actually sign in get a User. + + Personal/roster data lives on :class:`Member`; this model carries just the + authentication identity. ``PermissionsMixin`` provides ``is_superuser``, + ``groups`` and ``user_permissions`` (the basis for the access tiers). + """ + + email = models.EmailField(unique=True) + is_staff = models.BooleanField( + default=False, + help_text="Whether the user can log into the admin site.", + ) + is_active = models.BooleanField(default=True) + date_joined = models.DateTimeField(default=timezone.now) + + objects = UserManager() + + USERNAME_FIELD = "email" + REQUIRED_FIELDS = [] + + def __str__(self): + return self.get_full_name() + + def get_full_name(self): + member = getattr(self, "member", None) + if member is not None: + return f"{member.first_name} {member.last_name}".strip() + return self.email + + def get_short_name(self): + member = getattr(self, "member", None) + if member is not None: + return member.first_name + return self.email + + +class Family(models.Model): + """A household grouping members together.""" + + name = models.CharField(max_length=150, help_text='e.g. "The Smiths"') + address = models.CharField(max_length=255, blank=True) + + class Meta: + ordering = ["name"] + verbose_name_plural = "families" + + def __str__(self): + return self.name + + +class Member(models.Model): + """A person in the club. May or may not have a login (:attr:`user`).""" + + user = models.OneToOneField( + settings.AUTH_USER_MODEL, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="member", + ) + + first_name = models.CharField(max_length=150) + last_name = models.CharField(max_length=150) + # Contact email — optional. Children may have none; a login email lives on User. + email = models.EmailField(blank=True) + phone = PhoneNumberField(blank=True) + emergency_phone = PhoneNumberField(blank=True) + license_number = models.CharField(max_length=50, blank=True) + date_of_birth = models.DateField() + + family = models.ForeignKey( + Family, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="members", + ) + guardians = models.ManyToManyField( + "self", + through="Guardianship", + through_fields=("child", "guardian"), + symmetrical=False, + related_name="dependents", + ) + + class Meta: + ordering = ["last_name", "first_name"] + + def __str__(self): + return f"{self.first_name} {self.last_name}" + + +class Guardianship(models.Model): + """Directional guardian -> child relationship between two members.""" + + class Relationship(models.TextChoices): + PARENT = "parent", "Parent" + GUARDIAN = "guardian", "Guardian" + OTHER = "other", "Other" + + guardian = models.ForeignKey( + Member, + on_delete=models.CASCADE, + related_name="guardian_links", + ) + child = models.ForeignKey( + Member, + on_delete=models.CASCADE, + related_name="child_links", + ) + relationship = models.CharField( + max_length=20, + choices=Relationship.choices, + default=Relationship.PARENT, + ) + + class Meta: + constraints = [ + models.UniqueConstraint( + fields=["guardian", "child"], + name="unique_guardianship", + ), + models.CheckConstraint( + condition=~models.Q(guardian=models.F("child")), + name="guardian_not_self", + ), + ] + + def __str__(self): + return f"{self.guardian} → {self.child} ({self.get_relationship_display()})" diff --git a/accounts/tests.py b/accounts/tests.py new file mode 100644 index 0000000..e0d85dc --- /dev/null +++ b/accounts/tests.py @@ -0,0 +1,87 @@ +from django.core.exceptions import ValidationError +from django.db import IntegrityError, transaction +from django.test import TestCase + +from .models import Family, Guardianship, Member, User + + +class UserManagerTests(TestCase): + def test_create_user_with_email(self): + user = User.objects.create_user(email="Parent@Example.com", password="pw") + self.assertEqual(user.email, "Parent@example.com") # domain normalized + self.assertTrue(user.check_password("pw")) + self.assertFalse(user.is_staff) + self.assertFalse(user.is_superuser) + + def test_create_superuser(self): + admin = User.objects.create_superuser(email="admin@example.com", password="pw") + self.assertTrue(admin.is_staff) + self.assertTrue(admin.is_superuser) + + def test_create_user_requires_email(self): + with self.assertRaises(ValueError): + User.objects.create_user(email="", password="pw") + + def test_create_superuser_rejects_non_superuser_flag(self): + with self.assertRaises(ValueError): + User.objects.create_superuser(email="a@b.com", password="pw", is_superuser=False) + + +class MemberTests(TestCase): + def test_member_without_login_or_optional_fields(self): + member = Member.objects.create( + first_name="Kid", + last_name="Smith", + date_of_birth="2015-05-01", + ) + self.assertIsNone(member.user) + self.assertEqual(member.email, "") + self.assertEqual(member.phone, "") + self.assertEqual(member.license_number, "") + + def test_valid_phone_accepted(self): + member = Member( + first_name="Ann", + last_name="Smith", + date_of_birth="1980-01-01", + phone="+32470123456", + ) + member.full_clean() # should not raise + + def test_invalid_phone_rejected(self): + member = Member( + first_name="Ann", + last_name="Smith", + date_of_birth="1980-01-01", + phone="not-a-number", + ) + with self.assertRaises(ValidationError): + member.full_clean() + + +class FamilyAndGuardianshipTests(TestCase): + def setUp(self): + self.family = Family.objects.create(name="The Smiths") + self.parent = Member.objects.create( + first_name="Ann", last_name="Smith", date_of_birth="1980-01-01", family=self.family + ) + self.child = Member.objects.create( + first_name="Kid", last_name="Smith", date_of_birth="2015-05-01", family=self.family + ) + + def test_family_groups_members(self): + self.assertEqual(self.family.members.count(), 2) + + def test_guardianship_relations_resolve(self): + Guardianship.objects.create(guardian=self.parent, child=self.child) + self.assertIn(self.parent, self.child.guardians.all()) + self.assertIn(self.child, self.parent.dependents.all()) + + def test_no_self_guardianship(self): + with transaction.atomic(), self.assertRaises(IntegrityError): + Guardianship.objects.create(guardian=self.parent, child=self.parent) + + def test_guardianship_is_unique(self): + Guardianship.objects.create(guardian=self.parent, child=self.child) + with transaction.atomic(), self.assertRaises(IntegrityError): + Guardianship.objects.create(guardian=self.parent, child=self.child) diff --git a/clubmanager/settings.py b/clubmanager/settings.py index fb77a37..418b78f 100644 --- a/clubmanager/settings.py +++ b/clubmanager/settings.py @@ -43,8 +43,12 @@ INSTALLED_APPS = [ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", + "phonenumber_field", + "accounts", ] +AUTH_USER_MODEL = "accounts.User" + MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", @@ -123,3 +127,9 @@ STATICFILES_DIRS = [BASE_DIR / "static"] MEDIA_URL = "media/" MEDIA_ROOT = BASE_DIR / "media" + + +# Phone numbers (django-phonenumber-field) + +PHONENUMBER_DEFAULT_REGION = "BE" +PHONENUMBER_DB_FORMAT = "E164" diff --git a/pyproject.toml b/pyproject.toml index f8070a7..42d664f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ requires-python = ">=3.14" dependencies = [ "dj-database-url>=3.1.2", "django>=6.0.6", + "django-phonenumber-field[phonenumbers]>=8.4.0", "python-decouple>=3.8", ] diff --git a/static/.gitkeep b/static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/uv.lock b/uv.lock index a17813d..30d8bbc 100644 --- a/uv.lock +++ b/uv.lock @@ -18,6 +18,7 @@ source = { virtual = "." } dependencies = [ { name = "dj-database-url" }, { name = "django" }, + { name = "django-phonenumber-field", extra = ["phonenumbers"] }, { name = "python-decouple" }, ] @@ -30,6 +31,7 @@ dev = [ requires-dist = [ { name = "dj-database-url", specifier = ">=3.1.2" }, { name = "django", specifier = ">=6.0.6" }, + { name = "django-phonenumber-field", extras = ["phonenumbers"], specifier = ">=8.4.0" }, { name = "python-decouple", specifier = ">=3.8" }, ] @@ -62,6 +64,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/50/23f9dc45483419a3cc2085b498b25adfbf10642b2941c73e6d2dfaffc9ab/django-6.0.6-py3-none-any.whl", hash = "sha256:25148b1194c47c2e685e5f5e9c5d59c78b075dfd282cb9618861ba6c1708f4d2", size = 8373354, upload-time = "2026-06-03T13:02:41.72Z" }, ] +[[package]] +name = "django-phonenumber-field" +version = "8.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/87/bf/8aa60c9834773b955dff1ddea842e361e8daaf6b0945d5bfc29fc66d53ab/django_phonenumber_field-8.4.0.tar.gz", hash = "sha256:2b83e843dac35eec6a69880a166487235b737a71a1e38c9a52e5ad67d6996083", size = 45512, upload-time = "2025-11-24T15:09:51.904Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/a3/f6b85a9246e22cf719752ad83f5e95aa9ba12419b2f9eff70f20d30e55df/django_phonenumber_field-8.4.0-py3-none-any.whl", hash = "sha256:7a1cb3a6456edb54d879f11ffa0acb227ded08c93b587035d0f28093f0e46511", size = 69528, upload-time = "2025-11-24T15:09:45.479Z" }, +] + +[package.optional-dependencies] +phonenumbers = [ + { name = "phonenumbers" }, +] + +[[package]] +name = "phonenumbers" +version = "9.0.33" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/37/dfc4cf24169f1a7169ebaedaf896c818f0add8603409d1e748e3085ccdc0/phonenumbers-9.0.33.tar.gz", hash = "sha256:9ab8a02b940b90c64f3866c0b25a30e567ddf7bb9836a3e11efdb0478f65fc1c", size = 2306756, upload-time = "2026-06-22T10:23:33.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/29/f7e30e3dbd3c7e3d9c4a55006112c04ee62b4765a31f21bcc28c253ac3f1/phonenumbers-9.0.33-py2.py3-none-any.whl", hash = "sha256:ba1d0da52711d5fdda6b2b673b2621fe80774fc5d1b2e5a6ef783396b0343186", size = 2595422, upload-time = "2026-06-22T10:23:29.925Z" }, +] + [[package]] name = "python-decouple" version = "3.8" From aa2c6329b9430d5e101e33b1451604594fb97f46 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Thu, 2 Jul 2026 00:18:35 +0200 Subject: [PATCH 003/137] Simplify family model and add Member.contact_email - Replace the Guardianship through-model + self-referential M2M with a simple is_guardian flag on Member. Guardians in a family look after the family's dependents; guardians/dependents are now derived properties. - Add Member.contact_email: own contact email, falling back to the linked user's login email, so a linked member need not store their email twice. - Update admin (is_guardian in list/filter/inline; drop guardianship inline). Co-Authored-By: Claude Opus 4.8 --- accounts/admin.py | 18 ++------ accounts/migrations/0001_initial.py | 29 +----------- accounts/models.py | 70 +++++++++++------------------ accounts/tests.py | 54 +++++++++++++++------- 4 files changed, 70 insertions(+), 101 deletions(-) diff --git a/accounts/admin.py b/accounts/admin.py index a31abb8..16c00b3 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -2,7 +2,7 @@ from django.contrib import admin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from .forms import UserChangeForm, UserCreationForm -from .models import Family, Guardianship, Member, User +from .models import Family, Member, User @admin.register(User) @@ -29,29 +29,19 @@ class UserAdmin(BaseUserAdmin): ) -class GuardianshipInline(admin.TabularInline): - """Guardians of a member (edit from the child's page).""" - - model = Guardianship - fk_name = "child" - extra = 0 - autocomplete_fields = ("guardian",) - - class MemberInline(admin.TabularInline): model = Member extra = 0 - fields = ("first_name", "last_name", "date_of_birth", "license_number") + fields = ("first_name", "last_name", "date_of_birth", "is_guardian", "license_number") show_change_link = True @admin.register(Member) class MemberAdmin(admin.ModelAdmin): - list_display = ("last_name", "first_name", "date_of_birth", "license_number", "family", "user") - list_filter = ("family",) + list_display = ("last_name", "first_name", "date_of_birth", "is_guardian", "license_number", "family", "user") + list_filter = ("is_guardian", "family") search_fields = ("first_name", "last_name", "email", "license_number") autocomplete_fields = ("user", "family") - inlines = (GuardianshipInline,) @admin.register(Family) diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py index 1b18bf5..0b39cf3 100644 --- a/accounts/migrations/0001_initial.py +++ b/accounts/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.6 on 2026-07-01 22:04 +# Generated by Django 6.0.6 on 2026-07-01 22:16 import accounts.managers import django.db.models.deletion @@ -29,13 +29,6 @@ class Migration(migrations.Migration): 'ordering': ['name'], }, ), - migrations.CreateModel( - name='Guardianship', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('relationship', models.CharField(choices=[('parent', 'Parent'), ('guardian', 'Guardian'), ('other', 'Other')], default='parent', max_length=20)), - ], - ), migrations.CreateModel( name='User', fields=[ @@ -68,30 +61,12 @@ class Migration(migrations.Migration): ('emergency_phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, region=None)), ('license_number', models.CharField(blank=True, max_length=50)), ('date_of_birth', models.DateField()), + ('is_guardian', models.BooleanField(default=False, help_text='Whether this member is a parent/guardian in their family.')), ('family', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='accounts.family')), - ('guardians', models.ManyToManyField(related_name='dependents', through='accounts.Guardianship', through_fields=('child', 'guardian'), to='accounts.member')), ('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member', to=settings.AUTH_USER_MODEL)), ], options={ 'ordering': ['last_name', 'first_name'], }, ), - migrations.AddField( - model_name='guardianship', - name='child', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='child_links', to='accounts.member'), - ), - migrations.AddField( - model_name='guardianship', - name='guardian', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='guardian_links', to='accounts.member'), - ), - migrations.AddConstraint( - model_name='guardianship', - constraint=models.UniqueConstraint(fields=('guardian', 'child'), name='unique_guardianship'), - ), - migrations.AddConstraint( - model_name='guardianship', - constraint=models.CheckConstraint(condition=models.Q(('guardian', models.F('child')), _negated=True), name='guardian_not_self'), - ), ] diff --git a/accounts/models.py b/accounts/models.py index 7580b4e..8b7e88e 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -59,7 +59,12 @@ class Family(models.Model): class Member(models.Model): - """A person in the club. May or may not have a login (:attr:`user`).""" + """A person in the club. May or may not have a login (:attr:`user`). + + Family relationships are modelled by membership in a :class:`Family`: + guardians (``is_guardian=True``) look after the other members of the same + family (the dependents). + """ user = models.OneToOneField( settings.AUTH_USER_MODEL, @@ -72,6 +77,7 @@ class Member(models.Model): first_name = models.CharField(max_length=150) last_name = models.CharField(max_length=150) # Contact email — optional. Children may have none; a login email lives on User. + # Use `contact_email` to read it with a fallback to the linked user's login email. email = models.EmailField(blank=True) phone = PhoneNumberField(blank=True) emergency_phone = PhoneNumberField(blank=True) @@ -85,12 +91,9 @@ class Member(models.Model): blank=True, related_name="members", ) - guardians = models.ManyToManyField( - "self", - through="Guardianship", - through_fields=("child", "guardian"), - symmetrical=False, - related_name="dependents", + is_guardian = models.BooleanField( + default=False, + help_text="Whether this member is a parent/guardian in their family.", ) class Meta: @@ -99,42 +102,21 @@ class Member(models.Model): def __str__(self): return f"{self.first_name} {self.last_name}" + @property + def contact_email(self): + """Best email to reach this member: own contact email, else login email.""" + return self.email or (self.user.email if self.user_id else "") -class Guardianship(models.Model): - """Directional guardian -> child relationship between two members.""" + @property + def guardians(self): + """Members of my family who look after me (only if I'm a dependent).""" + if self.is_guardian or self.family_id is None: + return Member.objects.none() + return self.family.members.filter(is_guardian=True) - class Relationship(models.TextChoices): - PARENT = "parent", "Parent" - GUARDIAN = "guardian", "Guardian" - OTHER = "other", "Other" - - guardian = models.ForeignKey( - Member, - on_delete=models.CASCADE, - related_name="guardian_links", - ) - child = models.ForeignKey( - Member, - on_delete=models.CASCADE, - related_name="child_links", - ) - relationship = models.CharField( - max_length=20, - choices=Relationship.choices, - default=Relationship.PARENT, - ) - - class Meta: - constraints = [ - models.UniqueConstraint( - fields=["guardian", "child"], - name="unique_guardianship", - ), - models.CheckConstraint( - condition=~models.Q(guardian=models.F("child")), - name="guardian_not_self", - ), - ] - - def __str__(self): - return f"{self.guardian} → {self.child} ({self.get_relationship_display()})" + @property + def dependents(self): + """Members of my family I look after (only if I'm a guardian).""" + if not self.is_guardian or self.family_id is None: + return Member.objects.none() + return self.family.members.filter(is_guardian=False) diff --git a/accounts/tests.py b/accounts/tests.py index e0d85dc..46a01d4 100644 --- a/accounts/tests.py +++ b/accounts/tests.py @@ -1,8 +1,7 @@ from django.core.exceptions import ValidationError -from django.db import IntegrityError, transaction from django.test import TestCase -from .models import Family, Guardianship, Member, User +from .models import Family, Member, User class UserManagerTests(TestCase): @@ -58,30 +57,53 @@ class MemberTests(TestCase): with self.assertRaises(ValidationError): member.full_clean() + def test_contact_email_prefers_own_then_login(self): + # No own email, no user -> empty. + member = Member.objects.create( + first_name="Kid", last_name="Smith", date_of_birth="2015-05-01" + ) + self.assertEqual(member.contact_email, "") -class FamilyAndGuardianshipTests(TestCase): + # Linked user, no own email -> falls back to login email. + member.user = User.objects.create_user(email="login@example.com", password="pw") + member.save() + self.assertEqual(member.contact_email, "login@example.com") + + # Own contact email wins over login email. + member.email = "contact@example.com" + self.assertEqual(member.contact_email, "contact@example.com") + + +class FamilyTests(TestCase): def setUp(self): self.family = Family.objects.create(name="The Smiths") self.parent = Member.objects.create( - first_name="Ann", last_name="Smith", date_of_birth="1980-01-01", family=self.family + first_name="Ann", last_name="Smith", date_of_birth="1980-01-01", + family=self.family, is_guardian=True, ) self.child = Member.objects.create( - first_name="Kid", last_name="Smith", date_of_birth="2015-05-01", family=self.family + first_name="Kid", last_name="Smith", date_of_birth="2015-05-01", + family=self.family, is_guardian=False, ) def test_family_groups_members(self): self.assertEqual(self.family.members.count(), 2) - def test_guardianship_relations_resolve(self): - Guardianship.objects.create(guardian=self.parent, child=self.child) - self.assertIn(self.parent, self.child.guardians.all()) - self.assertIn(self.child, self.parent.dependents.all()) + def test_child_guardians_are_family_guardians(self): + self.assertIn(self.parent, self.child.guardians) + self.assertNotIn(self.child, self.child.guardians) - def test_no_self_guardianship(self): - with transaction.atomic(), self.assertRaises(IntegrityError): - Guardianship.objects.create(guardian=self.parent, child=self.parent) + def test_guardian_dependents_are_family_non_guardians(self): + self.assertIn(self.child, self.parent.dependents) + self.assertNotIn(self.parent, self.parent.dependents) - def test_guardianship_is_unique(self): - Guardianship.objects.create(guardian=self.parent, child=self.child) - with transaction.atomic(), self.assertRaises(IntegrityError): - Guardianship.objects.create(guardian=self.parent, child=self.child) + def test_guardian_has_no_guardians_and_child_has_no_dependents(self): + self.assertEqual(list(self.parent.guardians), []) + self.assertEqual(list(self.child.dependents), []) + + def test_member_without_family_has_no_relations(self): + loner = Member.objects.create( + first_name="Solo", last_name="Jones", date_of_birth="1990-01-01" + ) + self.assertEqual(list(loner.guardians), []) + self.assertEqual(list(loner.dependents), []) From 8f71bb74c02833c189d36b32ca088290198afd7d Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Thu, 2 Jul 2026 09:40:06 +0200 Subject: [PATCH 004/137] Refactor accounts app into authentication and club apps - Split the accounts app into new authentication and club apps for better separation of concerns. - Migrate the custom User, Member, and Family models to the authentication app. - Introduce Club and ClubMembership models in the club app. - Refactor Family model to use UUID as the primary key and consolidate family-role relationships into a new FamilyMembership model. - Update tests, managers, and migrations to align with the new structure. --- accounts/admin.py | 51 ---- accounts/apps.py | 6 - accounts/forms.py | 16 -- accounts/models.py | 122 --------- accounts/tests.py | 109 -------- {accounts => authentication}/__init__.py | 0 authentication/admin.py | 3 + authentication/apps.py | 5 + {accounts => authentication}/managers.py | 0 .../migrations/0001_initial.py | 60 +++-- .../migrations/__init__.py | 0 authentication/models.py | 128 ++++++++++ authentication/tests.py | 234 ++++++++++++++++++ authentication/views.py | 3 + club/__init__.py | 0 club/admin.py | 3 + club/apps.py | 5 + club/migrations/__init__.py | 0 club/models.py | 20 ++ club/tests.py | 3 + club/views.py | 3 + clubmanager/base.py | 21 ++ clubmanager/settings.py | 7 +- 23 files changed, 473 insertions(+), 326 deletions(-) delete mode 100644 accounts/admin.py delete mode 100644 accounts/apps.py delete mode 100644 accounts/forms.py delete mode 100644 accounts/models.py delete mode 100644 accounts/tests.py rename {accounts => authentication}/__init__.py (100%) create mode 100644 authentication/admin.py create mode 100644 authentication/apps.py rename {accounts => authentication}/managers.py (100%) rename {accounts => authentication}/migrations/0001_initial.py (51%) rename {accounts => authentication}/migrations/__init__.py (100%) create mode 100644 authentication/models.py create mode 100644 authentication/tests.py create mode 100644 authentication/views.py create mode 100644 club/__init__.py create mode 100644 club/admin.py create mode 100644 club/apps.py create mode 100644 club/migrations/__init__.py create mode 100644 club/models.py create mode 100644 club/tests.py create mode 100644 club/views.py create mode 100644 clubmanager/base.py diff --git a/accounts/admin.py b/accounts/admin.py deleted file mode 100644 index 16c00b3..0000000 --- a/accounts/admin.py +++ /dev/null @@ -1,51 +0,0 @@ -from django.contrib import admin -from django.contrib.auth.admin import UserAdmin as BaseUserAdmin - -from .forms import UserChangeForm, UserCreationForm -from .models import Family, Member, User - - -@admin.register(User) -class UserAdmin(BaseUserAdmin): - add_form = UserCreationForm - form = UserChangeForm - model = User - - list_display = ("email", "is_staff", "is_superuser", "is_active") - list_filter = ("is_staff", "is_superuser", "is_active", "groups") - search_fields = ("email",) - ordering = ("email",) - - fieldsets = ( - (None, {"fields": ("email", "password")}), - ("Permissions", {"fields": ("is_active", "is_staff", "is_superuser", "groups", "user_permissions")}), - ("Important dates", {"fields": ("last_login", "date_joined")}), - ) - add_fieldsets = ( - (None, { - "classes": ("wide",), - "fields": ("email", "usable_password", "password1", "password2"), - }), - ) - - -class MemberInline(admin.TabularInline): - model = Member - extra = 0 - fields = ("first_name", "last_name", "date_of_birth", "is_guardian", "license_number") - show_change_link = True - - -@admin.register(Member) -class MemberAdmin(admin.ModelAdmin): - list_display = ("last_name", "first_name", "date_of_birth", "is_guardian", "license_number", "family", "user") - list_filter = ("is_guardian", "family") - search_fields = ("first_name", "last_name", "email", "license_number") - autocomplete_fields = ("user", "family") - - -@admin.register(Family) -class FamilyAdmin(admin.ModelAdmin): - list_display = ("name", "address") - search_fields = ("name",) - inlines = (MemberInline,) diff --git a/accounts/apps.py b/accounts/apps.py deleted file mode 100644 index 0cb51e6..0000000 --- a/accounts/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class AccountsConfig(AppConfig): - default_auto_field = "django.db.models.BigAutoField" - name = "accounts" diff --git a/accounts/forms.py b/accounts/forms.py deleted file mode 100644 index 6bada4f..0000000 --- a/accounts/forms.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.contrib.auth.forms import BaseUserCreationForm -from django.contrib.auth.forms import UserChangeForm as BaseUserChangeForm - -from .models import User - - -class UserCreationForm(BaseUserCreationForm): - class Meta(BaseUserCreationForm.Meta): - model = User - fields = ("email",) - - -class UserChangeForm(BaseUserChangeForm): - class Meta(BaseUserChangeForm.Meta): - model = User - fields = "__all__" diff --git a/accounts/models.py b/accounts/models.py deleted file mode 100644 index 8b7e88e..0000000 --- a/accounts/models.py +++ /dev/null @@ -1,122 +0,0 @@ -from django.conf import settings -from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin -from django.db import models -from django.utils import timezone -from phonenumber_field.modelfields import PhoneNumberField - -from .managers import UserManager - - -class User(AbstractBaseUser, PermissionsMixin): - """Login identity. Only people who actually sign in get a User. - - Personal/roster data lives on :class:`Member`; this model carries just the - authentication identity. ``PermissionsMixin`` provides ``is_superuser``, - ``groups`` and ``user_permissions`` (the basis for the access tiers). - """ - - email = models.EmailField(unique=True) - is_staff = models.BooleanField( - default=False, - help_text="Whether the user can log into the admin site.", - ) - is_active = models.BooleanField(default=True) - date_joined = models.DateTimeField(default=timezone.now) - - objects = UserManager() - - USERNAME_FIELD = "email" - REQUIRED_FIELDS = [] - - def __str__(self): - return self.get_full_name() - - def get_full_name(self): - member = getattr(self, "member", None) - if member is not None: - return f"{member.first_name} {member.last_name}".strip() - return self.email - - def get_short_name(self): - member = getattr(self, "member", None) - if member is not None: - return member.first_name - return self.email - - -class Family(models.Model): - """A household grouping members together.""" - - name = models.CharField(max_length=150, help_text='e.g. "The Smiths"') - address = models.CharField(max_length=255, blank=True) - - class Meta: - ordering = ["name"] - verbose_name_plural = "families" - - def __str__(self): - return self.name - - -class Member(models.Model): - """A person in the club. May or may not have a login (:attr:`user`). - - Family relationships are modelled by membership in a :class:`Family`: - guardians (``is_guardian=True``) look after the other members of the same - family (the dependents). - """ - - user = models.OneToOneField( - settings.AUTH_USER_MODEL, - on_delete=models.SET_NULL, - null=True, - blank=True, - related_name="member", - ) - - first_name = models.CharField(max_length=150) - last_name = models.CharField(max_length=150) - # Contact email — optional. Children may have none; a login email lives on User. - # Use `contact_email` to read it with a fallback to the linked user's login email. - email = models.EmailField(blank=True) - phone = PhoneNumberField(blank=True) - emergency_phone = PhoneNumberField(blank=True) - license_number = models.CharField(max_length=50, blank=True) - date_of_birth = models.DateField() - - family = models.ForeignKey( - Family, - on_delete=models.SET_NULL, - null=True, - blank=True, - related_name="members", - ) - is_guardian = models.BooleanField( - default=False, - help_text="Whether this member is a parent/guardian in their family.", - ) - - class Meta: - ordering = ["last_name", "first_name"] - - def __str__(self): - return f"{self.first_name} {self.last_name}" - - @property - def contact_email(self): - """Best email to reach this member: own contact email, else login email.""" - return self.email or (self.user.email if self.user_id else "") - - @property - def guardians(self): - """Members of my family who look after me (only if I'm a dependent).""" - if self.is_guardian or self.family_id is None: - return Member.objects.none() - return self.family.members.filter(is_guardian=True) - - @property - def dependents(self): - """Members of my family I look after (only if I'm a guardian).""" - if not self.is_guardian or self.family_id is None: - return Member.objects.none() - return self.family.members.filter(is_guardian=False) diff --git a/accounts/tests.py b/accounts/tests.py deleted file mode 100644 index 46a01d4..0000000 --- a/accounts/tests.py +++ /dev/null @@ -1,109 +0,0 @@ -from django.core.exceptions import ValidationError -from django.test import TestCase - -from .models import Family, Member, User - - -class UserManagerTests(TestCase): - def test_create_user_with_email(self): - user = User.objects.create_user(email="Parent@Example.com", password="pw") - self.assertEqual(user.email, "Parent@example.com") # domain normalized - self.assertTrue(user.check_password("pw")) - self.assertFalse(user.is_staff) - self.assertFalse(user.is_superuser) - - def test_create_superuser(self): - admin = User.objects.create_superuser(email="admin@example.com", password="pw") - self.assertTrue(admin.is_staff) - self.assertTrue(admin.is_superuser) - - def test_create_user_requires_email(self): - with self.assertRaises(ValueError): - User.objects.create_user(email="", password="pw") - - def test_create_superuser_rejects_non_superuser_flag(self): - with self.assertRaises(ValueError): - User.objects.create_superuser(email="a@b.com", password="pw", is_superuser=False) - - -class MemberTests(TestCase): - def test_member_without_login_or_optional_fields(self): - member = Member.objects.create( - first_name="Kid", - last_name="Smith", - date_of_birth="2015-05-01", - ) - self.assertIsNone(member.user) - self.assertEqual(member.email, "") - self.assertEqual(member.phone, "") - self.assertEqual(member.license_number, "") - - def test_valid_phone_accepted(self): - member = Member( - first_name="Ann", - last_name="Smith", - date_of_birth="1980-01-01", - phone="+32470123456", - ) - member.full_clean() # should not raise - - def test_invalid_phone_rejected(self): - member = Member( - first_name="Ann", - last_name="Smith", - date_of_birth="1980-01-01", - phone="not-a-number", - ) - with self.assertRaises(ValidationError): - member.full_clean() - - def test_contact_email_prefers_own_then_login(self): - # No own email, no user -> empty. - member = Member.objects.create( - first_name="Kid", last_name="Smith", date_of_birth="2015-05-01" - ) - self.assertEqual(member.contact_email, "") - - # Linked user, no own email -> falls back to login email. - member.user = User.objects.create_user(email="login@example.com", password="pw") - member.save() - self.assertEqual(member.contact_email, "login@example.com") - - # Own contact email wins over login email. - member.email = "contact@example.com" - self.assertEqual(member.contact_email, "contact@example.com") - - -class FamilyTests(TestCase): - def setUp(self): - self.family = Family.objects.create(name="The Smiths") - self.parent = Member.objects.create( - first_name="Ann", last_name="Smith", date_of_birth="1980-01-01", - family=self.family, is_guardian=True, - ) - self.child = Member.objects.create( - first_name="Kid", last_name="Smith", date_of_birth="2015-05-01", - family=self.family, is_guardian=False, - ) - - def test_family_groups_members(self): - self.assertEqual(self.family.members.count(), 2) - - def test_child_guardians_are_family_guardians(self): - self.assertIn(self.parent, self.child.guardians) - self.assertNotIn(self.child, self.child.guardians) - - def test_guardian_dependents_are_family_non_guardians(self): - self.assertIn(self.child, self.parent.dependents) - self.assertNotIn(self.parent, self.parent.dependents) - - def test_guardian_has_no_guardians_and_child_has_no_dependents(self): - self.assertEqual(list(self.parent.guardians), []) - self.assertEqual(list(self.child.dependents), []) - - def test_member_without_family_has_no_relations(self): - loner = Member.objects.create( - first_name="Solo", last_name="Jones", date_of_birth="1990-01-01" - ) - self.assertEqual(list(loner.guardians), []) - self.assertEqual(list(loner.dependents), []) diff --git a/accounts/__init__.py b/authentication/__init__.py similarity index 100% rename from accounts/__init__.py rename to authentication/__init__.py diff --git a/authentication/admin.py b/authentication/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/authentication/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/authentication/apps.py b/authentication/apps.py new file mode 100644 index 0000000..9635c9d --- /dev/null +++ b/authentication/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AuthenticationConfig(AppConfig): + name = 'authentication' diff --git a/accounts/managers.py b/authentication/managers.py similarity index 100% rename from accounts/managers.py rename to authentication/managers.py diff --git a/accounts/migrations/0001_initial.py b/authentication/migrations/0001_initial.py similarity index 51% rename from accounts/migrations/0001_initial.py rename to authentication/migrations/0001_initial.py index 0b39cf3..5e98af2 100644 --- a/accounts/migrations/0001_initial.py +++ b/authentication/migrations/0001_initial.py @@ -1,9 +1,9 @@ -# Generated by Django 6.0.6 on 2026-07-01 22:16 +# Generated by Django 6.0.6 on 2026-07-02 07:33 -import accounts.managers +import authentication.managers import django.db.models.deletion -import django.utils.timezone import phonenumber_field.modelfields +import uuid from django.conf import settings from django.db import migrations, models @@ -20,11 +20,11 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Family', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(help_text='e.g. "The Smiths"', max_length=150)), - ('address', models.CharField(blank=True, max_length=255)), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), ], options={ + 'verbose_name': 'family', 'verbose_name_plural': 'families', 'ordering': ['name'], }, @@ -32,41 +32,63 @@ class Migration(migrations.Migration): migrations.CreateModel( name='User', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('password', models.CharField(max_length=128, verbose_name='password')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('email', models.EmailField(max_length=254, unique=True)), - ('is_staff', models.BooleanField(default=False, help_text='Whether the user can log into the admin site.')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('email', models.EmailField(db_index=True, max_length=254, unique=True)), + ('is_staff', models.BooleanField(default=False)), ('is_active', models.BooleanField(default=True)), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now)), ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), ], options={ - 'abstract': False, + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'ordering': ['email'], }, managers=[ - ('objects', accounts.managers.UserManager()), + ('objects', authentication.managers.UserManager()), ], ), migrations.CreateModel( name='Member', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('first_name', models.CharField(max_length=150)), ('last_name', models.CharField(max_length=150)), + ('date_of_birth', models.DateField(blank=True, null=True)), ('email', models.EmailField(blank=True, max_length=254)), - ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, region=None)), - ('emergency_phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, region=None)), - ('license_number', models.CharField(blank=True, max_length=50)), - ('date_of_birth', models.DateField()), - ('is_guardian', models.BooleanField(default=False, help_text='Whether this member is a parent/guardian in their family.')), - ('family', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='members', to='accounts.family')), + ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None)), + ('emergency_phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None)), ('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member', to=settings.AUTH_USER_MODEL)), ], options={ + 'verbose_name': 'member', + 'verbose_name_plural': 'members', 'ordering': ['last_name', 'first_name'], }, ), + migrations.CreateModel( + name='FamilyMembership', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('role', models.CharField(choices=[('parent', 'parent'), ('child', 'child'), ('guardian', 'guardian'), ('other', 'other')], default='parent', max_length=255)), + ('family', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='memberships', to='authentication.family')), + ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='family_memberships', to='authentication.member')), + ], + options={ + 'verbose_name': 'family membership', + 'verbose_name_plural': 'family memberships', + 'ordering': ['family', 'role', 'member__last_name', 'member__first_name'], + }, + ), + migrations.AddIndex( + model_name='member', + index=models.Index(fields=['last_name', 'first_name'], name='authenticat_last_na_0a0eca_idx'), + ), + migrations.AlterUniqueTogether( + name='familymembership', + unique_together={('family', 'member')}, + ), ] diff --git a/accounts/migrations/__init__.py b/authentication/migrations/__init__.py similarity index 100% rename from accounts/migrations/__init__.py rename to authentication/migrations/__init__.py diff --git a/authentication/models.py b/authentication/models.py new file mode 100644 index 0000000..29c9fab --- /dev/null +++ b/authentication/models.py @@ -0,0 +1,128 @@ +import uuid + +from django.conf import settings +from django.contrib.auth.base_user import AbstractBaseUser +from django.contrib.auth.models import PermissionsMixin +from django.db import models +from django.utils.translation import gettext_lazy as _ +from phonenumber_field.modelfields import PhoneNumberField + +from clubmanager.base import UUIDModel + +from .managers import UserManager + + +class User(AbstractBaseUser, PermissionsMixin): + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + email = models.EmailField(unique=True, db_index=True) + + is_staff = models.BooleanField(default=False) + is_active = models.BooleanField(default=True) + + objects = UserManager() + + USERNAME_FIELD = "email" + REQUIRED_FIELDS = [] + + class Meta: + verbose_name = _("user") + verbose_name_plural = _("users") + ordering = ["email"] + + def __str__(self): + return self.get_full_name() + + def get_full_name(self): + member = getattr(self, "member", None) + if member is not None: + return member.get_full_name() + return self.email + + def get_short_name(self): + member = getattr(self, "member", None) + if member is not None: + return member.get_short_name() + return self.email + + +class Family(UUIDModel): + name = models.CharField(max_length=255) + + class Meta: + verbose_name = _("family") + verbose_name_plural = _("families") + ordering = ["name"] + + def __str__(self): + return self.name + + @property + def guardians(self): + return Member.objects.filter( + family_memberships__family=self, + family_memberships__role__in=[FamilyMembership.FamilyRole.PARENT, FamilyMembership.FamilyRole.GUARDIAN], + ) + + @property + def children(self): + return Member.objects.filter( + family_memberships__family=self, + family_memberships__role=FamilyMembership.FamilyRole.CHILD, + ) + + +class Member(UUIDModel): + user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name="member", null=True, blank=True) + + first_name = models.CharField(max_length=150) + last_name = models.CharField(max_length=150) + + date_of_birth = models.DateField(null=True, blank=True) + + email = models.EmailField(blank=True) + phone = PhoneNumberField(null=True, blank=True) + emergency_phone = PhoneNumberField(null=True, blank=True) + + class Meta: + verbose_name = _("member") + verbose_name_plural = _("members") + ordering = ["last_name", "first_name"] + indexes = [models.Index(fields=["last_name", "first_name"])] + + def __str__(self): + return f"{self.first_name} {self.last_name}" + + def get_full_name(self): + return f"{self.first_name} {self.last_name}".strip() + + def get_short_name(self): + return self.first_name + + @property + def guardians(self): + return Member.objects.filter( + family_memberships__role__in=[FamilyMembership.FamilyRole.PARENT, FamilyMembership.FamilyRole.GUARDIAN], + family_memberships__family__memberships__member=self, + family_memberships__family__memberships__role=FamilyMembership.FamilyRole.CHILD, + ).distinct() + + +class FamilyMembership(models.Model): + class FamilyRole(models.TextChoices): + PARENT = "parent", _("parent") + CHILD = "child", _("child") + GUARDIAN = "guardian", _("guardian") + OTHER = "other", _("other") + + family = models.ForeignKey(Family, on_delete=models.CASCADE, related_name="memberships") + member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="family_memberships") + role = models.CharField(max_length=255, choices=FamilyRole.choices, default=FamilyRole.PARENT) + + class Meta: + verbose_name = _("family membership") + verbose_name_plural = _("family memberships") + ordering = ["family", "role", "member__last_name", "member__first_name"] + unique_together = ("family", "member") + + def __str__(self): + return f"{self.family} - {self.member} ({self.get_role_display()})" diff --git a/authentication/tests.py b/authentication/tests.py new file mode 100644 index 0000000..431dd9f --- /dev/null +++ b/authentication/tests.py @@ -0,0 +1,234 @@ +import uuid + +from django.contrib.auth import get_user_model +from django.db import IntegrityError +from django.db.models import SET_NULL +from django.test import TestCase + +from authentication.models import Family, FamilyMembership, Member + +User = get_user_model() + + +class UserManagerTests(TestCase): + def test_create_user_defaults(self): + user = User.objects.create_user(email="alice@example.com", password="secret123") + + self.assertEqual(user.email, "alice@example.com") + self.assertTrue(user.check_password("secret123")) + self.assertFalse(user.is_staff) + self.assertFalse(user.is_superuser) + self.assertTrue(user.is_active) + + def test_create_user_requires_email(self): + with self.assertRaises(ValueError): + User.objects.create_user(email="", password="secret123") + + def test_create_user_normalizes_email_domain(self): + # BaseUserManager lowercases the domain part of the address. + user = User.objects.create_user(email="Bob@Example.COM", password="secret123") + + self.assertEqual(user.email, "Bob@example.com") + + def test_create_user_password_is_hashed(self): + user = User.objects.create_user(email="carol@example.com", password="secret123") + + self.assertNotEqual(user.password, "secret123") + + def test_create_user_without_password_is_unusable(self): + user = User.objects.create_user(email="dave@example.com") + + self.assertFalse(user.has_usable_password()) + + def test_create_superuser_defaults(self): + admin = User.objects.create_superuser(email="admin@example.com", password="secret123") + + self.assertTrue(admin.is_staff) + self.assertTrue(admin.is_superuser) + self.assertTrue(admin.is_active) + + def test_create_superuser_rejects_non_staff(self): + with self.assertRaises(ValueError): + User.objects.create_superuser(email="admin@example.com", password="x", is_staff=False) + + def test_create_superuser_rejects_non_superuser(self): + with self.assertRaises(ValueError): + User.objects.create_superuser(email="admin@example.com", password="x", is_superuser=False) + + +class UserModelTests(TestCase): + def test_email_is_username_field(self): + self.assertEqual(User.USERNAME_FIELD, "email") + self.assertEqual(User.REQUIRED_FIELDS, []) + + def test_email_is_unique(self): + User.objects.create_user(email="dup@example.com", password="x") + with self.assertRaises(IntegrityError): + User.objects.create_user(email="dup@example.com", password="y") + + def test_pk_is_uuid(self): + user = User.objects.create_user(email="uuid@example.com", password="x") + self.assertIsInstance(user.pk, uuid.UUID) + + def test_str_and_names_fall_back_to_email_without_member(self): + user = User.objects.create_user(email="lonely@example.com", password="x") + + self.assertEqual(str(user), "lonely@example.com") + self.assertEqual(user.get_full_name(), "lonely@example.com") + self.assertEqual(user.get_short_name(), "lonely@example.com") + + def test_str_and_names_use_linked_member(self): + user = User.objects.create_user(email="linked@example.com", password="x") + Member.objects.create(user=user, first_name="Jane", last_name="Doe") + + # Re-fetch so the reverse OneToOne relation is resolved from the DB. + user = User.objects.get(pk=user.pk) + + self.assertEqual(str(user), "Jane Doe") + self.assertEqual(user.get_full_name(), "Jane Doe") + self.assertEqual(user.get_short_name(), "Jane") + + +class MemberModelTests(TestCase): + def test_str_and_name_helpers(self): + member = Member.objects.create(first_name="John", last_name="Smith") + + self.assertEqual(str(member), "John Smith") + self.assertEqual(member.get_full_name(), "John Smith") + self.assertEqual(member.get_short_name(), "John") + + def test_get_full_name_strips_when_partial(self): + member = Member.objects.create(first_name="Cher", last_name="") + self.assertEqual(member.get_full_name(), "Cher") + + def test_member_can_exist_without_user(self): + member = Member.objects.create(first_name="No", last_name="Login") + self.assertIsNone(member.user) + + def test_deleting_user_nulls_member_but_keeps_it(self): + user = User.objects.create_user(email="temp@example.com", password="x") + member = Member.objects.create(user=user, first_name="Keep", last_name="Me") + + # OneToOneField uses on_delete=SET_NULL. + field = Member._meta.get_field("user") + self.assertIs(field.remote_field.on_delete, SET_NULL) + + user.delete() + member.refresh_from_db() + self.assertIsNone(member.user) + self.assertTrue(Member.objects.filter(pk=member.pk).exists()) + + def test_user_member_is_one_to_one(self): + user = User.objects.create_user(email="once@example.com", password="x") + Member.objects.create(user=user, first_name="First", last_name="Member") + + with self.assertRaises(IntegrityError): + Member.objects.create(user=user, first_name="Second", last_name="Member") + + +class FamilyModelTests(TestCase): + def setUp(self): + self.family = Family.objects.create(name="The Smiths") + self.parent = Member.objects.create(first_name="Pat", last_name="Smith") + self.guardian = Member.objects.create(first_name="Gale", last_name="Smith") + self.child = Member.objects.create(first_name="Kim", last_name="Smith") + self.other = Member.objects.create(first_name="Ola", last_name="Smith") + + FamilyMembership.objects.create(family=self.family, member=self.parent, role=FamilyMembership.FamilyRole.PARENT) + FamilyMembership.objects.create(family=self.family, member=self.guardian, role=FamilyMembership.FamilyRole.GUARDIAN) + FamilyMembership.objects.create(family=self.family, member=self.child, role=FamilyMembership.FamilyRole.CHILD) + FamilyMembership.objects.create(family=self.family, member=self.other, role=FamilyMembership.FamilyRole.OTHER) + + def test_str(self): + self.assertEqual(str(self.family), "The Smiths") + + def test_guardians_include_parents_and_guardians_only(self): + guardians = set(self.family.guardians) + self.assertEqual(guardians, {self.parent, self.guardian}) + + def test_children_include_children_only(self): + children = list(self.family.children) + self.assertEqual(children, [self.child]) + + def test_guardians_are_scoped_to_the_family(self): + other_family = Family.objects.create(name="The Joneses") + outsider = Member.objects.create(first_name="Out", last_name="Sider") + FamilyMembership.objects.create(family=other_family, member=outsider, role=FamilyMembership.FamilyRole.PARENT) + + self.assertNotIn(outsider, set(self.family.guardians)) + + +class MemberGuardiansTests(TestCase): + def test_guardians_of_a_child_are_family_parents_and_guardians(self): + family = Family.objects.create(name="The Does") + mum = Member.objects.create(first_name="Mary", last_name="Doe") + legal = Member.objects.create(first_name="Lee", last_name="Doe") + kid = Member.objects.create(first_name="Kit", last_name="Doe") + + FamilyMembership.objects.create(family=family, member=mum, role=FamilyMembership.FamilyRole.PARENT) + FamilyMembership.objects.create(family=family, member=legal, role=FamilyMembership.FamilyRole.GUARDIAN) + FamilyMembership.objects.create(family=family, member=kid, role=FamilyMembership.FamilyRole.CHILD) + + self.assertEqual(set(kid.guardians), {mum, legal}) + + def test_guardians_empty_for_a_parent(self): + family = Family.objects.create(name="The Roes") + parent = Member.objects.create(first_name="Ray", last_name="Roe") + kid = Member.objects.create(first_name="Ren", last_name="Roe") + + FamilyMembership.objects.create(family=family, member=parent, role=FamilyMembership.FamilyRole.PARENT) + FamilyMembership.objects.create(family=family, member=kid, role=FamilyMembership.FamilyRole.CHILD) + + self.assertEqual(list(parent.guardians), []) + + def test_guardians_do_not_leak_across_families(self): + family_a = Family.objects.create(name="Family A") + family_b = Family.objects.create(name="Family B") + parent_a = Member.objects.create(first_name="Ann", last_name="A") + parent_b = Member.objects.create(first_name="Ben", last_name="B") + kid = Member.objects.create(first_name="Cody", last_name="A") + + FamilyMembership.objects.create(family=family_a, member=parent_a, role=FamilyMembership.FamilyRole.PARENT) + FamilyMembership.objects.create(family=family_a, member=kid, role=FamilyMembership.FamilyRole.CHILD) + # parent_b belongs to a different family and must not appear as kid's guardian. + FamilyMembership.objects.create(family=family_b, member=parent_b, role=FamilyMembership.FamilyRole.PARENT) + + self.assertEqual(set(kid.guardians), {parent_a}) + + +class FamilyMembershipModelTests(TestCase): + def test_default_role_is_parent(self): + family = Family.objects.create(name="Fam") + member = Member.objects.create(first_name="D", last_name="Efault") + membership = FamilyMembership.objects.create(family=family, member=member) + + self.assertEqual(membership.role, FamilyMembership.FamilyRole.PARENT) + + def test_member_unique_per_family(self): + family = Family.objects.create(name="Fam") + member = Member.objects.create(first_name="Solo", last_name="Once") + FamilyMembership.objects.create(family=family, member=member, role=FamilyMembership.FamilyRole.PARENT) + + with self.assertRaises(IntegrityError): + FamilyMembership.objects.create(family=family, member=member, role=FamilyMembership.FamilyRole.CHILD) + + def test_same_member_can_join_multiple_families(self): + member = Member.objects.create(first_name="Multi", last_name="Fam") + family_a = Family.objects.create(name="A") + family_b = Family.objects.create(name="B") + + FamilyMembership.objects.create(family=family_a, member=member, role=FamilyMembership.FamilyRole.CHILD) + FamilyMembership.objects.create(family=family_b, member=member, role=FamilyMembership.FamilyRole.PARENT) + + self.assertEqual(member.family_memberships.count(), 2) + + def test_deleting_family_cascades_to_memberships(self): + family = Family.objects.create(name="Doomed") + member = Member.objects.create(first_name="Cas", last_name="Cade") + FamilyMembership.objects.create(family=family, member=member) + + family.delete() + + self.assertFalse(FamilyMembership.objects.exists()) + # The member itself survives; only the membership is removed. + self.assertTrue(Member.objects.filter(pk=member.pk).exists()) diff --git a/authentication/views.py b/authentication/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/authentication/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/club/__init__.py b/club/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/club/admin.py b/club/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/club/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/club/apps.py b/club/apps.py new file mode 100644 index 0000000..67bff61 --- /dev/null +++ b/club/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ClubConfig(AppConfig): + name = 'club' diff --git a/club/migrations/__init__.py b/club/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/club/models.py b/club/models.py new file mode 100644 index 0000000..c7ab82a --- /dev/null +++ b/club/models.py @@ -0,0 +1,20 @@ +from django.contrib.auth.base_user import AbstractBaseUser +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from clubmanager.base import ClubScopedModel, UUIDModel + + +class Club(UUIDModel): + name = models.CharField(max_length=255) + + class Meta: + verbose_name = _("club") + verbose_name_plural = _("clubs") + ordering = ["name"] + + def __str__(self): + return self.name + + +class ClubMembership(UUIDModel): ... diff --git a/club/tests.py b/club/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/club/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/club/views.py b/club/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/club/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/clubmanager/base.py b/clubmanager/base.py new file mode 100644 index 0000000..8b1346a --- /dev/null +++ b/clubmanager/base.py @@ -0,0 +1,21 @@ +import uuid + +from django.db import models + + +class UUIDModel(models.Model): + """Abstract base class giving every model a UUID primary key""" + + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + + class Meta: + abstract = True + + +class ClubScopedModel(UUIDModel): + """Abstract base for entities owned by a single club (tenant root).""" + + club = models.ForeignKey("club.Club", on_delete=models.CASCADE, related_name="%(class)ss") + + class Meta: + abstract = True diff --git a/clubmanager/settings.py b/clubmanager/settings.py index 418b78f..d55bd6c 100644 --- a/clubmanager/settings.py +++ b/clubmanager/settings.py @@ -44,11 +44,10 @@ INSTALLED_APPS = [ "django.contrib.messages", "django.contrib.staticfiles", "phonenumber_field", - "accounts", + "club.apps.ClubConfig", + "authentication.apps.AuthenticationConfig", ] -AUTH_USER_MODEL = "accounts.User" - MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", @@ -61,6 +60,8 @@ MIDDLEWARE = [ ROOT_URLCONF = "clubmanager.urls" +AUTH_USER_MODEL = "authentication.User" + TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", From b30522d3d07d3c48abb0368b16aeab892e377554 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Thu, 2 Jul 2026 16:37:56 +0200 Subject: [PATCH 005/137] Extend admin, migrations, and tests for authentication and club apps - Implement admin configurations for User, Member, Family, and FamilyMembership, with specialized inlines and filtered displays. - Introduce `UserCreationForm` and `UserChangeForm` for streamlined user management. - Enhance Family model with improved string representation and made name optional. - Add `Member.contact_email` property for prioritized email retrieval. - Include tests for the updated Family string logic, contact email functionality, and admin integration. - Add initial migration for club models (Club, ClubMembership) and updated migration for Family in the authentication app. - Configure IntelliJ IDEA for local SQLite database access. --- .idea/dataSources.xml | 12 +++ authentication/admin.py | 98 ++++++++++++++++- authentication/forms.py | 20 ++++ .../migrations/0002_alter_family_name.py | 18 ++++ authentication/models.py | 14 ++- authentication/tests.py | 100 ++++++++++++++++++ authentication/views.py | 3 - club/migrations/0001_initial.py | 36 +++++++ club/views.py | 3 - 9 files changed, 295 insertions(+), 9 deletions(-) create mode 100644 .idea/dataSources.xml create mode 100644 authentication/forms.py create mode 100644 authentication/migrations/0002_alter_family_name.py delete mode 100644 authentication/views.py create mode 100644 club/migrations/0001_initial.py delete mode 100644 club/views.py diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..e3a051f --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/db.sqlite3 + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/authentication/admin.py b/authentication/admin.py index 8c38f3f..d663cc6 100644 --- a/authentication/admin.py +++ b/authentication/admin.py @@ -1,3 +1,99 @@ from django.contrib import admin +from django.contrib.auth.admin import UserAdmin as BaseUserAdmin +from django.utils.translation import gettext_lazy as _ -# Register your models here. +from .forms import UserChangeForm, UserCreationForm +from .models import Family, FamilyMembership, Member, User + + +class MemberInline(admin.StackedInline): + """Edit the member profile attached to a login from the User page.""" + + model = Member + can_delete = False + extra = 0 + max_num = 1 + verbose_name_plural = _("member profile") + fields = ("first_name", "last_name", "date_of_birth", "email", "phone", "emergency_phone") + + +@admin.register(User) +class UserAdmin(BaseUserAdmin): + add_form = UserCreationForm + form = UserChangeForm + model = User + inlines = [MemberInline] + + list_display = ("email", "full_name", "is_staff", "is_active") + list_filter = ("is_staff", "is_superuser", "is_active", "groups") + search_fields = ("email", "member__first_name", "member__last_name") + ordering = ("email",) + readonly_fields = ("last_login",) + + fieldsets = ( + (None, {"fields": ("email", "password")}), + (_("Permissions"), {"fields": ("is_active", "is_staff", "is_superuser", "groups", "user_permissions")}), + (_("Important dates"), {"fields": ("last_login",)}), + ) + add_fieldsets = ((None, {"classes": ("wide",), "fields": ("email", "password1", "password2")}),) + + @admin.display(description=_("name")) + def full_name(self, obj): + return obj.get_full_name() + + +class MemberFamilyInline(admin.TabularInline): + """Family memberships shown on the Member page.""" + + model = FamilyMembership + extra = 1 + autocomplete_fields = ("family",) + + +@admin.register(Member) +class MemberAdmin(admin.ModelAdmin): + list_display = ("last_name", "first_name", "contact_email", "phone_display", "emergency_phone_display", "user") + list_select_related = ("user",) + search_fields = ("first_name", "last_name", "email") + autocomplete_fields = ("user",) + inlines = [MemberFamilyInline] + fields = ("user", "first_name", "last_name", "date_of_birth", "email", "phone", "emergency_phone") + + @admin.display(description=_("email"), ordering="email") + def contact_email(self, obj): + return obj.contact_email + + @admin.display(description=_("phone"), ordering="phone") + def phone_display(self, obj): + return obj.phone.as_international if obj.phone else "" + + @admin.display(description=_("emergency phone"), ordering="emergency_phone") + def emergency_phone_display(self, obj): + return obj.emergency_phone.as_international if obj.emergency_phone else "" + + +class FamilyMemberInline(admin.TabularInline): + """Members shown on the Family page.""" + + model = FamilyMembership + extra = 1 + autocomplete_fields = ("member",) + + +@admin.register(Family) +class FamilyAdmin(admin.ModelAdmin): + list_display = ("__str__", "member_count") + search_fields = ("name", "memberships__member__first_name", "memberships__member__last_name") + inlines = [FamilyMemberInline] + + @admin.display(description=_("members")) + def member_count(self, obj): + return obj.memberships.count() + + +@admin.register(FamilyMembership) +class FamilyMembershipAdmin(admin.ModelAdmin): + list_display = ("family", "member", "role") + list_filter = ("role",) + autocomplete_fields = ("family", "member") + search_fields = ("family__name", "member__first_name", "member__last_name") diff --git a/authentication/forms.py b/authentication/forms.py new file mode 100644 index 0000000..b922d84 --- /dev/null +++ b/authentication/forms.py @@ -0,0 +1,20 @@ +from django.contrib.auth.forms import BaseUserCreationForm +from django.contrib.auth.forms import UserChangeForm as DjangoUserChangeForm + +from .models import User + + +class UserCreationForm(BaseUserCreationForm): + """Add-user form for the email-based custom User (no ``username`` field).""" + + class Meta: + model = User + fields = ("email",) + + +class UserChangeForm(DjangoUserChangeForm): + """Change-user form; keeps the read-only password hash widget.""" + + class Meta: + model = User + fields = "__all__" diff --git a/authentication/migrations/0002_alter_family_name.py b/authentication/migrations/0002_alter_family_name.py new file mode 100644 index 0000000..ea2c70a --- /dev/null +++ b/authentication/migrations/0002_alter_family_name.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.6 on 2026-07-02 14:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='family', + name='name', + field=models.CharField(blank=True, max_length=255), + ), + ] diff --git a/authentication/models.py b/authentication/models.py index 29c9fab..0d30574 100644 --- a/authentication/models.py +++ b/authentication/models.py @@ -46,7 +46,7 @@ class User(AbstractBaseUser, PermissionsMixin): class Family(UUIDModel): - name = models.CharField(max_length=255) + name = models.CharField(max_length=255, blank=True) class Meta: verbose_name = _("family") @@ -54,7 +54,12 @@ class Family(UUIDModel): ordering = ["name"] def __str__(self): - return self.name + if self.name: + return self.name + surnames = sorted({last_name for last_name in self.memberships.values_list("member__last_name", flat=True) if last_name}) + if surnames: + return _("%(surnames)s family") % {"surnames": " / ".join(surnames)} + return _("Family %(id)s") % {"id": str(self.pk)[:8]} @property def guardians(self): @@ -98,6 +103,11 @@ class Member(UUIDModel): def get_short_name(self): return self.first_name + @property + def contact_email(self): + """Best email to reach this member: own contact email, else login email.""" + return self.email or (self.user.email if self.user_id else "") + @property def guardians(self): return Member.objects.filter( diff --git a/authentication/tests.py b/authentication/tests.py index 431dd9f..2cdd8e5 100644 --- a/authentication/tests.py +++ b/authentication/tests.py @@ -101,6 +101,20 @@ class MemberModelTests(TestCase): member = Member.objects.create(first_name="Cher", last_name="") self.assertEqual(member.get_full_name(), "Cher") + def test_contact_email_prefers_own_email(self): + user = User.objects.create_user(email="login@example.com", password="x") + member = Member.objects.create(user=user, first_name="Own", last_name="Email", email="own@example.com") + self.assertEqual(member.contact_email, "own@example.com") + + def test_contact_email_falls_back_to_login_email(self): + user = User.objects.create_user(email="login@example.com", password="x") + member = Member.objects.create(user=user, first_name="No", last_name="Email") + self.assertEqual(member.contact_email, "login@example.com") + + def test_contact_email_empty_without_email_or_user(self): + member = Member.objects.create(first_name="Zero", last_name="Contact") + self.assertEqual(member.contact_email, "") + def test_member_can_exist_without_user(self): member = Member.objects.create(first_name="No", last_name="Login") self.assertIsNone(member.user) @@ -126,6 +140,29 @@ class MemberModelTests(TestCase): Member.objects.create(user=user, first_name="Second", last_name="Member") +class FamilyNameOptionalTests(TestCase): + def test_family_can_be_created_without_a_name(self): + family = Family.objects.create() + self.assertEqual(family.name, "") + + def test_str_uses_name_when_present(self): + self.assertEqual(str(Family.objects.create(name="The Smiths")), "The Smiths") + + def test_str_falls_back_to_member_surnames(self): + family = Family.objects.create() + smith = Member.objects.create(first_name="Pat", last_name="Smith") + jones = Member.objects.create(first_name="Kim", last_name="Jones") + FamilyMembership.objects.create(family=family, member=smith, role=FamilyMembership.FamilyRole.PARENT) + FamilyMembership.objects.create(family=family, member=jones, role=FamilyMembership.FamilyRole.CHILD) + + # Distinct surnames, alphabetically ordered. + self.assertEqual(str(family), "Jones / Smith family") + + def test_str_falls_back_to_short_id_when_empty(self): + family = Family.objects.create() + self.assertEqual(str(family), f"Family {str(family.pk)[:8]}") + + class FamilyModelTests(TestCase): def setUp(self): self.family = Family.objects.create(name="The Smiths") @@ -232,3 +269,66 @@ class FamilyMembershipModelTests(TestCase): self.assertFalse(FamilyMembership.objects.exists()) # The member itself survives; only the membership is removed. self.assertTrue(Member.objects.filter(pk=member.pk).exists()) + + +class AdminSmokeTests(TestCase): + """Exercise the admin config end-to-end to catch misregistration + (bad search_fields, autocomplete targets, fieldsets, custom forms).""" + + def setUp(self): + self.admin = User.objects.create_superuser(email="root@example.com", password="pw-secret-123") + self.client.force_login(self.admin) + + def test_changelists_load(self): + for model in ("user", "member", "family", "familymembership"): + with self.subTest(model=model): + response = self.client.get(f"/admin/authentication/{model}/") + self.assertEqual(response.status_code, 200) + + def test_user_add_page_loads(self): + response = self.client.get("/admin/authentication/user/add/") + self.assertEqual(response.status_code, 200) + + def test_member_changelist_shows_grouped_numbers_and_fallback_email(self): + user = User.objects.create_user(email="fallback@example.com", password="pw") + Member.objects.create( + user=user, + first_name="Grouped", + last_name="Numbers", + phone="+32470123456", + emergency_phone="+3221234567", + ) + response = self.client.get("/admin/authentication/member/") + content = response.content.decode() + + # Numbers rendered in grouped international format, not raw E.164. + self.assertIn("+32 470 12 34 56", content) + self.assertIn("+32 2 123 45 67", content) + # Email column falls back to the linked login email. + self.assertIn("fallback@example.com", content) + + def test_create_user_through_admin_hashes_password(self): + response = self.client.post( + "/admin/authentication/user/add/", + { + "email": "new@example.com", + "password1": "a-good-password-42", + "password2": "a-good-password-42", + # Empty MemberInline management form — no profile created. + "member-TOTAL_FORMS": "0", + "member-INITIAL_FORMS": "0", + "member-MIN_NUM_FORMS": "0", + "member-MAX_NUM_FORMS": "1", + }, + ) + self.assertEqual(response.status_code, 302) + created = User.objects.get(email="new@example.com") + self.assertTrue(created.check_password("a-good-password-42")) + + def test_autocomplete_endpoints_respond(self): + # Member.user autocomplete resolves against UserAdmin.search_fields. + response = self.client.get( + "/admin/autocomplete/", + {"app_label": "authentication", "model_name": "member", "field_name": "user", "term": "root"}, + ) + self.assertEqual(response.status_code, 200) diff --git a/authentication/views.py b/authentication/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/authentication/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/club/migrations/0001_initial.py b/club/migrations/0001_initial.py new file mode 100644 index 0000000..139ad1e --- /dev/null +++ b/club/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# Generated by Django 6.0.6 on 2026-07-02 14:24 + +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Club', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ], + options={ + 'verbose_name': 'club', + 'verbose_name_plural': 'clubs', + 'ordering': ['name'], + }, + ), + migrations.CreateModel( + name='ClubMembership', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/club/views.py b/club/views.py deleted file mode 100644 index 91ea44a..0000000 --- a/club/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. From 44fe658efab5d253762ae1c9f55785c7b7fce821 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 5 Jul 2026 15:50:54 +0200 Subject: [PATCH 006/137] Localize authentication models with verbose names and update corresponding migration --- ..._alter_familymembership_family_and_more.py | 86 +++++++++++++++++++ authentication/models.py | 28 +++--- 2 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 authentication/migrations/0003_alter_family_name_alter_familymembership_family_and_more.py diff --git a/authentication/migrations/0003_alter_family_name_alter_familymembership_family_and_more.py b/authentication/migrations/0003_alter_family_name_alter_familymembership_family_and_more.py new file mode 100644 index 0000000..cbf8e1d --- /dev/null +++ b/authentication/migrations/0003_alter_family_name_alter_familymembership_family_and_more.py @@ -0,0 +1,86 @@ +# Generated by Django 6.0.6 on 2026-07-05 13:50 + +import django.db.models.deletion +import phonenumber_field.modelfields +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0002_alter_family_name'), + ] + + operations = [ + migrations.AlterField( + model_name='family', + name='name', + field=models.CharField(blank=True, max_length=255, verbose_name='name'), + ), + migrations.AlterField( + model_name='familymembership', + name='family', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='memberships', to='authentication.family', verbose_name='family'), + ), + migrations.AlterField( + model_name='familymembership', + name='member', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='family_memberships', to='authentication.member', verbose_name='member'), + ), + migrations.AlterField( + model_name='familymembership', + name='role', + field=models.CharField(choices=[('parent', 'parent'), ('child', 'child'), ('guardian', 'guardian'), ('other', 'other')], default='parent', max_length=255, verbose_name='role'), + ), + migrations.AlterField( + model_name='member', + name='date_of_birth', + field=models.DateField(blank=True, null=True, verbose_name='date of birth'), + ), + migrations.AlterField( + model_name='member', + name='email', + field=models.EmailField(blank=True, max_length=254, verbose_name='email'), + ), + migrations.AlterField( + model_name='member', + name='emergency_phone', + field=phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None, verbose_name='emergency phone number'), + ), + migrations.AlterField( + model_name='member', + name='first_name', + field=models.CharField(max_length=150, verbose_name='first name'), + ), + migrations.AlterField( + model_name='member', + name='last_name', + field=models.CharField(max_length=150, verbose_name='last name'), + ), + migrations.AlterField( + model_name='member', + name='phone', + field=phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None, verbose_name='phone number'), + ), + migrations.AlterField( + model_name='member', + name='user', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='member', to=settings.AUTH_USER_MODEL, verbose_name='user'), + ), + migrations.AlterField( + model_name='user', + name='email', + field=models.EmailField(db_index=True, max_length=254, unique=True, verbose_name='email'), + ), + migrations.AlterField( + model_name='user', + name='is_active', + field=models.BooleanField(default=True, verbose_name='is active?'), + ), + migrations.AlterField( + model_name='user', + name='is_staff', + field=models.BooleanField(default=False, verbose_name='is staff?'), + ), + ] diff --git a/authentication/models.py b/authentication/models.py index 0d30574..1ac4fd5 100644 --- a/authentication/models.py +++ b/authentication/models.py @@ -14,10 +14,10 @@ from .managers import UserManager class User(AbstractBaseUser, PermissionsMixin): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - email = models.EmailField(unique=True, db_index=True) + email = models.EmailField(_("email"), unique=True, db_index=True) - is_staff = models.BooleanField(default=False) - is_active = models.BooleanField(default=True) + is_staff = models.BooleanField(_("is staff?"), default=False) + is_active = models.BooleanField(_("is active?"), default=True) objects = UserManager() @@ -46,7 +46,7 @@ class User(AbstractBaseUser, PermissionsMixin): class Family(UUIDModel): - name = models.CharField(max_length=255, blank=True) + name = models.CharField(_("name"), max_length=255, blank=True) class Meta: verbose_name = _("family") @@ -77,16 +77,16 @@ class Family(UUIDModel): class Member(UUIDModel): - user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name="member", null=True, blank=True) + user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name="member", null=True, blank=True, verbose_name=_("user")) - first_name = models.CharField(max_length=150) - last_name = models.CharField(max_length=150) + first_name = models.CharField(_("first name"), max_length=150) + last_name = models.CharField(_("last name"), max_length=150) - date_of_birth = models.DateField(null=True, blank=True) + date_of_birth = models.DateField(_("date of birth"), null=True, blank=True) - email = models.EmailField(blank=True) - phone = PhoneNumberField(null=True, blank=True) - emergency_phone = PhoneNumberField(null=True, blank=True) + email = models.EmailField(_("email"), blank=True) + phone = PhoneNumberField(_("phone number"), null=True, blank=True) + emergency_phone = PhoneNumberField(_("emergency phone number"), null=True, blank=True) class Meta: verbose_name = _("member") @@ -124,9 +124,9 @@ class FamilyMembership(models.Model): GUARDIAN = "guardian", _("guardian") OTHER = "other", _("other") - family = models.ForeignKey(Family, on_delete=models.CASCADE, related_name="memberships") - member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="family_memberships") - role = models.CharField(max_length=255, choices=FamilyRole.choices, default=FamilyRole.PARENT) + family = models.ForeignKey(Family, on_delete=models.CASCADE, related_name="memberships", verbose_name=_("family")) + member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="family_memberships", verbose_name=_("member")) + role = models.CharField(_("role"), max_length=255, choices=FamilyRole.choices, default=FamilyRole.PARENT) class Meta: verbose_name = _("family membership") From 89c12c12b148e05129fa670d690d20748a80f35b Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 5 Jul 2026 23:51:35 +0200 Subject: [PATCH 007/137] Add ClubMembership enhancements, CSV import command, and related tests - Extend the ClubMembership model with `club`, `member`, and optional `license` fields, along with relevant constraints and ordering. - Implement verbose names for Club and ClubMembership models and update admin configurations for better display and filtering. - Add a `import_members_csv` management command for batch importing members, clubs, and memberships from a CSV file. - Include extensive tests for the `import_members_csv` command, ClubMembership model, and Club model. - Refactor related migrations, services, and test structure. --- authentication/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/import_members_csv.py | 43 ++++ authentication/services/__init__.py | 0 .../services/member_csv_importer.py | 180 +++++++++++++ authentication/tests.py | 237 ++++++++++++++++++ club/admin.py | 21 ++ ...ip_club_clubmembership_license_and_more.py | 33 +++ ...ership_options_alter_club_name_and_more.py | 43 ++++ .../0004_alter_clubmembership_license.py | 18 ++ club/models.py | 18 +- club/tests.py | 162 ++++++++++++ 12 files changed, 753 insertions(+), 2 deletions(-) create mode 100644 authentication/management/__init__.py create mode 100644 authentication/management/commands/__init__.py create mode 100644 authentication/management/commands/import_members_csv.py create mode 100644 authentication/services/__init__.py create mode 100644 authentication/services/member_csv_importer.py create mode 100644 club/migrations/0002_clubmembership_club_clubmembership_license_and_more.py create mode 100644 club/migrations/0003_alter_clubmembership_options_alter_club_name_and_more.py create mode 100644 club/migrations/0004_alter_clubmembership_license.py diff --git a/authentication/management/__init__.py b/authentication/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/authentication/management/commands/__init__.py b/authentication/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/authentication/management/commands/import_members_csv.py b/authentication/management/commands/import_members_csv.py new file mode 100644 index 0000000..61ad519 --- /dev/null +++ b/authentication/management/commands/import_members_csv.py @@ -0,0 +1,43 @@ +from django.core.management.base import BaseCommand, CommandError + +from authentication.services.member_csv_importer import MemberCsvImporter + + +class Command(BaseCommand): + help = "Import club members from a CSV file." + + def add_arguments(self, parser) -> None: + parser.add_argument( + "csv_file", + type=str, + help="Path to the CSV file to import.", + ) + parser.add_argument( + "--date-format", + default="%Y-%m-%d", + help="Date format for date_of_birth. Default: %%Y-%%m-%%d", + ) + + def handle(self, *args, **options) -> str | None: + importer = MemberCsvImporter(date_format=options["date_format"]) + + try: + result = importer.import_path(options["csv_file"]) + except Exception as exc: + raise CommandError(str(exc)) from exc + + for error in result.errors: + self.stderr.write(self.style.ERROR(f"Row {error.row_number} skipped: {error.message}")) + + self.stdout.write( + self.style.SUCCESS( + "Import complete. " + f"Members created: {result.created_members}. " + f"Members updated: {result.updated_members}. " + f"Users created: {result.created_users}. " + f"Clubs created: {result.created_clubs}. " + f"Memberships created: {result.created_memberships}. " + f"Memberships updated: {result.updated_memberships}. " + f"Rows skipped: {result.skipped_rows}." + ) + ) diff --git a/authentication/services/__init__.py b/authentication/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/authentication/services/member_csv_importer.py b/authentication/services/member_csv_importer.py new file mode 100644 index 0000000..8c810e1 --- /dev/null +++ b/authentication/services/member_csv_importer.py @@ -0,0 +1,180 @@ +import csv +from dataclasses import dataclass, field +from datetime import date, datetime +from pathlib import Path +from typing import IO, Any + +from django.contrib.auth import get_user_model +from django.db import transaction + +from authentication.models import Member +from club.models import Club, ClubMembership + +REQUIRED_COLUMNS = {"first_name", "last_name", "email", "date_of_birth", "create_account", "club_name", "license_number"} +TRUE_VALUES = {"1", "true", "yes", "y", "on"} + +User = get_user_model() + + +@dataclass +class MemberImportRowError: + row_number: int + message: str + + +@dataclass +class MemberImportResult: + created_members: int = 0 + updated_members: int = 0 + created_users: int = 0 + created_clubs: int = 0 + created_memberships: int = 0 + updated_memberships: int = 0 + skipped_rows: int = 0 + errors: list[MemberImportRowError] = field(default_factory=list) + + @property + def successful_rows(self): + return self.created_members + self.updated_members + + +@dataclass +class ImportedMemberRowResult: + member_created: bool + user_created: bool + club_created: bool + membership_created: bool + + +class MemberCsvImporter: + def __init__(self, *, date_format="%Y-%m-%d") -> None: + self.date_format = date_format + + def import_path(self, csv_path) -> MemberImportResult: + path = Path(csv_path) + + if not path.exists(): + raise FileNotFoundError(f"CSV file does not exist: {path}") + + with path.open(newline="", encoding="utf-8-sig") as csv_file: + return self.import_file(csv_file) + + def import_file(self, csv_file: IO[str]) -> MemberImportResult: + result = MemberImportResult() + reader = csv.DictReader(csv_file) + + if reader.fieldnames is None: + raise ValueError("CSV file is empty or missing a header row.") + + missing_columns = REQUIRED_COLUMNS - set(reader.fieldnames) + if missing_columns: + raise ValueError("CSV file is missing required columns: " + ", ".join(sorted(missing_columns))) + + for row_number, row in enumerate(reader, start=2): + try: + row_result = self.import_row(row) + except Exception as exc: + result.skipped_rows += 1 + result.errors.append( + MemberImportRowError( + row_number=row_number, + message=str(exc), + ) + ) + continue + + if row_result.member_created: + result.created_members += 1 + else: + result.updated_members += 1 + + if row_result.user_created: + result.created_users += 1 + + if row_result.club_created: + result.created_clubs += 1 + + if row_result.membership_created: + result.created_memberships += 1 + else: + result.updated_memberships += 1 + + return result + + @transaction.atomic + def import_row(self, row) -> ImportedMemberRowResult: + first_name = self.clean_required(row, "first_name") + last_name = self.clean_required(row, "last_name") + email = self.clean_required(row, "email").lower() + date_of_birth = self.parse_date(self.clean_required(row, "date_of_birth")) + create_account = self.as_bool(row.get("create_account", "")) + club_name = self.clean_required(row, "club_name") + license_number = row.get("license_number", "").strip() + + user = None + user_created = False + + if create_account: + user, user_created = self.get_or_create_user(email) + + member, member_created = Member.objects.update_or_create( + email=email, + defaults={ + "first_name": first_name, + "last_name": last_name, + "date_of_birth": date_of_birth, + "user": user, + }, + ) + + if create_account and member.user_id is None: + member.user = user + member.save(update_fields=["user"]) + + club, club_created = Club.objects.get_or_create(name=club_name) + + _, membership_created = ClubMembership.objects.update_or_create( + club=club, + member=member, + defaults={ + "license": license_number, + }, + ) + + return ImportedMemberRowResult( + member_created=member_created, + user_created=user_created, + club_created=club_created, + membership_created=membership_created, + ) + + def get_or_create_user(self, email) -> tuple[User, bool]: + user, created = User.objects.get_or_create( + email=email, + defaults={ + "is_active": True, + }, + ) + + if created: + user.set_unusable_password() + user.save(update_fields=["password"]) + + return user, created + + def clean_required(self, row, field_name) -> Any: + value = row.get(field_name, "").strip() + + if not value: + raise ValueError(f"{field_name} is required.") + + return value + + def parse_date(self, value) -> date: + try: + return datetime.strptime(value, self.date_format).date() + except ValueError as exc: + raise ValueError(f"Invalid date_of_birth '{value}'. Expected format: {self.date_format}.") from exc + + def as_bool(self, value) -> bool: + return value.strip().lower() in TRUE_VALUES diff --git a/authentication/tests.py b/authentication/tests.py index 2cdd8e5..c344312 100644 --- a/authentication/tests.py +++ b/authentication/tests.py @@ -1,11 +1,18 @@ +import tempfile import uuid +from datetime import date +from io import StringIO +from pathlib import Path from django.contrib.auth import get_user_model +from django.core.management import call_command +from django.core.management.base import CommandError from django.db import IntegrityError from django.db.models import SET_NULL from django.test import TestCase from authentication.models import Family, FamilyMembership, Member +from club.models import Club, ClubMembership User = get_user_model() @@ -332,3 +339,233 @@ class AdminSmokeTests(TestCase): {"app_label": "authentication", "model_name": "member", "field_name": "user", "term": "root"}, ) self.assertEqual(response.status_code, 200) + + +class ImportMembersCsvCommandTests(TestCase): + def write_csv(self, content): + temp_file = tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False, encoding="utf-8") + temp_file.write(content) + temp_file.close() + self.addCleanup(lambda: Path(temp_file.name).unlink(missing_ok=True)) + return temp_file.name + + def call_import_command(self, csv_path, **options): + stdout = StringIO() + stderr = StringIO() + + call_command( + "import_members_csv", + csv_path, + stdout=stdout, + stderr=stderr, + **options, + ) + + return stdout.getvalue(), stderr.getvalue() + + def test_import_creates_member_club_membership_and_user_when_requested(self): + csv_path = self.write_csv( + "\n".join( + [ + "first_name,last_name,email,date_of_birth,create_account,club_name,license_number", + "Jane,Doe,jane@example.com,2010-04-12,true,City Swim Club,LIC-001", + ] + ) + ) + + stdout, stderr = self.call_import_command(csv_path) + + self.assertEqual(stderr, "") + self.assertIn("Import complete.", stdout) + self.assertIn("Members created: 1.", stdout) + self.assertIn("Users created: 1.", stdout) + self.assertIn("Clubs created: 1.", stdout) + self.assertIn("Memberships created: 1.", stdout) + self.assertIn("Rows skipped: 0.", stdout) + + member = Member.objects.get(email="jane@example.com") + self.assertEqual(member.first_name, "Jane") + self.assertEqual(member.last_name, "Doe") + self.assertEqual(member.date_of_birth, date(2010, 4, 12)) + self.assertIsNotNone(member.user) + self.assertEqual(member.user.email, "jane@example.com") + self.assertFalse(member.user.has_usable_password()) + + club = Club.objects.get(name="City Swim Club") + membership = ClubMembership.objects.get(club=club, member=member) + self.assertEqual(membership.license, "LIC-001") + + def test_import_creates_member_without_user_when_create_account_is_false(self): + csv_path = self.write_csv( + "\n".join( + [ + "first_name,last_name,email,date_of_birth,create_account,club_name,license_number", + "John,Smith,john@example.com,2009-11-03,false,City Swim Club,LIC-002", + ] + ) + ) + + stdout, stderr = self.call_import_command(csv_path) + + self.assertEqual(stderr, "") + self.assertIn("Members created: 1.", stdout) + self.assertIn("Users created: 0.", stdout) + + member = Member.objects.get(email="john@example.com") + self.assertIsNone(member.user) + self.assertFalse(User.objects.filter(email="john@example.com").exists()) + + def test_import_updates_existing_member_and_membership(self): + club = Club.objects.create(name="City Swim Club") + member = Member.objects.create( + first_name="Old", + last_name="Name", + email="jane@example.com", + date_of_birth=date(2010, 1, 1), + ) + ClubMembership.objects.create( + club=club, + member=member, + license="OLD-LIC", + ) + + csv_path = self.write_csv( + "\n".join( + [ + "first_name,last_name,email,date_of_birth,create_account,club_name,license_number", + "Jane,Doe,jane@example.com,2010-04-12,false,City Swim Club,LIC-NEW", + ] + ) + ) + + stdout, stderr = self.call_import_command(csv_path) + + self.assertEqual(stderr, "") + self.assertIn("Members created: 0.", stdout) + self.assertIn("Members updated: 1.", stdout) + self.assertIn("Memberships created: 0.", stdout) + self.assertIn("Memberships updated: 1.", stdout) + + member.refresh_from_db() + self.assertEqual(member.first_name, "Jane") + self.assertEqual(member.last_name, "Doe") + self.assertEqual(member.date_of_birth, date(2010, 4, 12)) + + membership = ClubMembership.objects.get(club=club, member=member) + self.assertEqual(membership.license, "LIC-NEW") + + def test_import_links_existing_user_when_create_account_is_true(self): + user = User.objects.create_user(email="jane@example.com", password="secret123") + + csv_path = self.write_csv( + "\n".join( + [ + "first_name,last_name,email,date_of_birth,create_account,club_name,license_number", + "Jane,Doe,jane@example.com,2010-04-12,true,City Swim Club,LIC-001", + ] + ) + ) + + stdout, stderr = self.call_import_command(csv_path) + + self.assertEqual(stderr, "") + self.assertIn("Users created: 0.", stdout) + + member = Member.objects.get(email="jane@example.com") + self.assertEqual(member.user, user) + self.assertTrue(user.check_password("secret123")) + + def test_import_supports_custom_date_format(self): + csv_path = self.write_csv( + "\n".join( + [ + "first_name,last_name,email,date_of_birth,create_account,club_name,license_number", + "Jane,Doe,jane@example.com,12/04/2010,false,City Swim Club,LIC-001", + ] + ) + ) + + stdout, stderr = self.call_import_command(csv_path, date_format="%d/%m/%Y") + + self.assertEqual(stderr, "") + self.assertIn("Members created: 1.", stdout) + + member = Member.objects.get(email="jane@example.com") + self.assertEqual(member.date_of_birth, date(2010, 4, 12)) + + def test_import_skips_invalid_row_and_imports_valid_rows(self): + csv_path = self.write_csv( + "\n".join( + [ + "first_name,last_name,email,date_of_birth,create_account,club_name,license_number", + "Jane,Doe,jane@example.com,2010-04-12,false,City Swim Club,LIC-001", + "Broken,Date,broken@example.com,not-a-date,false,City Swim Club,LIC-002", + ] + ) + ) + + stdout, stderr = self.call_import_command(csv_path) + + self.assertIn("Row 3 skipped:", stderr) + self.assertIn("Invalid date_of_birth 'not-a-date'.", stderr) + self.assertIn("Members created: 1.", stdout) + self.assertIn("Rows skipped: 1.", stdout) + + self.assertTrue(Member.objects.filter(email="jane@example.com").exists()) + self.assertFalse(Member.objects.filter(email="broken@example.com").exists()) + + def test_import_fails_for_missing_file(self): + stdout = StringIO() + stderr = StringIO() + + with self.assertRaises(CommandError) as context: + call_command( + "import_members_csv", + "does-not-exist.csv", + stdout=stdout, + stderr=stderr, + ) + + self.assertIn("CSV file does not exist", str(context.exception)) + + def test_import_fails_for_missing_required_columns(self): + csv_path = self.write_csv( + "\n".join( + [ + "first_name,last_name,email", + "Jane,Doe,jane@example.com", + ] + ) + ) + + stdout = StringIO() + stderr = StringIO() + + with self.assertRaises(CommandError) as context: + call_command( + "import_members_csv", + csv_path, + stdout=stdout, + stderr=stderr, + ) + + self.assertIn("CSV file is missing required columns:", str(context.exception)) + self.assertIn("club_name", str(context.exception)) + self.assertIn("date_of_birth", str(context.exception)) + self.assertIn("license_number", str(context.exception)) + + def test_import_fails_for_empty_csv_file(self): + csv_path = self.write_csv("") + + stdout = StringIO() + stderr = StringIO() + + with self.assertRaises(CommandError) as context: + call_command( + "import_members_csv", + csv_path, + stdout=stdout, + stderr=stderr, + ) + + self.assertIn("CSV file is empty or missing a header row.", str(context.exception)) diff --git a/club/admin.py b/club/admin.py index 8c38f3f..b526846 100644 --- a/club/admin.py +++ b/club/admin.py @@ -1,3 +1,24 @@ from django.contrib import admin +from django.utils.translation import gettext_lazy as _ + +from .models import Club, ClubMembership + # Register your models here. +@admin.register(Club) +class ClubAdmin(admin.ModelAdmin): + list_display = ["name"] + search_fields = ["name"] + ordering = ["name"] + + +@admin.register(ClubMembership) +class ClubMembershipAdmin(admin.ModelAdmin): + list_display = ["club__name", "member__last_name", "member__first_name", "license"] + search_fields = ["club__name", "member__last_name", "member__first_name"] + list_filter = ["club"] + raw_id_fields = ["member"] + fieldsets = [ + [None, {"fields": ["club", "member"]}], + [_("Member informaton"), {"fields": ["license"]}], + ] diff --git a/club/migrations/0002_clubmembership_club_clubmembership_license_and_more.py b/club/migrations/0002_clubmembership_club_clubmembership_license_and_more.py new file mode 100644 index 0000000..cb993cf --- /dev/null +++ b/club/migrations/0002_clubmembership_club_clubmembership_license_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 6.0.6 on 2026-07-05 13:50 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0003_alter_family_name_alter_familymembership_family_and_more'), + ('club', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='clubmembership', + name='club', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='members', to='club.club'), + preserve_default=False, + ), + migrations.AddField( + model_name='clubmembership', + name='license', + field=models.CharField(default=1, max_length=250), + preserve_default=False, + ), + migrations.AddField( + model_name='clubmembership', + name='member', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='member_of', to='authentication.member'), + preserve_default=False, + ), + ] diff --git a/club/migrations/0003_alter_clubmembership_options_alter_club_name_and_more.py b/club/migrations/0003_alter_clubmembership_options_alter_club_name_and_more.py new file mode 100644 index 0000000..d909263 --- /dev/null +++ b/club/migrations/0003_alter_clubmembership_options_alter_club_name_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 6.0.6 on 2026-07-05 20:51 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('authentication', '0003_alter_family_name_alter_familymembership_family_and_more'), + ('club', '0002_clubmembership_club_clubmembership_license_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='clubmembership', + options={'ordering': ['club', 'member__last_name', 'member__first_name'], 'verbose_name': 'club membership', 'verbose_name_plural': 'club memberships'}, + ), + migrations.AlterField( + model_name='club', + name='name', + field=models.CharField(max_length=255, verbose_name='name'), + ), + migrations.AlterField( + model_name='clubmembership', + name='club', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='members', to='club.club', verbose_name='club'), + ), + migrations.AlterField( + model_name='clubmembership', + name='license', + field=models.CharField(max_length=250, verbose_name='license'), + ), + migrations.AlterField( + model_name='clubmembership', + name='member', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='member_of', to='authentication.member', verbose_name='member'), + ), + migrations.AlterUniqueTogether( + name='clubmembership', + unique_together={('club', 'member')}, + ), + ] diff --git a/club/migrations/0004_alter_clubmembership_license.py b/club/migrations/0004_alter_clubmembership_license.py new file mode 100644 index 0000000..87ffff5 --- /dev/null +++ b/club/migrations/0004_alter_clubmembership_license.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.6 on 2026-07-05 20:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0003_alter_clubmembership_options_alter_club_name_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='clubmembership', + name='license', + field=models.CharField(blank=True, max_length=250, verbose_name='license'), + ), + ] diff --git a/club/models.py b/club/models.py index c7ab82a..da28c33 100644 --- a/club/models.py +++ b/club/models.py @@ -2,11 +2,12 @@ from django.contrib.auth.base_user import AbstractBaseUser from django.db import models from django.utils.translation import gettext_lazy as _ +from authentication.models import Member from clubmanager.base import ClubScopedModel, UUIDModel class Club(UUIDModel): - name = models.CharField(max_length=255) + name = models.CharField(_("name"), max_length=255) class Meta: verbose_name = _("club") @@ -17,4 +18,17 @@ class Club(UUIDModel): return self.name -class ClubMembership(UUIDModel): ... +class ClubMembership(UUIDModel): + member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="member_of", verbose_name=_("member")) + club = models.ForeignKey(Club, on_delete=models.CASCADE, related_name="members", verbose_name=_("club")) + + license = models.CharField(_("license"), max_length=250, blank=True) + + class Meta: + verbose_name = _("club membership") + verbose_name_plural = _("club memberships") + ordering = ["club", "member__last_name", "member__first_name"] + unique_together = ("club", "member") + + def __str__(self): + return f"{self.club} - {self.member}" diff --git a/club/tests.py b/club/tests.py index 7ce503c..ea5615c 100644 --- a/club/tests.py +++ b/club/tests.py @@ -1,3 +1,165 @@ +import uuid + +from django.db import IntegrityError from django.test import TestCase +from authentication.models import Member + +from .models import Club, ClubMembership + + +class ClubModelTests(TestCase): + def test_str_returns_name(self): + club = Club.objects.create(name="City Swim Club") + + self.assertEqual(str(club), "City Swim Club") + + def test_pk_is_uuid(self): + club = Club.objects.create(name="City Swim Club") + + self.assertIsInstance(club.pk, uuid.UUID) + + def test_clubs_are_ordered_by_name(self): + Club.objects.create(name="Zulu Club") + Club.objects.create(name="Alpha Club") + Club.objects.create(name="Middle Club") + + self.assertEqual( + list(Club.objects.values_list("name", flat=True)), + ["Alpha Club", "Middle Club", "Zulu Club"], + ) + + def test_verbose_names(self): + self.assertEqual(Club._meta.verbose_name, "club") + self.assertEqual(Club._meta.verbose_name_plural, "clubs") + + +class ClubMembershipModelTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="City Swim Club") + self.member = Member.objects.create( + first_name="Jane", + last_name="Doe", + email="jane@example.com", + ) + + def test_str_returns_club_and_member(self): + membership = ClubMembership.objects.create( + club=self.club, + member=self.member, + license="LIC-001", + ) + + self.assertEqual(str(membership), "City Swim Club - Jane Doe") + + def test_license_is_optional(self): + membership = ClubMembership.objects.create( + club=self.club, + member=self.member, + ) + + self.assertEqual(membership.license, "") + + def test_pk_is_uuid(self): + membership = ClubMembership.objects.create( + club=self.club, + member=self.member, + ) + + self.assertIsInstance(membership.pk, uuid.UUID) + + def test_same_member_can_join_different_clubs(self): + other_club = Club.objects.create(name="Other Swim Club") + + first_membership = ClubMembership.objects.create( + club=self.club, + member=self.member, + license="LIC-001", + ) + second_membership = ClubMembership.objects.create( + club=other_club, + member=self.member, + license="LIC-002", + ) + + self.assertEqual(first_membership.member, self.member) + self.assertEqual(second_membership.member, self.member) + self.assertEqual(self.member.member_of.count(), 2) + + def test_member_is_unique_per_club(self): + ClubMembership.objects.create( + club=self.club, + member=self.member, + license="LIC-001", + ) + + with self.assertRaises(IntegrityError): + ClubMembership.objects.create( + club=self.club, + member=self.member, + license="LIC-002", + ) + + def test_deleting_club_deletes_membership_but_keeps_member(self): + ClubMembership.objects.create( + club=self.club, + member=self.member, + license="LIC-001", + ) + + self.club.delete() + + self.assertFalse(ClubMembership.objects.exists()) + self.assertTrue(Member.objects.filter(pk=self.member.pk).exists()) + + def test_deleting_member_deletes_membership_but_keeps_club(self): + ClubMembership.objects.create( + club=self.club, + member=self.member, + license="LIC-001", + ) + + self.member.delete() + + self.assertFalse(ClubMembership.objects.exists()) + self.assertTrue(Club.objects.filter(pk=self.club.pk).exists()) + + def test_memberships_are_ordered_by_club_then_member_name(self): + alpha_club = Club.objects.create(name="Alpha Club") + zulu_club = Club.objects.create(name="Zulu Club") + + jane = Member.objects.create(first_name="Jane", last_name="Doe") + alice = Member.objects.create(first_name="Alice", last_name="Smith") + bob = Member.objects.create(first_name="Bob", last_name="Smith") + + ClubMembership.objects.create(club=zulu_club, member=bob) + ClubMembership.objects.create(club=alpha_club, member=bob) + ClubMembership.objects.create(club=alpha_club, member=alice) + ClubMembership.objects.create(club=alpha_club, member=jane) + + self.assertEqual( + [(membership.club.name, membership.member.last_name, membership.member.first_name) for membership in ClubMembership.objects.all()], + [ + ("Alpha Club", "Doe", "Jane"), + ("Alpha Club", "Smith", "Alice"), + ("Alpha Club", "Smith", "Bob"), + ("Zulu Club", "Smith", "Bob"), + ], + ) + + def test_reverse_relations(self): + membership = ClubMembership.objects.create( + club=self.club, + member=self.member, + license="LIC-001", + ) + + self.assertEqual(list(self.club.members.all()), [membership]) + self.assertEqual(list(self.member.member_of.all()), [membership]) + + def test_verbose_names(self): + self.assertEqual(ClubMembership._meta.verbose_name, "club membership") + self.assertEqual(ClubMembership._meta.verbose_name_plural, "club memberships") + + # Create your tests here. From 19b92d61f7d65874213bf47c566fcee7db45dd38 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sat, 11 Jul 2026 23:23:36 +0200 Subject: [PATCH 008/137] Add ARCHITECTURE.md to define domain and model architecture - Introduce a comprehensive architecture document outlining the app's domain model, design principles, and shared conventions. - Establish the foundation for multi-tenancy and tenant scoping via `Club` as the tenant root and `ClubScopedModel`. - Detail the decomposition of the app into planned sub-apps and their responsibilities. - Define model structure, relationships, and access control mechanisms (RBAC) through `ClubRole` and service-layer authorization. - Provide a roadmap for tenant-aware features: seasons, rosters, events, shop, and dynamic forms. --- .idea/markdown.xml | 6 + ARCHITECTURE.md | 720 +++++++++++++++++++++++++++++++++++++++++++++ ARCHITECTURE.pdf | Bin 0 -> 368159 bytes CLAUDE.md | 17 +- pyproject.toml | 24 +- 5 files changed, 748 insertions(+), 19 deletions(-) create mode 100644 .idea/markdown.xml create mode 100644 ARCHITECTURE.md create mode 100644 ARCHITECTURE.pdf diff --git a/.idea/markdown.xml b/.idea/markdown.xml new file mode 100644 index 0000000..00405d5 --- /dev/null +++ b/.idea/markdown.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..d10135e --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,720 @@ +# ClubManager — Model & Domain Architecture + +Baseline reference for implementing the domain models. This describes the **intended +shape** of the data model: what exists today, what is planned, and the conventions every +app should follow. It is a living document — update it when the model changes. + +> **Tenancy: multi-tenant (row-based / shared-schema).** ClubManager is designed as a +> **multi-tenant platform** — one deployment serves many clubs, with **`Club` as the tenant +> root**. Isolation is **row-based**: a shared database and schema where every club-owned +> row carries a `club` FK (via `ClubScopedModel`), and *all* access is scoped to the +> current tenant. The mechanics — tenant resolution, scoping manager, per-club uniqueness — +> are specified in **§2.4**. +> +> ⚠️ **This supersedes `CLAUDE.md`**, which currently states the app is "deliberately *not* +> multi-tenant … there is no `club_id` tenancy." That guidance and the project memory are +> now **out of date** and must be updated to match this document — see the **banner at the +> foot of this file**. Where the two disagree, this architecture is the intended direction. + +--- + +## 1. App decomposition + +The `isort` `known-first-party` roadmap lists nine apps. The build split the original +`accounts` app into `authentication` + `club`. Per the decisions in §7, the people models +(`Member`, `Family`) **move out of `authentication` into a dedicated `members` app**, and +two apps (`formbuilder`, `shop`) are added **beyond the original roadmap** — add all new +labels to `known-first-party` in `pyproject.toml` when they land. The target decomposition: + +| App | Status | Responsibility | Models | +|------------------|--------------|-----------------------------------------------------------|--------| +| `authentication` | **built** | Login identity + tenancy/role services (global, cross-club) | `User` | +| `members` | **planned** | People: person records, families (extract from `authentication`) | `Member`, `Family`, `FamilyMembership` | +| `club` | **built** | Tenant root, **season**, season-scoped affiliation, club roles | `Club`, `Season` *(planned)*, `ClubMembership`, `ClubRole` *(planned)* | +| `teams` | planned | Teams and season rosters | `Team`, `TeamMembership`, `StaffAssignment` | +| `events` | planned | Training / matches / social events + attendance | `Event`, `Attendance` | +| `news` | planned | Editorial news for the public site | `Article`, `Category` | +| `pages` | planned | Flat CMS pages for the public site | `Page` | +| `home` | planned | Homepage composition / featured content | `HomeConfig` (per-club) or config-only | +| `formbuilder` | planned | Admin-defined dynamic forms + submissions + reporting | `Form`, `Field`, `Submission`, `Answer` | +| `shop` | planned | Cart-like shop, orders, payments, PDF invoices | `Product`, `Cart`, `CartItem`, `Order`, `OrderLine`, `Payment`, `Invoice` | +| `search` | planned | Site search (likely no models; index/config only) | — | + +**`User` stays global** (one login identity across the whole platform); everything else +that belongs to a club is tenant-scoped (§2.4). This is why `Member` — a *person within a +club* — lives in its own app and carries a `club` FK, while `User` does not. + +**Open decision (§7):** whether to migrate `Member`/`Family` out of `authentication` into +a dedicated `members` app. Recommendation: keep them in `authentication` for now; revisit +only if the app grows unwieldy. The roadmap `members` name is reserved either way. + +--- + +## 2. Shared conventions + +These are already established in code — every new model follows them. + +- **UUID primary keys.** Inherit `clubmanager.base.UUIDModel` (`id = UUIDField(default=uuid4)`). + Never expose sequential integer PKs. +- **`ClubScopedModel`** (`clubmanager.base`) adds the tenant `club` FK + (`related_name="%(class)ss"`) and, under multi-tenancy, a tenant-aware manager + auto + club-stamping `save()` (§2.4). **Every aggregate-root model inherits it**; leaf rows + reachable only via a scoped parent (e.g. `Attendance` via `Event`) may inherit scope from + the parent, though denormalising `club` onto them is recommended (§2.4). +- **i18n everywhere.** Every field gets a `gettext_lazy` verbose name; every model sets + `verbose_name` / `verbose_name_plural` and a sensible `Meta.ordering`. `TextChoices` + labels are translated too. +- **`__str__` on every model**, human-readable. +- **Enumerations** use nested `models.TextChoices` (see `FamilyMembership.FamilyRole`). +- **Phone numbers** use `PhoneNumberField` (from `django-phonenumber-field`), nullable. +- **Through models** for many-to-many relationships that carry data (role, jersey number, + attendance status) — never a bare `ManyToManyField` when the link has attributes. +- **Business logic in `services/`**, not fat models or views (see + `authentication/services/member_csv_importer.py`). Management commands are thin wrappers + over services (see `import_members_csv`). +- **Migrations are generated, not hand-edited** (ruff-excluded). + +### Naming & relations +- `related_name` is explicit and plural on the "many" side, chosen to read naturally from + the parent (`club.members`, `family.memberships`, `member.family_memberships`). +- Deletion policy is deliberate per FK: `CASCADE` for owned children, + `SET_NULL`(+`null=True`) where the child should survive its parent (e.g. + `Member.user`), `PROTECT` for references that must not silently disappear (planned: + `Season` on rosters/events — see §5). + +### 2.4 Multi-tenancy (row-based, shared schema) + +`Club` is the **tenant root**. One deployment, one database, one schema; tenants are +separated by a `club` FK on every owned row and by disciplined scoping of every query. +This is the lightest multi-tenancy model and matches the existing `ClubScopedModel` +scaffolding — no Postgres schemas, no per-tenant databases, no `django-tenants`. + +**What gets a `club` FK.** Every *aggregate root* inherits `ClubScopedModel` and so carries +`club` (`Member`, `Family`, `Season`, `Team`, `Event`, `ClubRole`, `Article`, `Category`, +`Page`, `Form`, `Product`, `Cart`, `Order`, `Invoice`, …). Leaf rows reachable only through +a scoped parent (`Answer`→`Submission`, `OrderLine`/`Payment`→`Order`, `CartItem`→`Cart`, +`Attendance`→`Event`, `TeamMembership`/`StaffAssignment`→`Team`) may inherit scope via the +parent — but **denormalising `club` onto them too is recommended** for leak-proof filtering +and DB-level constraints. `User` is the **only** global identity model; it has no `club`. + +**People vs. logins under tenancy.** A `User` is one platform-wide login that may belong to +several clubs; a `Member` is that person *within one club*. So: +- `Member.user` becomes a **`ForeignKey`** (not `OneToOneField`) — one user → many members + (at most one per club): `unique_together (club, user)`. +- `User.get_full_name()` can no longer assume a single member; resolve the member **for the + current club** (via the tenant context below), falling back to email. + +**Tenant resolution → `request.club`.** A `ClubTenantMiddleware` resolves the active club +per request (recommended: **subdomain**, `ajax-united.clubmanager.app`; path-prefix +`/c//` is the alternative) and stores it on `request.club` *and* in a context +variable so non-request code (services, management commands) can read it: + +``` +# clubmanager/tenancy.py +from contextvars import ContextVar +_current_club: ContextVar = ContextVar("current_club", default=None) + +def set_current_club(club): _current_club.set(club) +def get_current_club(): return _current_club.get() +def require_current_club(): # raises if unset — use in write paths + club = _current_club.get() + if club is None: raise RuntimeError("No active club in context") + return club + +class ClubTenantMiddleware: # resolves subdomain -> Club, sets both + ... # request.club = club; set_current_club(club) +``` + +**Scoping manager.** `ClubScopedModel` gets a tenant-aware manager so day-to-day queries +can't accidentally cross tenants: + +``` +class TenantQuerySet(models.QuerySet): + def for_club(self, club): return self.filter(club=club) + def current(self): return self.filter(club=require_current_club()) + +class ClubScopedModel(UUIDModel): + club = models.ForeignKey("club.Club", on_delete=models.CASCADE, related_name="%(class)ss") + objects = TenantQuerySet.as_manager() + def save(self, *args, **kwargs): # auto-stamp club from context if unset + if self.club_id is None: self.club = require_current_club() + super().save(*args, **kwargs) + class Meta: abstract = True +``` + +Prefer **explicit** `.for_club(club)` / `.current()` in views and services over a fully +automatic global filter — auto-filtering via context state is convenient but hides tenant +boundaries and bites hard in shells, tasks, and tests. Keep scoping visible. + +**Per-club uniqueness.** Every constraint that was globally unique becomes **unique per +club**. Concretely: `Season.name`, all public `slug`s (`Article`, `Page`, `Form`, +`Product`), `Team (season, name)`, jersey numbers `(team, jersey_number)`, and the +human-readable counters `Order.number` / `Invoice.number` are scoped by / allocated per +club. A bare `unique=True` on a tenant model is almost always a bug — use +`UniqueConstraint(fields=["club", …])`. + +**Cross-cutting consequences (checklist for every feature):** +- Admin: register a `club` list-filter and scope `get_queryset` for non-superusers. +- Roles are **per-club** — see §3 (global Django Groups don't fit; use `ClubRole`). +- Sequential numbers (invoices) allocate per club inside a transaction — never `count()+1`. +- Tests must set a current club (a `with_club(club)` context-manager helper). +- Config: wildcard host + CSRF for subdomains; see §8. + +--- + +## 3. Roles & access control (RBAC) + +Authorization is **service-layer, and per-club** (§2.4). Because roles differ per tenant — +a user can be a treasurer at club A and merely a member at club B — global Django `Group`s +(which are platform-wide) **do not fit**. Roles are stored as tenant-scoped `ClubRole` rows +and *all* permission decisions go through a single service module; no `django-guardian`, +no per-club Group hacks. Django's own permission framework is retained **only** for the +platform-operator layer (`is_staff` / `is_superuser` in Django admin). + +### 3.1 Two layers + +1. **Platform operators** — `User.is_superuser` / `is_staff`. Global, cross-club; run the + Django admin, manage the tenant list. Not a club role. +2. **Club roles** — a member's standing *within one club*, stored per tenant. Two kinds: + - **Club-wide roles** → `ClubRole` rows (below). + - **Object-scoped roles** → derived from the domain graph, no extra rows: + - Coach / manager **of a specific team** → `StaffAssignment(team, member, role)`. + - Parent / guardian **of a specific member** → `FamilyMembership` + the family graph. + - Purchaser vs. beneficiary → `Order`/`OrderLine` + `ClubMembership`. + +### 3.2 `ClubRole` — club-wide role assignments *(app: `club`)* + +``` +ClubRole(ClubScopedModel) # ClubScopedModel -> carries `club` (§2.4) + member FK Member (CASCADE, related_name="roles") + role CharField (TextChoices: MEMBER | EDITOR | TREASURER | BOARD) + Meta: unique_together (club, member, role) +``` + +| Role | Grants (representative) | +|-------------|------------------------------------------------------------------------------| +| *Public* | Anonymous — no row; read-only public site of that club. | +| `MEMBER` | View own + family data, own rosters/attendance, own orders/invoices, submit member-only forms. | +| `EDITOR` | Manage that club's `news`, `pages`, `formbuilder` content. | +| `TREASURER` | Manage that club's `shop`: products, orders, payments, issue/void invoices. | +| `BOARD` | Full management of that club: members, roles, all of the above. | + +`COACH` / `TEAM_MANAGER` are deliberately **not** `ClubRole`s — being a coach is always +*of a team*, so it lives on `StaffAssignment` (§5.3). "Is this user a coach at this club?" += "do they have any `StaffAssignment` on a team in this club?". + +### 3.3 The permission service + +One module — `club/services/access.py` (or `authentication/services/access.py`) — answers +every authorization question, always taking the club/object as an argument: + +``` +has_club_role(user, club, role) -> bool # ClubRole lookup +roles_in_club(user, club) -> set[str] # incl. derived COACH/MANAGER +teams_managed_by(user, club) -> QuerySet[Team] +members_visible_to(user, club) -> QuerySet[Member] # self + family + managed teams +can_edit_event(user, event) -> bool +can_manage_shop(user, club) -> bool # TREASURER or BOARD +``` + +Views, admin, and templates call these — never re-derive access inline. Each helper scopes +to the given club (§2.4), so a user's powers in club A never leak into club B. + +### 3.4 Keeping roles in sync with domain state + +`ClubRole` membership is **reconciled from domain facts**, not hand-assigned: + +- An **active** `ClubMembership` for the club's current season → grant the `MEMBER` role; + a lapsed one → revoke. (Season-scoped membership: §5.1.) +- A `StaffAssignment` needs no `ClubRole` — coach status is derived (§3.2). + +Implement as `club/services/access.py::reconcile_roles(user, club)`, invoked on the state +changes that matter (membership activation/lapse), so authorization never drifts from data. + +--- + +## 4. Built models (as-is, + planned tenancy changes) + +### `authentication` + +**`User`** — custom auth model, email is the login (`USERNAME_FIELD = "email"`, no +username). UUID PK. `objects = UserManager()` (email-based `create_user` / +`create_superuser`). **Stays global — the one model with no `club` FK** (§2.4). Because a +user may belong to several clubs, `get_full_name` / `get_short_name` resolve the `Member` +**for the current club** (via tenant context), falling back to email — they can no longer +assume a single member. + +### `members` *(planned — extract from `authentication`)* + +`Member`, `Family`, `FamilyMembership` **move here** and become tenant-scoped +(`ClubScopedModel`). + +**`Member`** — a *person within one club*. `user` becomes a **`ForeignKey`** (was +`OneToOneField`), still nullable (`on_delete=SET_NULL`), so one login maps to one member +*per club* (`unique_together (club, user)`) and members can exist without logins (children, +imports). Holds name, DOB, contact `email`/`phone`/`emergency_phone`. `contact_email` +prefers the member's own email, else the login email. `guardians` returns parent/guardian +members reachable through shared families (all within the same club). + +**`Family`** + **`FamilyMembership`** — households, tenant-scoped. `FamilyMembership` carries +a `FamilyRole` (`parent` / `child` / `guardian` / `other`), `unique_together (family, +member)`; `Family.guardians` / `Family.children` are role-derived querysets. Powers the +"parents see their children's data" object-scope (§3.1). + +### `club` + +**`Club`** — **the tenant root** (§2.4). Currently just `name`; extend with `slug` (unique, +drives subdomain/path resolution), contact, and branding. Provide `Club.objects.current()` +and resolve it in middleware — never hardcode a PK. + +**`ClubMembership`** — links a `Member` to the `Club` with a `license` string. **Being made +season-scoped** (§5.1): it gains a `season` FK and sign-up / fee-status fields, so each row +is one member's affiliation for one season (`unique_together (club, member, season)`). This +is the record the `MEMBER` role and shop fulfilment key off of (§3.4, §5.7). + +--- + +## 5. Planned models (design) + +Field lists below are **sketches** to implement against, not final migrations. + +All sketches below are tenant-scoped: aggregate roots inherit **`ClubScopedModel`** (the +`club` FK is shown implicitly and every listed `unique_together` is *within a club*, §2.4). + +### 5.1 `Season` — the central organizing concept *(app: `club`)* + +Everything time-bound hangs off a season. Rosters, events, and attendance are +**season-scoped via FK** — never global state. Seasons are **per club** — each club runs +its own. + +``` +Season(ClubScopedModel) # -> carries `club` + name CharField # e.g. "2025–2026" + start_date DateField + end_date DateField + is_current BooleanField # exactly one true PER CLUB; enforce in save()/service + Meta: unique_together (club, name); ordering = ["-start_date"]; get_latest_by = "start_date" +``` + +- Provide `Season.objects.current()` (scoped to the current club, §2.4) rather than + scattering `is_current=True` filters. +- Referenced by `Team`, `Event`, and `ClubMembership`. Use `on_delete=PROTECT` on those + FKs — deleting a season with data should be blocked. + +**Season-scoped `ClubMembership`** (evolution of the built model, §4): + +``` +ClubMembership(ClubScopedModel) # -> carries `club` + member FK Member (CASCADE, related_name="club_memberships") + season FK Season (PROTECT, related_name="memberships") + license CharField (blank) # federation license for that season + status CharField (TextChoices: pending | active | lapsed | cancelled) + fee_status CharField (TextChoices: unpaid | partial | paid | waived) + signed_up_at DateTimeField (null) # when the member registered for the season + activated_at DateTimeField (null) # when membership became active (usually on payment) + Meta: unique_together (club, member, season); ordering = ["-season__start_date", ...] +``` + +- One row per member **per season** — sign-up and fee payment are tracked independently + each season. `unique_together` moves from `(club, member)` → `(club, member, season)` + (a data migration must backfill existing rows with the current season). +- `fee_status` is the **source of truth for whether dues are paid**; it is driven by the + shop (§5.7) — a paid membership `Order` flips `fee_status → paid` and `status → active`. + Keep it denormalized here (fast to query "who hasn't paid") but only mutate it through a + service that reconciles against `Payment`s, never by hand. +- `status = active` (for the club's current season) is the fact that grants the member's + user the `MEMBER` `ClubRole` (§3.4). + +### 5.2 `teams` + +``` +Team(ClubScopedModel) # -> carries `club` + season FK Season (PROTECT, related_name="teams") + name CharField # "U12 A" + age_group CharField (choices, optional) + Meta: unique_together (season, name); ordering = ["season", "name"] + +TeamMembership(UUIDModel) # roster entry — through model, club/season implied by team + team FK Team (CASCADE, related_name="roster") + member FK Member (CASCADE, related_name="team_memberships") + position CharField (TextChoices, optional) + jersey_number PositiveSmallIntegerField (null=True) + Meta: unique_together (team, member); + UniqueConstraint(team, jersey_number) WHERE jersey_number IS NOT NULL + +StaffAssignment(UUIDModel) # coach / manager on a team, per season + team FK Team (CASCADE, related_name="staff") + member FK Member (CASCADE, related_name="staff_assignments") + role CharField (TextChoices: coach | assistant | manager) + Meta: unique_together (team, member, role) +``` + +A `Member` plays on one *or more* `Team`s per season, each with its own position + jersey +number — modeled by `TeamMembership`, exactly matching the domain note. + +- **Jersey numbers are unique within a team** (decision §7 #4): a partial + `UniqueConstraint(fields=["team", "jersey_number"], condition=~Q(jersey_number=None))`. + Nullable so a roster spot can exist before a number is assigned; `NULL`s are exempted so + several unnumbered entries don't collide. `team` already implies club + season, so no + extra tenancy field is needed on the constraint. +- `StaffAssignment` drives the coach/manager object-scope (§3.1–3.2) — it *is* the "is a + coach of this team" fact; no `ClubRole` mirrors it. + +### 5.3 `events` + +``` +Event(ClubScopedModel) # -> carries `club` + season FK Season (PROTECT, related_name="events") + team FK Team (SET_NULL, null=True, related_name="events") # null = club-wide + kind CharField (TextChoices: training | match | tournament | social | meeting) + title CharField + location CharField (blank) + starts_at DateTimeField + ends_at DateTimeField (null=True) + opponent CharField (blank) # for matches + Meta: ordering = ["starts_at"] + +Attendance(UUIDModel) # through model Event <-> Member + event FK Event (CASCADE, related_name="attendances") + member FK Member (CASCADE, related_name="attendances") + status CharField (TextChoices: present | absent | excused | maybe) + note CharField (blank) + Meta: unique_together (event, member) +``` + +`Event.season` is redundant with `team.season` when a team is set, but events can be +club-wide (`team=None`), so `season` stays a first-class FK. Keep it consistent in a +service/clean(). + +### 5.4 `news`, `pages`, `home` (public site / editorial) + +``` +news.Article(ClubScopedModel) # -> carries `club` + title, slug (SlugField), body (TextField) + excerpt (blank), cover_image (ImageField, null) + author FK members.Member (SET_NULL, null, related_name="articles") + category FK news.Category (SET_NULL, null) + is_published BooleanField; published_at DateTimeField (null) + Meta: unique_together (club, slug); ordering = ["-published_at"] + +news.Category(ClubScopedModel): name, slug # Meta: unique_together (club, slug) + +pages.Page(ClubScopedModel) # flat CMS pages: "About", "Contact", ... + title, slug, body (TextField) + is_published BooleanField; menu_order (int) + Meta: unique_together (club, slug) + # If nested navigation is needed, add: parent = FK self (SET_NULL, null) + +home.HomeConfig(ClubScopedModel) # one row PER CLUB: featured articles/teams, hero content + # (unique_together (club,) — one per tenant). May be config-only. +``` + +- **`Article.author` links to `members.Member`** (decision §7 #5) — attribution is to a + club person, not a raw login; `SET_NULL` so deleting a member doesn't erase their posts. +- `slug`s back clean public URLs and feed `search`; they are **unique per club** (§2.4), so + two clubs can both have `/news/season-kickoff`. Resolve within the request's club. +- `cover_image` / hero images use `ImageField` → **media storage must be configured** (§8). + If page/news trees grow, consider a tree library later — start flat. + +### 5.5 `search` + +Likely **no models** — a search view over `Article`, `Page`, `Team`, `Event`. If moving to +Postgres full-text or an external index, add config here, not domain tables. + +### 5.6 `formbuilder` — dynamic forms *(new app)* + +Admins/editors define forms with a **variable number of fields** at runtime; submissions +are stored so they can be **reported on** later. This uses the classic EAV (entity- +attribute-value) shape with **normalized `Answer` rows as the single source of truth** +(decision §7 #9) — one row per answered field, with a JSON `value` to stay flexible across +field types. No parallel JSON blob on the submission — reporting reads `Answer`s directly. + +``` +Form(ClubScopedModel) # -> carries `club` + title, slug, description (blank) + is_active BooleanField + login_required BooleanField # members-only vs public submission + opens_at / closes_at DateTimeField (null) # optional submission window + max_submissions_per_user PositiveInteger (null) # null = unlimited + Meta: unique_together (club, slug); ordering = ["title"] + +Field(UUIDModel) # a form's field definition (club implied by form) + form FK Form (CASCADE, related_name="fields") + key SlugField # stable machine name, unique per form (for reporting) + label CharField + field_type CharField (TextChoices: text | textarea | number | email | date | + choice | multichoice | checkbox | file) + required BooleanField + help_text CharField (blank) + order PositiveSmallIntegerField + is_active BooleanField (default=True) # soft-retire instead of deleting (see below) + options JSONField (default=list) # choices for choice/multichoice: [{value,label}] + Meta: unique_together (form, key); ordering = ["form", "order"] + +Submission(UUIDModel) # container only — no answer data on it + form FK Form (CASCADE, related_name="submissions") + member FK Member (SET_NULL, null) # set when submitter is logged in + submitted_at DateTimeField + Meta: ordering = ["-submitted_at"] + +Answer(UUIDModel) # CANONICAL store — one per answered field + submission FK Submission (CASCADE, related_name="answers") + field FK Field (PROTECT, related_name="answers") + value JSONField # scalar, list (multichoice), or file ref + Meta: unique_together (submission, field) +``` + +Design notes: +- **`Answer` is canonical; there is no denormalized JSON snapshot.** A submission's values + are always read/aggregated from its `Answer` rows. The submit service + (`formbuilder/services/submit.py`) validates the dynamic form and writes the `Submission` + + its `Answer`s in one transaction. (If a flat per-submission view ever becomes a + hotspot, add a *derived, rebuildable* cache later — but the model stays the source.) +- **`Field.key` is immutable once submissions exist** — reporting joins on it. Deleting a + field with answers is blocked (`PROTECT`); **retire via `is_active=False`** instead. +- **Reporting** = a service/view producing per-field aggregates (counts per choice, + numeric averages, response rate) plus a wide CSV/Excel export (one column per field, + one row per submission). No extra model needed; add a saved-report model later only if + users need to persist report definitions. +- Rendering a `Form` to a Django form (and validating a submission) is a service concern — + build the form class dynamically from `Field` rows; don't hand-write form classes. + +### 5.7 `shop` — cart, orders, payments & PDF invoices *(new app)* + +A cart-like shop where a member (or parent) "buys" products — chiefly a **season +membership** — with payment-status tracking and generated invoices. Fulfilment of a +membership product writes back to the season-scoped `ClubMembership` (§5.1). + +``` +Product(ClubScopedModel) # -> carries `club` + name, slug, description (blank) + kind CharField (TextChoices: membership | event_fee | merchandise | donation) + price DecimalField(max_digits=8, decimal_places=2) + season FK Season (PROTECT, null) # set for membership/event products + is_active BooleanField + Meta: unique_together (club, slug) + # membership products fulfil into a ClubMembership for the chosen season + beneficiary + +Cart(ClubScopedModel) # -> carries `club`; one open cart per (club, user) + user FK User (CASCADE, related_name="carts") + status CharField (TextChoices: open | checked_out | abandoned) + Meta: UniqueConstraint(club, user) WHERE status = open + +CartItem(UUIDModel) # club implied by cart + cart FK Cart (CASCADE, related_name="items") + product FK Product (PROTECT) + beneficiary FK Member (PROTECT, null) # who this membership is FOR (parent buys for child) + quantity PositiveSmallIntegerField (default=1) + unit_price DecimalField # snapshot of price at add-to-cart time + Meta: unique_together (cart, product, beneficiary) + +Order(ClubScopedModel) # -> carries `club`; immutable, created at checkout + number CharField # human ref, allocated PER CLUB, e.g. "ORD-2026-00042" + purchaser FK Member (PROTECT, related_name="orders") + status CharField (TextChoices: pending | paid | partially_paid | cancelled | refunded) + total DecimalField + created_at DateTimeField + Meta: unique_together (club, number); ordering = ["-created_at"] + +OrderLine(UUIDModel) # club implied by order + order FK Order (CASCADE, related_name="lines") + product FK Product (PROTECT) + beneficiary FK Member (PROTECT, null) + quantity PositiveSmallIntegerField + unit_price DecimalField # snapshot + line_total DecimalField + fulfilled_at DateTimeField (null) # when this line's ClubMembership was activated + +Payment(UUIDModel) # club implied by order; an order may have several (partial) + order FK Order (CASCADE, related_name="payments") + amount DecimalField + method CharField (TextChoices: bank_transfer | cash | card | online) + status CharField (TextChoices: pending | confirmed | failed | refunded) + reference CharField (blank) # bank/gateway reference + paid_at DateTimeField (null) + +Invoice(ClubScopedModel) # -> carries `club` + number CharField # sequential PER CLUB per year, e.g. "INV-2026-00042" + order OneToOneField Order (PROTECT, related_name="invoice") + issued_at DateTimeField + due_date DateField (null) + billing_snapshot JSONField # name/address frozen at issue time + pdf FileField (null) # rendered HTML->PDF, cached in PRIVATE storage (§8) + Meta: unique_together (club, number) +``` + +Flow & design notes: +- **Cart → checkout → order.** Checkout converts the open `Cart` into an immutable `Order` + + `OrderLine`s, snapshotting prices (products may reprice later). The cart is marked + `checked_out`. All in one transactional service (`shop/services/checkout.py`). +- **Payment status is derived, not typed by hand.** A service sums `confirmed` `Payment`s + and sets `Order.status` (`pending` → `partially_paid` → `paid`). Online payments arrive + via a gateway webhook that creates/confirms a `Payment`; manual methods + (bank transfer/cash) are confirmed by a `TREASURER` (§3.2). +- **Fulfilment writes back to membership.** When an order (or a membership line) reaches + `paid`, a service creates/activates the `ClubMembership(member=beneficiary, season=…)`, + flips its `fee_status → paid` / `status → active`, stamps `OrderLine.fulfilled_at`, and + triggers role reconciliation (§3.4). This is the seam that ties the shop to the domain. +- **Beneficiary vs. purchaser** is first-class: a parent (`purchaser`) buys memberships for + several children (`beneficiary`) in one order. Both are `Member`s of the same club. +- **Invoice = HTML → PDF.** Render a Django template to HTML, convert with **WeasyPrint** + (see §8 for the exact dependency + native-library setup). Generate on order confirmation, + store the file on `Invoice.pdf` in **private** storage, and serve it only through a + permission-checked view (never a public media URL — invoices are tenant-private, §8). + `Invoice.number` uses a gap-free counter **per club per year** — allocate it in a + transaction/service (e.g. a `select_for_update` sequence row), not from `count()`. +- **Money = `DecimalField`**, never float. Snapshot prices onto cart items / order lines / + invoices so historical records stay correct when `Product.price` changes. + +--- + +## 6. Entity-relationship overview + +``` +Club (TENANT ROOT — every model below except User carries `club`; §2.4) + └─< Season, Member, Family, Team, Event, ClubRole, Article, Page, Form, Product, Order, Invoice, … + +User 1───< Member (FK, unique per club) # User is GLOBAL — no club FK + │ └───< FamilyMembership >─── Family + │ └───< ClubRole (MEMBER | EDITOR | TREASURER | BOARD) + │ + ├───< ClubMembership ──> Season (unique: club, member, season) + │ + ├───< TeamMembership >─── Team ───> Season + ├───< StaffAssignment >─── Team (= "coach of this team", §3.2) + │ + ├───< Attendance >─── Event ───> Season + │ └───> Team (nullable) + │ + ├───< Submission >─── Form ───< Field (Submission ──< Answer >── Field) + │ + └── (purchaser) ──< Order ───< OrderLine >── Product ──> Season + │ └──> Member (beneficiary) + ├──< Payment + └─1:1─ Invoice (Cart ──< CartItem >── Product) + +Season ──< Team, Event, ClubMembership, (membership/event) Product # all within one club + +news.Article ──> news.Category, (author) members.Member +pages.Page (self-parent, optional) +``` + +Legend: `───<` one-to-many, `>───<` many-to-many via a through model. Everything under +`Club` is one tenant's data; joins never cross clubs (§2.4). + +--- + +## 7. Decisions (resolved) & remaining questions + +**Resolved** (this revision): + +1. ✅ **`members` app split** — Member/Family/FamilyMembership **move to a dedicated + `members` app** (§1, §4). Migration reshuffles app labels + tables. +2. ✅ **Season-scoped `ClubMembership`** — yes (§5.1). Data migration backfills existing + rows with the current season and re-scopes `unique_together`. +3. ✅ **Full multi-tenancy** — adopt **row-based multi-tenancy**, `Club` as tenant root + (§2.4). Requires: `ClubScopedModel` on every aggregate root, `Member.user` → + `ForeignKey` (+`unique(club, user)`), tenant middleware + `clubmanager/tenancy.py` + context, tenant-aware manager, per-club uniqueness, per-club roles (§3). **Supersedes + `CLAUDE.md`.** +4. ✅ **Jersey uniqueness** — unique **within a team** via a partial `UniqueConstraint` + (`NULL`s exempt) (§5.2). +5. ✅ **`Article.author`** — links to `members.Member` (§5.4). +6. ✅ **RBAC mechanism** — **service layer**, no `django-guardian`; per-club `ClubRole` + rows + a single access service (§3). Django's own perms only for platform admin. +7. ✅ **`formbuilder` storage** — **normalized `Answer` is canonical**; no denormalized JSON + snapshot (§5.6). + +Infrastructure/config for the above (media storage, dependencies + exact setup) is +specified in **§8**. + +**Still open:** + +- **Tenant resolution mechanism** — subdomain (recommended) vs. path-prefix `/c//`. + Affects DNS/TLS, `ALLOWED_HOSTS`, cookies, and local dev (§8). Pick before building + `ClubTenantMiddleware`. +- **Auto-scoping vs. explicit scoping** — should the tenant manager filter *automatically* + from context, or stay explicit (`.for_club()` / `.current()`)? Doc currently recommends + **explicit** (§2.4). +- **Cross-club users** — can one person be a `BOARD` member of several clubs, switching + context in one session? The model allows it; confirm the UX (club switcher) is in scope. +- **Payment gateway** — which provider (Mollie / Stripe / none-yet)? Only needed when online + payments go live (§8). + +--- + +## 8. Infrastructure & configuration notes + +Config required by the models above — settings + dependencies to add as each app lands. + +### 8.1 Media & file storage (needed for `news`, `home`, `shop`, form file fields) + +Two classes of files with **different exposure**: + +- **Public media** — `Article.cover_image`, home hero images. Served from the normal media + URL is fine. +- **Private, tenant-scoped files** — `Invoice.pdf` and `formbuilder` file uploads. These + **must not** be publicly reachable. Serve them only through a permission-checked Django + view (`X-Accel-Redirect`/`X-Sendfile` in prod), never a guessable public URL, and scope + access to the file's club (§2.4). + +Setup: +- **Dev:** `MEDIA_ROOT`/`MEDIA_URL` on local disk; private files under a non-served path. +- **Prod:** object storage (S3-compatible) via **`django-storages`** (add to deps) with + **separate public and private buckets/backends** (Django 5.1+ `STORAGES` setting). Keep + the private backend non-public and generate signed/short-lived URLs or stream via the view. +- Organise keys by club (e.g. `club//invoices/…`) so tenant data is easy to + isolate, audit, and delete. + +### 8.2 HTML → PDF invoices — WeasyPrint + +- Add the dependency: `uv add weasyprint`. +- **Native libraries required** (WeasyPrint wraps Pango/Cairo) — install at the OS/image + level, not via pip: + - macOS (dev): `brew install pango gdk-pixbuf libffi` (Cairo/GLib come along). + - Debian/Ubuntu (CI + prod image): `apt-get install libpango-1.0-0 libpangocairo-1.0-0 + libcairo2 libgdk-pixbuf-2.0-0 libffi-dev` (exact names per distro/WeasyPrint version). + - Document these in the Dockerfile/CI so PDF rendering isn't a "works on my machine" trap. +- Render a Django template → HTML string → `weasyprint.HTML(string=…).write_pdf()`; store + onto `Invoice.pdf` (private storage, §8.1). Generation is a service, ideally async/queued + if volume grows. + +### 8.3 Tenancy runtime config (needed once `ClubTenantMiddleware` lands) + +- **Hosts:** wildcard `ALLOWED_HOSTS` for the chosen base domain (e.g. `.clubmanager.app`) + if using subdomain resolution; add `DJANGO_ALLOWED_HOSTS` accordingly. +- **CSRF:** `CSRF_TRUSTED_ORIGINS` must cover the wildcard scheme+host set + (`https://*.clubmanager.app`). +- **Cookies:** to share login across club subdomains, set `SESSION_COOKIE_DOMAIN` / + `CSRF_COOKIE_DOMAIN` to the base domain; otherwise keep per-subdomain sessions + (decide with the "cross-club users" question in §7). +- **Local dev:** map a wildcard to localhost (e.g. `*.localhost` resolves on most systems, + or use `dnsmasq`) so subdomain resolution works without editing `/etc/hosts` per club. +- **Middleware order:** place `ClubTenantMiddleware` after `AuthenticationMiddleware` + (needs `request.user` to fall back to a user's default club when no subdomain is present). + +### 8.4 Optional dependencies + +- **Payment gateway** (only if online payments): provider SDK (e.g. `mollie-api-python` or + `stripe`) + webhook endpoint that creates/confirms `Payment`s (§5.7). +- **Excel export** for form reporting beyond CSV: `openpyxl`. + +--- + +*Conventions cross-reference:* `clubmanager/base.py` (`UUIDModel`, `ClubScopedModel`), +`clubmanager/tenancy.py` (*to add* — tenant context/middleware, §2.4), +`authentication/managers.py` (`UserManager`), `authentication/services/` (service-layer +pattern). + +--- + +> ### ⚠️ Banner: supersedes `CLAUDE.md` +> +> This architecture adopts **full multi-tenancy** (§2.4), which **directly contradicts** +> the current `CLAUDE.md` ("ClubManager is a **single-club** app … deliberately *not* +> multi-tenant — there is no `club_id` tenancy") and the project memory +> (`project_overview` — "Single-club (NOT multi-tenant)"). +> +> **Action required** before/alongside implementation: update `CLAUDE.md` and the memory +> to describe ClubManager as a **multi-tenant platform (row-based, `Club` = tenant root)**. +> Until that is done, where the two disagree **this document is authoritative**. diff --git a/ARCHITECTURE.pdf b/ARCHITECTURE.pdf new file mode 100644 index 0000000000000000000000000000000000000000..983912b2b9f62da7f6ecda6a9a3eb7a89e40c9cc GIT binary patch literal 368159 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

7JL~y=uo8>e$mjMv^ z(~m)w>W`)E2DFeTYgbpapHEWg$*UKav#-XH7sT5)#)Y^RUM?yzcC`+l`g_KVs6+E2 zXB+EgmXSYm;xh>dddcn|k~t2EQO6#X9Y*UeK8dorUZ`1Sybqe09cw#|^+)^j$Ysc? z3sK_IO;W5bu$Gx*@`Mf{L&xZNvzjIjmWSlx)sEa!H-vZ==7?8;v%QDP@pdL$4Kw2FP;wnwt6lW?)nQwnf%yAQM)PP?@;IX&j{n{}k z-n4--{)NPb5osSEmeC51ITO039)pnxpJT;AbU*Hr@GmxDJOHfMv>~s$;}Veb!0 zx;)XllBhH?%*I81y_%kzLt&QWZ$L(5H^ehVg)c7=TCy}@Nw%uy?7OD!Vw`f2$mc?| z+b<&q{LTpAo*RI-8a0-i#IVfx+%{QDetcSqiH-VDUls~%IaVLg z{t{Y5fSGUVUN!deYDl*-z4IpgTzeCaGZ>vT2R-Iq*I7Z5CazALPU)#}>P0jHpX^jv z9XH_tmTz)ySXVlo2kC&oRIl<;JQhZj9}#DTW~{mMMa{^zvSDAj5t3);;G3f+d7?+a zjY$#@7pg|$?_RuaaAhc|CDrr{wj8ecyJ%)ys}FFLa*N@^bK&TC5KTcXE_F$lntN~< zGuAA@MLA|7hLQPB@U~Pe`m~8%+VmJyI#VmGUuN%^L8#L$oD>Dw6u8QPy*zJs()Xpz81ix#xlyjTaz~kTaWaAI zm{UNJkLZ__=C}UMJn0|SPn+BkowrQu0d+!d_b3y!+!GDYorx-njdf7sp5O-MGI| zSRL~6B>mxZp%gX9GDe7T{lY6OX<(K`c{;d$6=ZGU25YaJ!#bjfYO9y0GZ1Bj=x<#% z8JKrZ3n0k`a`P!v32*Bh;-OrfY)qk(6r`H`aYgfFN@TZwVS0g~xV@zZ&tx|y;e6JY z!jEivaaM~A$5JZ9X*@C-BdoT*xVe)ZmZepkL$h>29AT%{ zG1Nm{sb9ST^5eUpii^BZjN85O6lA<8QgFJM5^WF>HWb;$w*a(mumP*k4W+%G!gb19>jyMyD&?tT8{3xd7Y7hs%tQ$K02Q>vJ{ zGWc$F=-=wqL#P&*qv~L|-#})z$elQ_`$-o7Z}EsL*^f=Ow}MvzJ{QlecPR7l=p`5e<#|jwK8i19fxCj!gCaOn=5H zojgEOf`ghmbqB+LW6rd4vo)})8$iro01)%nk8Q8>*;=OBldAz$Uq!BM+(*X6@4)`? zq$rG`{fy2Pl(sGnYpiu#+AK3a+nzg1S>S+XHwO?T)K z)Xd)}^4*}>Dx-O$(R|P1rKECYP_`0-fqh$#ce84!ty6O`VbH`GptGHLFowUqW-r}r z)KeF@bL6;#H35C;)uiX8c1){7j9n|QWO+=RrL*>zCS7%A;NNP;f#xwS?lv8vWuZ!S zR`8+z{e;_E@I>U*|n^UD_0Drnmr4l0na?<_l&|zJn zzi58%Q(GyT=<4RVy?j~Xn8((lmbj=KKUO}DSL%)yR46`*CdPC$m#B_7Wpudc;$ljl zE%D{J42rBy2P5?uV+8XUyr1IHe^pDZ1~|VKVc2NYmka56V6>JbbW*dkX6JI$?nuNA zy^3MrIMw2dzw-JP zO>O}okUIki?zR zN>~s-fDS}mP|by{uN9$-!36&u)`HFhPZ8dd@*7x58MnjF;!L`!c}@yyIyD$JrZgRO zba6?R^>aq1qwaE8W-~4jZKf)6c!zZZSf}o*@oa$Q*$G{@z$Is@VALqfwejqepq)1o zzuVqjW^?JtAyYLTU>@53Pqx{WR#5@i(&e#_oq9%J4>h)KHAZIy7o77b4}|D`g9xR# zU=7AW84A4|U2(WvKUxa_V`Jplt^aW7g|&$ET!&}_ zDInY>M85!^D9s#lJQ)`?;5eQLb616A1bKL=-Xs*vsxScZ`L_^+=e1qXr{W$6J9Q6H zFCMfab3{5JPq11%ZD8G4rVu8Z(5`6oJcwdzm&KB>HK|4AGx*bnXEb`=r;xNFe}OF5 z=={4VvsodvK;sF+OpLIkIX+xk?iT6`$cirhco*0ML)GU(qR!w?@nBmQ+QmBjOhFOE zAr1LEo!*csq$t&;`SzpGfgokB3;dFl`<>e5UfS2g%Cy_72g1pS_fJJm#(a1Hy;o%} zH$+f-A-q{E$l@W&M+z6XR0G~0&%Ts3oS(;f)1o|-=>y+{QYjxr5V0=J>J`F|kI~0* zD`Ql0)Fkvtyu#=&TigeIG2aGpeoOfX&~j-V^5R1$*3^$ z)W#X8NDvVl`|Noaq!nhH1?u!92|Qk@*^tUW$YJ(R-Qqh;ZO92N-Heow-Hl|mzFLuw z#i|=TeJ!Q!*tMpifasA9(!b*!EL`rdO2zRd*<2j?WcY0A>4>aIj{toEDhwIPWv#p| z(d5>Ow}pxw_2mtT_FXCL^6dD?x{Vdk7_Rwu?aRE`@^=awd9p5F=LWc z&t@D6`52lysB;F3>LaKE$DA@~04^^oCA0#NW+MaAY|UndyyYEp6<=dOn%#f|KYXjxMm>QzD&>Q!X7T zPplU^^W4~hVy9`ZJbRv?6fsUn8F4pMM!bF8{|UuDCp*tpw{wlrMb0Jm10pkvUg0V^ zSpPiyC5U|iON=1s<}XZz#~gz*MfU(Asbf3{mh4H)XH;{2I~C&=e(oT-56;m49Xv1G z_`;>xp54FYc$~SPstdDoCy5+3=*HU`YhE0Wk`mXmh)cV3NlI1L-d$s}?hycBUpmT| z)d1>}Ya!Jom%;@P1jX)T3^RyGFS5UzVk9{&g*kT&TIQ@onS`U zScg8e1Fq~*&^JVmYgYU*$H+_BM^T>j%8c#=;mD&k;LI9#qV$E@mQ(ci0M=2WET z;`;&XKU0fq{U^qZArQK@_EG2i?mc38GChbv(}4@ZhZ`AAmcvJFO{jfE1;&anDh}be zE|?%T^&IQ(_s^XwF#<*|ii)-P7QI^XLGH^ZO-FaaCiA}M*y+8ZyZmf=tCzZAy=+0K z4$Wvs#B)2oa&C9ec|&~o&9e60=zS-j9%*4V{i!I2`YBL-ewtF~?gvCK!K{N<>k8`= z*l*S!DG7DydV(k75{K9KLGWpfH)WWoal@0Wfo0{BR@POQ$_-js1|(xc zHS3gxRJq18@|q3WFNSz`OcBxhz;@{PQ6AByq5(SR&EIOBWQ61mSDcl|cQ>kG4II@j zlNK_fSV_a&w7|@Y?yq8^vdJpq*yy5=Ju4oW>f@z- z2GwhvfZzViYK(H3?p-WynWbjUem9XOG({G2Wb5G_zgOpGD-xIU`0&XGa_|m}T|t4XGG)dt$TOXlr^upFwu8y||%?759!|$(Zu! z?BhCckRTF!ZvIDu`$k!``;fAer2jY?1Xk*J7BMO9R9IKbBEd^3HWih+8%ww@umzH9 zGr|C$-!l`lna^oTRk_T!3c=}xUQSnbO9SXlt^BSK?k_-yKm1t*=i-Hsbh=xKy#GLl=^U zTQOFA=B&6(brjK=JX+61|D45_b6AmMUpE^^uJFn|SJrw+JZ3v!f%1AWw(0ly3oy3T z`fQB2=ys|RmsXRCuyW6B-z~wds?rh)I>Qq3?;0X6EryL57ZOkgzL6?m14L_e_l^Y-rU?RiGIqhHArN5M z^?NX_hP~J|(A-?>1aKhzvNzm>=-wk+-=BOO7*;uUeRP9Po}Bgs3n~UpXhql`<0%Q%-aErjuVf2D^Lc{u4Gj?1iF%(rudg=sTYseBw`bz!tM6w&e zQQ1hx5JAS?{%Bj-iPqT(rnNrN(Fx|U_}}=x?X^o~S5`d_>d;WF2yx%_mZKggr%i$p zr?)8EM7zC0dO{M5Y^-bzfQMK%2`(|zVzVjYgkx7`3vC17A!q=2h*na(nfitd|l=+Xd98EpqNQ(-&;)E^}IaU8oj-l zbqZ8ga`9nNvHIpT1o7Qz6+F1ylNKtcCzc)%WfxQeBZ&{V=Z$Bg%xG?#x%djQM?s1? za*ZzXlXqB8Tex%p%IBPW8PctA+$TffCMf&%G#7VNLpe^g3YH5)d;p`s)$?C8r7NER zEkzYBMgv)r1X8pH{3Q#nx%^sDzF**BEiSj%FJUeBwV=7-6_dx*h$WKb_$XPl77q%5 z$D#DIt-$F=Uf$NFbKsb9lcw7g(j3%&)m6nKFZI)oHHm7C2;sRo(M>Scd< zTTmAaN)Z0BF>Z74Ow}8b_I(Y;f1C&}rb$K3E&iyKRR;FnQC~u5mGAU@iCg ztv|bK;l3l)(xX~@`n&O-9=Rtms0Kk}R)buFX!;H5k zPTjr0y=&rb(IV^T()eaI%U;vO?mNh|rZpt$Jf@Cm^Y%|Q7My&&{x7d)@HNL{^-N(y z{ajT9{3a88eRiIFz!J(WS{-7{A92by;%vA!Vvrl_%pQHFho{2Wv1|QvN*&f@cswPJLGuDl#vy=}ROHIlBWL78JD| z#uYbm{>m+=U_7`pkAsOeH6c}gH;TynPc^1K#y~&Z5O2M&BNXqZPc6n74vd@SB4a_Q zR=m+OBAKgj9CEC`zf@&n-rbjgaV?pebuj5n73E-oq zzYnluGKGywGBb3i_&8gXBX9B4jCrjXft1__HD%l4AMgt5K)drOT~CBDE4{7{D6K9? zwEUV&=H9PaaJHmoU_C4uI`LejeG=ALoS7Vc?+$Hn`ZkIg=a|hc`tYLboip>mU-PF3 zv!2xt^zTnZyTkra*7dAAXUuYjk40edbzYn{pjbl z`nh~y(s+?g!4{zM`Mqs=vtb^7i$osvIKb-W7-7E_*jjQCh?chR&7mH-PmO{TBM$okFV9wEe)-JIUNd zZ$!Q*Ljv_{rQyrf(`r?&-dzdGlwMTLF6= z+0p0?o#c+UrDF-$P4PWh{YGQfSieipo9=rr#%z}Q~wPn_+~=kAKyx+Do$yAccCK}S>h z$W?Q7b!YWLMU3sXu9(+%zK(bO=j~hQu`hv_Vr>tUx?WNx!$GB9mF8n0K~08sZANFw zDnid0!|Fc=ftubnIt-d##niPlcg}dJUhcX* zyMO2^{`|YpBB4)z?Wor)pCH?;RXtAVOr7pF7&mrY+)Xiim_@s$xVAQPxN%tiveb6N z0%P!X!@I0;|8WrLnWL&kI=H=fg-+LNJTh+kcAXlkoER)F|FW6MWj~cB52@s?={}nD zaOMK)SV@>Hpx1O*u8_u=N=5csU!wQK|AWO8W9EA1gBQs#?8e30q4rk0c(qIS`0)Bt zsp7*W>EVuP7md?vIgSB0NuT-QeDbM1HzZGNBG*f7@EygLw2Smm>z~#Ok@1Bs@CgU~ zr>3&2hF^W+j;HTO73dpw{xp5x3)yS(l`XWg7WVsTeE9f%cQX0M2DTUU^H$)K=XL*> zaIks#&&Un^-}5J?pVeVeABdpdU($M<XO2Jbv zz8Y4$x4a&L(R1}U@@qFqJ-cI17ov4zrs+TI-v*1#7(9Kr=zZK>=P_aee17&{2)<2y zUI)+yQ!xtYb-&pLx_R?F@LX#%KbX7z6wF>~U-*s`ofymG)>6weU_4oJ)~<5E`s~Bp z%xZM{cpp3(MgKbnb^O^+<$q5aplkWy#ltOfI=$=xQ7o2#oROMr#$)qZpwn&~b!iIM zxFu?q8=Xez(7Ul-k$DlvbrMiQUcnWA%miP#SP9fi*_#Otc3(c4l!qg=wDlGaIWIOl zm80+E#BiQQpZ}GhOF)&4xGz+>pt@W8=B&}&gUnc5rWwk0`P*|f;ILh@UeC*Ze1}ai z%4u4Gz&?f~f*1J{El}_DaA5oaWA(Rg_!sE!?xc?=itCHSTfO5utqk|MVVdc0!S?GG5RX6a60wuUiNk$XmIro2kX1kX5T?66Pj30rA@;-Os1z%zJ3P&GidfU zohH|&e;T1J1UZ^}+x-Y2iyFDICZYSRq4gcS7qO-$5C)JN61L0-L*%k!Uu zkc@LyMb{7?2?bG)zjbNt-t8};J*)~ZeUGH2QI zqnV@B-n&&Hz`HUP>s}a74q;Wl>S3x8Mh8>Hj)A3CIh(F;{7}B~q5j?RiXSN0IGeL8 zLw`hHhwk||4S}CEV0jvH_4QIzz!jMM^dno){ZXm)K-)M@e%jrC`2y4z$6UW0swq^$ zFpAfoTfdtlU|w%Y_w4@B0oIy^{uO(Fc&=s7(*hnF&e?L^_1ee@?;=G@ioTyvWZUbe z+dWO1QoFKxsVmcoQu~HYtxt|p+lu}LL<7zkHh&njM`T@xZbv;&1`*^L0#)VRxBDx0 zGdB#i%R2X9z?6Szf1bn|C36&$en<)Wn`}Bppci=zGni;9 zC?AE;t{|F-Bf}TJ;zZrp68jMgE!aqoVz_?|awZlVvBoO(Yu%_HYJ} zc)iJ*P?h1>HSG!w#DI&M|C+a&R}f0=IOY|M!As#9knCHuCB0W8gf#BN*1wRy|C zR{NS>1>~8IJ9S|Dr6+k0n@!>Cq|lZ2dB&FN0~H##<&W3Dm%RhEh?h^BEiJkrC-oLa zqHGPGA=K6m&>qA(E2fr)5AKqd2V*ZP_V&P$HOpTX1`n zM~6(P5cz22#z;w4+^M2l+-IX8u5K0#q)=jC?^O4ybV!X*NEWxd=$V-mFNPPo;Iv!q z2)?*OM+iBJvxnH$-LJim?T=~W(=6B*f*+@cgMv4n_>NPLpMSHhy{=zKBnU5z4E3Bq|Bi^r|wo;5k+++oUQ>77$WHJB7!Q z!~1o=4z%G`eSOohQVO+{?0%P_*35Wu^^-;R1iGqUJ@`MoN`!oTD!Y6K`@VjUM*bQN zF24R~^6vgUi}X-Vid28R<{dtPeU3ZBUfS53Nd3X50r!)&08_p5jYI+crTq1zUNAFlzh7G z{ubEOBtA$bc#O2RS+b>mrVoMGGfRV%k8QgGR^LdF+{ zO7QU9T@2w#*8axPI>P}#QDpH%nSVRnm>U>XQZ%JFf&W>^WJF8>1ReEk`XC=v_tMr$ z88M#u-|S*gFkR%J(r_q#1u)u!5wpZY9|XHWN@kIQq-Qs={!3xo+FZuHOck54!nz#D z&XKf+jc%eUPp1nx39W%PK)5(jy4wCIe%tM4q3lvc}#iYBeP&ma! z@sKw%KUu&efR{GL-=G1@YOR>QO-Sp*63TPP>!^i}h@BXUINygb63YA5Su?KC1bXYS zCTv^Yr_pt3{*d3<2A~# zAmyIA@1wyceLn{goL{!7IK1efqASFkbiO*ql+mH*XQ(w?9qjJ9k0!u8)vVd;t>XS^ zlNfS9%2rp*?9zGABVY31>&e(-9(bzEAFYTr&)p$jsnz>;!vj*2>Pp=MKE3J*QpvCofp3|i$1yK3nRnw* zdax;$w41lr{nE=0{xiAp_GeDa{nrEWLuw$$y` zjMi$8I|E%coYg$-DrM@HjNV5Ui@f^_wOC%{|7@^htpw>M*kg%yTY38A@O0Uf8>Uif zR$LxOSd<$I*tA``SE*UMQ`znsoc!$C?0+u!v|SNYt)IT*FHhjV9dq9{fI4jl>V+8i z=KO7PzL;CEY`=o&GSvRKSGvT@w3AkD+FJQDV~8czX%kd?OVGWl>6Mv_ven89@+Iq4 zj!?8B-x2%;|2V?^*eZH3Dmlxuke@2-z~|C9u*`#e>vOGhAD_wEdyMS`B;}*q4q?lx zLi9ZWx4faNi~Q|?-`814ilH(mkFD`)4gCW!c}X}M$#vZr;NTi}c9%YTFL>&>Y={}9 zuU>wV4Ix{lFt`yQjK***R%W7qToK?!xZ>Czzt!MZycRL5V_1L+ELD$|TbxGN z-&RAI+IXkqSma0~LJc+1tmZY3jRBUZMUxX5^QZyFX;JM)&z;aF{Fkn9C8Acxun_9* z#yc^zwiU6+lN%PdZGmu=r>Mf!KN87+hlrK_C%#S`B!-)aojpJiZ4AN@D&KM)DUDtz z5jG41L=i6$28wj8%nhWCoN`%AGLaGvI~gU}jfk6w9xvBL_&ybe1qA+?F``;< zl~1c44bv#LaBU(>I<{~HMkYgER=OHZP+tB~wu=PYv!JDWp9e-}vKs1d*%pjY@fOTn zO{t=7u;e6~bI_jBB$B(3-nM;oOF10+dyVJOZ@jY@b7!%!UB??9MFrODAhG%Idp4xr zMA71TIg{ix@*M&lYqB=kB$-PMOJlb&go&WXVT4O=%1SL7bIqg)z zzF~i*i~bAXNgweSo|Y}>APL>MpmtFgf%})`pcD&o{AZek%~D` z2KC!_QBUhqJu{q0OI80e)~^VI1TIRyNPtF$FyyhX3sD6YgGLF8{lW+vht`v6ViJ*-BJJ{|o{Klo`Fkj{)|un~w(7-D^NOwnODHYl_- zr#OC=Z^Gge(xTpQUFdb|nNws2jTGbH>SP~Xt(4|*P4;GN)jwzpTG(->6MRZkH%FLk zwrV#_9n~paKN6Ar4#O3{R4e$eD=qDHi@9kG%vEVV#Qq-Q)Eq2r*CeE>=7PoFFJ)>FM0Z=M#LoV{>HWI({hr+Y z`MTxz{(AX&m}L+E;7-3kz68F`eZNmX1U?799&URT#Py8wzrK9;d)vz2N0JSxwdHi8 zF0T{m))j>=zQl8Ly;Tn03QPHt8b$<=uAwP{F5j=e>ZPgIQegn^Q!De=C!cC%lSD9+@WSOJZ09>hI_Fp z5!i1|YhI}`8PEbbxlKZBN%7p;x6Fz1uFEUo=q!C=v9>Us*2Ptgn*6JR|dwg3@-XtFz&$t`pAHygp>Ba3??*Z}%id_FvZ zohg>MTvT{o)uJNI_X?Q@LT##Q;92_08R^ovh`BnYcXjelt=ht5@XvkZQ<>UR-6{L= zz}+H!-D^66|)_MqCoG;E}+w0bHz%A9Eer0LXxoTjPziZ zW*PytRceDfaqkn@Zl(_AofDCc`X%e?^B0~ZH*W_cAuBt#TfnLettppoTF0?B-ikxnUWJ6gT$xY5>KorHUTTs)*Sq3Hwy%CtemhTXj`zkKm;FFFt@wiR zT?hpHYsR6xsy9<1SQf6r{>gnmr++A~jMvFrT{Su|7!br1Fm(a$pB=Z6ihnk3*> zO5I+4%DFUg)01BO(Z1!XwbOBbJlFuA)83Y!YAq_VbG0XS%HA$m;z6D_+42S(B&j1Zwe1W;tREI?5k&I9icD`qkiVl?>no+>zZ zp&z0Z1$aqmaxRwJ2N9Tcj0egU_r`FumE84}lAbA+B3oJ6&q5cy3RWXOo~a>UovGb> zV-3!NvX;83RN8M{Hx+70RRH+C%hWyP!r|~0cLuAv9wG;2#9;TDOGNIl@|=rRV{k0|Oz9)oSySMApcosN{@Q@@$SR{{9MB+wM8v** z1S%n}=&S@Nu?4Ah_}f%f9|Qbs$Y)$m4jIXuW3xuG0@!RU_9?>U_90JhBc<%X_s;Y zF;9%$+u7%XHwpwUt}CJ20(VT%+)C~x{8!&q2%d8^8}>EX3_~c^6&mM44BokGmtLbe z9@i14_>aWyh2jXN4bTx91QWywPefhj17PqOb3&_xIgApChG|g%)qWgSP{J`MD8dCN z_&nSuWW>>>RnlB@F7lc)5iGt-5q$_xl(Z3Sup7X>KmhEETEM=rN@E}MBZ4v-M&t^H zVf-~<+BAMz91#8hNi($7O6)EmF8*-nVA1GVu<5Z;Lp8Y5V!ZcI1tU(Fqq4uoK&+a! zwLU6AkH;qhlC44n99t2mFRm(4*GMfxpQ}RDAM@Y!EMvRFLJ%0JY?_X$Y?^~QARuKW ze51ic7lX>%AT#3f#APMw3dv=fyuI(?P;#}`8QP#-bnH;By7tJ=`dN(T;w=u`T)v6R z$cH#oBkF{b*{?h%$bKn-V^S0b#4P4e10xkpfFhMkK`8PsBO!~UVZ_NlWg@o1imNUM zjln&WGe)y9s^ z?r}>rZCi0oWf0Vbq)XM-l~?LilJwArOFByA^;g+&nWmFvv%<()P@$_A#Q%-5>XCB3 zA(zoM5ppVow(1h$g?xqiM!E*7s`H_Vw(6rc+??CJu(YL#!O^-n+|l{)#LoU8iGi`3 zmL8f@A`m;#_WCV|g_7h1b!Gs9=WM?Z$|_!)lvmV4tRJjD{GtHl2}ZJNNAMT07Bt2R z1Y}4Ray7J3@fhu?biHf>`i#b}*NJa0kcV!m8koeJv6U5jk zl;qj2#?19oEK+{^P3d=Frj0l;*dtc=k0ncU0a==F-c--mQ-n|~U6e0gMEl*-uHRoY!U1Q6st*?mz8=cBM- z6q1l7T_*|tqfKV(xW-hF>layl2^(0%9 z(PBPaD8Q2^qs+`lKUI6KQ={7mw+Mi5TuCre1NUzLwt{A^o6tobA5kmi?HQ{nwV9ZF zj`@w~1RKvDod7{nuC3MS}^*jaL+_63lu+bbA6`y?Hyh0 zseuU_P*K25)2qsgk3*8MxfM1v``9c}^0=$B)=DAPn4y8uNgAUM?Zj^%teK&+b*QLY#?V!`+} zyaLuY>|}^2);8p#FJXRT7D*onwPh7-c>ADsBG_hZ z`$0_i2(Idf>&KZi>NA^c>Tl?S0}Gij0LPoC0|WdV{5ef3{C!Lo(z^^k($5S&{qe43 z^>Hli?=hUXLCBne*B^JQP zc}f19CEfl$CCAd?76=WrhU54^JTj9^>n2)h}w9EaAdfyz)N{aFLd z@Rz7Gto_R0CG|ND+|eCUkE0ubIs+0tyla7VT8CAt_s2zkuB7QTPEQPr0Z9ujb~;YH z(Xwi_?iQiVNt1rU6^$Pr z7T&g`HD>*79jM0cJ5Q3mt+TtN1-c`*k^T$&%hOY18_CB!x{2O6$#eDE3rhZoC8 z{4xam2xr4?o@K`1SYn`%*fYlh9tFxegL$X9__IE$c|=~Ccr&}8(Nv)pP9Cp`Gy>Sc z5N}1Z?EQE>;zdRnPC5x$Y}N~C$nrR$<*a!jB4vPr25LN| z8p<)Dj}%UlQgQzHgE)pIEKcm^^$h5rn&cKI!F%SQZa9xxA1E4EgLdui7!`r@&twBD z6~d2#r>Jr3!y_&?xW6kJ3l-|ImD16>TXhWz@kc8EEHsR~)umUid=?W}LvQO~RvJaJ z+NB^CHx=VH$MiFDc`F@LJo$a@yCRf#^V`n9?`d2ThNw#w1IfsGYx@%tc$W2v*76~w zBetb7X0VaL_*V3Z($dB?lZl6)^B`b_V%apHT&nto>y+WqLGUf>>Rf5zA2^TMGHfkN zjLl1?TacU4sE^8EWiMm=I*6O?o9VUF9Pegaxa~7$QU*feZc45dhnzX8mqhJdO=gqO zx6{{I68=T7jL*w382PmCXL8MVJ>cO?nO9n`+RqeKKAdTUN}QkZOU`nmN$JFTtvSnO zAa4aKdokHiBK(0#lmSz!Cgb;jPMZlHA#7q!IAW$Q_R!HbV$IXME zIn%V_qLTE_RaP_q#;TM@<$I8~P40un=9ZGHUfnxxHM?WnAXkAXENF`YayI{($Av!} zT)zXMgmkl#MkZnMPQf9lwrsHc^$8+7KcC1*O9^otA`BkoT7o?4qOr!k{? zP~^jB$wLgYHkKO7`heVV3>R6LKAkF8Ys>MT0q$H5<=qyQ*+{-c$ap{7d!Tw@78F2j zNpCa7g28Als z4%MsBKipU5K?oOcBX#qr$|~I^nu87{tFK|(BYL<~99yn7>=_T;OkzrSz)b>gvP#_t zt2{cGf_(jdD9bzQ`b50kc-aT^090RY@n@e*`f2w)7U=0`Mvb@G!|6&rq2O5B?khsi}u7b)0jlA)U@(oVB;?RhOJwW$p{AIo`I@V>HEF^DhwC6 z)+&@n8RHTyLQL;_@F#?2%HA4Rs7?a5FIeKy-^!Gkq(=wPs}B{@0>`pN&uagWPq~Zo zN?g_c>G1VJSQMkiOF4Sv%oTrDAkqgRL06!_G$G~ZhijmZ2wZ}naB$0cwuATsI+zFb z2dEgrxZ33a8mLL%IHmBT7)m`t%9P`PPqXXe5B}VT69nsnZ63au@t2!LR>2kv^UQ`} z?$$i}@$D&yUBASrz|QH*Lo#t6*JM=U0IULqSrn)S^2PzP18DnWSW;rot17M!O!5h3 z?7o%}v6n`yvuNRol(!{b_F3)gdoGD1R$;1Ez_@LOLv9>ChN1OnmC#)bMdC*ICtRCD zZ2TkF7y%nzGMA#Ht!RM)GGU<+-s%0T!-m4Pt_WToYkY2==OGU^r9-(4BnMVW%%+{X zAP0kWfi%#*!Y5d;ymWLN)f~RILKK`h^M zm0z6>E&T>RH%sk^ycH42Vyblq_TuJ`3YEm=5Xk01aRSSboNzelssyVLl9|tdRxdCk zyb*}*O|v8pg7v;#+6qw`sh7V?qKWcvZ*gtq689ZV_lAe0tEYa1otqvU~Er=5J=!ptG=e6R5%dr`jD6 zpMYEk{YQS<$Ch-`$ckyPm(~j^bcn@f4Ih0)xy7=lwZv)vVV`qoiUX~(?U%%0+0ac2 zm9oghh>NaryAkRug8s^z@OZVxcb&&2heKd&y)+tFO>jli+y2 zx9Do$z<0sF&^Sgs*6z}{=xUuexvHBmuU|cP4avcQ&A7r1E=vZWw9aZ1dj;r{XG*0m zm6^x+0s=hXOk68vc>#bIq3CVTZXmPf481PAqwrL%{>P>&5v+xi)}9@ynJMOZ1q?6k z?U*C&vIBM741ZaVSlTb{yHNM%iJJTk`!p`A_jk|@FTNpI$Q*cTHF{`4W1%^9xzGsn+&l<>$MK2@{KB24MnQh+%;R=2f_HeF^RJYC4(DIJ_m zvngR$KU&Ai_BQFXB#kq1TVUv)484zor-R3K29}?cYgAv%MA*dc|nob_>^Q zc2O*dcX{da_>$)(-()Ejb+pSqkp|3{^T^i^|o~{x5 z;$Y#cR&SFw@o)0W&nH#u7fqte6~XoaBxbzFUQSo0=sy*-X_XXlG*7U?B#OR(8-D{uOFA<+CfrJ8vdPx&|ydH({CK5kW(Oulbx5HIV(g2rMHOISr-MhdM*NZr?+aUVw|5#PiYuE z%I{7ZjoIg`ZDSp9l}zN~;Hp*Z2U+}23zZp4Rl1Q;n2Hc3J{d%@`dENM38{Ba?1^TN z`m28tl5N|-2&ui1g8tjwDjnzCtP&CHuQW*W#pXCiasMpdu%ip`uTN#*XBSA{4dA=? zUGWpQY@NexF+%01&53|%U5j(3qhxO!{GRsLAR0ZGfaU1h28pRd^KUA;pvO>3p9O=Q ztZ)=)&S?+@lK;%hqrej=AnR9n45FvVljKD{g6G{iDcH`-l5$^AUEzGW-q;fP-VIeL zNSB+b?=D2?OeZ_t)om{HT*GTkjfM*f+b}<_=W(Ln00wU;c0_0_788)Kwse9BXm)}N zV0CztfKL%-(9k`(cbKN%)A4ZlL*=jLr|Tz z=iVV}cpd3ti1v8c>`50V#tmTdhB`;&=P#cB{mt{2aYKl_0nlM{IUB^9{ZK5Fy*ZXB zV9T0vai-hc7_YaC@_Z|l#K)M8#KLDyKF^L>|JU^bx@_s<0a>`YF>AZOjF}wi1#aUh z1`O>zzc*w{8@-VVuTmGgf9@|XQtqkjx-zZ zGnckV($K<)0|!DVHza^LRtQQcFM{D?`dXWGg;&U&eWO=g7jPUs*{M5n-l{K0?x=@2!R3v|EQ-5UX>N6_@MIjY+p-A zz8ASzDIf*yS6E7sqC<`eNYBDvJZ%^q76jL1v>ehtdohf}7_-^cdgVtk6>MUy#FG3F?X&gI=yV2U3cxKYdFv}>9jzuK(=_|5@xS%ro! zyG_*>1CVL=I}p^upfqT5yJUScyW~7EfT^I*XUBu;5%_M%U#M_Nx{w7<+A@5{^A9DkDKs=kqOrwP( zJ3@b65VVq&*U2@Yl1ACAt#z_DeBvnQ{!z$mtt`$t3+{{*a(M2Cx+mbCPt2}8Zl6Wm zxU1U4H-6xYk3h)GGpzgMxRKtS&t`16EEp3x#ABU&CT6jYXD12%o;_o_kbGgmvHyrN zGR)IN8Oh06;($;=uJhk`Yuz+)uqErFQM9vDhR3*?0~HR6%NdJO}LaEMWdEe;aX|*fbqAGxKe^A#7hW` z7A}YqaGU_0a}|!vpwCSGG4^C_23`WpHCO6;JegKw-RY4#q~vhxn9bxhSxOHhnrHP0 zQN-g_$vydjEdFw=_}=oAin_?~Bt@2ChIq9KQ6Yq>V$03JQHAq#%VJ1{vq-X{|2aL! zlDM1Go5>c0#*|ABiZ_Q6&WBUTPK1`v?oq-G^cFcb(MRo*t=QxJLP)z{XQP+r;a#GS zLI_5OYl|s_;AbnxlNdP8w@-@-&&FBxqx-+i;sNn(@- zU)_k)7@l4(glT)FFa4el9~szq1Ilt%*Nk&MfEXi}{^xeAIa^J)0}9&kdcWyfO3WsP ztZa5)i(CB~O9IA+=`mr(^g7u$Rb^ML$J~k|%2PkGp*UnPLEFX2kmJeuKl&W|mts(s=y_Se&jlC~W~ilOB->^ko*})RHUhm0JF)r>5F2J(3sU@R zpm(~DY>BPxYjlJJ;G3XsCkeujw@=3xZeICfJZ_V8DzYzIO-h+-A8SU*pL<)Yr`2tT zo3?PtfXZqI15VT@w5fh!5~=>un?V{)8q&3S&X0u%Lb^3+sFS|J9!A><%o(R?}2jR z3s*6;@A+CjLl&#u?p^Q4@klz}NiFO|KuTQ~?wN9S^e|4{YcDeag5s{fdE|(*^a@|sp8IHTr z`}qX0sc!KNfAoBARK4bPM{w8ID0UwCWOPw0dh<^{*M^Byz~A=Utxq#nz&q8}2Uo(6 z4##O85G{d^Pg|o?^3F11yb=w83xbK|iQoC}DZ#|%i(mPp^fpoGUf*I*i`{APpiu4% z;sFMhNkkt$_*AaKcF7-x9bvS=j?uKio|cut&NI+t9Zbby9jshuDe7gm5~r@S6jxxe z6!m1V>~|gK1>gjX57w~S8FOeEcQ&ey^v74CP47%R%^6oxL22mjwQ?@>RR3vL52}$l zCaE&?J?qel!6e_Z)IQb;e&LcTugFq<+^=%)m~iB-_CVjeVuOk@{!j`o6!k(JZ6in* zRZ18{QSC&3C>Q&I4XHy6@2?jvf}y?=r0LF%i*U5s3fl-M=4YVX=-cNVa!UqVDjo-N zheyj#%3OqjOGUB~3S2H<(spc{BwS1)|!b=aQq;v$mZ=+_WA zup_oBgwh=cQNEa}x&3zVNIWDICempTk`N8M+E(@@(DA^2iIN?c@L|&tqiX!-9Q(eH zOfb{!dU-6#z_Y@4r5szSLDNDL@GhWPZi#=C$^OjQb{irk&5N!Ydz*Ub5NGx=-!V@* zd#k>F7wMd@}qrDB1rJQ&&f`ot;n$r?#@lJJ;_)AkJ3V_p=JNX zOq9z$f&*`IlDb9~?&#)At!%*^ZkKa+lj@{;u)(g${}L#gxBGC9y_;y>Py9~Mp?`D{ zJh4WHnTnN_Ah7FE;+?)C95>Y;{+?}dqST1HhPpUDf;5cOO4-=dqeK&$E5m}?fd=}<@e_t{YJ?pB7 zAw3IZ6s)I&>SHH|BameC{V53Qjt_`mM(QiY2zYCLHa$}0|2<${3)M7eqlJlPkDm>St}=0Q22;sreUnwQTj zl$YACOS+6(;(dBtL?P3e1a$x!G&e0qUi9UWDAv7W_EJ%k(9mqPpf<*|{dALALNXvu z3OTmp%5<-pjA(Xa1}Zj~bhH0;`B!q~Dej{|h;Jn!d6ghx+;wbI)WL&BdTjCd!e`vA zKnjZ7?liJja2>CH{Sqr(Q7nA2azTJKmr2ZDo$LRC?vDi#?(!<>IJl@lz{tbnR z%If_n)<)?g|D_V|nRNi7T5_uzsq(&2O%tTBwk8x6xl50%$403k;#>Kn;@s@lVobSu z5J}e7?ov@}*N4?VPN$5=86dL*@3J_Cy1+5upDr4WfG)0SV0jIf= zR(ulVZ*k*k6Li&T!(PX^Y zhhs()g=Jk8`(&BFw9R#@iAmy&L7ZxWyEG$HxMsVy|BA~qv}|3R@dt!AUFkloXl^Vu zObe1VL-A<+r)a*5d#g)KbM6PX%#Y%yXPr(-{YkcFFRO0x<0DL zO=W~{pC3+r7nF_Q80afBn%B_#Q$Vr3Zz&g^np$qgA$hMvi3#bVIMV?j*4tasg-9e3 zkn!E%TZVrHRV8m61uy^0aou1K2cdapjTsT#>Dstc$1u4$aQib^@?wvDn&s*`Z3~=@ zK~$!G8ZHqKoiH8Z5MbePNCGDymTXRnA6S&e%trSDLE?U>1(Spch+1MU$rvQ#1NJ14Mkz5ky2+N%ZD^|8JzyItrZ8lrD*vC6lv3r}1~U9MbSb zdOG>1%)w$aNv?!g^+;-dZGN_@G&G}_VGU}?A~jc9e99%oAnEF2UBXw7Qix&RXRk0f zyS4iv#59MW7Oqj{L}a_nBErNlo-0Zs%9j~_I4M0^m=^xsE6pEiN|t9wk4!Jgg#6{7 zehNP|$UoDFh#a~g`WVV;m|(IV=F^P0WetL?Werl>=jB*GVLiJF<(c!zHp2eqG;QL* zEQ{#|h>|lJii24Eb_(3YG~nlr_M;lZ&+3_JB9dg^2-DN}{G*^RY?EI_cKp3JAf)Os z|CsCvYEL&0_k4~D{M}0anA5a`Gjf0hIV1af)96LD{*yjorMirFtA)uF+12dVbFg0w zSHs9bUg%S1so!#n?1qd;UoDZ9C{Pd`1}iz4dVJ@ za&aQFf%L2>tkAMtNhd}qsdbhv6^>g0p3{SY!Qsmw898S#dTx~UfW72K5lMfP=E#mX zXu}}9gV0r}3s#KxS&MZm4hjIPw0`%YYlQ1c6;DKC4g_?f)&@={!X`#`#(E7a%n2A68R;4RKS;?MlD7D5&#wrN zMp)~J7ia>A)9ds#a=w4Azj)}@sjH%y0@QT>gG$n4^wBX<%Vt|=omq@k`Omr+FiH&b zV2}iAg?WGe4^$E!`%}ZS)%W}8`G)Q0K~=DOqKELkK6!h9r=2nP`@N=x?>qHF&-TYB zG85jtaaittiSKpSamR3Kv7%?e_vZ@#=JR%iZ^!2)_U`-S#~b=6C!q30Z#ePg>DvL? z9XrqxghI={Z|6S%;^T)O=nin7pg9A)`nKlt(f1qLI9ldJ7d>;+>ebr-Lq_Minm;_0U`5y>s+g%6^v6;kf*uPCkxyv zkJICAdoBTq&-uMIEDNmCE^y13S}djUeG)WHy%XXu@pAjj*h zOBxb$TO8MH5Fo(_!_Nt%(%Gdzn9Ll$PDFpi?`a#kw(5OM<=wdc{RqqsObznID-;b< z)yi(w^{>rC`SIEd4yi!H%s1RB!oQ&$gqvMXo_7f*G6bN+hp+BY@w|O45f9{ls^7|( zLcMr~oxnF>|6B!w<8EVw3myP@FCYSXApUb;0`;mh1Oc4F2r5G$L0Iy-^EHh?qzYIO zoLA+r5v5*0peospZYUN@-PhBx#a zc|9qBhM_FT;pHW*JeQ9qY{J+F;;@jb`>4=N-u7Md>CM1s=nDG*CXnuph0&G4FqI2| ztp_;z#-shia~RH(JJ#m~EjEAT;Z!bqXt8`CD9**w=V#eGN0*EBC$A1oF7B@_z=Cb3 zzwJ%QXlwg;JQBr)oZsW0kwO(JbdEF@W-Q%F(9XO|4X z!y)yLiK&NS;pGHe;F9*m#3bkw)+%U(#O75WN95EOF>;fBaq*DyTyv8G^%7Vy>9^$% znCv?nPasnK-evfua!VtLaw{Oob*sv6a%(jXf9pHmvWFL5wV;Gl2{k4!uc){ko`#l6zk*AyC}DuQIcC!nw`6d zE}-2gfbegLNjwh@kwk?7C><;*^_JR(Uo0tPmmIxL;3|naFQ=b-&hR-w>%MEV1;w!2 zj`Eqla~<*(@C5LFdwp!l9bF%Mo$zgOgWr7PU8E4NC9Ep7{(Mecy-dGdX1z?ey@r0w z{5(7#%zWDL_+$e6phh^Jk)Jp?(we-lw`FBd`0tf-q)t>1Etrqy9j(J1&v7_R#2{Mu?)=DFp3gBy+lB z)ijdj$brtil5}JfV_6aQ(Rth9F9TS}^CDFjrX(D%EJnE@AuZ~BZ$dMcQWdVRj8;V# ziZzK1VF}WCcSq~fvSST9J);oAJMM4$37VCZm$C!~YeK+2U{f$g<=wv8EZ_}ibQ3Zi z+V`ryQekz^V%OhBMxgo5>=E>P9pZ{%eMyqIaiF`WIT(ZMrIdIg`L>(*ce*KP{YWi*E{UgcfP~3E;bnBgTvHnxtyIbZRL-2hY!15WybMIe%IH zQ73kSC{AkxEq(+LZ3&E?<%DH8#D3G-`Z4?W^?uD{o@@1V@r94XuZ-)VH*OFB56S zHK~ytp1b-j@0!bLOf)b2yuD}0Ly~r43Q&mX{Blyh?IL!=?lXyLNKr)auGDtnlri#FC2=O^Re}6>lvgjz@B>p+&M=oy2afOoth&~F=DSnQCrSr(0xwJf# z+mxZG-Vz%Jt8didj(CmFg4l$dT~W>f%Z2F>D@y9Ez5z>^DP9IeMlZ#K)d{#cMKutP zGX*Md#2fKO#d4FLLR-SXJ@>;%_~Lo>30!s&f&_(QBTN|khr6Q?uT(Y#-XZ~%q9vAy zEEpsar8ogXUG~COXlHqvpolX5$n7$VN%Tdry&TJH^r5M%Yo4iLvBD0FxwoqJ(K~@v zLv!7#lc;j-h%V2XXVNd@&$!Gd5mukDWq1zlEAFNh88iJ-On6aGjUSl!mXFqg`#pi< zn`>l|0(ha}V-jQQYaB*aR~XDpPZUO0UpUN6ay}f_?iP2C=g_!1SStiqLZb(i^rq|` zyP^W4`EW$Rhm*K?eJsZ2zYv&DE2stL#0V4ot_V-3y#$gcqtXP*Ji)E zVhcFKG|Qg>(w2W?$8|_n){I)CnY$wnE^<%)a7Ks0g%} z!{iG0cD^2d-cx%%zh8PjZr}L6XL>q6KWlzo_^yA>-t_On9%2|5@7@xV=NQigNl6PKnKU1AwM zjZ&HiC@_mQ*MLJojS(R!?>FPIQ;??(y7K(Jm8FjW84Gfji)99iwHSQnVPJm2x(1O_ zI%z*)u?$c^87Iim8Du8uny$zRHV zT3p2W!zWGjK?<{6K|vhkn7Crfkd#Y61W^L)yS))(^|R8@8B^a_N^jAH!2ZeY%M2go zq!&I)nDX{)E@y)1%QXPro9o&^-H8WJ+L&+JsDJD4J8Cg&5&P*n6SbtqA_ajn+>!*x zfT+6($H1*+nb91TzxGGv?xYrw0=XMrz4@v z?{+rOzYAgJBYMPso~`1qBv}!`===I*GY72)VtNR}xS6e3L$E8@SR(cZb4p-L$E-L! zZKApCE0_j3HcyIr9yGyIdlm#W0%>CLL*?H0rBY2Fc0Yg(Vd^!@Q>`BCK@^M zXgC&*Abo>@@4at&0WxYAfx-?ZZN~VfBx)%a&wF@MyP-2J%57aA&^!*!j-N8V68{LM ziq=^wFutNKxCJP`!yNUS9x`dJ5yhu0gd5~ZDeS9$DL{}hBp}4pE(?zcpwrV$Hz}Li;M^_fyA`?NiU@2A$H#*SG^)-}1Tu;ysq}#sdl*FD z(>)6CAaVu>(gYOlGZ@M91bbJE-r$TXRQwyU(02*9WO{GM!C$RQN`GkX4%;!_)fbCb zRX0r_8Ck&hiyjK`{m`Qf})-Q;JjuN)Dh`^QeAI6amu_*BtemSfr z%$r3>D5@b?NW*!B2d?}gfzFsgi!*r5QN@NXcG~H7iONm=`}(P}gG03zDcqfoPkbc| zvs8IM!rh{~oJU4<-1bxtmW?<};*J(DzR6J1_LT6PI>liseHjK(;_|1uE7e}3lO0{x z!YWgJm|e}xDyz~Pm^IbjO0$`D`e{2OMW&?6`IA_K$G>Z~Q1jKTw8yQ@fd$!uINuHW zEY|N_^|%*;Oanx~kI~?~3}q=EKa*A_jL~IHc7=S9IE`I`%rRXrk_j~gX4T(*PVJc5 zTQPm(zid+$-LOG4WKW<|4jMZkBk6rr06DU|FQK6f^+6xxR1mL*Hr6aduwPNfmDAl8xIXrKp?v2e5| zh}f3Va9#-1hjQwUJ8oM_)f6R?4P6L#BugK2wsLzGq#GyYeKcB~r0~bh0Chur+~EG* zL9H9ZV8ecpJ@1%_a%~)mhbCC8AChVLbV$)&+PRB3Jd7^PoAE7#3$q&t7iK5M_}DHY z3_zy>tE&fAa|5I6tAERRp|}3Df{)Reqt;0S@fZqL!$G}7nx5Tv0T=~f z_s;0%joJC#yZe;b7nswx+lR5V`veZKW_IJpXL0-T@4JZoU&FOsXBM}~8-rVUq(_-J z3>yStt0x%7G9eHR6=nVkAd5I(Qux#G1ba%9S!`yzd(cOvHu8+m>4o@NHZe0bA0fvV zf-39d%SnoD$z9$JIb<$|{wtYts2iIFN%FW7i=z<(%?0iwf2s^Gj&wjFUukL}31fO3 zB8)0s=UO5VV!|jc&qQwg*jxQ{2vNhUz5v81&VT0T2`7P=K-NTky^Ul!eTf!ReUSjU zD1fg50_{P9YRJav=SU;T`@=Fe3gH4n<`@QVO)X%OE>$L2YeX+WufeX>3T9E=O5~E%0jhSJlQ)lYM)A<;j4(tf@M{mc5iCzk5_k_15aJH~CG1=3Co~&GA$k}z zX0!{nH;I8ih^Qy9E0`3Iq|jbxk<5=mELQm|(59@cM@jrsnr(|cszN%LwT%z zGs%mk&wVI3<2yOA&ecwYjR`P~cs^YYgdbDSz>Z&M6oET#8=CB^*&nM`?F}sd!y@xO zx18mh)_mNDzV=F}$v5e|2Axe_`OtRg^SxI|VE%0IrEkbPwG29GX8xtGe9gF4RdP8^ zLp1Gt56-6!0pRxH@u>xw{@m~I`DA@J*xRd80QGI6Ol)_Yy9GKfDQ@-=Pszz78A}ym z))Bk?GG4jdTo#x>O}4$;Jm1vfen}z0dnV_B1a{cE$!YU5X+B&u?R*QC@NA&Gh`ewE ziKN^csgy|vJHjR%JBp)JcNt*5UCD$>F$QOP?65Ynv2t09*(iBzuc7au&(1f&yHwCY zo74B+qy30*g?X_dH>~I*9Z)|=#U8F<*m#4=FT%ypbUE-)H^ADtFbI|rUQp3xjk>Fs zHLIjt;MOMZpiv`3oZ_Xu*x*il#?zf=zI{e7(N34qc}{q%cV*ye(a9X5P}(gR0uaLG z@l;HFw&E!aPW=0_3GT~6*1~C&FeG~AP4r*xSv~OlN+(i9U(cJ$1JEG3Bz&M4%$|V3h>&U4R<5ux(gD z!Z40j5=U}_=&AzivV8C5N8!n_-;D*)YT=1D(GNxKVNBiMVBP&J|51Bwg@7adQhrT% z1riF&VXyKFyUa_mxc)`*VIQLrX}&^={`+eXdue3HW{=bvuPy{=nd+90HZ5yPPSH^C zoUVWP<@4|3MPvH1Lz1THUn&{4)lz2?T3oAwv-<16Y|gzL^W0UsPi8n{QaTj{#Ycg6248!TqbV2#qP`IHeIt$qy+Bh4JwC(XnDS^FYw;a^#k%3+!;r zbF!+ogP=;uex#oaKi_)nd$JQ)!3oS{)=2?RM?Cw+G#gVUC%hgeV z5;b_*e0VAOgX7th_pskAe;>HrP*kxy-dokN-QHyyzJ7`2#@fuZb06d>sq>0{-c`rk zJYok_zM@v<+^yAg#q%h{L=|_7=4RszdF(fdCcoY#ee_u!xjbvStCEWGOlbDturSU@ zp5(O_Mw|owxF5ATa(LEk%j#04h3{0QHLDRRA$;gr0BCPDjRX3uB&biD8o3&q8EG5G z8GRaepp_aQ1(6>i1UVlyBS%k7f^bixgKP%WqAhC=ERw=TURuXrx2;rspEb3cKTn%P z{T~8X_61KpYnrG^h`o!NIp-BDYYSKA4X;j{iYmAmM z^7So7DEt`(la%{FOg#2r`soG*a1n_LSZN#zw;_$>`GWlmMtX8qsJvSm0J!D8^<4bP zhMr~g<2^q%&#L*QFwWYUNwHsR z##H4{Z_K3zKGGO3?6LDvt{zDPtd;52sr>xDJY#p+EVVht)bE0M7gxw(mNO1c=7_}? z-e{3-JQHV9B#tZ8jA%k0yi74&>#O~oOLhdE+bG_HXd-rXH^qDGl69bA(W@|n*wuNz zpw3KC`6ap*e~Wk9_WasDWxTj<9|O)PFb2u~9T50TX~i#<99%GS9f8NgbSnXoJ7@yNixJROI_$*#CEuS&ceOq1L+tdO-f2Iao6VR**NH&VWLM zDiPrP6!tRjSQMuYQdFc4N79yoaTvWyw40Om=h)_G9Y{6`dpw{UlCcmr;BQrq0F-kW zK~)6QC<_5BR^B0yi~=KV@^6p8Yf3YIvE;ylsT;59h1l~aC!{m;?SG0^F6sOeooirs z;ai1?M3k>CQ`gC5E_U4YTbWPQuJ1xm)v+!$gWw9d02bJvKs<}VN~jh>WE z?p*z}*l`(I8rtr_?LU|L0Clq`0B6?)#C1z!Z_R0{g##5`71lPZwRoLe-~w+faD_J-wcbUlHF4<= zQ?UlDq0#`Z2x|&|s5gao8lJ&3OHu2Y=V|%EFSc|Y&f4SJR>c{$wRWA(A~PkK@Ekup zF+z4Vkd;d9+U1)ZpVTD_l)@4t&$fMo-0D@gc2Pcg2=E$vaswo9-NPhxylB8>fRO(_ zID*y8SKIG`dA(K3?;_{MWw24sHC8cwD_iM*g-6_E{;bD{s)0+^#6>gnE@z{#A>Ri@ z%ods7*K%BX{f@W#FymQVUS?oy6{75oQy}0vXnAqFKeaal6lESkej!*;@VBL@y+USh zMi9y{Or9}yaA2bA!R}#e!cw$j-L0jA10oEwWUoCg0~|q{a}x|qN^hYcQ-MgYJw1*@ z+-Cb4*rwcOk5KI`f%b=u*VUDR0gURnDmo; z;Z+0N$rvKi=H+jLlc^Y*lX1>$4aHj)%}(mpqb+k>aq$bL1kNq*fjGHvFb@6^zl>pl zR7UBj6bRtU2Lq!><1igE#}P*TsSZ}{MaNR=(?i%)n({?hfM(e+pfQRwPlcCIf5-|*_{48^tMqr3rHn>Qi+g0J0^cKKei z0D`vO{y2Mr1%2;WH2DF^1paKxt_#oWJq{SoL~_YQr0V<_7Y#10bGl?ok>4&W%Nb1En!d+5ov zi{|U<+0>k`r5^N@qGn7K)m+vAs%#k+Ii-TDw6AN|)qk}~YQlDB=Wj%XW95k}N=8kF zI?|le0pdRxE`G86ZQEEMV7Rv~jvI6*Vgm32-SV`T1vz1DL}okVdzM_tuS)J>BpP`J zw9dj(8@nQ091aN*dMB7^g*X*?YL6Zh_9fzP8`N6Yg%~GIQqac^vF?DIDaFNc1PhJ+ zxyf%8ccf6OG)m&fB$BMi3t;Su1KUGC&x?UTRmeo^Qn(3XD$DH`m^;v;GmZHOP%=%X znD|`JufsfDW*cr$XiG2R*>wEW^W8K5o-8pV*_$=MH?OiV&NW+gV?CYqf zv9@F?k4A6w6{+!YrsXtb+Mr#&24-LV^T zlWVthw=hw~$s!ai=}03)8m-)O|7%_AiQ0%@pu(pZ&G?n&-=9Ij#*pM%goMS~%Z9k> z5R)Vi8=+5$KyMk4BnHF?XF>#|W^~usP-OEgQLt74BA08XNr&glF zm0Wabg-ms6rqZaS@9DuM9d7E?9bRVe`?IsQq_-DhdA7u(`^Qw%QO5Q`hC>{Vk8ZJ? zpLNFx83bW@KU86eO@zEoWKQSO(}E*L@e+uD*c7-<7fdvq z*ad=;;u%V!Dg@S@2n}J4EG40hQ`*wiP*DNwP$UV}0wGw^6KpRLu^gB#V_O^tllT0&~tUrrCwyUuP_X~o>E_x;|qmWx~N{r1fwfRRJ z-jX+64YWdg{U03u6~^R##jE{m#c9GZ+|aE^D$583Sb}~+w022SUP22>p%lHMR+KBG z==+wdYL<)Ol#IIN4J28ciaiuaBFP_VpbAr~6_h+n)s`q3-Ti7q1y|HaYfkK&q=uFP z9^eN}h|`*^F4R5}-ztbjF}TP)$A&Q?$)zc`NVli>5u&oggJi;`Y)rK!qq~D!_16*% z9&3dt;^(eOiWo&0T0`Ox%sX8&97>XAT?))Ta)>e4gfZnSy zi?6m{@w}He-pR1M#3n2?2?Im0(%y6B(mELKf|WR9Io;Q-a!2qE4qDFOBVV`8n)cHY zErwkVu6FC3dTzgo-_F*=Y$itD4w%4D#8SubJ;qIvm3oazkzB=nJJC~>bkKh&EYjTy| zYaS@;Vk-?FzTO4nJ(Up(oZ4|vRKUIso)y<#B;*YwAM8SuRpJE%%Nfwb{Rx7UW@61T zmRU#mOFBu02wA2Dcn?BKU%R@=8%+H|f@Zpnt5||ZTM&zX`*s*mYmjoZLuHoa-_`;J z9;Ln5ro#B+?{0^hC-R?$WVHHJ8wKz^&x!qQ06B`RxC;by5{0rVBA=7+TaL*tk-X8| zF}sp!N=&YUIky`kp@=46w5nTx)j5e^AOcRDOCQc7U-^gTfWe9orVQa~=pS_P%tvczi-%+_B>`pbhr^QW` zkHy~HOrsk3G1*Leoz>+g0>)iUH!ECijf;t8<8wz{Vgj{B*_X_#n6Wrl#w{+~;iZ$+ zW7%@&ijb^!p}%jWW+@)V&&nWKZ!#5QWozA;P}zSj+cyo0?Exnr4QN;j2v)iw&3v(E zSvWiMI6?aC@@CIxtm~w_Jy}Ru+AE@8UmSAD=3Jxz=KB;l$TG@(4#t4FeQgjJNqx3l zmsTWY^7lk4kVG0^L7o7MbN(}Fc?m`cF|eo`1*PfaJcenvLk#e33XfVG7ES6tmYHmQCbL=3irFY}IwBW3PCpc?<%%_&{LbE^}a zI0R4Shx~qA*1kOGz1&UWEIei~tyn(ogZrB)rkAzd9x2NOiL56zx{<%QajpIw827u8 z;0u>VX3!oTU7ti$-59yBT)MnCTCBM0;*q2+i0JQd;Adh^UJP|VEMHFZ>#9HNER#82 zWc-GRY3PgZGeV{WIZ>t%-#C}Eu)U}F;y#F8=PL44 zO{1`X$@or@v^67}hdZ|yFOdqEZR?1M)u!jF{SmxK7y}Q2q?%R|7piSz&0{7>YdsLL z4VW%$A5R0yoEBim7?xMy{EPl+%h>+eL^s?s+BvA8%Jpb z?{g96g4yw;lP;VILS>}aV^$y|m{RxCb0x03EZ{$sSwIezzt4wCe7ksxot z6!w7sEJ(6@buEdbR-(m24ZZI5KE;v!bfYS@rGI{pNu`0l_8`~{3}O$3g%Arj>KB9nl8ok)D~97|Yk1cIod%#R*;p5R#$Pad9> zm}0(w%{+?{j9Ce@jPrB5@|OWLJa-|73JXmghd-;`)A#(_QO7F>a^Hma>IgawclQX0 z?+t?htq)fSNN&zd;ln@qhKI|UlDW3v2QfL%0v@>$3_;`GT3+TG^Z`xPS5%X z=KqJWZ;Z|)=+>P$nb@{%+qP}n6Wg}=CYjiFCY;!|ZDVrYbMN=#-gW+*A62Wm_gYoG zx~sdZ_I~#BSR?|y=%or7Go4%C#hR0$+4$sshcgZMPHO4ciDIb#w8oFmq z>f4_#7q%7<)^^ul)8{|+Ao}0v&DZ6SN%CHA+-66HYhoXAh^L#}2I8J%zCIr4#RUGA z7h-~@(W{`C_(_VxvtCVv99QI9#y*eM(N@(7CoGH`u0 zTvuHVkfk5kf7d*>;z~`K(+0W9+K#0a_a?1tXD)?d9~fdvQzV@ij}@2rw?WrVf!P`n z^^4)NFzG69NJ9@x1bi+r;(LEZw-m2L`w!q}ALDBu(<>Mrc-TF#G}SqurFxMbAKB9N zjqvO3oYGqw-gwm0Y7T#;3;ufORle93$~noX@X5Q}p+L^_5`B1rjo48XoaANe8#$Wf zAXo_-=*->b@cY<(hyu)a2?Z=nbOY)Awim3oFW+>%|K7x(J8qHt<_LBJ0Pmndtd2n( z_9;sdeh)R+o;sNU@RCmu%O{NzZx=-)PZMYI=h@KDReqU(P69~1KIRQO8ZF4RoR`X+ zN(+M(d*@k#TKBw%Cfv!L$SnU^%%p#*Y zxGVcwunsv(mv*F`?(IjoZ~BtoBxF+#G@IFW$_JSuY?FtYaV=EKoWA0+3U3w)Z_>L__(!0Y~fYl)S~V>+P-EHXmmSw>+{dwTdJDCp<6s z0p14&b~L_wi17Sdu~%CdUYyUpFAWnovOlKuXSHRXgr#@&(*l9?ty(O<`2_t48*$Zt ztfSM3DCE@nf6q0kf)y?PpLe;j{)>sPf-CUj<>)=tc`0hcur|ybp(WpJ%`mW(Ee-!h zaKP2a05iy8t%C}ddf5I&*a}fb)Gw4}_Abyd2-fAO&C;FuLHoUc5akXRQ~n15mU;F_ zrXZe-toO@qb3ZEil)2#0$*Atc>ZpK1W;LQDdm~>}5`Ptj$S7k+Aob(tp^?;?a&2fx zLcVs50q6Rhzcgm0tBM)qtJ0!%kGDr@7~Jx)UiszUUxQRoykbJQE`<5n$Ag&OLt^K# z*YTg1=(l_N#d5?n3Ku-+NIbu$JKZa z9=Dl-A$YiTAA^}SB_W(DZ{;LSPFkSA<>@iJRcj@Qurlaq^~Q;j?bHYfDMM*B{mS8v zOefSGWl6P4LOR?MJ-4eQ>a`oI&mio;-Pm7v^DxWi(fsicj6^bU)TmV>4>)Z6Gd!{(jE}Ggd;iG-r`JkEU z3}5e3vxLa?&N#1z4WJnVENNrbNaPXJPIs2e^lRt2GY1AXc|hMr7wTDnWAOw6h@|mq z;+i%e&W<4Wix`8ktYe5As~nN!@X4X;i@}N!G65>kNlIR!+2s~7*X0R+NEUVXN>5)f z^Cl3EuFp8e0A2;?nR<#F zH|c!ovb^9W)iG=xY59S;{PHQ@NcV|%{}v<7n2iy7TJd@tWQ6FSrH!0jy@UY@gx`@S zd>pz?V&i<6#kFUDwXOKgfa#fKXWLegRX`~SoDUUGJZ)QAJbsF|W}5snQoRTX8xTFmue`f0Z#^GU*AMAg+Xaszz12`YuEvjB~Ifk#rq9W_-xJ?#Ngv&S3)$ zMc%$Wh;+1KV29Nz_mQKPE`oIifM-K6_wS*k+POjQ$T2k@f%tL_STG|vy-=G|Zxkhg z3|M#}!V^PFAV8VMrABYU6f46FSimcA($s21IijGGAH0T-XSfj8-6^aT7XU*8OTtBc z*QLK!aWqd%&b#gsa7!d=(1)6#I&}T!N^C0@#JdO%TRlo#R_-gt;|Zqq03y_Qi?;{R zHn;M3A*+S2vX|ao8h9nIMq(1a?1k_K_|PT)2%#&SL6v+V(E4%W_%I&Wgs`iRq%4?n zMX`~>fHR|LyQKe_KETTKzeD`9Ff(y7{eOu6 z9^E?fzYY}7y#d2O$iEf!0Rad!Xy?Ua7Ny{rKwtrsxpb>CfBhlzZh>PWT(aR|sY}gB zt;9?0v@pQM*>WW8#fS-OCKK}erh&h8bccV>68G-zX$8p80$s#sONaQyS*BFr9{Vb# zo2U5wZ-(%DpS305z*`QCE)Az<2#k$ z`~BN@=$%T}F@c98`1@hBkE#5mKJcA1L5r|&Ty-qLk+6Z^>l99ErgQi<@~fi-M+8+1 zjueG%B>(5&ep7}Lp`oBjKv^&P8;^jiV#qcfpcDfFBieUMw=S*^;W78@LO1}QAG$eq zoxqvTb;fG~RMs9Y_z6X#7Br?IZFD7}d2Y<*26RiuI-&UvPRC@GRC*$zj?h%V?*nTu zOX7m~r%|Zoh3+-H4)z_k%Z`f9JMNvf2(^9xn1t_hw>^(e{s7U>aI|rjm85*H7(Tq&#h{BhlrS!?Qv2&++FS#AATtaKy)s*7-YU6HhGMVKij zGv(tm=fSKc>zT(xsBxi~hkW0kgX{`#h`X$O#z`3VvT5+L%{VI8_rhgoPo|!@O`Sp1PeEO@cTHkkfZQmCF{C>~d38^n$Qrpo49Olb+~#CY+D$_;5kcC zBXX3IlVurXDL`BAd>j3Wmh|t`f14aCkR|&frc3Hl8EOuU5pv#<#@vT*?y)V6EOdFj z`or9AoM4%G6eM6y|sae|H6G?fNkDAnN~O{JS!gByk) ztZm8@^D+S=skU~u0ax7bRDiv)^`;iGWpT^I^`^)9cb=8SeCx+dR3q{hOS%ec!PuU~ z{6nqM)?MzF6OC31LsotjeieIvv6}6XG0$JRrEL}!h$0uyn@%fyDDn7{@J_f=7 zuU3o+o&}Ma2is9;;--E_p6hYebS@3L3`;8G`LQ!)tD&!kS$P9^kN2p!sFAT3y zZ|!!wc+0&J?&~l|`*7F@MLjy84p-|~tg&ZOjdX-KBxR49$Z&c-EH}(?xdnVv<)532 zBaE>HWIr=Pz_wW)&-Io1>N{(k{YtWDfF63N(6p?4g5inj8o`c$$PYy(#6J#xp^3vM z(4etJo{-6&$gC@V>fSR^t8y#j2!{ieDf==_};(+alYElq#d9M6HxX&NOi+<>%a~w)s3;1zL z>60<56r0ojC0hVa9?>#*3oQ|EA80*WBdnWA0 zxo_g2-*F5RRWe3{vK<47#V^AWW$Na!tCAAw*OOWhk<%F(*}%@PYuUi74}>pi!VE*b zC+DrjqxS=sV?o2Oy$N+yP6WHyGww1JC_Vk=Yw{3g%dyuSwS7Q4ej{CMLzny;`;XtXWy<`r+IGt-4+aap1Yj&~m%ZedAk z;Z17w;fwhig(uaQm*L&4Yr7$LexB+DCsi|yX9bciS;4|c>?fS(8eYSEP9Ww#U2 zX$=x_Y7P3oL~yOp-RAtHi04R2n(h>1O9GaM0mBI6Q6|XX+t3p(m{GWnRjVG;k7 zjQnoK2gDEF+!KhapA`(JZkloG(q}B+mjH2uoK!?J%)%;>vO}#%8V9Zd$q| zieegI;o`jE{!=ipaGtRKQ|Or3&Y79mD#61gwjjb^5&NNmrh5a7khI z6+JporGy~j(Q6Vlk#O;6iWW5%$9)m=jpE_3G897d*iLvQy_IyHAcSn(DMkdvq*E>SY>T6>X!S*Fue*?sHL2YTF0< zT7jY8RgGQ!#-Rd3FIDo-Q;q#5WPC~Th#`0DP;ndrHZOkd0tD*+BrY~7rskr1+;ulz z?g|S!KUXY7a4{(Buc4)S)xw;St1Yp7Pjl?#7#7H=#94k2f|D55fowxdBqi1FW)vO@ zSQLT}T9k`ZV~8=32B3tF?+AR#gF5^ksO!k_m!lelXD})R4KY=tc zYyf}z4(=p!?^dj###m;VLp*Rxa5=)r02naqYD7xiHI9GOh=zjaiN75Q75pKwLVnRs zOU>rF<|a&VFO1&s#V)uxc!rkCaV_h4AoVerE5Kw({LYlQI(aWVEPnX4zaL;QOeoV8 zEqGWZ80-0sRF~Zu8?6^1TU$)>_PeU=&xUcq#ObuTD(8n!(WoVMGcrte{+G}eQEqQh zxUSpaosW#*aj}lzeRS-S|KwYtmC17$i<n%xA@c=DxnO z&LLg5yaPXg$GvhV!NXd6>w_1-zzwbg(p|2z%sn+k#$&zxn&Z5|g|blrOlT+{qkkPZas8BiedDkVf(Ouf zs^0Gk;GC>s51)$L?*CJ89Md|7ORmlM^OwV;#MU|~k$ueH!4*Wmq~HSnN(~aIKFI~$ zb%fGOsUh?(GOCI{-~dvrib=SpG5#4NMup5m6bz3=q}WCF{X`=)%WK$97#d`2O1QzY z3Z5|xIr`1Y zCZUn%HwKo%a%F2{YgN2#qncBLP$lZ7$g1R7vX3UuUk|KDZ0vB^8K1nF8BXTnwHfd? zj;jW>E)1GPH=h1a!$;tCnC2OI0o&!Z5k4IZGMWMHBKvaQz6XdIXx+KH>i9Aha+>`Z-YnW2Dgp(txIm(UbJ7`uAgADzV9z0e z*-(Xt!(MbY)@Pd>X0?sJ8!4zGbF^K5jiya~;@yt^>ieY>U(x93(6<7UrS`)m@-{DuE4uqFFkM~ep^JkA&FZV zUyc3T{n%-_S{rd2T>2P!(u=V|C7xm!ZWJDGHU9^olsc`Y2}QiwaFIrz`Os3N6x@$4J} ziCw+}hR_ZV^`eRxDNn3m?E+lljguR!Dg9*NmRxwRLmxp{6UCC*cxO9Y#-*zpKsC77%@YSNR#WiOvo-i+8)Y4_sX&SS0D3hd zD|OxElFcJ=T%bW%cF|(2gQuQu0QfRFB{8x_Z>MMoGX}|3;ceBjB(ptkG^GbmROdjRr)VW=L$|-Q{ zEqeO?m(%6%?bF(en{xy>KVw$=%bQXN!+H54r>kgjzieb5l#ELi{{C)skbbhqi)U{* z)9-xK#dSc8`%>pRBv|p{feO79Os3BmB7Spdm8cSFlXRFQukuq^RJd$aC6cvn8l7l> z>l=Zc9KW2recv2q zP+jb#G2s{bd6XtM=7xGVoq_!AeazKY6K%#DQikwoTZrC99B#5?E>zK6^wx;fPah@6 zwKF-GT+@}55`EwAK@=2n(#DGq7su3s0=LA1d8sWx`Uxfl2$umIIWg2*4-V=RUs9+biLAMIn&1h#dP2J zUBAf%{qqu{X1>e76Ljb!i64E^y^D8PTezgb#Z6e$U1L&)bZjuJiruI{lfQY7tIXNn z4fZ)V?9@<+l0&^Y2;z=qN{ZLIz*9=1$p%Q{i^&!HrBEzsCI_;slE?^_Vt$ZUk=|wG zs!b)3-)`gD&5&|-Uvhz<5bY?OmW9s!~Yh(tIyT?`JgGKSx z<@;%#y+;PhKcIbM3FeB|leVN*8pTcN@ChD}Nc&NCkzpxZQrIPljkvodR(@J-wF!UD zn}dOON#AWt?7TBzPQ1TR3{Z`lWwv82Roy^}J*bH>ApsIJ1|g7UYTs2{KwCU-FPg+^ zt8&SQpiJ^bR@lIBxQgggk7|`fgOxi5PkpMW+&Nb|Zy#9MK+hjLhrbsZvXW0`7pX00 zp&Wl9J5h|T1Cdg!vH4Br@AHNLO)s@L2Y0^8ev&|E40_D0}MOfR`YWdJA~ zq?Zyyo|5{7d+-fguV8Ju?G%64r}7?(uVe2*U5t>?4}{{iU${TE)#8HaD=h|k^`Z8t z)OGjl)T;Kn^}mV3)l^9+)B!Z(m`gFu%J}e$s_G2QX<$G|>kY;v2!9Vs~ zI{-eX-qR@&vHfh3Vtv8E)NXO>ZVrG%&* zGJ+H?X&&ig>gzy5GtglD-v)ftj$fGi|MGy%E0hi@2e>G`^`8_jUICe7rKE#u~DI^U(yZ1)+*8blfPZ~Opnu4z)_o1U8>tI%A)k>uTeVI`3!@uTC6O4YECRHIhpa0z~Os$Sn$@bmeT-*A!O3IRDi7$G;eB$)(NECLF`&%knX~u-E%gp24>4dB z#?fDbBqy^_bSG`v{#$SgZNN^PX$pIwor+++r`x`FWHvZv_i`}r!GP_lvo)DlnzesU!N#awbM&79RAOFgZr z8ndXp0R5;6OS70~XWWX>0i<)Tor4VO>9#x+#V5V5nug%su#96Vd==JK<_lKh{-gY! z%0^WxBa(8W$j~K)j&NLQZfUc#1KFu(ZQzh&XdbJtdo{rtm=W;n+id?x<-P23%3T4G z^|#5}l2{?VE<#JraTi)u%^J-kIj^#a3NLYj9i>cUlTB3>?)Y{iR`gO(8uW(^6?C@R zl#czH8xtBhsk4roC5#sauRFagQ}WD@^-QB|1l2+XhlZgn6Lj~OqvhKf8`{QMdufdK z>T4R=#@Q#UZpOcVZk@7$^(>s8Zc>;t6B@e%!QTgT?Zb0s^1Wy}mo#ks(!bGk4o!U5 zjU+J}14>MGk{vROIkGJNsqEO@*0wBGbIWG4oYjAgQd=oP)>gCOj^S-t`V(vZ;w80` zfyE9c!&#ZivJe%}|IO@7p|8aGc?ju;+99DU_V2zo3DrGytbqsH$i9GmJa8Y%T>Or! zl&02}B~`E#e!OCSmh9b?9s*OF;XLTycKZGo$?Xc|JR@oBO~UyLD=~YSB|~G*QzJ}8 zlA5NWU?WQJ5-r+=jCma7w%DD-Je9$Y$AK-B{cg}^Wexsj>mOjN=aO#>>RU;93VM8lps9K zVf;-TStaDg!3C_Op*Mkc8fLt+nX)Ta26If>L3KnLyX0pq2@b{`SG1Lh*z0Wcxl|T2 zZ5`Ov2;J;-Xj)~baQ^a-{N9Mo;wfTX?Y^-99-%|kJZP6@MQks_q!5;3DKTpoH^3&f zv64VqAkC;CgFmN1)zLrRZ6+7|7TC*@iTZ>#zPtz+A7e_tuhX;(kzP;oF&v`KztS6x zM=7zl64lJ}&Xw+)c96ZllDuhekh&AWqEjW|DJG|#nBl<#?N%i3h! zi_h0>Lv`!&Fo6}GsZCs_igi*a^$CmLuhd_v!hT!vcEn0{qj?ch3GF(YPKg@n#$wio z%04u}uLw)Z4~>THy$6MS6Z9x@F}slOO@Awf?Ko~O48tM@kmk+Dr31dfgoobTu?ba_ZleYV>iX5{!>W4|M!jKYimnlB~@#iJu#Tw;Be zbx`3WNh^Km?%X_Y1tm!v=f7`N%3P-{!58m^&r0I^2mqu>?C|PXyLmRxXTFF(EbLpY z`HayYI2p1%#(Z)jyoi5@B?3nO`pw1Tr6P-y$fN*pj}%}+?km96xc?c)Ymj7I&zxA5 zCK)uJ>{<;-?f{vN3oBC!wA|ybs6t4!gr!o{E9v-cC4$A4woUJZjb@eZgrrg4nvosV&6d2k3JDhw1^q_Y4PN5u19Qvc9^@(86YJ+0LV_gRB-W-e(3p+)Yv-Yg_-gEmBHIucVP+=R5Kz-_m@rs7te?eeylC1@u{2u+dRLvROJ@OMY46i8 z8{Y=kLJk_N*OLsnlES3a1OOIrVXj%Q99ax18pcgUVJH)`i3=C9(S-3bU4wr~=mYi} z*<+eLpIePz?kEr4y&3lcqyU*W^xWZ>s);lsXF{(_mkfu6ErW%nW_n)FF!|r8b{6J= z&y=TpbfzA`Mh|4AYp6hsB&U&=43>`SkR959#d<0>OYoGvYuhh!ht58SvLqu{LV-Jm z6mfr>^~FES&1bCwAg3qWL(6FK6zPGKs+BH1A(4hZ*qdY7vI3Fa{N? z1Q89*DHRS>eWZEI^&m{EL$T`Punv=7aLk zVOfLSeCh=(FQ6&9szPvddvogw{;)){!ex`(_|Qr257DZ>BT+;XKn;BB2L8~APbO_p z8wFDM#Rm+0;|3n-vYu0@|7%+aAvsj|dwlx`|K9eGuW;yQcF-c58+em-4!TIcjay5hkinSDudu_Vaq&ybO`6Wc-xcfh{tfqf~QnK=d)e)NC@ zU%f!GphG2twMQ1DBL2T4Zf*YvadRZ1*nKO1)FQw zrdnE$vAk;RqJ3}fPdykJ4hy}ukt43aC16wv{v#y zvX-h?tqo%_{i=Ma-Efh;l@H{2px6Y~oYKYow8Hq@ZIDSn1y)A%8eqAgB#e-*s+~(X2jKx9_&i=7Ma<}vXqz($h*$ZTp>QadiYY1NIFar0tC=7qn7FHckR@CcDwawu)Njqi4oN8sT(oBI4T;gG{|yk z?=vB6L~pRqT(5r4i?Jco@)Q}r3ou5T4>fjn$Ptv3lZ9{ z1baNb-5k_mw|dIfo9GIpTP8@Wo;L@TWgmatX2W@FKGA^@57T(dpffBRMo7?-ZGD}ZAFsy( zROZ(Pq(mV$L>o-1!<|bejquBQo)Igk;~42`>=IZu3=iO4BB8j?%r+vR!2q~jzj)^Y zo0yHRM7W$ugNv7IL7oqX-6fDLpC0;Ln~q&63Z!kH<(7x-_U_?{u-narKbZroB@g4l2)?B}7{Me1fcG zrb$|WNimPiq?uD6TqdTAQ#We-nG^%a4m3kXA#UHOA(9Vfaw>>PB-`7kVbTsAyVB@@ zwW}2o1A!0vOJZh_O5VdaFswici(%uiF7M<&87>SiQGC8zFO=(RzY!$ ziHk_;d@y)zvjqc6QmHzV&9W4-D9JJ^FnQ%V3Zq;axlVo}>x@rMi)>=@k9Q2MRz~%=?VbIU zH}v0~n|xyWJvPaCRB_7HPQnu$e9l{mXzTLv;kI7*E=jxjKI)5H+Fji=MHy;d(hOQ2 z@=`)pKn9lChc8{iBd*956p}ZQGl}gOd%EOP0qRr3%Dy|KnQ%f3L41vhi7Dv)EKMv^ zq)^g}pK=$O7`($H%KVSk^Ftidx07ssCODRno7IP(4`W73rCHd+dFh@WTqx^S*sHPZ z?s3!(z5Zu7?fYaccuS1s-0v=twY)LCCfQeG><+HhrT7{hSiQicL$Owb5kuE!^1P|k z!q!(@Z&jUV@%>rcrA9_d_zCc(u;0lc#>sDHW>vAVbOmTnkhfFT()GgZT&peG6nKDa zrra1Dp4=8)F`Q{II2MXF?|@QnwuibI$QiizhxL+HvIF@TrLSuW2a6_*`+-A*V1eX zx8cws=}X@Hu^FU$E8DrWjjqeH@;NsMVxDyZzwvTaNUs_p1}Ixj!5toxcn zJj&Q| z-ilg@SwDC7V0;vx4KCTsQlG?^s2+Quy`p<{FCIMj`bKK3xq+HqHUE?XIQQ2r){0UO zX>_3hLZtM+iYq)w?7}V_eCA zkxIqnH&%rnczH&@J}eaaA^18vnVkhqIcqm0rN>L=Dd5CAsW~4zQ2?fg3^E_AG#V?DvG7Aqoq&r?Wud8Uk<K>l$UM`qxUYuILF5__n=vtweqfix4Qt!GqhHy61X%{S1gq1H|GCpVKDS6=C$f8*Ttz5QO=9Pv??1C3oF+`x2r+3V6-4e zJb^j^V(SDhIZv^66{cd(K#^R)N4GK=nm83tv@9BnD31&PyUe-XkY=->3dF}hzoPGg z)a*tRx_2%1-?crBV?|$L@wuRv83dLJR}#Tik}x{f@wU8tmE1c;g7nSiPaQX-etpte z7}pSMnwknhGfEozXbT7J;Rc%0jt+HPn)(h{jrT>DigKD*)wtvwUPqE#1moNcbQ94z zNA^&GgYNJ8LP4GzX}i<5X4l%f=_cw=c#{p3e5m?ya2*I@J*f(IUcrO9 z-?)vM+_bOTKA=W+A9tp5$gJ7bA6HhGWG|ma8WXZE>CA+>XJT3g4lx;rFkZYynX1EX zMzd3(r%o!ZfAdOhu)7gdvxAh~y;zgBtno4fh&F> zya}K(2nt@@$R(omNfl?E%R&b=#fGYnPE5I`9FbP^V%gP7D>^gMr9n67fh82MJoQW> zx1h1J#~!E0xv}|UOFzUuBs0BkpjCDZX|a4+M8njMUS)1#oI6_SlE)R1E!E$I4!ZK} zl!q9Xmw5n$<2{kWh>C+LG!TrR^C2TKxTT_4m{8F%Hb}~ zU9Y`(_^xVuFsn0~Ewr3Qyt-mq{4?7{LZloAN@Y#QFn!6_yGEh~PU=?^aX*oTZD86a z706816X-S@EtDSIGt%o89pzY*2j_sA#OnJ62SqDt$A2OWv^Q8cJnd8F^vTGrzGj-tf5MU?WIiyhxh+Zi zs#=6;^hEB>Hk5PJn*ivnX25r=0u4F@g5;Pe0n%zPxemIl3F zKu$QR{KAh!-?b7X&aaOrQ3yi2m4|^jwc*3o5Tw_q< z4LSjMS8}F^MzF%LK%8QhPh=%gwLlwC+bB4|PcX~=KVz7!M2n>f!HJ(6qxUxDF76}q zR^=|EBP@Zt=C4lm!m`hM&A}KiqG;fC6+c&h>iDA9<1|ar?h0x#YZ9py0+yWl0#k)! z23cZk^&Px*L>`|BSQ0oQ>@XD0T{eCsISw{3aLQqu6XRXD;NmEE{?XQVAVnCjQc*s7 zkAOIP^RL6a9`97}Zl6@>(973fUzY9iTpKN=b!sxlZ+*>O2P)+ZHeNj)kA!irN1trAcV}s-WZZo8siw$Y)n-v-k{ndt}hP; z!*1R`9GBMLpuu<;0qU~d7GVgJ-^Kp`9&Q3kNN$tTB(>)HG-0KDQ-gNRN6+Z53l}=36p+0?Wa(qiWbZgzP`n)zhL-4h&GxFswVM&vY9{85y z8me8Nxne!)H&F{9Ah84s^NV)4{S+p~dCrmQ%`8sHp6V2#QfjTga6MK2$GOUYT2XKs zF$9f1_I)7*Mj>{kCAP2;?I5(I54M;_#fW%`0SAq2R0QoDbZZ|q=*4sVZk>vR~fd=9+O6+oB%Sqc(cmj4npp8V8M0 zM2f(F$;U(kP6jdZQAMu$0n4AlP#O^R zgYIq9Q-ZLH7O#(AI#2eQm}#DiX~w$jwRBQiM-s{&dJHlQy?N39kWaSS!EmA zu}|E)&}%@JG{xg^56Y5+*%(}A9K~tTIJd$*ZP8CmtXgazqK(zsnRU>K+fv;?4cKb= z8pnq@k4yhGqEgCTp4saRBQGH!pTK8pen^X5>x^(~$uHK;!Qy-F>zqMAcMIN8+Ot4- z%s2hT7gGa4nUTiVPmFqz^v^bBe#QS`qQWo!3RDrAcd` zk*h02HKgzfN9-DA@YEGflPGu&F0WBN6``*KNR~C}DjmGZbApiy=1(|t1dYf|IhuiE z3Uuu)yh*kUtBE3%oP2oFA78nnqt$sR_8)NGbS}w_{XlSrD!UydIRgJKq$~O9<}PbB zk{v-7>m{?;riG3Rw?&A{ zu}wXanB68&s>LtTxhY8shrLXSzok=TlSI!L5+_S|N33iPBd7})5Ej8FajBXHe*-RH zQ3_XurIqQ5<+yu+5h4~~gh&_|ArjcYEY5FuMMV9wWSS!v-x~AXl1o#|_v^!IZZh}& zeu~)}6FK=Vg*?F}EqwS+M*TtBUeO?(=C}T&r|4jZEgYB6*EmP}aNa5Ievj^H4yV@O zf^DED8Yo^nH37ENs#k}J`6O_&r+XEsx4HtW1J|*_$dE41@Z-{+UHG9=h|SyFfSSu* zKkGPEK|)hEVR?P-bZ(|+uF5?Zi$C}}mLTX^qOSF7=oHz_@e#U}(;LWhHV!faj<4)- zl1c)%pIP;vYP~0o~@ zcv<4n*u4H7;_6B)iG>SB%yl*MW_a;%io5js@O3n6PDG}anGeZqr6JS0=XAIk3`QCy z+n)@_%V4Kx{Eq4aPx<7p$`vbAYF|bcEA1i&r-(EpoQMQ|HzZ<>K3Ggj@qitWm=B#A z^4t(B?wlS*gq`yaTk&TK;+`QF|6A8i$Y&}96n92sYZU@+U^wvX^7#jrhlZpwNCr@* zvLY}3B2a+_`!oV#C8G2BFiOY{e&`;7(P&40UV-hTgA|Y*B_J}(K?0Nr+4&c zC5j4XrE*#J1|`D^k$Ak;jwcdEiuW!w)}1;G&zxZCzh+O4jgF+c*f0MuZsefHl2DW# zVDdrK64wwDTPU@E{&q6foEK6pxq#@L|Kb%am8w8;mkl6wF^D(BWw5yFKVm zzq+H_nl>in#lsC=M}Bi(YPM;H=QTVgd?wBfKg;a9fNS#S+10@A=FGPAZteCS;L~h4ApAcamQdS%+_}&< zw?lN1=CE03@~AP~XxPiPx!UFl`qMgUz|H2251d1a)NyM?2@@J2XvEI`P6p&D3|XI8 zQDVajgFogSL`j3bQ#29h^?N7JpAPnWsSHD7yQnPDCRF=mX#R#~w682@{K`?$3a>mj z%}TY&f&i&az%Z*g*O{&4aVQG39t19tIWeOvff@~*e=%b#qcAZVG?TKbl@EDu2(=f( zqbI9VKwDqV0KQaZO7Uo=RLrw6L{%YMtyArvyLBT#Al}s{iU8TUSqr;05 z04KRGVP4rBeN|g+iy|+c3xl2NV8VSa)0LuWYBUzobLZx0fr$B)gWCQAlHbh=er`I6 z-l1ipBTwxoT)a_@pKcx+{h0iEz1b7oyYf(;N3E|v|`Kp5#3V^!{ zDB*>N!Xwyy~4aA+19<>(c1 zj`dGqB+b`6;$1XpS~kD>!pd)E!Jf*KmXQk`eq^+5`EaYt1FuMl&2%-qGarW6D4QoI zvBzIxcwbaSgYkQvWC?p4I}8s8VlDA|xaXDPBeyl4h#w9)j`Oywp02H}^*0{HIw=f9 z%-6h{**GsxT4K`22pMR;0pSjqh`nK~8%6r-Ar9n)*DzB-$X0bavNFAY%~XNz*%p+^ z)*6a?XbW7uaEst`jZBnKW7k1(7|mp_!TYgF^qt(|DELZn-~>76l30QPre@%C*{-kv zCx~RAgu3p3F?No@l?P3uk8Rt|MjM+OXJgyO##^31CqSE%E{1Y-po6d&P0JCxd~;HVKVjg~$}aaVABu}} zHhRyI$7<|i3wn<&BRVwO_ZrcoWKzNjACi}1ZzdicxR9-Ds6(^!O+Fl00}Q-Gf7c)2 zMdW9WRRD{8$J(VFu&XM`X5u+|JK=%ZC|p6h+J-`omSe%36sE$NUvVJb6|Er8m5Zaz z+pb`o6`Em7l^tu+YzE_Y;W-{cu)g-Q9OJ=Q*Mt&y%w;3)sRE?3D+WA3uQ@rPi-)&J zz(7?6Whq~iSWWKhr@EG7zgbvK220TI$`HDH{Fd^PCZ7gcHt7+{V;=?D zpL(P-ek_q+fL;)}LRWYFviL|hZ z_5*58eS!UHHK*6w^W-;eg%Y`{8ujxD=9pLn{WNMJkkGsn)q{bHc&2KG!Whw3zsON_ zi)O%*!q*hdX{Cz?%FVOpi*jwm<066fZ2n^2Rif_k*CB;1s^-j__|B3d8S;g5{HD(_ z4R$0&GVutNbS62Gsgh-IW7V(tLG)$KRT z75@27I#;;5$=9~6?k_lOd|d0CILcU#pQXWeC6 zmdbjy782CG=eg6DsYM=?Q99F(iwe`8)D#bTdfWkMd6uIXQ#ou`eX=Ht$tslTCPASII$zQfp!y~*vTgATil7Qp{by_bP``ia3vVU0Ty(?Kb! zLo+5V3 z?J1HQuE$EDU(m!!rIfp~YoA%HrWlET_sYPDVN7oLPUGT=uE=81Nv94yYY}i?Tc%Oa zzCY9U@6A=ewCo<6iS3U>!0@u63V~kuL|IK`M2bXfgC%{Zhk0ja_HxI#IZuv(qx}yW zKd{7W8UrdI#Hx_pNrZVpylP)cdr`71gC=mg>E3JthRXbWe$cfB3kj?B0@tUtep3NG z%c6pTgTqwLci*kCWYnE?{oW$K&jyxLpw=dWTfjGhZn$@bO}jDffI?d-{@`~YUP+xs z?p<8);ndC9$c625#%j_l-Qx^SwDx;Bes2Px~U&crm|tL_%M?6KiJ zJio7=vu1Q2th*ZVs7suU)OvozRi^=DQ6)fUSuRRrXu$%fw+R(+A#ynVvK&d*wwQiW ziq#hx%{bRw{$@e9u~W?eqgdNwbdk$2UY=%Bu7Cia&Ngi>&Tzu5%wXLfG|0^Goz~^H z54H1Hvo-^}W@jCE%H&?mrtCl~lU`f5Gz+`}b{*$Z7Ir{QgSZ4PIZ)`tRAw9{u(S47 z7{xGDz5r*5#v!><(Sf7V!(pek2t~AV8YQXn5#^|sIDxBDGQhjiKBT=mBjm6;BjJCq zK}iB0N)nGLN+F4vPjEOJ4(@0rrV{aWXvBkY$fNzOvl`~%_vz&3A|^NMc-yOg`_8(} z(N!(0GU}^2)Y#@G6Z~E>KQ2LSO&%pJl8-~4qvYb*i@n-KVR1+*GW-BJ*{>Lp@EkH* z71r?h+vS>cXH?oN+jT*!M;m7T+?o;ZBMUz|POdH|z}^l;V8LlgS8#RJWH*jfLEwcK z9L6IxH24c(wcJ$u7o@hiWboqoV#q?KCvEiC5ke6L?n4|}ZSQDwRvY$&W%EtqF~!72 zIy8QW=4*p_Y*VM-`r?+c{dL$A+d*}WmAJ*#{J-jHU2UGF{y!_njLd{q`RRBAW*k-} zd)8|Ndw^8OAr}K@r>d>$c83ut~N2nuGZuX95H% zW=wjDGcJ*mwVlhx*ZIuQfJPD~V~mhZBp=|H$kPTXRsQ#1Bl(Zy62r`^(NL}3HG;n1 zvAtXEUz3}(oA$eAu4g_yedB&PBfgr2XN&L|-3gOECpL!!E@dAs;2BQyIrr9!KIeRh z?OcaZVJ&Gg5**C6(@xOt0rvR2oZ2ATO&|}GO_eHwDy}V`MO||K(hY~75^ek=rJF5# zHlf_|U7^=IsDgsnWY;NSwY`jfcoFzYk0~17_in=L1>D z3FgU$dsEx2XrU@0gAa8fFV!}c|cGD*7W_56z253C`7VV3kZs4 z$xJ6Gdd;O^G7mih#V!a{zP?-yem_Kuc6ChQucnXme?WC=xenHfoe&3%uxWQpJTfm5 zLs~f_se7MNq?=%woUM;=RGVr1h!&&ifNcB-UlO!|Zclmf>Z1JKUWF>fA3rJVO<8q% zz2s5-KApJ@NsJ@jeiDPW7_@WuM8(7J*C(OAq&lOGF7z-kC+BLg)MhUF8)vl4(KRt# zvw`*C+^OBdaLVG^Y^9xU`npwatAoSt!e*sChyGx()LJfs$!Vp%)QMuZ)Jh`_#UQ(t z$e1{Vg=(V)Zo!vWt;TR%pcca@k#}V9Qo`p9Kf6hrrI!arm@0Rpd6$q9!(@E7j}32n zASRQZ*2N_*&6N%eZld8SY=>azIvA$emuyB`jvXi8Wbs~9A#S#yLwkcVg)3rfxkzHU zLTG~GuN`A?LB?Tg0x5TW2W4Xs1vgO+qEIoNq--`@3;{-s{I;`P`)5+sV!bDbPkvi5 z4atR9iqLI8_6L;(Bk%`kqwsn350V^lAW=eIPto2<#>u?>3r76N6H`cX|7i0>}k#%+12e;A~kPHGsWy3TP}h@k>60{mJ#5iZu+A0O5q z5Y0d6Gl|B-On)sFz1LUQpQ6(Y8+O3ea)%wUvC*0o;BlRy9pLlN?BOu+cS{G<{qD{N z{b6jj9s~g{qenDA72h2XCd)pZ2wxtYlxD_!SfV*pz+~}T>8gO5L{j=ps6Dxn&d|ez zGTOcp!#IlWYOU=8lAy#2B|C-=FT1UZVmO|TX}Fk1M@BWsbM_G;t&aKX}EBy5Ngmv1{oAB`>*Qzk9A54 zj-JD_usYJ(MR|+Yw_H6rii#?=ZQa`UNbM{wp0}}GIlSZ%W=mBMlbTE|K{#`K=o)F} zaR(7g`-6NH*jDvQ2wpl>f!0RlN{CH*)j%md?9+B4Bp&uhf1s4}u$L7A1&k-f_bO*2 z`&_XM&EaMV&E}7lCF{ZI5}LDJEsh9Gt9p4izoSAq#5TQ3=#>Q(jd2K1ig2!%v6tmA zma0{QeVRRR0>H9ktPrx>F0e%7DoKV5D(S>p&)AA1=>+q$&Ls;fhmF%$(BeTEnS0>3 zjLz3PCYHGz^3J^Wl$zlCXAOk?0n;n~!Rd02ieLwjKh4dMOctjKr0=LN7IN7%9m-_t zW{LE2)w-8@Z|qIB*j#!ZNu;3a=#Q$Upz>49MS>_(yVGUk&n%u|ntaAyX1k-hn@<%SG@xvG!iB z*I`ajI>}g_7d6Ye+yzGz3abs_S}?55CPE}0m?KRYC=f?V2}jjAWYhn|o&K{2(A7#_ zNP%Ii-~k9G#^1$^u4^TW0Ff!Gk^JkAK<32;x6hM}@xT{$(y*tUd0M^{2>W*T2^#_iB*6cST?C8Gw zuTAy#WMAMely>BPDH1d?)Mziu{1ncS0u>CnyI|5WC+H;V@$+%kvGK9{@p2a0^>aCy zt-WV^ajuJVK-N+?_*%TT|MkU)yMF6s_ZwKS&+m)r`xe>ndvO(r2l_g(_j^D2zUdSA z3iEpv1X61RzYY#g`n(IE`XssT)4pG$hzi{=)J&9k7u|e6Z=rN=cybWrscVUhRpP`a zQGSHs+oNTpk0|W?aw&Cx1YOoA$>H)u3Vk*5`O?;#93Si9f41>a$0m)FOA=O7sUVng zjJ99&u&dHM2Vm!sbMhbGII?^z_W>aNtkQ3P9?Lg-3On-QhjO!DMlkGY*!*CC8yhAJ@Kl%CRtFS4G z9opvT6YP5(BtYJltbd-_)ZAw%W<1Og<#0?95zL$obU7Fk{x6?fqD+_RPT^#9^c6mUa$g;F(=f7be8ALMIOi#A19G4;{qfUUwH;2L${< z6+o0(x3N4)tiFT6U`B;;z7=`tf-K#R)tToYd&y&R5xodD4rvDebA#7DBR1nQhZAff z0FQVD$MgDcs6Ozz!HxWM$BlgLKQjN{Ha%+_)VP}aXs8dThsiGDZZ;AKN4YPUt1S{_ ztRg>hcGMd+m$jP-is1P@2;MTQh;tTvh_j+TJWUx<#$1XnX935%-5B%})om2}srL~f z`1d-@|DEeSpI=B5;bf>Kq0l2YvMOLFZaIm=KiyjpyNRI_ZpE>hicx&8Nm zxO6P|?XswC?WxyoY?e!DdeiXL*~Yr@^bWa|$kLfX!RmP^Q^0jA*Qbg$r3c=VquTW~ z%{Qd(0(G@Yo-xekBKMP`EYD|Q#f8bR%jy#Dbr<(;nWC=jKB`sKI)dddKwkP*aLVBw z_o?BXsP+_um(=P71eY$yV+OBs8-oTMfEq>OW z|192vd>yK}r7f;@IW_$#fIQsr!2^Qj!InP|XM-y_i*l`Z>qm+(UuqWRDX=y0B!L1> z$SbJBE;RRvDi(MZM}lUfM(N*V3~}@Z>F_r?Fjz|KnBF6bZy}|7Qnn=gg=hItGs=Bb55vH^_KSs|;>dPelNEQ0uIIRhyh zno^{~kfPy@luQ0O$~#e%2y>(+RdN5Bl}eIO>+k!w9=_`~fcbu9a7-SF=xpD4g*7+; z3z#F(q5oN+`(bFcm?Wsffw^;iFu6>8zD&$W{o+cz_}ZC0TGIHNUCuPeQ8UG5;?+Sf z+rSI>z@xYV^gyfwuTt1m+xc>_sZaGZ(QWFgVO_bHe)oTR+$ED=oKu*Ww!OZ@5XrT} ziu9?1Yls>~r8*qwq9r>yks-wTR|eQ%8OoH_7z{#CO=E%Wh5Ba#On`Asr39e8u+?W$ z;&@Oq57Nw1U3Q45*y>#9Y&^9vT6@B? ziy6o!HFw9dc%%;vbC4XnW(kf&5`yoG*2JWohKpqBVia6c(&-Ka#SE#X;haPG(?V}7 z)+d=JHsy4E8lv7uuTamOsw?EQTZABOYjUCJjCfy}Z9JXvd(n+WB^0&FBBS0@)}CZp zLSipKG$W482`N)@W2wku1n{`_&o78KJHMj#?RL{^hhp<&2?(G$;xe6_18-WpKL0g7 z-vgG3H!#VfYRj~2hmYD$f8u{Q60ZVqZepd$$*G&szL1YPMmA_F99KU*h+h>G;%R&ivB*5fX69EjE9enRo4iY#z-Wbg$}yLYaO z2TqGR8MKS8u-@4LHJGtcdz997b{J4ve$su;_%@vpQdU-^O3>;c*jGA^VlBfM?AkFpBwJ|! z&!YT+XHk_1{8sZQVOFmw>tzE6!~?}&Dw>k~8yQC+MYW=h%B-(9};P5sIf_&PiIhL+$cY5!lalt2Ifu#_yU zKe_*3ETxZj90jN2wby4PkV?rl@(zgtG1ct)aR^Nee+w|`?iz)z3F*-T|9XW+HGvQ` z!|LMEE?JS}T^KTvFini+#ibzUIHCV`V^??j97X(^TGkc(c7LslYWX9>sCT4)V!NSK zbA2v%F7F3qz3hJNznx6_>3*2$9>LyKIzmW^BThz{CV{;OzbWq`+r$#rar*R}DwR8+**TfpkU=ri1wW8T}`u4Ur zzZx0IA>u{WJ(yLmKV#cgX9!WIC6f>QuZF%|W=0v8I@If{%tB(5wDqqlvWr2e;$#%_#=C6?- zL^Ovu4t79f9cE~r?N)VDt!y|N8xA&oe6Yc|+Ks;>#AJtCAsdy2ORxJL)R0vTXnU%y zIi1e}_osINr><+TXSYZc@ExqnP1C-0p+@HbMKrbzi#mVAfyg@0J6Gia zRy}B3*kqe=NxQ(KEdkYDlzN_lN^b%B$a<9KH-M3hrp#Im*QC#)pczW&fz)dp`S0ZwjLKW<> z4Q=`IK*R95W)&=6By#>TL}Nn+M1hb)TXSZ9Lus^@9CM!auYc%ME>uVys7r_GiZwyj zR&w92Ev2JUHLxK6il+Uug~LQiTuel5FhS$(g)rHKBf#*An!&fh=z_551ckIy?5n4S zS87*>FQn>{Sb}*g*#jJFiMoFOwKGC;i3ZLn#gbJZraqlfPV{CqtL1_f7?d%%1$#gu zLlP>b#thYrmE7F9&aW1;^h%eXy;bb|a#l4=f!!8CB${*VWBEvECaQlxE1-(QUg%wf zWRfHESA(|l-6yaW6byk0Ig95EYnip$4gz{@dsXCNz7StW@@a8`I=!#SwJZ95F9##7 z9Z;I-*44B+elK_TH7vDw=(*9bBk8x}>G3fvy@G8se7`@2h`*leR{P!${60P(yKZWU`}_*ya%C^h z^74v-3_xYk=ma0Du?Cvpne?(9Rf(ky-r(Qro-z>4&-p#wV^6DamiF|>Pq+3taw`wM zO5Z!rPxmSS6$;ie_WL!TT|B^i^cIH&N?kk-SBXtFZ z%$mxW5={#2wTIqP&Q*N@m+N1xtH;-WQRbqlum&4qOKCz-v%^pau=E3CKlO|mRLxo; zDA4inlTZ2~$Yn0@qJ8KF#;B5{=zn$BF(uTvSpXT25b%^Q3kH`nu;O3&0UI_uX~Vwc z9Ket~Sn&h9Q z6Er{pzC*{JS_O6^8$bd#FcyLQ4>B~NjY-;Uv(F9?ID<^&!F|iHw6P|gxAgb&WYRqe z#1@Q4%LGSWMPj#D>1PRHkqHg&cmU$iS_V?L6z<3c5*$nN$+n2pt^uDQcII>4rkF2t zSODLF`V%|KG)Z{Jcsf;F2fu)RD04Jt*jzM}YJ2_xX|Qpys0K8ZXf^6d0Z0&!s3H`m zYEkgk=Vqx#5R<4V^f$1cG}|hTx;5?3fyK1(;^anE;VruzO!k{;<&$X${i+fs-(r5 zCby)#C2`Lu{s}9}UYxIqVG%n~VFjbthJ|iE1*|_*AHMbsVZ`5S@wTdU_;Kr1eo+(v;4usVu0M z?QZABQJil~U|QXNvSjtvX2nk*@0q}?s6`Hqqf|4{&i{85r`24y^`FUU18>INF|$(9 zh~bp@?y1RNnlKN7H3U|kTk#-X8a7!#{vq>z(6PYIc>cH+gpP#)mt~Ep6>PM z)WTBV`fT;IwkrkM_%4TiRosG^hiRB~o>lZa;nXpDb$~!@3*z*>J)>~m+uLaczPT1M ze~;1Q5ka-EJ0fY6x|YOJLvFZy+Yn@(CWB_yW+2|1*Eqa}#PFZ}H)Fr%L&$3D1yw1A zYRd$c3k`&tJH#>ZFn@~rA7NIxFw%v(M&M19;}lJTC2^S?NG93wjggwKYoXX zX3Gz;S>{2glCOkG)N{OEaB=004r|X-S}@ykDG3{%GtIaMdJ>WYRv4`$NvFnLg{g`Hd{wm3Wdk zH7a(w+pkCI#i12oO1yt?R(ky+adMFwbMk8xai}jc-b1$n;pV|vaOt9Ov(a|HTGjsX zMRCLX79eqGxt!!naK|3;xsv|Ja{ zVWy|?Qv?ceHq(%2GrmS;AQg#&ZSgyNh%-YzS*id@tzqoB}^>};l0tu!% z(k0K>@nH?C7{=b;gx$*&&+q2h$eU3DdN~7a#Ny?n{ht=INQPjZoR)})^9tW_cVx(2 zzcy^E6c)_%sa6eF>E`g(X85!xrW{8LCNpgoUDmDXgVIK`E&SfA^C|6zbzo zI3i68>ayZq27hO+HShx99EVS6g!J4c*fM_SeXu%y*CDpYaGpK9%s7#vgcpvuMAIe6oS{)pV*jxx1x1reHu3CL! z**J%|jVU*#?l^0A^CWRl`^_1JPX&L-U3A2A+{h%Z@?Qmp{$xKZTK8sjx!#g!<5ng| zSbUXihY~9rf?D~o6eOi>_UMh4uPGzRq-2Z{sOIdRIHW8>81+XL)>e1Sduw5xB z?JBHfP<1N@7C@@mtX@Sm#+Q}E(!|YTMm->i&1DApv^$Jbsxqj#sq1wz7ueg>3LA?* zu`4Oc{4{tIQZ~7n?rQ~P57Yj&v)$@bt^1jlI*%iFsk9)Q_h}%zQL4MX4lPjr=+huc z&sAIa8kOSi}(B^RQU1+`hcwoCh!~zhCKgMDKQU+V% zJ_HBdt3My?#Z-FXRxg4l)7s!yt)^s>T2l*3P<7yLU!}e*bv5pv5eHict{ta)i5_f>c3@%4~tST8MDFr;Ob6)~QW+nz9dqs6tDYDAu%&3r1&s>{7Dm(MUE zZ$~Qnp1Gzj?(X&sMH`vQtSB|I?Uxk|+dJr$VM|%NwKaFi06K?_O+ivg6}yVBEy-%x z&%3kQ7hmbk8CbeKRvHlu>gEOcS(N3QLb=*cG5sENbVSZ4xBP#O4FfLH9{+e3m&6(jgnqGqO0SZ_Otk$D+*h;mV+~8MbvEPwmFF2n>5xjP-*y`POsEB} zds@rpDk&nHPl(>Xd@{ZIFA%!&rMu&_>0xLISTu2-JCB;@T{h`vtJlo;#raU{JA7wUAJLGo zRvACEK9`p@*x4dyyk4h9n<3fF5FyWiyDK~#?MLcbA66pD(&|cFe5?WEo4*)UddbDx z6qzBkupFDSy59ORjg#1S8l_PCwy#0}&Mwk?fS%BpGapGbj~$A3KMI77C1_+mTb4>_ z`NjLHXbT=b72@R<+NaI4qncaCSq_bPD>GpPf#!+|rBR@r&K2vHY(Y?D=;$mpM$Gc0 zNNuRA+*)F?(gMY%zxT*yBNsL?4X)Os<7ifYcEmWlL)WG`fxflX$8A=OGee|vw4D$O zagN?~QPQ8p-I@ir2p+(dma3&t-kDzV9YdRng;2+~dEm|pnM2oq?1dxW+m}#B`3A@- zg~1FzIV7-dn$!lbU05UDapCJ-`{crcCZ<%-yoCDev~QB;yhF!?E@yvwe&UdPxM+QD zrD%m@z3*f#`qoq3nSdVJ&fNa5z~vn`^FsPkmW=z(jK6Ou@ZD!}XSi45Q4rP{H{!d_ z_lk~(F^Xu-ri=J%DAn6{`0ifT53#%XD8ifUeZo?KkHLP@PJvQpLZ(QdUU0&O#6d5q z`o}A;c7zREZSd73h;MWgF&q&;Id~-gEEyK=(FxET`t#5QE5WKtj7L~OB}=5JP!-(M zajMP|doRxxyEbMbwGHz3x%`VeBJ4hL=>=4XsJX#jorgWT^s1UA-F{j^q`CiK!maJq z_}6fKFq1NXE?8|(%vjMSGFNTKwbQkC9C`3g3aM)_nd^AMfm|GeFQxpqCzyt_<6q3f zpLZ|#Pv1%JoH!`KYqrxI>@f(AN5a2y;<(TISgOPHZr18V<6PYLhaV=|98oMC`6b}n zcd>?R2s|6Jmcm;9$a0m}@?pgW>%}-8RLBj}f8cvcC#JH|I<@H$XxW^lWKIM4r==jG z&b&%fQ)?^ywzl154MKbXI}JyE(kX_kYj{+wYvQ7sGB#H0#WwG^(tb%nG`M(;6-T9x zmgh}=l_=p`Mxa1uJi)1~d0=D}W-N2{c8B_m%aTXn zNL(%R#DN_3F_D(@KkR&aP92!&g@`uXF|Ipl{}y5-+3*AIrl`K(6c5>8e`gM#7_?;% zHVk6yBTQ@2zxjN!Ho!}gE#;GA%wfPvOu;o1T!qTT<7j+-awRJbfE{^|L?tqr2qU7N z7ce676s^EYDDZ|j6^gjHx`9=ql98Ql?AGsd-j^|dy|@>!OM#kVBD{BOcT+;@ zcBq=OE8QM>v~#n&vr$uclIz+WJD|g7q7B*&@ebV8fwP2rom&>vK4=as=~8~!A$Wi_ zWG$T$&J$W7KFODd(XJP`3KR%1+oh5d&O@PTHY)p1CoJtvpa3aBkN~OB9QCm7oP=LU zeq1Yu8s0X)R1tEyO9qUL*?it4Yq5O=FJ33ffELM2lf%)!{Rog z+-_klu$|Vtv(0)1nMHe&VqmntN*9p$KClHf7t~PEUPa$@%-3@CcQ#Qq0Q6%b0a%19;j<;wXUzdF2f z0ufzcL2@lYu6uMEcJ-*wU`z3iY=IuqC*y0`yvUSb@gc@mmsfb#T5ix*r;oKu9rn&h zCAo^!h-mRzt?{04j#@1<9dUGZ1}Cn3Jau{xV#WO`;|?hcyviim#Gzn?dafrHWPln! z{FTXer9FMIUE2mvxo-Ni(N^}e#n$O;&L1ZMf*5?@rp7^tJ3tev4FW#VT1qcIl9KOZ zNh?OGFpE;w8nf6&XoD|os!HD)W60afor_W*VjX9*e=DbmWQlr}{lLG;O;A!!MM6#& zt$}PS@fL0<#1jdL8tBEwp2JcQs3@`%@n}_T8(O<=%;8J|XLTZEvNRRofJhImRIY|p ztQtcw#=gci2g3d*pVn$$s4O%9zq9NZQAMRDiv|!>qfM!3_2t@XXV(Ez6Q|eM8$VAe z169_uzDaGWl_gxMwlr0eto+> zNXNB;(u7!z;ysA)@aJ!aN5(bb%9kbOfhxj7D@6uVzO44@BtAhZ58q2{#V7YQMKs=1 zkGF)8?nh6^dI?H$Kz$k|&IW^v>QXt|nv5F8OWO?Kpx&j@0aJkQUp^(gS%2A$a zuEgsNg=2eD@QtF70cMdl{_+v_iD=}c!!8=YbYjLd`BCq@@*kT}dFjp*A-TS|URRo| zPyH`hA1@PSVjBURXoJatCG<;+bJz{^q^b&Pjk=@)@fd?*RNtR|1CDUU856BM^sSAt zkj&)}^z*5F8E<>hhsNs4^32SS1DY(4ED-+@DR6m^Hz)fG<8aR@AzOY>Z5DPy+<>Q2 zfl%k6C3KZo5%+BtnM5LeIsO4pG9Ttgh%4IXFc$WslxP=2G=L#e zc%V0|VpK6QM#N?~m2_w5oHS%u0E~1bnj`Q8Z+?f&e3+RWFznB{(^z3oV6Gh3NMc_& zB^gDdyTKk=5RE#bsvtZ-Bv}Lp(E;D4OyooIbsm0iu(sXJp%7?54wuhUN~(YRlc zOs0~3U_Pz@mvVW!t;?|>iYL$b&+6I~!-qTR68?^c(a`Frx;n{7ZGiIJ$)Cum)mt6i zg6MK$rhgrsBDkP~t&#Rphm$Z=_dL0i^vS5MHt-&!LWqj@(!Hzd<>cApG9I;c3hdWX zGOud-1+g%+bw}C;6IrNoN6X%aOX1|#(&pe?N=h}MrS1JFp`4W?bIHv?1r_s@e^pGJ zalBA}FiW$dggV5=Llp^#h(^mHIvU$Za|NMN8ydn(E|WCS8hv17!BIWK!Q*9;Db&^H z7gQKTs%gxzpgCY?{I`P(T3I3lvII%^Ie|9tid-3jSF9AKJx-QImRxxN4-0M+xD(RA zz0twNv_^n&ctR@zouetBbF}Nva~`M!+ag{OfyZ$gI|1AWTQGlD1`2YCAL;UX;mW>r zGJ2i3;HI&Ur$!lx{=0gA+|;q)bl+o@>YA>tAZFf5vnS-f#}32x0y7nO{4_&{r65q9DiM?6hAE{ek*^|6FjtP>!-eX%D_bIe! znUQE5u{oA0|MJcdxbdFJa-nKW!PMx?0RW5gI5H~N^pN_@Sf2MvgvJ6YGjy%Ts|hyXJ!X*yb=uW*Ni5~{wT*{gjvEon>(#63|(Zz*@iB@~m**^`%9 zzU*{<8&^McHybPJV$UZXRrGyEW)JBLUtwu4)4)Df2#TnO@4AcLM0`ee?J6=T17#5T z?OD=RY|bM5Oj*jG$9#NwsPD<00(0F$W$@qIJ!b4j9{8ETUtXPW?du)NVy4pSb3IC^ zTkY$f@5X{NCqL%nqF#M71ic@J!-!KaiU3Efk!K^*{XD8p?hB}{J z;CqO8nY{exirKX{WCK7Ilvus@vdwYt2C-{Z1}V3e|05Jm>LkodYqoE0?!`mu<58@C zadVd8v$3F5|Gxk4YQ)h0eBNaVdL_%g7&%K)U7x$D1&Ead^idusozYL^sTkz`zHTIx z^?Z7e1Bi}JiE;mVW{+xS-HryUOl-=Z`P5YU3~~VVr>E0rl-lwa8UG#r?F$%|IW<+3 z{zU}wDxSUx|DC>y_lnH&^IH3tDps&>Fhc{qZ}vB-Md>F~wXBPyORZ+I4*${*X&o=? zT1~O~?7%E*EJ}MM_9&QRokoq15Q9;X5xj;Rwfm>dgB5sAfy0`Mb-+7vepS| z#Qtwxk>repY4YO0m-v1TOQT}G1dG44fIofmhhn9WB}oYp1}YejXbb5Pj0&aF%;W5F z=CAP-(P^^4D=RG2a`lyg0L6_O-3qI~D;ZVvONHie|M$XQ5S!QxWa%*+Q@^5MtjI8L zpn5yDaL(^_n#(_#DMb1p*KvFDlgHf^@JzR8+9FBd+7-Ysg=sh{WIGgFB-5GC3~*JW z@Tm1`wUWEjE+hC0QYdN{3YYnVicRE}q%%stZN&vfpEjE_c@Axg6Ip8b#!uhpo~C7E zmAg3a-HxW0bcfQZlJI@3Xj#}9Oe}MV0f4xBIcXG+6WI9CpT&mBo(qYkJxiwo4U~P~ zY;W+8;r{hI0_I2;M=~MIq%jcLRlzyr0moO3#liaDjrt&%K}+-mM$?M5%@QgFqM)ry{tR2D3Bt+lju~@GSQ#G z@PS4=Bk-uQm{y}gS~|{2RVmQmOvV;w(j=C#p|CVo?%a+JOd%ItfzX97g-YRC*%CTL zIAB6SMCBEYxaRFnofQEM9>6eu96lY{2nzMRxve0{BF>R`kq7qok zN1UWcW$<_gS;9sFlp*jf*sXhcvUj7i47!Y-hNp?*2R2ozWo$)@;$JAW^PJKJ2(>Yb+q!CWM&=7&54Ru$ff&JOGWq(3!HFs!DP7w{EgNm z-<_N+jLCZNk_@N;o?p)Onh;mZAxoX8)!4fV^pf>@rQlN~@Tzq96^4`=@;|Fq(hPb; z#_AUt@hU2cv2qN0p3pRXuxnnRhV=jFY!M{ON?f2CyjM20UB+D2WEj8HF;&WaspRg>VP!H{P$k0X3g1hG?t)_K}(c zSR)?>yF&{5G$X~N97b;_H-;1mB1XRx5{+CF4u^!uorV*^--q`&j~mS`eweTN4+34E zImsXz-6HE`QCy0NDw3c?{yA|%6tIF?7*fS5sBz)XU{0XxBf5M7jh!E1N1v_kdV)8# zu+<|9X3}I;sol*A8}B!sJl-W&Vf=}UMz&~f6s6PuRR6-`d(>uPTQGIhV}2o+eFglb zcOqwc!>)B-{*du9`34)Q3(cVuKet2hQkz1A7M;_ZFkTxoDn1;oFB#c#VD8nZz+u01 zYEZH>S-UeTVT8r`_*d*dev2?7Re zL4*0VFu~&dSd!Q;9Py%$fh2Y348n~Ab%99b&``RH@`zfH0)!9pJ%XndGiYK(#21F% z5zLdH=SD6S*bRAl1^m%MdsZ*nhwZ~?#_~58RFnu?GZy5@f zZ6?%-$#;U1!F~+lL#Dph)$fnbyuQz;EabfJH^Dx?uYKh2_tlHt&pW}dueWWt)wo^3 zc?o$RnbW*irrIiiWiZsis6fAS7aGc67gHDas?ynlc;R!&za51=SZo^02%dw?6r&|o zn^y#?5&`bKK-2b%6LO%c=rg)`le03{RbZmoUd%=B>0NwzcZ1VvS^wCl=sfO!o+^D0 zUQq2QRFL1r*)N}B9@N*$=F|)%z~6#;kO^@w6JVg)Gk!XZ*kTvJ+0DW{I*^;s;ktR_ z2|DOYwyy|oT;ZvDZLwEA)mw|P2lB1LriYrwc8M>~=}G0FvOnm!ZHh0)YgaC|qGDRE z+2gB<;`6w)*{UESw#x!XoP9I5TT#D3z({usPc4AQ3>gQl3DI6?+9M8Vx4)yLP^bRv zS}3?FF?g+UqS;0Ga!B;9v6cVgK$PCH#Y>*6q%~Y?gs6z+0?QtW=2j zVLCAYN`?O@;lT~nk33q%q0}%o@BLEiCNc zJFgN~`p+vLdOgf!mM<$Gkd?h11Ow-mCw_3TqLp52 z5E^siW-adcu>OHH2TYNL6;RM5hM?oxK3Z+)7{P8Ap}kfY5LpugdPmbUrp-2_Q_BsW zIc#C)<`Gh(InJ>{By2fBK-%G%2(YIIM z;U~`F_q4@H(|BKQWnDYMALd(UzaMfdN0yf?lMKY^(Ay3mE_X^j=ces#70eBNR6Kip@M^wb}iuuC+&6(`)GFz`D%K#{psv!arC}um7d&$D!hxe z;&q>?*C5US&b1?t&R(9pDOC@~KK&_;()3q0g3V`_D&DNw644KA+O*#tJd&j3MzSB1 zr0g=7FO4-im5GR$H;O+x+0pr;TDOvPz(3ikVzSli?dfFI6*;YKU1 zpjq&R(*77e22rPBT|aFuj4b!AH=={5U{`$d=cNN7mXuP|s*upy(<+;_@|f`*^=p(p z1vj5wWaE@>;`Jxgq{(7K83P zSehG|Gj-htF|EsR`7zgXM?6Vc*!cDCWwoY}|HZOp(vJ4wWuS>EaxQ&84Txsl76o6K zIq&U78GVGCk`!L0XD6@8Np==5<=T`90^QZAL)~6G-{6~S8RbA$=AKhmG-{ShuAS!{ zD?mbrRJ#-2hw4^DO%^NvDJ`wSl+ROH_!A~YjhpuE^He&e*M8rGcR59GMOqIkX(lbM zw{MiNO&|d0ZzKLdmT=+;t)x*IiBwy`v`lyXqiy8c{L`50Qik`Oh`Gs)TuO=%e<&O< zn5Q?V49eetf*TY8azJwIpNV321=^YFrYLjbCKyzt0mZ)SPt%!mj#51Sz{jiKjZrgN zEV;cgVbRnnH`ZBY51iP5&Gp}v_PZA!B4H5Qo4*4e$GQrID8b)BNrh7L`7`TGNO3f& z9)ut+@aKdq0-tgqOe1bkBM`Cy(^zftjcfAW%FRgH7>~ofVN?e+zs^wc8<4`EnJFYm z8@MHNG`RH(G(^R8*ofdyt_K$~k7d!YE#!x3Q=h2ZGONn0m|_BH%JjQw+vB~R2f3b$?Bw%t8#+jdXewl!_rw(V)#wrz91 z^LxH0;=O;oapRtdtUS3ZtFoeYo>Nu3)>@mB7uON{IzcZ?;n~8Y@un0WG-Vn-Pe?IW zG=F|lJoXRVVY=%q;ZhTpXrHMFM`4x;5ER7%LU*=)O-me*gd2afu76qsV>}wO8P|rBKC}sIH$45SPZMMH->r~Q*}AL!v?N$Ae`#^}rALUf z=Uz?Ew{P7w1c;0&0x5SwYU}MYiD^#tktH8+Bw>*JgB3C1WsKd~l<^~AADne_GQZ3KN?Ml@dw?H zV6bv}r2g6XFFXSGK>-!h3bnxA3BkO?9Mn$3K(rRXULLRjsVa63CCv_nuu+zMuD$6= z$@8^0&A1WbKJ3*xt73ZHTox_U=l-L*KHIcV$D?~v%=-Y#FumgO?C%Y{K^lkPGXXCf4nc4;ESo#97E70v<}lbG-Z1=Yqs zjVs*ckbzm~)jbmMsz4@Nq}wqqJ^#O+C*razyanNG ztW*B^YRUnhX!!C1B}z2DXhQ)Cv>xJ4{T_e&zR$yj{Oh@Hu)#OmANY{xr1^F1tVpVf zvH(c2K^$+GW#2tl?Hth67Nf z%YFI)z6166KL>vCI~QdE02RP^=Yy9-53xijN(2;ep)@FVa7< zN;KM}A654xzs$Ys8xSNLe@vn>pF;*9AssYCbN&vAPY1tbyDe~{Gou9smG150U*zjy z*`*E@wwm%PJxjm8Bh9Bp0;QKwQEB<;{;XzbQbY zarkmOV5W9s)7Bj{zN}<-&5F;yYVwDT5#mTJnamPd)b1&!k$O-R8^cF&1M;FAQ<^Q;Qns=dlLDNlfK*XaA+v;JbbXtoh>W<|)&yH^$y^QP=&^T0krJ zWBj4Jk7^x-tG((`Cq0f}J~uFFywg^s-j8)<)pU}%$bxO91npHOBGYzF_nA6f1ny&m(I`^p*O2pJ7C8$)mpml6?1R9~x=+^2qRgi(aQLWVIJZ>F8fnoIf>5^kx**&Qgt2;#4BMH>dR1zFgm#+mAX$BSE2^67~( zmLO28r{xm0twpe%hRn+KC@r$G?s_$uY6mv9$T%hD`)-iOTNl-pL0cnCm1a#yuKip6 zyWC49I{yu*tSs;lVb=D_ZxZ8@`#C;IDmoRA53WH;&aChV{^co)#l9o2=W-;62Oi$0 zg@>kM1tsDPG{Y#!HZsh!qk^q@j2V1Num{|Y%GR9_jh+=K@gyuFYz+cA;e|gM)pJh> z`3cfWa6cfDjalj<0y!O%X+Q_mb3b#DKM+wys*rR!T{ub`TFU$W{);zj8{WasZ=fgu za5kwW61aSq_;|8VAs8$N)g`Fo5=;Ry;He4q*=7!(9pLOt+HN#>|HZALlB$wNzekiQ z+ge+vOjXXqn$i)uZIJbJTV^wRKl}ehWo^@MzYXlP)UOQJOtXaLT#wva$p3O<%xhgx zZ{>CN`YMDZqjvUSV9QdY%NlI_{WznO$v_CzRKGqdFRbm;U?ZKq(C~U3z)tErMV*9% z|K@u3y=dHRZPjsHpW5TEx!}lN%l@$4vx}u!1*g)AB$UAN^LF9epWuKTYpO=+$vcvhPl9I2C zbDST*+LfX_tkXJZh_D1u_MPX7f5+ab7wb)uy<37n_gML(eq00GfeNie3R+^Eo z$p!p%0(r7zx7M18KF}+|p#~Pm9jULV(=lIo@8hgvJ;Ytj7~briDF*dlCZBgF(A6bB9>q=V!` zNA`!kd#>1@+Qn*rC0Q&O{u?1@cq$rl7ugiT{gn1+ zbLw}iz1uyjMw{=4&)es~Mmr@Y=g(!goOk82?M|`VbKEcdpSQft+3-HXFFU=Vm9sy) zA0t|MXAg^~!P|TP{J-6bqWr&(@2>5?U(){Zwl|xZKfGYqDvx`1{c3MN%!dJ5$c>iE zlIa>B-;iZ8mjO<99+n6h?h&sQFF9n${4@EdLO zz1z2Zr@2vq{7#J9ZEYwv0ZLHODc`_M8(72bK&n19#&7E*+dP*OY)SzThsNksz?zNA zyn);S$6Gw-ToC_Jg@oWkmbHWPwOeD#@1%>KzK!^xfzrLU#G{}9RH1oyx6&()qqUDx zB@zHvsErvn1l=Gw9bo-iV#_50M$CkD+7>GgMy^vM$obc)R3n1{s(R+r7J$EN`KxM( zvH~Qb+S&OFP5=uL!4DfAVFZOA5dlvGFPrB#i-)#+2kXE7>G*^|k(T!Tv+uFd{>}0)=!XGA(Po&67Rk1!v=6 zOW0>< Wg-+#m$S9*{_n+6e4MvIJC(Aju6CUeB^R!{p$@cFkay2RSr+-TMvGVzbi0ujyge#D3d>lg4>DbrTIwY~Q-V&#jVQ z)A(s4E!@H8hByz=rA@s*p{VjKa{b()ulFaz0l3fuuHxzWQYHAS1uAI8#l zO*&>$A#r#&e?7BAHG1U(rX6bO1}}-#kJ&XFV$%I9V$}xB4E_xTf6de#Ra$b1SNH{V&b_UAsc&k@oAuJj7H!lP>VK60571`^SA= z6Q3Wy&$qtt=CU(osy!zEuiI;06XAI~uw!wy3HV3-j{U?H07($hRaL2De_5azcixio zi=~$B$_rk@>eNcb>l6oNItOsuzIQ#sZf5~k)@#Jh;ON#uowDibxI!k~gUr^eti{Gq zL{_bE=ha#4`g1r+d9In26{}~Cj?Rc_aqP<8HllJ;kyPdBRH9SnWB# zv3x}pcNrC3x*U@hE#-Ky%M3z2EnHir6SoeBd|9WDI{8$U>H;iHsnDLJ998$l#}%~q zW}4o<{fyub$AD{_PTB_RCyjzC6}5h~;?g#0jyBC-Rk6=O7VPC2HMFWFWixH`A+wl@ zLeaEbGuN6TJ#@F03bVm7nqnkmm#sQ_ zmX&{G#R@N4y5)A%LY*@zo-_Ku+N!Qh)Ma(18L{*_`>`tR>FjCGcb+>5tEA69e)2eU zpTib;+}og5Fr7?1)%*q~6qnOiSyex1yw-b``1xE$>W-Pe1td z)qv?Xm(wB;H_Qe+vjWMt(JI(9vXnU`S6;PK+f-GB=_lV=heUIaWXw$b#fT2coxFES znSGY29;ym`xmTsP2{nrLFXbxqi*dwyhePW6$ycMH`{c~;aRv@-wvHQeT(|m$E*>ve z8w{{E?6u!ZQ}RS;`vR&8ijL$a_GE0j`RyN+Irbms@&!(wTPpsfVMg@sB&K1Y9X>n3 zudFgCZMP`bOw#A=vy)wTK4LdYy*z+X)35GjUKrR)9~YRRU1B5Eo%>X-@6}nOs1BDy z)GdLR{Mj~3)vzxIZhSrquH9RGl<7rjYGP0V&<5d&>cS&ao8nhgEqlKv3v{g3K+JcQ z2W#`2S6*vZ@hdqy7WmR|UPO)R(jYx{PT<+ckJLi-^jxdG7cO}>KN!+43Up_8-~NVt zw_=sL1ZQ$9Ix2vT-&xbBQwy)Z` z+-=AR=zVL7^!~y5um{(Mtz}JL8i}S(leT;v4Y}j@9GvS1Udi|1XkX}Zo8aCjtAopF zt;8LsaTCY8e%nqp7-(Pgs~0c=|N87}m!H*bS6wi=O0}v;nv{)h8XpKPgPlP`Y6p4L z2iWvhR~D;D6mnmTUTgdF-%9M=2u%XCEl11$-5-Lg-30}5e!pbY@-4+Yq)6ZQOX`*g zTMa0V=SnhFPoYgwtsTRxI>7u+egV+Z`N0=y13HAo3W5qMg`p;O1!xS%%O@1+*wE>g zshz+Ey;KL3g^Gmh)v0K?Xnd2Q{)9Gyq(n=j8|mCVy)G8ab!~nZpiyzE(z({B1c7H* zq17b+Ny!Sz`JhFY;WOiIRBk{~l{1D|7M!NCKj}~hT`oy%0ij(sM6W~vf2a%$l7p7% zfM?SP4y{o}0RzVRat?A(ltmc(I1kVOrYRUd1xd}^Fm+Z;g_LG-9DUQYXvx}uN)RSi zOrYdWLMq2f&UTK_WtTRl6s=?=Z@?m1E=YVsP^_IK>IF9wE?!Vi^dMG-rX#Zr_=(>l zwta;hhy2Pu!n9LxS~=5tAc|9ZpqUkOdQNXK^cRmyMiDPcE{nCN_K?M*&X4h<8&##f zJnw{BhDjCzDG5JrqZp;VVi;apf~~}nuP4??bVv26Ss}RGTf|lJC1v15CtUrrd^_V& zflf(Z-ktoyQl%UX8r%UKjG*K-PD03lNM?UZ@`^ zR)hqa7m^{7q~${T3^^Kig$^)=^qtz7VL%|#r@%ZrpzC=X0oUajZDaZz|2SsY-)vud^^}jUMgeCRQ6ku}C;bCr2^k4<9g3-P_CF0{# zVkL>yKo3Pec+5iLS%9@N>#RxQyE-?rWkvOuFKyS0HxTq%$ooU3{1%d`tAxTa3s_OV z#dC5)82&lTth!NlzGW-j6W@$-gnlA0#lG6g-Ws|h%1i?v?j}upQeGhpCVITao<6>{ zgQQE=ZGc>p8-95`EyTt3p~|vFOrAD$}m43XJyqfDvQ{?r!)Rvh}=PGnZohXHDaXpKCX5ngx zM?OPlL+r(d2!QH}QB7Swl&|8+vGSQMxyF`amoLyVb}4!v9}Cwy5nE8@2g7pD7buRK+KUrkD{?d%+|@tB zyC9|+t0{B5!MjzM&c{EiBCN^jfuKd}B?g1;X>zt`X%Y^jR4HKf*W7m%X?ry&s_CK6-d6dh|IBt=jfA zGpA|WMA%jl)GjjCHx)*QxLHa?#I=2>oJg)d&h!PvX(T2K=OsI+C?^XKrKZwqE2e;c z(bPsfqi%0P!iITm&*8Otgg<~`xGhHpxvXSOR!!1-4MKjQLvj>sg_-4!bsJZsN3)xje5@rLzX zSofvTj!~BOJ=1Lm?_HX2$<0XrmOd=JjcorlcNdq!@_=i88>BK`xyqF}(X7y=k$BUl zZF5Ql8`ObezgZU4DUKK}BAnMC`~b>Vt!eJg(L3=XL&p?@VpkeJg|@LTl|`% z;+a6V&fEj^vy74&$l2a&vzKu}(vu#C1yAVoBb4}D+HtH+wBkT(hof5}Lti#3W+i>; z!i6s)cCV;Bu{BlTJ_an%hIjr^dH`eNxPJW{rZ99A#370k;)#hx9(a}~BBw(5lU+v= z;R{0r>Vz&NEl~s!QZP~j1qe)o3WW{L;9kff18;yXg1Fx!u5^eXOPXN2UQqFm9C=)q zK&T-appDN9;%Gqw{scrSM9r20A|HUWC0v-b7nmvO8~iI~b;l#y7ccGG%w9LjJX#(p z>^@om2gs4j1P(X<%=FJWFkFsoAXET5rAq!3u8e%CAbtPbI3632;v+M7LpSr02}~gf zobQypAIg0jM%%IofLO6yOi`DFS>@&Lww|W4t+PzLV#hT-XtVzsx?eeCwe;n_e5>~D zj!%A3-aHd?A-uXg^XxhXc^&o#1wK+vQ&?OET>$Z1NJh$ zo&D2^-TEOW6e_@dZ>7x|bc0=!kwGUP$ZwEwtx;$h({hwTQ_}Pbw-IEdbY5g6gCHfUZ&1N z*zk*Gv%@*vj62^somhfc@QgB5fh+tz_${79$uL%q^}aPT=BakN&eX;`0V3v`V{b-g zp!jK)7|NNOU&sNZQFi$Lhg>{@papC~JwiO6h5tXZb(A~9o}B0i8ue?S)YEMj#Vlp? z!%k$+>1z!9uFA|sG#}>-LW>J82n}|U&J$gjoYLkZrzpX|;bz*t};b?`oUx z6cFgIfdnOyfm)ux5HskKqd?|T$AA=J1djWm5rK+`)DV1Ipt&*Vb5lqum4K3k4Y*&P zt7PcKQ%MgKB_bfOWfcXhqXt99A z5jWdphB+uO#inKarTzs}GL}iC4@I8@X^HFL4o*$r;4F3r@{8ItOecAY{Ug<8tSI?v zqA4Mo|AbyFfSF7z6q!2IDqmNUa8VqFda*w0(bdG21M|sKNJ!2buQF2Om5>c`A_tnE zoA;$YELGLbrpwO`hiyV{&BqG!1HlnL)6FI<7T-{gRskFJ%f{~Y1}Aznb2#j}YkkC! z0%EhVt6^W}MSk@l!2`aTORo?k4lTA;s604bF)zW&+6oU4y-K5GM^M~3Fi``m*JM2^ zbx#3j+Ix{|8BSKBt5ysG0)^2opaWJXss0I3kAa3z_eHtDG$iVf6BGMS~r2dNd{MzIz2#@hnbBPy-$vGw}Q#76=cIpYYmydknG$;gKS zS#n3sYw3>$FN%jk63W6NlC-OS1?kp(408XNUUJF@D)P|>8!+oQOBrlKFexn~QRxaC zs@I1G94a+mKyr(Nw=x zK*XV1WtLX9CQXAKM8LP``zJl`noR9iwQ$I67m3feGEjaWL-;yJfQh z*Dn@B2T+(K%4Py-BFs;;bM{M1ddg=4S-=mE7ej|Q0ycM*&%|<jWMO--i)!Wi@aUEgt*yor6gf`jd-)swUQQ2m=QC&nF z!1`!C%U)R=wQy&LVr;iicOSqgAt*&pw;7+cC*6pNnfdY54 z+ct3<_pq9~x97O5mRqpuM6}_<= z_Wfc?oS)-noA&>0z7W?-e>0YfNpfOBYLJw^?&HS9H|_d2=YyGVin2M|WSGg4B0Yoc1zq7U#?8L`U} zP<))61s5FD5hw;ur>%9stTE9McPLDGRG>)GZM1Pbp;tsh*nnf9(7F$3!0$Gzm# zxy;R`extzS4r1Ah-WF*FzB%p8GW)pYf3{9yXfwSw0kDIujEJM5;ZDh<7sLu1OnKAJVT2;X|(ytJxH7WW= zhqUVpR2H_E^U^P58Nv*6d+xV&4XR|;Wx%N5L8!c5xs>0b5&8N6HLuge7S>p0+(Q*` z#s-Tx+?LMs)zW^KOBGnOJ})m(N?RU$zP!;FduLeHUP-Cr(T!20L2-3=eII+u;_qB9 zGO&-!)wpF~%+?pSa<#6nb~iBQE@P-G(moT^+s{Rwq2!??*1yUIo36bOqfj+6VEleG z+iqXB=nP77t(tE(6jfa;O+D61+j!<=V}I==k5-lUnQm0OTy}bL609uy0NGi+Txrs- zGiue}Qc5X*-tQHJ&x4hw)Ki*CbCZvq4ekzmI!jX8ecPaw@iUtlN6nx$&==P+K(4k< zaV;ulxyyp9^D&!pg`d@kd#SOXFUQf`e$rj&f5s;HgPA;}`ki*npA0)8*w??IT4U=s z+{5kLQ$mycoYUG?dYsh3_ttnbU&(U-Ych%p>Q9_{bcq;Utw}3s(7mQjZ@zKPxnhFP zSnt-`;bPDw^D0eY#9ts!TEoCKnUZecih8#-a*Nuc)$a3p7?VnCPMG2M(*853YX?Js}*dJHFaKwBE# zOtJ6p?O_K0M?x6pV`R~n5t1dIlL;kkogdKJAt%15yP=Cl#3Izz$VQ@(CSx6;0~ znt8XBxjSQ)Hhs1&9_T@oI5O?@sdv6R=$Snv6D}XcjN4VtV3FQ`tpu`iWxhAJ*{R1i zd^JX+yT471v2k-T^!vRMsYCj7r5Xg0@DGfjOuWYvc!D2Ta!#j~EWnjZ)ugldF=3y|NCJotyd&Kr+hUYw zQGo(O4b3yVSUVa1Zc!LIZ6-#sNs(=n&wcF)#m;b$Ue|Xa5k?k z(we-_jcD@|1%VWNn&8`KuupTkXt+oe2W)OA9H9P*8u#I9#-b5Ccd%fl23pvk0|6dR z_Ty?6#0UVG0@eRB1%Uw_4HiJQ0BsktY2QT8P`)&>4)fREe`Xm@VADGds^w^Nnzn&D0f(B`7l!R(nYn^Se-M`?KpX=SZa2Qt*669 zwULP|)O$oNsQ(^3z{lTUfRA!_Y42jABjBy}TX>l9R)yBW0t;vnZZKraMzqn=91iws z&^)fFzmL?-a9g9yYPYCwHsx?k7!qI$Xfgid3$ydPwuqDhR#CBUVF#od{p9=%RFig_ z7U{Ynn*-zXoJ>g5)|Yd_Q)eyP15hF;wV)#*x@uAM5KAaV)>`t?WmXf7SA_FmzFekh z@7CCx``;9ggV^*=5f9)O1xKpz$!5ew>xKQ5>!+AVf;&Qxv30qk$M#l?L?H}hNLk1X zu@Z=>2P5DzP{fNTcj}2nM7!XPw0dii9SJ6MF)kIN$DS``{pef&@`8e2OA6*%(eq)Y zYo4YlemcLKFlhO!CN{3-an}-$m&7|8gjVE05-RZi;yl=+#dN~pVBqkkh@#S9#Z)<2 zfU{iV{lcHc42l@It_)q&kNmXhh@%ZR9xifkRDL|9r!bjo;Sts_2>xYZ(*OaBt+pZMgm3CK>rsSinSdcyL}&83 zsvWORIxxbQ<9mjr9Sz5H6W4+}F;xw;4m!AMhxn*K>P;r*QGpHaZES{G_S&B_q;W`h0^S>!H+w^#r`JNh&d(Iphq+1;=!?M z<5iP;5%6(^St!`n>{)T24*Ub<6_JO)2p5HRL7c~R!6w-lH_sbhqri37P+6p9w6)1` z8>~`)iw@PTdrX2^(#u%>1SpkU2&8s=Eno+yRqpz7-JC}gOR-nJ8`xYa6Md7&?-gStK=S~C)%Y;RKc%x$>m~0 zp2OPCy16$B4^PS39GnyDy!sM>Jpng@o+ODSqsiottWL%8OeiH>@Gdd~CVwS?%|IH7 ze(h4^WT^oX5*(nFn`%n=6XgBN??ZFcV(S|+yKp3SP|iig{dz(4|2#jv?frb&E;IOf zU;G~Z^?jLk{p;szb=PLszpv-tJ_1gMHDSdMg1_TCC&TZF8v(Zjam-p_jz&88va6~< z^Tc}T%SFn|>P=tvN0~X?+I5%|wVcVKKZ#YXHM=)pK@;7q5w9PacT)!_FokuAu+mlB zzvZMD7)YWnJG;Vmg~iNyYdy`pAle32zLOJn@;E<%O`p@LB$8czH(A!P>g7|Juzi7y zql6Fx0EPIsd?EbHXD0WD;>tT7Hbv~lWtyAlwS1|Ku|<-_aW$>hBzkDj;w^fpmh>T^ z`kU-AAISpE8?}~84NnEjZgNEjC9gfL)&wogJ@zentmlRjOqJJ}Qd(_oS*S>`70;rI zE+F|R8^UqdBg4U-FhH4F!&F@iyf(s?e2 z=&GHITF=v&0=)56N8o|HhgF4*1q~T%rQcd>0`(&R5=!^HoHVU0@HLO3#vbFS+qZ~| zMa<1X*NyX7+QV+#0a)JSZrp2Q$9l)j`Ajqz(#L`(P#iUjSDe{|9iT>gixc#HguYY-MKR0>h}{YV7hq7IAwgTNp-V zGiQ5OClfPgB0fGwMJIa`6*Cti9Y#e_aUw=lGY=OcMk!k(b2H)ptwsK~mePY^6!UPA zP;oJGG5c>*305Ljz?jMcfDa6#u&})ckq#5Uz{1SRLBz%e=q}-8@9OYh*Z=lNk=~Wsc&4T zrU%WlprzE-mMx3GazEmHk@42k` z2P6%wqrm@V5^(;XNgyIb#K6kN4Z~<+^uJ$ZMTnRHr&IcWmKsFNFpP4tL~Hp)( z06vr>Vw4BW5hELbGtBHv?TxKqSh@bsF8|-$!_30T%=Z6u&^18&o1EKe=kp!?-2!$^ zcZadxI51-;!}lS$7lSci7KTuY}B?(Gy8*)-nZckY==HNVO>Z!L1OFAYnQ>6QRQ zaNG9##6R2Ltef_3Z~wvhe%@c~Ijs+Kb+?ZG6aQAJv3sO=Hu(25+xF{w`z39&ZySK; zJ6hOa)LGPb+S0Z0_y2kS&H2#>5Mf^N`}*wL^81wN`=0#Qn=$R(^)36)j*+vegCE=6 z*=5ttsc|O6g0qcRN2JAbGly0{%YdURO>(H&xA(d?-86gmia;8Q0T}nO3=v(I7jQS}FF5 z*36v3Wg2SNume?9pfu&#f7P{3Rd*lvkhe~9z>+E-`l(;`Tu!xF>A*%xPkq`p3OA4Z zm~MFQ`m+bNoQjs%FK~oN7OOucbIg|`Uz;ewDOEiV+p9RrR@GR2(AwnL=l=8Ukh7d> zD_#0+snV;-SZ>ULoXnw>@gY`PMlO2~7o4G-9Txfkfty{)tl}(Jk!SzU>+|vY*51D_ z0Cm&A4g_Z9xcz(UA6yI4z;&tmC5O%EW%`!zvSo9I&)?F$447W6mg)~l)Z{{3lB75BL2w5-RK zYlcxULN8qRq(olTJ=+~;$(JRp+bIHFp5{&7wo|LaK#|+O`a!gmmL6#@%FvoZPCF_5 zM@R@PFSR!fAs$QNk2_ZtVIiH)ro1=B$KqG1{e&~_EbRPBH;NN^P<7o*f0y(+UG?zsU{L*NB)7);*YB$I06vpuoB<#t^=3CAEAO z@!3iIt`xl?<1qON6$9gs74VcaV=3tPe{Yr{{bjl){Aq6?WDR+{_J6kg9Q>ynQmhfq z&;{?B_3shUe@i}jB~ez+(R-_Va&wlm4KlWJLOcvB+C7&?8rO!|?~&nVSWSmh;sp!T z6`tXS;ZzvpscXCx$DXrs?F!}Dy{m4V3^QF^NoV6r--bCg-T8@ExbIa{1tBNVX~&jh z-AIc2c3lf&vYO?%vFoZQcm$b#RGXA3!D-vJA&24$g&o@f1}e;AS?y3Tpdx*HWa>&@ z33WMbtOlb5iN3kqj4IATuHzclGdIJ$%ZR(=F@`;rpVwU1zOK2(tv4Zj!UywcCF`eZ zymB&>D6tRt-E+`PBDuM3>rcM{Vvb^+K8Y{i(g}T&n3ePwrds-ET^ersfQPeoR5GMG zIC}7R3`I~5f_i^ppk=c!GU7)>1aglh4BofAYVZ#=jf= zzzA=ryCHoax<_P74*n-XZ_9g_9Q6+Mu~ziQ;e_Q1^s5N`bY8QhndCqaUFcJ%#L|E9%mZP z%uH-DNV`>y93J_y`^2f?=rRflTj(H(*EZ*jR~2_eWy46P+zGuS8(c0#8l_Ca-8tSM7p@Lz470ImU6c>Vu)+UAP#y1_iRHULf66xP{fnU% zZzym?fcF&qZw9vPuf$=dHx+*E`kBi%_wD+95Vrjf0yWw9ty&V4V3c$XbRt?Ix)7>1m}-$39{o9gNEq*jVmU^K%yU4|7Nwp60DEUFCd6YdPauaa^v`=p_x zt7>TQ;?wgel~jRL?BLSl27hKwe|I`G?l@bo)VhaSRS|%l*MW54?IJi;N>t_MyD?fZ z7Iqe#sP3K!inb`11!Y?aRjO1NYxF55SoSPx&4`vCuGLM9q890wsntqoWwv;$MbOY$ z?d1!+i8t8vs7;RCg{7PE7d2Kp^D`*xv|K64fBr2O{@R=%yjVv-2g2Z)wc6eBtfdTJ zv6Yv@Wqhr)$1?HD$~o1T?)i7&`ZF)#cX{8-{(hZa68oq+sGp1IDH5icvg+l$>@w=q zRvWzTbf7Q`#o!)(DV1Kw)7^Uc_G8^f%WvMi6@Erhss7XLCq4T5Y`V~vfunsx*4I({ zx5roD8b0{btfmoW124wPl@i}?R`$%=ib%#Ba45N21P8y8&Z9L0{#S(OFXNrK^w(?V+sncLmehI1IvJ1PlImHjK9MSc z&YQl3+Rshhs0RXHBSjVa*(~eT(NJw?HCm+@FaG&2hO;I-@N!Zn*I#oVAji*U1|MW! zPQOZ6+Yw}}2;8`J7n$$zMGIe);t0AcNlTMZ1>AJ_7$H8^gzl_z{!L#IIlR=Z3EwfV z|9Iv2$hQ7_4*L#+4tow6{XjcyEZi}1JA^O#Jbp*w7u6IZ`uFnuP5537?*2Ju-ox|q zJ!W1IJknN2tWTI)#oMxU|G}!UzIdND_eqaw5(pSzvMOB+B!-o%%36&@p*k1s8w5RzV>|6d>^7^GNHL+$DPfB(LUA{P~ylqaX9KSY|e}OYUYs z;9FyT7m0uW5q(=E)ywO+<&>+DH>#4=#iyzJEAX7(xSH~W^~u-pvPKzrFL1{H?VKC4 zXRBx2=zZwXuL zNrsMi-VC$%W*+;Nw*E);8qjvwIZ9ncL~&9Htc@*PQXz-F4QqG}!p)vK(6EEu}aiuoywq zR@Wuq(|J+jFK*DksQD`E-fcwPa6_EtQO>xs{mi?cZ`N?tg4Rb2*>J_(<*~<(@v$vH zARHN5JGX}!erLR{=kiQ+qUnZfYr%s96B&x5%dwxBm8+1OUJvUa& zZxOv<$F?~@w9zIB@-0XfM!;$o)l04|NQh<|8QyArS*y`9BshCMLGgorj?i~jfVX?| zk&jEyVqiGP){9CO<|YB#wlaQA%-qbY&69GA9T|9QVR_(uEs%({K~Aq*laeK4d?cgo zWT)DDNG9G{JiM60EP7a_c*X(WSzMCvkwWbsRzv4AKGZg=5ep+J5z7tI7_j)!DHjGi4BmqV+wayZLpFMVu)t8>p1 zCOpDLOD2*?jO4^7i5Dh&%tcS;ubvpt zC2+vu1(M#r|TyWexp{c*<~ z-}mFG#j*CNU8|~Qt@S)}0#?N}<>pff$25w%Xikq%F+}!k>Fv{ZQ8f4P89zNx?Dndd zFO^{{HnY~;crepmLZ%xDo3o~uG4jT-SK(OorJ{}!r^B!)4udMD@c>k%&Q@K6kUFns zIG-YBp#kKg;>g)|l7<*d1+!5|!)@8ueZ8*72r7jTDrcS?#$dE*0_L)wr5NdhSNe%d z^!~&mTkp+q^G-U-dnLN>@zcLh>vp1n%1b5ZlJF9z>}{@NNzW0TpB ze?D@zZP9My`H5hI{z|e?6K2c5E=$H|+Tag7Aq#DuLRuxdTtlrZUO}HYj09+|1ff{c zu4S3v-ez~=-T_b=*XA@z zaGEQ%N!Rn)5iixWgroDOiY}wI=A^{^PUKbRPCSdy9u$=~Zw`(_dW&Ugu7v~gFSUjB zKjCYV=b}}*Fl&;PNVfyQ{a_CMupKP4#4aADu^nVzTXQDMl*Kg+^?jV{XbNAan#Fw9 z28#8;Q>L$PZ@+K(|JqWY`ySPhj95J8SnrUlIA0xZc6r%e?4)?ho7jE!v!zc}+>W~o zxR|y7b7-plkH}`*v#F~1dC;?I@?01D%EOTWCY3Cdptl?eyo{hWH0M9NfW@2v9_#+k zt`J=sH%wzkMHDRGzgxq4e}MYR#3i22$R*C0#eSYD1(d6&*xZ)u)AZuHuP(?)18l@~5|v zOOr*jyfwvoy;JD2Ag)v&3U1+d1T$cv>>q$yLgJ*WNcQ&D(4k(H?8(pI+%yO3-hhz8 z$`rKDLsN%P0Uz=a+J@LPceCj>1)oZxFd4PLVkdRxXcXxHcZ6U&S9s&WQ|=rD;-!=T z>SliOkHQWg$QM{a{`&Sj;McV;zTos-x#?~k`Qr?Y@n8;6!rpUA#dn`k+omYDX;Hk( zqN3+E{RanN23P@kt_VEl0jv2eD~2md7Y#;E^ftu)Rw7&Xzp_aRo&wCFF7#Y;opp#GoV`>}7p>!G-FfjkZbanPI`PME+pK_& z0pG0$1)>RZJZNyGCNA}n$vMd)$@5IH-QQNj$5DE|vZT=s@)l&oMJ`U@E);msA|%d5 zk4~^|Vrp?!WltI6p6#x?XeEF;u>`8R;Fg%dGHsC>qrhT+qd%d@^;SDLc7bD1er97R z-LNHy0bCDYUpx=g0Uyzj)>|cFJ|;Z6)n#7ql^`5JWL!48$z0ZI+e4W|6E_(z;rlNc zVwudNO9T@SEKMbDVoamhI_wGq1Sy++h-kb^1{SF6*z>(DaJ6@{P}Q|*iKrOR?(Svz ze-k4mTX&Ed71bKEz;=Y$8*5=mMA?5P$+uf?YGD4g<56W>Z~!wBC&#{lF5k}!&w&(9 zv^r2FRIW%rojZH?O@Tm^m=AkUpdoMIj)HYBAt@YGl4^8eXIM;JB={lDHlN>b$4;(H zXF`exstIw68cQ-m1R<|R)Ey#|;h~ZnuhE|x$m#Z*E8J5wawxjc;l|VIP#9{*f&}IU zQ41UmuN)$Y-~^HtItTm`K{)3Pebwxc)5|=S%Ma2Nhe;tjb6Ea-FzOHC43&Z}VReu> z_`(jj82=8vSj>((iPKGAA>PeG;ovS{RzeF;rJG1s9Y#Noc+v8`H!y)U)}c<2cy9H} ziBNn*F{nrxD$6i07~Tv`Du;fUl#kX_O#X8;E+W5!ma)u#d%Hqx+GcR6Ykl9{QSr93ZN)3s>ey>@ z!}pd~dG?kUFaxanQW#7vKL$4Z@yeA3b^zu#H?Zm!fS=~Ig_!PiGplJ?wh(V@*yx2A zmU_{|$d5ckk0TL?H}O@ok!(RjC_P`k0`*_}_EJU( z1#O5G%FA1DWuYqF_B-)5_x9d~s+FE-Rlc54OYgo`v?>G{i`7{6ig*#6bTI`SiUEKt z3Dy&0GU103TAK$RWw^I88b!aC^ysC{LZODUC>ZzLFy6wa&AYz^IeR%6URxn}H8d?9{uM26!k5Q6KU0YI$0o+tW?G-c1V!^!AA74rq3M%@=4%2&1~A+n zP01Pv_^iiqUB7rXugMiV4p0<;R{MW;b(kd9ee{3Uw3k_qFqv@#pC|+5s~0=)$Z;7b zb%{pqFq5fWe!6CfmqR8o=jr`@K(z>FJqI9?PM7IospIAOi%>lV#@0f{Hg&3SYVxG{ z5TtDa(EPqg5kvaQ`ad&3;%T=9T5H%re^pnq7}6E6<$vt|+fpm%IYD|4&J5u>P3p=p zlkYM%pBIYSX^Z5|>{Wlx7#aH61fZtWEEMR0e0sLU2k^8tH}&TD!5OE zXb?$}<&}mptTT$nA7g|y>E^_#MUDExb=&-q>6;5rvELtcloy)Pn-zI6&q!bO58WAPKZO^T0$2 zq`9b$2~^k^P$Xyo7Gx4z3oKxYbESb4bXrAB0z3Q>H)~0lC{B;ycvJnmzAeU=bl1OQ zF>RGOFWqdk3uW)m8fQStDte7-T$C^0P%IS{Hs>mly0_b>_ZN> zygqw@q|aLv2ZITs4uQbv*Mv>T&eNWZYmM=cR(h=^u(-Swda6}nod!Pr;=EqxkE-dC?Li`_8lyz&TFB+IGO4{psC)6B*_{YBp1?50Cc z^;Z|AQi1YDP1jET!>kh-U049jJPf$dlzQsz0=V}|F0(Dx-55Dq&48dEY1Kt|WY_-S zj1*lP<8>+Lh1!Pm=Y*;wilU+a_X+*XR!*OfmSzFu;xSgyM!Y(91NHkG1U5C6kbE0+ zV|{ZJqJWZ=@`lAs*}v361mQqSm71MnFT%9z^OP)$4N@&q zanlr`58j}j#A$SSycf=3Lw5@gR`U{;9`paoj8x*EgM zn$N_={tGx+f$QL5z{v^^oUEn5$-0ni+8mIG=o2}H^3NI$|5ITxzkfCCWE3Z|I-G6aDj$ zsJx7fd}WPwt7*NInxMo7S#v{hmAtm~vr$U#lFxQhdBz{PRw}l{XGxC}T=Ug(NZreI z$A;-PsH)})Bd&d2R~tn$I6JtipKFIs+De#S+C`XtjGzFsnXSP6Ofexf6+hWb5U;U_ zWi0wDs@h0TiAihMs<4lYex2h6frz;TJdd>mMiBDuNz+P#i4gCh52jf~koJA6f}o%! z|L}vTLM8vyDu}IJ0POG7xhob*!)kEEFp~HW&%IQWx(KZc0r4MCQ4zxis12*W5nBH+ zQKP&5W{RIj(uNZCBAlmS4{ncsFs*JP6fn*NR zYu-%|8XAenPq>|eDQfmY4~QY*K*G~37krA=`ZQA17!eXwI}2elEo>vQ)!lya`jy~F zqqU&O9dK~;0Zk~X5KU03kZ(k}AD)u%djQ3ezk{QWCARGqj)wY%u;#r8jzAANRMb%T zy5Yf{SR>>2aV?yG93wDWdi4ob>7-n!vtkZKx#9oByF`r(O8u#6DAswnDRd%R56D@S zS8QyZB8<04F`%xYF)a8Bn00>*+SMs#Z04RMlppl3vaX+AD-xImBEvTIkr_X^L`)cK#Ubx&adf-SQeo#mX zEJ!$J-iAgf>}*C(X9`9MnE2ToKEWdF5Z<5I-x zW8~eeiFi@lV*E>P85+z9!~%p6b7dMtB~8}k02?2!dDcW%q=Ec6kc0{tJ#pchI2XTx z_~sZa3+UebWPVms_UDxzvYPzSMid(RAI|muZxTm>g46oGcVESfGdfM4%I5Fua@_Xd zR{Ou!dgsokkG(Uv6istA-EJ_Z!98+tRo&~3M*%fh_EJ+HCoXmgQ>WuPN%v7ZARF|P zugx&}!x&96XEiG(7N>eqd^7BO7kkWu{LIGci>$_6cG^*#%)y>INSk&qpssF7I^Zee z`^Axz$8g~LyW}ImAk@;i;sOfZDo9Yf9p!I_-tA0lMckeN{IL?CJ-+=9x$#8>Cm+%l z-)6r%?4a@g6hFKkdyA)rw36N*Y@KI|<)Zs?S&^k71dp21&Xe1EvIQ{gk$stuE=%|86s44?*?fmeb4`wbnWfUEx`1=<%OT3_vs1OtN&eyK{D6zQA8Bl0b7UWy5Tc-2V&7VuSx=|+9%AQO3 zfMLBEm3)5u3s^^B>5bNM2^C?wzy(-$2;y!Hm*RcyyoQpN)Yt^fw&Ih-A|5|9T>{IJ zHV>|L(Dkfk6$YL2y?bwcGVL}+F#u>`p}jLrrRT5da}z9h2=2>fYMK!u2*X;q@=4Y}ik<{P&jmA4rq}ghaMLNW}U70Ex&0HZ(t@v4yf($lk>br#>0Jl4lPibu*mko+JlXnW1qk5fJn&PUDbF}^K-zdkE z?T!7sRW5P&mVdZ-yP$I;O0!vf+H^HL{bgloDcTHaK|0$u|F@Z?)z|Dn1e3*y6zG z5F9xI{Qlo$EydeEhM`z@&zhT8do2y%4`3(Aua*nHeTu7{dN(~edEBDJ>JSQ>`Meac zEJUrf7Am*3p>&zzemC}^ovHOnLAU&a#4*zF;LyqdTxPmSu;2&%SJrUkI6;Yu5h(CD~;Xs>Xgg}c$G z55piU)OjBfOE9Zx6mg#DUhYVBw)vy6cF&EeWg8tY-4hwVWmdW{n~t9q+6|+@HUa(~ zk~M%Pg+E{d&1AIE&*3$Yfb2DyK>mU$O~CJTQs(QhlJ|0(3{Kl=Xx#t43qk8XA+B%U z6kxXLk=SKnCSQrfq!hnNEC z5D!#Zu!=goo37@K=26uQeD)kt*!I8&&NASErerUaeTG<|npW-SpXvBTw^Q#%#lfUlD6~^A=PIJZiDWIKT$^R z9E-yC!1`7#t0OJQr#-_PE?6?1b46fGgDI3Ze8QR2%F%0TqG1~MlB630$YM$}#qH@C z2DJt<5kA+5<~Q7qp#Hnj=YQVHw$#AiXekiV!_<$^$s$@zo@pgA^OT!jg5Bwi(uLEe zo|&t!rWicV1B!Fbxz+6GO5UpWbYy|Fjtftdh}+FBnPe$e@5&KcZG_=eCiM1wW*{96 zpR8a98?oT{%A(zEgTD|V#&wzRl=qso$(J-97h})=$P~S;KgHM3t~k3_yI8K({{f;W zBevphYds|vAbcXzI^Th~!1IDa#+e6I#8t^*gxrJL#*YFeCWOOq47kgRO#spFTlzJP z31ge01*U|393=$WDWk{Y2{(U-;bnYMf&(-co%Tmx|ZY6yvB3Jdewa5N>3ZPzTYs zq}^NkwLif1B;2GA^fkcEX{wT_h+AMop-(6jfXRxiTqa7FKwHN3Bl_`sVU5Hv?A%4? ztgYW%%~=e+;p_KEZzV>tXu|qG>WQzeKP@(de(r*c4wh@!i(e;*yqfRb=3Yv^zMI%; zT`Hgww(3HGnwR$mr;Z$bYuj$9Rak`{uXyjBh&A*E(cf_+!&8tid!#dpg z)Q7Gf;}jho447Z-J~!kIep{&j#bZDBsPy@`mr+v`vA)ZLt9bEg=?vZSP<$YSN*O`2R!pnQ^37eBWpqT%zWR`O;t zwJPWRQF+jX`Y!hAX?<1gx%^SXf2sMLx1VWf-q#HKq44^iRi>|z$WDSnks!c&q=4(W z-RxZ?9Di?vPfuN!E~T=f>d@g>(^2ny^B8}Rfa6a6TS%bnOS;nCOJGGj{+v7C{6Zqvq_!sPn~ z$wtk*FRxpjxV*fZ;G!FJTCJgKV4M$R-&Za&usC@*Ye3t*(z&Nib3f2o9O9ub#=Eo zh9_^!G4r|5v)R9VhurFIG%Hd1EW`_;g>6<^Axf_|LECncUY>6*Vbi4D?q244{ z*!=ikD2e%a1R7Lpo0eBL~sp6W+ZJxUIRFvE#ga^s`ZAG}i;4cT@5-l%0NZ zp%Y(KjTKDuZf%>Mz}^J8%6}bAh2p)72TFB^@7yXAr@|`F#My?&4NIO|PTj7NCRzu_ zG4C!+qvf-z=ode^p1-gsrQl7Eh*Iy~c2M%5OvfkRxbTTHaM@J5?HQCMD}^}69=Rfl z7n7@se(Nag()+c`{EW3>&286wK}b`}Y#si(KF+>`oVUht%TkywML$vOCFBz2Nt>C1 z9rg+=Pa1PelKXFuEKPVaJ{Zwfw9Af_h?~lVIc`QdbC8tG&sWaK-HDAgWu$KrKVxMklo?|_40JUDXA0?Ol82T;3 z&~pS3N@%&eiJcJP^)fcp$B|mH4&s6)ifSl!<;e> zkeJ1!_yDEhr!YTf*x%h@4Ba9yaD6UtjxRt44N!0MUIlCC^8xvnSxz)G;bEbpysjsI zJVftZ3f{+q!>7`tygkjhq(c8U|*SS9zXP{|Ym8@Q8{)9&F=Ji9{6SPBjBdmhgf(q+tjxBG7Ve;RrvS%Ao}`7HN7v`45^Hna^L5 zp1cv?L!N|(1PK`oL8Ce@1U-^J&I=l{Kx03eL`*9*M=SmM{(@pxokFh=@wc?{fwR52 zaeUI~-F(uup%5t*wZ6ll@BM7il&fwvgzydevwkHr1!LBtUy;GP{HXZCTt^WkeOwDp zGM{fL>?XJkDqfTanrHR zgA!v@K#DU7BF0=5`P(QE9DYCn8Y4sk93$isnF4|sR~TlC-Py@!Q&_}F0aWC6k2=IC z_T3-h;PfM!T9%(Ff~c z3mwtgS7Co9^x=@@{V}@Szi=7A#-Mo=nr5Ozf4yP7RC|CFszc~+vYUsIj(ZB_p%|SM z5XEE8+w{;N8M18Dg8%ujshoV}H|?p;93(4rc`20Vb}^YcqZP~!M1Tryv#tQRJ} zw&U6&C}aYOWSDCV^+|^Gc|D)A){SktfYk+=%MK%W42U!T_;IE7E5bZd3jXO9k%0yozlh(Kh0bQ20`k9{=;5@BL(xPx6OBGX;&77q~_})gJuugc;8FTYb z!>bbP5q`qhYrn$f+s~@{RHWGew$^1X6I-`lQ`-a63lYUcMKK{0+Z5FWk^971%{FSg zSU3wr~5`}`V2e&iFN4?BVY?vdryol=f)AN0=f z=%EV@$n8mqUL95pGgbI?ecr{NW^vcx?C}wlAjq8KMxY&(HYboC4JZ|qre!Y?%Z;2g zRz**y$Kf&=BZc>B`j$N{Eu8Ls|8C@0u4_kubcg#y=N=+g4g(tVHT5xN$++0OX<#di zNHmzcH}<9M)xxNqtE0p;4%pB!MK*dDp?Hh=ruUm!KKf@`GJQx|bk~K`(%x43O*YM9 z)|Bg2^`U739g@T-uLX4xIDo3Me!#al$H|`}(c5D~FvU|HQUWSs<_9sd8LNZ9|2;`uN30~opU{~Gea%)-L;{}b{dFdj$A zD&yRVRZ@5j8j0@Wvd{#>R>b@xwa3f4IYo(pYND}K}omA!UpuZv3PRxAU>lc z7lrpsOljsbyzQH|vv|K`{F+*_H|*2@a%tea8Xu1RcHb%!SUTf$RCx!6d$_#rJGKfL ze!1Lsxufb98-6S~>c@o%1N&bm;WUqwf9z!G#e^J#v7ZZKX_QQI~ZM3dhwk)4b#0?)5)GCu)3 zitR5v$14mDjv*!SlNrL(cn$c}n%)~X%F#2Le3A1_1SnqA&}nNV=-9{}1yqu!?|28= z7mg-KEXR@j4u+9nI_x`$DUGHFe&1I?81avjBr@cF31qtrogU*E@P z2;BOYUjt?YHrKh_y8gdj_c6!fV2#sRC3uj3M6$FV|I2R-iu0r=Tb^Cxt~i77!G1Ze z9_nXw0dy`W>NiJjDC^|ee@>|$_B_9GE%&w+vqCulBmT&PJV6zRXB*Xa)pbQSvbHR$@MQW`1LmN#lLZScu zh|d9+If;kov`20&vqzp8wMXvdt#`ECsdvv0?Dl&eUPcVVjAa?I7y3Fi8OPXQXs-dG z4gBzU&1D(+4tT#6VzgESlq&U$e!t9as3H$yUy1;|$1g>+FHQa1>u&og?TPX=oiRD| zIx0CHowzQ-Vvqw~ZYB6r_AdD!e3Cov`mNhndL!1$i+Dxs+xxJHbWrK-Lkvm_I4jFJ zN|QVYFO^M+2_tQY&-j~}E}pAa)hz?0D9&=3&Tm(6t~z&4ObR&M6;re>gQL(~xn!%0 z4&u-k`81CUJMk(JYK#5-iOvz)rY>g@pMSS@t!CRlkA}uW6C)cO248V^m%6$rE|k%F zEj3s8=xLUovwhPKT)jcK3rX3Zg{y0d0cnEAHqj~1=}FuRi&bunhtXF2nkWrCHH*(8 z%71C5_n!COy-hd?pf%07SIRM=8+hKcBY$l|x>r87LAx)vL{&~L38tBH)iq3g!kpz0 zm!qpaj=1P)W703tFjO3!IUpyUpGoF^@q#lkO>aL08;+*Z2~;ZUcz_kt3E-{wa8}|M zVbV?7BIocTb4WK&Y3?p;a^HTgwcVg6VbNWw3#$9Wr;@83`9RKmIkjzNR_I~tyN8^| zecy?0SidPw@0a_C5ihYt*XFM@TMt%rfmU)?nG`51Npz{3(wt8Yxl=a%8wJWEaoC?v zxM-(WN8j^WJ4(}3SC#V3>$!Jvj2i1do>IB%vz4#*7-~m3T&bn}s1cvPJjL_3I;Yi2 zC0`UwlXzC2aBrsQJX`m*5BVBe^3tpgi>op})<*`f>B5`*_a*3xey&p0#Wo3=WQbE+ z%{vgEvjVF*sV;92eIxNW(AYZ<=)DU40qtW@oWWV4vsled{rZHErYTi_Rciy&RcNMg zsrTroe%|PSumpW>UR8NgEA^t*%tY<)0Ha&w|8KUlTt^#If0L+!))|wsQmRm(-X?5c z--5!xl%eA7*SX~1@_R4DFx5j-f7QP}UQZnJsy4*%7Ju-D)_4R+i!0~Ni~iO7X(<{o z)D=BWcyoA{3Mc`CvB}`?u4rw|Jkh8_wLG!o3DqUuv#_*7r7n9fAuW#vN0`M*+0#cIJ9VfP z)86yw>%+G_{OZrMt#I8WpYy`aM{Z&!--Jl02jD|MJT72=_Szmo#A~E7jCj zY*dLSp2c2!d+uV}!UwiJn*P13_4aMa?-L_$9Sjgw5iUZXQztXzD|!t#8FhLtRXUpoZs$cy2TEgRa2cV-f}jg%N#H1;BfzZ60*k@dc08$SylvL-y3%co z4vNCd@_-LRvKE>ml`ME@85?plQkB@DibGuF{M= zL1CL2Dm&9=c1S=<5{VDq)tzcet@?ram6=iqE_X1Dx;g|4uH~2Yttry==^^_nz@KFk zSlG8G_xKsF?E(%AXva(#_9WC#?dmOS`!9FuVD0iPi+6A02EIm2Ql}VW7tW-P>HDr_u_Cx%g&bB z9r)M9%eD7<`I>ft1GM9>e^)yK7mhJgS3vi9dA54r8O9mcmYc5m6>$2r+JfAAOHXY5 z+*>%Bcmx;ne1y0iN6PC511d(_{mk};Sl_)3!J)_@d`7CD zR@~WUWu~4?8b!?GW4&9Ye$Rz`J3wU&+2ie|7l~_TNw$UU1$#wxZ?_dUFF+84ejl=?8{} z2SI?+3_-i{y%09fXCva+ue>39h}|r^61^2{{(ILKmC5?3s*dCLIn;=^m0Ub zR2OFZ_2hR#FzX#==i(56?6MjT8fE|H@XD>@^3Gxo3U!W@TyO#xZl6n^n@fP{&#`x5 z>jg#7d@G}n4i$cxg~iiJj`(|{Ox7u6P^M{0nRX~?N=ae>KMh_V^l0`1btv)qJa+7X%Uy8@?7d#Z2r3l%(czmQN@%Fq*)E4UW)YE=x z<&-;dx#zjPz<0ywx8EBMLkl5BdbPGLXRpUR^TZVs`pIXT|8yzJ{*DU&@_VC-g>o{2 zPPKVNCpod!7auw%^%!t8{YAY$>xcGIZrb}|kJ^q5&41^czupvn{Hc+WqPO1N&S3}3 zyuW^}IL&u-{7)>ef!L-vsTkMe%z}s`{>V``UCrW>e@!_VtH?O(VpJssT8x}@O}U#=mv}4Z433&2V$|~#IKrY9Db5x? z67!sAn$h&?z~NQ_F1ZMR5exy7W{}g8;Pb^NID6vunEXTB{NENVLm7&IA6KVa)-snyH0-k7PELkdHxiYOr4438M0mTgzNq=ru0kqKJ#K@#}Mu*uo zb{F?b3@Hu8IYePh(i><4>i7~yqPh@bxqc=%gVSl)S~NoVuX8aBzX{bu0s)I3-ucet z=D(C_O07wBBM4O`WT;i^CU1fN1Y1Dem|j8MRu!gz6hYC3Y;~_RM|4!~l9}q$vd81e zq=N|v|C3T>eNH9i_&%<#Rx^=Z>_!{Ij)yulw5=BOg5u)9rDgKI5u&%M=oYAlMjr^| zGM#8R$VGX;f29E(Guw(@5D&FJ5O9&q6yy4<5`R#r5Gh250iWgGaret?0vVQ$66a7M zk-20m>JHm8?1BRRzhfW}Go2a2cjb{ff&A(~2^lBQ0clX2a!%?5==vO8YQkv{NcU4n z5flylJG|YbYq*SnBCs^aMwm$~?M3r#G3qhp#;1fPV+QhLfrmt46ne&B$z?~hZ83D2 ziTat8zqXaB($TybFmb$EjiSEFE(mSZTDERKyc5D{yi+a7I(+1`?}W&uCSb{xWfq0o zQUs_FChem0>mjx^-=Xk5qU>in+aUAmP9&b22fOe3tvWS7MO7yl&ioGtfB?Ez`8JG+nYuG|L2g;S_Dh+~wt78223(21PIIBfMIq9}mUiaNf zXOo={bB6W&RXL%*mz|VHqqW(ld<_F4f4dk`tT%{9cgOg8*{CTpEs+c;_cUy9y`93f z!_F-4B3Qu9@L6`w!j#csL%2_KYM{;_aA8d4!oN_ROb9dM53IS1R>eWaGz~$^DAc%3@C&Md_L_kk#o;XJ*07?1z)yO3i|j$z0n7RgqG z{P=LrMnpH`5W|M>JdI^dC*0q>c`?d^TjpVjH%$&4(1NrvLs22#@q^}Ap8R43{tLoI z$l!1GR&zp+6{=AO4%7S7h%NzDpW7>JB)Yw*t?fREJ< zfl;>$-r{Y0N)U&}AL5V%xK$tF+MEAA{xS;txV26ObR8IDjc(#R~XO-QvShX?N<@BHElaIV8kkxRh*LxY9rhXzI0de?#mER2HF2B$lKc?aKB zqi2Czno$aXl{!#Y{+65(ejsiRgAO5l(H-Y5MjtofCm4_7w;ONv4@m05MOA^rF;+2v zvMDuVL2WEivB3M-&jW+#YvobMi>VZ z(F?P*M zZ7W%$V4wqON8mJXoJLavG<#MgvDPP}E76FEPcTA&zd9>haHtnb?2VN8XgU{0e-uDt zyybZWL0q7kXNSW6HI)yy0$J`8UlH7O zA}->A`eVmBerlX&dhjI^pxoPHwISeV@+uy>EKv_kPWOe=Y3Cf8Pau zH+@d$6McKUe_prEZVUAn5M}v3dA?vX%{kKqh|d=O!9KR@iD<>vvA}nzisM8b(OzU{ zEgkJ<{GxYRT>V2Fn9W26q~?a=l+OP3tMbpwZ#G{zDE|!O#YX8Vp%@XlERbdZo*%^VYroI&PZj&zWZ4HqMe6&M&s zqbf>4xw0alG8MVXCD8Q*^J;K#%s8WZaBxaE4L=|uhohsi&(NhfNCj~q!}m9ovuJ#A zC1OnZi&q=sbyFmwaHGH`;^397)|{ziSRp+mZY}$|+ zB{U`EQs_Ux5ls#h)Ve?s$o+~KXTn`dAutMnZ9#-G!k})9ppYjG5E$J>(ukp&$0w#v zw)#6RA|UQWJu^GbTxuk>w{8*vn(T-?M=Taob9y<)p!=3p_(2_ZN;bATGMSGtt+kCYU zxLG|*h>}%$Vai4oJ4T=!T-0=FIz`Z-x9cfo$A2{>)=>ul@&Q#F%z_)+N|3{63Tz;_ z+ysJ4CLp*B1cJ*2Ah?tRg3AjaxJ&_pOK%{!>;QsGzi&It0>QQMLPN!a3e@?-e`Q&? zwp=o`)rdzaYaJOFjgRd9H3<)euZ9*cR!wbQT6`sz6UMdNr#(zo-%VajLYI+m2OhS} z2?B~igt8vqUExH-gTpF46doV`uFS+opyTQgQt4iW`@h)mqUNvIqVoguv4kviK2mmb zT?6Wmp@e36#Q!c)D;rQ)KZl5_8WPlT;;EzJ3YQWeiO&CtO8H^)7p*`EBhVtI1*b5h z>5wj^)^lo06qvMZbczdqyKA09ENhhKwnI#*TAj_+xTugHUgnL^&iZ}Lm3IgG$|2) z;cprb!D4tucReeACdMxbJoIpBR^7U|5{%Bw^Hn)xhz^J&Rh;D}blTsjyypkvKg`03 zB}_s_Cm(D;`O@|{IdnwX#$7-tpJ=ji=t~R$6q7$it}u)3&_F34X|lz!%nSh(vdw9W za*Y%sKQ}34T~dMB66>F4$%zM$o~0z@DKXVdUca_erWUhPVyHXQ<<`UL);bhr8heF< zFp}ebpp{c3u2UfAx(0t!m0IQU)s*miRyLk&^8g=p`n9+xdu4K^8Wejize-kILJXRQ z6q+g(;J02a5z^#Q$Vc!}3tbB(f47?On^w85@qDZbU17!5#IJhJ?Hj0GoXV_4)K#~i zQqwsc`yx7HyB*W7bhyWYFq`H5^ag2OnsFBZXEu{6j0b_r(WP2C1WQ%0EIb4makkV5 z2N4^{Wd8%-x&l(4+S)u8{DT3!4{y-rKEESqg|%0eA9P z7Hed(Jr}+-Fg!BLlrEgrE(hJ=9!WSR5>zbXc#3;|G*Ukamy1IJr`?nJ2;&)Q60<4w*&)F(XrSt0sC?2sODGg$+suL=C~ae zK4UOk3pM^85rHc%ae-oj&p6+;hdLi!N3I|E6VGwC*IBxy5eR{*^4o4;VEu8_Bp?@l zU|_ev!x?z3q5atirETqNTF%3UWCR$Fo zRE?7c$cna8pM?yck;tP6i62k$5&ii0%XMJ76aVn)4A+OVsIop(*vr?;KkD3ZMEPrI z@ogs6j^2#tJoL!o98z{!@c4mMT#%@SYb;|=51pUR&XOlBiW%qBHrEiEmZdl(yoX6B zbjGKSJ{+PfC3RTxNdvY~U0G)rXUN~%`>WuDJUEG)wh1Ek%zm&+*mQN#qi#BrFipei zBHa+KB{lf}5H`AaZoo-g%CG-k$P7*Ntp;i>f!3jZ4*!NhCU-kIwD6IPEN}5*J>DD{ zQRewd=eo~_M>Y_CK@nGvFcY&|I|*L=BUx$w$rskAw45h^S&L&_#wW=Z&vuPTcO{n@ z_enAG`j^1!f{O(;zuM_XMB#Mt{CEia>w(#CGerfg{x}7V{(W8r1q~NVYWj|*N>y}N)m`TCb|NA>w{WA(05swFEko*MZ9*rO#E{$OM8Dxpr z=i}pGt83$6CxCId8WRd7p3nkLK^z1?A7(Y1Pomt&e&W>Fewy6|HO(vV@*W1UaQ=+x z*fVpdlVe>iA4a3gsXm`z9WG8buS>Dkp9Rxb%;`7KGI|3BMaN_SW(4N&Zj?CW0@8_+ zAdO^`(+wDKi>_~dSKT+@=t1Qg0yKSkv4Yfm&Sj8^p(7@o6#7)uhR`#T5Nlm zSDb(bS=upDD&B%f@$loM5n~0HEo*`gcBhn+U*)*jkqXJ8&R7|WZg*-(Nir$vb&DVi z$tS`?3V$Ji7;`t7p z0k$>f5FAy7umWAAUX90{tUSv`Hn-oYQ6!)N_|?Md4BJP$R>4K9t?0(o-W;#i=$fBw z=FbvHOGT)uQoyTGdIJk5EpoY-M7DW4F&}vRFY=EVuZUq!dE@R;?yjN*ADx6u_&mRl z{l&Ej@277A(^In7@x+(5UJl{&oM6l3bES&G{kCEYDw7uYq9|!~ch|e*m(j z2g60-v2E?KZSAq`*<;(bZQHhO?y+sJG56Rr|9t13`=2_uPTjhzYNc0JC!JI${U-h9 z$@5ILl#4oA)aL-2{E7t1&YY%gTzNpw=R-%@vP+_AL5N%Yjf#rjQ%DnwRxUK2{JOO7jfAX!Xs6++%4Z2Khd{>o! zpC!3ul!jVBAO$^uAGZv2*sn%N+@i6kj?5>*W||OY?#$9dytt%dO zp8zQN_@9waM*-rqPb#1*2(ko^F_MV#Veh$X$t%41W(So-?Vm#p@|Z7RYfD@{19#g; zI@gw6)Dps!ig`Ri#p!eu4|=id=>XeU+7CQ`>wAZ8D zWs5Up-!BkI6X zkNt*9K*gtN0s@TyO3(mWpvCjz0yZ^}1@6)WAdsv7l7(xl8^i0vTtH_MBLVz4@nk&I z-yYqO;RbJ@`OdGPV^MWrcP@+2c(%$&+I6P1$3(>w4_ZdSn}aZ^!^*B0G4WsL9da#ob%c<6i@GxVeew(cFY?ht!e~=v z0k(IOi+ImtU%?3b3ok5E@?coAS9v)8?tbW+Lg}$V95ps47gD*NYpEEMu{ip?pj^!W z1jIjO5EmX^-H|e;K}8y9*o%%RECXDtCr3u6z*6Skg~N?{qX*MxzCD$VOT!0@hIxg4 zKTviD2{`}71?-g1qS$})4}>vzLxcxEz@?}3l?Z78$gf+ozMRXmEKFtNFEs{|RXfi= zHxMdzs48_qT773KU32n1s~@3V*~?aIX?4ZRbtlRUn_9iS+C9)tISLogPiJYRy|Y%~ z-lE3Go3Di;adi~WGD35{bM^$8xu&2u&KN38?oupGU%Pi;Iqg;{oj$}2S z_tjE0M^!F%?L@D!lq%MBpPP@{(xkfSvJ5R~m#`qT(*&1!9^66P((lTIf`e(mjkduqLH zMGAV{)W@tdJ!s=}N%Odo;o{o0n! z3CwKNUIfhS){dr`RZMPrl6v|aeV2M_D@V)0Lz~N|~KMrwT7!WD3q^S7>S^QRcX*#=JHt@oQi%&w5O{JpW2Vv&%%_ zOYY+{D)i~$-RkN!{BzrQdAU_@u5(=Am3eCNXWm0%8E`?aiJGXUZP^nq54c+AYr)dr z)-O-*CQ|6C?r@&@bJm52wS>ty89?a+-5Kk;{6xFihU@577ys&8HhxettlP`$;2wnf zQst8SHL;c4#d&nmYPQ{56IPp=v~$7z=SG_anuE0XMxFMBsD;7o{C6$ujhUJqrN5S3 z^=^H?9s=Iy!eG>7KsWB8@Z)dQC^EW#mFoKi~h9^os>Xx5% z?PN#-(OozD>RrM4XLKRD&2C&v!xd@w>oU~UZGSK3tYqBhT|afk{NQj!{`mm?BRZWIoi+s zQ1}q8nbZ8D#6!KBiUo6G$LyAA(uqg6IddB-^=11da2C6^gt!NSwT+zhHk~nl>?>}4 z?Pd*vCXXGS;adDVaQX94c!{H6$0A3DAUb+JB_-nhvjlgWQy8(0g>5opXT#g!HW34zAm zMTpb?6>dom7pzs8?Kg5w#D7*7<;UtSR^`gGCdCZSk2+6hVENYpq*$5MXTvJSEe2}% zou@Me1hF8SC7r?frfP^V5l>(NRPr2sK{*gbS6lXerU886HPu!7g6xC zquPG|uAt`R>Hd`r0L^|sdS923g^l}Zym0jK3*+{%d+?V!OPMo@yU zcQ5IWr^(fIBj`5AA9W3@>Z7bpZGDQbW!pwfncI9GcMt|u8UE6=e}_xb3dXRtfn8Gc zulj>Mmml^}sgq{EkO!+t_^cjO%W9pFxwPHAWgnP2PoS%v+NhDl6Lez0F_g;|mB5bU}S#k#~(oKM&KXwL2=+VdWb5 z`Gg=anvkTD+B1`lYk9k*uP7f?daOK+D=6iF>wAIe_$*k7){pI2;kK%tY zIG_Ii8Jzc*?fiH@H~Xx=IX~|Y-7XBRYV_u{cHL2%wxwg|D!2E3z+}yjQM@F%8HUwxG^M zZ1L8Q2;=jiOS$kP!;wP=(l#PLe9Hn;5VbZ|gqOpskxTk`-ZYoU{3-CwcDU1_hO{V7 z?4R$xv%|r`#;4B^B6?0UF;F+2-EZ!W=nQ#swU^_ZS!bm*T}^DsgPV>X4D&0uz#p}f zUX(=X{qLuDwnjC>cLVLsY1&G$j$8EqaYl1RX>a&2HIUE9q`CfVaWd7_a$!C}RPi-1B1m3vPUkSL8=TKo%&qG* z?cNTsuk#;x?iM=y2RujmREua5S1@MxV(ywJ&3h3S$JmMEv6~a8pCTBI5)nvr^(pr& z+lza8RVUnngS^DXcQgcwJDuku5%!0kqIg3*qQ{L080D~jT9Et2y%ynZu_GXPC1a*p z(1TlcrV%p?>KL-L*j?*VroFg9MPOet%^MJDLAGlYB_BCMB?pPNnk6e#$q$* z-eEJS4x#@xku%`#Jx_l3%lyLh%C?Ycr2XooFUNY+p$oii`QYm5zOIZP%jkokE${n2 z`3X%227K@H=>eUEhmF%mU$@}vLXLDRvevI&s5(`yY z3+~3)bll`d?XA?2?OZQ%vxA5Y-W$gfNA?qZ*A`9F22ELyfeLwLXVsIGm6S=LC4pt# zRG2FuEKd)tglHF`M03?SMt!BgrQNqkp|UbERQ7_qc+lm=9uLMmP!m$GFB(_TN=h}) zrBxh0(3!l}i}4Enz03qx1zFik_$DQ^;cYbSGhv2i8BPUVHESU-iR3fEQ~2n*aeu2{ z2c9|bd_W~$@mFo+(nYVA2EJ7--D{RkmR>CWiqB4G!-Zh;gdu!$A+@_IrR7#}DDWgs z1HR%ZaIGaQwe*s1#d&#PYNTwY?1i=3-|olpe%@n!95JI?xHs- zLoJ_c$G-mkkaBI3!bbe8UmRYJl?`=}dDKzMKx)g%=u$1$l^r0)68G;qq9~s`+Yvv z0_u zKnc(Jtv&XpQafWz8@n9Z95 zKzc_#&$2?-kr%S>Bj)k-9DX_8l3k@K4wPl@xxeZ$B@_2tK=pLdR^27Ur~^U8=3Eui z%?`?dKVjLupslX0F0^m@e2K&;9#Ew&eR zPtk^_$7z7ZHA7d>G)@vf0B-i#g>%&C&I^o>2O-zXL&700=p>co?vZ48CC*YQ$jb+B zluo6NfUBi;>G|~f&GP$A^A=v&@e%g|Hrq}~fuk5hg+*N~)WriYy|-PkB5smpTcOoi zU(Afolsd2B%I|Y)(jyDvHaI)*K{DDwD1C4vM$r6YgESVGg`flSy4w)a|dwy z=;h+#Z#qsS`i>Eqw>-Rq%c@?J?S)tVURo*~hbzA2o}J|-VE>(Ya=gPN;9emwk(Yqz zxlG4uwxbcsD{fpD``99~lC~zO>R4)#r&$%FwuI+?*cQbdT4&1m8qt%Zt#Ka0h z%}&0D?@7Hcc7PX#U2nK{=gy#uq1n}pzli7E0q+o)@AfB*vg3&jS^_Cf)9m_=uG}5P z4r6$}ii1hT6u+}$IeB>8eaXpocu_KjYddkgNigNAR{*HO!V-+VbkKjW`2O+gfl|A9 z;5NEeIPi|M>=&Qk#dLY2p7EpWQ@&^SdB3yUuQq#Fu>#!Ol5joS?}!1e;rqCq;)D@) zjk#}C{Wv7r;*OL-oMlytk{&->FQD`Os>$o&%tjLwt0^0M?#AASa(t}K7s*`PaP^GF zJR@82z+51NC+(UUY#+to%F*-wYj5VzfOq{X=rGCLxBn1vR8sgTQaCeo6nBBb!#M5| zrf1eYn)}^?Ze+)A1b-%rv$ffMTkhHOU*N-4_+eY6YbUU~nl0{o2BLYxXJlff5zB$M z@dLtz>!a7@|I>;l;!#l;YVW1*u$TM&h&s5FQOFlt+!aoMFTRkotIoeKH2YXbkY5tg z&XFvLw@Tx)p&bK<{GJo+Y2SeW|2fBV zYxjnzp5tSyaAVu#?|f3rWK(<>o^@yUv7a#fE`W6bRK4-I+o^NggJ84)_mBRHhj$nq zF5=0jHf%d=J+qHRU9$SVZ@Zeq-A*gzok-bYdDsCSEc#!ze z-<1>~yC%H*4L@kcx7M~cQGQNM%npO^CY;k9gVL_vem`U6uD6)S3cww!*7B(M_Vvxb z5l81ze85GjG#=tq%&hK9$4^iz*VO$4-}eQ(6PJhRr@{F1=%)Z| zQhWNwao;ckj&RV}DvBon{c(NM{#Q9R{V#8```*_5?j52v1i!y#7LrFVo1k8wToNAd zWgc)R6~BLLNqSUA(Rx*;S=v*zS^awxyqXl>K8y6sKI}_P>1o4;>?u-hGq>H1DK67R zF$!;mHJM0%9G(%hA|5$XXZd^g zY5R^Uw%#;5GCNi=e=pycEh(q3#K~#qqVN23`oZ%K(0zl=bs9;Y7uIwx`_lRB+v#!i z?0=jbMX$hT=^%c5767D75k7QcaeX~3KldJfSL5#-d9VKdc-YaQrU_I0etkGuK^*bsC}|#sPTXNPmk!5JBb7UG~%4sr%{8JvXcKR|+H` zzn51u+O2;E?~iCxy8C_=%C2cb8~#|8u;>xH9ztvEfR1mn>+qaR3>qUP&v@19QC(Rxzj{z)2qB?r>HHOLc#bio#JNQ z6prx215NazW-Q27+uj87fo>DIK4Tqsnt498 z65cyT5gpkv8zG59HmX*Is*q zYsS*Ph#f;sb#L3RJN~Byn^si5Nk$GopyAw@%>jZ>pAq7=_sq?r=nPx8`DK=ou5B^g z&b{<_yoYXUrOa5ct4>EoUUScZ^M{q6H9@jo<&&E-*0eDf5D2aKW zp7v&T9NDmQ1vkG(S}q8Xo+sdcHIrX0zopw<6tp~EJ|Ya)-dpv%xHa)ztz@BSG+X42 znwwqa8lKmV%&OX>O*UztK8S(NDpb&X^0~*aEc0w)>pZ_y|1bgJemtk;wff#}GEnGc zdO|jRO0Ck{$y4ZQEBBhp(g%2!O=I?DWy^>ZHu-A~-^}b+IWWw-u03`!l>YcFy(syb z#f_$Sh~`H^yL=fsoRgdl81%R21lDY-*$BB-k~s)NQFxGjY|Zias?aTFjzam#u4h(| z6098q?S_B0aov7SV}4Ml>x&~`woPb>yR;!>2V-%@2RSr2k^?iXb91h)K@Vly&?Y_#)W!x!C|M3Uc|e#c>=DSj|B}Jw z^Wwx3;j?BZVmdjmfUSrjQrDLlq8D%@Nh}H}_eZ2%>v>TkLk$eFiqgh|gGJc6&(gO6 zO|>k+0uiV?wf;J-ekej^8Jv=J#{@VdU6}a@FjO>0Qy#p#%jKt8+Jott` zeY_2(SJiUD@;ZqN)v0ECcp}Kb+18aQ)d8df)q$ISjF23IVG6Z#jr+k5!nB3hFe}MD zcJ7a(_ykn$z753YxoeEn4lIVKk79LH3L|nN6&F^j0lJQf3WbRS2E?>-38oH&tRj6N z_0V8JG$8wzkT9?prJ(>FNxC^?4km%_ZE33C0S%0J4Tim5RG#wf7LBFg0#%!1@%}+u zE-fC%$+i`5mM`0>$!mTzn*o{a7{v8(Vtvf1Lqvs8myj9>SP_7I@e^AT>qatIKd%)~iy$}io8-nN<^_)qbf9X&u6LM)~39`Wi zDh!H)W|mSG{_sd+xbj}|nh5ovF;^>u{)`DTii2T}QdV9`-0F!l3GOJXbeCM2p7C?U40Bf;w>woE#D%y3nKhr>H3Q z%Y^#VBJ_)ebF-!O70{D1=GGEJ6KfanzEG|*TPjl?UivHOp4TWi;iNN0jDs8S4KW*- z59t^rE29khOIi;uNe+Ud$HF^Fttq#b$p13(DB(Q0gGjB_wm1)3N%r{yyagg~TH^5| z^q9Q*4o&Hvqb{=>4EGV;i(84=G)AIotE*YwxXT#(_R|_=WC-7Xb|cydG_n}7D;ZY^x()S;rhI~gH-V+!5|Ho;ZElPl<-4L(%9kn%KE4{!G44@ z6^NcNgfkWk_B2e7r086Idtp!$Lqzi2{zZ=Boi= z92e;mvbj@ZM3H1cW5gL2SNsVnMh4gr*S4rDh=W`V#9ymm*4-%A*$K49J zna?PhfwM3vg<>IZ(+E(iB{MK3rHT%pd2mRjLQ^PWqSojToT7x$DMMsanP9(}_VzS9 zC%k0Uw$&6^@^#J(U7)t<6DpdQun z3E4OaE4mW-_<*x7jj+srKldJUh-The&=^&R(w6r7-!(=Uji|T&@O~zWnGiN7E!H2P zM%gtmD_DOoK2+gbC-CTqYX3!ZGJNIgVIz3C8#!}o;r|9as+Vd7Dof?^-H%=c)$=L^ zOPmx&|8Zag+95iFKSOE;fyxkDnv&Xs)spq7ps_9@3J(71nKW*~4(sq|{x>Nsxt5m8xN{mdn)TaN9ik27-uLn`0RAKWu{G4{W znDHnvoc_rW!1X?-DJxbGvrOj?H74$30#Oi2;MSyu|+De5w zpLAxMBzvBMl=SF?Lr}rnzbo$e5owlvlsF+Jx$1jx78eN3-t+Es3yRnN^+4w3+Oe*n z(dtOfQVhPy@Zv(cZwB>mNcD9aAe)OG-nUNIz@m7GX0*}1QF%p_8q~El{i#UCX9RUn zVM_^Qv)P$}DNhvt)S{Uj=JFa>r#aDyI)_2=Qq6HI0;@Vy^oLY!QK?a7#<=j{m+pjV z7FQ#XM;2cDQ(|-pr*aSBXDTJ2pf%5l@$z5ig&1EGC7iLxfkcflD|r5yxtXQM%77e< z(IWp2;Dq@m|1lfPe@Oo`8E`e(o zbZdP9+WLusjCfhBWt?aOfom#~5lzC@^xmif%rD@C2~h=O=y6^Pw&4wPS?1449TN~; z{+#yGxhCxru`HSQwUds**jmRF{GfyLS*(6p6UHdqWqleB0df%AYGLiI>M(r@2)y6l zQ1!APQ8$fD3{t^ow^g0VjXcnEC#c6GOBua=V?#Q?pBEP>EAX7xtG{B4TGXmnJ2TGl zTy3}6I3e!9@j#KCX<>>Ru5s|hFV>ebkfz?GIUx?l;8?Vq4OTqi;aUtZf`R8YIBz+R zB1R)h>i0W_COpNBDyAaYp7xg(Pb8h0Q+mLRy0PTWMaTB7Pb+kR4^`@b91Tt8qF?`u z3}N7gN;GBoZ`C5lum44j*J%3rVsa8TUCRlWf@b+orS}78Fj<2z8(o1JlS3ek$rz)? zXJ|tbm+>HVTnLLB*U|tqN>!Q^OZ~e+Wi-spGBjrmm{9j<^^cj?hvBT4Q?ZFKL0HzP z7)>{nn?WHFbhGL$+SYZ0jO(V)A1kfV_0t?wM4#nraEIXNTguSLhhq2gz)y)CXT0T) z(6MYMZg}$TyPDyHKeIQDg)wkVTfmUIY{GOk=0i9g2&bsw2%m@d886uU*D%Apz^$&u zI1|d%x{jj{Q%7PQ{zL;ckaZhfE|W>%Dy(7N;Uyxodlj}60jbCXhDVcz6(9-M1`Fwz z`+=aL>Q~G~tT0k-g_#B><3~9?aevkn(pLyDF7kk zoUJGT_CT9mCSaXXq68!Wp*^FmIfFs3^wC7btIJVLu-dLLnnhfmKMMg(Qy2v%Xvhfm z$8ZmIjXVqrI3gkV8J;HMO!t6#f&xz6R=PwxJbanbc-3VYQ9CVDkWYYe-X2rvDBTGe zhFd#9;ds9__?(4;0?wpe#6ab*4-Bet?;otoe^Z0)VVDF8_OxT0Xiqdib@U}a2Acb> zPOZ%Mpc?&TDqE842oB9FVZZMV#~3<~S7ncEHxyuX`MM7>o4f_aKZjq3yV12bvhg7j zy65FiI7jlZz!K;zl({GF&@WQ5w_cp^kLE?uvA=;Hj2)Xy8_ocYS?1*@W#JWpwt@5=6I)%H#5X|hZ@Ql9G-tM7uzPm z<%%alCE6lFst7mvhZ@e($K;M^OdX5uXgS2xA{Oyx4!Htw`RjShI1pf-8ZYc@Fc^JG zZ;*(EHISx1IK=()&LlRVIjBGs3f=xg05+pt4F#ZdSxsSwpTf)(`U<6S9sR5Z`_#{jTP-OqvZXH+kGmpwZyPak@8x znm(;~Yqzj}8p-5uIs2*G1t#FzK_*}aQ$7lwlOc>+s0k+=Gi{lBU5b z@r;^!L9av4{8s-)MS1(_Pm~WXVl7J=b*E_tV2|UnhA4q#qEDms7a5XxRPF`{fp`n)h*H zM*`gQ^t1)1r>!R~<(?@w2$by{$r9Fz-#;$ej_ZautFw~dgO2qDIkTr^^l?4<@fU;I zpK;bVII7J&%5bEPpSO*3Wj*q$%m#d$U!T|3ej9FP@0-1ULGs6*Ry!1ZE9#_Jx8YK4 zXt5rJJ_H!H_$WH#^P7_iUY7YDulm07{JuvCzW!ag5q!UYf4wix`hEI+_k70neU5%_ zeZ7ocz4QC{$9bPd(er=QJ*feL6Sh2Np4a|_=744&`LFicT+x|~$nEGuRI5~4Luk(W z4RjvQeRD79Dctf}mpqv$5h_`*A#DayBbiC4i(SJ`Op3p#Gd`-q!?MDf_U0S=fR&h# z_EwqzTMu+&E{(=Xv{_>)50}Un{_7MUVlh(EWaYW7o&$?I>NyV6(f43DCq;5{SOwvX%2sC;Fy+=Zs875_NFzbPOe{))rI%eb1MP*}17F@dBgIEhkV!uv&x8*E zu}<}Xb$|d=ch$cmw%Xh}3>^32@3UY4u~@JRbO+}6`E2y7%%R;0J9YkE3cq>W; z)TlfeLPnc8#6A7J*R1x~1V*{`+_!V751_~$U;*CwvTsFSKlrlfm7bbvF}oupn&e#z zxZ;E5uQyz}*Ri++lW@ei;=2arASjkU)T%(Vr?!;6q{x;rmLq4TK%L}4x9V5AoY+jm zg6cO^b9RGpZNLQ|WCk%3xsSjTBFWTDG{>E%g${lnAI3m3y2eFFuAq$ziYXZ)xx6(X1RU?pi|0>^pu|Ev^~84Oayb&tb^tj- zhZkvvzpT40>T==`#!-_dDwE_&v${YAgt2$*n-BdBmMGL1q@NFoa4PHyjxf{@&JBb{ zwE+JMmBM8P-5Y{Lz62YPpoOF)`W>KT*GtZ!C;Ma{UyX(w%V5=+2i*;n`Y-!m6?xu?q7?YajU8WT`S8^ z03Y8?&c?O(gdljwhmA}?h3Gj8J#L+C_e4Xs%}=0h+YNFsJVHaLyY8N1)WbuJAC4(L z1bRDxd@In)`9EXGi0S|>3xDs1Tk7YdzTRIH6i%(TmRqJ;o~FO8-x0sbH>p|QN7>P1 z68^zUSTA`uE$kufuCTg~w+S%y-d`yRi+LL@$CLMXwBAyWZXrS>(H%{a90a%@g}>k} z9{rSLjg8H9?R*sOp09h^^;4hTpZahSa#D8Njq)IJ*mXAF;6rd%X*PA^K}DpSRTbMN@v;2?q`@0RnchL!W4_hf^G$u&>3bWM)ka&_JS{L_ATeqmh9g!{_8 zg0UwWAn(N56y7r>WTw7+34UV|3SVEjE)9T%Wuj&%%2VFumcAZ!^etD()-wZkE5HBH zW0@%cX3j;o9tmLM>ThVnsN>63Vz7d<&lfu{S&G}w&1$^Ti#Ks2SQgW*?1kH z?sByv=W>WZ4mpnfV0w@$m2Tu%M{opYeLpn^Wv-D^shO6ZDTKE!HQ2qbQ?ZW=!cDaF7E$RKw?E(#&j)ycNw1r|S z>Wxm$Y0_WwzuYd)-WF#2PiW8|>0{Eq$-;D?N*u$M9`Jp+Woo+$KP3El#Ld3Cah*s7 zuL4E*)eqzv2M8Y{6tVrhhQe*WG7FAu*|lVv%3@83Mfgb%fx_iKq@%~n*MretmdXdA z%9)0D@f^WZ8p>z#8>7<{v0gAOd#^?;ndgF;xmxZh9pE&Mumr~jqk!bJk3ejPW|I8D z3k(HO+86k(?-6seaM@g(a5Z9T!nnB{Vjx0Y5fy=M6vg3F)4*IqB*It~K2aJ0Jmo~} z^`X-f6kt(70X!?{b?b&3Eh}X7>pF8})d`Q8oW}LN8>WH65heOnp`R<^F#ZgH3_{dD z(}gLBleqwIWF_ER;$o14^f;*5wZQac$yJO%4ZDa0>0*SsZ$s}n&e0vbQsRx zgvlw~G)tMGWHS1V0_m^~(pVTwf3Wt^0vXj&(iv?~u7(1FPKKBc;@)#ZR60qNb@Duo zURj5KUk`hCTucf^K{%Uvj5dR0FGvnkFB-i%tdM*KKYc?^v4!^hA5e{~{|nWq;$d$> z$e=E7XlY{P48x%8V(9!o{t>ftw1HtzGI6qVaWpb!Y$WRSEmFf$SQpGV>Ud6d+FVGwn97FTvQa5nibt2hfG%TJj~d_Nx;1|cCkcS5cI zh!UBZSl9_!S$~p?JKDL}|CjXts?h(4{#56u7)JwJCwl`&6I&w>7zRl~XGa&4|1R?X zMy|-r{=b1SRTZUeL7AYoonYt{&LLm^LO05B@b;I>VoH`2Kc`V|CS^o)d#Lxn+f$Iv zPgQ#3&XEvf$u@GRLJV0o{$U}JmV}a@V1x!5t`Sa##uP%Hw31G&9M^Ob3m4PFScoV- z4!}_AA6mKFm?V~!&WRC_sKFUkg;7;h@4lm6Dlzs$u9%}0ep#G)D&h`Xp^h?J+Hl`3 zlBLz0I1;ds%xp1Dx~-UzX%+*%xtlf_S_@MPJ6^oC=Z~bZ>A3t118VCDtB-?0NLwGi z#@v#%fz&+><9kiJHU^o_=DZal3FUBS*0lw@z5xuO^3Xr{GWUKc8b0qWMDKsR6xwkw zW_$rJtk|dgA36cs|LO$)Z}*akk%g7(e&rJ&+2{F)~)M% zCY;|X3O=t9Azly8~i3wvSskvrm!mSyZiGj@2StmZ@1e4Y18^MQv7q~9nHGS z`=znt*Y`UB(W~#}wd|^k;Nxr2&pO`q#M;@b62tDp<9+gJ)M|0XSju*BlY*kzOTzWL zaeezHE^B*$6qa6mEoOQ=0Dk^TzkgXlX9r&3cKo&Rw=yxv;#WPkp7jrdk(m(mz%fu) z1lv2F&03B_!wO3FrtCP`QD%3$=h`8t;ZnAx-0V&Eh5Opj`C%e9;?UGJq87DB+dIjZ zmG?CE_uz%LwS=QN^>DjFwX&U{xiizuGGXzHW|O)L^)jcu0$LyxJceOPvw*(ia*zmm z02jcD)CXB};DnZQUcY69P(if7{H7|WCT`Q4nl#Eh%||XYKlanxMj|%;rtXSHgp&B? zIRa*o`wjIh9;mLHB=GSa>dFWL8F{HJcS#J3zEC*}J7^B3_r-^~cEHk)?JI=y{pkk4 zUcr=?sRCF}%xCD~=pc^_Lhl};%wSi{_(+kEmrOsz24Sh-1eadW8rw6D$;rNu@mc%Y zNdAhWu%7*Di=hJF zzw)B3aB2icp0<&^?O|jz4qdQQypd4~ul&`IaVdBrKy7<(Y(C=fp? z3p0sD6>Qlp7@nsdg|yUQNi+w_Mf4g7CnTD#uauHrU+tW)-hU1qs38>-Q6aTBulZ3S zieDQ7S$?Fm#Y9^V|MXw?PB$2E`$if3y8X^&Wr;#ML5jk{Jm>qq86|iEEoLBTa~~bz z9cuPl?+?cC{8?5s4}9F4zwg+KeDn$*Kr5%e@?XFN-FU--JJg;F@-%9yS?S^xv12Yi zb+hx9WfwGOgu8OJi=_36{JABOBGE5->kMS(?&1X%;a*13gTTKNSSezX*$sUwVWtKF+mKe=Zstsp+ zBS;Hxz_o${2|}d?h$Mj`iJUX`p-Li#s#KtZ8Z0r4iRv)mmW)==Gt^>Y0W~NHBM7p9 zx{$dtdhs1yy>iSAb)r6xiFVSXud5;XqsR(n zVF|<5W|I)MYa@j_s0Ty9m5M~s!~&DFs$=89fO%sGcO^re8Ulm0Qr7ovWhFWdLmR?k zIg#5}Gu=>rlf7s{ojO%OSu0Ww0A&uxRt#5xLs>fo@|I$(m2L!h6|c`T%~djfxKOp| z&(H7u0>+X|r8RS+YO6<0^ko}l5|I_8M=cH2zx+L!`MBbuepd;ElE$n1+;Rg^8QZN*PjWa;5Pjycm=}RZ{Mnm(_a`o zK2a!+I^jUFB@(`9&UvQq{ncs>HC78a$g;VPfJ^$#OdS7e>Baq)h+b z2-rUdjsZ3Efno4}CwZ-1vzu;t6W4eU46@pM-yy`syB)k3NsU%aQFQd!h{IQTeb#+# z?4|sddEc|CG4Q>A!i4$0H)A>O4#c3$#D^L?8$3H6SM3h>4SeAx~Q34N%@rToTorKotjw5S}A@K{S>CbzEdOcR^WiP)+}=mL~MH9s!k5 z^)P=xS+4|a3A4~2iu1gOen-gt#pxsQ{}XVlYTpEIQsG6=0sN0o=Iua#S@(^inlpq8di z9?KDpgeoE%;3$eRvbQHTT4$i6mZGb{83i$9NW})Y?|4Iv3po)>$(?FolvADoJH z(w0v|Q6`uc|Fj9nPn#J2KiedSeXrFPteb5>@17X{PbyO_Ens1cK4;jH`o(!ex2}8a z?#PzQ(Bv&mj)69<>w%btXGNLLj#jPZa5S=Ib{JT??1bhT5Y=`4klEzaPUskjtq~_u z&+eW7oq}*N6JHBFfBlRVx=QruWiumk?_KBz%C+|X3K!erwvuXG^Y@#}>R0ViuU^u< z=rAaz6qdPH_Gv76QONkk*z@GWwN1cu7r$&HzT(@IAg;XV1;OGjs!guv(Xah;aG(9R z`+#eMe*rg^r|kvsyM8-DO=3)!dae4R0#OO}gO*`zY${MG{tYH{)l@_$w$Dt!G4gzS zU|gi9xan9iE$!fKFFTJSfi`VT6!uOL12`AebsyFvj=^>0 zWfDZZwzxtQ(W&;ku=O#hj*bzWg%GHl`MB<)wQxP(ISJFgH_W z=@_=EN0X$cXL>0aZ}x^EZ3K0%hXC-vG_<>Qfd`N7>iLDDHjq<#^M!4*sRh$afA_;h zblW=#yy9!GV!^dX8T<$&M7v>DZK%&0!w`Rwe_bQslj=x_?x0_a?_hyq0!%T5kzr{_IftUqVQk`cM}8w^EHGUCn3{LlO!27T}DaY5JP=bv>?tBf-NaBsZWB^&T*B!Def!9yS+lu*V7 zBlnF|Ln5$3%^vpUI!)d#64rwDxz6@@P8Lxap1_t(#l#U62+&$W8?8 zqK|mfWC9e8n0O}Z<`93Mdn89#rmVvdPIerkHhWf-b~`B{+W;D*dOJNKTS41A!sr?~ zN;*PUuDTn!Q`bifia(>S)~>9yw=zhvBA z1GW)6+g?IJKzQnAxm4o6_KGmC2;OT;&FTR_sUTg!@`Z9SS@cosLhsEB!jLAcKy4;e zjaI&m1KNP55%3c{k_Gh2lWY!b^XOmOklseOg(Lq&IaZ+C{hZi z8f1z*WoW&xo@8a+DrED~Nr_((eG6!%DGOAkl?#32z$H~vbS2AEs}>-(UBmbD*yRS!@7Wib+akq&+!uaMq`zoDmln7DsI$w+qXiZQ^N+VdL z2HYOUP0H;4zd+Q@aXW63#cnIYc|4@Z(Fp#nXDG@;qr)L_Hr-Ig9w?({yrEURsEEq( zi&FC~XMD*g$a*#QD8)lH{fFTXT zEklVft_Df!k_$=Kr&5%Fs6f@c88(R#?5kHeh%Ues)Rd!=X&`S<3qax%;^hcbC;tB! z`^F$mnxNZfY-7i>W81cE8#}h`*|BzP+qP}nwykg8_XWCk z@)*E{ga4M?;;j2uDUh%CYgwEin>HF+!QyDI1i5fYLH}RgLK%!ajiiCPUztKU9++jt z6p{?tV>nxHUZ>hW+AU(WXfKMb1FNvXfQUgQ2}y?b7v1=?IeGe}6G27#1^GLn7)pVC z2@TRfU4i6neF0iE2~MM;2E%v(bHsV+Q>+BF)5C+{IRn#-IhUhbdf4Vi=F#Rcv6unJ zxGYlY&!M@ZY}u1e!quh+=5Zvc;zmLGEj}eW=#_$=#160BtgAopmIoc-FI+Cq1Wgi{ z`D5X^6VlF4K>QLcve2vjHix5>sOCUFiVZebppvMyu&FrY62d&jnTYH}1qNe?+{i~( z&xn85dWLFJ#X<9qYBrB4l}{-=pO8DDRfHX~FPVYG&@8Z=$ve$@rk{RHi(rgfT>m(6 z!i)Woal@Yt!0YXY`G(PhkMDS0$_uw6c2u2{Ers=M$Iw>XvP}tm4Wsof*0PiZcS_Fb zd63wjAp)Nuvpk{p5}r@v^Sj7A(@=8HkAxTct@IWetil$Mixw5fO<;-8FvBdh3^BU=nfQuGw~ zDWVr8DN+@cEB20p6jn{p6|PLInuFPBhnCgD7xIKQ7xN4|47ig$ObqqCTYU67#K?d? zMD;RS;vb0`SwZtYPD0vnMq|EW#bCN$ympR^sG)wuJ5AMyCL~M`M|#N&t$F>|7H2my zvTQ8K$D6~Lfd*EM$9Tt*iql6x6a&{pplf_!*%lvHEn$41C08%GduN?`j(b1SQR7xT zK@jseYQ0ZEiH6erNJ~6UvM?$0*2-S+ic!#D% z^C=?Q<)I|SVSG^D{nIVpH#_NnSFzP!l7d}t2ZsiJJkpKsR5eJ;Ej!V$bTA!z*A$YZ^2$)G&%6);`9rQg{1Hb%=v0| zmcKgBgQX_em2f)^^X7sPV>(r=R_yYdeB3LCR4>4ecBg#h`?Iy%9PKc+mX7R8S2a%* zxKOPY2aTvg-#$3)s@w_&pCUJN6w@_TY446hE;G4<80~>6aqd=DWAZ~5q&XxLrLuIr(2vdaAb@eLhBECX}?swG% zTZeL&Ro-}Y`k%rUjO+c-a#)6%Ekn^_#E+2sw=A@-@U_fzVC0B^JRKNYkj7NCO#gIX zx4MHq9~j#a@r&xq$%3;5oLbzA%YtJp0)g%u_~ZIx+o3d7z8ZGxXD0>bAf&zy^W?&3s^KJmFPw8oD5BP$3YHk#to1f z#9*@nqBsKV=MR69#Z$C{iBH3NGIZ8AA=ijn@Ym72EP9B>xGaoxwbQ?D8Fw^Wf@=v>0~LY z1W#HAryhMkBZ{JBK!OTp>Lm-@6iIv>fh3jPYb8rCX$k=ez>*p%nv#_%Rgz$22>;p` zyZ;$7{V7Ikv3K+m$8b340;XzCuxsEUC8xm+OmR3@iqj~AA5O)YO&f4_WAIC*+*IY z=nO(xGVC;*v^;`EodUw?8S#s=ubImm-A$($i_>-_8RUd#4jV%E$XR4=s!pWSVjHDg z9MvH>Rb0VAQBh8~T9}bezRqH2C%7J4j4d#vSwes(2Ed~{~{}W?F96CQY5S$xbJNOO)eo+ z%{#v@@K+}x{S14@NeUcy15Vj;wJ80u)-WfMdVnxQ2R?bypP0G0G=vvD;s1)hPexq*ZacZir6CULo9o>Tq7pEC)jJy%O55)ScJ z3V&2&5A|^rx$;8WDuFP{YBedA{32(4WC2T9C27lF^DZTZvyn65NVZ}BWloAYLoXLqDQvjYWHK`Qx?}DLFmp@I$WXoQJL5b6G#bAN7 zjMs6cVVNNRt@R|>n2m2!Ne^j*S00`)1ZmY(IahixlrW}SBWg}*`JBQ6ov+!hSE+!t zGsfFOYF=siyqqYj>8hptEJl2FRaR?fBJuQj(`l1Z?D(3bb0t#08YH%^p6uW3PmeJQ8{} z&_Hr`%Uv5Z=KzRHqECu=T=BV(`yJA_?!zySn0ufwkO3??N&9fs9W3<5q(gdo;rp9%;o#yjVX{3&9UU&`-3~K zeR%8b-|oStUop9%F(AlH0KL&vu@UGh5bn~o9PJ1TLxZQTZ0*&exx`cZZgTF_(4^_( zdFMyBsK2p|L8F~^nYVb0XG<7id4OB5 zeOf?46(hQC@OY0?*Nx=@`EB0CTn`*EZ6XxYC#}KO_7H#1u#%6q26Ue_0$W#iXcGdr zCY$|6d5eY%7v+AK)KSjjnPEjF<`CLugvYvnMO@P@n<1PLRc81Hx!QrQ!&Y60XL^o) zP?dQ3n`ZZ<(XCZsO)VHh%TOf9yb-N;rH5*+j!NDtMOUack|)xb%AIh}!%>qMZqkxM zBKJ?C_-%&b+tGPq&YfbSIFq8}2N}@6GWyzoiVPZwz_RG%CMGTvjiy7g*mnCMgfBdG zGcczR-HlHa$CGak!!3++F3t`Eyz`3K+}<$q7}r81F;g$LZKNZe6WFbkl5Xc5tiXHf8VyHk2FQr#5Boo~&no z-A$EZJdUjZ$mi*mf+#4nD#W$uxZ(+dd1~f!icc(1ZR2<)y+o@tEG?r%2%E}{gm4{K zMS?pbY-qsMPCxMR6On8dOtD@+W}MuHc&N5s5p1^hg=RLD4T2y1{N+6bw0ZScW?iaqRa@ zL#kh^_jGvD%mZz?hTmXqg@se3E(40 zxUV)z?&JP-uX4uqNrUo!)x~>`fAf_#wBECnC^OjisY;g<(DRxKTM`?sBZ~k`39>(g zFvo9b=@f^BE{{yDx2JZw&m9*R9n5(7bi1fCy3|JR*H3ZEd4rUuB50uqcy9Bb4Gpp7 z@9FzUyKsJjUx)f6FdM<2_}SIvo1Iaw$$#ZkVoNN67gul3T?-Qj%&9qSvUjZI?5DTQ z$#V_s?F1{Jik@5{>+x72n&Y0l6Z$q(1Kr4#iB6CHPC9ixN=K4bxoVXLP5M3SuU-}WXFUFd_=&8jr$K9_4w$?!h}&mI6%>V zc4^ZdY-tuF_6KmW?otry_j5PG!&>ZK<3DT}|%cgn}jlhF(}Vdvi{g%^tRfx9*lRl(Nj)b8XmW@L%&$3rU{$ zxgHR>nu_STP7yk_Y@E znQdf58x!dl8#zChkr zm8nSe&J5mCdqb6m+)f80k@A|nMTb@?bG>f68gHp9J%qQQkg>avZ-3+|ul zUHczpQ&R7DxK!gc=x2rW6$Rc=!xk!*+*3quT6Zq+rT3ioI`&)j`7T(gRiyuh4oG14lGT`#+*=$@%kwoL3=qa#%5^ybdcQnFpM zr2MtYdd<(a5CyuOnyDuGDI?oSR@E$OSa+p-He@Z68#KE=oTR%|GYZw(GqdQLEzken zG`M#b#%P)^tdV9Vm8+RuPA>fRhNX+Sb~sqLXx<2J30_nW2|u>&AZ+<46tQd`r>B9V zpk`w(E^TMlkYdrMRMBo*FVxVsSa)bWu7_jWh}@<}aOZq{u(r*Z0`*#V8dDUO+XpK5 zgHr9QdFZd{Gr}yU-L~%+K406cx_M~|cjP=HHhLxeDXwODPkqg%>yy8cDLb-B+xe(s znADu==bjEUzeYfP4k~r;qbY1)@M%NAYpW@#YmA;bdF^t$NxRiU`=#j+XRF|~brxj; zGB}n}iA|nFl$87EP$Q4{zJXFr$TzjA=LfrP)`Iy z_IbNPqE7e*z0nk+nwGVDoBjGiZwN(i+x-sB?atnSzz0NO{(b8P|Ieib+iS#mhI_=A z`}oYgwJOiXa6P0?V&wU2+FW`ANp;V@HRW-tI_=PN%WmbM!w)xbvmSNWB`US=N&UPx za#4Qp+AWEFKAE?or{(s}n6JG>h0|@2@72cRxT)V>aZ2-!G9~q)Se~TPqw;IK?R~!k zfn$68%cj!TlPzg7`q;cNQbx8typb~-UUR8^^l)4)$$)23QFuC3v6yRbiLN`>XTt`i zn|G|a&&&JYb*jzOFA*&L?QtqwYpH0yz~`xiZHtFy_@1O=uPOWtjiW?4v6S=DDSQj} z1fZ2>9=+GD7+u6$lca@ivW)ILS5K{SkM3KT)?1Y}Aw7e`uG1+`4JzHY*m=EWjcxTS zzGTmhd!F(?tQ5DMqK`+c6r~;a2W;&U8pS1YY)%<`!Vdk?4{+&mTSu#zT0RGRR2L#P z?oF2u5JO9{7fz!)mIWo-&Nu6j8>iAEX{yD1zK&lvua^^ZT8q6*{>D((0^`^)#GQ@T z+^-_mb$a!dI#KG)yxFwHRpn8|2{F|*-G9sfoc34ijzF zMcf10q?q22cX~iFO<8J|tuWNsmO@NhQ`=uDLXuO9 zZgk7MyKjB=WrDKCr_-vto9*6&PUH^!dB}*jMVf9rk%#GA2;B88?`g# zva+rcJ>{&vjJT5fz*?GPp4*mL zbbIhQ_XDm!rUPl$H{@I==(cNZZaMK8GLxU?M=$QTUcD}S-m3>XFT|v9$;_tr>kjtj zm}%s!zvq0wZd`hoU+2b}O%`6Q=U>(BTQ}~l*PGun=hYUXWqqCRIug0$;_tWaf1!U< zn>^Ei_R}$q^^~4I%9UNq9-c6N3Lt7n#;T0anpcub2cD^we%gT3%;)!Jz(E2 z^Y<%=?(YZuP2!zj2Zk~UD;ea#6Z??8tY=d$K29dHMwe{O{rq-Ci*`Gz4 zjp*;s+t5zEkB|8zOV5rAy_31YPy6vfuxuAM5 zK;Bho{v>T!z(7C_15g=2YClTc54(ses-bDaPkZmTJV8>H%}YWI!7ZpYeOQI1r{ z-k$`G=eI$~7}_E2LrNsgfJRpFAo(I3_UpVMyxm?wLQRy?G|;v)sej5MW>d5Ri`4vX zV0wQy2CBh3^()W69lbxpg!k;{<%9(4oMcj%gcvx~_?Bn0D~G`H#Ppnx>6KA*#M_4u z%P6`QKzKR^`EX{P6sNUt&Rk{8ZEsnwdVbl;Of^*tIn1ty^i{cC+BX!Jhgt*h*+@9H zegxj!C`tZac1C4Mnz_eGc3|QcLZi4BvPx4F>PssZMkatr+NbJB4yM-kV{u#gZhkrR zwXh@vRf|w*TGEU$quJ&Z#kZS>4+-VoUvy}U4T0;GI}YnCjN97Tc$)vtq#7E`_s#OI59>RS#8AyNvE>K6)!;YBhG+ zw(3x$&nXqu7x*YcT42tP5gxPb0@_P%p@D@9nkQdJV(|p!Pu?Se%~X>M#TA%-D5S3h zV@{0pm(r=GMBiwh)dgwtgAh?A-gL9~Zm}Pn0Nx0mfECR3L;LS0Y{{sg1?&Ef5Mm^_ z=qolTy2&8HflVX4Tj+*#2BE^pCI+Qp$Od&W7b5S0jVW3JqFb+wL$~*&Ns8?w{dr4T zQY8@Gu73NX!|IrL#|jPwvh+NQ`5>!o+`r0P!taKaa1|>PG(Mi_sDAwfSmubz1CZ#W-+=!a1lum`@z;_;BtPj)t!$Qfw>MRUM|) zjG;FFQJ+qlQ29v>Dfrp+d`z{XHx8xf>Yh3X8%s`{?;~G(?UsHX+*XL_?dy}S%bQq= zrf=q^&2f^@6q%>q>W&^mWrv|*4-By2C0(3i}(_5s5$051xRw23#>J0u%%c^Iw zCB^eML&8|$1_G{bDNk{zXa3Ct3p|TciydAH>zY`Bdqt_SK7oBLdSlz*FCm*$Atc1` zMD$K7`ZTEBFvE2`OC#&$e_D#@~>rFpt-3rq+3&L|9+Q zo+j!SkiQbdV{>NEWN)F_6k-82Z&!9$7pL;KmP1uIrAP}YaScOjb%F%(WevBuhGM(@9fO_~#2x!NewcL2)c3mL@5bm6j{?NO+QzNtKh# zP0d$&w>@t(EGv2|m!+cNhBJwwwUs~m8vFYv(x9&QAT25P-9ot)`G&1OV}v|{XV{M2 zs$^AyCz+VCP0`h0Dj^%NQ;PCBB%ck_AL?VmoS-3OAc?u2? z7xA=|t_|Y9zZ9K@_a_o=x6IjM>>ig#tNZuFc8gfp;)tN|M82N`;ygvfA8RZ_FiUNJ z)M0m`ia*c1PCp$3>m)kZ@bckzCA!A;GsK>?i8)F0o{J^ckL!{LvDb{JUv&jb&!mrn zr{gHA3~a=Dtpl2UclYRTNcfP`ruMZiK2J388u3sm?{9mZd)8ZE)xloLoD@zov`{xxSjzNm5p z=cXo25JF{~?cJjhsCw-mUm)@Pl8t+}6LLOv*o~)XG#;Ug>tabruJ9U+O%LBj_GK($ zQy70dUml@Wc`XDkzd9%!+Mt`!Mk$i)MU1ZyMQzSWQBEdfuD}+lDf*qL7IGni)r20g z4EuURxSl_QAXQ5}zFa|(Z%k3)CD*E|-o=0aSSB32_l=N-K~?pC$y#Lp|IAusW9MM{ zKeHA`tB55WR(QGzW8wivFI@l-V3oy9?$h6;K$&645)y7ge^J3s?!lOvSqMK4$J3Vc zi*hiTDd%*lubSG{k7E9jeO=>S?S82t+}BS#eSa0N^KBYd#3Y?%e#>w)iKR1bI+{5> zPW!sPPk#Wef29g0b#Z8ufDly;9*hQ5w;_G#_oetqMAA9c}wz2e*WzNCA7hkmG;vN26=G*+j5Tqs;zz`hq_5ObB-uC%&=Fxt;Jx!Z%ENyL<%dTS^c0qX7`yT&{ zc_KOvdv=Ji53>87Z@Vzs*tX%}i$#3tn#HFHa=AW4@`zwSrXS=5q|A+&{I1~0Y*XVyVJ)q96XjK&InvGeh7J#{-_j<(jKHoNknR}`l0~2 z0VoxnLkl7crZET9bb;$tZf||BUwPBt-6e`<%88x_B_IzIJ+X&ib96KoA)aa5ie@~3 z8F}q?dw=472hXx#Rr+`TnYBnYoLhRVGjD`FxHo*%r9mMkR4yk3N|_ViJXZn)7H}NG z@W+l?JTe0WywcUq8be6VmVg0>{{omS05~ksKCYsbOD+GRScCwel>u?+jRJm>0GOr*V36E^w0oe_eX_Q2g|T z^M-l+diU6smIRv{4zfY6uMPVTJj_|&DQEqk>P7X|2*Pa|6Od@E$ZVLKMB>R^17&J~ z(Qk)LH3-9;YyTNDHIR2wFYWo6)KR*O-V% z+*k}w&Gdwq%#1_V%d3)nJ4UHizUGF%=ygw881PDRp8c_{-)fej350EiDgV_KLcg}Q z6QqX@>l?TP{*J;|6O3s;N5zz0LN|^YM(`p!L>y0G;3!g57Lc3rQp)#H{C(T}{W8q= zwLAXo`>N;r{&c?mdF|W!o!;&H-u&GCb^gAb-PJ4BHJkFq^R>6Ey=vF6DN;$KDci>O zQNb#6^(wOcY;|Os%`Um)ZRl;1Ju>ZWmMzYvcZE{@S|qj>cK+f(B#f%LgD9gvA`TOB z7=wA;lGIDG`8}{~-p5-|+=w7B;?{t;&hr#Gt`R^JeTAvkbv_GE3HL0>lizm=N(;j5 zJYANi2$7hCNNz#R(lP|6L058zp-on@t*gvY1g@6?88qli{Co9kg^F@ha;Gu)!ugUM z{RWRq&%3)&wz{*sm&HaA62R9>Dg9oxK&BZ>xGgddr%%s18I9zZzfxL&G~*+}XAMY> zT@sB^0OhX@kCX-kfYIRH0?;kTg@M5=PDc2aA&3}y(sk(`uXI(A^?$!lCw=?{tGx_S zzsn!$I3iD=cx7aIkvU95Yn5B2HyzA=g#`?L8EFzGZ(dI6+9@S+ZI$g%n z)MS=Esj+{spRLJ@pumQj&l=?%LJd6lD`b5n``$!eEw-VClHVH3u>V4Og7kk;x zO(3BEC2zX-aqYEQB)kH>>ns`rj`X_ST>&NA6DCQ*HuAEqcD$}6%qrrMi7UM`J46uWsi5%G^WVIAbt{WvLu$^YkW4c4b!+@j49ka_NR%wmR5FQHmdJD zhI3aZAA4~H9}^ZApc3U$yK01yxc>93ffG}Q-Q41{R^F&fjqZHOyH99#UBbR=A&2vF z@itgZ8Wt82V3crH*@1sPF(jMSuXlnaf)EfOAdQ}i*{oCxAL9lH%$-aUhs0C6}P%^ab#=mVD9v^9=9Yq4w->YM_}7fT>_t#sYq;0bP_j1t0Ky^GbmpagR0%Y zRn+osV21}aOLXmwRxe#td$GjS`9*hf*d=sYjI4JKP_xUXK>e#g30 zpSAN{WRdIXHodpoUQE6xkPP@_W6kBGGhQ3o>8@@<6Bv1#yGl)aTe-H|eC2Ld(*7cI zM>YfMIfoCa-4z>OT~v<<29tWb3`_Sd7%pj0ilbX!M6S|pTRT|GmLolU6>r7aviL>9 zaIz+@3iH{VYLsf58<(QIpDX!2qGIz*@iY2g=p$W`!Q59 z-SLJ1Oi6e2MIN+lynw|DIb98adJ;gen46Y7jVul)D{TPUmd*7Zz~@%(>SIl$e@%3c zm;{BL2$zmc8$_dza!v|v5D}TeAdV3wQ!a!NMAHMywGiC|+eFlFX0?GkDyjtoB&F&n z1G-xsO|5kLc6r1eeBDS*a@}z8r^{ADWym5`)6@j4N52O;LVr9wac437_gs#ehyP!y zIm@GV6sz0{GyiEFoqsdLR$QVV*5066o$AoS9D?Iyo70*aO@AROwcZWVQ&CCV&F^r_ z&oldmIx49P4!hmI-9Jt{cg}c*FrkN34Hi&voD&%#W93Y>k+rR>dLyNl&c~$GNj<>f zD${U6FxeX{>TpeWX3iud)x9`b8rpz=CIVDVmQ!>i2Q%kFQ^ZK3a@GhV>F^50w&6M6 zd)?E^8g9~5{%t2q(m>FqRcol1lPBw1=M9U3BR)90jbv0VX}@U0KPc<+Vue#UJ;AzT z;=n0xe3+Slfd`xz4?x6~F(W~&+MBezASm(C<`N5i-}>YhHwaXfHw{36WSu}7)@?~r zXlHkdzpQ`4(hw01Cl(wG1*B?RW3(mBA2qrE6t-X=2h0>P_~e8I#9U5HHHc7SsXiyc zvUt2X{SdWlvF=~Tf?YPIpNTSNrHOfJiivL^5EW$r4V7|$DwSUdsH%FfwrWLiO&$<0 z#}Fr88;LN6V+l#N7&5k(Fhs4Z%oBJkbl55xKb?=?0{ci5n?1vY~+PtVV z0!-{fuW%t0&lys0CeLKLAt|ntW{H#51|vBa2o4ek;d2j-yo?FL05mmO)7D-}c)vPk z1!qY8Sgb!`0QEn568;s&K#+AQoxD@#GzEE!n*dNKS4up;c>CO^-^F=8gsgY~P&gsF zmOq-U6`z0C2-TNI7#;Q)#rEVss*u};DjwPaOC;1$NPaad&PgSp?etk~bc_*ZL&yMu zX0-@8z}TF&J%au!E`27VTY(vrMflPqqsWu#tol=9-ueY%Bwj;5TBbP}>DnS;ubE*4 z{p=cgW8QWu?Cd(Q6e9z6NQIRy`eHJP=0=Tl+>bw(fBZRLm;KshqJmj&Vv(AN2KezO z^^ZSQ1JtMjLcmqEgLPGFg6qn_c$YCu{)RU@8ASMG_jb12dtUJrYI#2A+w>y$_hZ=A zqFj}_#Zh^9Q1f9G02=$YqhH6zw?hb#zLTCiWd?#ytO?U&XEtML8|0?a;z&IxtJz-! z3J)v7Z1TSDpsUD=$?Ks8unW^7R+H<2r${^K$~b@3ud6j?ov&s9X3|hxSawt?!5PR{ zz$cl5a1nod1jS#DTSN{`=4zE;kc=y^>M>+ZN<^KGWdJZ5Y?{=8^&^y;xGeKAP&m`e ze!!L75vYF#JGs_k%HWR>nu+YiWE+6Bda>{ht8X@A#;#s3AU*8VaPxOB?FC$)6G~3X z;${Frz@`R9z$xi~yow8z_@jv|Ivcg4A%}=`i~$9ULda(Y2UA5^(KLM{mIM)CDF@3y zVc&8{oo$H z-*4bq^FFWEmumOQg${hmY7!k|nhfXLZA~=mO-<)DEFV{$<7&|FC3!jJZyf-BB1_N0 ztW1LWjt~cxB_R7B3qbC_uE(%?$V{Sohy=8-^D34f6_=h&RuL`qk} z!rp4d>C46lr5DnAD^B>NK=Y~KI)y7W)9Su-n90bLmkPTNQD)CWau)yx`nbQN}A)=6J1%@Dsza2Fo0nJ#}@fqeJ^HEXPN zQeI@}XLl2|zPzB+&wK26z#F-BjXG^jQ>Rcc33dER*?kz0{kA6oaCMh)cB>xtcuL%| zFDNrm;8h#-QSatOyYBp{IJitiaRoG4_;{+AN1VM~tK9hmI{H96^I(4g53Q+XwT*FE zd?waXLrDO1UY$Er!L|!N+O{V4>>T(xpQ6V~Q7{6R0S6nUVXqGlly-2&Azi(VZ^B1B zf6m!*T@Nd&svbAPWqHGwSh$k1-@DVF9{ZA z6q68tOS^p&!@JE}-ubVn6-sU)zppM3mrubXdEuM{*CzN+_&zMJxC?FZy}D3G|Gvjp z9KmH((i=U3zb|z(Y|B?XI|yU^C++QCfu4)9ls~TrUe{p&-?nUz)ID#Hm- z5gp^M8hwZcBFl}TB^!%TEqn40T$MLfTQ)YehVn5x6N7J2BR(VeT`DEAlh4W$rTNJD zc>v*H+FHkw?$1w!j@vu#CpoKp#dfseEs>VSh3WV!VoGYnTv8A&3Y}r}ByCcR_d)SgCyo%wyKrR1omjyS-jasYtN-6Oa=lFEdY;G1$-V~F=?jftu@6vE$Dt=mte3V!?>>?lY2cCw&N+qZ*!0L| zCLZyQ%Ra(DrI98EwYxBJRH7A=*y@SlfLdV9QW=tmi&5xV#evhtxf-rue=HHogdlDv zk`srHX`tFrX!9?{4zU*z8jEfR{H0h*rGt zrKCb7+cGJ>GNK8?kUa_)%5##Zx-RO)ne?COR;>Bt26->2I7Eynq>1yA0Hhe0*dVBr z1`B>d0gbIOWin%Aj`=!2j(w#l_MOWk8`>zzhYL$7tuP%M$ijL8r~cr0@DWj0l)!j! zaT-T)zW{K`SrjV)9+0FHje;Zr5|1Q+iFkN73A%EEQCJqiNf~`NO6T}qljyQ7+c1$+ zwGpXLu@U!Ymyse~xzV4jL?iFaE)*2eL=D!bC#Gv@`!&q#22VpTi7y34ZMKPCC^bvT* zAHk*p!%%+QFytSln@1^4tOcTrWn{;|D4u$Tv<#P^pRs_K z0G@AA0Q!=`Y@tHHpK*HjUPk6)Sid|z^NF{h6@gEx=8ZT^i-4qG z$9FWkusBZ*$9LO?m9uCSr>s`pa2ROnw6v&RsT%Vpsk2f?lHi!cC@`v_Iqp!4jPT+& zYL(emva+04>}87ASGOA*tN{t3u%9l^?dPq1F_7a1;ObH*=0EL`Byoid-19%wQJrWS zwJ{S4VjVQGhFT(RZKhEHVn+h+_TWz%b^3I1%n*F$OxjDT<$Te)>1X4z3H!yJs%b|-e+wgTTWNTJTtyE3y$Sf%s|b2dd`Ypb>i`G~$)#hHA#}(pHEqLmOH0 zl%)T~CVIf((1<~ztqeUi$y3MDWT|}7##NMEGICbR?PbaoZOJ+LYr!L`d~RTier}*g zwbT#atB$~>U5n>pXvjm29QjxSiE?g0|6@S@F-XzZ;w6Y# zCu=@2fz(t`1~y95)Yk#))9VL|)R+uP-dKpt{O}UxKfFYfylO)anV9yt+e!4$YHViG z`t^B}*1(^*&bUI8LXA^=Y#(_?>C(Q>rc*sdzNL!^n+SRiNGj(64cnp!{(>4Lw8!r?6ZId4IUM^x++N#G^(d`D!w* zAO?-imR)$|g-e@b7WsUHm;lU%>R^FoVX3%l_*xWe$p_&S0T90seI~n_8F^>$aLOtz z!e+k^scqmiTW8q8Yp03gZP&;#BWLQ#`nrv3NV|Z(5eLFxb)E)4T~zi)KXeO@T+_Dj zk8c;695Utvma6VcNndQ44bj~u_ZYh2pDN5@m2!6YGw&T=xYG{8+xAKR+w88k&f5K9 zdeVI5+1>dPSVeE>x35)D|fDl3_-9Zf=*SUiRD zD(+XER#u(DvQ0pZGQuqe5V@3ImQ2;>n9WbTwh&1IkbDWgh2porC%*#nV#I;uR%E8j z9;7rb7lI9KPeqILq$ww417*RnX|ZD87*NOLBre%=f;7TCmmgnSi4$OHCc?CImr|_* z7j|aG8Gtk6z$u>Tj4pi1jQ)Ihk5>8ld6v4X$^uY9WYvFuo)s%j>`2Pc z<(fN=)?xvI$4Ru(!`E#n_b^5K&+__B+tmvco#7p)?3aGLD_0t>{7?%JmwrbuT)*9~ zh`4@ty+ZV9n0n3d6ZQInafg$#naE>}s3jEl@epQ%`tPZ0ZC`X1sJ=>MP}XRtgp$mu z$`F^PswONN4|g@ zl};hUObT~wr0>02oDxMkZ9HSNv;)k{^{@Q8IsOC=F-Sbk1+evQtcdcWLxN_Mc+d_| zg%LFZ%~|72laWyIHsf>zNZB(PLHX^a8hN2ht3?JF)7nd`?2AZ~f#OkUE}`326=T)@ z1biE?h~XM1mPjpoSe!wq0o3plc1Rs#y-tRZ+QCNQL8!kSlfx%a4Iqa)U;;Hk4MRc* zK!7>m3y6hopfRB|epwMh`tF8*0tBL}s!6R$P zngJPkRDxR-3p3hy#?;Q=AxV_&4R66$Q|K^(BCEoX+)#~b2|n)jR691%Ked@P$TUc{-k_F`xF8-vLO!?~ z-B?9faIPXeJI^$r^_Y~j_T0)7m4r%gGhLXKr}^=HUr-lm=cnA2olLH5{d8z zhe-H_K@_sbv$$GNz4Y!8TU%qg{R&N*>lrgsV`U0p)jS z{|68}Qe!Ml6#&|_QmXa>i7&>0NOQE?#o-Jy0c*PCmz2?s-|9vnssM zCtAf8pv5I{2@B|-A#oWSkhpi4azC(q231)!6Sd_kQhyt^-(H;o3qnt#iIog+i^2fP zz!QjH-GJVeEQ44pD}xx~lp!5z$V-|>*?~@Dx!Sl_v&*C3C}O!2J99$mkKU;1o^uI; zY^-u2^k&Xab>X%~K|m2=ZmuHkuLokG;Kx@hO}n=vQ&st^#)`2GMCHrY-MVlm6eE+f z*qrcXf=K5EAsS0ZG4REYXk#l9YK_q}#Kqa0A1rZb|G!wm73+#AyE&3lsF9i`q3*&K zQC~zP)+AVR#^PXFz9+m)ejK`SMuoZOPdj>oRcy!ZYH>wJ7W-Ge%DI+GIySLF<{~ph zVT-yJbWH+yCB|0;pWSEz6WJyB;jxkO$O5LX`eOAMLy685)SN)n>Rz4wmY>h&L(j3> z!G}7x1I>E~YSnF*OPbKpiiPT(!riq&w4ziKG#=mUU9O`Z>-)sMzrE8!1zzQsaR`(# z3H3c!l@!254xTr5i<`oK`pR&yNFw3ra_iv(;ik;oQAlicwwKv5LJG%nvGNN=`$qzR%-G~bg6aScO{3h%es-mL1( zhzN4Ite%9qXfZMzV^0FdI;e!(28)Cm&qu;it~V-of(64tsOlC2ZL${eL?D1WaX8B5 zKBJ^myHDUAtRJxC6FJAV<_maIRYkZFqoeWnB_7noC(uW*Q{R7_gG`z8unYrh-z2Ev zAN$n>u<*1KgVn$I*~&&V5KFebE(K)n^PsvMg5j61tj#?oCIf_xNPy9NY?q;0u6|PM zR}-Xp-)nN__MrV6VEDz2S2(Tu=_!w>d3GmLQ9Sa=FkDgJek(wzJypAw>c?fLEUnU1 zKeZEzQED8KyMbSNM2jz^EvQ$Nrg#$&M z*>3r3+}&Dn3sTWA&P>PHBv?{zDc(`$&alT;&R<#7rxEEG(8g@M+ntpd6Qn6bir$go zO(iCV+<8Ozeu$A@AsR34!|Hc5(?QI%(^bqM(`(!sbYMZh=;(q3=(GZ?=|Y7u>1u_A z;!YYPw@ykQWpwWj58KQTa&VcuW)9E4E(oEZiQ(RKGxy$)i_}QWG_*I}bbS4aZcRBX zVv>uBI`ESc9U?{M-`(6?>=mT_5veCE=68ZnZ{`hY9TXH2!8Xd9N`1VyITRVDe&B^Ox!V8s*S(Kr=fb0hKg$^e*sAc*ZbH{k$d#D<)%Pw93Edf0OoMnZOk9Mf^ z@G>s(0NvihnHN-G&?L;YG=hc_mJHLDsHR&R!zk7dcqo|+5O{-~ z@&Wl)5o;+_s%B%ey(~Lo%Y#unC+qP;0jJ>vi!l6~8pB~KuWA!Y_tIUyd++fwM%D2J zMk*=*IEbn`sFrF4XtgMC9BAd-Jz<3)E!C3X4{*)a?!!DXiVuYZCLiLfyUcE`>|f@Wc1tw_Js6YKf3={<^6^=~MMJSXp^yTDy zz~6(dO8n-i<0&K=i3NU?e)Zss}YC(|-oE3EIWSauB+UdFqY$Iw_<<=%)v(A2QAiM5m+&IZ5|kVeOH8~P zCo-Q{igqNn5IQ;vn#4k)K?lm&<1n+FWnp|2bS%M5T`S@cjGWjs*$(wOo*sLli!XE$C9*|(8}Hn0g6JsLi8}I8=88yc76an7^jF2^jDnoL zm(L_fPi&CnT--OmqG%EY zOL|P80X_RrOe0nccPAgDrXBqE&u}@{*j2I?X32xjiay2Wg6nQSN!DIU7O8Qi@A^UwM2fX=K%G zP;0u#`08>vZ-bCL(@_YO$C9ixbKRi->=wFS-YJdgAgMYf#=8LpY0*%8qV=5ey~ssP zv{ZA&56O`ALoz)5kPNavBtzDb#t+E=`$IBV{g4bDny*Uthr)@Yhe2sj-TU?0BqlP^tFy9-A;!%qlw&qgZ{GkVq0?nw+h?F(3?jf>*rnqgBe zuwfG}NXV&|gv5;NCgR3VVvzTU$f@3$R=XcV_5CqQ;>Iq#f{SkXe?sMO1PSZ+?V<_d zR_#xCiGiJ;6KLUgV>!IYg|nLR-0x}5JCp4-`1q!Kf07|^MYv%QB`+sbr;1oGPsNYFq;&d|X*ahxH7rvCW%7%-`agy@XpBGjN# zPE)n98vn!FTgJu}G~1$M#+aFzneCXFnK@==W_Ds`W;ub4!jic$fCG0cI{NOsvObTwU6#vPB6HWO_1ei{|YQanE%TZ zm5EaX#`zy8(*#keam4!^{p=joces1tdhwfMP*0%QD2M9QT8ZNG7(CA(1@Gk)K zDj6O%Z}%htdr-MCtxuM4dshrq7etlU4z9`}MT7aXO)Pi;x*Dyj7b!?gTmA3%pUuci z3xYl#)hG&`8}A#MCkQdhPi9mR!*|i^T(v=dFS;A0b?f{!>1XhDEQCmh*MX+*;|j;M z;p|`AlrCvQj{wScK}L{UR2~|mJ&Zlf9k2>0Ev5OZ~LAf|}>GhYY4@J1$`GLo=w zcMBx}zj_aN98QE43r-{&N>5}L2li|J9h&%qA}i6YxQ;4pdc&gW9XjhaOe%unXXD14 z&n`?mBUCy69o}Fz)Xj=g7}9q1xjwrWUk%nw$MB0`{-W}=3%-JwPC4Z2=?7Vae_50` z-)MuzEtX@*5OR485&vp`Xa*FO)4kGcQ)!c2ImQ~#GDYpHK24(-sX8!6Qq=9GHcjto zONa}EeN=_MC_hoVU&8%DdY=6CeA}D^#hKBV3(3^sX_5uN8=Rs%7~L2<-v{Z`Hlis2 z3?(}h>;liz993aZOqg8IO-QZn?leDCe$4sz%(9=l1G!P%-t@FEoZxCycCD2@=bf4| z+-RlZD!O&2&Qzx4Co?s$3-J-jHHDOxmIdzGHko3D-V{GBuAZgfL-+WN)$wW~kez(! zA;+ZxneFAx{$TOEpbVT8&8BHlb{HjPXGMZwR$Y%meKrb59|=^iw}hh9JpJ(QLSG%{ zuOnRCfq6t^#%FtOK~kO@;fFFZK?R<#)SmR4-cTN5P0C+HwyK|+$<^h(mq0$bJK{jK zXp#^wqf`DQoL0xKYj+&@T~`KLP)eFMy}gD|CdM5407|jd=PDjB0|>aualx*WY~Xt% zO^oN|hfRzDISJ9JX4VqZH~|WyL(uysdr3!q9Tff?-MGH7H+ReSIJt2#(;l%V6pMdN z%xt@qgNV<1P;|8oQu{g{B4MH{q2x+O6$p53U}EC=yp{nTx3wVG{R5CMd6bB_0AhHT zH1{q^4#;C3za?VS1ld@R8jVkOK<>MBo;<68$Jgp>;z6?4V@ z2D(!zN~J7}xI%6y6s*YBc7}|>4aYTuVw~z0M7Tu~|L{%+QfyJZ4nFB025Q-ghCnLH zP?w7dViM`lo(OU-$Pnoe^jwc3BQnas-EKBLVYqDRjy#JKQ74abRNBss^-1!?!maLR zlk)f`5vqCuV5tI3l<4!60x~>WuxODuU-jg-4Kk!en&C~2m~ELVh0UmkL9{C+_dYRGh^lv*)WkoBs=aeD}Nrl)>aw>pDH`1 zlpMIY4@Rq8IZDYwsqDnK*@cS`WC|QBNjq;jADSC*JJlmslcq3wNKGq8%;GeYg>r^F z$P%eK5J!w&C8Q1ImtiU-pEZ9??3%9KpJLIEk%qjhrM{(#drN>6KXS63y9HhtYuaSB zAo&siubkjJ7==V0@FNfpJsTW*rD^x z8oiDYSO#3jEl3WJOO|l6!-E*0uLycJ#5_d#_{~UNjJSJ;8#@x*cEnx+!^a>V5wl>| zR*182g9HWcBy8q(RD|2P@C_q(DRuWcF)*IGF?ybmF-@lR@~~Kp^7>f8`pt%iCyl=Z z$L}K73G_St=$gf2f_IBVT+BhJ-?$6yEaUMSXVlexkf&!BzV!1^1Unb0RD} zIz5}!+g6lM;58a4`!Ei)e>6qme%sxW(iGdnM~6hTnBSs$1sP-bQH67uuSUz3QBNj-7qar-G#ZNcBrx|`Nk-nR>N-eS1D zU`Lq8G;*aaYdvjFFnVu@uvpT~?+q(Gp`gI`f#ISTF^t^4i5AX#U82WhXe*EV92*Ok zGSIotroead7bII+Kg%Je_TqlJbFoNtMLZ-BClb~h-QOes{>O*ar@Zh{Oc0a)PAtqc zSCYTTyY&=u`REwip3R}oWKGZq>}NP@@cP-h)W!6?n^5k3!35UQe5XYX=e{DmuXM(a zY}L|tuhZBv-rWhDYJ~UfJ+1;a?^#B|cBgqX#ESKdUC(5=-o!$@0L+-%jJXvhg#U6X zbOE_|2LN8~F%Xa0WU$_b`*Za+=zTT2!RKT>%+cIQQ0XmW44ubJvmeEKtTJ5bzw|EB zwbqjJ4M(bpAyVDCEU)dBiiJr#BG7^1Xt-Wx9b>NU5#8ei`+TVT%0WGcirnP zCsRi%oNhotHjXQVn&)SiYY z$g>4jN^OSCtA6J48n|U*H^_OflS_B|74PsXIfW``2`LM*L}TJ zj6M;>ZJYmPfZiF@pE;dHI1M~#( z*ftJ!16$(T0}PUX2Keeik(%~%0`9W=?5x>$mT>XFx8r}WKEv*1;eFuH-Ru7h_k zSQB!)ho`H~&)axE<18bQ7YUmICrWMLy%&NvCcU}m%xPw#|+=O#*KNwi*;Ys^yG9=_W2LH_*LjLhdnp+B;L!0GCE0X z1IrGJO^Uo8-Mf1*@8r}LvU%S3C|(ZX+D5rG8~gXMIbg* zV^a&f@G^R^_z(mXygHNe{SZDF0tPAjMZmWmSOdeTca7tvz*N_nqX+>JdtuDKzG*Cv z7E0sw?jjkH$MllY%czmdFPT0BwJYud^S^k&{BL~gPW_xCs`10VLteRT{2hvGA(HR~ z=v>CURSpsPO-6JSZ66%4ye$kur@^3KUzD-76+SdiVzsdE)7lR*!acSM8a5N!{Ij^? zD^0Npj*lZZ`B%ekXW9`X+sw?t&Kz%j>33v>O+uRSF=kxLtSZQgoHrSWP5FXab_qgC z)A{~W1V3E5k7jwis{drVCPRrn{W`LaJDMoM&4MKj;;D>*sZ}1oS#a_FdU0Txx%64G zaVnv*9nZCMp5?1$T>l|{++OVwZU(M|DNVfji{{R6`i;elm&@>P{su^03p? zvxDd5tem2XJ_LaFEC6npcUeba7S9HmWl6)=+Vy| zyaV{Bt-D$32uhWL#e`#51!jmUB`yg|(=OuIpm0d;OZCX%`vOumfT!7o!EN^fui=H= z-LRMnv2ki3(1qHs?0_E#bWsHYT`Y5wjQzy|P|?LxP-(;~PzA{YQPn9@QLQN|PXiP# z38A%`_kUVlBHeV-V#Rd0_Hy_&ZH;tw4y-XCH5aa`d!F~)IH_JdfL{7WoohuFtz7rc zpHGxEdkSR6V$A!@xa!IM*wtEVj4LYqeM}B0W*hk+IRxwV4Cz;gn1ll4|E2&_Vg*Io zAEuqfsCPL<3xyNBJfSYf4k#Sz6J#Nz*G)qN1SuPe^f`mv&Ay@mFPB3wzuXB4+`eYtIF%s>PU1W}Tj>E#xW|XKeo4vXV?oY66licTfep7*0vbnA8 zb*!O@8=Jr8N4LaP;zv~p)E#junSQe$8+9==c9srn%m__B;kJw3-@g;CZ*dy;z!pj! za(+z4?4%K2CL#d^JzA@9JE=CkHwX5zLvB-^m-}sG0Q?TTrh4WxL^8)Z2rn~L1i0ai ztJ5zt-xSRGRX;$k3r=f@3n zQTQr-HUyAu>U59*%>V6_u`>b~weLWmRUe^xRaOitQmaG&KGNnEO)S4-t3(!a=~{Ff z5tz4bbv36={aL`K*B;8*YYc~ECdBTLg6$c9VNQyKM3DSdK*iBc{F%w!E!;HFBp>KH z%fj;d?#)c88_)>(?fW7*fsnu{4o^&>;#Sja;}!{k+1`z9{f`mYmwHj=n#Jlca&Idl z*0+|SWoVQDEG2BYj-O2=7yH}r*)UgNP2u1#)jlnw;@ zI~STE`Qc5vI5!m9&71X4*D6WC#>P@y(DFXwJhWe@+0A1N{QWJ3_)TGuqU)Nj4!31e zr%+fw$K}P%`CX)iM|>}pr+6Nj3-WM>NBtnw>De&ZfPvYHg=nMNyG6S1bg#ub%mKYuiUl5@WgDc!Lqd2orh{r`~O&xvIi`l|us0{x)#m zq}+kQRF?e5V<<)~FZVOWYqEWT0UJBSwphN{8K#^ZhzCt00O&vjD1>4Fc+s^1*9Gdh zy(QYV;1Tu(xjv{L{hY*Cb>B_yYg5M!z-(HrT5_)90Q_fMOl`ouDv)4eoDb1k%?rGXb#{eyRw3t?-?i*L z?X+mmqwKx2iVAj*`FTZgaS%Eq{ey0W?SIlT)=i;7B-7I$C|0lt`o|^Rzh2YU4@I{~ z^w?;A52qy-Tm-T>Jr`3uB|Zvoce2x~{dR9$FxW!izqAjZ&!p^<`@2dI4Qh&U)?OoQ z5k1)=FZX9|o{As9j0Gjw^r&(c&;A|ItJ;w@fn(&9?96f&osT}7kqa;^&smg(1SrQh zk?{d!PwUuwAq>km7DXc<*-0Aa&}-5Ei z{qL{`4$H(*G%o~ht%b^VKeLxhlf;bTnKDeWG()1rSEwF)dPQ_l)Y4XRl?q(O=hPcs|lV^uAm$g z`c^Nx_g9WDnIC$i^$rAW5f^D$LL2h4_2rZs`i3-c=lSUYQXSk9s?1;5?)|v#(zo|4L5g6 zusnfEQ2KN>`wu{jlZ>b_IMH&Yk$GZaank7@kTLS`AW}fYtO*dw2VZGK&}$TxE)8vR zF||K29wJvsHfX&a+>+VV289rou>|Un?2$(7!nCA{U=#+ot;St`g%p zvuKQ)fTar{oPh}lXQ&0j8Lq7$fY8X1@;D%z0ZE;`mvZengV3~XUC5t1cXi6bZhCGr z9IN@WI(t_3dtsaF@qzgTUEyJ&m)wVb;zY88$Zr%~9_Gfqy)x?2j_SrI+sWUA=Mc`+ za_R-IQydg`Jjrk@?@{Dm;*hlEZ}i0j@y%-A(eS>H-qzY^r`Rfb@@6imuSzCQ+<)lE zAG@K`a1GACk{0p{psm&W*QPpLBf4Gt;e7-HcweAEmUVbup}c>^BDFsuf?n?vf*bxi zIVJ?Z9ufOwV=tBL(^tc`%-A6DA4ZZY6dPKbM_zM$57Xof7T$ng-XnsZ_X|L8C;Hc#7dsO`eQp1Dh6LRY?#-8npy&s;*}={Cp_DQnY{N<} z%OCIDqsqG9sZH9sJWXLEcyYhB#hcJa!%bp0FbjoxR;W>Uw_rlGd-9gSa5QO~6kv z*|~r2F>vSs4g?)dYjfuPKm%ys@iH6B0Yr?a#!KXy2#uw&dP`Q^>neCI5EEld_73#p zJ$S$vbwlvBnzy7iYSBFv(NA8_>lO=SB!((o4n_#G5eUp1-%YIp7p&$-^#P4rOT=0S zOB+WCk%!(7?Z*u`o`KGYuVomV7Ko;< zJtgFSOgumeWZdxp3DOc|d*7*)-neZ54?8c%r>Y!()Q~>2HnlRL$zk5=Lz{Q_A6)&v zPVpti0>D$e8t@cvm6MDL7z{wg7)n8<9jZVTD(cf-z_m1W;;u96FGqOZSM$yCK{E@- zgV<|*gUjm2Yx!)mjqf)wRIPc^S?A&YJ7wCI{nAZz<-U?*cE-3NSNm~abmrKHQjjcC z+^o4Cd!hKlaTZ)|J+-rNXAIf8(DQA^N2$wNYGopy5L0d$mXLFHfxNwfqE15Q8ey)* zPt-<~OaDb_N)4y&F2Blgfmn>IlW!Vh*zswOfZdQk)>5tQ1q~F~W1Mtyz4tr| zHs)2f?a0r?U4(Htg;?Wp`?4F0iFta#;I+bln7Ie%2GPg#t^R{<_>xxVJUmpoR?fLM zZ07{^N|xMK;yu4Xje&s8l1D@HqyA%iI8NhkxmL0+%0h~5LcL3Kv5MIj4;9nj5!#(` zjsJGayjg@`P0K4*74~VF;BNOiR=X1;KzoGQ+eU(1k-&CQS7K2hX%dy~1qfQC!OS0Y zA#{w{lbyOE5y2upU>N?vBz4c@|GSfyAqDq1#z4EeL?Y+<^(8uGfidiJ-0t{0n@zZU z2)}@ktO_Ch1IW|`X>NA7*xWopQw#r_)Q-O5(Rd0E<8(-Shb}x<_j^3ykCIqE7M->n zX=c?dGYZcvv3HnL3F%>b-IieyOqsG}0((8o^t2v!R z`!h`z6xBH=O<>&!K|n356nIgsN~)ghXu|EcBT~nD&;aTTcCuX%!3ghfD-s}aZ+m^r z1WDQO+Y5w@@=_I4Gv`>1IQqUNmjMnR%^7c19|W7?VhodAku*lTI~}V0i5pmDbHyDi zY?JrRtOo47X(a`k1x|#6ly10{_2K%hi0jyKPixW?<=nU6dYY7k#-#F^zPtN}Gz_9>GDw)isKl~Ao*afO ztXCG2{otda@mSXGq{pb&hLVg}uu!ruP=@sA`J48wb0*nW|9Fg_(e(ftv)w5KPLCVZ zJW?x}eK)imZ?V9ZPJ8I4`Uja}nhh(zNph)M8*h1;rtv()=ZJ)?>G>B~Npgh&#R;uQ|nZ zND-y}-5GS`zcPxfRTF>zfvclA0V?T`>y=6c|1W@q44MB2Mu;fTCGtz50m!6G05U1bFOZ^AfJ{nyAd^yi4#=dG0x~HR zAR7wE=#irm8o^P?Eq{(lJI!`hiBDHD0SVj+&-&wvxAFttC>7rn@fs?O?!>%m<5YaVNSX19XiaMr%^+8cDQ^NHB<|8_~Y|Q_k_dI0h{GWO^(MmG%V9f9v zZ)p0Y(Kn3pK0(#vVPL9?82ClhPpOItLim3m#Uc!H@V_=8#S0*?u_?825*LCZE0VD0 zO(FXH^yRtwK47OQ2hK1p^3o`c$qJqN-f%kyusIZ|e?@M?M!I;Pap>M~=s|IH5-LhG zIaa-qW|gHNA2k(%DWy$l%a5_*t3@c^U|^-pPmuB)nXsb|;4pv3)i<0d&O#puD$0l* z=_TY?DoS|dc!ZWlhOvJhxlq;W48dzxv`W@#;&HzCfURpNmfSV2jejz~ zF8kvs;cHr>@C>siAV%hnTEyg7G=UZLNgm$%DWl6L?Hy*%cWamZe_0Fv8c_cCTKIqM zcgW4m#{NJ5$qV1L81hlu?YEbRI}64vMdP(V(OCErWmBcP*3xW>d=sj#VwWqaD- z8pp&;JiA<6D}_Fq$OS_|pF|RJbeo=lTH*aa+Ch=wFaNZIUhggp?3foNWF8p4it*M8 zC(|#v*}7kswl+VHzFNON9~oQTf0pg89ZToDisf`6g8P2FU|e1I^t?S}GkiV}+HHOC zY<=x~Z76pPXX$jbU~h+SZTd;n&J~A#TwK_1F*m+l**i85A3x-X;Kk}+3DA<65-g3= zA0!Y`Dthnpu7Vcp4i%=vgRl|igSVFN#J{CIB}sTh5e$8t(H~?GP|JFl6-gl0^=1d8 zvjEfyScj;XI;#X|_@v_S`hE80MdB%)f8H?bgb9seI{iFA_HH&FC2p30_T0rao#+1Z ziPg3(>){lV!tz5k1Wagt3H4Op?&~MjN2-pf@UVAQ2mz6;;PTeTt2}08xyXhRWFh%wxf!`E6Q0eTy&#gR)A<`zGJgFxT);jTlY%Sg9-Ffu+ z4`UlR^ABS?_77u=?+`b>(E}651ccZIgy+B-M>jXgQ^du&h)2HG+jq5-@O+3VMTYQ?j4?N<+57N|zuCR1U zP8S0V2dgmpat*X1tb1V78T`6fAYA$UyFT_V+*_{v+kKxPl7>G@w(2Th#X2>#^Ityy zPPZ6@#Ysc|{`)0@kl+9CM4Tk#zh_SxlJT--Vd$_ST$(HLQ=s>wr~T{R-%jAuC4Za5 znZKOiFwDolZBU368(OMSbN6TLjE;$Bgf9`@?|7Jl8)hpDqHGDDePkL9LFLCZT zGki;0f~=Lc5C{sr@0r8ePb%Isuz9OA*R5uC()fG%s+)k% z=0`2Q+J>*sTY2kSexz{gq8K+QYmleCnsU@F8g(PE;XF<0R$`!bf8mf;7Y>9NoI3&h zJPITHJm|YV0;JWiSWuRI!#oVEXI`vQa(*vrD6pnKGQew?j?@zjJ-{k2#f44rz}))% z*t;@6WXpyUOl{b_iZT5c>-=J6wh|?l(_!xRTzbUMr-r}?HXfW(VL8uRi#$*s1xTwL zOh_7{yMDpRJ!T03tkM9jxH7vA>(3t18&uhI6YK-S)ns6}x>(lEPNeBrA$EPFj!NP68h5Bm=Jw-1poQJ-6=s3{+wn-g3;K!#*syD@6n2WP|UbEHIs3X)YZ7O2|pkSZbgLcAKuZkL&rR@+e6N>8=H$ zt{ycionR@Vt_h@l@%6CDXJk{FvH=3v@_|P}Q{Sum74oy(@u&*xE6WF3Y`V;>Gy+Hl ztIc<0B-7Y^a!v z6soQEtq3kP^h-gW}7EHR$PWveUBe9LTM$vVAlqUyz{lMfhOhZd4w;a9aE^_3J5y*SrOl zrq)`P`I~?@V8E_ROBDp^@nO^mJ)pOEAQ8~J%{Y>x51qvVN@2Tm5Mc%@Qy24>le0x0 zSKZUrn8?o76S{HBb{q9LIXrK`HrQ>#&d7FVbCozhUxtJQ1O3Ohbxfdck`*o&&rl$| zZ3Ja+e5Kqij%Gb5Jaw?@6-avV-NX1~1MJp$oAl1Dk5Mb^?=bL8jqhe`wEK{| zddjr5?8d!xQ(cR_@_&AR)vqR74>Y&;{F&_7)YMPERDzVSV!4Y^dc#4PW{Yg!fHbwn z(WpM3d(gsdZ0kIIZVeF@aPPq1L?nZ&lsWR+jbLLR~0-6DHz ze6vFGPr?1svwG;FIDS$)d6K%8#V>`;W8${1C>#6S%|RF@uJ=ftlA<@VuJD%Sw|sP8 z;Va*>d30P$*Ki6>XZ$oC9aAt^jUkq&qXoBOhu$6?GgnP(yVW|2O=5>)ZzipmgN%ep zWW_RYrm0`R)A>{S`~pURAvWSC?XO*ZFFSc$N@=n5G-nl9)3!fZa1`sRV;wK8^A!5G zd!G%uD%uK!<7ih9l1rB`nnf*&*l9%TV1l(@NGYbQ0L5B#+F)ig7(pglR50dt7{TbD z4`gfcY@j%mp?$5cAbCS8Uc2iQlD6fJW{|h)bbUdw4XTiPhcE$#`gb~4mSn&kHB+kE zVBf31IO=N0`tH4G0_}-7Akgs=l0A6&&<>O}&i1mH2Q2*%fu2<|ZzkViVtB-N6TcqH zBy<1rnB|Z(Mf=5*dRKr9O%{D%@Ph8MO0J#V-u-cR zk#0RRXKiwzV=wVbj{}j=BJs22QlxnT$(=B^BX)R$_i<~P?}jq=5k+`>$>PdYw{YWA zarIKo^l9K|gY)z*_DuuZ3(t=#Q-eFBgYdSaC!Vtgwu2CUvW1HKy;elZvV(9l!0l^5 zXU4Br?Ll1G%(-;v9-wo*cTs3y(#UBp`;>bS%S$_DuLgHB{8W-d3jEZ04WdGYH7N zCJkw$0Hlo=w@p;1-4O{(M{vXX40sL@$;aeAUcev*t12kdifv>9Fcofu@NBiRbI_Bb zCaMBEsU=EyjQ-m2&~~AAcZvI@H~+=y4Z3BlXuonKrM$!M48Fm~+8CUROq#w`V^LBp zEmy+(J<_@xb1S>2Dr;NV_?aUhlzbK4xxD z0Z`bK1?U4Enxmp`fC7pJc|u?f+7|Gwf_CCpsc)tY3e!kp5UHvmp(NTXHJ@;w6MVhE~D(xfvvGVOi8FOyq^$D0+H zr#1uTseumfKSz2Mu)UHAGn2@P8bAsB8Pl7x0Xd!NtYXB@ydJD4l;bov=aWSW$n|(| zLk*nHa=pxMo!4dzjjanPz*{42T3{1z%otW(e_yNtpK{%s6rDCUt})eYBG%wGqf!=k z@U+tKw0P_~kU*BA^6Gwj^Z%T@@_!k>;`jX+==nH2DB1daBfRu~=8U5Z5-cp~U5e|NGonOm|`z5Ic6_ z{rny)UQNmRA*ifXEyhVCy`LGpNqJmv862K&+l@7UGR*D#aDdyWS?>xl@ zP~iRx=p0FhyrwqC2`I_31nBR}f<3FwhYOCT=TQB%$*B6YxpiFfmc6Y0D3-d@+hc|4 z<3Y{*ZewP97lkguL8|3w*^v5e)3&%_Zsbsumi=HVIWYHKnI5X#xKf8=yD&Y^F zBU!L()p@91;GBa0=Hxw;1v@V&MeHpp=+;V@;l_6=2L98uC%W_I-3Ojb*-pi-?>TjU z#ikmK25VAWrqVfDA1j!0or{x$+H-O-9wu4H%)LmvDoy&C<`Iv`jM0baCMw9@Qy>h8 z$?)Vmr*;l+0_ZcoQ+U?V_HO?UAj^tXId13z+6yI&#y->NiImc6J#fXSV0K_GL{svqlF-BK$;{xV;W8; zC@>oapi-)aA(alS>E1j=I)>8?j-LN6vTc7|y6nCnmE|s7wLOKpHl4mK)or|OcL zZ^kw_Hwnc)1a)mcDlA$19tUEu`xjwa$mcmIUO!>F*K#%79GAMjd%Q*4Ztlk%?ns>) z;cFHFLDlT{yRlHTlvPy~8%A^X-VMQcF!(4aC-uU0UXw!vRoTgC6gmMM)${k$Xgb+4&9-{1t7tg>m;MO~DGt?i;QK9YjFQ z%agP`BV&yqH{7On5%3KVNun_UY6AyxTwzoIz$XbaC_#GrWJ~|f(cY>Dzgr2fN_s~} z$iW@Ai!dbgbTo8~^|VRIcew8zOC{c42OSyu%N=nVt(yS;gz zCe)x~X!s(DcP*D$F}b*upJbbm{=nhe&Q7H=at`zPR2Z{*mK{i90klYBJRKWZH}Nya z|EwoyYC4QdC>~;D7%tI|iNUl4gxc7Z!G)^P_*9ZB4;Ma2Hld)pxsRhrU1?l=m+F`- zSP9Q{vvRLse)KxmVINZe98MqrC6_Xg;6}Yh^+PHinOvuGC7GZ(qLY^rsx6b*`=x6yVBp5 z#0EL{(j@g3^QwI44cJr(=hb>`wj_ht@5-kf!2P0%>~UX4a(%8cnSYkwyiIkmts-_!H~FFp!&N3Agkb8Cd;046ayH?eVn6Nc+y*lFoHF1r z`R5tqqB5G$>Boo%9VSDx``_IS4`IycsA(4^m7IJ21-xE#pKIV$%i*AbNBn5qNlv#w z?^s1_yG#j3I2Jf3floQ~_cv8!pZhXU#be!0g=GKwPGxyts%M>Hel#As>B@tB6?T;e z>%1(VayWlK(}hTJ%Sd42WAQ-66t-tGoFp7al-+lFPKeC1;TwE)=%Z|`GV7Sl9fjpv zK>Z2ac&Ayr#y#c2B-;Qih%B*fZ7~4D3p7j*iSehn7r=_ zYo7|d6bm2P^t+3doH;^U#e#OQdH_k5%{oqzCR(5g!ct!HZ)o(rHu0uXB>7^u-&r1> zH+<@!_z9J;h+y@DuS)UO@$u zBN)|y5!-RBsyIrG#zE}A0k`|GO(L`@hR|dNvjw*0fn{G0LrcdD&YVlKi&>o^J4W{eaiqve8l>I&0G@K3wYW4>sTqV(S?L@9~o( zVYXmyMW;);tI+$|X)*w{#5qtM2$v<4#9ion^tIyE-7K)5J)6^_UzTl~)12~niR+!a z<)+$!Kd>pYoU3Q#S8`q5a$FmnxQeCrDi8H*HsNLtQF$To5F0NKFtp!N)b7AG%f; zgkLD@fPITzlpDbQNT3=Ngxf5dsb)D?0A7sXhq9vD&*nAbSZgF zmL%&9{d2;0tt8|de?;?m$mmY2Z^#o?chyYVN&Du6Vss1W@Z_|r zm?mlKY}D}gpF^%(;mH|YCtA?rj8(BKjV;rYjlV(xEvn#Jc1keX8=$)F!}RLJsXB|o z%j97E>UU@k)$A@lgy_?YAN5cB=V;UXxZasPU2->jjcY(!45>W~i*P$X2kD##FN6)S)?xjvOT7n*9X?@^*u1n4A$VT`p=QP^pa)FTDuZ7w&W@`%5+&Ik8RZ$utOfi9U;P3 zH>p5(MrPK^by>*Hk{p~`OfCcidNq2PqH7H7a^wQO<8zL#@m{%FJrAB&M9~b~^U|~4 z(i+Nb)nN7|>uCk4_P}C7yl0?0y*$t->CFK_F8bTp7r|YUhQ4W4OLn>Oe#;-Vs861m zuyrcJYqqm~;SZ_VyTcb~ln$0e9g=F<s_R#Pdm% zv4OF(jG%;t2H^bSJ@7t)<-0o}Sl~HegoSz#{Nf$J`P>kE1Rr5mn$vq(<{atad{-~6IzC; z+-SCC3@G+=XCq^%xiN$Qd+XVu(k?TRlkGDIhIR$*R3W{ivw(b45@TyJSWG5F#cOf< z96T16n(R=xPfzGSHRCqT1KDapnAZ9YPMoBXK`V`S@w-+WJTim%E*w0rgZci==&eje zpcKWuq4|`KkJ%@Dvvw5xX@;EN33j~kSx05?_vD&Z=wrt;@ma?!>9%TCI><@VoFb(Z zY0m6C?mO6NikvF6ewM?11%Eq?BOs0dGomnOvcIUkK>Qvny}FoVAKhD3)$%63xl(!6 zLMbrwzM<2mqkG|0-JBVDfx6~9rV@-T?1+z0cpLF+JZlK{q60GbZ>j?R0K+uc%lWQV zF{C<5tlvFx<|^W-vR*%Pjmc$L(Q0CilJBCSPa}zwwWM-rZ3ru%%&>3FF`z#|LH!V{ zb}A?96`W`!{v2KE+kP>|hcG)wAl_=huTmV!ygToopeg;?hIj`Bf*_>?4+zr{bMIc( z1>2X54qcu1GyYCV2Vj(r!2 z0%zDrT{V%kU%4>Gu;2zyO^cmnK`EMrGY`iVsD;A=)YW5o`_-Mqt2bW8qCWhWg?ji4 z4#chsx_-SJrEwLam1=ttD2G#Jm4Ai;LYP&8OSIsrd_P(-TTidIqF!p_zZ{0`M#FIF zqvN>7WxuupRLklKN%=$*k+Bs7V{-GN$NzlUZ;04&C}$R&;->`=AV!O9zs$7a?Y*cP zgXj3D13Is@K%PCynL%Sqa7Yyq99aFJ0fdOrq8oqimjaSsH0-|M#6>{8hI z_u^*LHMgSEn;!0{!YU;s9WA>gLY@+*BP?95wWxuSUGOwb*j?EWH1A6#z+(qEwGVLY zy3EE*_g1b2tbCW&GBLB``L97@{`*LkJtQAqc7y=gV+XFqG=G+ff@z&0?JXx+sy(N3 z8dir&e1<7@eGkVo@~mn0=H*8qh z_AGkUjU1cy!`t{G^Y83+MJ^CF(fn3Ev};e{c{UE~)k6(9gz zh#Pzx{Tc}*2a~goIy6AV1}xB~R+JY%mba8%ik&TJwjY9MNscs58=ft(l7;dqbz*YA zNtUAyq!gPekrN&;ZbcImqq%6Tu70glWuLe9$=nY`d4V0hI+? z-F&)QCg|9j)>K~>$m7)HZ~6v?eq=>E{s)8xS#J;+3O-5I!3Z)h!rV37!2PMEBODVygd*88F(&DJn*UjPX@KC2;^O_?V)#EjcB1&pQF-xDQnED-SHQ zlmE$a19x>D;I6KJ@hXBcKQ?`)jPBGXMjrwU)`E558z1v(Iwt!UHJV(dF1mEqG;BA- zbPN_2tv1AcTME5e3h6Gi6E5*hud21TULPI@Hkz51hc0qGPV`4-B4S6pWonF!z4Uw+ zbX9$C*vo4&?m{~Y51E{`AJpo&Ck*#wfGl*@@0Y261}~W!BMs`gRV=GhciV8b0-)bz z54vZ|Ag#t(Uxgw~%1Pwq_e^LwV+vB7G#(bTf!A>WtD3>Jd+IWHPsQwwYs2dm2d_a@ ziqY`$>db^zx@@8GnzGZKoo%^_B=h_=8^wAa`(^d)tB{F|;V)SV|Z@L#Y-O@{oTi~Q^K|7vPZk^!N zY`=9BEzrz9eD!Ts^AZ@{ew~CweHJ+L%OA0EWt#6wZPwVHvGv}C;r%b_-U2MHCS4b8 z+}*u#3GS}JNpN>*+#Lc0C&68U1W#}R!GgO>aF^i0HF)ky=AUzCX8&i;%-PS`ci%kK zboW}Fs_L&lud4UkOy3qi{gFIxJ(udf7o(>wJlGm>|NfiE?J_PgZ@SY0%NIj4p$4x? z0@>r#9h3`)5yid$Jc5uo<0DT~!nEcU7i;VZrCN3U^)(|KLChD_AyXT#wxm18e8+}W zy2FgC!%^D7mq%YtQM!DZ=-a_LX|FC(X1(_bdM;csn(RTs+ci+9RB31(R_$Oso9gC| z#RbMH0rjl({&6148+_vd5@hc^dr%e^;)JxQT%J0n|g~4800L%V#&U435ke&P?WBcc`^N zpYxsLj-|g?S*Xi0i*X@#N*%LAf5BJF^}bKBSm<q8PsD?nJUs)V%;VpJReeEJ|>q z|7f--om&@RVGL-UO^x+$H5~XtOJ$v#H?%~nmR^*fe_cgK(j15KLM-#MZj^G^zUU<* zD$S8TmeVplAlO|=tW&V%8~c!F26nWDA_&|71ivP^?GM~|G5|L|JH-${TJrqFP=8pX z*76R|TF!)^EOD6Cj<$R#5X19lr|R>!ko*-1f2m}D5~a1-Ec?y#6EPF#%;jna%-cfK z-6!$ypes!dLx#k%cfCy}X$Xi0TM3$OD$(_q^{UiMY8~0*)!J0^RdFeZ#s%0mlogL( z2D0$ab>D(iT0x45P&b9rAO@ZhCJJD8n6w_lFoe>TAn^M#CK6f)3d^LV;Y*x^2av{z z!dowU#{s>29Q5t6oD(|w|>SfIsY zh8RTkTPCSMv(|BwZ@Uq4@?RXndn;HB{<7CyeXUwOiGaN}1?=_Fa?f*CAQmF}j!Abu zYX%r{YQT`knoymRY3g#Z&!|SR^X1~ZWKjV)xRZb2;Fbb7xD~ZEC_t?UR{Y*nL#;^d zW>_?|Lsw1Tg|LcbO~%ttwuNDpK3@=|YmRbm&=4lu#QJUTOV4{wU?T|Y3ZlItCR%!D zrnf<7e9gYAI@hfCNyA*W=#}Ix{e^UjRno1y=WoKI{1G{HB8DW}9M^}&^O!@5&TQ5N zG#n(cNZ!Mvi`(d6G15f!Ie%PSrs5SvCk@m=91$OqM8wm^tgsM47mkpj?L(!T68Q{S z;rVs@OE?FOq77oa619YR^q?fMc zKgXU3m}k|ZAvl!k!ZX!`_q!COv4*%G9$Q>HP@Uc@zgTnRzXrg3{Clt8Rxk~!@C>cE zr^+-5L(bO=#RMm<1c8Syjq{Ws0y%lolD{fHNQhqmBi5b2ga0ywnhb+oRB&?Raur2Z zkle)2y|B9k05=xQvh_N&!KlX)XQw&dQQbtQ;y7E$|$yYU%-aw5B+ zyc0|m4du{dFNs)z-VK21H98lLgG13}Wd)xsGH4C>C(rb~6Z5UQoHyjsh`eXfwUO$B z3EGXinkXte>y0DMs5-T*KR&x_qV^xMsbBY-?JzO^z?|Q!!y23S@rk+S!%I*8adp4L zS~A)hf~nnaZ=0!9je24Ald2HhtrP*25Es&v%v)H;xN&jYdz{7;QRum#ojm3`{;kss zm05JuXw>ptbjZ=H%&gggI1xT{akxyLEfgE5BtU4-1{yJ$S3ZvgV|&1WfPB#vBAMYH zUslziq(SQaQH{q^zODQHcZn1)h&3-g$@Y~YsLV>M4^e!@f7sr&&NCG#OJbXZlnVNI z|GJYN^eQgWf`iviHvldV0L2}_jI`uk-YXd@KXrBEQ~o4>X!n6I(-YCnwyTKj7`JTT zC2C?Jd(;$|QX&@D%8xh{D*cc%L=Th*XEgv4|HO+n2vS1-F0Bot+wFd1P zxooGaux7I`?zdJ`8Vf+6j192@MKl@!*f~dCB%>8<18(PG&An~>dKFLkptw3-b;3AZ zuBK;}uhe%Y5N0DC4qAWUAKeevt@>Ba@|Ib>zUlKMU!tggx<49e;Bi{YaEi(Uq8L|NTZtsS~>E6+Zt@ZQ(h zPO;2IJS(!CwM0PbP?a3;6ihi6@eov7Ue$W$F0Ed~tX9=#t1q&Oc5atxb*1S*UKamZ zE&o{&!xT>`n^!HOzz;pF2uDOk_b(ff=^i*ZLG}qIp$*dM$i7i}nnY|XMXJyLe={I$iwfLds>f4w~aFho(0-$dj zVctP2zPIn*RSmuMM2wtHjr5h?Tgtzzj=ZxGlnWs-y0VEtQkkz}SMZTZXtoYJwxnp< zz%N7S)8KAx!u^nN?;W;-gxR#Oot>(udKaRpI6pvw@9W4e)w2As;*!XI zT!vCk7nLF?nH%!(r($)5jv|FTK*BfeuNMF*o)TWVs-fs)s^UIAa z!;)_fC0pFB2)|5@J$>jFT23crx$+3P))ta}7giIw z`Tb#E7aYQterR}54l*IgTeSm}9jXk63KH%$1bJh5L7g%lLwBez!vzum7e#vwF&SLa zxR)r8^TkNgo_yn6eopZzyQJ}m;%H1{%-@7$)Wv8rgxhOKx!6#HLw~44!F=6Q>Bt4L zLl`b;up9a2Ku;eUQ9x|pSmy(u=I29+Cn$(BL8qi%P@@;+ZpWHo2-HWy5~OCalZH20c|!(CZYYkNBX!6LD8>>5}4ZdzzH*bUI1_) zK-1_{zWDl5JEmV)z(kn0bb!vK{Jr|Ctfa8zpz>=5jEiPEC^Vev1yrJ5@S7%z7XIng zsQ3Q!h{r*WJtb1on|$QjVNE&$CX9qeAE1z;X)E4{2g7@N8))j?0)TOwrISPcaufTp zErOubY_Y(vFtFlhSfKdWG6j!YoDP)cUqLoY)g^AoI&qJy@ilh7`GY8-Zmzc&8XWS_YEq9oVTA zcrNb+EHAR_R#|d_XoTjJgd+-hKS^K*mJIot;+SZ}I$&am^9&IrfJUzG^DT^*td1 z-0G3G-%i}ptD^4;xfgoQnhGu7-hRVFcvnacF2xGS0`E8*cjO0gA^$+da*%8n-M_$M}l{79h36U_d`3VrAd;Y@AC^w z?s`|tYX;i1ic-mfzjAsuGNB#yXgKQ85E9v86`#+WXu&nm24kJ4nv`NPYf8IulT(Ku z#FNMk>%3HrvCy$CVeJmk8YR*)Wctc}HJNP*r6Xd8KRh{TsmpTYkeUnh*u+}a!F&i2 z`-ppEy{kU05mNBZ=XLSktYKYBmpOUHMS;`ley6 z<>7Ma_0R6ds(>HLK*iRXs^hhvr$q4Q8JBBuuP=1Rz|)J(bBQwps0vt#GxIcEwU@5b zS@;Ur5!BqSoZ-s5-MpOA#QO_$W9}ffkkW)*vOp7GJox-L@<2N%&FkF{Sv(aHi9L$# zFwPZ23Z~GY;2->)Dy}oA z$PPp{0JkZPxoe~px!eBAR--7l&eWw^RUhzEjgFwX zxvQfz_aSL@YFdyEgb$?$PKqdOfq|x|>;*pU-61i)8cEHDaKv4Wp}Z zFkgl9k_EX|B1HI(T^*_*tPkd)cc1`ojfMCgkrCEQ z)cHYd$V4DN*N;}`)JgAol7to;O|Gmq%M4FA0u1k8Y{D3Aa;=(Wzx80w)YYf|Y&P(v z3@>fA&f(N?WnF>#fL$Bd`u%If?Y|i@oYg}%LJ*`344Oms9V8+!4do3CfU8X(AsDEP zew9^=DM&&3{W1xX>gV|})5(W!#46&;SVygAoAbwKMi0J&z}sKmDbT4l@%PAp?<(Uy z3h!_Teb%bfpwT!vTi&()#)~(+m)sGA&qkXEv z{wvigiH{@CXk(imIK_nRutLv2Kexwv9%*A1k?a^9PstJ-u4*f#2OCot6dd#65|Q}b z9Gb~77B@ExY2Y%oH6h;e`a56P(QOT-7@U`z+>xy=T{c;IWJu!j{4Y!$_|-Q_icAg0 zBgYf!v^u{95T?~n+H9dj#&*&4yXsknrDZlZJ;&F*uA_DR^npxLzKnMcwSndsVonb~ z%D}13@(ept&jj5xtb!+EvCA`?T53>_)J|Tj26vaW2A^@e06}$vt|_2N;AH{;*tZV= z_GO&tYt4MpE1uO|fCEaf5JZ^`vp(xBGXx60c{VY%9(HLBmEzv4yH6#vhgil3x{F3v zj962rx=dFxX7TWW9H%rvrcjk-RoU6JB z$qv8JRdV&WS9%EuZ*dD3XF3f-NL=+4N(p%mYQVz_YYq#lzqDC#*a5(hdGH{F>uXmtJiqcmc zjZnmgLinT@;w6S8M^Fhm8u#mQV8D=XBs91Tisl@)nD8dpP4IOfnhkp(WGEqFr-5Og zA!M$h0;*d4{AQ-wRy?E*q))VUN|(Hl?;$?{q-sF`sao(^GjNjd&`i}nL$k=b4h6t* zVb!8dGn`fUwH*58uyP*?8V=s^3+&_eoBDB~Eb zk|s}08TS`Mh)h>wHD%Wj`jX@_I@Zt0Sb;N7*bcDbb<-MSS~wY;oK3M1sM3c@y49k@ zwpfTZafQmRE(w%Mj3%K!N@RkA*I? zGPE(Zyi9-{D@g3qB}w5P!)3v}$xL?U)FfHR)U;lhEA_6GV$YvI*X}LOP9M^af~AgP z>C$l2pDqn;uwY;8NL1p9pe(aa@2aQGPQlY4(S4~p3emB(;RnnX39**pyD7kIeFDsu zzdc~KRsgfb;smThBaXNl&W7mD509xZo_>|3=#4<6+0|Jiclj(5`|1^OcXL$54@Q|2 zR^r$73yc}X*{wCJ|M~OU@R_qBw8JY!8%Q;s!{=IBvihWCT3kTku2ER!Q#3 zYxdG~@4vHXY}M8N;$JZYD+Eg|cId1*JO8#sqehM$dxT1 z(f~h3oVnH$H&tC@l_XWq*VN^`CS|y4+7}MyR@@}h*JV8;YP!hd%%w%db9c3R)66qP zE~EKIUz#5J?HLqG(y$-mCVR|*Jx+S#{kKE)L+i>CRT|uA43-YuJxPxeVa44aBH9jT zkSlg!i!|MdzA8%xs4h;(oT;NN-g?5j@**51zE$5HddQvZ>ogg|?o64)ff0BgicGf; zC#9x5z#~j0HK-i+a;xhY_DnsYI|FWDq60?g140O+T&Iz}UNC0mMpPBw8=S9KCA1Sp3Y6}d=GQvn;lAMOpC4Rq#%5YVY*Zw^M;y^9VT-`6SdH?#vXJ_ zrg6Lz5d4BMbIdn`5`2ZxH;@l-`o`xx#hHzn>Kmn|_lp{h*7N9;nuGWNs41*hIT7zg zuz`0uVLf4BxeqI0{d_R$t8#*R?-wz{VHxllLCljG23rLRF@@~~Hu>ISQ(249GHszR z3&Ry}wEAD3zwGxtxQ~4SDUZ#AXvO{@fL|8GvTwqpwe~1;O6aK205&U3N$MCrQIH+z z^mfrc_R3zxh?e?a|D?$4oiT!OIv^KaUcs~Su|RJrbl*Q&giVs1bgViUG%w=zaP~{I zg+rpxvW4`YZj~gG=Y8&0UlV&(Ive#NCm>4kqTi;Hc$XIcAV?>2R?bVwf^`!EuqXvl#dz%V+ZWzYgXoY-sHxsjk@4H*h z)aN~RP>Z@mc?0%atAC!k=SO=Y>uXKsgWzACf?IRz*vA5fk$B)cVVPpMoOGBCwQ-wr-W5gu^gI4aevp2yBwIsrSdHDv>(eJgk!l3R12)5v zRillIV^+&o1xM|vXH7A-0WCbTOYQl)814AAJy$wliELJ`94zcne7cK;Jq-iQ0+8yp zDSX8rtype_NniMV;x&}$e^ig)?)-zN!wb4zer&xDaMAIujT#?%q^Qu$EGxDi0jN~8g zL6&xhrOq16ej2R>JRUC1Tx=7R4JmAtMFY+lYqm{FM*D==nXRfu#SkN4dyZR1nm#rP zlIIm-*9aBP$d~QLf%VkNi4hi8%BrCdjBE8v**AWYc=p7r*zFHHmL?~sTt68D;)V)?jkHYav~|x$Rc`QBtb83!Ka%v^eC5YtB-9_F49n%Tz#LZ z!eyfR3g4b6ZuWh4mDk|u|4cd}^R~Zm`@Q1E6udzU^b^-tsJc?&%Xhtz{xjI(JGC-8 z3zVfMwMnW_*7S}RMbe2wno%!PI`FR)_4zqVEcgm>;lz_=3_D2!hq`Jb81Vb$OM^+* zSi&~aHR>`qt!6@F?_*Zo?WF6<^|ebAR+zZ&mMU~-t{wf$Ai0z4<{q*m>b43PrJHOd zRX?+0=K2|T1jW#`PwJ_^B}s9(l`o1eGW8^BKRnRjd%TjiZ4R|v5-5p6JZPc(*jR9% z<7duWk1 z17nH_eQDE1UB;u`xd)-=ag3n}F+ zFltj>Sxainxuxe!e=%4ae~*j#>Jo7mOKc-z`JmvvOgX}e)_6lD{SM_vjWVPff8Lg- z(7569xW1*(hRyPk+xTSm0Q0{v))T=}%C6Sr=Xx8K3DQ`FT=zJVHus9}!qV(}nb=e$&M0I7$Ylla~N zyBi5=Dgrb>(>o99LNw6nnHS0;PtB=ARt*~5U-WkP#H(TS;Z&?a;M0cm&zGEU&bMad z!bFu+bA)NiRQeIwt4oe7^m1$iGT*}JybEVc1Th@-;%0-At)nDF0%s=tFB$A4YXrt%^FM0X`>?!yY z!8LOjZgqr0aQD>Td?-K5)jn6(s?GQEtK}Uf(^dBOHM=b2Wy|rst7ucW7=7rG+wEIj zd{uZS>SF;b!nI#~yimU>2;nfTI}#p~88h^@{Je!vF8fY|M?~L;l>dax@@Fkp$^%Q5 z$~hjr@Nf~+wmyH}^s|@MmW4~j3scPbf?U^}3|&5wLXieK353_sLfckNj_Yj|E`%Oj z1KYYlWM*>;rHn<|K%GGM-M0gW6u+CX22>1_^KDu*4^#+~lbaV&`jrkrxU-Ynzy(bO zaM8sFdqH5L1m}4)?J+17-|xlN?xH{9q1HLk7hCC`h+^Ih%}uD*5r@DmB59ka3=I2n z;g8%oI}Oh#a9kJ1F7RZo#y}O}oUF|21sANwXwuCeuIM4hZA8${@NJ9 z!S!n;RydK@$gqk`r{Mh~W^OGipJPKena;g3ONNITOfxKMTu2ZZ9Mz`E8C<`}w@z93 zPX(t)@~Q9z<+1hQMg?*|^rfa%8(*-t#`4ph0jm%kz$%1*g0XGWIBJJ$Df;aB=dq!h z_Rc^%sYsod{gRp9S=X_Rx4;Q98ZVi^N{u zb~d~8v_`0-7sD*o#$=@{JFVxMA|v6VmGoQh5B{k0EKZwy2yRyW>+1UM{g4G($ZW%tx7z-}ffnP|;JTyzx6eNf3$CUc!mnL*T_@ zlAtuc2P){R@&u}XRk3*n(ncUJ>en`Qdb<~Ec%eRwJq-hTSkSCF9aej$7i7_fYBIPw zZ3^v-E0j51iocd#0;8iKhNlcl9{7-t8;DKIbAwR*Tq8vXF^ftsqw;O&chnmV^H}eu zg-m@DMr-4xYQDP>M?{v70l@ZRcFm#mw@~r6wy4;|$ri4UF(xP`(@K3{yufMCK1@(x zxdHPhP;}$~Ca$SmQuOOH0U({~>ZU}+CloN*Ab89rRSg^i^)RU5aZNRsa>fyTH#82nutU}TEw!MAgP75y;$jhL)75XSuuOJ(Yn#@?=#99{sqm? z5!k84@skkiD?R;DTTO&d2(Nj6@6nQi%fC{h&y)>^u7%e-Zkbi|cFZkl^J5rni@$% zX7}GO?q5%x!$x{3JYoh>?--|&?fTNCQq$!i2$M7r=@(qFxWA8Jn@dV*zQ-3zrKht$ z-ZVq4sz0S|&E9x!-?oEaUlw7fn3B}9_dvEB3dP>bi#cFOtOqS*qC&8cj|B}<4_IW- z6M_~WvLKQ6ie?$inDEhHk%wZS9fW*{XZ-vXN>CB=z= z`U|||B1BwU`$%F6q)7E4PL(Y~xW!keS}T489kw?Em{9!7O1Z1jXpGt1;xjwg4{w(l|me*1Y64CN}%h=ZH zCv@-?&0J=^>$%Te@R%*|n1jZ>BBp|S&%bWA?{!YaYK29fo}}i$vFqI+-xU5@J^Um~ z>8VPd=%wq~J>EBTSenHs1?w6dFJ``jTuSb?fDMu`<#&gYucff;Y+TFr2vk_hx{rr^4w>J8DgKv z4CaIfSA(CI(xau=q%;Kj+sZ-b74$@gcF{YYHu;*e$CJ$9qU|>1%P!NV%7=7*jKx;0 z8WR8h@sK8)ye2J6gL%26Z*ZK7o=ww3ra^5MfSh9{5coPcblepI!(%%#Hc3oU+ux)i z(V6;P(J*;t=XD8wPcSJGIAfPv#_>CArGGXW={U71hzlq^dfaS=6ga7(bS5A1^jx|| zYf~Cck0Be=yU_aOTX}S4+P`GU;7af~s~m!o?v60mJFH5?IlCQzoY&&pItI-b@JK8b z&v(HK~njg_L%RS<}j179td=mBx;->cC+nH6R8I0AfG@ z>nRq%dUAwfMs4h+ns*&mIQ^cw4n!M3VT*?9uo^{VKWD}5PiDOp*FWd!tna54`3Dr4 z(bF(9gH_)N*)Xf|avUz;3&G3h8&bS_cT755kRy9@n0JOrFg%nj`uSpxd9w@#*`0w`Kp20MSVzC3SiZ@+4 zvoud#7}F*xBGb7X9u1i@-$9yJ@e3sPyrKz!kq6@wYssFtx?baYr1= zc)KOa9^ix%REvXjk@|uo19Ar~#HmV(?>k6zowskFuf1XaHtA{@SABx^y-qEbnst|C zE=aN=YgIODv$5dK29(EOUfd?g;(GnVYlmz9U>Rt)Kx52{4_}@=B=bX1-*v2jDQ`m$US5vKu z_dd~$jbcI`cEWx->Ybg3x5T3N%e4kk)N{Gh_-s-)LkXF++R(*nt(yy^^J>%h$dun% zBkhU4XJfwB?m_0P8du9PcV(ll*I8h_X&`g+JIfWgzKIllzsj=CKkX*7^30&xa0^(E z*BcZu+wOY8;=^l5f1h_xB&*L@OEzDY5CqTrLT+7R-A;*bz`=oAGXDImemM?Qm~Er3 z3_+Cen+D;1pbO-<#h9{QctjFc!hr=JzURE;PFJUbsZcnBmgKm_3^dpY6puvZcA244 zB3|KLIiO6he|W(chQt>a&ljk@EJj+D_qx&ZyTY`E{*q5cngqSsYU3h?d5D?PjlL8t z;Ys+MMXpSc*O=uh2#?}RK4M@EnYIPA$|ND$Hz?*MzarJ`9~@-ceB{SfcWZx!Y;>40cdW&*`n7q|1O%1D_ zzez;{^)~&6ASZZH(71gW)orgeJZe^vpnKtjWYxf&$DoA{LbyuR!GbQ4sxK3))%-0U zdgRQ~g{ltBYm)Y9!lUBW>6{=C3rgaHl%KyS-N}HYJ;rh>zcJJ`Ml>}cP_pAxRuwgffZ^zE)-EcpPNl__1sE|gDlZqbh-$iAlxB6%3s z$Q!2au5#Yi+;5QBw^vB6Ithr1Wn(T*Ovs*RI_$OG-^_+vazUvNxK>zF-NK-aIC02$ zYAj5;DBm{d8pK{MExaDy`9{+A^U0wZ~N=p?S3|6wR_yQ2Sg(=-k$b* z=$N?J7De6O67Wd?qiuS7-^q0b?&|t}iI7zvAM=J2`+ma`x9Heq3)Qty+Yzr#`{q4- z>hkfkwIhXFeDZ*rg7#P;wHP^KOs~QNM0krKLuL7o5}fZY1kDKplkc-iWW{#UBU0$I z`;Uw7Y5qpie=LZHd9;eR<41)?A&5PZTsbIEg=CG%?VA!*_ z^n!E7b*0)rbSayDv^(d4UF8;s1Kp@U^W8EOs0vKGWeud|F9r%ar{WIUgW+DdP0x)$ zp*j#J^*RlkQaL+owX|BGpH2#ta~u#};h?6}f@5P8pEaQ6DZ}hSni;j*_n4 zPEA>#VUZ|=%1$&*0}8{l*ykN4LQ^!v$;HF7d90SS*ia;R(m5{@efF^;`H-EF?Wx>z zRB2-OQ1<@X$?5g0`=3I_>%{*N5sUNx1;o_Y5@hlGjnn`=R%c{GZxzWKNnD^H;WHuQ-Fy9}q_`&>tyb=Hl{PBu|9SpR; ze3F9$475L9bAma3eVsqh)?Pp?Y)mDcyutdvKFkZ;;uGKm<|hS=2XS}z;OFTyinrRas3O_`EQaY0F9B82Mn~|zc}!1|82eVw>EH7g_rE~FUmX2ERuccJkACsj-%#*>N#B2wYFvMqg1@I4 z&p)Rc*DrDY{~Oi*R6_r&6#Q*8|Iq^Q{tFcRCG!7cDfrJMR22A!%=S;c1g_u4{~xKt z_b*WAm!|n|pbntdxcI<8`$Mn&t((Qg3kKT1r||w(NdK>}+n=gA{y!n;Usd%VZWq^o zlbZXl8;XC%dcOqr-!u-~f2!sL{yEjS|C`j@{~^`>j@$msQTrFgl>1MW7{{M^^Z&qB z-2Y8l>_3xJZU86xuR+GY>X-ll&0oTuUxxL+Lb>1c_(uiD@n;dhKT__0bOi9Xx06#X|Y`tQK}zvK!3@BBC(4lvLFdIIdp z!wCl3zfN18KaC|g{w$dI2ipD))&GOhb^8_ddjOFtwzdHwW|79B7zX$$LhWb;f`(Iy?0|*%YGS*+x7U17gQ!@mdoxjfY7n}dx zT%}CiP3@hmetkPDAmzjJdm_keS~d_bh^vY##1aDJLH8 z?qsfE<8BWD16dWwZ{_{hVSfqCAHym^9If1~Ie=J+oez+h{}hUymyiALpxAr-k^FF_ zSFhVkoA0s;cH82{EMhHOBMfNbp|HDHf+d6Do7f;t%DvLtT4&<1aKaOKnC?iJkicFD z7XE^VSEs(Pr@wTX$ocVEUNbV+?do@VNfl4A)itN~A4h{qUfOdSa~B#j?N3)4SFo5M zLK6XY_T%D&Fj|tPSyPZTA1FGL-|fMESFy<-R1E?tD;e;MHPV)iM}YjuK^GIr3+h&} z{8wtY9fUCP6xqIWiHszR1Hyp}pvwTS#lnmzoWq9z&@oM*W5?&gP5LA2bvCHn1(1rU z!{XMN8vaix5HU1pVajsMc)4Y~ojh<9-lVbF@&)PIPla(oPzxET;+;Uinf)h&22{`! zEU5D&=Dh$B8a)DV(j{j+hsP=4o;w{i@ci>dQST zu8Ij0=(TD9uFeq?G#7iI4%X@GPtfepbln;{Ag@mEbQDx;{d)`FxC48{z+w4a(B6a? zov}?4#nj|s+v8`h!lO^x1Z$?{x7bR-o&o(6vY#L6+gZGSP$X47-|i2sf)YPe2r`|` zl??*LQ^c!*w#E-LYT4n8-J-TZO8HB-hqP|H641eE90>aql57Fb?jV>GQ+$-5`gqa; zC665=sgE%Dv!Fl29;W?pzrWQQh3DJ-e)q9~<}1&z`b6qlc!s zu0~LI6t9WuW0-gCm&sb*uJyJvG6Yj;Ag6R;ZM-?fI3t%&Jx;uqe?Mfm^?u-F-{1?{ zmx8;eI|ukQ(^S*c4ZBUo&9&s5AeMBeXV&~e%2Dm{*$6z| z808SCyKhX+4+L?;x}c0Kc&?RN5HEgi|Ib>F2R5hAEl&*>r$;Y9*7sI(^FBvuaDnYl zUQ-rOE$eXLehJEcuuN)5CQB$t{2l>xw7W0rZ+B0y#m%svar7Ee3boGezv=FPMj8=e zN^%H;?%D0~rc-Ag+G+xoKiV-u%bVaf50nanB2NhIlzV;c#ts*NcyA(_6&O=Z;f@)S zVG=@FO&+$C7L1O~4olY&d2K~W!eoj^8p1?~a*$W?7@cX+89E?H(p${V z=BMgC9Ok-ZI>s{-j4V5f$u^9Ucm#h_{$>zcThV-3nM^cvmx^XSUm=r@cHNGoxA zOCRdk?8TOz7)_jm31qtDbi(z%ei#w;)V#mG7W@2x62vhOhQ*G~+u>G?aNQ}&9}Ai( z5c=x(8Oost$xqA?qKz#p2uHdl4#V96N{6${#Y69wzajC9d5X@C?i#UrS(jG#lOo=f z9rbIxQg~Q3rdGGD4#ukuhHolZ-y^~cAbUukv0B62&MF$Q*t(+52u9dZF(cv?_gnL}mwJ#3`L^VHGxsY|D*x-W{?Z&d6idRpapli|Z7p zJkS<0jvB$?`FYlaUyxo%Onp_~Llg`)nmIqmgGbfrp*!p_62?~U;5u`CKsOrmPB*Z` z5wdSpod1xXB=vIK5I@|86`J){i0OPi8DHyYIsoZ9Fr>{ zi~7X%hwDKT%<}uD_EU}kM6syXhlh1bg zrH9VuiWMwi+BsPsLx`QR4uiBe9MeJirbOis#|W~c6Rub5BJ&_GC=V^qPyU^#ACBGr+K|69YP}TquAE{6-hwFs04n< zj{rLCYhfU^D|n5MCSLzxSiD|23jdN`mERQE+n)ql#9sX}lqe6Z&2KpPkh$L&9d_Qq}^|3%}ek(a})|43WC=n*vm0){td(269 z+Nq0ZpR9Mvk5jEqW`woHorO)F|=mzMzFNdfd7VA6aLp%3TvbU?DEa66cVAeaqS!3! zZ&ALT-E6g;-K=9NqO)d+m@7PPsXR8SJZ`z<-MO$}ux9Pp>xxnmE{mD-wqn|wQf<;H zWy$>5q;D8Oe^F_dfmMofY|OtcnD${}7)8qC-c;d{CFcGE4c#1>PP&c`1+np*yR+n4 z)9Bo?ox@z~&UNccGk+U1xBXT+ZsT27u2$io)v}CV%}&xR*pn@?HsEfc?P7=i>#WJ+ zWoKvV{6K0DU!09SQmj*{Vt#h#-M$A32wR{@;FC2?7TJ(m2vqy{flG51dh@6)M<#CT z%hcyVyNJUkus&CLPn*Fk2`()5G~6cQq9PbE81>Klyif?|ut_QG_g_0?rLOa{=$$U6 zzdh?rWn7EQ80b-Q?|rUN4R3pX5qy30z!(O~yBtSQW!4^ZnhivJcs^R~R16^TFMZ{mMpj?X(=nolqH5 zE|>NXQZVNi@NuZCtMkFyygc=}DP$SBK3S@1gO0_Xq5Y$X^>6ap@@x()qe&?2gRo){ zmXR8?sP#;|H=(qumyLbV(ftVvBST;I{(ur|zc-tpGGdF-oD_ubje!^hT=?DQ~}L?I@yy`QS1+!T^* ztUjlmkkXql^eBRhIT1Gch$ZE;i~6EqySch;Q+TWV1fM-X?~R(&#j=?LBav{6ssj>l zOx3Wmq_C4ScF#u4J(k{D^7Er-Nh-TSs8sA;(WAVi5w3KYxz$FQz z>enbZ$I1r^&W6q_9r6dkk0Dci%C@H6iviEgt+h>v62k8^fq&`pjaNz8! zu0H-igFET2!;US$LQQ8*BYj9&QgzkD7B8G{b$Py~Wo#-D*j@0^wz_Tm z=Cm($(}TROgi#Es*H8e>^2>!%j^cTuN>=9k&`BjGY7+g_S9;hl4rU=^lD5NV9tXk) z)+EDO^jLmE;ICNmuq;q?2JfE_N7$vMMrZ&>jEU7Ug>7-RKg+JJCuH+5Awhn=+c-Z) z)!w4bGca#FU+Zz6iD6&92q&y;6Yb{lk^fkX`s41sdE*Zr)VU|SaGVvQD@uw$8aH0YZa~ek3!fTu34avo4!KBVmoxVsXn(tr$JcdQ zO=5Oae82@P%OmlrhC4mIH}~%0L5+KuLtUH9-q62DbdKsf{Rnhi;1s&i2XwrDhZ;Ai zD2W5Psb>wx$-&#{^NV_Nfyc|(sgq=H3 z+|y~0QDwzw$zg*CyfaP|BIt2pA~-ssWjUNt`=dVhI661yz4;Q`cwR^7euoO*1{0EN zx-P_r27?hS*YBG+;0(n5(#|c((Bf;7kS9G<_>k}* zL}K{KAc?(Y2Bt`fL|WBk+He9N-O0CL`4*b*&5QUIdeGE}sNGyOU}WeGBE329jb@^z zCr>FXG%^>oIWu-9HF|g?9B638c{`3b;*Az{yn<<{L2n)G1@H~=MNu)HArMR$iQsoZqFQLi|n95Fo8 z#-7LXc@>S=@=t`I zqsl~f7CP5RSg9((vlCxNnw@mz@?gBAuYKN~JEe*$7`Y?EvF8YWNe`cWq=$Nq3ZI7) zvLQ%#X~NAEsjAFD$Ouo(Kn!ngCFq;|^1~eaK)LShfqM&U$VPb0^;(}vJAO#F%{2QS zR5UYNU=WUVn-E_t3`}osx{^%DLeF62su?Fqrv>-BFXj^mm%c_-Q|x<}z9Lnd9`Lne z&fSEUe3%GXWG>k&Fpvd#j#FazA{>~XWT=s5oQyJ28B>hDnG5-W@*M9*$5@3{f+UpR z#Ohqaw;D~H`G#D*h7Ea%kY4FvI-Y1QzS10!@m4609myW>B59GFd6C4&Q)t*JXrYp6 zPAO=ER1?7gu(I6RRvHh8CSZ0ZBMk#vwHK;Mw%>;FUoT`DbB5@(^NMIO^?6sGJjNr7V=Ys-qI<> z!^m@TctV^P*dp`>gN*4sFQ8AH`3uIrevu9sGvUm4 z;)++k|9eGE0*4L+PDyLw8;3rmx!*pCl)N^{>(vPB7z$nHmN_c#;5cEHA4uutsy$~l zsd3?=V0&_tx@Y71;VB>Xa5Z9RgLjz42=Hq_53jM^53*G$yGvKhdqgR?F&Cl;gI4uDT!O4yPdQ>wX^X+JUj3Vo|FfTtp zAy%1~i?3(y!EL)AyvCJ(NcHc%617!2fro#$C`QTQihH@Z_{IynjM^vRA+ja%f!6mM zL&+Q(gAH>AEElu-VblWLU2kzOZPc(k-#+~T0oXF0e4AjKVDAc3m1@JeX~6d*!!ZltR)!BAxK)klV^j^^4jS+p#?h94xeyBXeJt~R{aS~u59McOtI*m$pWBHhU| zTjsdo5~2=drdbxRZx;qzjZI!|&&C{bd8*K~&m9&&TT?2LCh1Q_ZL{laRXRQUawYrv z^VSAYtJ6ygih%!zuy=s6r0M=O%eJe#Y;~Dkwr$(CZQEV8ZQHhOySq52zdQf;{bug0 zJ7?8dD>5T??1-KF{OaV%%qJ8F2F{YGKi02{e*ydMG+VlCq4Cvbr3Z?bSyLp`?vi5@ zAcTH*z`~UDSzq!~7&s?i@dj=Z++dW3ik3U6NG}#V(OM}P*}pr)trfTlGGhtH=or|; zh|Rwu@I$wj#X_n5g`3}M_1sh88f{1RCm|U-V++4yW83CJth2clNf>_csnY$1iI)l>YVno{>CcOlUeoh-qd>H`^F2Q&G{=TP*S zEYRGN^CitP1fvp3=cGbFU_hr*!Xv`-EPv~Wth)5da z^0LLkbtT17Cr*p4NR0)-=X_O{0>Lx?j)lNkYCk>MR~ik3lRlt7(40YV^CbKVI0$Sz zf*D`|6YTa5WFHwynyPnQTPj+I6f5PX%t|U24G!;%pYz1&uUk&mX)8bOJG;TAzCoqrU0E zT8Cy{&Wz2ow`|`sUvQ1+U@iTq3z-$}YR^u(4k#l+;tQ5mrR@s?E0d4siR{JrETMo-*WE4Z5jr}E+b;m z$TjPW{M5c4@7;LBtUow6k@OK6yfJfERdarveci7^Cns)3?MY1Q8)b7_+O3Uprdqq1 z-HsQ)&J_1}Tn`n&Cx#|r#%9++BB8bT_O_3yP=q9g_wsFxzK?OBK42cwjGnLx&*~A< zavZ2|KRDMlN#RT7%k^7ODR$SaV0w<%L+h$ky4pl6rze=n^;0NgO!oW#+Vz$~OJz-R zIw-tKF!9=$R1Qv6(50}3F%D%cKvvLEJB;uXBkHzO2?8a9pdm&RXE=lhH`qnsGz=5= zf*Tazf$&*E6|4>uEug|HQKDq|ru(u5IaQiIWL)GD+SN!R_2=Cj)f2J0#I7DqA~24OK)!I2C6zy z3K)0)Z)K*DyEMTdOsv5>6di&@UZ$Vf#tEZ$jO&G%I!OtocYteUSj?Z@ol1%Ad>1a# z<-#+T0v4ojAh29i%J%->9-GOjE@zIg1_f}*mece9yFB+n+FUx z(_hqV-Wk2BuV=qD`wn`HIY9N#33>UsO9QE%y=sbR7Hh0#2gu9>$}oO|gPp{PT+@Tg z_`#)ylTaWfkzX04`Q;thXL@aJv{t`sp0hou+HT5kOkcr2mGqoyO`g|WRmEytP1R{v z){s=e$e~mGDuB_^SE@~o#}rRaptNu`Jk8!8CkJns?njoDcqShr=J>#* z)0J#I7k$MBnAlW3_t?)A<0FWL!95f_ZiWd<_dG{F&d7EFV}&&Pj)sgX?)p=7#+7Ou ziXafu2x&)GE7yLjLjD86uTWr--lu0Q^I(jJ@}uEA9mgxDXP$Y2aPJ#s$7=in(t??| z@)ncZ;C@lC%+bVj3SXvxrYYzta*SJ`2&Wwh3iGQWg~3FO|H^(W;rchE2{1I4kp5>g zIIm+fI5W#xE3k1TZ;h85!Kqm|7flM3=%D&rWkvyYJq4=C^0q*l^L~2x5d?}n9%@$n zIFDFdmICy%d0(!?&(ax2=ELsLn=KCCodv|il5QP)%el12n*~*yiCAy@y{1RaQY>lF zdt~Ob%vfKCuNN8Ht>%ztl)$EpZk#B3MtK<{%<3f1^YX{Q$CiEhOE9mn)a$vHbJ;U& z8ltW|ax8WE(6aKM69h^Tbl;@lBShwlSdSbvSaMU=X8@`(|4#os)ZOAjz(mqLSYsGt z*>{`$=Z>mf`}O|%lqzk)k?gZ%3m$K~^S-DV_4;EDU6<6-dH2;#olQNiksTlOJJB3H zOf<#_Rwhm2tkIp!fzw2OExTOXMiV_V8yB62>>W3$rB+2?MS&&EGMcUZ+4mq6LaW|A zl(tEuabjbieTs>N0|}OJ6X4DMteqTUeeuKFl&&B%QQ(5TBV;fD$X2y z+t#&`&eue($8+F%F;+e2%nTRNFSk%Ht}=5uTsJ`pYu&5|XE>&hF~V*zL}LD97-r88 zN95oFCjh|`0)oL35|V*m{f;@0SaThSC4l*pF3ou=vHYWJl@_AiMBT# z@%p9{zId5)f6?=KBCOO@dLxZkDBI(XmwM^@dX2uZY$asKs`6(?5cCEs;+9TFu~**Z!y!7foNdkj1#RVZ{odX@x3D}Y^G z+e>!12=G9tO91MNIjenH0q*4yQJv(2&6gQsb)GsWVSMxveys}DpO8b2s%o}@3K z!Y;sQK})QGSsb{}xCq)(#z?xnxXN>W=Z?fZX2oW4$B@tLnA0EcrRQOqwOv+7WdHPYP)5C{F0_vOAt+ZRSwB#43Yz5lPFd@z^y|9_uzM}B+Ixe%VL{UQAA{% z^p65tm*peYWrwzC%AGDwZigG4jBOX7W&UXeI`n4pZwAM}DIya_A0XW6)tdPO$bF@! z)*|Xo>ysYo@Kgn5yx-p2$TI~^4C_$%60tkRzP3A}ns!HA^na z7H%zI7FT4at5;~*q~@S|>C-_!0<+y*2EJF8AUZ&2^E6`TyA|*7PwgpaLQQH29=L(l z2YV#xUKHjvFO)32(RMkOTR%qFD?^?i!_PF2U{r1=kCH)7XB*EDd@gu1snFPV-)xIM zPqk4}ZScOll9Ie5O2#k6*X!pY)H4Fb;N?@g<*dDog|VtLZLecL zoJP0aOWR6L%oK9PQNolNL;IXniWcEviKMJzD5-AuHYv%3s;0->%d)~jZb1^A^YyrzQ91nIbE%JyZN-QxZkYPYZLb3qzejbxG{&^ zar9&tl=o6r$#c}CF`OP4P47muVoU=To07i5i!3qv;>^&A+d3cr?Sr-$OO<+v%vFxY zr46JH_dJSn>tdTU0>^^W17bb@?T7dySPijNI97jlm=&fZpRKY)+?tV+Ta6kBSQ<(Y z{e+l|HZ;~$I||aK7PJAf>ipW%!ZhzgnR<)KA`O9PDUdmLclysR^dg*@vOcrHLZze% z!sS?xG?~&81h2Ty`x@U*Gab4w=$C`H`J1MJSRUUyD2uT8nT3YemY=U1hlS)XID z(y`iJ_p8mi`Izz_YhOTcGoW$dhiS!cQqo{5nXKvd;RJ8dSnmk@6wR$$ueE(g4m}mL zS{ocZg>bX`vgWnKqc$)bL^!f;DW-0ds4@ycC_$G3&?r+5(EC+#3FZ!MXv%5(x$shb zS?Ik+NA?K)rsOZ_yrt{b@4Gc;CAuTT@_W_M(Hrv>3UAM=`3)I(IBR}VrMveS2JSaP zubcQWMTkJfh8oS$IH1^0^fd8PZ|W*0?@+ZAze&|+^|KH_*>F`OZ9s{G`ij^{;=Say zR-r{JW>aa?7)i^`oiw_pqq3!OZ5v~Mbk_Ta>$-=wo!mp?5R7Onc)t|FznvBot&e7G z3C8$CDMOP_1+K{7$J zM4`C2kHs|#tWhKGM4tZy9+H@%@vw8RPXvt8{o1Yj+0ON9+^6!Z)@$Zt-VWjWd-4|-D>ct)aLb$)^U1q99Fz-CoIus~ zBAF8ZBNJ8wlw@b`y?#vVlNijz?)=%~OeriSu|(=q0jKoAY-?^-TllyDVWI`h6?PQS;bx-xRAONmL1BD+TF8up{=5D-1E ziUipO^hRQhV3ApwsK*nNSYd!=*Aji`(QD%f(o@&L9c7_jA6_5RI>4JeR%jPqGN2uCra%h93E z&@ga!?25~|Xp!j2jZ?v6>py~7gA$e9cs2c(Q*KPd912ppl*;GdZ_PT99`?jNpqWe{ z^wmvG*!dUa${t&OUoNjLdbv#$j%8j|pb9fJq1Z z8kC1ruYSd)e02%@ZfQbB9?MAre2z6zim>q`_IIRVk2tpO5I4ZL;cC{- zqhU^uSLU)tXiE=Z*~*zdD7@SLcA;$xhi@_kE4LT@*x?uCs~+F5Wo3094QfT52d$z&FX#X#rgj1%?x$a9i3#GEiBnB1XnJeBlGpD;K7T%G1u@Z_&`ua z&m+&O&9>Dk)Ur5}Y~Ztx+{pxnYAguv@=Yv#?Zr z>Lp!VqRhAzUJR?o3?B!|Y<&9yKN&Pz!7$6Ft4(M~)#W`^**Ftd3T0~l0%!4&!0 za18K>+-xl?NsVvJ+g+Z=HKsCHEE%^^R8Me8n-H7DO|o!Z_uVxp*w?(OQBXg-EnD3N($R;6cF*^34QENVx> zI~lx%Ooq50r<;2vR*fXeBT0aOm&p{PC6hEW%pWywkCq}2{2h`fG&*qSGMYGb>-&rt zb;LYrp0+dXDTQnz6k{Ml^m#qrnv&FnndPsk^IE_uRzv;bMk(Lu%uCwusml#SIK2DI zvlSa3sC2q7geOi5n(iyFiDiZ*Q{Se$e6pifSc zw|q`b&vv}pY5^;6qh3=%W1yQT%W}J)O@-pU%c>N6N5SxYfbQmr_D8ex-ln0K+5x39 zppso0Pm^*nKX)|9Y48@nx0lt(4`+*K`ed`l8@_e_`Oa`zP^BmP2ALsi2+FxgB7lHG znhZk>Ova5qH@$kL!cghRDZhH|z@8(|Hr<^aUT$9et#N7YjM8Wl4%MCzeX7RXgW6g1 zZU(Yif;Y*>?1*nO7N>*pD;;~Lllj^FIFGty!PlGX13dvxRHPUNX=zfNg98GGE1}dA zjXTf|WGkQ+H*e?=HmIb2Vf0&y{gB=2Bt1>g6pkoTAKVBKStY^jHawFoGM}tHt76x!Xj`|N8MHvhHMpA)? zUmT9YQYA%k0WSHtJ%E!Z>L*W!%lg<4z%>_aUmm#L1s2Gk%v5#qNOJ~0;jL;q(JFO? z!0^M)lRz$|v#J`@l_@$SCFu+A7xmTh+2gW_EBnfmR6bSjpq=4sqqfPqy`1SMf4J-A zkxuVLQ}t3s9)8i(qFm!v#KzN)+#xNW8N~}nMPc`!LFw@)VC&G!&4t=kpnweMB-FZ@ zx51euPZ#xsz_^AWxVlQLLqQWJOvEGABCo+nI#|~v zlvI@2D^%oEBa{0)VP%`~KW@W7PL6Q0krN_J5@2OM^kxQIn=ZNGPW7SedeQRW;Bfh) z;zH-`yAaE5-^RuP5%y^TeU|s9kXL3hCeLBhPn4-(t!z@sg;0m4lD|TYgGzN5MuAd_ z7>o(ae48BEjsI}KB*rda6xLvsW(-9njbES1FDjg%0dAk;)vSVofj|rgmRSi2nXdf_ z&f)A)ypXe0;ryUlKQ-j1EY*OPF1&O0jm-rlh1*v1_;dI6Tj-jVR}O(GJ? zdeM{U6a=~ANG7yy3Hl46Va0yaNFL#nikO3dU+02Lt?eXiRw`~pZUyQHwkTDSK{(w& z_)qAgtEyxxw=66=)H#VAoEuvki3hkwQ7Y{oPoE6ya8x)VnY-T>ddG=TRoC($U;D={{D@VpTvi#*jN*-vVuXd&yoOWLvxeLC&dn%B?+ov+u5 z2T5DXvi0y1uIJSOSNs^}Uj*a|zBnF^?LWLJvuw%`;I%t0_bceU?#J7v3|~M)&Yxw| z8!qYX0wG;UVO-m0F-~msV-vAS(wfz=hsXbHc5a&{b9as$$mN?13e}BWCFUcfuA~aR z^E+SHS9nWI%{G^pUr-uz_mT!Vvr>XUM52tD8PQS-urMkwj2d~e%j_VM2=B?pE}zB)-~bpSEC?aLPu56zLy z1I2@H8(%v^mqm)a3xy8%#{# z9t3qxvGJC>=H*Pb2!YTM7^5^!L6f`iU6;Gta4jZFa;4L6IhyADX}+O}Zu)21=kZKV zE+=ib>oI0c)(1!R=^lQR_s-)sdmZwT?*k7!T$WCbNOQCi9x}T+Izu~e+}CQiu%1y;3lF42O6M?1ugzS5@txa<=317rrf?1Y zrG|VtljSeV8njk7m1TdjkVV&jA{jsn((?x$fgmRusL(_x>vL$cPbA{{6Z|F`y+OtG z0yD}a40sHQ(~KtIl_wUnlR%!ORshSj^ii(hYP;KfoBd^ywjfo3^z^Yf*$$%N?`D05 znTAFojExyD+WNFL?$F?HG;=sNg}9?&v^BG%#AP6!gIrVc<@f8$AH>0GR`TVNjAD>= z)BR$~72e~yO>yc7ekm3m?uCjXh&@Pt$H|EQ>kYA55MLgD0M-xpP};6{R_ND@eL^h1 zh-I?&Ogd@mz#i+VGzS8cE$cTB2&+~DXAW)E#j+Qd+Z8Qmg@UpUN41Knue39vx-I>J zs(^;M^x`;5B^8ZlhtlSyq^sNZL*zctDo9ov5^vAVm^(xUz-8Er8YzS3ni6VY&!UR- z$V)ybJ|48*yun$A3qJkaRSM>COD8%vmp7fT>nQx^QE2}<(<3Yy3H5HE83zuYRHJ9{ zVUX@zKeu!cPktW+d(1O9dG8ys#S1E$^JRA0CS`NtK3Lt1>Swa1bYT))2Lp=Z=ZWIW zS`_F%#K>W7#K`nx5a>}5Sv;2$5xJ%0cN&rLRH{%DvBx=<50SD^9V1x7v%Nk zWdem021-e(gkCeN^YvLQ4GX3^uZeqcJnI+!y5vMse^QKjC3HpJk(FKsZhF>vJNL2y zKRgx9)OmY`0uYl5_rv`U8`=0sRqkGkBg7$JxPRjC&v3fXMJiHk=Z8L>)Nee?VT+hBAe>2A&6XuS~bg zsiv+cPzhy*EJK@BfS841F+Pn(8B67%SB1~dy?pr?ed_z(ov+n2CZLwZ+Ne}yTY6AA zGx^x)3m+`ID%LFES%CwUU#pdAX5ZfDEaZe9Vp(G(3bKcqo^l;$SNvg5FXvcO-fKiR zP--qjjiN@t{pl1o)%8xx@eHy1{BX8csvx;01U?;qN1V~gN|g;Jq>7OeS^qc>*2Q zL#7u_I^&bzke7MJPSrpGRfHS-us`esXVZ)^nPv~;p<+z`7|t;}{%4^$L`|>kF1`VT zYPxVdDUNreBg{G#psH83M2y9_~_(Ff2M?Ru8enmd|PtnC2sy^p0 zoT-;VuXfN%`_4K306H(aOHZr?cBXxX9kiB9TpXA)r5Y1=~h4`Ln-#W^cif%MQ;rtxk*~bijJN zZeEwm;baz{M?%aGrk?}ZPS{Yq@J2_Qjv{rP)|r;XX;JQCu{ipZQ&n!Z-SVQeBSt2N z*64grKColy2s9i|+A#%jVxrpJ<^FBJA$FU?X&4cq&Cx`5ZD}0Crj4>g)!OAtp15VF zo=4fAZkxmFV|vl>_vvIppX^6_MGTYmC7+%D_hGUKMEeqsNe2~L3+>UVlW^tGDw!cs zx7}a$hsRqsvqxbzbX0c7t-VKgJ{5#Ir>rW~d6BUl--wscthaK7nD8oq*8peG1~+ls z3$$=EoQKryZ1GH`j5`YvE_K10!!d3ij}48=GNtK-EiXSZnYf=UT?n#zs%*ael-Klg zLCITp=Y-BM^b_wv-ySY)^h@poxG6hnJ4bwMes?LaDDHa6=$tT3&~6{q@0HG7As4Ww zZX^or^E=x-@M01@^!AAuMPdQd??LV38i0l2iN+Z%2?J5KLqWOF24I0=@^39y^Eo!R zRTe~@wxiQis!at%Ej(=`3k$|@^_VX>-Vl8yKko3#=<*-$*gwSEpA^^0YH`%QfH?Ae zlspzWQGF1j^#s6C@PAYXpJ}eqb0WdTfj^+S(?NCMu#M<1aVO9;~LM+mZO zOqBaFktN1-eWc7#xiCOeV6r*OG4~#8_Ti7uCUgLr2eYz`}#f%=sMJXm#zm{QTY%zB}rt8IO>TED*@p48uFk>IZEjA(SsAq}#__Tgfk1 z%g-F%Uds19QygUzL#WHtF8znNlCNBroo$Oq@ei{y?)0DH+5wX=pr=-P+TGEVjV?7; zp{^(;vUe1gc(%r{h^e*%lNE5=Y-~-zjN2_A+{frH^k{s9RAO8Tls1H!lf1Pyb=G4? z25Wm}ZR@($qc&kN<9N7P3EV98LM=i)gSGU`yq-R8K1Lsj)mH)=t#!<$1P zd`f5Vb0DhncmA}pY1R63oq1G83guKxru>le*hfh*^<{gLV#R`TJTa^N*I^b=r34~w z=nU?;AY(_$hfI)4w`y5bFy9)Kup>m*YZTZ9@&Y>{0R zSi1PL+FDHg#58gdFV1K>?x{#Oc|GNY-D}@UY<`TG_B682`Gb%5xsmm8i-&Ud5lFdk zVK{)1b;|h>DEsM^AsRY+%OG4Jx}%a~{bFo-gwSa_5xWwbzFbkLua@syJ+V(cS>d}M z-+<2**aHC;^UP})9|cSv<)*GnfXkLkQZ5~&?wK682bVB*W!H*5c;q*w$HWK>!K8Xg zoB9^<(|OJL(|<0fqckJxbj z#!75SRl_J~C9>aJ@vaQsPv8B`bdnsPRDP(#WFBNyHJWv1$d$B_6WavPT=(VSDYAL-Fy_!Iiz!E+GT6MNk=O0rx0QGtZQlnXTi(6doRg0MP2#T#>>|ma zJqni~g_P05%NiCtRIITWVO|akfWq}YT@utsXiLg8)S&9?S?NSgH-|$qfyVU_r;u!z zwh;A0tdV%^A`MJ38wQD^aL3&`Zb~r$5#gYtF_ou1DlnU;x4#YH5*xM>S<1^R zr!!TFsv^NVLY|&;XBw0vZffKX!z-9ZBb|zQFBud(kK#_+Ba>pLwb@G(FcgH1+c2_E zF|ATy@r?WkHQ|iu#UXv0{1a@)8e||lsn$&aqEY%}UU9mEdS@qKnev%cF~o^vM9Mdq zaHMi!Ovy>BYOtfM2a$|M>Eq~3)?W^V6MYvF6`~wy;wN8vjme=C;LcX^ylZM@-=@H& ziKIkr!o}n)CR2n-^XFX!#3Ka}!pH5J>5um*M9l@$m~7t*vsX;3y<`=W(c}TCfg?w$ z8tR(5;H;-x#bzWUs6HR@QtLg57f{uW{-k^iZT-kk?N!8tu}*wgWo0MlAV`kED2>an z4^>^?wC$)AulP@@tZu)Q+TgP#GiY&b8SgMWSzpA^vZIzFNQ(otbM6nWyDsNumT7I7 zkQhj+DbUI2hSOxzCr7Hw>xKx_CICf4Sd_o&?^Bl63_kA^^_ zk+6m1TH;-+pKGV6aEoj8o~ItNd~Z0cCp=rkE~y(2F2%ue!M_6@G6Ke#FY?ODQB?C1 zBZeAH3lh;yGT|?u(@jDL#F6r&N7~v6WXei7^#QTUS2S@v-2|sP!kRRkI?R-g;x2#d!DHxiFkyAZu*Wsq3bzFpSj zwv~Kzx^jWNT1P1CQVR+^u={AYETXgI^YCYDP>a^!0<|uv(m2Jya;E+~;xpxQIfUoq zExLJg-E@+G_{8zI19e(w0;P8`qQAO$Y%dS->Z`tR zwN>zU_R^WKbH+$PyQNl(Ryi6@H2TpsoMA7HRMqP$x|5h(mR;cr)ch;GxtPKVXWLy zji#t)B^*p>=jU2wBttIT3kmjok{we9^QxvTTKulq$z0#UL)xMsFWBv0+j=%t1}_h` zf?-Ew;6fpB1qUDxr6?}`9Q*UeToK|UaS2C;ptE|Q<;<@Z-1bJjXJW)mIC|7}tZOP1 z%gFd@UHw?-IA86_@$onu=e!qXhL2e3kTiY&JtZt%^bvXTqUmih5hjfaBeI_FiavNL z<1J0mTAC-iP-(wWZV1l(rj?$Utjz79CaiJ;%I|x0+qxe4$qE13any;;GtUf(ZGl3i z&tOob6@rR2i#MD|7XGD2mgj4isFrGT*N`4Q+g!8W-TpHdXZ@Ihl2&0t!rJMHc8e)< zMqAzenWpXOQgj`n^n=mD5Yyn5m)8rL*o`wM(NnbRpwAL7!B8nvVzq;=W^jT{ZsJjO zark@V&D_aFr)o|O6P}W}OZSu4mM+K6O3qvjqtcuqhGhhvw~D)0{^mm)Y&Df9Pe(Rv z8p+b0o##8pbgwH|ckXNP&!(FeoK)AEYP=FfDO^Tf>+i2861uIGi^HiI0z2~WwM9)$ zO-~)|y1UVAGP*vPwIO;u?urBRX%xeH_mnO>U$}GyaG6CUluv5a8&B-KWERV0 zY5mBfr6eA^A|6A;9@NWTXsPK;OE)G&Er=;Jw!{Yo1qD)Sj~V^n5uhyp8;|5K5Jbq> z(a^!%&dJsRz-It#%jjDJ=%D=nc~OfQTe%oJnH%Z@>a6rl0W3`b#ev_^@GmWsjg6j` zo`Hh}K{31bpJ27K(Q!{`e0~;+P zI|~B3_vshTS~T~;zH8;b^ti45g-iaPVWEN zt?2Fu2!gnciS1wPse-YoxucVVI{^hh04{1w2}39AU}WrIZevP7@jqsOor=zOc2>sL zf1^qNms_U#7i+4?%EUy=#?C?i7uW%yS+cRwvNHju4k$CRF#oTR1Na}TvY6a{{ede!pFeI0bqv#difhXhW{!7_%Q5DfF78c*#E2Fe_Q_R>z|VLUz{c* z`+qqEFjN3|zJD4JFfkHn{@X3U#eWU$-+}#GVq;{aWnld~#D7cw*0M6t1EvcYA|U1d zXX5|1`^T&HU$Bbizh?hBW&da1|C;^V)xQ?>|9|fP8Row>9L!9#?96Pe0I2Q%L|mJI zm7Rc<YtJ}fNKjd`={ccS!ojhjwm2H|5~%N z5NQ5$2m#e>|DW!(2{-_$^-oB&0iaxFz;XMZV+Gg-j9Zg|o}T`1k^R4_09;B6O9~+#Kd3Pt zNx_fE{#3B&4-g1!lTwuNFHm3Ix=b*NTlBv(o8TK>vi~^FqF}Hziqiq#wcWql^KsyR8mkunG2BSpMd5spyn&4+zUy1 z^QCThkMwNd4-HZz6fS*hx)(E&IrJaJ6pG+-4k=tm6*rB~uMdkVz2}Zc#umm7NsD?; z!p6ixNJ@_v#Lx$&5@0MDc5Ld_CR?haJ~#9pcHVr!Zc@GJgLp?}Pjx05?*G_m9jhvL z{s2KKEG_uowf}!(3H>h_{72LNiWxwF|4;fSY-4C^^jEI_QMn&t|0DfI`Tzk2Xd-~P z^dH4l0OW>}t+I_dz{D89WCjS;Ujo(tWXWGD)BlSQ#K1=X_kYX(DQP%ZSs4C1Ylb|b zy_A;LJ{_+&ITNk>FPX+j5;sYV5}?P7bp80k;{5dZQ)tEw25BxSlKdj_6+qOgmL=rO zJ~pA`Ed8H}RZArbP$+`3 zJMrE**_!qPqBj6_N@MU&;q}#A^6i0$^9HifXsy&YKCkQh#`hc9FAH|1QYMCKJ`4O7 z{o~hcW4-&D<~D95kTxx!3Jaa~%Hv^YCND6ND6ny&=C|)Q9N*uL9D&qfb6bmw>IfYY z^S%=_02emVXm+_QvQ*pq@qo^TORCaX-Hycg!XxDXj}9BdTdIo0px!`+!SmhUhZsk* z({+R7V)DI@qgSXPHs>M}1jFgNYaWcJlW*$+Ly-Y}2S!rM1w|$%Bmm(fRneog0p1Oa zxEd5q`0dKHgdK)X9Z%&<_}xm{Cz>>D5Cw~%7YtPeB;=vJgdl(q@{?_K^qPATANVod zf(%Kp`x5uJ;iL9@#GY@@F)`R`j{XB37Q-{Xov7VzSrq*H$JUFZjPDxfZuIN=EiBS0 zAy^M(1ZJ=?;5)Wv{U~7=(rVuXQoKLO;6Q{K&_shyNKpI^R^J4{=^*J2$%@hGl9fz+qA^EjloL3J zZ21V_RjQ8ezMx4-c>%}I{z%KI%5_(AxGxk&eTmPkM{kT$n7vGFK&SbZtkopTR&QaV zZQ*^td|0JuvzDQiyg6?-!DCP2o1>n(-rBUC(}~z{jzM}eaQV|+SGenKUfH>DN7qTZ=(S<11LD*kgOez`Xm;b*ravYRk|{G) zu4=l?ZB{EA%j;RsoVX8JXS5BL^1mpn39lP|KmLB(M{Fs@Y5UTx0o#kHF)g&= z!m9_)yF6zlx)koJP5th5`p(j?^ZV?D70Nge(f*reFyxSODHI-N=rp=&9{r>GYUINu zKE5x4V*d@HbAjCXaH0a+vBcIY<^ls9wzV~-SmmCqnuV4 z)X$oe+nY~epq#IcoxGn(K>llfO0>@Wkf6tox@AC#=V-k*KK_3y?4SC8{o$cz8-VLn z0qX}B>f{sj*AUFt5cC%UabIS_5J|%XZReprb+x&xtkJ|WPaKa7mAnBEOL%VD=dT{W zh^8-n@xY$W1@`pM;3~0Dc_PJJ7Q|R#9iUXi)RLT`MG{@1z$iRs`(AA`2E&&}4 zW?rLDl3Ch|`}5n*Zb++yCUz=upK-qUy1qUy9JwBmu3*|IVX`iA%Swo`*2SqEy-(s3EAWfDOK&%@JkJ6m_!0BZE>)D(W4?zeLJc7aBS-gqa$M)6glNWure;PuecO`dG2#Bsh{3knC(T$V=yCxaOdaGjkCAERCtUz4Bj3@`vm*E z1rPLxr2(g_RK2e-dku>X;^G|Z#>QQ$To z@K;voSBW<4h2kds`e?}ng$L#YVNMq{3bEK))taJe{nHyfSL&_i}GD7lqMJb=D zoaa(bGx`czY@>*8KIcwRY+Z^wxmy)M)RwUNdMuhzknYw{_4Rx;9$vY>sG>Ro&KdYNmFc^&9|>vYsun{U}sSA z{onTuHPO|WH`KNiT_3?4+t}}0i5I3U+Pu~VE??80cs}R;_uwtl!M*R1r>WUOXi=>^ zUN%2j*|J#KuKWXu&gN*C+Cy;y5Qre1$i6{8Qy@vh7x> zSk-Ar7*$xUT(EArX#bfy-x%)saI+`SM3c|+d@DOyU))$pls;4^VnOX>%NCv=3N#&z zpZ95v3*o9Ke&>;K!8aBAw)Ts$Q*7@BMPV0^&j@`uTnf9)lwzk@yCJ~Pc0|BBB)MSF zr6M2}@qQ*Gx#-Yl1#X-}oD|#QcQz!w?6v_xyad4BQ8Fj-f?)?Rc2KM&`tr+QY>#1q zp!Q;-I1$L9RD*dC_GSV&%LE9S6y4GL9TYxs`=ZDt+RteFb}`zvc$uw!>j^}}@55{dh}tb7Do;{hDCKP)9uG|b;>GR&Mg*_O9XFb<|Nh8cpnWz- zrZ_*pt&NVjhrGVMI07XV4ehAws?%m>J*x9 zUn{QLe=dFtOQw|Pd|lacP}_974^^>kIbAGnW~_t5T3CLw*_?HMn|m)Wsqnqu_T(+| z@wi+qXmKqMJRD45u}rB{Yl_&+@|}3h2noqJW8UIKgp!#X^WIF-Zw;}GKw}0r`Og1| zLMtD=S^)gI>q*i8-GDa0S&GCg8omF;kEH&*{^2yg9~_%;aa2h!=T|P z_JJ8OeGQ#HZ!4Q~>g=QNmE%Q*=bT3|`*bWpFC=>Kx1s3vw?F0VfjWGeL4+o7JO!TQjh=K;X-rEtC?@!SP&!K40&r)z#Xn!!-hr)};+%VR=h z4vo1Nh+eaW-6VqvZ30OiyFJONX)Aht0dYU1;ND?=azTs1D`2)T;2$F#goZrehsHs3 z`gWVG#*aN^$>%nxCOZ5?4PY9Lk3#R{&TvG^LJ-*mEYLVPcW%`1Yo>G~)8^<)))=(? zzV}DJIOG0uIO;-8m$Pz_t=Ch{N;zWU5d07u9R<~QPQ!VWCsiZ+z(rCX*01j7bdIxh z$m{MJF7P6)U>_j>h@rjc6||WYVt7DWCIaD)#gB`m-{28&~{|*w$M~5eBe}UhOD&rnH{16 zQSEPu5JIk_?tHsz>no09^yCN&cP<|5``pmcq4PDS==d&9LTBjyHnt*8 z-1agR=(|(zY=0+xexmFBbhKZ;!4ukhPkx3_1}oBfyV+A}xE_%npkEU_G-B@qe&+*j zPX-=@4E1KEfV5N*A6}%2nxR;rL(UVxB-AC-eV%TY4<{q#kNRY1fc#4hlDt{&ogv~T zkKNIIWG~1*ySM?NWZrOCY^^*jo{C*kIFM zLzo_0G*qTAds9$;Mi^4jMIczkNdi<)M=Ng}MI-ofnQfTMo$!-sAe2>NjGx~0F6aDA z(SsNv=~q4Yy*~+ST%q(JC|i#$;HhXfX;|7`6h>)Sty%(5jHLd6vD#b&jKOpQH(fAeAholzR|b$1^)1P++C zp(qL`ze~rIO(V*-RbS_>z$w{-LGRY1u{=10t;h>uZ?BoG6|XD z3mdi-rm{`3$xA(Dx}`ZEXPAgzH@)D-$P0^dwBMuQ2L`hQ>%U1*g);0+1RO(MBZUQE z^?(pN_Dr^Vq7Q`C*~C9GQ)yI*2Ui!{23<6f=sR6P$s=WVwzVL^+cAKs5J3P2@u(-(l2 zDh+{dqR+!7YwQnVjc8Hq4BXE`y9uEz6eD}A>vY4D*L$F@gbXKTJo_hhbDMsvU0g3l$WTI6mA-PP;0FyFY*`FNzDxO=NYo zTBE4CH?cpUH<3SOo!XgS;7<97!^w6yHr1$nY@X(ZB)C8QldiD()8a{zY|dp_KA~N& zFH-eMFtzN}SQeMokq7Y5-mshr926I4M4J;ck(&^j+DC?6m3|BjiJssM;kG#AZ7Eg0 z>I8oJ7=0qvT!CJYuA@}^FK5`u$S^4@1Wt}pAHJA4EEHjc3Vf(@@Ta8LIs)gX8q|$$ zyb{jTrh%0l((L=I(fMWm3<_sLl6~`#ldRF}wlRX>uy%0>Br2mV!caE~; z+4g`@p6r^R*uLO5t(zW zoH1jrPsX=9!mL>qiY0Yhh+^$;@kKK~%7qZlni|p}WFZ9ITm;qVj%*kRPQaTgA!V>X zNJ@3~cN7USB48K4V&%&hh~}YsHIzc(2lZ_HoDLn zO9I#bbfxGw`bAs4Y!^d}N)LAj2co($Im-sqrz$xC!a<3C9-4tKW<#%+&e1gn7DMir zj{Q_@>&vbO62`^{_SP|A!+9i`?iasW1vKv$?Dt+RQM>R7=4hz36uNx6PtN4(=K@HV zI?)idh)#7&He@&r^yu(*g~4`3;{dTJ>Yo~Zj_A&W4;2si33&kPnj-d#s{j)$^aN8Od57<~lTLnrVGU2p1->m%A;pQ}P2 zs#GuZ`Wc%Dk=U(WPzlSU__|39B3P!hUhYNmwAUo2$+_$HvN`tHOF^IFL8bx#*n=c7 zCr1OK>IIVb23gzLK$_>rYsW|PnhFB(W{wgEs2(*a0znLN&83!11&sI=!*cbH)G{Zw z5h0|_Zr9g=_emIh9yLSf3yL$WU*6dMIRrr^}nm8aUAp#-*f8R?qSddtpsk z#8&{-ifwgJL=P-YYi2Jdbk!H6uKfh(Wi$jwVTi z*wh+Zi9O4G1KMadIY`tJfl!V%IUv;K0Z?gVtr{wZ}?`R=n}ka&k-J=HSS zO>9`A{XH|5!y}}6s^M2*hKQFX#Y?qyRTr|c66vVJ@!k z<|2)&zI6PmeUmAGO;IFyDQ$WIX(&m3LFTKoQBWBB{FGFtg|3}Kon_kNcGl55=s{|- zqv9BU4-cWCYuso1;LZ1bkFbOd9uyGl`o=39&!-DBTpQpt;`svTFsBo#swv{nF_ zb9Y|WOWgN_y`D?~hQezku&q21(bGPY)_TepfkADk3PFe0wv*)j7{2BKA5Tj9(5YF* zM$@-*GnVr8;Q*ZqDd39BuA~=p+X&)0F)G!@WVHv9JJ1!4iSXti9fy7q` zOd+5&F`|AgTP%hOL9wGFNL)_pk1Vh9{zHk<5BXGQ;BV6?e?|5ME5}Dtn^*hWd?f4E z#mV5}=KN;dO}!=v_Jbq2+%MYQpU*=E zMi?4JgaN&?5@V`94sc4{eh#a0o#%iRHhO8Zb-$_IZ=u!5q=)reD-o_+1gtAp1F=Xe zwA6|>cT+0KJ3&y34Ey|&6G{FK;TSy)2w{$=`?mYOKo!L#Q~U<73$}@jQ9wZ3!>{zK%5&QAd&Rp9 z^jR%;kkV_d`IvDKqLYiRFvMALAXYs;~U3|i%_pe7Pix*37r~h z1OyuZstZFn6dM3FAubREsURRR0f3{(Pbh?H6o@5>>vN<{N!x&(As6uo!wfpkaf0&b zLR_}w;1oYi_#W@z%fi{#3sGGS0-%cd8A29wkmXx=6p1%|QDSBdp+J}t-Z1!wCg4o$InNTo1hE*L87 zkS(6umLC|}RW=f~L?D>sRW=BAc>q`%S?50o1!J*_mb6sBx<;d~=}Gqvn^s!WLG&C_ z2k)KW=GD6|c0(9p#;QkQYcDz#Fy>1jUEPTo=cGs<77Mioia0sCm8)NWH0BO5o8%Y< zvxMhX%ILU+-D({G_@lUWuP?H41fzvTHwrVHhHhszsDK7JO5F^IePj(~ zdl=tc!A=FzS%1I@T7M@0T}Zx9(~x`^WbzQ@uroM@W|I*Xce&S7|0LZmw{29cRQlp^ zXzU?2j*Cjuj?4TMh4lj6h;nJdj)yI^!P7{|RZ7}&`?zjwQ!Rk`l8-+$YcHrDhC-kP zw=Xmy?$;Cbwjl5-+5lSMq`p31$YX$y5x7Z!&!3-FAe4wxbOsKP5ds-0ro0;j+E�htx3KuqLHOkR-p^G26#gz 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 Date: Sun, 12 Jul 2026 00:16:41 +0200 Subject: [PATCH 009/137] Complete people-domain move into members app; keep names on Member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finish relocating Member/Family/FamilyMembership from authentication into a dedicated members app, and revert the half-applied move of member names onto the global User. - members: add first_name/last_name back to Member (the whole codebase — tests, CSV importer, club app, admin — assumes them, and login-less children in families need a name); restore local ordering/index and the member__last_name lookups in Family.__str__ and FamilyMembership. - authentication: drop first_name/last_name from User; get_full_name/ get_short_name delegate to the linked member, else fall back to email. - migrations: create members.0001_initial, repoint club.ClubMembership FK (club.0005), delete the models from authentication (0004, rewritten to plain DeleteModel ops in child-first order to avoid a SQLite table-remake crash). - fix stale imports across apps (authentication/club tests, CSV importer) and missing imports/URLs in members tests; add missing _ import in members/admin.py; export MemberCsvImporter from members.services. Full suite green (64 tests), ruff clean. Co-Authored-By: Claude Opus 4.8 --- ARCHITECTURE.md | 139 ++++- ARCHITECTURE.pdf | Bin 368159 -> 416779 bytes authentication/admin.py | 61 +-- authentication/apps.py | 2 +- ...remove_familymembership_family_and_more.py | 23 + authentication/models.py | 97 ---- authentication/tests.py | 485 +---------------- club/apps.py | 2 +- .../0005_alter_clubmembership_member.py | 20 + club/models.py | 5 +- club/tests.py | 2 +- clubmanager/asgi.py | 2 +- clubmanager/settings.py | 1 + clubmanager/urls.py | 3 +- clubmanager/wsgi.py | 2 +- manage.py | 11 +- .../management => members}/__init__.py | 0 members/admin.py | 62 +++ members/apps.py | 5 + .../management}/__init__.py | 0 .../management/commands}/__init__.py | 0 .../management/commands/import_members_csv.py | 2 +- members/migrations/0001_initial.py | 71 +++ members/migrations/__init__.py | 0 members/models.py | 99 ++++ members/services/__init__.py | 13 + .../services/member_csv_importer.py | 2 +- members/tests.py | 495 ++++++++++++++++++ members/views.py | 2 + 29 files changed, 942 insertions(+), 664 deletions(-) create mode 100644 authentication/migrations/0004_remove_familymembership_family_and_more.py create mode 100644 club/migrations/0005_alter_clubmembership_member.py rename {authentication/management => members}/__init__.py (100%) create mode 100644 members/admin.py create mode 100644 members/apps.py rename {authentication/management/commands => members/management}/__init__.py (100%) rename {authentication/services => members/management/commands}/__init__.py (100%) rename {authentication => members}/management/commands/import_members_csv.py (95%) create mode 100644 members/migrations/0001_initial.py create mode 100644 members/migrations/__init__.py create mode 100644 members/models.py create mode 100644 members/services/__init__.py rename {authentication => members}/services/member_csv_importer.py (99%) create mode 100644 members/tests.py create mode 100644 members/views.py diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index d10135e..1bdfa00 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -125,6 +125,36 @@ class ClubTenantMiddleware: # resolves subdomain -> Club, ... # request.club = club; set_current_club(club) ``` +**Considered — `django.contrib.sites` for resolution (rejected as the mechanism).** +Django's Sites framework is the obvious "does the batteries-included answer fit?" candidate, +so it was evaluated explicitly: + +*What it offers.* A `Site(domain, name)` model, `get_current_site(request)` (Host-header → +`Site`, with a per-process `SITE_CACHE`), `CurrentSiteMiddleware` (sets `request.site`), and +`CurrentSiteManager` (auto-filters models that hold an FK to `Site`). Ecosystem code +(`flatpages`, `redirects`, `sitemaps`, `allauth`) is Site-aware for free. + +*Why it does **not** fit as our tenancy mechanism:* +- **Wrong scoping key.** `CurrentSiteManager` filters on a `site` FK; our tenant key is the + `club` FK on `ClubScopedModel`. Adopting Sites' manager would mean putting a *second* FK on + every model, or ignoring the manager — either way it buys us nothing over `.for_club()`. +- **A parallel identity table.** `Site` duplicates identity that already lives on `Club` + (`slug`, domain, name). Two tables to keep in sync, two sources of truth for "which tenant". +- **`SITE_ID` is a single global.** The framework's happy path is *one process = one site* + (`SITE_ID`). Multi-tenant host resolution requires leaving `SITE_ID` unset and relying on + `get_current_site`'s exact-domain match — workable, but the setting is a standing foot-gun + (any library that reads `SITE_ID` silently binds to the wrong tenant), and shells, tasks, + and tests have no Host header, so they still need our contextvar (`require_current_club()`). +- **Host-only.** Sites cannot express the path-prefix option (`/c//`); resolution is + purely `domain`-based, foreclosing that alternative. + +*Verdict.* Keep **`Club` (with `slug` + optional `domain`) as the single tenant root** and +resolve it in `ClubTenantMiddleware` — the resolution logic (Host → `Club`) is a few lines and +avoids the sync/`SITE_ID` hazards. **Optional bridge:** if a Site-aware third party is later +adopted (e.g. `allauth`, `sitemaps`), add a thin `Club.site = OneToOneField(Site)` kept in sync +from `Club.save()`, so the ecosystem gets its `Site` while `Club` stays authoritative — do +this only when such a dependency actually lands, not preemptively. + **Scoping manager.** `ClubScopedModel` gets a tenant-aware manager so day-to-day queries can't accidentally cross tenants: @@ -489,10 +519,16 @@ membership product writes back to the season-scoped `ClubMembership` (§5.1). Product(ClubScopedModel) # -> carries `club` name, slug, description (blank) kind CharField (TextChoices: membership | event_fee | merchandise | donation) - price DecimalField(max_digits=8, decimal_places=2) + price DecimalField(max_digits=8, decimal_places=2) # list price season FK Season (PROTECT, null) # set for membership/event products is_active BooleanField + # early-bird / prompt-payment discount (§5.7.1) — per-product toggle + deadline + early_bird_enabled BooleanField (default=False) + early_bird_deadline DateField (null) # discount valid through this date (inclusive) + early_bird_disc_type CharField (TextChoices DiscountType: PERCENT | AMOUNT, blank) + early_bird_disc_value DecimalField(max_digits=8, decimal_places=2, null) # 0–100 if PERCENT, else € off unit Meta: unique_together (club, slug) + CheckConstraint: early_bird_enabled ⇒ deadline, disc_type, disc_value all set # membership products fulfil into a ClubMembership for the chosen season + beneficiary Cart(ClubScopedModel) # -> carries `club`; one open cart per (club, user) @@ -508,12 +544,17 @@ CartItem(UUIDModel) # club implied by cart unit_price DecimalField # snapshot of price at add-to-cart time Meta: unique_together (cart, product, beneficiary) -Order(ClubScopedModel) # -> carries `club`; immutable, created at checkout +Order(ClubScopedModel) # -> carries `club`; created `pending` at checkout, + # frozen at finalize() (§5.7.1 lifecycle) number CharField # human ref, allocated PER CLUB, e.g. "ORD-2026-00042" purchaser FK Member (PROTECT, related_name="orders") - status CharField (TextChoices: pending | paid | partially_paid | cancelled | refunded) - total DecimalField + status CharField (TextChoices: pending | finalized | paid | partially_paid | cancelled | refunded) + subtotal DecimalField # Σ OrderLine.line_total (after per-line early-bird) + # order-level discounts are SELECTED from a club catalogue, not typed — see AppliedDiscount + # below + OrderDiscountType (§5.7.1); applied by a treasurer while status=pending. + total DecimalField # subtotal - Σ applied discounts; the amount invoiced created_at DateTimeField + finalized_at DateTimeField (null) Meta: unique_together (club, number); ordering = ["-created_at"] OrderLine(UUIDModel) # club implied by order @@ -521,8 +562,10 @@ OrderLine(UUIDModel) # club implied by order product FK Product (PROTECT) beneficiary FK Member (PROTECT, null) quantity PositiveSmallIntegerField - unit_price DecimalField # snapshot - line_total DecimalField + list_price DecimalField # catalogue unit price at checkout (snapshot) + unit_price DecimalField # price actually charged after per-line early-bird (snapshot) + discount_label CharField (blank) # e.g. "Early bird (−15%)" — shown on invoice; blank = none + line_total DecimalField # unit_price * quantity fulfilled_at DateTimeField (null) # when this line's ClubMembership was activated Payment(UUIDModel) # club implied by order; an order may have several (partial) @@ -533,6 +576,26 @@ Payment(UUIDModel) # club implied by order; an order may have reference CharField (blank) # bank/gateway reference paid_at DateTimeField (null) +OrderDiscountType(ClubScopedModel) # -> carries `club`; club-defined catalogue of presets + name CharField # "Sibling discount", "Volunteer", "Hardship" + slug SlugField + disc_type CharField (TextChoices DiscountType: PERCENT | AMOUNT) + value DecimalField(max_digits=8, decimal_places=2) # 0–100 if PERCENT, else € off subtotal + description CharField (blank) # optional note shown to the treasurer + is_active BooleanField (default=True) # soft-retire; keeps historical AppliedDiscounts valid + Meta: unique_together (club, slug); ordering = ["name"] + +AppliedDiscount(UUIDModel) # through: Order <-> OrderDiscountType; club implied by order + order FK Order (CASCADE, related_name="discounts") + discount_type FK OrderDiscountType (PROTECT, related_name="applications") + # snapshot at apply time — the preset may be edited/retired later without altering past orders + label CharField # snapshot of name (shown on invoice) + disc_type CharField (PERCENT | AMOUNT) # snapshot + value DecimalField # snapshot (or a treasurer override, if allowed) + applied_by FK User (SET_NULL, null, related_name="+") + applied_at DateTimeField + Meta: unique_together (order, discount_type) # a preset toggles on/off once per order + Invoice(ClubScopedModel) # -> carries `club` number CharField # sequential PER CLUB per year, e.g. "INV-2026-00042" order OneToOneField Order (PROTECT, related_name="invoice") @@ -566,6 +629,55 @@ Flow & design notes: - **Money = `DecimalField`**, never float. Snapshot prices onto cart items / order lines / invoices so historical records stay correct when `Product.price` changes. +#### 5.7.1 Discounts + +Two independent discount mechanisms, applied at different layers and computed by a single +**pricing service** (`shop/services/pricing.py`) so the rules live in one place and never +in views/templates. A shared `DiscountType` enum (`PERCENT` / `AMOUNT`) is reused by both. + +**A. Early-bird / prompt-payment discount — per `Product`, automatic.** +A club toggles `early_bird_enabled` on a product, sets an `early_bird_deadline`, and a +`PERCENT` or `AMOUNT` value (§5.7 `Product`). Semantics: *buy in time and the unit price +drops.* +- **Anchor = checkout date (recommended).** The discount is evaluated **once, at checkout**, + comparing the order's `created_at` date against the deadline, and the result is frozen into + `OrderLine.unit_price` (+ a human `discount_label`, with `list_price` preserved for + transparency). This keeps the order/invoice total firm — an invoice can't have a + conditional amount. To still reward *paying* early, set the membership `Invoice.due_date` + to the deadline; late non-payment is a dunning concern, not a repricing one. +- **Alternative (payment-date anchor)** — the discount only sticks if a confirmed `Payment` + lands by the deadline, else the line reprices to `list_price`. This makes the total mutable + until the deadline and complicates invoicing; it's the literal reading of "paid before + date" but is deferred unless a club needs it (see open question, §7). +- Only applies when `today <= early_bird_deadline`; otherwise the line charges `list_price`. + A `CheckConstraint` guarantees an enabled product has a deadline + type + value. + +**B. Manual order-level discount — admin-applied, before finalize.** +A `TREASURER`/`BOARD` (§3.2) applies an ad-hoc discount to a whole order — e.g. a multi-kid +/ sibling discount — as a `PERCENT` or `AMOUNT` off the `subtotal`, with a required +`manual_disc_reason` and audit stamp (`manual_disc_by` / `manual_disc_at`). It sits on the +`Order`, not on a product. +- **Lifecycle refinement.** The manual discount forces the order to be *editable before it + freezes*: checkout now creates the order as **`pending`**; a treasurer may set/clear the + manual discount while `pending`; `finalize()` then locks the order, computes the final + `total`, allocates the `Invoice.number`, and issues the PDF. **After `finalize` the order + and its discount are immutable** — a correction means a credit/refund, not an edit. The + access service gates this via `can_manage_shop(user, club)`. + +**Computation & rounding (both kinds).** +`total = subtotal − order_discount`, where `subtotal = Σ line_total` and each `line_total` +already reflects the early-bird price. Order of application: **line-level early-bird first, +then the order-level manual discount** on the resulting subtotal. Percentages compute on the +base they apply to (unit price / subtotal), round **`ROUND_HALF_UP` to 2 decimals**, and are +**clamped to `[0, base]`** so no line or order can go negative. Every discounted document +(order summary, invoice) shows list price, discount, and net so members see how the number +was reached. + +**Extension point.** Both are deliberately field-level, not a discount-row model — enough for +the two required cases. Coupon codes, stacked promotions, or per-member entitlements would +warrant a first-class `Discount`/`Coupon` model + an M2M to orders; add it only when that need +is real. + --- ## 6. Entity-relationship overview @@ -624,6 +736,13 @@ Legend: `───<` one-to-many, `>───<` many-to-many via a through model rows + a single access service (§3). Django's own perms only for platform admin. 7. ✅ **`formbuilder` storage** — **normalized `Answer` is canonical**; no denormalized JSON snapshot (§5.6). +8. ✅ **Tenant resolution ≠ `django.contrib.sites`** — Sites evaluated and rejected as the + mechanism; `Club` stays the single tenant root, resolution in `ClubTenantMiddleware` + (§2.4). Sites optional only as a later bridge for Site-aware third parties. +9. ✅ **Shop discounts** — two field-level mechanisms (§5.7.1): a per-`Product` early-bird + discount (toggle + deadline + PERCENT/AMOUNT, frozen at checkout) and a manual order-level + discount a treasurer applies to a `pending` order before `finalize()`. Adds an + `Order.pending → finalized` step; no discount-row model yet. Infrastructure/config for the above (media storage, dependencies + exact setup) is specified in **§8**. @@ -632,7 +751,9 @@ specified in **§8**. - **Tenant resolution mechanism** — subdomain (recommended) vs. path-prefix `/c//`. Affects DNS/TLS, `ALLOWED_HOSTS`, cookies, and local dev (§8). Pick before building - `ClubTenantMiddleware`. + `ClubTenantMiddleware`. **`django.contrib.sites` was evaluated and rejected as the + mechanism** (§2.4) — `Club` stays the single tenant root; Sites is optional only as a + bridge for Site-aware third parties. - **Auto-scoping vs. explicit scoping** — should the tenant manager filter *automatically* from context, or stay explicit (`.for_club()` / `.current()`)? Doc currently recommends **explicit** (§2.4). @@ -640,6 +761,10 @@ specified in **§8**. context in one session? The model allows it; confirm the UX (club switcher) is in scope. - **Payment gateway** — which provider (Mollie / Stripe / none-yet)? Only needed when online payments go live (§8). +- **Early-bird anchor** — is the discount earned by *ordering* before the deadline + (checkout-date anchor, recommended, frozen total) or by *paying* before it (payment-date + anchor, mutable total)? Doc implements checkout-date; confirm no club needs the literal + "paid before date" semantics (§5.7.1). --- diff --git a/ARCHITECTURE.pdf b/ARCHITECTURE.pdf index 983912b2b9f62da7f6ecda6a9a3eb7a89e40c9cc..17347a08985bf0fb8d248f2732bbf539acb3bb1c 100644 GIT binary patch delta 249575 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

7(jyMfja@e=5q`LAag#+l&f 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 z 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%0Dq9_ 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

)HCpQ1e@v`^7ekDvHrk|dqP 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 mMi_Ls5C{&Rwv&?^6Y^{_XM{nIk32O%K` Date: Sun, 12 Jul 2026 00:20:27 +0200 Subject: [PATCH 010/137] Add tests for uncovered members code paths Close the small coverage gaps left after the members app move: - FamilyMembership.__str__ string representation - FamilyAdmin.member_count display (with and without members) - CSV import skips a row with an empty required field (was only exercising the invalid-date path) - MemberImportResult.successful_rows property Coverage 98% -> 99% (69 tests). The only remaining uncovered lines are the redundant re-link branch in MemberCsvImporter.import_row (131-132), which is unreachable because update_or_create already sets member.user. Co-Authored-By: Claude Opus 4.8 --- members/tests.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/members/tests.py b/members/tests.py index 658cff2..021e23d 100644 --- a/members/tests.py +++ b/members/tests.py @@ -3,6 +3,7 @@ from datetime import date from io import StringIO from pathlib import Path +from django.contrib.admin.sites import AdminSite from django.core.management import call_command from django.core.management.base import CommandError from django.db import IntegrityError @@ -11,7 +12,9 @@ from django.test import TestCase from authentication.models import User from club.models import Club, ClubMembership +from members.admin import FamilyAdmin from members.models import Family, FamilyMembership, Member +from members.services import MemberImportResult # Create your tests here. @@ -167,6 +170,13 @@ class FamilyMembershipModelTests(TestCase): self.assertEqual(membership.role, FamilyMembership.FamilyRole.PARENT) + def test_str_includes_family_member_and_role(self): + family = Family.objects.create(name="The Smiths") + member = Member.objects.create(first_name="Pat", last_name="Smith") + membership = FamilyMembership.objects.create(family=family, member=member, role=FamilyMembership.FamilyRole.GUARDIAN) + + self.assertEqual(str(membership), "The Smiths - Pat Smith (guardian)") + def test_member_unique_per_family(self): family = Family.objects.create(name="Fam") member = Member.objects.create(first_name="Solo", last_name="Once") @@ -197,6 +207,22 @@ class FamilyMembershipModelTests(TestCase): self.assertTrue(Member.objects.filter(pk=member.pk).exists()) +class FamilyAdminTests(TestCase): + def test_member_count_reflects_memberships(self): + family = Family.objects.create(name="The Smiths") + for i in range(3): + member = Member.objects.create(first_name=f"Kid{i}", last_name="Smith") + FamilyMembership.objects.create(family=family, member=member, role=FamilyMembership.FamilyRole.CHILD) + + admin_instance = FamilyAdmin(Family, AdminSite()) + self.assertEqual(admin_instance.member_count(family), 3) + + def test_member_count_is_zero_without_members(self): + family = Family.objects.create(name="Empty") + admin_instance = FamilyAdmin(Family, AdminSite()) + self.assertEqual(admin_instance.member_count(family), 0) + + class AdminSmokeTests(TestCase): """Exercise the admin config end-to-end to catch misregistration (bad search_fields, autocomplete targets, fieldsets, custom forms).""" @@ -493,3 +519,28 @@ class ImportMembersCsvCommandTests(TestCase): ) self.assertIn("CSV file is empty or missing a header row.", str(context.exception)) + + def test_import_skips_row_with_empty_required_field(self): + csv_path = self.write_csv( + "\n".join( + [ + "first_name,last_name,email,date_of_birth,create_account,club_name,license_number", + "Jane,Doe,jane@example.com,2010-04-12,false,City Swim Club,LIC-001", + ",Missing,nofirst@example.com,2011-05-13,false,City Swim Club,LIC-002", + ] + ) + ) + + stdout, stderr = self.call_import_command(csv_path) + + self.assertIn("Row 3 skipped:", stderr) + self.assertIn("first_name is required.", stderr) + self.assertIn("Members created: 1.", stdout) + self.assertIn("Rows skipped: 1.", stdout) + self.assertFalse(Member.objects.filter(email="nofirst@example.com").exists()) + + +class MemberImportResultTests(TestCase): + def test_successful_rows_sums_created_and_updated(self): + result = MemberImportResult(created_members=2, updated_members=3) + self.assertEqual(result.successful_rows, 5) From 638f85fa07f0770d0c5d679cbae6e0eba9aa405b Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 00:21:48 +0200 Subject: [PATCH 011/137] Remove redundant user re-link branch in CSV importer update_or_create already persists member.user via defaults, so the follow-up `if create_account and member.user_id is None` block was unreachable dead code. Removing it brings importer coverage to 100% with no behavior change (existing link-existing-user test still passes). Co-Authored-By: Claude Opus 4.8 --- members/services/member_csv_importer.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/members/services/member_csv_importer.py b/members/services/member_csv_importer.py index 508c289..2443c09 100644 --- a/members/services/member_csv_importer.py +++ b/members/services/member_csv_importer.py @@ -127,10 +127,6 @@ class MemberCsvImporter: }, ) - if create_account and member.user_id is None: - member.user = user - member.save(update_fields=["user"]) - club, club_created = Club.objects.get_or_create(name=club_name) _, membership_created = ClubMembership.objects.update_or_create( From 919a68ed3a53fa9595daef53cf2216fd50e87019 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 15:31:55 +0200 Subject: [PATCH 012/137] build: add coverage dev dependency; drop contextvars backport Add coverage 7.15 to the dev group for test-coverage reporting. Remove the erroneous contextvars>=2.4 runtime dependency: contextvars is part of the standard library on Python 3.14, and the PyPI backport (with its immutables dependency) would shadow it. Co-Authored-By: Claude Opus 4.8 --- pyproject.toml | 1 + uv.lock | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 78d562e..d65565b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ dependencies = [ [dependency-groups] dev = [ + "coverage>=7.15.0", "ruff>=0.15.17", ] diff --git a/uv.lock b/uv.lock index 30d8bbc..f174501 100644 --- a/uv.lock +++ b/uv.lock @@ -24,6 +24,7 @@ dependencies = [ [package.dev-dependencies] dev = [ + { name = "coverage" }, { name = "ruff" }, ] @@ -36,7 +37,49 @@ requires-dist = [ ] [package.metadata.requires-dev] -dev = [{ name = "ruff", specifier = ">=0.15.17" }] +dev = [ + { name = "coverage", specifier = ">=7.15.0" }, + { name = "ruff", specifier = ">=0.15.17" }, +] + +[[package]] +name = "coverage" +version = "7.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/8b/adeb62ea8951f13c4c7fef2e7a85e1a06b499c8d8237ea589d496029e53f/coverage-7.15.0.tar.gz", hash = "sha256:9ac3fe7a1435986463eaa8ee253ae2f2a268709ba4ae5c7dd1f52a05391ad78f", size = 925362, upload-time = "2026-07-02T13:10:50.535Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/9c/1e3ca54f72a3185ece06c58d871099898c48f0ed6430d17b6ab75f0d180a/coverage-7.15.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:41cb79af843222e11da87127ad0ecbfa878abadd0f770a4a99391a27d3887324", size = 220906, upload-time = "2026-07-02T13:09:51.339Z" }, + { url = "https://files.pythonhosted.org/packages/09/37/f718613d83b274880382f6b67e78f3802549ae39b0b3e65ae5b5974df56e/coverage-7.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7d2008989ef8fe54188d3f3bfa2e3099b025af11e90a6a1b9e7dc433d04263d8", size = 221239, upload-time = "2026-07-02T13:09:53.138Z" }, + { url = "https://files.pythonhosted.org/packages/a7/ce/22bae91e0b75445f68d365c7643ed0aa4880bbf77450ee74ca65bdae53a7/coverage-7.15.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:769e8ece11a596315ebf5aa7ec383aeeed016c091d2bf6363ffb996d41529092", size = 252286, upload-time = "2026-07-02T13:09:54.996Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1e/bec5e32aa508615d9d7a2790effb25fb4dc28606e995816afe400b25ece3/coverage-7.15.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:65a6b6164ee5c39e2f3803f314292d6c61a607ba7fee253d1e03c42dc3903502", size = 254789, upload-time = "2026-07-02T13:09:56.678Z" }, + { url = "https://files.pythonhosted.org/packages/17/29/0e865435b4354e4a7c03b1b7920046d31d0a273d55decefea27e011cb9bf/coverage-7.15.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:75128817f95a5c45bb01d65fd2d8b9cb54bbe03d81608fb70e3e14b437ad56c2", size = 256135, upload-time = "2026-07-02T13:09:58.343Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/33a870b58a13325d62fc0a6c8f01fa0ff667cef60c7498e2382a147dfa18/coverage-7.15.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9887bb428fe2d4cd4bee89bac1a6c9932f484afd5b36fbd4ff6ea5f825bb1f5e", size = 258449, upload-time = "2026-07-02T13:10:00.057Z" }, + { url = "https://files.pythonhosted.org/packages/18/7b/6fffe596bf3ddba8462758d02c5dad730fd91055a6634aa2e4226229181a/coverage-7.15.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0bfc0be1f702042207a93a00523b1065ee1fe951e96edf311581c0bbc2e34888", size = 252313, upload-time = "2026-07-02T13:10:01.946Z" }, + { url = "https://files.pythonhosted.org/packages/58/1b/11468dd6c1676ab831a70cb9a8d4e198e8607fa0b7220ab918b73fe9bfbd/coverage-7.15.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f64627d55def5a43282d70e08396672692f77e4da610a5bb8bb4060b432b6859", size = 254142, upload-time = "2026-07-02T13:10:04.065Z" }, + { url = "https://files.pythonhosted.org/packages/79/41/29328e21d16b1b95092c30dd700e08cf915bd3734f836df8f3bdb0e8fa9f/coverage-7.15.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:2c6f0fa473003905c6d5bac328ee4eba9fbea654f15bc24b8a3274b23363fa99", size = 252108, upload-time = "2026-07-02T13:10:06.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/de/05ccfb990439655b35afbfd8e0d13fe66677565a7d4eb38c3f5ef2635e1c/coverage-7.15.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2bcf9afaf064172c6ec3c58a325a9957ad1178c05dd934e25f253321776e0676", size = 256385, upload-time = "2026-07-02T13:10:08.141Z" }, + { url = "https://files.pythonhosted.org/packages/51/0e/486828a3d2695ea7a2609f17ff572f6b01905e608379440a11da4b8dffbe/coverage-7.15.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:baf06bc987115d6fb938d403f7eab684a057766c490367999a2b71a6883110c6", size = 251923, upload-time = "2026-07-02T13:10:10.179Z" }, + { url = "https://files.pythonhosted.org/packages/18/c7/03582b6715f078e5e558354c87616d945b9894cda2dace8e4009b17035e4/coverage-7.15.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f0405f2ff97b1c4c0e782cb32e02f32369bcf2e6b618b591d67e1ea754575dfe", size = 253580, upload-time = "2026-07-02T13:10:12.052Z" }, + { url = "https://files.pythonhosted.org/packages/db/dc/9e578bbaf2ecb4959a81b7e7601ad8cca772cba2892e8d144cb749b4a71a/coverage-7.15.0-cp314-cp314-win32.whl", hash = "sha256:ab282853ed5fbd64bbb162f19cb8fcb7087187508a6374b4f9c34ec1577c4e8f", size = 223107, upload-time = "2026-07-02T13:10:13.994Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3e/c8c3b75d8dbe0e35f7b0cc3ff5e949fc59500f70b21d0398813f66740664/coverage-7.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:3bb3040e9f4bbe26fcb0cd7cc85ac63e630d3f3a9c74f027abf4caa27e706663", size = 223597, upload-time = "2026-07-02T13:10:15.906Z" }, + { url = "https://files.pythonhosted.org/packages/cd/bc/3cbc9fb036eb388519bccd521f783499c39b64256013fbc362782f196fe1/coverage-7.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:346771144d34f7fa84ec28386f78e0f31653f33cf35e19d253d5b35f9e8201da", size = 223020, upload-time = "2026-07-02T13:10:17.844Z" }, + { url = "https://files.pythonhosted.org/packages/28/00/199c4a8d656dff63102577a056c0fce2ff6a79e40adac092fc986c49cbf1/coverage-7.15.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d34a010905fb6401324ba016b5da03d574967f7b21ce48ea41e66f0f1f95f641", size = 221638, upload-time = "2026-07-02T13:10:19.703Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8e/9d0092c96a3d3a26951ecc7020826aa57bcb1b119ca81acbba996884ab13/coverage-7.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:bb25d825d885ca8036795dacfc3924d33091fc76d71ebc99420c6b79e77d96fa", size = 221903, upload-time = "2026-07-02T13:10:21.514Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b4/c0ca3028f42c9a08e51feb4561ef1192e5de99797cd1db5b04590c215bda/coverage-7.15.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:94c9686bfe8a9a6810297aecbd99beaa3445f9e8dc2f80b1382cca0d86b64461", size = 263267, upload-time = "2026-07-02T13:10:23.261Z" }, + { url = "https://files.pythonhosted.org/packages/5f/aa/a375e3846e5d3c013dc600b2a3231089055c73d77f5393dd2192a8d64da6/coverage-7.15.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9bd671c25f9d85f09d7ec481d0e43d5139f486c06a37139847a7ce569788af72", size = 265390, upload-time = "2026-07-02T13:10:25.152Z" }, + { url = "https://files.pythonhosted.org/packages/92/e1/5783cdabb797305e1c9e4809fea496d31834c51fa772514f73dc148bcfc9/coverage-7.15.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:110cbdf8d2e216577312cf06ccf85539c0e5a5420ef747e4a4719b5e483c88cd", size = 267811, upload-time = "2026-07-02T13:10:27.249Z" }, + { url = "https://files.pythonhosted.org/packages/85/31/96d8bbf58b8e9193bc8389574a91a0db48355ee98feb66aa6bf8d1b32eea/coverage-7.15.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2c5d4619214f1d9993e7b00a8600d14614b7e9d84e89507460b126aa5e6559e5", size = 268928, upload-time = "2026-07-02T13:10:29.242Z" }, + { url = "https://files.pythonhosted.org/packages/5e/7a/5294567e811a1cb7eda93140c628fa050d66189da28da320f93d1d815c73/coverage-7.15.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:781a704516e2d8346fbbd5be6c6f3412dd824785146528b3a01816f26c081007", size = 262378, upload-time = "2026-07-02T13:10:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/69/3f/3f48538421f899f28946f90a3d272136a4686e1abf461cc9249a783ee0f3/coverage-7.15.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd4a1b44bcb65ee29e947ac92bbee04956df3a6bfc6143641bb6cae7ede00fc9", size = 265263, upload-time = "2026-07-02T13:10:32.942Z" }, + { url = "https://files.pythonhosted.org/packages/ce/d3/092df15efcab8a9c1467ee960eb8019bbad3f9300d115d89ea6195f369ff/coverage-7.15.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0e4950c9d6d3e39c64c991814ff315e2d0b9cb8152363594212c9e55208c0a8f", size = 262866, upload-time = "2026-07-02T13:10:35.104Z" }, + { url = "https://files.pythonhosted.org/packages/e5/ab/0254d2b88665efb2c57ad368cc77ab5de3435bd8d5add4729c1b0e79431e/coverage-7.15.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:fe9c87ff42e5472d80d21704972e1f96e104a0a599d77c5e35db5a3c562e2571", size = 266599, upload-time = "2026-07-02T13:10:37.05Z" }, + { url = "https://files.pythonhosted.org/packages/a8/79/1cfa4023e489ce6fbc7be4a5d442dbc375edb4f4fda39a352cedb53263c2/coverage-7.15.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f00d5ae1dd2fe13fb8186e3e7d37bcbd8b25c0d764ff7d1b32cef9be058510a8", size = 261714, upload-time = "2026-07-02T13:10:38.966Z" }, + { url = "https://files.pythonhosted.org/packages/b7/eb/fee5c8665656be63f497418d410484637c438172568688e8ac92e06574e7/coverage-7.15.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:363ab38cc78b615f11c9cac3cf1d7eef950c18b9fdedfb9066f59461dcf84d68", size = 264025, upload-time = "2026-07-02T13:10:40.789Z" }, + { url = "https://files.pythonhosted.org/packages/ab/99/63005db722f91edc81abc16302f9cc2f6228c1679e46e15be9ae144b14d0/coverage-7.15.0-cp314-cp314t-win32.whl", hash = "sha256:54fd9c53a5fafff509195f1b6a3f9be615d8e8362a3629ff1de23d270c03c86b", size = 223413, upload-time = "2026-07-02T13:10:42.597Z" }, + { url = "https://files.pythonhosted.org/packages/c1/e8/2bc6181c4fb06f1a6b981eb85330cc57bfad7e3f710fc9c9d350013ba228/coverage-7.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:87b47553097ba185ed964866078e7e63adea9f5f51b5f39691c34f30afd21080", size = 224245, upload-time = "2026-07-02T13:10:44.47Z" }, + { url = "https://files.pythonhosted.org/packages/79/b8/4d959bf9cc45d0cfed2f4d35cafcab978cdb6ea02eb5100009cd740632a3/coverage-7.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aeefb2dd178fe7eee79f0ad25d75855cb35ee9ed472db2c5ea06f5b4fd00cec5", size = 223558, upload-time = "2026-07-02T13:10:46.368Z" }, + { url = "https://files.pythonhosted.org/packages/52/30/21b2ad45959cd50e909e02ebac1e30b4ceb7162e91c11d4c570223a458b7/coverage-7.15.0-py3-none-any.whl", hash = "sha256:56da6a4cbe8f7e9e80bd072ca9cefe67d7106a440a7ec06519ec6507ac94ad19", size = 212632, upload-time = "2026-07-02T13:10:48.641Z" }, +] [[package]] name = "dj-database-url" From 72e7b5e0705430bc88d0d89aa78991e4bcaf70cd Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 15:32:05 +0200 Subject: [PATCH 013/137] feat(tenancy): resolve active club from request subdomain Add row-based multi-tenancy plumbing keyed on Club as the tenant root: - ClubTenantMiddleware maps the request's subdomain to a Club by slug, storing it on request.club and in a contextvar so service-layer code and management commands can read it via get_current_club(). Resolution honours CLUBMANAGER_BASE_DOMAIN (e.g. ajax-united.clubmanager.app), falling back to generic slug.example.com hosts, and ignores the bare base domain, www, and unknown slugs. - Club gains a unique slug (auto-derived from name on save) plus a ClubManager.current() accessor for the active tenant. - ClubScopedModel gets a TenantQuerySet (.for_club()/.current()) and auto-fills club from the active context on save. Contextvar helpers live in club.tenancy; Club is imported lazily there and in clubmanager.base to avoid an import cycle with club.models. Co-Authored-By: Claude Opus 4.8 --- club/migrations/0006_club_slug_season.py | 33 ++++++++ club/models.py | 51 ++++++++++- club/tenancy.py | 103 +++++++++++++++++++++++ clubmanager/base.py | 21 +++++ clubmanager/settings.py | 6 ++ 5 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 club/migrations/0006_club_slug_season.py create mode 100644 club/tenancy.py diff --git a/club/migrations/0006_club_slug_season.py b/club/migrations/0006_club_slug_season.py new file mode 100644 index 0000000..ddb6cdb --- /dev/null +++ b/club/migrations/0006_club_slug_season.py @@ -0,0 +1,33 @@ +# Generated by Django 6.0.6 on 2026-07-12 13:11 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0005_alter_clubmembership_member'), + ] + + operations = [ + migrations.AddField( + model_name='club', + name='slug', + field=models.SlugField(blank=True, help_text='Drives subdomain / path resolution (e.g. ajax-united.clubmanager.app).', max_length=255, unique=True, verbose_name='slug'), + ), + migrations.CreateModel( + name='Season', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('start_date', models.DateField(verbose_name='start date')), + ('end_date', models.DateField(verbose_name='end date')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ], + options={ + 'verbose_name': 'season', + 'verbose_name_plural': 'seasons', + }, + ), + ] diff --git a/club/models.py b/club/models.py index ef17947..8b1c136 100644 --- a/club/models.py +++ b/club/models.py @@ -1,12 +1,27 @@ +import datetime + from django.db import models +from django.utils import timezone +from django.utils.text import slugify from django.utils.translation import gettext_lazy as _ -from clubmanager.base import UUIDModel +from clubmanager.base import ClubScopedModel, UUIDModel from members.models import Member +class ClubManager(models.Manager): + def current(self): + """Return the club for the active tenant context, if any.""" + from .tenancy import get_current_club + + return get_current_club() + + class Club(UUIDModel): name = models.CharField(_("name"), max_length=255) + slug = models.SlugField(_("slug"), max_length=255, unique=True, blank=True, help_text=_("Drives subdomain / path resolution (e.g. ajax-united.clubmanager.app).")) + + objects = ClubManager() class Meta: verbose_name = _("club") @@ -16,6 +31,21 @@ class Club(UUIDModel): def __str__(self): return self.name + def save(self, *args, **kwargs): + if not self.slug: + self.slug = self._unique_slug() + super().save(*args, **kwargs) + + def _unique_slug(self): + base = slugify(self.name) or "club" + slug = base + suffix = 2 + existing = Club.objects.exclude(pk=self.pk) + while existing.filter(slug=slug).exists(): + slug = f"{base}-{suffix}" + suffix += 1 + return slug + class ClubMembership(UUIDModel): member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="member_of", verbose_name=_("member")) @@ -31,3 +61,22 @@ class ClubMembership(UUIDModel): def __str__(self): return f"{self.club} - {self.member}" + + +class Season(ClubScopedModel): + start_date = models.DateField(_("start date")) + end_date = models.DateField(_("end date")) + + def __str__(self): + return f"{self.start_date} - {self.end_date}" + + class Meta: + verbose_name = _("season") + verbose_name_plural = _("seasons") + + @classmethod + def get_current(cls, date: datetime.date | None = None): + if date is None: + date = timezone.now().date() + + \ No newline at end of file diff --git a/club/tenancy.py b/club/tenancy.py new file mode 100644 index 0000000..a519f29 --- /dev/null +++ b/club/tenancy.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +from contextvars import ContextVar, Token +from typing import TYPE_CHECKING + +from django.conf import settings + +if TYPE_CHECKING: + from .models import Club + +__current_club: ContextVar = ContextVar("current_club", default=None) + + +def set_current_club(club: Club | None) -> Token: + """Bind ``club`` to the current context and return a reset token.""" + return __current_club.set(club) + + +def reset_current_club(token: Token) -> None: + """Restore the club that was active before ``set_current_club``.""" + __current_club.reset(token) + + +def get_current_club() -> Club | None: + return __current_club.get() + + +def require_current_club() -> Club: + club = get_current_club() + + if club is None: + raise RuntimeError("No active club in context.") + + return club + + +class ClubTenantMiddleware: + """Resolve the active club from the request's subdomain. + + The club whose ``slug`` matches the left-most host label (below the + configured base domain) is stored on ``request.club`` and pushed onto the + ``current_club`` context variable for the duration of the request, so + service-layer code and managers can read it via ``get_current_club()``. + Requests that don't map to a club (bare base domain, ``www``, localhost, + an unknown slug) get ``request.club = None``. + """ + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + club = self.get_club(request) + request.club = club + token = set_current_club(club) + try: + return self.get_response(request) + finally: + reset_current_club(token) + + def get_club(self, request) -> Club | None: + # Imported lazily: club.models imports clubmanager.base, which imports + # this module, so a top-level import would be circular. + from .models import Club + + subdomain = self.get_subdomain(request) + + if not subdomain: + return None + + return Club.objects.filter(slug=subdomain).first() + + @staticmethod + def get_subdomain(request) -> str | None: + """Extract the tenant slug from the request host, or ``None``.""" + host = request.get_host().split(":")[0].lower().rstrip(".") + + if not host: + return None + + base_domain = getattr(settings, "CLUBMANAGER_BASE_DOMAIN", "").lower().strip(".") + + if base_domain: + # Only hosts under the configured base domain carry a tenant slug. + if host == base_domain: + return None + suffix = f".{base_domain}" + if not host.endswith(suffix): + return None + label = host[: -len(suffix)] + else: + # No base domain configured: treat "slug.example.com" style hosts + # (3+ labels) as tenant-bearing; leave bare/localhost hosts alone. + labels = host.split(".") + if len(labels) < 3: + return None + label = ".".join(labels[:-2]) + + # Use the left-most label only; ignore the marketing/www host. + subdomain = label.split(".")[0] + if subdomain in ("", "www"): + return None + + return subdomain diff --git a/clubmanager/base.py b/clubmanager/base.py index 8b1346a..0965486 100644 --- a/clubmanager/base.py +++ b/clubmanager/base.py @@ -1,7 +1,21 @@ import uuid +from typing import TYPE_CHECKING from django.db import models +from club.tenancy import require_current_club + +if TYPE_CHECKING: + from club.models import Club + + +class TenantQuerySet(models.QuerySet): + def for_club(self, club: Club): + return self.filter(club=club) + + def current(self): + return self.filter(club=require_current_club()) + class UUIDModel(models.Model): """Abstract base class giving every model a UUID primary key""" @@ -16,6 +30,13 @@ class ClubScopedModel(UUIDModel): """Abstract base for entities owned by a single club (tenant root).""" club = models.ForeignKey("club.Club", on_delete=models.CASCADE, related_name="%(class)ss") + objects = TenantQuerySet.as_manager() class Meta: abstract = True + + def save(self, *args, **kwargs): + if self.club_id is None: + self.club = require_current_club() + + super().save(*args, **kwargs) diff --git a/clubmanager/settings.py b/clubmanager/settings.py index c60240b..05bde7f 100644 --- a/clubmanager/settings.py +++ b/clubmanager/settings.py @@ -55,10 +55,16 @@ MIDDLEWARE = [ "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", + "club.tenancy.ClubTenantMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] +# Multi-tenancy: base domain whose subdomains resolve to a club, e.g. +# "ajax-united.clubmanager.app" -> the club with slug "ajax-united". Leave +# unset to fall back to generic "slug.example.com" (3+ label) resolution. +CLUBMANAGER_BASE_DOMAIN = config("CLUBMANAGER_BASE_DOMAIN", default="") + ROOT_URLCONF = "clubmanager.urls" AUTH_USER_MODEL = "authentication.User" From e85a5f630048b872d57f900fac66fc2d5199fcf0 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 15:32:23 +0200 Subject: [PATCH 014/137] test(tenancy): cover club resolution, context, and scoping Exercise subdomain resolution (base-domain and generic hosts, unknown slug, www, port stripping, contextvar cleanup), slug derivation and uniqueness, the require_current_club/current() context helpers, and ClubScopedModel save/for_club/current via Season. Add a with_club test helper. Tenancy modules reach 100% coverage. Co-Authored-By: Claude Opus 4.8 --- club/tests.py | 204 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 3 deletions(-) diff --git a/club/tests.py b/club/tests.py index 6ae2922..bed8ac3 100644 --- a/club/tests.py +++ b/club/tests.py @@ -1,11 +1,30 @@ +import datetime import uuid +from contextlib import contextmanager from django.db import IntegrityError -from django.test import TestCase +from django.test import RequestFactory, TestCase, override_settings from members.models import Member -from .models import Club, ClubMembership +from .models import Club, ClubMembership, Season +from .tenancy import ( + ClubTenantMiddleware, + get_current_club, + require_current_club, + reset_current_club, + set_current_club, +) + + +@contextmanager +def with_club(club): + """Bind ``club`` as the active tenant for the duration of the block.""" + token = set_current_club(club) + try: + yield club + finally: + reset_current_club(token) class ClubModelTests(TestCase): @@ -162,4 +181,183 @@ class ClubMembershipModelTests(TestCase): self.assertEqual(ClubMembership._meta.verbose_name_plural, "club memberships") -# Create your tests here. +class ClubSlugTests(TestCase): + def test_slug_is_derived_from_name(self): + club = Club.objects.create(name="City Swim Club") + + self.assertEqual(club.slug, "city-swim-club") + + def test_explicit_slug_is_kept(self): + club = Club.objects.create(name="City Swim Club", slug="ajax-united") + + self.assertEqual(club.slug, "ajax-united") + + def test_derived_slugs_are_made_unique(self): + first = Club.objects.create(name="City Swim Club") + second = Club.objects.create(name="City Swim Club") + + self.assertEqual(first.slug, "city-swim-club") + self.assertEqual(second.slug, "city-swim-club-2") + + def test_slug_is_unique(self): + Club.objects.create(name="First", slug="shared") + + with self.assertRaises(IntegrityError): + Club.objects.create(name="Second", slug="shared") + + +@override_settings( + CLUBMANAGER_BASE_DOMAIN="clubmanager.app", + ALLOWED_HOSTS=[".clubmanager.app", ".example.com", ".example.org"], +) +class ClubTenantMiddlewareTests(TestCase): + def setUp(self): + self.factory = RequestFactory() + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.captured = {} + self.middleware = ClubTenantMiddleware(self._capture) + + def _capture(self, request): + # Runs inside the middleware, while the context var is set. + self.captured["context_club"] = get_current_club() + return "response" + + def _run(self, host): + request = self.factory.get("/", HTTP_HOST=host) + response = self.middleware(request) + return request, response + + def test_subdomain_resolves_to_club(self): + request, response = self._run("ajax-united.clubmanager.app") + + self.assertEqual(response, "response") + self.assertEqual(request.club, self.club) + self.assertEqual(self.captured["context_club"], self.club) + + def test_subdomain_resolution_ignores_port(self): + request, _ = self._run("ajax-united.clubmanager.app:8000") + + self.assertEqual(request.club, self.club) + + def test_unknown_subdomain_sets_none(self): + request, _ = self._run("unknown-club.clubmanager.app") + + self.assertIsNone(request.club) + + def test_bare_base_domain_has_no_club(self): + request, _ = self._run("clubmanager.app") + + self.assertIsNone(request.club) + + def test_www_is_treated_as_no_club(self): + request, _ = self._run("www.clubmanager.app") + + self.assertIsNone(request.club) + + def test_foreign_domain_has_no_club(self): + request, _ = self._run("ajax-united.example.org") + + self.assertIsNone(request.club) + + def test_context_var_is_reset_after_request(self): + self._run("ajax-united.clubmanager.app") + + self.assertIsNone(get_current_club()) + + @override_settings(CLUBMANAGER_BASE_DOMAIN="") + def test_generic_host_resolution_without_base_domain(self): + request, _ = self._run("ajax-united.example.com") + + self.assertEqual(request.club, self.club) + + @override_settings(CLUBMANAGER_BASE_DOMAIN="") + def test_two_label_host_has_no_club_without_base_domain(self): + request, _ = self._run("example.com") + + self.assertIsNone(request.club) + + +class _FakeRequest: + def __init__(self, host): + self._host = host + + def get_host(self): + return self._host + + +@override_settings(CLUBMANAGER_BASE_DOMAIN="clubmanager.app") +class GetSubdomainTests(TestCase): + def subdomain(self, host): + return ClubTenantMiddleware.get_subdomain(_FakeRequest(host)) + + def test_empty_host_returns_none(self): + self.assertIsNone(self.subdomain("")) + + def test_trailing_dot_is_stripped(self): + self.assertEqual(self.subdomain("ajax-united.clubmanager.app."), "ajax-united") + + def test_nested_subdomain_uses_leftmost_label(self): + self.assertEqual(self.subdomain("a.b.clubmanager.app"), "a") + + +class TenantContextTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + + def test_require_current_club_returns_active_club(self): + with with_club(self.club): + self.assertEqual(require_current_club(), self.club) + + def test_require_current_club_raises_without_context(self): + with self.assertRaises(RuntimeError): + require_current_club() + + def test_club_manager_current_returns_active_club(self): + with with_club(self.club): + self.assertEqual(Club.objects.current(), self.club) + + def test_club_manager_current_is_none_without_context(self): + self.assertIsNone(Club.objects.current()) + + +class TenantScopedModelTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.other = Club.objects.create(name="Rival FC", slug="rival-fc") + self.dates = { + "start_date": datetime.date(2026, 8, 1), + "end_date": datetime.date(2027, 5, 31), + } + + def test_save_keeps_explicit_club(self): + season = Season.objects.create(club=self.club, **self.dates) + + self.assertEqual(season.club, self.club) + + def test_save_fills_club_from_context(self): + with with_club(self.club): + season = Season.objects.create(**self.dates) + + self.assertEqual(season.club, self.club) + + def test_save_without_club_or_context_raises(self): + with self.assertRaises(RuntimeError): + Season.objects.create(**self.dates) + + def test_for_club_filters_by_club(self): + mine = Season.objects.create(club=self.club, **self.dates) + Season.objects.create(club=self.other, **self.dates) + + self.assertEqual(list(Season.objects.for_club(self.club)), [mine]) + + def test_current_filters_by_active_club(self): + mine = Season.objects.create(club=self.club, **self.dates) + Season.objects.create(club=self.other, **self.dates) + + with with_club(self.club): + self.assertEqual(list(Season.objects.current()), [mine]) + + def test_str_shows_date_range(self): + season = Season.objects.create(club=self.club, **self.dates) + + self.assertEqual(str(season), "2026-08-01 - 2027-05-31") From 3575d8f7b1af9188e6b8bf145f4ebc55b35215dd Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 15:32:23 +0200 Subject: [PATCH 015/137] chore: document dev environment variables Add .env.example covering the required settings plus the multi-tenant dev config (DJANGO_ALLOWED_HOSTS=.localhost and CLUBMANAGER_BASE_DOMAIN), so *.localhost subdomains resolve to clubs without editing /etc/hosts. Co-Authored-By: Claude Opus 4.8 --- .env.example | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..91022cb --- /dev/null +++ b/.env.example @@ -0,0 +1,25 @@ +# Copy to .env and fill in. Values below are development-friendly defaults. + +# Required. Generate one, e.g. `python -c "import secrets; print(secrets.token_urlsafe(50))"`. +DJANGO_SECRET_KEY=change-me + +# Development toggles. +DJANGO_DEBUG=True + +# Hosts Django will serve. `.localhost` matches localhost and any *.localhost +# subdomain, which the tenant middleware needs for per-club subdomains. +DJANGO_ALLOWED_HOSTS=.localhost,127.0.0.1,[::1] + +# Multi-tenancy: subdomains of this base domain resolve to a club by slug, +# e.g. http://ajax-united.localhost:8000/ -> club with slug "ajax-united". +# In production set this to your real base domain (e.g. clubmanager.app). +CLUBMANAGER_BASE_DOMAIN=localhost + +# Optional. Defaults to sqlite:///db.sqlite3 for dev; point at Postgres in prod. +# DJANGO_DATABASE_URL=postgres://user:pass@localhost:5432/clubmanager + +# Optional. CSRF trusted origins (needed for subdomains in prod), comma-separated. +# DJANGO_CSRF_TRUSTED_ORIGINS=https://*.clubmanager.app + +# Optional. +# DJANGO_TIME_ZONE=Europe/Brussels From cda4960ffcb5939c6ffc84ae4deab41a06d6c44d Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 15:32:23 +0200 Subject: [PATCH 016/137] docs: revise shop order-level discount design Replace the ad-hoc manual order discount with a per-club catalogue of named OrderDiscountType presets snapshotted onto AppliedDiscount rows, with stacking, retirement, and an optional value override. Regenerate ARCHITECTURE.pdf. Co-Authored-By: Claude Opus 4.8 --- ARCHITECTURE.md | 68 +++++++++++++++++++++++++++++------------------ ARCHITECTURE.pdf | Bin 416779 -> 423751 bytes 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 1bdfa00..7fb2fca 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -652,31 +652,44 @@ drops.* - Only applies when `today <= early_bird_deadline`; otherwise the line charges `list_price`. A `CheckConstraint` guarantees an enabled product has a deadline + type + value. -**B. Manual order-level discount — admin-applied, before finalize.** -A `TREASURER`/`BOARD` (§3.2) applies an ad-hoc discount to a whole order — e.g. a multi-kid -/ sibling discount — as a `PERCENT` or `AMOUNT` off the `subtotal`, with a required -`manual_disc_reason` and audit stamp (`manual_disc_by` / `manual_disc_at`). It sits on the -`Order`, not on a product. -- **Lifecycle refinement.** The manual discount forces the order to be *editable before it - freezes*: checkout now creates the order as **`pending`**; a treasurer may set/clear the - manual discount while `pending`; `finalize()` then locks the order, computes the final - `total`, allocates the `Invoice.number`, and issues the PDF. **After `finalize` the order - and its discount are immutable** — a correction means a credit/refund, not an edit. The - access service gates this via `can_manage_shop(user, club)`. +**B. Order-level discount — selected from a club catalogue of presets.** +Rather than typing a type + value + reason per order, each club **defines named presets once** +as `OrderDiscountType` rows (e.g. *Sibling discount −15%*, *Volunteer −€25*, *Hardship*), +managed under the club's shop settings. On a `pending` order a `TREASURER`/`BOARD` (§3.2) +simply **toggles the applicable presets on** — each toggle creates an `AppliedDiscount` row. +No arithmetic is entered at order time; the treasurer picks from a list. +- **Snapshot, like prices.** `AppliedDiscount` copies the preset's `label` / `disc_type` / + `value` at apply time. Editing or retiring (`is_active=False`) an `OrderDiscountType` later + never rewrites past orders — historical totals stay correct. `PROTECT` on the FK means a + used preset can't be hard-deleted; retire it instead. +- **Multiple discounts stack** — several presets can apply to one order (a preset toggles on + at most once, via `unique_together (order, discount_type)`). See stacking rule below. +- **Optional override.** The default flow enters *zero* numbers. If a club needs a one-off + amount (e.g. a bespoke hardship figure), allow the treasurer to override the snapshot + `value` on that `AppliedDiscount` — an opt-in escape hatch, not the primary path. A pure + ad-hoc discount is then just a generic "Custom" preset with an overridden value. +- **Lifecycle refinement.** Discounts force the order to be *editable before it freezes*: + checkout creates the order as **`pending`**; a treasurer toggles presets on/off while + `pending`; `finalize()` then locks the order + its `AppliedDiscount`s, computes the final + `total`, allocates the `Invoice.number`, and issues the PDF. **After `finalize` everything + is immutable** — a correction means a credit/refund, not an edit. Gated by + `can_manage_shop(user, club)`. **Computation & rounding (both kinds).** -`total = subtotal − order_discount`, where `subtotal = Σ line_total` and each `line_total` -already reflects the early-bird price. Order of application: **line-level early-bird first, -then the order-level manual discount** on the resulting subtotal. Percentages compute on the -base they apply to (unit price / subtotal), round **`ROUND_HALF_UP` to 2 decimals**, and are -**clamped to `[0, base]`** so no line or order can go negative. Every discounted document -(order summary, invoice) shows list price, discount, and net so members see how the number -was reached. +`total = subtotal − Σ applied_discounts`, where `subtotal = Σ line_total` and each +`line_total` already reflects the early-bird price. Order of application: **line-level +early-bird first, then all order-level presets**, each computed against the **same +`subtotal` base** (percentages don't compound on each other — predictable and order- +independent) and summed. Percentages compute on their base, round **`ROUND_HALF_UP` to 2 +decimals**; the **summed** order discount is **clamped to `[0, subtotal]`** so an order can +never go negative. Every discounted document (order summary, invoice) itemises each applied +discount by `label` plus the net so members see how the number was reached. -**Extension point.** Both are deliberately field-level, not a discount-row model — enough for -the two required cases. Coupon codes, stacked promotions, or per-member entitlements would -warrant a first-class `Discount`/`Coupon` model + an M2M to orders; add it only when that need -is real. +**Extension point.** `OrderDiscountType` is the reusable catalogue for the two required cases. +Coupon *codes* (member-entered), auto-applied promotions (rule-based, e.g. "3+ siblings"), or +per-member entitlements would extend this — add an eligibility rule / code field or an +auto-apply service on top of the same model when that need is real, rather than a parallel +mechanism. --- @@ -703,6 +716,7 @@ User 1───< Member (FK, unique per club) # User is GLOBAL — no clu └── (purchaser) ──< Order ───< OrderLine >── Product ──> Season │ └──> Member (beneficiary) ├──< Payment + ├──< AppliedDiscount >── OrderDiscountType (club preset) └─1:1─ Invoice (Cart ──< CartItem >── Product) Season ──< Team, Event, ClubMembership, (membership/event) Product # all within one club @@ -739,10 +753,12 @@ Legend: `───<` one-to-many, `>───<` many-to-many via a through model 8. ✅ **Tenant resolution ≠ `django.contrib.sites`** — Sites evaluated and rejected as the mechanism; `Club` stays the single tenant root, resolution in `ClubTenantMiddleware` (§2.4). Sites optional only as a later bridge for Site-aware third parties. -9. ✅ **Shop discounts** — two field-level mechanisms (§5.7.1): a per-`Product` early-bird - discount (toggle + deadline + PERCENT/AMOUNT, frozen at checkout) and a manual order-level - discount a treasurer applies to a `pending` order before `finalize()`. Adds an - `Order.pending → finalized` step; no discount-row model yet. +9. ✅ **Shop discounts** — two mechanisms (§5.7.1): a per-`Product` early-bird discount + (toggle + deadline + PERCENT/AMOUNT, frozen at checkout) and **order-level discounts + selected from a club catalogue of `OrderDiscountType` presets** — a treasurer toggles + presets on a `pending` order (each = a snapshotting `AppliedDiscount` row) before + `finalize()`, rather than typing values. Presets stack against the same subtotal base; + optional per-row value override for one-offs. Adds an `Order.pending → finalized` step. Infrastructure/config for the above (media storage, dependencies + exact setup) is specified in **§8**. diff --git a/ARCHITECTURE.pdf b/ARCHITECTURE.pdf index 17347a08985bf0fb8d248f2732bbf539acb3bb1c..c9c9ae1ebf357a11add99c87252c408214ada312 100644 GIT binary patch delta 106603 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

12kwbcr}EB8*|cI&Vp@=F*7bG{*%6b5o6p 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 DBdJV; delta 99460 zcmZr$bxhttlg8bpxLa{|hvM$;#VHhbez;q4cXuuB6iV^p?(Xv9FI>M%?vhLHkKN7A zCcBxPXJ?<8%;+USOglm10}ML{JJ*jFvi}}KNFXCfAR*gGAfbjlA=%n?yr8&9fDn7n zh&W_HHLr^n(9f;E2#p&b$HSJn7;C7B+UswTclc!JI_?rSzna>snoR`PF0+Z3W*!U# z1Z6j8`Tu}knJvL!hKUYV@S@`Em2*!SzFA*HGCM`x;rUaNjw>A3iJzOX;Us}!w6CJF zF+QQ;A78dQ#eWOwlb41)pHgH1e8cdKU1>9E81cdOoMgybM`g7divUg0NNPcjWByJd z1krGhU+kl2{P0?<7db3u_iG~VqDOJB5!1c7*r`8BFSS{_G@ZU^IwfdcWoxpcXx7XB z>*wqIT22q5S4%_BJjDfCQQe78VdGETW9{m>QYt)4>#*&>Rx-s#SRFvX(b;QE7#`tJ zC5_k~R<$urRa1ok>b$}?Exn>1-9A6f9wRNX(HDs6WSW4*-+4HTo0IR+sf;lT*-={h z%qBhE0Y^0f*p~vqt&A~O?TDUwOs9LQc%hczIyy+;(*x{!Y~x-ZJ!>zJi)kvh)+YGo zWZ_sUnIWZ%E_Jmx-WzcLTvc>uStmHo>ei!pP;j6%+d$Z#i2n zivH3XXX(Ad(q*hCk(N&16Uxw(R$sM*2a~#%e$X4A1j1DzDG(^0$I1$wOQ%oWNh4a) zO1}H<4nI`s=U${}XsqiNZjd)+?NW3qsJp1=X4n%x*isNw@*U{Y?>lehSBnNRqr>Zh zYJ3GXQz~MMkl^D8w50aFr4ncf>XH^zH6ObG-T_r3`O2TQrfj&{R)32VZQJYSIsGYO zfOW4wY`XC*a;el%#ZIf=T`+vqsgyM|{_Fnng4eLyZF9r){bRRdLccT7!1JxXOMlbt zW?q}B`o*-R>K-^AOONtc@Y8{>7>R2!uFQj|F8tDZwor9>Jylh<-dS{0=3H!v*u_Gy z2wQ2laoSzYqH#6#qngG@i)HKYg7a?GOWEwBq{m${G8gIJ?`FlV+s^lQBxp_U_lhy586Q~vda*m*6B;fSJ?+B1 zQFkd9VV9EDMiJ3oMH1Q31s4QKPm?OvG##7VwRF@c!S0l*+fwb;%RTiyG2~6^?|qa+ z`^-yjMpuA;7`Ks=pPv3%PE(#rOOE&}Lwt)THyFmJB0kuCwggS}*TO9X=$x3Fz_UZg zXYb5;BW#(QboYjZZ5lhn>ZPwu(zjCm*J*rAy2?A-{SKr+NQ7*xE+vGGcvKCUSlj)nX;xNV%0#L1Je^+ zG3;djHWvP&0J3D3`KmIHyY-l4*yFXO;1Vu%<8I5zFsYK0b4KJc-}0F|U2Za6oj|!> zUa~0fll$f-Y&9Z*yLE3KhQ}_%QgdQoT5WbBAZ0h!Rd!=(OGxJ-TllQ|(WZDNjLBNeERa}sDUZhppr>nxEB^kUGJLS6xcf&apFTaQQ z=`UYxhiIIJ*4$F_yVk7RC?xzDKTZ|C3A$}H>!Psq*^0`UjSeXMfU8x|u(STV{yd(~ z@ZaD`tDSe0YSz17B%8n8Clxu<7eg0PRJ-*iX^K0aLu?%>_b&JH`HQ`?BmZyPc>n{Md zERd1K*`0+RL9b*q%c#U9-@4x~VXQg7tx0+kuRf`rUr4`RnUSto114i0kqMM%s+Zus zUcyj#5n8+VH=M05UNF0k#)jRQw-v($uO>>izBgD4?*Sw_jH`<8xV&lIBf7m4++T%Z z+;>l6ouVHs{WYgMv=o?hN@*dh=m`MNiFM1fUsyUul<5AL;EMGrq^T0nw*!E4tDj9_ZjK@xHIhHMVVb&|n;Fc{WiPi#k&9z6p z^}c<*EDPFghQ=yRUYV*7^#tKf2x@5A{i(Z!a5x4!OJk^qXD1 z&j&)O*ZF6So+mn9=Y4e(z=@MoQq@{}A5ROwsIyqB*mlc^R`gz5Ue-J7_DY$2)d$uC zO_Hg)C)Nv_Z-bLP`+U#-Q$jH1(z;Q~5M_Ksx zx|^@DuB$#@^*(Mtz@PDvUf9!ncYMF7=c9dvQ{_hA^QQKdZCatIYpD3nJ=f+rO!?#y z?_&b*EIh4Kw(fN+eXZSM{MCoBLsK02_69={(=eTzHK1BrNWLXcR%d55y9#=#$&;6YaShv!0Xhh zv^`QO8Fv%+Sm*clJ>k+$zK+o>Y4uq8Ttv~OAK%&upIzW4tu3B7@BSPYL`%@BR_9&@ zeLjp@JT-8H6Ly6^Du?}6woV4^3*5Z#f`2WQO&-%joOw#x+4p90CTt3*^|BI`dak22 z1mL`LU}?==p%HzlX0U6As?{mjL2CJB%-ZM|eoN9-m^Sp$S=W9!r`yY5?)CZI>v=C- z+#8At(8|wipTEjMM105@e7JsY^8M_*CQSOd&sXRCp*;Vm{WdGmxQqWWUvfolduw%CuhC^lp} zN;=pB*~ebyYBz)6J@ZR4r0i=2S#UH0z?#;%YHJjfs(n9BNtXKXE_!93tJ>og@tI55m^_0ecIslCMDE;_`?k#jKmY(hOviCtT?FZyh`Ar&<82wj=YVG zGt$A~aRTb~4lhME%FR0Sq-BB4;nBq1X~Z?Dhds>EN|^L zSdy=(#(rzb`POu?6DtgD=id8-YU2+Rwhv zi{N-gOLwLinT9yRHsD?uAOr?3?$hU3%S}Gve0zP3YWd@hFVSH3%4O5nO!2(&iR|nl zbY4}VcvRT>XvoTb)0LZr*KX6^nfBYyMPYsoA0l_wMVdB$A%2TTjBnRs(;g}VYn}Xi zk0~|ZJgwVCbtN`)QsrQnfTNfvH zbPylez)LVOmpV;^K6b89=CfNCGoRD0WB1LPU-}*G6-$FLztgE>lbnAGAJkFpN_C@0 zwuSrVYR@u^if5f7QbT8>Q&OIC^7Q#E#gFG`dg0AXdfU_c`AGg21%Iet`&o-&vzLvH zpxGnSth^%SUZmFtQ~OomVcT#0VY?cdxSLv8@oi^m&Qo-yQ?D!ACYIgt6~s!ncC+fp z^KH?w?;nIV@%KI&z%TjbE#=4RpCNYp-mfk?kZ0}iqCEya<4Mf3R4Cc1)FkYY;u-#h zdkXQOsEJ?Ks;xw?UPl$R#k=+0zuX(d#N)!%%2#!Ngo99RHG&2RNYc_8scph7xHxC@ zisB^OK3_<M`$$c6&_Y+Wg3+d zu&a?Fx6^PUsI3Hc5?-AN#x;3CB2)%S!3?z3_yxt`R+J3g)uy#Pt!O>GPd*FiTmML1 zT$f!+@uQudRPICQsl+_O7wcD!`uQ@=M71t^k#5rDkUo;+*7%d?rW$>LZdUk{1ludj z?FNwxEq3pXFY~r((PQHdOLRmG*h?W07l^N;L%WpV0|sEQ>UKuDj<+-n^uo7DM>M|L zduiatsk&7tcioS8C{XXltoBcZ*RJ!rOQH1Cm*2x-FKFC(Pn|ICl2FpE1C}^O>7#3_ z+s-R|!k>L0*GzCRM_ynlCER1j!>dPk$JNTKK6^$XELm$*MOAFt@Q>Ft?dQU!Cm@^L z4xSQ^gMo@M!)dmjgL(bQv@HzDx})B!YZu`eul(yyA%aE!eP?}_{e6YWmugvmcxFA5 z89nsjrI4?=G^c`^X-pPl@<`01+0bR6(nDyxcu;}EM$yvHPTMe;s7A>@f)I~6LbQ{h z&f02TRV{8t%bQ7sNAq|AE<=P#vc9gU%0k1k7eFcLe5v4Rzm!OG{s>>W=v=U)GMS!Z zwI4%ej6B2E+L&6>fYHa&RC%s0G72xPcbJtnN_y44U9$z&1XQ1l%cMtaS*lfALav6GT(x z1JG2hri^^)AwF%_!Y>bs)jui23nt;~2hs1iZ z{%R3-p!;|S_@*08o^hrhpmw|%%4`@{)6SyRkamquyf-4gf_gfKXdR06FOXjgwrJ0V zn%=Cvt?ot64jmC@x2!kTU4j`|&ijIg$jN3WNQyEoBmLKG4%=UZwDv&Prv8wUu#7NKXfo{Z5+=1m&kYVdoW zU7#FZ_AKntNe|mM9XoeHQf%=E(aF1ESXf^7K9v*qcbd-BJvLfES7WPySo3x~rKMBf zvupi*6)bm-x&?Wyj$LATr{l)I|7|1N5z))gD;fE$)#Wh~Y7zU(+0nP|lg=0CFl$c~ zbmJq;zt-mo{t9Hb%Ba9S>94@6-<<`zv}|?Z7X6~9&s_$`3nYIoFP$L{ z(!s+~jQNdQ4tghxREirrWS$V;#pBd2 zZu^WN9Rz)mrhtkj1aCk|xcy-9OVqI>8*G}^tSrkSm>q59@)TAF%FzBCs({tCOO zg1soy%dvrw4>an!zzh0?k|$v7JMZL%lV8ojElsZ)ew>d8i`e>>L>PaxFW=U@h7*R4 z(j6NoTqLN>^z`Z^Bwja54BitEee3-(nc|ar_JTM)x}bhMd$3n9z0?eD^PM@$F00-= zpvXD>yC>|ogKGcabc}q5z6t>fnOa!>(o=A2#~t6b10ZL}2$*xDcGh&k=fQ(hFC0y_ zD(`<5)cYKPsJJw}YjgzC!hW*?(z&yHYiIR3kun_D5Tc;=>pH0JQM>+wFi`EWi6!jP zS&l+aPlJ*QLLKy^Kn8QU8`+U3@lHAEk!QCE%(M0_>LVYv&x*aC}caM+?kWaPb+Zu3w_cFNr;_`4CUPY7^ zp-IcqXPCYnWNYhQ)>+pzZk<_wbtU{&#}Wa^cQzg9;%a;J-f^a#I0zffWUC)d-a5}@ z<2Ai9n4;`Ezu}+=7!kN%0u2PUNu7-fv;@=DYs|gTh%kDt|Cl(FOZBWOOE_JOSvC`% zS8M$eM-tB%A^5$~2qbiN)O*JNz85{{_*qY&PUj^S)E-qC|NYFwTz_jeVBOmA>hA`y zSLL?Uf&UZZ*Q)gSV?eOik6 zn86X^0HmT<$scjR|6BMiMMTVfu-&-hF%7z#;>SVzP< zdEjYx)f9idtalA7yyh#|DKHXx=rPpM@6<8t<*)Yfy9fl0E~b}fTD|&IF%L#H){UQ- zpUYW)S%Dd~oO4>f^EN!>>9z#3^}e~=PG010anh52dpv+TCbTT?|a$(dIxO4?az{nh(q!kX&3V4ckwN(21C!HvSdwf;-}k3f;xXo zB9>k->zpu;VfSXyqUhjjYANtJVOi(L#Kjo}ub;MJPXqA{V9EG70hDMd3K9UjA3i0I zIn9iKaGso9KC~C_HmyHBPDe#?xJ9g(|sd6=z8d95x;S+kjq}OaR3#b2(I} zIvFWO-xTKPw63%fnzmM{Z~2UysiMzzM^8eA8#k@L^`C2H)*8WGXk@E|@n59vf+NgM z6&lFX^E6W+N^1W~Z6%oMcHf9^LoxwBp2e*Dh=Xga&p|ZGpd5NoODTvdDeWkf*Dw4l zxV!BD-L+0It%PNv3RpYUQO7%ZSolk%>}5PQ^x#)}eLXUUFGXy$mm&y=t)X zO6N3O!h6}Y=00FMF;VWzd3SE&xg7fy_wK$~CcJRUQs;EFID;UF@a(J!8wnSXjAdwQxi^t+FVKYny?t^p*+x2)T$> zyo*orkB1oNhi7>j_fh)H6yoT98^xTP*d5{LJI3in&6$y{j=c%xgPfMrBKwBV2Tc)% z8Yvek8_$V`BEHs}_LYrRvwhu1=RI+L)FM}n!-iARz{TCAz4L%8+H-f;@tv36Ug>mK z+|)DE^Cc(WZY*j+tY0Ukk`ERs))MwLxkFPajAEpWB9{LGcmv|%I48nezIz~)5zFjK(Q>O*1$b^H5xiGOUWWtI?&3m{%XQ)IUc$MahqPCDJLk6d0P1DG|!=tKGCGnnDVyS3H|c?1kM#w;9fGK{oz$6 zw8upfzE;k+RpfBZ$L5=`Z#Pxr*&CtFMo*0j&;=^{^2uTKh+yiVDlvbcn9q(Xj~EhRJj*ixj5O6vH!DF zTUqJ3>UYyMHui;D0J%Wx?a~n7TI~6fodtT?z?%aJEehk1t=2YLT_xIBILGO_oX`>g z+&ZDW;l@KApZ)JhAE*B0nwU4FZ^Hw)a2v5Sf;m8klywXfuou5X~!u@T})m(9t!rO z(@NyZIEvAYaGXZunT^PO8;Z}{(Bsamy_#d8IooYgENI~eE$|jYw7S{Y5aXHnE~~CQ zJ@ATO`7ahAi#Qt(56O5_d>mP@N7J(3Ih6){!{pVn4POt>!iv;&!sO=)LSIw>I0Ov} zE5u^TpDwTVSYjaSGWow)(n>D52s6Te&Zu6(J^K|i1m^W)`h`tyN!i#YJs#^6J?F1K zU(r74*9zy)m>7#)_qb4qqW2DTY%FO0fqjzL-w@;W?00Bkqq_0f*#5vw-0_L@xBEMw z4Zj%K1aJCl3stxHQ3)=H$o$?6;N@TZqx30qDwPyZ=__m#D=mfy*H>8cnia$u2+E4I z;q+sRIf>xha49hue;RX1X7V%}b6lH&+0h)%gv0k`j|yL5FbA%%ybau(*PS3#gv`|BSmPGgM=weutZg4 z+GkLBO$92dURc>ok722Fu_0I&zP&1b%B7UF*!*Q0zhB9cErw{pl##a|v35{5qP<-Y zeEK6ow*cmv&UOtXk9({ZDvHu)2ZdcwOdr7X9U zrHHsHRVoEZh*M6vm<3}e#l%^bJ`k{%l#UEn&5L-3KmS0T`bxbkL}aDGQmXl5{cxYY zHPfh2d+nUW%7*@hlrL4D(?4TsAa<6bBYbyzlc)CnOO3li+}%PyClI70d*WilxkR0X z=#VsSe6r%_3;=A0nQov;oiPpMUtu`SUdm>stLw{G?C>+l`!; ztl_k8xL%C|{Ud*kpMBYL-MQJRs|A1By!EWhIwIYAl2qmNBn_vA0*h>K`Gz!6=WR~uDx|qL3tL{A@0-xH1b6xV8=BH5BNKDzc|9q2qHt(utDwC?(n8uNG zY|Z$zU=Cf@wQa8d7DLIgp*O_YQL>KNkz~R8GYC)-J6H_~1kAIu`=i7!b|e~^bWkJkR_t+9jnR#mklKFlULL1_SHXZ8RomoLP7EUPV%4Q>yz&jxjkT?#7+S-;z&m6uv%&R*M(p6pg*ocs>gE4vKVks{}K6c@pq>S&mBpSbmH&JXPZucacfmFxN-Uo{TE6-yJ?pT?^EU~C+S=k z8QYPr73u67TfHPv;!A8sHqNtj$kkgQAmeZ(zxIs*jJ=s}{r=Z4aN;CGqKnW|Cw9DM zRg#t)7?MIGg7@64T*j_suFNvYD5jIBSGn)?HQBdR>e$C zd5ZmJ)yyAhZNS1Tu&IkwDqfz-3U`|!XHFIb@RaB9W#KSn=f(ek_Exlvk`p6NHa4fj z7=`g}BK>W_p=541>>5-i%Z>u$1!Jy4=gb+DW0SV6>SiXuK5G=L$Ib@zbs!XRGuRB~ zKBsvfGZdrj5d(@!g{mZ)6tQ$O2hUFO&R9a@+PF}CzCyKc1@a%n^$O>?80tQfAqO>q zNWH;|$l5`O@<*%@`CTk4rQlrr@7Kh_2R&ugmy8w9(`4E`DVy^N<4Tn*>Y$&eunU#F ziFHcMWyz!o-iV1E3fw;0Op%udbeRhzmQEi_S-O4rOo;*>kha{VUCbE$y;XN2a` z7sSmPSi_Jalug*|&st2K_L!`{`k5%Jw}VJNGd)>iHo#glOLQed)pVDx;uU5LJCwmT z(`#iro7H-jbHx>Isn8XE_Nt!&KHF*7HE$*cf)J+4||B z9jmEX%;u?=qaa2*DJn#cBeQCbls~fwQjlEsxC~UORIr-l#qVU2BG0jJSXZ$w<*C9y zm9e~A_4!!_uFh+vdcR$Fw}BR}7O2(b^nZZ+k!|(kG;R-TY}mbB^;!^sVHO_AET3>|rYSpJwpEbX| ztg&%6w1RbRCeaoe-5NVRxBAeP+Qdg>-Lxn^3=0U6?56&Hos^Ew(R-&pL_uYo!dD`v zl84JotK;jT-?DF~Vj2Uqb;^%9&PwXA#C!_MXik=j5sOVMOwo!FyQIO&$*#M<6&vu@ z`MkkevX7KT;^N#HoaAv(y$tCrt&4J_s=Kt` z*6<}Mu0bzpx8Vfu>{D_%XMA^lR9V^;G*3pIF@*^4?Z;l(u z3O-;wdFIm1FCX%z&OdImw=~_HK$!QgA%O0h9>bx2IBg2Yzz6g3GR$_9faJv-OBuvU z{?L;DtR%&#L}CCs9=cA-J&Vnee)l*|b$wdErG7vXysAJ!w$Y3|0#__Uxv;L^1*CY; zShpFM!uE;enWLK-RnN0~z*J2+UR#gc57{sh@0hf>*PMnQ@pW zWFUcpmX{}_qk$4ES;n-k|3K#U_-`oZaLBr!p_taStQq=t=EqWjVkKNS(M%K_W@YTQ11Z(i2py)`(K`i1f4^P@xQJH_faN8If5;c6I*fuBO^^AM)66Xq06+g|@N-Q>oVa#Sro z0WlgXd33X5KgUM#S;Lck#9jD^e5!OH_!k%hbFAdJ;P*A_AzZQ)P&G&g#`ITIjCB;3KyK9Vi~oLxiyh(!WFdQ9Tf1vmY=n`h!ArXvu|B zFSunNR%?vwC$u3w2xPR}0NVv}s|FcGdXz|#0a4@`sr{Vihd+#L>nWmxQ)BO(To?}y zj@W+rlA~<)G=a-WJ?!_L(X_*C;(QZ?DCCc&J8ae5fy?RPz973x);5c8?K+*n&tP=& zQNp`Vdo_5F=Hs5D%k~P1aLt2OA^rr@6f64(53AUTD_%NMu9^{lZ*HB!bCgia@`IFen!~!d0zwwE2!3 z2@yj3kB9iDP@{TtGo-Sj&aSEgst?zO;;CK&V_g;_=5(_z6T0WrFPP(>J$7@$SKR|q z`9(e(JFPem#OYRM=-s0ISt`8NyXDtr^1xD$a>o%wB#^$Q!x%$S8#P{u?o3p4ReHh} z!k#1T)~X=j{=4<9 z5jMX_HhB$ABB5SidKan$!&iy7X+{L2GG-|5Ul1QG67O57^8xDK6dRjIf(@ZKKab4P z)uIRq$X>@ihd-^*awy0Hj+aK0o3X*UQnF%5Z>aqPgnBRV8Yh6WDH4!-u5L@7REEvt!`^>c_dU{IQKBXtY_0!V#3o%1NSkP=bh zT^J0!jYbT|-=!onBi690rgL)GQg*Py)KDMk6~9w3NiWF??CbrR^>p&PA z$r~i3xbK_PEBWs%;~q`q$VnaW;rRa8>AS`MikL5Ky3KtRp6qb`EDk-5vwr`pApGH} ztFQ;zv2GImD?)Ds01qWB!74;0OILyz@ChD86DdHM5Iz7_Q~?ctF^r9_7#dG9BMb#v zL^4AP9edv>vRN#cjWn`2393MFrapl#fx>a^f$WcL{2)7B;l#6mcy~mR)E{q^NcdaX z33aj{KJ|7kLbAyUpRZK#9A7Cz1z~nSn^OcpIq&n*UIht70~WH%|DXl`S&i(X-{)DjSBgi5?JsvR!F?u)N9q&39?OJSGH(OSC->a zAgAkO%S}3F2`b0OAE+@M4Ba1N9&{Ygl<=kqJoypY@Z0x$P|?3SBHn~hpBbT~8O&Q_ z!_06X-NJ{LBuRC4N2r4Xop&5JzvHVT^^m`-D`9+I_NJTcprQ=K0IE+Y;m`}ZNKENP z=grbz1<%j}Q%S+9Sp`ERSKW=@z zcU_6TKYoB8%S}GsKHhy_$$;mCj}Gt-`Wia$2EqC~gJ%@*W^z$Kf=JbImvhzFgCaP@ zkM<pic^tUlp>mgHjjesuH67E-XjS!PWzuE8goz%Y2M@RaC~lu`h>TDP1`lCz2cCn10oD`$q*q)R4JqtP?E% za>PLxM~KCoq;o-tv$2ya`t}8ArpCBLKC|ina*lkq)T8*t^!qydmoFH%q(8hfan7ze zFuuQJaduYJN;Ra67LKT!V?Ow?Cpz0-A5+apQ$|TAC*`^eyY7pTxf^at1^ET{B> zohU+2>_hv1FCkPGeTY@^nqX(vV`c?EBBfHAVAsDA{!iFu29XRss2gc8SQA-42vd#{ z+L6>9YL@(U5AT+{@uf%g@M2=g=7gg@@4nC*t=yZgzBbyowi1;7`qb?S)m##HG$;7O zlb)?r)_=ru4-^OJEaW}i40%YQ-N{IR_e>*Zr6 zjl}ymYYg>jyg~YzYvj;fmG`Z?YR7L6*XYo|M_SPDRmJz{V5E9hw6`)+nv#cnQY~gu zewd=MHp+|sr8h%5?Vz{XuI{|6Djeq9)T2?Wf}!3-s=v;ikvih!zmW^7a`o_!^Gyir zU*o`#XNNVdy`Npn9#Tm^KH0B0-x!Use(S2b7dC%-k&ZD0I_V`G$S&J|8l3s2Wte56 zMUw+3iLjXWoz49#+dVCysCd=UTzyv35wxjDbyaTm@Aq5Nci|%h{X6GeQJ)`^;v|#T z-?IP8*I(T5kw=wxO`&NM<~#?vUS#5TO;g%`J0b)TSPmk+>_o3YE?pQFbL4sm?sVdo zr90o-p3)i8F+q@pOEt)T(I>;P`;oyb?8Za#+!tXe*O{qrM z$R9)EXsv@5(Rs`zASe%SJNg6HEgWbUq-)|9-(8d}nf^rr&Ps*89rAI~9WSkDGjQRR zRmIJQm7^8ykZfq*EeO_kt+=2507{8tVi}BM3iv#79!32F>>eIn3Azi~b41Lv)XI(?y; zPdW71=q-6s5g_)7Ve4fe{GTMd$ddX|K$Gvyx8mzChbAAbUxib@g)}(2|1q^ z88J7?K8iS;7ddS?p%x^ITk&0;_)s}$9a+_tL~apl5Y%p{WZ=t1b?{rTKh%q)kSy=vACm-Yr(H)E5S~ zENPl~z(p(r$yos(_eKkUI|Z^$3oD(?4(t3&U`X;!X>Q0G7BvAN5^NZq7SSP#1L+Id=|`ssQWLCw50c%om#ZOQe@ zc1j^v&lk*qFKc0Z;TV75qp=&Tk!60Hh!;Acr*V?7IzEXUAsnWEecwo+0K!n3W)g~; z2*%9}L%%E7a~=T{0)F}Ntzs(gTzyiOVtPbf{}{262^1FM#;70G5X%b^l!g%Njw}!K zB2f8tEbS5Sg=ATTZoNZ2s z%*nb2cG(pZ#EL*gSkvNUv2KKl)tCMZq`$v|d@xlZZzu}@sNI|xv_1psw+9i23>)pILDEI}{S>0_RU%0GRG5D5Bb|iFq z1ha2?oD+EdXN4bf#nO%0A)6he%ihc&sab();m=Q2=#Fyz6=`eo2N$@X^a5Rc%1mZN z6+!DNyk-WqLCe$PnO#jmJn@Q|Yj1(Eqtz3+o8PqoVr8Efw64%Tvpv(S?9m{Y&>Qi% z*(@dRfPj#m)iNQ`MfOg_d8{WJ2L^{c0co^-u{g@)KxqSZc)=i9Zu?k}7-_N{R{#T-==#YDcv2pSVp{X07E)?_U4vxn!WPFD0UUvNf8V#lw8E{aikY`3op( z!q*o7LOo7(`$8A_2g2!M-!00R_F;XkSK(DdbzkGNP!C9jauFd!4>BR#s#WRjQtY?R z164IoYgKItGgLpuAk7$tnawnZwao%GUeIWb@X*WdGfQ0b6r_q+$*BsQ zrC6E#ETpekN!DM;M6r?=jquW`$+;%@ODN~VS&Y0OK37NR@mxkG(TINZ;mqGL2GDr- zRC#`11R+JH*G(PrQ*w74OPmCz(IJ{2UpPe4**g)qBHO~SgYJG?JBYxceO+zg&wv>Q zG9{Gmwa)(htqu8im7IuJ$*gzde(?N$;Vl;=UVH$)%Dq|v8&^RLk&9no`D12U=RlN5khn%dpasJZwJNJa{R%O;L z3zv0A4C%aUq)(G+>AQi}Q{N&8_s#1CdtxLPZ)-rdL^ZwXxNMpmf04FFms(aR?aj&b ztJx&X&*__jtydfmrK;s@P29`D{rJfT^neBFLb_}dlpw&t!O8W1E45eZn8uu! zeE})aRjB0&e@Fz#$GpsOI?>;>1=eOvvm8?hv^3D~epHkewFjDy3x(Vz^b#2Y&ehEv zRFqU47XBqdHy@Wfq?dg@8KSR>kNE+;;GItv{wn4`dW^Weil_s!a|Shy3=9&E{pInm5R34t#(Vp*e8h`$I4M`^{sU$p_CC@KN7} zb;bUmcPY9h@3l?57A&OBc!Ac>p^*tw<4pcNWz(L<)Tv_&2!`~rt)zijl0mTO?_wS{ zCWO?e;uDLa`6b(rbss$+*B*`($Nx2j^A@o~;^*{A z%iGzQw~NjVa^1YsR|(mQm6SVWGmlQov5bdCC6Ca6l5Lqkf{Wl3z4zXg@6?A#v4HUh zp-}qiYHJgC6m{x*cltOZdq3dqdj_TFzh3|!L$6owHy>|nU{Ua#=*Q9P*82tc4BhFv zxGj+N{mJCI{;$MmCyEp-iCT*JHVg^0!YK?pWD4mQqIaOi^P1NLIN^;eQ(da)GvIVu z%Phwyt?XPGYPhz=r`*5F5}s-gQ}sn)Q_ET5%_XSYhJVqVO}(}w@V@qaYP(L=!MwWe z`*>M<^?BO6a_WG9j>otZopUAIdk+}5{Pr96gqmF*2;CecH;wiuXDj{j<3`#ymQXc5 z_5C*$(;~rznl!@`;Fv2+2Q?yq1tmlF4ONv4`T-e*jkIy<*M70csRQ^QtmIi>CP;=Y z2oluxsCDH@c-=A+aI7m~(0T^i4vWKZP97f(p@qS2jE%E*fg4$7F z%y9&@UWK81nYdOfK`DrMA?wB%dS@Z?XxpbS==RVSYD6jkF_2k#fVc=du)#Y*MmW&= z50wE2#dn1n30b12YPp>2fM(VN3ZX+$1hKbb zL@4H8sxl13m{>N5bc{c_Y6=aX1T`QZwxAw(qB3L~rMENxb`w=mq98VJ{_bc0jdJ}K z&>TB>r5~RJu%q7X9QjrZcTbdaXpPS{(T;ErqI~==~?(l@o)c`3?!mY=5$+`T6a0Q6i3Y!dL*INk_5Q$y?w@!M7p4*9f0m z^wM1L&{8w-*?NnQd3CwH*J45=^O90O;-$uB%*IJN9>U_%~qROB^SmO1ONeW$&vI+`hgOV!{ z2vo3$g;8uog@BT&XAOVAw+u9_hVB43@GVVA+Ew(a$mPcczN>&*!KzJzS9Fejn~3_> z7mt8|b8*regtIU$gqh0S^p2_f$H^j;Pt+`ok(Y4G?(qq(4`zOFDCj11r=;^eL@ImBIjW?k=uP-imUU|emvf8JLEW(O zA+eIm9lQs)o=esXi0p}H(!}Hsm~UYv<^>WHU-S!#oCSdl z#G?m+Io;vGqtnbZGKxjZPW;)EOqVKdWf~tp5T0cocxY{$;p`r-oZ-wN2y|g#OR4}2 zlgiW^SM_BQp_n9LZT*_qa;Hakl{>wd9SjXYnGSN&`(8Li1}gO*Jgk#gDv{JmvcU^v zDYVw4wbgV@qWF<4&1p@lP75)A_XGHh4FR3d%=M)MUThha2}SJ-6ov;{446iF?znwS zSEk??mfQxrToD4nfYqu@e@tK=BL#vmk8`ID_Pj2Ommgw*Pp4zqd^X~p3uN2M-u0gG5iYCF) z1PVsg#x!fv7>P>CG>T0G`u4LUb)NA>4ZCj?Z+m5oxp@dpmb;!;<2xIK)*kTS>@7Nq zE)oIjIXX9*tj5YdKN$}Gw(?HgK)aJhWj*^j4>IYf3Swcw;h&b-8gXnDE%2c4vBqqs zY$l4_%wgkU?v;XNAy#97Ix9(7AHw|Vx^A!wrMp)#WsJ~+>g22N2FM4dY+<5_Vyi5f zBKBa8FJYqX;N~?Cz|^FocXR$BCHYli3A_$u3h2kuNUR)(4}5p-ua1w{Ttnn%4>Jb3JW{Y zc`*&dc>dxTb2Kev}Kby$CSDzLg#!-*VPHws?k?XT%)1Wp*#<0E#E%hwO*Q}&|C zX%`?t(VB$p*+SM3)dSlhTli9RFlWn}mF|N;FD9zvJFXE5+ma$$Hp|yI;;=_h7gI%4 z2kjnO+U@H8_Rr6kFI~gl2)+0|PZK~Cv2)ZClVg9@;p<8$!w8k(XW&ue~4s)oEnnK^iML1qnLd%D?)sXruLQZ2L(na@V~pMMO!B~ zG1vQdmoqqkN3-9H6uxyWGH!##w~vlqJsZ~0J|`$ZH+=?=-W&FAtH~Me_Tn*r)Na~G zdW%-V4ln~m`6u#D$c$J3xZ^@=|J-`L4k9`nJ`jJnW=OAp75Hu^cS~+%ZmmF6p@r`Al0FjKiQcqn0_;(Z9Vb z{HIsn?%1~UUq7HqP1-mCmVD#axt;#$@rhi(0U*p?r%4yuG@6q;S_SYxy8JHfg0_>& zp_FwT(!(sZe#5aC*iAS9Lb~LAbRAbPYX4?n{D{RHs{O>WSF}FnI?)o^SZBzG3W2QL zsHr)+^uH&fk8e^mEy_fjON3}Y3mhRVowmL3mo?$Yi)%VX+ae2hZxa{Kw8s{Ds}2VU zKn<4(=?yP*Ff~JA!vO2qnl_ruI@*{^xSZFMP7Zi@G_(tQj<&vl>}{R*b#>}-&qFyf z^&XV}{=A>4(49Y8C7Q0vxpvpKj%}a&%JVDhbQMe=BJNSiD;nU>XG>=Ud$eo9B@=7Q zy~inUvYBWwskIb|qBX@Uyn6kuhyJ1r$pm+yv#c-uF?~5K>1SbZ*ZpEP`2nCW?NKRM zyy79L2V<`0>5WPlbSp$lB0eO5GsYi{hI9LbJ^qdJIPa~<3;L{&$wK8FaQr~Cvxt6S9 z#T|Hs@wrBSCQcR<94x{?vk_RMql*S68@fR&)4@-s9`k|hAmr~}wgiU)-JNg#wtClK z4*Y~hC&lso=BhIDsuAqKJe|Zq{C;TB25O$5IVf^k4nf+WJzJ=p(^cqk%e>j1hvvJ1 z6w}cO7oTwiXi&6eqKpE6E)Msr3XCpOm5>S`)+{-}Rhz1+ilk?UwZR)2UpK5>iXJeF za&nDkTEoW+QecG5vtzSRl57jj)5uGf=KoI}KU|uV;Lx z>-Kz5FjZ9oJAAmp(Ij=@+QbY5ypt1)&u2|2`Kpnbo0~x zNhbL^S`+&^LCjP>=1-zCKDR;2ChIIH&35RSyrbO&+Pw;KCekT6!&^@v@eDd`T`uqklrqriZQ!^VDm0eiX^G0Yc9PhyT8NF?hvAFw5ERrZ*D!rmulYDGn9Y8`y! zhD)VnW+U@i?cUsW!>p6M;bAUDMqj&{6VO92cJ@foIA6GGNVrY|^c$&{aQ!gx1v&K-6 z^K)7Z*zLgcBray?;Qx!zj|PYt!0TU8eM}dL6rH7Jeqc;3D-mLNq1DGPNemsQbH8LcgwAsoVse!?cCW_jM7YY!Kn&K5;d}Ikzn&kxSM}R=8(yeG} z`ZVQp4DQ_&qPc>q_2xoHq%6(L)Ouo{Y2gX*b+UGx$3dxnemDhW>Ez0XOvw{0jKQYvkbm?{o zPSjC4YpP}*&<(u|swdr{V~~KfaM@u_nTF}^s`lte=Q*;=n1+jmw1#Ip=!V~-0L^On zdO!ts{mlmr%$CZreb@9f#B$7?1PzH7|AxAZW1>orDm7!g)8ghqP7IPMSLr?}KF84v zp~^fzECp_bl>v1n1gzB|btvcL#m_y#6^Do{zZ|u1B_Bs<%LxKM)oOwOTbA4~|J$p} z0Jt4l=M~~IXCN~DEaHXDDvOzb1EN&UgS@Dj`F5!nIH#cXtnR68Vp{qOJrupp8Y9-+jJ*ydF0Wk$q=RQ4GL z_4*rzg4S(s{0A`tdusc)Fb`|rmlH5peHy*vcTg+v&^f$Yut@jBl%6`WkH-X_jhHAb zlNKOTTQ1j&Kkab>uo4tBM>KV%u?~S&jd_fCw-iG-TnR~p{t#o6hN890i?n{zqVW9h zF;xKNJKGXAVL8KT&toq+wdZG>y+n1d@m z;l5i&eDtW6iMqOEc*#t1fUix}+L;2z@7@A#nZ#NBfVFHf+E^KaaRRc^;N;8n4o zHv67-GaGrnm}@uvxw5!Q2iqR<@+IgD_I?5Rp_%z`O(CSw2!8YqWKsViqO8!7$?|+y2DlnaN|XTary3cBJo^L1?Q> zG+0>B3UpO=s}E|E9t#AHwuIPs3=e@+zHX`)`UGB-8v)O`xBjSHOYNEFJe;X^2Ade$ zk;NOY4o}X*?$CMiBi`8P9i6N}F~NIEV*>^&dimgTQbZF00Nl>$1$oI;AYyvR-Pg_d znt|8J&JBT+?}83z1&@ER*LGm}csF@uFV{mG#iF(;bCpn+H5?k{e!$?CI;w+q0%=6k z^PZS%*QlF+P}rm^T(kN<2A;>CJii~Nh8RD*4dnY?%+q@tA3XbZ9Tq+t<;Y(7g|Qz# z;!!PMN*9J~w=42=Pp9@V;E3qmOJEn4{L?mM*sv{)onP3~K>k>z-(3j0ZS(tcKQidX z+V=?0m6468?~6TFplY;>JK-c!tA)t3wRiV(K#m}cLzzafRg=MkRt8mHE;x?J0R6 zlS%+oJk8~3#WjHaKHnc$x#o&Z$$;>=2wHDjabiD6wnc(7y4{>BYi%&}@4xtAr6oBo>zEBKrh`wU=K+wg{>~UEWe(iyl9s z9ra>GwbzWYCPI+mM+XqraH^z%vJaujbt8f92X>A(!Xo{63Hwf8)Q(m$Q?#qtPMHQc znU)YXQQWemi58-(UY!Fu-BtQk-2sxVUWB(kKAC_qph4Bhji^a|7B~*wNT%w`0ulgq zp};D*sEt{~o+_fAarqoNX#7+x>|{UzRm7k`ApMLOJ;bDDo}CIG2s4q|{qdOYM<5S2 zeG4IJLXFC<(FlY=ky2SZS(3ZPvNBS2hl4ojk@Qv1!o_ru-9e~z5urCM-l&w67SK^p z7FYZTy%tdHl8!%^$j^vu#;WcRw8lQC&y<5Ry=8}eUVMfNA*;hQWOaCrf@BlH*RkSd zm*4EtxN8?4D{yU3jbtGDsNYmo=ZB}BaqGphX32B=SXdpU%KEqKhy1$}efT-@Pg?Q3 zu*uFxbpOgYJE;0#{D_2s^(~aX2;x(??i6+zk^3_%5xe?6wMOHFKY%M>Qx}PrR zg8xs|Tla1_!^L4^l;z#hdacbl1N;zS9vy|umB>LxMf*X;`>JGyOqxGGr0)@D_CDd# zu6=FP&33RhXC7vC^j0oYg-*48ACa}Pz~B2q%vCIa58}*H@^7sZZ%QFMTG`C5(MNI} z$g>tQzjiy29J8#B)%Jpccn)=+6@0CBh((040C8sccXyCw+^go(Pw=ra5RC><@WSou z4@G`JyOyu45P7zHT#FE>Df;P`GP5PW***S##G?5QwR}j=HnwnHu!l2#g)G+VH^AkT zE}-BEgkz-qE7IKPd=5$xmp}>&K))k{fh3CXR7&~`C<-K_!G_jRFNkko%~fBjt|PA7 z7_a!XSyR!viLE)_esUAp!yKc6ii+qv;*78d6Dh$=W<*SIm$D~2Btt2qBOrjWxjr^B z={n)sA$}=Pv@1pea|%mbe`7&na9g*5tBTzM`rCKxN)(N8#*>TL>!?ec;_x7)x%rxH zn9a(3Hn);pH}x)GJoc)&`5`lea-cE3bRwV(qnzEw@;Igc>o2;5kj;^RjSz#$nPp%( z^dFSbr7v_LgWbApInmY}-@FhII&Jk4F@NA2t(4g9w>4<8Kre@FV3M;Fd-TOIF~PEA zg4oUn^Hs?$(jQ`>#WqwMOxM;GeJtW~mBy_oxp@(v5! zmF=;U&RPjRo>i^K$W4el&F28Nw$+{Q@3XZVVrxmQLaq3Nmdwq#xG?l!Hfj5GeB>eZI8Gq&|@5dK^aU_U&fso z)Rk{GZV|fASLDJxCWYMC+{Q#y;O>RACkwrfuXFe|5~~(f_5&(75OJTt6S;d z{HLScQA>X%jycA8T98Y>E-1F-LUEr5%ZU)-0J^141-D!;9aFA9mv_EtSLzr0QP8X6 z?XNcw#`5wERK#?-o&pjvY67LZfgWkkh#m_9HCoO>tUS9cF<0O-1Bz+P78H4-sTesw zRH3g{6h#CU8_m?V*wvVvU}UQHdnkB^Gk63869lk^+R0s2A+b(Mwn^&2jR zaJRYiEK-JEwTJC@^P$tZijnWlUKnKpgImexi%EAB;%ilHXtld^VJ|x~lzlX`k0`U4 z$d`!-e0&z8GbO)Z>>lLy!%URSg8%#fUw<`SizmP(gsO!7!U@T|NHC^|_lkVbXsn0t zN!f?Z7#zaKMG{?$1NwC2NKi1;OJE!ZnBlUuWsqTVsF92`SDaalJW;-jjb26i&#&_9 zsvVqaq1f-G)|X8ZJ*LUbIiAO~m%g~JM43{zF6V65oYmXCStQQbo*PATj^^46V2CMs^7`7Nk15Mc@zuIi~Oy9`aY@K1thk;g8Guyi<)HcB$F?%e-JOx z=Z2cfLK4HBwy}jDcwqAz45&TNFujClseOoMN;LhB9w-~J!l5>-)d({@Ka|E~-fpXfP|7&rdVbVp^vYE*;8$%IMiDU;97b2AM!474h((C9HX z#a1=ADVqnSJbD)V(7!ZnoeOC3xHu_tgT*&^v*fN+$meUnk(VQc@!7Wuqt>+l|LN@6SW`H6A<43K27XIcptAB@ea3)9mo7t2X0(XzV;D9vGt zs`ls^(d!2y|JnD}D-2!2p@cLu*d0fD=Uwdgp_i*FW+)|;HPA{Pj}^&j2_vxE@+i&K z$sv4UO~loOrsH>`e3+B_ITWZnroBJ*I}l`EAp9E@tiQ+@6F|_T=_gT)C^{?Kk++a$ z$EqhRdQdl*0-a+xj$aB$WXwid1-OX@HIb8V{KpnRtw_dCfigm?3fDX3_`;xyd9`?)# z9VJZ=KJhnh(2i@2K)Ev65h)Epe?W^pSNc?~3?05qFHRj)y(&W&4Ffz)ll?D`>T6CHVWCeK`EV|+zAjC|UlMxZS#($U z4&%)4m!h82MRk6{tgFIjy+zqd%y*QeRvPwQ7&P@HvRA99)K3mFnEnqih$4bI6P$A{ z4^)UE5b`FwU*U>hDhMwC$BqbWcPV;Rj6dkjG@-My{R7a^4g&>3pSlCG7v2R6qKlRS z^70S@iTRmFV9B*S8zMekU7$h?Ykp)S)7l*zl1#y>u1m`nBJf~PdQcQPuMsXUZ3dDrAteqI`+pjV1tNH^=m;Z@4DPy7c=?M;VCV&;$AP z3tB7#s0RN^V}R&^&5*A4_ZaME1FeFV%>~MFDQN#HCRPgBta0g-k34f>w!&k}cHEa< z&o7=pBC6Q3=yhl=-3r0cOPG3<07Q1R3!Rm;FuVC-Ru ziG3z(I}Ugcuq*(3uU(!M5Z#|bLjv%!g9-F&R7JE?Og1jiC%v#d8-N7d}n8FQAL9Ux)qfOs+H%hEMx|1rCM zmjl}d*Fx^#U6Mu=zXyU9$1PHYWG1kOfeq>=tt=kQ)x zj5Enzt%i6qVkxJv?t2Nmy={?$;GGUm^?2#kf! z1Hq5=x@m-s->khWJy+ZOn{^iI<`C-kFbHbw{YYe%5w1OFg_KWNqZ z-MW&_VQBWW{~}$#bY()tp72aw@!7oHPJ=;@-6D#Ckwpp0D%wkOn{O5Il1ec z6Bb-~cotC5#rPW2^L_OQTV_4!yEJ(jTK7d0>uZc^R8+%=gQ~Gl@S~^dJwfk!*)(Ep z4_ehJ=X=6IQd9YhL-&D?v(h*xBWQA)({ml~yLQFX=A5TgHgnZG%Xxd=w7l5rw2oA^ zMur@VaFNci4l{abX!fslclnkEl;W+1VI&i@Q$2XHSR)6cQiwb+KA1mdV!shFgDxli zm2J69Df#|OUY8?zM`BdXa$y~F_`OSud8tHo28SiRY47e@*w+VZbWpD7I%uZkv(y4F z$*T0E!v1@c&64&ragyS@8TX}{=B{uNHX}Su-(Hm5#8@vP)e7_gb(uNWw+e&sl!2t& zbaed2!d6-|+SuIkH8#W_*a2sz0Oo;L2KzMza;!E^L7Q)AXto zm-5D^t$~m7`N8YFGQI)fN1&uLiLYJ5i+B0(y!r*G6f|SSY9*3(U*?K4tqP{7LNr)B zlry;{9>;$zgkrfR!0M{|CGyaa8q{jgD*;*c5!%Lpm?W6p;~*cyg)+;l6~=!( z7`#=eOp@<>SJE>7?Il?6%FLNa7j8@wt(aqte41iweZB#)=x{f}z9qI+4z75ttJ42z zVoI2F1Qykd{7~siL!n2`nxkdAzVp0ub(_2d7T?p)e!?UBAU|so5okZxXi#d8#csMo zV=WqKtVI7_pd?4N3%V_Gey~Nm38qvv+=_@6Be64ICqDmVP`Wmu6BU$gUtp%G&Mr`@ zX-isoQ_h4cvll$lBHrFR-Y-s6tp7*PCc5_AunKD}mZ?;Z7H_Ss!d5j9Z*Bjxq@IZ^ zsdtj5th;8pB+@AU%X=q&=0ebEL6ZPcjYvl``S%j{8r%ZwB#@RYv&~KU1V*64Q~Y8B zxl#T3m_b}sTvQ=$fk(=2eWfx+kOgW2+MIym=t9Xyh=we4iFls1rtF0!u0Bu~*9nNk zfVAnZ=g<~f8|Wdc`t2NjC}=$wEY(33d`cz5u|ynORbUozQSviNO(s?cVs~i@l^lH4 zl7X0ARS>fa^#9mhje@FjJ3NdD?)djtD`)qs6}rysbusn|~QdHmhoc zAmBn(kBi38g=bPMQ0L^>GxVVM<-rw{#P?@BOB?Hpe`>JfoIk4{ymH2+CE9o-s}3To zPSTHZ#L%qnTsz&&!<4CfF-t$;g|p&XyDmd0?=)R=lPcs6%8FsuWMn;I+{@&21aqXE z95Oa}-8dxG&Rudi?Z|lCM%7tLJj1Fq`WW)W!6hYGUk3v*;%mr`*ZN{+PkIWpF*!U* z<>d%4wa%nh7_Da05KoYiSt6B0^+_ed<(ISu0{2Ja8?oXc`Xzs6l`}>T%nGocW9>~!CW_Pf)nq0=W)3BNgR|6J2KWnW zVk7Vb-UU0w!apW3WAETe|H+V#DpCST{=Ev|)^Qnzg9MgtZ!#&@60r$cZObyfp z^8=B}7&hGXz^$RR3vPU>h$mNra|_?V2UYn&V>UAExJTeJ>W9eV)HUj;DChr0uGpC$ zEtlC<`Lqu0eDp!umaE&QJwO~tsu|57opz}DlavxaEzS1L1##Wqfb4QAbvn$y?TZ-I z+hWGRn%n3ucxnXpu?fvuS3R)iN{^Suj!)6qT;->NMOIV`m)4}PG}C9<+is>~VI zyd3ig9L2Q-Q-j}z#C-xoE;9|q)@KA2m?td1AKTqnnV2EFgM^0#2vRmPCH+Q;{yK@N z>Yrw`3k{_xGRXoJQkr9*nw7(3p9>+mv?a`Q`v9Pd7@ry*E@AKQ-UM+n2i~%&R6YA# zJ>|9%w+WMDP!mw8K{No@P0U{FeDau67c^7$JC02BmO>i?-Wl~PolM<7?}x@!*m1{U zMn-V;+2Tpse!#XQamvj;1~?S~YEu|VhB|xnuu05X@@g~Oc7_KOj55hIfuL{A(j^2= zrEQ?Kf18n~C=yC|yj-8T8#5qoIxxgd_j&?LTn=&5Y221U0w|wbCi_-g*Q2gan@kr~ zHQJ3*je+KzmRd;uHzY)M%o?8BWg-%PEH(bx`eokYFHE1cyV;@si{)FM2YCydzxoNc zl3|MyWEAf-E(2@5z^^}yl82Oa@(tnJ z%dr+!B{N$d&60ksm78;ZG!D7G^ho0aQw&BQhfY~P zoc@Rfv;uNmViCj`{+ReqyL@<~#^W%q^x4nqdQXD88dAgo^BXW;&3m%5MP?)u@WWoS zpk-!1GH5?%1APiythvv}K7oz3@Ow#Q3kRj}d);IUXh?zQr({wr3ZPy|xES%|Let$( z@Z1H^IT<+NT-9a012i#;EMumW_|mj^mNTZ|6r1&SHoi3^3%1CCn4(ZPFJMLo3n2q| z->`(bCa=Cn{T*sn666lsL^P}J^*rKZ%P=KB)f4@+hw{2JpqvwSP=1k74K7db_Id;U zP~TfrhvS-&LgWJEJvs`bs~odL0ZEZ)TTT}T{7<5eVVS`l2A)W6H(H}gUdkeK>7kRO z;UFn-R591N&wYRnGiscAfI)9!7y`h;I%f_!XWY@oCQ7v99rg{mn?L~KE@H|aX%EYF zS3MAZ_31AX(CVLQqwRn5eD}b3=tABFJ~PL^S#+o`A&e{q${9m5H#8w+kqr{Oi1ku z#5&9Gg4x3gCu^t9okNRP!dS_$>h|WCoOT;Lo|bVTUui z$?u?)n`}b_jIv*Z-M#r|cbCV=8>xudZaS_*Cr}Dx2ZqKvCBq z6qss#$!#l1gQF!c+rePXP@}vJ-uxk+%v2-7z9knM2mR||>jo#%CA?Ms=?n4KwCEa% zZ<|c-Jp)5tlDdp0@h&e#*J!cd3};XqALc-YlN6R2DfsQNUy>i~Sd(a1Hyb`}r_%lM zWs$tsZI?)pOOr7doqya1PWohTRJ1#%5`(6}NXsGSOpdpf=|fRgC^oF8lZmZMovE0C zf}At}w28#lrvEDK)P4Zc6nZyDKS-;WmHdAds18mI%DBlyf=sHVI+_m$MUbgLEPL{* z6XAH;)bz@Z1U;VNQ zb45NXB9VBsq=@)GAfzA}93XP=P%KRf4V`r5x3+wJxL zy)VEGp&$n-(Dl`{0y<6sj}Q}(qGT@43VZV&F4vCK1AX24;xvSNeQ|&*htKu%?*#ro zw1cN5?1M9@Y42`Y3o2gvgqvznM_bO^W*D_Kf+e4%kX57+M86vV0!1}?-)p&_{9 zhQ-va<8l4LTvqK_dIO;iDj@El`eSyp&Mp>z`punz|1@QbQeRIoh?VUuu%YPvOxv7 zo+r$50IY7iZKwgRl1axbfGBR+d&5biDCj6<*qoIVT|daJK_OH!xK%|%u(t%bsuGl3 zj2(gCB6OqmxcsB_Hdza|{#<58In@%EdIRc@!jBMYGQ*r|%V<<}P)$9B5y60wYyY6> zFuhT=xQUaq+6!(W=ustFY{%b`|ChSbdujisr`AGWqU|zoiL;M8v^GW|rj;&%eXb-c$i1z(>@p#C_2&5MJQi zZ$4YW=2p42FwQF$TcW`BcwJ{`5xPjY9iurOfS24lftkxAO)a2fd<}jF#A8on`blpc zf&n@9b20qvx^Tbih&_rQjh=T@!U|Zn@&0p`M%p2wwA^1cI>4b67;B~jbEMswIWh)i z=sh#_yw-2%BT#H#%`-`~0Ha&29-Pn^F2IqJ6BBX2C>L`T;KurEpxBG^T-JYvR|8)U zt8vnu0^Y)i!rcfRx{iei1P_M}5U;F+)j|LGp-dJ&ulCesP7k2i^XIR-BMhxp2Up}@ zRU&_sW#pjz5)n%``R`WgUj!pU9wYqE}+DuJ9{IBe>u4-1YO_lJM>+{%qU-?H{{K4_G14EEXhOwA2T0?i)z~Ua& zl}D=z(f0LVVp2n@zmXAE*J}a262T*BZCxp%|BBRnQ5lJ(zlqOiiz%Y-`PPEv?mx$6 zOH-XqH@vM#%0fxR%Zdu~<&mJY+G`-m0SWvb_b&CAlT{?18kolieHITJg+0T-3FG}O zQ{&g_qm)q^tQaU`3@u!Dvv~Pi{F6Wjohz%KjHrp^*j}MYrkBK5G8zfCJv*}ik+JZ> zH5+){B46Q8e5{ET@22u4`w&H4-yEPNs<9WxRH`P4TrCDLx$sc|8|4j49zF==jAI|1 zcJIEzDz;09tl3iREC{H6;8#TkT?x?(U^w_ju$b)Oy@IB_4oq$C8gtFMPo;> z-J#o%2ob$STt_&%2K!jwhAkbMM3f0bJ+42BMmOG6g~o0-XXbv{P89n;8s;fv%j5H) zxxk?Urr0Td($T4I8s5Q*3C2zn64kgPgLL^ONFE{wPSpo$a2jbr`@5F%*pw;@v`UhR z`Feu+F1I~Bbp@Ol-96*@FG=rihHs_pn+QSNeKL^TQDlZj?ASh$=DyM{hI{C@39DUh ztg%odMQC5JX={{IjRO@;>jp$Ixo>4jb#^AlphKkaov*b?VXj0;b&mZciYR{%11!=p zk=7D5NzI*kN6oR`eqP!Jwv3r$DF6>@!!pmXmHgXSh6d`^L(^Nlq~asvbllTye4|0W zSU%Mj9PBUhGmUyd@OKG9O{~#EP5n_qO&V-BE6O_iwMHo;=FzZJznJ?ZNr@&P9g>ia z8AcM=yJ#U}E)yj%E^7s?0$AnAXk3+}#8M<_I9AQF3S4NZ27X6@KG#+|u;fW9GIz>a4Xv`loKW@E`yh9s*>%>+dULnV0!=7keyLG8 zvfvYlJ^QUGa@MW6^Q_z}PZA?@5$Dv5^;d5PUH6Ocefca}fX8zsL6YJs$jV3(m7ud_ zSFCUq+tvSXyY4sMpOe+FAH6zz@Pw2^ZzC?bI$30d1tvHv?! ziY!HLzmJ!II3P%N9o_glJQPH9RtFKCHKhH8h|awJ6P?-SmLmkJbp5gRuno5TGu`Fk z$G@PPTx3{sR5L#?-2kj!vEF$Ta!b7UDhaOti}YF(*0ud+dbu|03K+YWltNk?lKl7k z2`b0XS63AZeI>P!Op6*#V@x*k35NVgEjmYXo)jb%j-QJZCfYgq8z3#1hjD08VCZOL zkM+&C_MX~=W}M_7?jQ2t#Q^F_D5hrJEc3sIZR6#od!i{Y?q4RL=pG$=tw=EOE!a(l zjZ<_8f6>cfhH%OR0+Cp{Q#R?iMXjupTWvK-|K!HDhKzCUEN$uU3J~h(= zRLxD^923`Deav>iia5#rboezr;IFBr?|n))!{H1R&&b^vE&#qn zC-#8Z9pm6GCNCq~_N;k^#!6`b2Ef0$909@vTq^Ga_DlFNQ~?{=BL?_GAj9>npD9QDkc#yvqI?8RB_O0sR3&Fxo)^pc^Mr!$ijb5T< zKroSJh+jS)l>|CsZ;$y1QGVh+2$DF)?zabfy|fd=v|w*RW_ z5&8CpI?ZSrPkzMUfdNflp%sy8Mq}4s&A{FZAwWAaobE5e;8;mKb80knNjv2=;!g5z zS*s6j+GZ3GaEzFcX7XDY+CT_ZGxrEu9!&DHa;v<@GU0~|M`m)NQz~>vW+$y11zvex zFkfzVh9A@S(0vzOG0{#^l)ql$Cl;UTrb1RfW1Y(;1oJURTItet?}&f}R$u~0>4v9O z-q_TKAx*uFPNye7^B<42%12kSc1ELX`c(JqW!V#<E6 z<=Bh46y0l!KHt7CfB%h6|I|B)U$yatUEvW+Q09zk>#R2;icZ{DS}7#~cLD29Y65r* zi#=UK4Y@+rBJgxIuE0xqFvUY|>PT}UQbSW*c`+qg?)$#xBtN*}Ghh*cIf|{=>hn?o zqMO|i3pD3x9?~jwEGa0CYf(2g7SsIKH#Aw)p{FX+IN}AEJ~yUSWAYW4_`jCjUor#N!)+*X2JS3=Xpc4eV^r~yMF#wk zUcv$?%~V>$vS8yORVR$8hjTolN)(&napRN1UPHrnY zT$79XGRF;UJ%+Zynm2c39lf)-0g&seSpr;(L*!Tjmh`AES2pH9Mz6;Yy zi&a^!ls|m==S*%Pa)~XQ?vMk*C-wi>9CM9|`~I;yIh`No z|E{&}-`vz{qvm!Y27R>u5a6Dh%XY;Nbkfp+3RkTVNZxE_5jHg1W8c@Y1-WhSz9wUf zD-_}H)o-zku&gr7pk*3KRh!<$l+`3v0MWqNmBfan0U%ng| z=!L6RqZ9tSEjy(i%l{)v!_Pf`m4EJuuC?ME#997Oc2#NRPryde8N~e$mxsIDTi-gL z=B-mBbR|~tjrsJJ(6JZ^3e=<#>Uc+`g|L&!084J&$|xYiOZr9X{n$?{qK8B**#H}d zQaE26GQaNJ`|C11G*lapA=1=!h%~iRBFGLdoNb95x4yy-GQXH1^9$ZhlSWHMQ>IaN zBhYTv1*zL!PaeMU+?v)@Pm9(*RbSikX`37pZ4mn}+MwMIr3GScn}h_<*Yyz${&6MO zXb({>`?w9DRP~IN_MBRo3XFyLDd4W^r-gTwn(F=Z;GROdXbgN z_S%k&t4LuElLozomA~w-fc7UyKOGj0{__t*#?T0c49ot>+aL)qxamR5hXUJPCkZdG z{qS1~eg|Zj86z!Yz(v_jRZjJPrOUFLB~VWYs+&fV*L%V@+~-b9rgyNpcviBxj`0t3 zN4<)5xEQ=<6a3%+r_lVe4sn8R^Wf64b{qd$kqT*GQwra6H=520^we zC-LjS7nE(Mb=Tv>_kgDwzV~&jZ*LbfgoO8$#g(3Nz4MWSWIWu&;U`^S{{ufYmnN5@ z(y{8Fuq?DeOd0&imwIM4(9_R2bS!EK6piUwSlWm3O2RGNYEIQD+DsS%y-@PCp^R50 zg04jA}yj)W;0%Ng(a3`LsX_{$Z|#1e7bSDMpmik?50r-Frq9=51uv zDhzND?kn6=86CK239gEM+Z^@oZk`$Sg?Q(i#r6K4AIuq!Ht&jVApoEv1bFj?#{t&esaNRqKZ+2h$B3R5^)OEC^SBo#0SzrlVxcc36W{2nJ&V(adZ`J0{ zFihYX(&`2e9#XXG)BejWXeZO69Sc0d)zp`a%$kk&@_jSbOJE`SY-4*!KpeJ#%Z5do zQNMLtR%PMmg9Qpuz#n(+3;Q!CQZlOG!qmr9LZCZ^cRkW0O!Ay2jreqlW1gPoLN?$i?n*s<)Ig!Jj!+RL2o2H%Yp?IvZZbu2BRHkk{e zR|71La(`C?wnlXTbqeZIGJ=WKjVPlE8ow8CLF*iGc0{(mAxUn3MSGy*DyQlA+L2Z) ztBjy=QTn8N`nCn3qP34Y1L3Z;d-B-*7)^W6zmuCnbzs%l@N6UL0_rX|5Oe{TuuRa+ z*hXQ`G^Qs6a20;3ym>_6=8E(npcCYxVIddINsOu4#TQb`RF`6zdk~QWJ%tftnVSz{ zKvc?m!K^WYtJuTF9fF*Lzyl}dR$ZA5%=khc&suJXN4nI4nqXWF5Sfr>8C9}keI-v| zJzI9*;cn-1?9Yfo$pMw$(berID&5!p$|Gr#Rl-j z-NqF!%>|+XvKPFvT=Rr0m)V${{G44=UOqdXzYb3xbzMP7y61N}H)D11UAunEk;~X& z{M6=eu^o*VTW)$@^e4H$p5+Bc1(e@?827K3s`Oe#eoFVCPNkZEyKb~X!JY&4uIeUE zc%^gu%YHhlo6~WppIlRSU!5e*gp;b2A53Iu6`3R+QL0km3?rjZpVy@-q#c!;xYwJ; zS?66(W^BdSsKLn(hXg*6I5-}>sJ?q8?7PS>gGU8nmz&sux` z7EJj?m`$?$8HXY8REJ`yhKDG;y7@Dpx?M&oDl9nujAHWwJ02P?rc>43MSqYr?Y0>x z*<{}YMF=Pm_w`PZ8=iWT$1is7BzQMb!_c{Dathm6*P_<8+0=Hxtm?7T836uDRu5(U zW()bN;VMa9mRzO|4z4LVq%+kf2cch#1cV?D>1DRb4@9@atcs{0vPzvbf;BH%BV}X> z2NQJdOHX;qE{H*Am|FK`xmdHCUzSxx)6-v>+gUp^n#x8=vNSigrqvY<@r%ovroi>= zPdxzz$$JO4fxB+tqa63V7%;)IOdkyyE*cDn8_t9!h15jXPJpX6$&*+o)o4aOC#@#Q zEV~(Kvg{1ROlSd@iWem&%XKs}OU&#w7M+v{S%x zv&6t4eUN_H`E_9pdz(}OBj95XnNdtc&ou7E~UdUc}aXV-otTtWGEB0Q~O*xD9#9}?o zz&B8gr~rx)#U@3E9C#qg5H?bD9ksp?FOAycX?no&gZu#?tbW@Dy z>2rP~(?XMI2-`|r4>U5&ABAWHtYF%|GjR`Sum`v)%mX%lMnN3>h=Z=GwnIpTWs0H* zM8I4FFyS2JsG`;TZT^{vDY0P_R<^(}waoX8Xg(Ua5)0uZC+BF|HyVO}U{J_V`PiKp zvcI3|ePKN$^+He_r5BQV^r;3P2s>$0eIBsW`3LF-NqSiDD0UWK-3qc&nmR`{NS!I* z4-$hJY{f#@<6Kp!a@d$I@96D4UNbkM1W zENKb13uU;(6C0xs6rygRnd+}eQ>hp*M2Ov=?)?&jR%A1RbQWCH;E zu$O5p6QHbV-e)-y9V3~XUU#aCk!t{eHk@YUuZg|)=B_+G4ri<}Af73CcNv*+Jvh-0 z94h0z{5bo#>-uEFq{+%|>bK?_Jsp_|^XY=vOCe)EF2{cJPIU6!#A`5@*5|svPB|!4 z+O?@$`f>6v-2_kXSIznE@CZIN`Aea98gjIwA{(Xkp0IRJdmwUGsH})_JUl<3q}>^p z-7S~yJ28C(?@e(Qy$JWt{#7w`MW*2t|4p%1f0gaJJO3NP-}q1A&MZJAfOYEF@kB7l z#Ydj?8??3jik%v5cGh1+fVOL(=}%#480{O=_An0Nv_6VxU{yHaOc)xomdrLT%V;2r7~9dPqu= zRP~b{qDpL>hLCXsjv!kR<(Ap;o!e<~VS1VK6Q|1N_IQeyBF3CR%jK4Fj}tNgTHEq#mhr zKWQSgRss85GJ$FM|cMN6YdRhK5blDPp<*FMkg{U1Wze*tyojlBK4 z=hVY*AmUFIn?72M-w*eFLB}gSQ$VUUty^cjD>DB<8}wwl9f~Hqc0Rjk_5$@{xjn&T z8+5N-TZ$_($l7oPUKbJvwCFlMVr7woQ<+(YOd`@#j3rjRJ?!EbC<$O_<*BigzHMYJ&E6P^b zJ>Q(|06%B!w6FwXP_80>Aww|w70kY3;%(`I576^tN0gQfocoB$YLfO2P9L(*?%1-< zv<3eo1+_kySwhKfHZb@a<^E9^n2{ZL5*JNEcc4#BW!Vj7H{Pu$je^qcVmNTNqcCEj z5P)c`4yg!X&g-I;y?4qt(Kb$E)lVj*`E_3Z1xR`1^{ciT^>EZwh;t*;R48)0FLi;u zZK%#FI@QqPE?n3vj9hdcwf&h#@CQGVMG}q94?W>$s$Y!QUvg*N0yk!%5J#H0&TsF? zTyyp^fy7UD8IO5QC!_(zOC~$KCZauNlvzy z?uvc=TEk708#|+cl0^G3GOZff*7K?Bvs5$4Xn>5kxjW6DK_rwHT>>mVEt)@=jsgfq zvEHfDU``kv>1u3Y>y-q($vuo(U^tX-@Bk6eQAB)%Cs4XrVhVK{9UGz2KX40#0Ye}E zu`V05JO=93JXDqPFN!Bv)~ch(>qUd$%!V*!O~X}Ehmk&MVhGE$@OXU^xhQ-J`6vX0 zyf3_fHr3={2+y#sPC~CU^~|y>8{_1CPeJcB)|`=pWn{DK`|OECi>-K4J0XJ(K=WT8 zC2x9O21drwcR4$U#>ae7)&0E>+0u(tgMGlz-gPqym=DJGqU^15Uy)~8g4k7fN`l#e z@HE&D`zeSlC{K=>;yu(A!QInNYpLOsfkGiSModQ^B6TI)c;Ak*nmcXxqQCSY&h%`W zS6WvraD%vR#z=h@cJ_tonkj`*0HwbIL@_Q6h#B_J55ioW2ofUrsHzdG!P z)5k`ozuqxlaL;0MPPK$J-Gmd6s5Sa*BO^r|nXM%QLqSBr##HFCfS(E$y1U+#ih@L5 zX5zhm&EqFlD)Tx3w5Tbx}uWfKsVGsrG;}UM>vWpOS>yuN1wg)4S>}RpC zfwr0;SSH&kfT$!~aV41rfZ>FIfsRraAXeji(6(|`k}dVJ?KA(x0N7(-oVgV3*3GBm ztrzb=*6OKy#e(Q@;u*;N5v~!aw+WO%AtHkbM5`Vtg~OuK{{bNx;A0*98Zo>2EJyjCuC1wJB zklU%dOA1uAwacGUO+XVnPXjN|qilBE!7{)6>Qk}%VT#m!Bag4CYTEzE>19x1|Ma2Q z;|YipboE?E<nO%cF=rT zQfa~X9eTDLYElsS~K2wi%D) z$;fz+dHo+$HW+zq0VFjBkT$O*6IY0sVAT}(-HcK2AsXzU3s)>3B=xp)?=rpjdKgRI z$_Qs($U>J(H*wz8&`6>>E+(Flec6X3;YunNS#Ut%16`MxuuefeMU{Tjl}T8UJAEOr zd#}(?P|Duc)=z31ni~ zHZAH)f78-Ia(}Eg>EUajX;kR)74+~`wK*P9qz=>A(*(JWpLlmTPRDV&3+3G6FwPOT z{B*z=z#9xrk$Dib1*IVo1S6cBB-qZWg#ru{FXM*rmM_mNy$qc_%X{x_>O6`Gk&V8# zex4mxBk4?x?q1MfKie>zFvII^>Ni`8 zyAJ%2Z5BOnW7Aa?@Fii&y9hNh5OAlqR8a}Z{HhF4UZ`YGvo-n!mr|k)vg&X}*kSqrj1j2cf#sN)~Lg^YFU2v1lDlnAD8kT#_V#3P1oo0B!vL$fgS z#p5$7Ij62h74!=#Ve3_xUDKzPSg^Q2q08>q=c%UH7viJ-sz5}Uq?`-fK=fDs#dpq* z%u7C*@cNxRG)c`GeR zVE*ADh=x=j`vZA!C46aZ;?8{ryNn^>9ZFx=N07K#Or#=s_Cukurajli^}*!=HeeSW zu!q#Xh=^Zp6}cDFWMkme4tnq*pwJpT0=cdDJB?D%$|@D7Y&0y6@g`$2EhNu#mCbfk1%% zi(gz2(4yG&XSX06_ii|>F8-4O(frT23+I;Jn^@gE1%Zv6~{0~E2bQ#yww8Z)!qGjzxp6nrc9LDDma z&nm)UL_+0G7Qi{#^={gpVhF#)G}%+H~4`^T*)DzvOowFLRF$!Cx4h z9sD;vVQW)8FFX|IA$E8wEYpdSqyyjyvkK}|UDQjT z!+S6qz5xSB4{HHDZgL>9m_G&B6K5hLmqCoS^&#PZwj z)ed2W=1J9d+f;`;=Iq4Pp%cPr6 zW-1D@#@TLFfb5VB8O%-;oW%f^o=!MsPU4ls_csL;rtfdGv=bZ&?vJiC1o6?Z#FDi8*#YZ+gKo`ZT*2BjS$KZ&yRq zMd(tES&<}LKhP~IObSz-q*p2ZtE0-lGkNXj;3@oz$R6Nf8WMWDMJF6##k@l?j!tXY z_@WxguLp}lAgH32IRK_>wd3Eh6orfB_m~S1aJ>Gqq@1J@o|a}aSq(h;_J6+g5Ti!F zZ64$c<4{DPUc+R<#;4^@lVbcX1 z^?jv#Sp?Rhc=tQQn}dRiKfhJZ zbzOMw>1>Rs%n~>JiZ)%eu_vgL4LjFYtBH_Qkl&I-^nJorYR+cW8eMFN6-rWG4#kWg8zMqbByT9HlIG?{?dISaiKE86lR|D<`3;`Yf z_ft<#TZG5~mgj!og&%mLH)r-hr|z!PQjo7(DtpqwUoJXsbwwzgUNK|-CUjvGYpDP zp9$ds>h3n0@ejI0l49$CsV5te)p;z*skW7FI|6#$?-M~e{Sv3XT`ngz_M63XH1)#X zOVy6pj0r2TMH*SVHUgWqTGV3_W-S{rClX9jpDGklV?}TjN28583}_XwyE?}53|VDW zf4IB{2Q}ogHq8JgRd(*vD=wsqq&P^_&e|xOWqja5=v*zq#W2XwuTJ#@QZ50M?dB%Y zS?m$iKhHA=7mH_0Cd^#4(l)S%2Y)d$@=2vehv;Nte3-C=>{8%NH16)xfKv=xm5sZM zMvdVTJF1k1ro&f#l;bVb{6JDserr%ElqgYoAB6Z@96JV($tJ2uJY(b{mN$2j#ar5~ zVC$s`5vmGts1!AruM9V%gj(D`n0Df|pLR~jGlPQ1f~4(T!28AK=O}LLL7LuhK=I?- z_CV@Tp=~bkS`PjaD#(M(nlsDeAlMZ)kOG}Y&uJwnwgdFCcgsiLxkVotUR-y4Sb>xQ zeTJAJNf8B*7D*W~F{nCt2!>>P{99Se<|rMDZ9v69@fPLeGuu6{#PkW(*@$+k)FP zA^_=O;~g#LWV4S_bwzA0bs^iT!nfw^g1Rn4h;fS?z)FmC0^#41;{^9qgGS3THKd&qQW77eoe+xiSaNe|> z5-W#yeEH>WbyJ-gv2Maiab8;IOPKd2m2!E;qqeAosQFm_ept8TePyuA+Uh$rE4HA& z1-Q(;f)}d6(>rrc;AkDMu&ng!xmUwfU=P{a?AixYc5hi9@4_0^PrCerdxBCue&C}~ zaVPV`&r_}jKlW^?+Va?@y%-?*t7Gr+kAO@A6(PFk`4Vg8lZ^$+_G`(AXM&F3vvvbY zNnRKp%~j8B^zNN()N-|^u}Toe;x7wMPxVoiDJ6Cqm8~kcsPd+=g)Kw-Ruh(hA)bF% z^9#pabscCo-V@a2zpo6Stw7PO#`E5cXP5Jb%g%5#=^D?jG;~Lf*|Pv!%9NGwsc~<6 z7dg6?ZChS%F%KS|!dE_rRoIc*29}^l?oe{=qDs%OYF;~41$lWp>`TY$wqr>~b8B7$ zL--5^pxEE~HVHM#{s!lWk07B?7H^<%O`&)?75bOR$u%v?AZK%^OYXMx52ARCwL<(`fENE3391Hy2Y!8!?~LU)C&reo15SqyMslQF?+) zPgC)~&I^|6#~82vP1>?e+lJ-TxULLzZFKZvm$hE)sQRR6-9@{1DjaO0PoQq?hpk{u z3~f*h)NrQyMv}#}XkCpZtVJ!p8vesMUn*DywA?&d=VdwY67&E}l(zl(R0WMHu1?F2 z(VuKPZPd>g@;rWv3x3w^gkK0w1c;s+?jSza9t0Sa)r~o5=4akt<%!QwEcLOQ&332& zlX&6UNA2Ye553JrMhe)cHu-e~Q3c|rKtny5&FT@XB%Y&9_N ze9_h}2oM%Wj$OP=nQ4u>gYi`71!U(ob>ujI;a?ZzbzBx~I&3<81Si}9zZ15{!Jqs~ zR-+H~?rlJ=pcr6yx5E_ulk4tFv9O%}31Fd(=<1P-h2rR|)`H)H-viTaXpels4O{3T z3XR8}(eb8>pcp~{*eLJXYTfd}%rAMLs; zZ3AkKec%@#Z2-4!&)RLe#`lI3dL}!8zKED(ng9#FmY!F)nA7cU9FO@W%pR7yZ^!h~ zjm91w3LeZV!HTlajrTHID6jW#zEYn_|K~_=cZAP~m9Gfvp5|)?i|gmqvC7Xt_);OPV39|cAkiCwM0amxwN?1ci&*Iy>LPNA)t`F9 z;J1X1nT)D4p~B|X%luqa%S$8;OT-i8a6y8v0H;_vkoC8oK!)^qRy!-vjpJu)A!BLkW{`%eeo8dAHA zql{7@a<>Pv-#0<(f%auzvJPfsgz5_IFl$E5=Kckg&kitepCcg~FFpU=Pvrvm2K9)L zH%dHCoqV|l%~-yn;UMK|KO~hK>X^BX@lf^TqiO8QP2%9a5txGTV9zYs-i``deqT}ZIkmc?S0*(4IuLpSA(G2 z;+k4dbJK&Q%nBhepV#Pysp`Y5MS42yG5ihC02>jWH>$x#ZtqRXH3W>M{!`2?d}isD0Qh)Gg94I z)au|O6T2-%3=|7eUGsBy@Qs=)kekw#J**%>rTF)(Bj4R7AQL>BLbmFjWYAFfF_zlyS?0?nISK z8PAK|(>^!KA5$hnkax7adz4PderkoH`|B^pmU~C;9$STikvqo?f8A4oX0Go6 zslFv@-`^Akl2h1JRl%%MucerOIi44fyf}|1tIJg!jwcO{m*9UPGYDoBx+_ZyJ%Z!PCgtP|8zRBnC zT>NFAr>PpY-;O12J;Q*v1O)6mDj3Zz3ap8jyq5#89&-t_N~la@>3hm+od7TR|fGe9&E$2E3}&dC)(wy^s+9h<`C} zDukb3l{2Kopr!AvzfW%K*@#+Z$pBNG1zN1qp8ZMccBa;zc zoi+jEJ+@yhukC%a!*ZwiQHIF;hrK*T_jNX=fR*^0KYce%6;P zCH1Uzihj*{1(^)?X!8yacZ^w`M1C}JMlpbvk;2su=il1)k#kB(615>U-M{aovd3~x zSR*`~gtz_;Fc4sW&8&p@TcfoRO{OLDuY@?KMpBtpK)-7%nV1BXpIF{TvZRYOFNS>5 zsWGonK73qISu@YwexBIAR?HHNkX>MrP08RU$zGsYFE6HR6=X>@d<_p`p$sW)S_A_m z82VHZ*xCQeY?|PwoQD?Wjjtg4=`Tzzdzp&JrC!Wuit4gx!JW^_9mkXSaw`_QC^vYn z)AXOnAR)F^=bjcB{rOpw&Lh(wQYA-Nq(uT=?0O@n4OgkOW}lc`C2G}w>@n-MzgNpg zXlF-myvC|(E3GvhlC8}MJE6_mmHG>C2$!KF+n@NO??@Rfb<@;`9#&R?wJG&ikzP7p zVG+$fwRo|mQNFA(whe>Wm%7M9!OwiL6BWQ)p~@U^7fn5c@E0uZR7U*=ExHCtD>0%~ zt=ymW-D);j5;*IAQHS}}@CaxIJx;zT>*ZhEGHAEj8}WkZ_SVMf+rb{5MNM3QyBkWR z*?i9U*uAb5D46r_-_Nj*fBa2ud9Xk7P5<5CDEbC$`1}L>7HnY0J4KBK{}R#846?_J zLarLW9p$g;*{AGa96~Q~??;~@f7a@=nC@H=^5L5W*D4;<881y3?#w(s6M zSwFVFvE5n!B>{gPJx-`ah%hq&C^|sou(VN%fcs^vsul+=$X%SKfVdYlf0OZppyOg- zl?)GxeMrt>&4{RR<|Vp`%F*$vPidp}-PrWK?JfOoJLK{+kXF%Y=iz}*h4W~4H=<~v z^O{Vs=GP~xfaSpWcsd`?m^A8fii{j$lnP*}TN#49&X^>!bT+3mC7JjFETkPD+!!CU zuV7ecf`cE{f-xRqKsS#MAz-A!V@J?uu=VOYxq5+)dF?U(jSuroNW5?;m1*~>EC*dO zm%coikzvQnmPtWJk3Zw4pvCmAl%eU`4P=Y&qqmj`shPj{h442L$y?f#C^0(~aQFu- zKSSu_@V%0hg=_eD$x@#R0AqxcS#FE`WIv&fJ0+;5^Gc}O>_#$EUJn2HF1xC~n3>w` zF6qQsK_Y9jqNgrBdmdC>^Yzjq`2Fy>V%XL8J@Wnex_9vW{W0>C`GY9nf9rKFD@PAM z4mGcjvXB|OHx#e6_$+;wyRpSHz9z%hc8XFa_~ycMDCYsye05E}IyqWD1TraeRx(f4 zyC<@Kgbl+8CIzHQuL;3c#e>n`cn9BCAJYdbZj=PX40=mty&Ho1%j)c}Qkm*3op3!b zg6)rn?r#FMEXCc42Jk;9F=Vm(2b)XqFk~cAuF&yg?7mR}!DJ$`4<=ihZZWG3{aVk& zWeK)$xW@ns^W8?;r;2y*!DV_!n_;!YWQpBe|0K5uNai?1b!LX7wKWKCNeT+aG%!Do zBKxD?=Byd#$J+G~kpr|I7Ay(;rPtAp`0HZ|j=H}Ffk$cXr(E;)$8|~r_nxDsg=V+I zq)6rio=Qt32%CXe>UwCJ_bM0Wi>mSINfR7rd! zEr2p}ekwMPqzSC$x=*AFSyC6;U-pwaA;|Gr2Jh7%4(6*}48kK4MxZjMTTXv5b)?n> zyX%^$6ZP?!lm8x@65J=7Co(%c$8%wGyG@P0l)*o>2eXGS7;nY@mO$cu&HXLz<-(er+|hk+`$!XK+Yyvp}$)ChXSHUhk%umBP@kgAsp42sQo$DwHq3Z?`{0bWQ@6D~iG z-(WYjuNN4QY$m|Z!{L|HE@#$O|`b2#g`Hc+C>7l>ZGG=-Ry+u zCY(<$JHo4SCm$v`Mu8OIhxh)iEwKI4g<)$y>#67@!!RGaeA}x%k@EFWF5W&a@N)eS zmwwm7cE767Q3Egln3JZ{%;LW4>>5(iIJu)Q|FLP42 zmcw&XwqjqM^Rwm=K!%mObKW>dcH)qTa)+pL9{RgG4(@R-?S=@nC1lBc0Feuns-2xU zy>@woL|I+qd4p@X_%T)_Uo5tRlitVU8#)B@g>Z%~r}bL`kLROHdX8Ie^yZ?|VN)B4 z5F3jTyCO7$k#=T@iWep6lT_(=ZC)Z23Y~w?STnflmRcHIwoQVJcS?saS)IX}Ylt!< zKhrg+cwO=pdS~uz*AmEx024Dj_Wxj&JK6=k>{xdXP16-d9Y0)o%D1tHT?FU}V;>?~F~Lf1wM9yExt}$GP{e^4Nx?4QdYd?a9c#2v_XC$m zTpNFcpBzNVQuP|-^Lc77+I4k{Co-|7!|kglOHEdx0 zf8wza3ApwDD96xHdYsKS9Fy`J7=jDb!q*w(kGl`QKM0c^Spy`cDdg5ii$;tAb?j+G zl8t@?FC9%j0&%R_8#-GA6364yHVFu6r={6HN`25OAd~p4<4t*+^%cq0ptA?Kn&}mp z{wXp4`llUEK(nsIi=_-*(pIO_QfNZf5~J?CN~*e}RRzt8S4BcL#!8Su^IL`YLU}Hn z!=}sCY12ZTW`QK)p3IMeOATBR;QTP$=Nmyj7swP=ZN8_JPv*rfUrfQMc_8w~e7wzt z;(goN6m3D`vY{a<^3(iBpAUuNg`Ek_AkndBkcY(Ic^Xb}FRsz>LRWfflr5IJVq_Dz zxxP`hLAMM!q%)U|_H>N}?7rX=r@uVensOD~3IC{7L!uT6wcy#Y zOA0xE*%V)S9Akr3iWG1fx_A=puI8uKDE(4eRN!^= zSdh@xiORkX_|i${ns!}|(;r1dG2$xl(dS1EMj*v=A{lim6ed8ov|`zfAG2eRqBJ2r z)QW@fh&LzQAg%U_4m_}r3fhu0gEu73i9F~WaubIM(1>iOH6bOFJ2w)|+l%VV?v6P4 zgDRl{>pIM!<|(#4+-A!DiIQXqBg-FD)_1iZ*_jdqxYgoLvP2>Dfl{x_{wbKjC3@fI zOA&XGH&KQXM}7uu$9ZgTCw;vUqFcM{<=}=43UO%%XFcqPYL}sa`I(AIjxeN-0jDgHt%=$wb_m(luKDw*K}Ps>x5#^d7o-|`+`2jzG}atx%8j|rZu zN$I&Hi%M69+pqxqjlIy8q-nSQomXvN@6P4LM*)%)7w|0MAA{aR_PZF%`Zr`! zqFE!GP#?xyI^mF1%60?rO5%)ncSyZd9M8~dfI{pwfvG>p+3n~QGM@9Yh~yamkayWU zMW&}+!?Hlz-h#;(B+Ntsb=Cfhdv#S-U9^DfALH>$j z4zpy}Fjbrumq5zIF+{uj0F0Me6>xBz4t3BaT$eK%dq}z|TUIYw7yTLC!=<-GLY_PP zheP#2UAD8hqIG)fuia}Yvibz~5WdQ~0OgJA9{hwY!R&i$ySZbOR`OI8*@WRJ)A{Us z8N0bCBeHAgu(x(MiYFrz+YLye0iBozN;sA`Kfn+hk%F)qk9M4gUmv{$dl0s_^{|bm z{+ZTVtkb!*4E;ix31UxD?EMo-X*4y+3Os{e%P?pf6c!JqwQ>%(e79%3P`_ae_;i5- zqyFaxT{;?y_>v%pqC}3%VI=S5v73t#+$%#;16rX{ObDo$)J27}JYjq^+ih1WJq5Y3 zJ7L6h=fUDbf0KPo+m=Zmf3WK>Ek+(}$nRm(P!4mk>}2|4$rm(c`h098YYoT_N2+Y^ z`UW#+t#CJQB3*rCO=Jx*bmH{`j23lGkr2#mjg71k*kvK)c3vT^Jqi9eD2Mm7svMZC zsV6!#>x*~q#qzyFT|U6SWX!rbMz265I8D2UVu5m;A$W_xWisLWggd__wuw5bn5i1H zkuL|Scjh4O+4TKc&*~q;*g`Z@;eY-pmtaU;)02Nr2Ez%Gy=4|oY#9kNClh^ojfryUOs{hZuS`7!r@$( zl%>3(40Q;X?W|}>chlZochgVSd;h=5>wvn9|0J(p_iTTiVwrB~lzV=09A zZTrZkZu|G$`6Z)hDgQ4Pky7SgD$6|}@Z7)fM5uN|sItERzJS3Tu_j=K z0NB0k%j*|7pj>xZThkQBbwe1g53JBmuw@ESJ_F>AI};a^K*ZT?=_7UEuEs7aTHy^md&JXEK7z)OTzmDx}$v*wOD@^ zaamKMwx1S(5MyaEgM^t;Uorpv08> zv)fdWsKV4NC&kofC=eZGI2E0IxB{K`uW;1^SgI058oD_}<@o3G2EZTKgX|lfr*_+< z5iD@I_OKFE}8wL>&Ed_pK74Rt3|c>bJeirT(%i`KJnn;o!oOBJ`|%sMm9+^w(!3tKoIi4GuJip)Xl zjig4G74t(?BmYWDBX-S8)N%BbjdwF&n_oivsjofbXCASTa;l)IsHZzKu_OnmEK$X; zJ-arLU+}E9ci6F!@ZV(gY& zhZ#~rqK;o(YNtuOkhhm~@;C30Lw!hee9Nrt`gTsZ>(KNJ=KVG*I*@I(-DF0n9DY#00R#{w%{K@@VHx6u0H1`$p`iw5YuqM@H{${4x5 zgTjnw82B(UFDyOl%0Gm?^Xi3kUuP5FaN>ko7;3n#nw&PgA%o*Y#}EQ%VR(IxgggRi-2v=YViGtBq ziZGx)h^wT$&ou3mr_xVJ{GrvQ4Y2m9;a{g6X)@p$Ny`tH1#|#T*$sJK(>%WK)HQ9R zCSV5KF%Hnk(+bZN?R=(>Q(64FIPjY=nDNhjGGcV@BCDw(UzpWOQe?PUi9nzPY7{n7 zT-b;}_?U^B)btsny6GRS_&~U92>eS1Wkr>LDD0($@d#InoZ9feDV@)Sw-EVfX~hZ5 zeMWR3KYKS40sM2c;!qW;MqtjUU{fVf+$&!Q_7;7jL2PoMX57Gbg9^yXA90kc!rQc~ z!o90>kXa#Y`6r#0e9|M!9+{+Smj{UDs=`e)s=`Tvn~1`UZ1@q>hkPDLmQ8^v?nJ4o z{|p9H-c$wxJV3(K-D|_j+%+UX=%Au&shXp;s10IZ06KU$&gwq6eH!Dxv0n7ZR3=sj z3vW68$xP_KyQeYiZ=iQ^-C?~4AWj=|AsO!X7+3W$C%@Wb7voPLN2Lt7|L{CY1D96j{j&d(q2n}}8M?4RgDU5GB)7G_a7e^~<|^G8?j<;?*m*HtOjxsnCQLNZnP#td~c}e|myW&Qar%fe42p@@8@sKy*${ zg6fI5aDm~R%o^9h3TahQ#(9Idx+#MDNBh}U;Il@bLTloK$NoTSOkzp`#GVvZ;o_jbNb;Ec0Zrl8Vzeua?j9JFyZaz zzh>&4=<^$Vf4I%J!nDvlJ+vTC<5hc#IGt})g`Wiv_HwZv&n?D+EQR_MOqsQ8pOm}y zN;nik$Y?YB4y=~WMr#f<-u1Qs4HPC3pxX8HODo6gu+kco3Z7|s3hK-C#@!&y^5~oH1V#$rm+1!V_&tkkBcpGmd*d%y$auTjsr^}}Jz7Cpa9UZPGOc2i% zosI&a?pvQU&o(6vmokxm0519RzS^PHXnck4$&n*+-qWEQ9lrA2QllAHP|!X!i3_Y~ zRG~0~{MglLcBn!hf5Kvnm>gep|J8Vv&Y>yQ8y!fruAoR-Y)prm^3%@XQWbOM9%QRe zeorTHVVCpk<{GtK(jNZQ?4DMVjW<;yOoY!sV~hH0??O@-Vv$gQ1Km!+8>VskEfNHqf4Zi zi7Z!~qf>Dq8QAfA{M10}0!>7b9!CNJ0sM~ZzoscGRiD2G)1t$PLk%=J_u?Q|kI6rs z1V!Lz)TRSfB6W?J0jQzCD0h;y8Ull-*bN8WM0Rru%-g)YV>(c2fo$d}bEWD2?q-B> z+f|ZeYHk!>z0%E?qOGmLJQq$;q&d=j6D4LeEJ$OeHhhyd-bD=<^;kk&o8591$$Gq0 zk`}Wnl-#j4HP8hh+D*PIUm{DbF)a%b7`5?R*6UU7AWH}gxy#BCoRA#;15 z%fE8V4V_Wqk|eNzvfMo3=;!*&<;J-ubA)SBrRY#U&|2n z+ZmI}OQ{lFIIB;ds5e$#yNz)e<7Z!~ON25&sF9sb9^f1B)Kp2J44;=v zSpg)svGJq&`Vd<&CMCVtG*)V!?edV3!ftn%BxXNzomD^!coE_hEDh_6%oB!698}2a z6eds^GEnRizKc`=f_yLwOb`iRL16T}g8N8XyH*XbM?U2(`(p2{SX&pJVMZ5xSTYT$ zH3vrIl^gDvNvS}iK3L>}$uL~wm9Rpkeqb15!W1lzvW9d2RVpX`RGZr!bb?6^s%%&D zMN0HyQI?OlcwWpJ0cSu{V^YC~LB|v>|AsN;+i7+nnDJKs>32blvPlo=s_MghzNtQ8 zj%1z5Xt7($&B8~f)>a{8i-;4vC`BZShe00Nki0oKH9LTbzJe;rPjcaYPjp{md7w?M z!T`8}5N&>xQz~AN;S}P}u$9_)XyO!{3VMH>@Ib#+EHH)TCw?cHZ&8ZXvXQ6<^<0qO zT2wc^=#yd4&G6@ygdq)JfnzA=>z|jD${{?zc&>QZJ}Icm+rM<;G+k=iMZK21FQp22 z9aOpzhl^7#ZgWH&MFei4#)iXj<%~o|A6#(qHPoxO8)4aHXBRui9|la z_A^eWFq*N3T=?&i#5bC#BnJk(1(p49O2npH0hXhHSmAW}TMui&i%77i0LHPaMozVH zNb`N&-Kf8M02^)$`@Xh7pk|BW(R#vWVFr|MN(D=X8I_r?pD3VwnOcdOyW{>2zyw7H zHp*)Y7MdpukP5`Gl&_8=pV7@!O#WPQJ>|4~0iNJ%O> zNSERW7lPb-(i*IFUf2uQz@m8NB8`mNWX7cRnJ zB0OnlDLgmN4;ZILl7eYXQhDYp&+q_icz!@NIr~L4depU5e)u43UUNm?;Vi`Xw%OU~ z-nLcz*e>`sXm1AdsvNBzl}oQTHFYsf*ujsltd8%Js!^p0(iC(zXPl7N+Hrb~q>iW8 zccZr^#DcmBKV6@0#v7WhJ~uVjX6LQdpl`urcj$-12l`S5WA(P9P!0fk1MXz~10WwH8CzSl24|Zw6!j>1Z;Na4Jhd`_Mgl%iQWJBy`+Y)P2=c^8qAan-vOMW!>1&~g%LCa0b~7m6NQxg}08HiSprzsF zM5XjTd)uTB6lLiVu+B$e8G07zopog9g19Ui(4JT|5bl>Dsq6BF!=Cc*7=`yQO$xcO zAq>Um1fMFR3c#`ra`^_X8YR4h63x*C9I(>#P%~vIrhk5u_`T)3Fwy{(l7MU2pGz73 ze*hKHb-F*E#GD$n?uRzL%@J@jp8Z(WwZ1ePzFwvnlrGrEXkV$r z=vk;E^2&J>)=)dF1Bt~ReqqT>7?n&)uz>#e-l`fOgGJl z3}E{ll2-Y&bM5VPW~gpa(0LU~xodsjJ)H7iLET!B=FBlUk&@J=Z&Z@mx5*IOv|Dh$ z-~;9@O^DN$%9b#Mn?xG_kgeS6+#mYJrf?p%oYG+iWOflzlDUf$6-g^TF3Or7Sk7xYhG{ zkEnMY)x7vF{toYb(Za4$c$abW5@NW3{JbCXSdl52TVJ6eVdKMdIzdr1i9DWM*Ya=y zJD$Aa0^ds2Z(Zuq=YP0S*n|#H?{nn1Y3C_4;^u79j?EnmKeWmB1xEIr~nw9`>pJ;*1YQ#dej08 z0zK9!OwvVDz=UQGznXpJRri2?PZ=F;B}p^RHJ6sA0-GqF$%ss@t+B}=%{jJ4vB@LL zMp&@1NhKU6Scb7FBgv+LTK-i-#7cScKG|=%n&H0~opccJ%Bw$*+(>z9_D?U)zyk2E7U#_zExJjHEB$T<&y#*b971(e1xZH zsn!Fm^PJZHzCE1nO|0G>w%NTo?BldKJYPVR_A|=N2OzT zK?g)f*!`aX_;`|nqVw1Dl(IvL(X@VNr&&$ z{c?c1J3F(y!Mvy(ws#U>1hbgj0u!9xmko*u&-crMU87SOGS}$Bjvxbe?yuKXQedD$ zb4|I(fu)0H2<%@KR$J|)kKZl1m&F`;d|}-+Kei`Py!BTg25LlG(kA6-x|1q*I|ZoA zwd?0_g-LtKty61bmTJHImWK_~WPs;dRmowhRbfOayMpXt5<|^^lz(7;ZXJUCjP^?@ zkDK43*3&BwR^jc8?S@0H#5W%~7+7S;aNpL+a=Ga6?5)l+e!T*39bQt98HV}E^vjin zU&Oe|eG8Dp1YH2*YXuIQ1~~ZOhq?i%El7!g#xj}wKk_;Gl0yG8_AxtYiwdfFY+_Xo zt0Vx&*M9Q$m$N(akq;`HDbb-s3s{Dkj-+M%2p_!5LHJIyX3>pO^ytEEzjX9m(xUXcIKmH9``VK8 zTCby>A3lJ|p?{c*P|BT|AzCxa-h?MGB-pj%~2{DtGcl7|tl1zFA21dIps!(UK~Cvks(P-Rxu-#e-NPq@Fl)r*}JFtfbT6(->1M)TCB^*Gg*~yhfW$yqgEP5N}?O zm8krzC#I7xgtQ30_T#4%%vEK46Vw+=W9)Iz1amRv=vNJByFR!tfmA)f^|mdzl{G;J z$`e^On0c7ecoOF$!VAjgKN7d+R4YD}P_$tc5Vo6`Mv|UV1Q;jEA)B@J!j(f)YpSa* z%OZ`HcG=#_=96}e$%L_7&I@jxZQ7#g$~od zqQ|S51BGPyW_%(5!h7$sB|1UVJwP&W2aOEY7up-S7u_4#fCfgD1=PXKDrKDfX&3Xp zvNcoP(VNg3KF`yx0-YEBDWHX0R$ z_Hu;7%n!{U&J3Snydr$xMf9pkH2mcL14U_eVAR^B({*+h24tsb;e!WOe;Cs9PFOwJ zVDXGzIaW*s*yF8g{ax`J@W6q%>@dL%nR>l5p(Jd@hbzfI4-qf1WlW+kNy-m?ZwcG^ zb)g&+^IoU2lM;c2*s;!xpv3~DID&TVrYIeRk6;)&UdrL;Zq~6Npq7`SnaAI~cQ_z8qP&!=L@?T-9TkJsPWY9Vnb$vV_-%1gIkg{1nuqynM`-e=Y5|OvjqT-YiL&CtE zld1HN@T$!&FIbL|81K+kNY)39(6Ps@_gC8+=R;L*U&d#Jw=}La^#_dM7o|<4tCMA_ z`|4CjAJMr-04dM@5Lv@-*l5|i&=AM9rLDiHWqqR~o5~HSdFnB0BhaAoFP6_1bMk-Y zPf;NACnA?4jSL{=d6Dn+-r+Z<)jPRnz~3+^gM(PAeez@rDzvVRM0oz7ZJEx-H-65W z&!g!<>J?LT=gw;93d@S&{s^qCwcm2XgDQWP@~*x4_&QptC?F8w8&s`ZDn<&1bPn#^ z%jtIXnq&=jiGp&hG!{ax(RIab8gL42N<{1#>!=M&GgW~?J68HQG!Gn9sMBCN?AicR zaopbX)@p^JqUhE{KMudV@Hc{iM7;LtCP$c zVJ5Liz$kMN&d%H~b$cW1&F~Q*xg1vYTW}&EEd`N!-I0heP2Pst7+*7=nkR$gLCW^1 zKte^Yjty7{7k&+^OCi4dPP>~OEtqgdBV4S=^)8H&iTP)%{;`2F( z>|PnLkJ-J#lGM3)o%7Ods)}1-YL%9R2?zkA1=9J`)2jKallcWhs%Zr2s8t5l=0ga) z+&3__`4eGOA_@*oNL5ZKe`9!}&|iKB?lJgPz$moC_0QT9RgUo%>j$$6%&;QR%3v&D zS%t;Y%md=L2PU{-PB6nBRDTZ<c9MN5H03yxDS0_)lty{4F7g z1x63)9M_Y*^-H$WvgtL^fA?pO)NAtl4qkUsq*T*FPMdIMOw$x}4yhZi@_m%VzD&o8 zL!8MLtkU}wrE~q8GDHV?zx@wV-JfoJ?dTbwRp~Q>f)Kee3ITt#E2T-Qmg0Tzc5=I3 z8wVgyd=Y-uvxZ|f8Og0p5%$g!Fd;=y`vUjWR;=RS6!?m4#KC+6@bp7X9pj+P<8O0d ziAq5u49_V<)c7h1jzK@QY+CRSh__uEO;^N^XfxU zlp?3=H?#4-m&)jjXk4UAqC?KJ{V1*G58tMk5jM}bBJ{1&6rgvWlf=)tGANG%e*)`v zAcGZ;L?Y_W2NfiDMUqdC0y8A(Ja-a)5A-&lde;eo%0^}8lm^*0DeWwHFG_j(ggqW< z@~y98?c)OaF=yjOy0&5yGaMx ze+_P3T99co#B_85yfoQF6)z0I<$?UIx^u{XPO#Nu_);DsUlsP!BQW3kRt#mOaNUsjO|V=BjompC zv&cs9^g5nPQunR%Mp{#ju!ZXKB`>)SaC2$iORcQmw~gnX%-ajC0Hf0q2OyS-EK`)R z;WF|97fJnc>acE{>X-_;*osB}##H{{g|#TrItk9EWeBb3vYZTC{gB_>7M6bgDZSYc zg$51In;|tt@o;?7?|GFw(TiHSqPj|ZulQgcw}{a-v53J3*;uPFp$+Z-ZbrvMz&_kf8`svvPYWJ zE@$PaLcC~V$|vaXRPR~FlSnC2{oJwBsQIg26uZ2>1;pyj72O2FfN_OqNx-GOiLFXP zxDzZ_K7--7SgOl_$IAIOYd|w$TfhdPNECGlO<<(1USr(#f<6cvcq=c}E3V*5Syy=d zp3T--HMq9tI>!}vR#_8Xh;Zw(Ec(N$-*xt_71p6vp7HKm^4Pnj$~amFR}>l$X>N3Z zvJMsN8y%|76~@3HJ|!T>OP(A$ZS%g6$1{CP^S%T!whRm{-(er-#9WfoR2aV>SjjjM*wm0Pvn`GJzCm3zvR?Hg9eXcty4B%&R@sKKmQwymNK@>h zV7+PMKrsvXaq(AR$Ryz$XUUGWG)K{!k7Ob83=!^^RW~8 z)u|(QH8KLa(4j>J)x)4$n=#Rv&7l24Y%EH_QI=M2ZjU&c_^A#5`vnx{BNSn+Gv%<) z1uHIzd{0i6)O_TGsm&)HtrsGVP=M}D@aJ(xef;JE;KNv?f7%1B+R@xW!B&zZy zc3`etQFO)ZqWhRp3?(ySUOr?((Of$@xsH_w8*I~-zB8ZPjNAE_g)1+PJ{8^SHlf3x z=K@H8Wa{`SyQ={@MT37kw>;e05g&DF#$9Eu?QPzV=3HAjDxJFr=T?_94SCLEN&YMh zx#_l=l7So<{wzMb^V56n>g?#?!|_x1_v%c!;lBd2hCH<<5 zeuJ|0-Ka_`1HCKyuO;Io{px+6zi$7WoU))x0#xwi8!jx!gfE~*L?=ql6Ld?Perg|) z(dd%l3-~mYxg>CVZ9aT8y@|R)eR1o1^^TSdQf8bL$D!9)?NljXcQcSxS9Qe2N&rLj zRU72-e67a>*dF7J=E^yv767R<2WnDU0o29<92pr>OE>2 zc!z!$D7XOzme7PR$>GUgL(?C!X|t25qjc>Yg}0TRX1(}RyQ5I_hB0Z*LfU1EIu)yN zCb+x+OKy?xXHh_isxv*%ss~v$f}TOr@S&^xPK(W`sGV?{FsU)H{5ZL@IVP&6d)q=W z`b^-Efg2Sh;p>c+-%Wq$Vqn+JtBY*FfPtcl4Dto?Qp%3sW=?=+D)NU^1ng(V?O^z3 z?y*1um`Yj$wO|S>lTbJ?DL$UQ>lXO9aWx>~m-++gmXn1#{0#ERK?rL>aN6ZVX`OvX-7c@Ig z@Qg=AS8!4ori?BC?s;U&g$h5wR{pPP8N`=bGZD^R0=UTG!@hf{+Lv@1nH{ zOtEz#PDt9JdO^DxP|mRfi1MvrUjiAtoPU3qc#snoSRZL;L+_KcQ|v!QPANaI{M5La zM~le-s$y3oXQ*Q9zB05AV_KyrWwsJHt<}n8fVGo=-ATn&m?h~qeJm{^)dE|WPv6mfgC26P(t?39TbBdx+|D{Cz zCXUr6EJDCDsJuI1b;A^A%A#bPl0j{a{*5>fd~PhPJRZYdBZ5}ok?)q6M)7sd_Sy|_ zLXnU>!Y^kF1;6Hj6i_L1mBo!-Ev~8FS2=wph*2v#QgQ!Y%2ZyzTA8@Yjvf-Obv9YVMI-C!PR${m;G++J=iK;v;hXaj#Gy2Ap82T= z(B8cqo$P-q?YDEyM@Z3MQ52{V_2G&ym$4hj!^E)g~3<~7=rY63-@ zN_l5t|B{W8kb#^kRn8Lcq@F_%huEwtdf3%{(n5prpS7Ka&+EiLe$ zt|hB92Fc3n3np7+JF~#KFZ5JVtz|q-=3`f z3)}o8S?AzPEVjmrQjp2@Kdm<9bI=dx;RnCqnWU%BqB68m?3r5xCs%gkVip8*Io;7^ zzWpLPw2{jGPr5t1mCW}B-OTYn7edQk3)LQ`Ual^atPb#&Sxwr`tic<%nGUDPx1mqQ zo`jZ-@-wXLFTk-b2Ajdnml{2o%hiP^dkmX=<}mu#L<8u}L^m&>AzayZeW zsx}!Dk;js0`7_GfCDFX3(5o?#?y21YYJKKSvBP)aMW4{F`3{>f6EFD3L|d--vSc@eQpJk;0T-arNDCMAU%ITbTKb=yq%dV;)frOXK41CF8C=L_5dZ z1F|if9Wy$&n_5>&QC+w#A3uQC;6x_3cdw7sZx?HQfv|z##TSVyOGE-fPh`47(u^E0 z*ME5fr1zRFwlNfcKo|LRW7-iN*S(SHozWmoAdF9+i{!Xi1!EvpV0xdOHV0-~5t*2(Y!qJ;g zj(Pcq9`QmJvkj>Gf4e679K1VV5K^r<{5IRg8Rj;GZg&N8(GZ`mY`Z$eOI#tZ#B{8z zDeNZ@ZYm7Vx3zW2oojykb`d$zSRkDfU;L*02LA7y`|;a${B7bN(C3;;_V8h%y%CMfHoAPT zO6zp1Q`DjPzU%3q$Oy9@%T8{BoSQcjie*u@hUR~T2BSRL{?g}#c%9lIY8EEC`gs(B zZB9|=I3pHR0OV?ip#}g_HFM(_U+Xa7e_H!6fFCr!0qhuFjk&`?_;yW#qa{ws6VvS! zwIUy^e&8{o{z^X*z(zMx$>%e&%A!gM5s67jA1TPFxo^`DKRYyW&Qv!mx=UH@khL}_ zR0M3MKCH*(A?h1g8(_!m-Wu5|wC2@=vxT#S?$iJ6n-x`JuqzaJhJM<@f-|4V?9kg-KRlotAmQ*-!Np zm|kpLIlkxoVXEp_;#|64=PF!Fz736+p_hW);}yDgo*~6Rn*`#@ z&m5kbF{fdI+Q#SocI(}m7tPgeeb?83BM^#1;F9HuJ-55mR z_fvHtcKU((cQryjpx6nD_GUYYwURuDJzEc))D>xU3wP0qO9E3u=RFiFs~)L z{rdM`jfZv!H$XmSNNU@}>De}~L3KmLfMnyz@>b*8A8YI@;BIkrnx42|_P>W|D_ET4 zsK+bK;o z68av4rW?A7J=Yt_){=hpZM(EWxYy{zMGt;>z;gn;H}Ft5*6$#H=V3Ek)Y4J{tS%DO zSI3I3Va!rWvtABASErAT3!M1L7t015WzgJx5+e0`AZ!u}mj^&Ze$Z);K(qwlGnPqM%S_uFzRmYh2QZ8ZI>_^N=+zweJ?%%FJ9eCG@D%dhcjmU<+R@whzKD`N z=Rn$VeJ_mkiGYddqRFy+_7o4()Am#ZjE#O<9jH3jxVT-Oj4Vmpdbki+08Iir zJ&pw2zBTP#{A#NWsfZLZWbr9+b-va*i6`*qkK2 z0_shSZy^3kYWvPWOTfvjCXWPNK8~STd515j$;D1#_(0wMrZ>iK*znK}{LwH*X9NTiy6& zUeV96DCz2biWGUBN6I#pj}*nSl?-CRGGGC1Qir9c=x0VJU=7MDW{8?2gg)m4vEiSm zPXO#Eo6p~U9lIiOSY*BM*o)mw{0)zK^zx!cL>@Fx^A#KVP0`OI;i(CQWT&;1`ZOh9 z@599cm=%F|^0}&pSBG|aQo{gJ%i{N)+ANM#vxSZ*<$4HrbvaWqJB7KRshWr~4B)Qk zv*kEtxBcbpLww$SPzRDst!TVpz}CN`CA;FSO>1T=_68zw*E< z*8~J?-mL?6{WKi5Va=%$M6}v4B=#H*m;oQ&RWB{(h$xTOXc(O4^ow_ zUPZUc@1Mwnf5q?+;y3lEWaSgR5v6yf|L1p_-sQg}-a!TmNAxgB!FyaQ~3*@A+?F zwUgNlp7nHdQo1qF^++Wqg+Dz8Ytd_#JstVl2x%Q^15-d!`W}skW07a`gb8qr3a5>^ z=!VEk@ih2zF%kgzawF9kL`NAI*8hz-hn%7u*LRjb+i%|RZqN&Wk_i4CE*qXh=|Xu@ z{Cpt|`T5O20B@1FcE(tMufNX=aA6>@9%_CB(YvvyAIAo~X99>G&jvuyor&|MBSRAY zue!^m)^JzyL)>@&bYxDQWhYysN>q?*;2)4{U@^!wFbQNDcm^^JWbZ1VNrTMiNk`s; zVk3%He%`V-62N~;1Jh7T>eDJt*2Mk%-^s$#qXF28SlUcfj(35hf<7Qtmpn6Jih0&& zFRZJ)(Vv{X7=f{OPOh44X5-B=JCN?Ld@|5&-()i{m^`*#E`RxXWO%XtY1Rf07D*>s zz5I<-e!Sdb;msz1nKuPIP?K{FGTGW8NHO`PJex{+&#?8a@2{G$OqHemFZtN*kDrR4 z`^bZNbvBkqYW-m_4{44w-aU~1Rwc=KbGOyyM<%_AZ2WIUJ+GK>>{(II(A|zN_2-(= z6*W3Wf^;gw%~9OB9J{CJ$i?vt;_rEz!j#B(r#u5^DHF&(2p;Uf7J=24-Q$f)7(^&{m9UO;yDX6Q{6#^%+S^51z52X}_Kgd*s)>vmfikd_>@=5_gvWU{ zsg=^QhSouTUQxZ8Uy@fh>`(k-JKEEap*CgBR&vW48VBik#U!$@a=Nl`P}5R{1&#y7 zhFKk(3IG=(#_i%(Y+(VmQ&&PYm_lrWtE$-T&NV0;a0Fqt0@6R9*StN zIq6g_xr0=TOsN%IsIrzpQfx(Wf(6u7rQyYmLiFiM zY`v6Lx*sTn=gZ>h^#`>#R6Y^ubmU>_h1MzwMT6*dG^eeFkUNU9 z;P<%{pim+Kz^9l{bWo5Ex${dF{JNAvnp&J4_GbkJ{1*apWVMx}a|y*rxjb;1Yq3XnQUAGkEW>Q{X*slGX+P#Z9QZxZiqh2M!dHZ83-66Lg{%ms4PRLi`Mn>x7zXH5AUY8? zb%uNIpYwaVKOQc>0K51-JSp9ZP(M-_Cm=!~*jPAIY$U)Hfy~VRn|U4;%Wnno3nA!F z*4~nzs#p&zkDqnP7?{i+hHLNi2gwE9;P?4J>ihdy;;t*P;e5Z~U|ITtx`*C9vJQKz z9cS{sS3RWF!4H`k=z*6AHWF0+*d^j+2Vj94&wP|hel1LOcVF4A6EbrEG*`E<5|11elzq5W<4oQKZ02!#j zLqib&nYdWl|M$&adaqT}N!qUd@fPJ@hf*PU14n>r_3o(2ekZmDys5S>OK?n&CeuTF zdIea=rs0;z>Eg$T%{v#&d1$nW#3ID^?c7Ru>EBR}=Hr~VJq9@zS^Pe1%>QMPsX zMLqIp;Z*zwDXQjkWqa%M4hU@h8n`5~dqD+J?dOvC9|ocg5bexMX3wr;u5q#_&#wS~ zrJBpQvyaPU$FUf)LVia=SwrfSn^IE~-+OTz_HE_r-MF_@!1ow#pmhhZr$}dkEC}|> zdY4&A+MaT!2*LMIeAERF!Oj?A6Qg+Fn^%7K^6`nQ1=uM%=-xRZ3GoddI`E^Ur*mPb z|3?0+X!(utYhM(4O0k3U(j{{y)Njd0XIia2Ibsso~S zQiTG-Iup~k;9idw2*JDYLZCM*4PgSoB_M3qlBHbo0GPC zfy(A||FHBsPNmI8rGn@Q|%}??`cYY7SEU&X_UmZM!0y`TQqa0o(U=+@ce}sIErUbu63ZF|q%~w7| z`}0psl&LQo*beJqAB=f8iSzW>mBHRH#bU^DAoZ5*r>(+kZRC4~^q+fyJ45^;K_kng zkvcKm*MeagR()2w4`!CpZy_-(gv>#sT)L}3+I>YKDz}(Z7DD<*GF`aJSFo?O9St-clnr6J$)7I(cz+eG^Tj;#}S z>L|5ZY~!9H7v1)9%}p@kSV?(3#fs75g#3%mO$6fvek6E`sbP{(fmdRiSc4^i(9&gB zkX!)6x=UCO%$}&Qe@(;J|#&qiI=e=IG}DKrGJ) z8WvPU)KMal#Qf+4(-9$O3I{-e&ys-nQS=Goe7?ULIi!C;5|*Wnqpf*Gk^bGJt61w& zTkiVaAHQPTN-BTRDRRw)K}XVLPd-1Hr*e=j{1qP;twJhKz^{27E-SaOtOAtaB}DbG zO_L<=G36_upi3y1tGlQ?8>#P}Q*})%HEMvDa>>>8l9u@EQa--KV6imN{wk<0F|tL( zy7Ty?9~!&m?M2(Q(DG#JKvTPh%Air~48tCzK&G2wckgCNk|+cu!h z0lvTmJWKs}eh}Q#zn0t}VEY`*-NjsES0Pl|-QG_Jp9BvXecFwM;-?*0BKgD@iu7k& zA8B;ImuYn0O|yt|?XrDZ%)f};l&dHbhxyn^vpDT%otrN*&o6?mRtC0E+nmlxbGuV$ zc}6P9XNM&-@S)S=&WKEi&jh~PaCW^CcKjN)%jQSt5$L!~PHQE2O>g=;1s}Z8t#5zo z1qpvOokPs#m<^p*%;y0*r9J4ByI>*$UhI5I(UdbHSuxz}eXNRt2J2N$OLrwZYGT1$O0T0r|K{+qvT9~T6{RrO-zI^TS1zHlwH~$;H}-E zx4dNjrQ-RM;Hb+tXn4!F~q4Q5zwFIkU~J}>Mw!AQ+$8t zpBh*1RR#A323WIA;sAiVj(6E(iF-%it=B? zmMHP4u;L1I>wpXKkN}RMW#?zBgbSsRHI6ZVavEnK3-R~S3Q02JaCWEyQ=2ll+A!kh z3h0B>@fADH5fEWwA4Hfi?SAkW0TCuf=2)ml-r>P)^I+;X;?Wx04_hR5lFMT!=&eXD z;sH6v`@-9u}_4kL2GZFjs?VwJBA>ehA=-**B8Yc|8>@tgx$WTd(U`vQw+$LFk&n@EhE zg-gf!JbetM9oR8OGb4R0>OEg?X&BV|w;Fk%Gaq#|^Usk5_H0T@N3z(93(o}x{*;SgC{^#4j)p3wMj!>x?khHAU<)ns=3TAfQVJ(j925#^yEDt1r}9N7BK+O zf1bvbgS&suLuOzGzc&{lV+akP`Wp^J1f;>Tm)9cfEsu_qt0e=^9d#qIpdcUKXPAVw z<8-@=ZBTeyXC9T@4`wPZ#o^pw9(&rS;Gm#p2noa$8RHK0gXSM}bSur;o!jGzHH1=Z zh&Ex36>B!h^5du#c?e5SYBkIkTyg}$ZJ%pMZJn;#d2iQ&s?y9&ljHQhL0~N!klMDI zuzH(lI&8HSs-uU=O(?EozVger?1FG@8@ukSFN>~LtXGRipP`25SL|3n8G?w9&* zk04c-qoyw%>T8+~9r{27h4p-NHg{ipZtVH6E^z#7=ko>YZL!?&vup#n(CWUl3H?2| z<9G(&yycZi(V*}%j0D$36uu(DrzUz2AUhNXze;$J0o3|KZ+kKWR{K2mKLW^-8h|Li zlwZUE#`ge#zzZ~(`z$wzuK_;ynNc<7=l^f&E-*J}2tRvTNkI?1D2&+B#~6 ztH+sQS>DX-x3^U|$+@?(j+T5@U&q54byVe-d>OSoA?TDv{~f$!lpLTZ!VrZ)>-w zw-%ybXA&@ir(n?gv@wBy%uA{JNlW4M7$i8LpoJ~{YK{9{7NLipvWZP5)Wg8;o3A2r zw?f`vPu9mvfw8@vY_YtFo$+vOtMvUU^I6DrwfRveE?JZEzQb}X%B17Nc2ybB6OOMr; ztPMg}w*Vor5ihJT9otOl19e$;5~g0chyteB#LlMOS{lwKTCxPK%pFp)REb&`+BZ2r zaSGN67g9FEzLsawT=ga{CtJ2xd48UIEpfNyj9_)hd&Lsv7`zqn-D&xY2lGAUoBX)yp-`hAKP?t$ZS%25kl( zwE=?mReUX2o2zl8qXxmPbm#x@=I(9#sx4o;{ybKa|7Jh9Rb&* zKfroR=%K+)Czur}wP;m=97h+PG0fhVoKZ=g!{!&3Sz+v5QF*Ur<-B zz=B-1dTqEOQ189!I2k*mjReRB9klbmh3PRg0N;N{-*N=pHy#3Z945eOBS4puBZOuq zA$07#>NqQ(464YEfGV;x0jC)8xgZQt7K1B(IA)L5Jhy89&U#J1*OtToq3a#vBkP`M z;f`%{VmlL0Y}>YNcC4A$_QbZGiEZ09CX=_Hd;j<2`{C@9vpckv zj$fLUgUe0fNf{l~ZJ?!{u@h+b0Wf!{0dpGA`S@y$Hmg@s6R(*mgAS4^H}nA7OAm^t zMR2772T6nDohm{PzWWA@y<1Prp7($vCI2>1)X%|h**%!I@4oDL#Ih(Vzf%I_zzh$M zoW~XR3cHI}yrUIP_d#^{jby~brilrrw0RvZ2dH4|MMW`c{s@!eftHng!&peU#mC*X z*MAt`ss$$@rrDU8#-*HJKt}8HQnIIp=u6me!^qn`j5{sXLNDTipjc10;Wj zkfH{jX&IrR8B4)naA0m6wt_-qTzu?uf7ufb*QwV{H=bWz7KKGfutq;W!cC);c2|!jkC|_W$>Bc>FD9b zziNOp*2s3#QL=ffxT{CeE3pUc#M;D*ImPX}k}5xbS-AmjS$S@wHyF?>VTw<+lSN6D z?_&te%RK+Lhw=X|YtTJ5Lp&e9>`=LdGv=>d%s2lyj^x`c>bzI{yyhM=s%a(NMh}S` zqtOgtu&gB=+4c-H-TbLlK0L?KSWyss&EKRTc!LknAn<_z!EuBDfz|c=$Ibu%GESQZ zGVV2g420SZQzh)9E`62LhRun?ru%1>L7A7c4J^yXdNRo)Q@g(e{b`D;l}{E%mY*rzEnFbp+W>R;&srbp6H zSKdm@bTBXISGE2+K_^fG&ggSIiA5uqN?Fq4g#V)Y$T8dq<|d85HXsH5J7ZQ20Y^r{ z0a%=jQA$TFBgUmjxD<07N=~jpl1yYT$9vM_^OP)rPLa!3Vq(sr(6((O8P%cJFE+3R zwt20avB-=rJ7pkw+cNWPk%qJ`>A^r~FSp7}aIxa%z*f)W&wyt?#Aeht28JPm>CzD$ zo&=i|x36H5J5N$KCMfWB$+=7_QY4L5Mo3$d6JrUl!K#RxQx#;PSV^)jHBu|HnFvLV zDGrEUzrfSbcz*qs0Uri7N8jF&Gfz3Ep;9M-#r`iI+UmQV+jb)v-L+2qYP>;CeppAQ|9kFL@DL+eGrl~5+hrXiH@BCv|kfb1za1K z;Eg6E&UsqZH-r=-0Hfxpbk~qcLOcW{;711x@&+;G5NJ`^r;m=oZzR1Txe8&mJG(ku z)$Ln*iafYQY0YlT*5Z8&X>$ia<@xB;6=QIv*kd;lN)G-J_C}aG!Z+Fq08KCxWEL;O~+6qkYX#UXo zHJ;FUSDy59K>n#?)0^2~WA8=B#{b)ami@*-b^1i_GB;3bNSbYUA#y;8C$PpiH&XOH zx-}Qo!g`v+lkfaf!}nrzwH@&}_ScH%74r$jWe(+r=+YL7ujSZ8Zl>bo&Z?>B#lYJB z`AV9rm;v?GOf9j{OG`e1w|mOkHVeW8r^?FGq*;PAbea$%BxV39=WMyAg$JqX)o=Cq zd)@1Tb=HEBCkygMwny`n^~y%}@)*_;!=^23T86AMM%m;(>5o0K5e6soKY2~idY}!MlyV*woq>h$MD`vSvTiSzCGci=;RvV2kc6s_D{NyaP z$5^_`E)qN^5~(wtRztx|y!8DXGN~FOB?{hn>oBIX`!)ibmrc3MdPAWGt@6XVln^Tv zNgnbhd0GWeu0JaW-4Gpu7Okt6B15~AA~+71Z`}wep{TECa|B39n~+;^e*;ivx`|aM zLKsC;5h-K%orOh#xYibl7hLVvTESMUwhw-#`Is-HfyU?YL~6D8rdHH|5`UJFS%qfJ zSk@-3Gj-Jn2IzuAl;6nOdM^XzqTvimHxaS0L{PrRkjApMG*=;T1LL@6zrNtq6a9hc zL)>*e%r&-_=Z-OR)%u<6n61ydD8*Mje&63}%~x)tMotAUpv@)_onP+SVj#ArOuu5b zFo5?~%%n|zG0z5nIM%OFsCb`lD;hrbF0Kil-u`@6BJv{(CQnroPHmyQ9XTFORSsOk zh(0jNAPZh9?FR>Mp?nkS{MsY865It=_4)@T(~Ui9W}F6SVbBC z=C4#u=v!UG&k&1|_EC#cF4EYL0ak`(t8vU8IQBcknup}W=)&?Ci(@I`cuQ`w`}e}G z0FR`lO6s!_iTQ)v_$d=&@?@U$+75R!T5*r!o?tsP>Zi22%&m__MY_mJcDfPJFw8In z6Z&Hf?zdE$4K42wt;WxsNz9KC1wRd4M)8F27Zp{7r`hT@b-0T&9p}xsZ1VM+h_uwY zUSifwwn&0qpA!u~y=3Or#PUv2-?=UvHt0|6e^2zHL+nQ61lFc6>OOm;E4QKGlPb>q z`h(A1*ZAd4qR(#8Ifw9UapP zW^i1r9C!nHBQ&B7$9C;gqtDo+ZK~*$zsuY9 zz`y+)QdDZuJZe(jBgi5N{`>Bz7>W1fre>$l$|mf_|FfqEvd915E}YEBE)Pe=PUjNf zgnqN(Gv&gqZ@J3w@vv=`62i0;kuMOmMO#;-0XYw$G2ZTa|z!UVVogT-X( z{|tH!oDG7kF|hs;!p3w6EzgsQB)LtWiRe>p`z{jq<_G)Z@PYJ_kP&3M={q1}y8HuZ zW&LmLHd?^(&BlM?js#_b)%Lv9Pk>#$6&jM>p&9ny-*FYa4|Q$@%5a?jqUec}N>WR> z)NLtd`iyP*f2DZSDu}P?QI{9YBNMy!cqm$h?M1bLjB}%$=bgj3k9VA>&W$^;q{6fA z)MyhNX9djaN>b^Y z=GCJ}sGw~BmACJ|!!f@rGkfr~3A3dAyKor_5&A&^LGle|380RD8_)2Elv)z@Xgx%9 zIvsUX9X=vkk~BU?5s&HY62AYm44mYf3)pS0>TcytD=*|b^vW*nSq|wfSDPyEvhzIu zwXI@jxFrez&HSH)>1s|Rm1+d^X@)lhrpa4_I2wTG+!2KN5|Ub>p%@{n=zSO2HM4`~ zxIlFoR2_f@fg!Fn6seoGt|bc`T%ns3EYnCh!EF7XM^wP5_4?V=dHpt(M|OYpFm1I zZ)~bG&#@t_6M}gBQEGDjQjI_{AZ_?N0#^#n z1xe2?p)8-_c2C1$OYg*UlgmrYw=(EY@3!@KjRlKf70n})U|pEaRG`uZ#@q_^YXh^R zuD|6C{C0pT0aJUCw-8j#r&H+)9a(4Jpsv?-o(S~#Sbt9DiVk+9%EDF0Qw{SHcx@-aB!>)1{SZ_-f4T(zi zEHE-udSbO`xwI1=7GNpo6>Fps;$%7IjY3Tt`gO_=!m)^x}=rY(S(9HY-Z zmy#SKJ+VM(U|d3(5wX#gQ|@C`*+b`4Ah)TfP|MBC@6%pJhEIdNll;qH_KLDX?{0OlVRtfh^TSb_h000?*k)xaX>-j^Ss`Vivi0u_mu(lr-a@E;1S zH{RF0E|z_NSj@$50X1L_Z9z?1ZESn$okxR+EKkP5U z??*#yul^@LOK7(|s9BJp#p9!9npp~t3H!ra4b1Dg4E*s>8ESW~2)@oW>^tN)`eLwn z;8`}Z{KhNwApO~9Q%tY>ec5;&VdMBKk?Don7CvfK+Y;I913kT7a-LDg>pjNL;Evq9 zl)N%l*_(8Vr$h0?mXnwbivgd1#;%8e_NfK`mr=CW|?k zvEoVbOl>}r!Kv9{?>86Ejv1)CE&&t@Tsc zBcS#Q<>Q!vxm9QFSz(*k`0|63thK&b!NB#;6PW% z+j)`ZQhG*6BLST`R&d17iX6GC_2=HQCG&fxs`JOp^e^5Ot~V)mLo? zZ$5i5cvLN`Lkmk5>rwhP8oE+OVFaQFIAJV+yDGUBRQ`28HW;8PTBa;aD8Wx;^?TT3 zy4euJorf#eN`Zg;if!d(|0U}A;-NJk=x{FN?(15qT z9-^Jdu@-{Yz_}fawnSj>g>Trq^`r83D!1~mIu~XIm2hy}iQkcHD|juXZWi=w-kq$e zs~}+M#K)ohyN=cH=5F8)LB()8M!-_Gwz~H5ZO?DSuCImB$fLTZRkx)JDPonj=<3+2 zcjo;87;yLS{4+jZ3w%v-i4$?@^v+`14>?Bk%nqs-AU-i*^lwp7=s`G)Srdblo<2*I+Ri1oL?>sW?RiEAg(G7yrF@_)V!uDEvy=z^t=``k=G z5288$>>}5^y`$mvEEd~6yDj=NF&Ibu-&Qbr|A1`7{ks*-^Yp`FULQQI`_Y(7ppses z&e5lJS9!DF0s>y=O5m_5ri6$z>=O9|oo>-e&bol!W4_1VlQ3K~ zR!#cnxhI3n7@Twmq7sr>&7cciO;lzn*HF~qA*LJ%tF?;Qu-mX&hm(R%i83&sr1O|A zB{ML;$rO*gCztG;uu#D*N}ppGwS}Y+to>v!ZFYzE_M;MS;KWyL|AYDpzqt+WKN#uy z0&XnNYD~`@(WPvr(x)UO5`pbnNn0 zuT^q0C!7dZfRd9i%|q4-rbO4B;|{hBiCJGILkOV!43q^g&2UZjDNT%t2+RPIoyCVrSIex^ytlm=IE zX(1g!|3NwMcy;W6ZG2%sZ?cAD9OcBNhxX!NQiw~4oQm%JitcZUMjt&kXGE(k$vo#7 zTULde0X!`e%fM5L;*|gey^>?H86RT0<8N{1YIv2V))~nM|H1&RDv;U^@^Cs=_qv!) z^y+0t%T2THaDj@k3rKzDNecO0=!_eSDfZ#x&kgvxt0-{t&Ev*|0=X&|5bQ9SpMoWZRLi{1$GYi+sC0E_ZxD|T>V1RdKFvk0OX6| zjtFTPf+fS!Kke2AL+5O^PrZ}mORN^?5`rxma%{{QawuN?n)0pM8S-1`&~o!z#v^BU z4#?uLJvqoY@fbl!n|5uwz3!3|AwP-B9Dc8~;CZh+ZqH!_kK5Wu^}5D}xiiA)@9@${ z0wRNUGdGP}@W;U!_J_w$(OvOR2teNXx1o&C*|XES%rU;INAEvcXKXr}BS&i-IUU0p zWAEzm3S0Zw(4XraHdb9U2by**#Fg3tyY-oaURARd3VQq_Zo$6C-O);l)VJ5lJ6?8+ z2E)0`0w)S|Uunz&RWh>v>ZUhb7(Gw+Kq5cjB}sPPmq)YZ>J1K?3N@5ffA*Ry_($00 zFMHP2zR4*q(hzxNKt+F{2E7m^0e+vFQ9k8V)<|sis$pA3V2W}Rjx&2dW6>-cGk^mT zdQ#BNaZ_$|lE%d;JB#B6zE$2~895Z~ZpO$|oEUudXFc9V!{*+NdEoto&E<#|a0vxM z>q>{1ye}1OvavkgrDmjIny}BOhF%LiUqT2@0IY5Dsaf-;MaGBs0LSCQhOP40kACkNuBqFhT25YfkH5ZZ60Un5f{@npote?*t%1 zUz#Y}t(lj2anR4k_c3Wurs*%stWrV}MIf=lSGk+)zS7j&- z?~qvJ+HB@SeF;O9;h+k$$#i_#+9m&|{()p#u2E`I*%HFhFWl^=VZDF*|arVU`Tt!u*tRxxdMfcMldwl-MLYlF%k3rBDD4br>QH9i4mZo2bzMI5v zFw4ZIPg4h67A+FA&6!!?tuB>JZx>u!KSP^=F^N%4Ug=7-DoW9vFW|M&rXjlb!nsxFxD6GO+Hmyb$lg{{ao=6>Sc&E6$)BtkM2Qt0lDB1*P5&&=qMe+n(be z0{HO;`j0&V$lEcl^ifI)rXsNfqmis)L4wu=pEzgbHAj5UW&Av6zcab2IFP=|`vRuu z@`FU%2Y8h#H8~BMiv%3sExWn+SVBnpaHKzF*_A>N(0_$@354g_v1?8{jJQy1SqRJwk5; z5T!OcDp{A&zn6p7r(M_k;bxD8KUes^ssFb;j)hZh>2j%iUBeNZwVFdo0G-CDNvR!( zd#qm9SNWESH$hmEST{gpP#PtU9aBnzB;zzX_lh|m4_`F#8Q;MRD7sTCR*>8TsO3Rx z>F(+z9SU_P^_(9o*N&*`5 z8x%(La(dQKO%?JqE+F5!_VQqJ)E?d7BuJNzy{- z?R5Nvw_!96n$j7=E8b(b~q>QHsK+p%jwE3srPvh~jelw zxP?|c>W!qg6#_(uL4p=avb^NWP0zyc7$)jx!o^Yb<9)rl321N}XS1cIsJX=L=grYn zET>PrXSz_j=LGmYTQyQ(%wJ@N0qx_a_!&CLz;g(#M8rO7B55KBuS{hi4-X#$x8h6`oe6g;V9 zxp3Qc&1laAMFer#2}NWDYx9){)3nBk{+6%;yAVL~+Y~}6TEnQDmV5aVnRx*m2f!4M z2`MB8Zb2eW8X8R>so}Ch19`bYLB4CT3(#vJ;(~wy?&=Kce-H{CQ1Y-b<$Mr^_DrWD zbj2Hnfy&VMtAG!U>jYB+rQ$UM4>;X)S-iK7@e8 zMVhL3XfUDlt}x+u{E@2cUX@znu9EtR1b(TaY_=v|V%3cBe;t)(8(GzO;LLUyeKmUG(u-(|=>=iBR4_!4j? z7I8gHP0j1UEWkVW(dJfY`m!%7!VOS_+XHmD6uvksz)I%x&S|URw^shO`)qtCzL?XKZu3z6|p`dB?`*p>9`F&HceU=i3Rg0rXISh1nGs z@j8FXMn&#Z1+6{;jCI3&tb1hvW3XOa*+3Q z0d5I54>fOc^H(r8+GH-W`PI^T{ErKv0xa;7!iEByYX_V80{$uua6mG{&1|Y`gY?PE zY$^^04D)~8&0OfK%H{Z3W+2&D_uTPLHnprI{rh(&VBiuhJ&3O4&~47yu0t+Mg* z#~KSL^9A<*=aQpMSijq93qTd1gAWg(`dAoX(XgNS7?qWFdz-aLUfN1pObjD~*-6mB zABOX_6?A2-?uOqZ!-x`$^Z6k;(A=>6H(4|=8-5>a5<)PpTY;?bcQ-|raN+I!>KHfd z2Ix*m32JI??YZGr2cE-AVnY@kRU2v|rXuOU_%%(PC0rzFt;~ z&3y-g`||gi*Y_b=wxn~W9*M0K%dU8!yKBmYo{BS7qqOQ4l+hDxMN`i4U+JTJIh+o9 zMc1m5`GA__wGy1;@v9bIv(zI@b$@nwfBO_4A-m=}3L!2Yszgk!Bb$KV?9d zPrVVTqHA-CY=6^M9Jhm%q5mwLUSwHbZm52+AAJkiknEVqLY$PFj^SvJhAMg_A%z1y zDJP?);@PE&EOYuk*+y&vDB-JPpe}FQuH)XI50C(E3>S86GtoE0U!s4<4%M%*v0n|V z_K1=RQhZLDe5TQsyF^)X+c^i8b!VBKJbLL%UiIGjNS0nf8vYyE_HJZUy?4|XHW|B$ z#d1P{N7vOJ#yX4sLttM^QZK5FEdGxD8xO@Fp}^>@+>sQZ=#Q|V#kSt03c)hwy5NNr zSlSb2{j0Jk3_ulTqSFf&uNKEq6O*d5lna=|w|CE#HC5UXY3noT_LB#?Yc^--53mht z#YK;coo*Cj)<*wZNTAn$vW|dLTCJ%wpJ9*I6QFk!{2VQ5(Gr%U)#$2lq16CE67W+= zBrfgD^O7R9G%5WRl#C}XauqMU>69RDf`gN=YmCN&MW746COh)2vF*+Pht*?@YPlA> zpj)x3vUUj4W4kPzP6&|CZPs*0^NOLN8xfYVq%wMX6xQ2l=T*Cj!;?+t` zDMRl2yAV|BL$PGSn|6$~WCHxiX$RcyPg1^k2!KqNLy!VyK(qW=(Wy0CKhphjMDl5cXhag!xu2H>I=6O*@`CaHVbP^B& zKruxmfH_y5FKPsSp!o@kk&Svv#XBq@QiBCBoEP8EC{|seqPhYhRIaXWb6yM}X|9lw zC&CAqHFg)*OrrpbVo-$c5)YwgkbA&GnC_nl^lT%^L28=LGp7?a`Z0-<5nz-3th^F| zV|(G#zn7Ovt>p>x_dQey!ITKqqU8ZVnJu(|A6HA~Hgw`@!Tv-Ufg;jx7`Bk}Pi5SW zJ;zNZA9S}^#^4Q~42!&Gwu{Kl-&^#q%p^<+R;GR3nj;uvB`i1ANL<}m8<4go4WZi$ z=1xsI2}EKQBm~g#U!o=fZHjJ_{V%*siD|sCx)4sAv?1<(42>=fy)3(_5k?b0MvDGQ zT{o!^oj0fsp)dmnS_Jczj;}*#b)Fw>N|?~z8ASclV~sP0s~vVNf_9(c8{tQ_lHCq` z88-qwpCdizJIk%l4VrfuTn;x$74%Ni7kw$;?qa`1YZ9Jf^;|DPI4v3L&zE^3)oXNv z_aStH8x%?U9LV*{{+rbpx5C2#Eb+GcZ&?bR;6p3zB4_-3zfJ;?864C9EjG#{a*t_g+5S0 zl0KYNVgseWZE!prrN~}%60|`$`=7SGXa*3@UVi3OM^QDHCS?lzsfD%?3oSdu=M%;2 zBxc^-FD6MeILT6mRBVhnCz)EL=B$0qE;H>?K?ka*RPZo<5HQ-NMcbHlmZ^DeK()F3 z(yg+=ST(O+4gtkF9(>|RY}|AiD+i*ju@%*b+jN1{2LDnAO`WPW$d$ce&;mFdu$;3- z9UyEu14yU~Lm7Y>q+RtwGYAOwjEPn*mCZ&)jQ7*TxUHTxY7sI&`;y*Y&uYI;cK!}J z20SdJ-F!U){~cd>0zZIXXa8;jzIOh7ezx{@fBl`%#2wX~59s~(r*__7>EUV{L1p55 z4z#e18LIqcVOo(yV_y&{RQZv+OJu~)RU`5RT5^7Z<|KWf0d*qaca3MpG!p_uxuAi; zNwzoiRY_W-#3LZRaPeNo9g0M}++5~o3}P}(a~Ity7-*J_Xy;z{hW26R762CncSd6E z7G*iGA+eH9?StcnMpzlf?T?d2LagG%fE9xVo;{)ku58Kvn~Gv1BhH7d1m06i4^f%|Sp-c!J%B2A zN(L@~_I8}4AhbYO2XxfW??9#y9e~Pd8Yu*TQf@LHu@6(FWi*J~J3o(7Tq0T&YX#iNM9V`Z6BVn3)QJAK^`m=!!N`Oa> z@j@6Fq$Y$h(usAw@!6-LXxEA@=rjAJu+jNQvnae&2GDJXt^BGpv{HpCD|T}ForXCL zhY0wg{$DH^cAL1*69g7m3JDD`pkM*KCiemQRKXzt%-LrE0d+8ueC`E=XWG_(nxAXj zK${g`P{7yYEn}p0z@BT86Xi~qrL7CoWHQ=Oft9vgE$=1Bvk)Z#W7(ksidwlBWV0#**fdDUMs4^kvO;fD=fnn#*u7kERaL8FzED}I&1pw@pECj@cMhr)X!QhPz;mQeM zn=G8s9nslwk)t_qxuj!vs;W+MuM`5_?k^*l-0mt++vRPZiDiWF>HnVup0B%@OjyC_ z__He!_AxMFZ|U+gPr5jrMv+JT1&(L1i4yyA2jG&?ah-Isy()gqzSgP2Bc=PO>@R$Z ztRh3``eq*Bt$_`DWNo8`4O?s4-$CP#N6IRzonN1`tgbvIb;1*4of-bzWKL-*nD9L$a1XwIUth9skhS{TSyU(j9Ex{+ukeNxlp>j& zmRK#2gZ;pF1~pfcF(8eNag@o6Cb}Tu&1*@whnt-O;T{~uqGR#<5cnh06@JF6?pGMpQMNxqkrN;Fa8HfS% zOgv&!ueAKOw#vZBj>48Jz|pVHGPn!C;jhlfi^xD}_UlAmWH1?drGg696IQTzpAr#8 zX5?Uqe2Yzxuw6kSgE{CfT4E;%VwO*MC2HI}2(l{~>~t?FX2o|5!lWcvu& znMGQGIVg2$`)3!|ix2Wv#z`NHZDo$ePA21*Y!20hk2=BPNl-L zm*LlAW(7SUAgWvD$6%uQyCf1As*aR{L?`J9m#+B2J{rDy4nUy(fptcT=@+QKM-5Wf z0|e>=x9)W*oMb@_KG>iJAL+mug*Myu@A-iTTZ_=|uBI(Sviz>Wo zwSKDMG})Yf9+$OysEhl>RKU7CPB&COn^Tr2S6=9i$_%ZiJ(bKiKAV@8XIEdCjYl5x zroNTVch(%#P@Y_9&G(&#^3HUC&V(s9dCABJc9c=fDLHa+zKG#qxlWhfkUiWL|CBkm z)K{AnE}H&b>|F3u2H#R&Oyf=%C*^yM8=Ce8jK11D_d;WFc}HX|&%JrJdxCE@EEiWO zCG3`yxK&3~l$$%ZR^sZOY;ntA)u^XrSP25*u$ZG(MMHpb;Q~`|I+%NcDdwCm$hq|b zLEf!n)}`WH9^~DOLEg>Y4&>b&K;G@o29(t$Sf|#KN8N-@HDU@_-qt0JfEFQ-e3Ro^ zw|!stRnM(U;*&tv=@Cd_n;<8V7KY3>>hEKD;h(i{8w$w{Ef{_=Y+ZHqv$b#NPhA1h z5$GibHCPmsHKK%Ayp%Rd>*$J<2EfQ$m3i$@2&KaPR4)c;9YkGeoqOI9m2%w%iK7B5 zCR?ro5gbvRMhW9&1=1vHG=qX?$O!jz6o_*RvKYYB#*DID()?3ZIKLK^hrG78tvClm@FSsg^okh@xbl+??g~i)?<|CD{gQ&`%;rOF-S+lt*VN(LA7N;8H}{ z-7+niPNMPs7jiR#`ct+3d_~5@SKgEu?UPEtm$HnfU2i?o+1n51H4Uu4v{ot6;b3_h z%%8Vrj!FfSw`T>8z9qpI?Z9w2COVyUTqj{-<@HIEq=VIK>)D!Cc2xh}znqN?MUK4)?l~GE$9#_a0?zu4 zc=L>4IHS;=KJSNL2nf;UKyj(60Ile*<0x-)ZFG_hHO`kdK=uiS2uKDHmO|#s;4Wf~ za^DApXY2eV50t)VG{zX((i<*9d&p+_Q9kRYOczA@Lq0uhmVJ5%GNKgyp?_m)TW^p8 z*yhg&m~(ZAP^w?2ID*gG15bAeTj>HwWDGz-VEeR0LXo6~Vw0Ic%cQc* zA@HjKB;FjObjk_(fH{!{ znW@sW{$wd-v{I2cQDZ|{1s^ygaH1tWfCcXR@3P<>4P{B-{wq#t-KI9Al?Ns7`Z5^$ zB?DRC<}_`)W=dE^N@vfV-+MCtcSnrkKo!Yq=wQ_)Ztf3t;5>P#tJtkF^uvfGmooLk z^yQ{6>86QFfXkB3HS{fN(XKA_@~W$($5VL&J?xfrSRM^`vNvHwP9V+x`VA80;_r=D z6P~ZWS4eN>)moDI0l0nCod|&{d9s!Z ze-fn`$z^3(3-EV>4ZIG)*jgEYqur?qk0s$JlP`?E=UR;9Ae?mpgtJ0{a8_CAJDao| z5YAc%!dcT(8f=e%_rwN%b&|RlGgy-XxqA;TP>tPd&&(5lH*;+QdiW&?`igb#yV+&+ z;(&Mq3*Ocymho1cth1I#AkA=(t2u0P;xHQC_*-e!sSRg#*264YarCa z>*N>Q_Apkg&ScY$En@Mj#P}dhND%&Du!}ug@}Ry|@7PYq$v8;pfc0M1*p&E8kDe9s z--x|KGxN`c;_nEwpDcaNv#GJ@`6Qp-wZL(V=!Ce7iah>4;o2c9Whf;z;UzK-l`?ko zV6uN}OQ`KxCaNYu5oGh)6mO)VH3{bae@>_#1~;a5av(InA8={*B+zHxaZ7a{($ojC z*$nu^9u_~bPbQck@(Z{m28zY;?=mcMBQMlp#9m3IWiM*)m8G}ybQih*W_q4PSOhM& z+*5{P;$83QGNz5u6N%Q`@Exc0^Kzznw^P=~8)?RoOfskgD+y8fu&4~Fm^9xgLm@nubnylvY`S4rg){o>4meGH^$O!ZB`AJiE z^`0$S2{^D;C_NHSXs+7LsA<__YQjKMrVPlenaLy0h7}S3%tBq18M${_`7g<*23FUp zIJfxX4ERtrWfTfe*M->@%0l*LZW(Z_pxkmPX$&m!eL+ES3#gp z-4zf=qH%^HB^q@8%0>vc2}oKo_`~eI`=PgeCx^UvC@)wlM6H=IzL7l`DF)6_$a4HK z)0O7!yh;SptN$296IjM$lmYk*)|P+`$Vo&roa5IFCm?5YI=Ekg?&>r&&qG&R2pxsH+ zPa=w_{wO{p>V171gB%k#NLYW**Ktv3AR9PmhkWyJ+evbqq{=|oT=&@l^Qor9(>8db zv1^UAH2=jCNS1T^u=u6Bk`W|;l^1Y9Y)hXA%qi&#r**1edawvc+6Pu{CUs;sVfy|E za`2(OelM=p4{iCGp=WF>1~Zw5E^=TMr+2bPD>qBnbIP!M%OY-KOq_1u>bw~E;B$(e=&fAU_ECqF+-1mMz)qAx_XV4w3Hcan5qYcKWnYP~gdo;}Ef$GInQvzq3Io z&lrwucbYt400r;8DhDW0b!A_9qN|DW;8dkn$z$xbL6wEGz&QzT_hZ2O~;n z)}|J{1;8;UJ$*cG;AXrzPggxMh>d}_E=7=!d9WJZvI$KhE(&Z>kA)A^Nd?+?ive7< z|CRIi>LPEploU)YvGsyY^VTTuJ5~> z+3`%1b=w`AcUThpvJ4>18#f}7oTwsvV1{f9BO7&*m6<(H-60Fotq@`QAN$UQwu!hp z^Gj*gVjXJ5E^G60@X-@Hz=t2s=Hk=Grw_#y=%{17Z&@dj3ws;-=dqaham=mEIWL6O z7+y1}zv?DSI zZh~UA6jf1MF=RVRQ9CT!;MKw&pRXbJ{eI{$2;20eG&i8Kw#koOkae!TxOJJRyhnx@ zm?1QS*ip{ca@D4wESGM}7uT2&fG{JMx;bzl)mR#CNgoGs(rxr8NT56>H~>d!=seJ< zRiP3PiXqdz4{0mbA)k3=g0_@WpC*hZqUwR!UPpR2TglNxde#yA&@!T~+AIv`&_sHm z7hP^KO&!E5Vr>LgqR~tOA6W8C9ax?Zbja4B#0*qZ!in~d#IM##Ecu-ew*vSb-9c+b z0}UuIbb!y~g%OHfEmyjhNe(yyX9K8R#<_86pT7cyViXRsU( zc9%C7nO@3&HrR4@Gb4;ocDbNXK#jjWV$Q z>t$PRx;^)?w^b=zLM&h;7(s4*Qh!&cUVf)TuavTyh;D$jHF3t9Y!Qw%mJDr~w~J& ziuiz@1!YI3-u?*j03z=HJ-ShTZr+>ko zG?r8~=7yIiuFlq0B^5w>rvUu~bjNumZPG*G{`-)B^G6fD+z7JrfZi(m%*_uGibJ9+paiV}2u&X#` zJ(pIZ@f~i=53disyL=E-4)NeDSC{W)RfVZ52GZpMXsGj|RRH5aT5I;BD4ivYrQapL zbe5c?N^HprWH@}zeB81-+cTmMmpgqKmn9;yvH2hwx@Gz2uAJ)S91GvhhwAbjvxwp1 zQ>Ces5p=aEaihnyicR?U0!RR6M9$#xl-SF(;bx?wN_19!mOiH4AYsj_m&=S$$LGHk zj!&BrqPQ`5TH5Ns@QZdGi{q8PfgnTHV3ytv$kqTxCXv?^dbNj2J=iX@0qVT@)H^BC z%19USm6o7i8?OD;%kM`^2fhBmQ_qO*zPIh4#=gJa{&1uD&zryG=>HuLXqGuzV-lz- z!uCJ6o}Qw?Q7i!WJ(djupUi%%&z;OWEDs_hp255bK&kx)?B}S#6;W^!ic&QFS)?YS zQrP7)@Z#3EqcP>o9#m_B#P5yIJ9B4z>GSS{n>E1S^5;hwE)EH`pzvrg;4esTQnJ*{ zyGh%X$oo@?OOfR(Cr|1zCY_izIrYLPGyw^Wmsavr$rtwTpi-s4KB1(hJt7LMSeQa| z1lZ`MFitlY(D(EZm)bK7zoZ(fGlq48{M3F_DJM1ze#HOfrrg; z@{OBpcVC~OW%|%8!~1UZnVzA3=Eg+Y^DbcvV}rJ#EBcDniu#rCMQjN+;Hg3Bw%`@#z+$I$|Y2G~wj8#86T5eO|m{XPaY=-VZ$^i~D;fA3>Iu)7H!Skare zK=`(%0)6Re(DT0$>BDRq9=Ak$N6fWd8q=T0L68h%wvvha4ruNDsXldP4J|^rcrsSQ|GURrcvj&Q&(&OlTu!)mKi7SD{hmv%_M>|qH~zf{H+`gc5voL zDPFXTA1zV6C>OZcanJQ+t`s1#*>XInl|r>_1B`%WafZ4uv(e94UG4&-<8uP_`;R#JYBvN)(inW%_LT@Ia0CvKduRbRh7p>S+yEE^UDWalu97+nJ}@)S_(eR2cmhAi4@2|&hQzPaj@n!0daeRq!8RN#<6m@RYZa-*kQgTZ zIumq&Y7NZ=YjS}m2OKLlREdIfeSgtR%_LJ8iC&Vw(ua?vdUHPG?3e;}KWZKMoBn`m);CDZn zd<~-f3qkeiku|bKKTU7ad$8e<^vu`u&1meZ=iXd%Jnr^3iRkk;0<8~&=?GJCx(Hjn zqvUlZ%XTt(RhP~=W=1%|e@HPu+vzh8S^5j$9?@Uws@)>Feg02^TB;~!ym(mAg^mQeojOtHhqrNZ~ zKpdM%ZpZ75WFe%zzF}5&6O(Kyw}JPjPeZ@uCO4>?n1bhkKE}TRA4J{~gW^&xe4FHZ zWr8HQ@`rT+MDDo{{|1CNnUY$@ruE*K`f{>fS1MC`#iL8Mg}ScZR{Q@_H)$IXY_4SW z?b2g0&QXU(4%L2$gG9)-l8?i; zWz^hSsYj81M@fVU{Nmo0padQ<`e^aR9QcIc^B`?05u`1#`PTWGiG+(*J`U2t;t ze0jD~NMj;gky$%A&(E0brGCjd;YgoKW(rlZ8_aRA_w{8v+brSFOjj#T_HgdbWNl~n zn}6Op@@f?hP}?|JU%T#VW9hhe*khzU+fjX`l?ISjGN>ZQj1CoG#rgvJ+-cG*k&@ztFq2_W1zd< zI?@Jb%~MVH_}Oamcp4HrR5X6*g=YIxmKe1wOQX^noY_priqGN(HeGdLdAw&7>=S)l zYV6OLILtG%*bK7L?IwS*=K1zL_{e$iHe~&9nCLskt}1vfv4K=F#bVia^Mv>#EhB)m z7|KM71zfI?3K1pH62E)=+~|xmHx6FanfLak9Q!SGW*uhLGZA)!Qvu$Y8&9D3`gRxvG@UIxMdLqgkF7cKqnaHq|FC-qe`%=Y|5Dax z_u>|2yI8k<_*JY>$-`(tYt36dMw-4G=ZUI;7Rp_5p}r9n0<0G;a*!hA&$uXpd0$;& z6dr!Wn6-6V>9D3*tZI;daieNG@91}H?g(nl*MqXiUCXV$fz1-#u@J_O+-?-eo-}ir z$J--Y!2G<5n)y!e;Lbgy&xY653m;FYm+T84H%_an_zc|4N0!VN&*d9vbyzjh^a#W@ zcWNGis+#`6!F!6a3ort1#3xD2zx$Rx*9M@UOr&K%Io^o08?v)P^>Nnpd4p)3~vLp&BcP$OC<(m%i84o`H zSQt&?oU|HxsW93()pA!mg;d&Z)aDxh4Inr0;m(RKk6y22bQfMQmwf2&Y`1o+y ziwy;`N-&k&Il7@5hfyzDcTeulyB`_^#OQCNNi_2Ee>s-xdpzStOiPtto7f?Jmxknp zi=y-@MYP!wB@YcFTqs&bnaljyI$!#cT~K$VG4jsqv&=Ts^PMT*Qh85T%~hs5&wh=V zeCLc5x&GN%#64~=o95;&wlkxTWIsFGllz3)#Taj5g)c;8eDV;*kn5=!uOI4Z7U>j# zO+^UWbA+m0T0Lc z{~;z}KwX#A;AyKS=Jh>g+iLK@7m3hooU}i zA(Jaqx1wmYWM;(r91m608@65$MW1;|y#G*KyPej}URCR4^2^;!vlH)p-h9tduDA(! z*3h4ci_dt#zR*iKG9?+Dt6im;(~b8S8X_l_=Jj5hyS0$Qc66{hXQ}dm>+|WWQW`>1 zoyX-*Gb02SUNuNI8&vw~+8IA&fOd1H*HsC4gnU%%?|E|JQ=RHv7*Bj|nAv?QUWollao|-cQo{;Y5y)Qaf%g4VtboYX|aFtoC9kCdg zEzIZMer?dsz#-UDX%L}u`%9GB9j)Vmed1TL+Na)p(NU{QHFkH5CI8;5g)-N5cH=l- zP9|-7r9_R-f<+~ zx?bkW(s7lBAh?)RKN-1XFdP!!9t)t@dc0!iG6bhHOAV(IhuBjgPj|~vB~shfSC`wI z8COqHMS8U!jX0}P_*&dxm(1hk#?8{iALKYvAP)|q1BSNA*Y~eVzwD56{S+3{LFj&7 zm5P!=WPe?HkwFB1T^5l4dpQY#F#M8I!1kI6(?jS=k5fQS|GIRegb4k-1fElY_bLuj zLU6!m3dkWKdY7CTFoQ#c0DWr6iT#E^BsHj#Mh}4ihanIYaGC~U{PT@$8t{$V6cBEx z1c6tA01S~q!~u44$T27e!HYovemmqR$uLMD9|Bg+{I^qS zz!m~w|E(49<`CrMPi-hIQRd;V{p6}+%MbspSJnwe%hu3YUn`MoarF)q!NG?14I-Uq=QIsqxPEef(L7Wzs#t` zbu3+NoUZd?_x}bwtYO^$4woK^Meu_A3(&j>z)A@bB*XslPm=+{0{z({mjSZh0s@%P zL8yRe1_%xBPZL=5PfLV-OENP5?pQEDIRk_j98^C8oD7F73)o*IpUzRX{ zRym}O6r936uW%KR1yad<-~Y6NJ222a$M-gWV2EhiIN4plgycnH@n|ei5C-8P;t1G3 zc0}`DUfxV+H`mwOFr_7G8u#VmY~NqL|Nb$v@#QyFavBi>@@Y!pqHnqU4Hur|o4tZk z7oMQ~B-s2QEf&94amX7aWC$<|EyqV_<<+wu0qW5?tD=eBfr++`Qr z1QQb{6A6>wZ*^WHrY&un)KG9CHbQX(JgHMK{-;=Lw@YBabrX0>jbQ`dyZsb#{4w4yN%VrZh|JidQpoZLM znN&gdhSK+XyM?XfXk@o243q94Ww>~${P?}7%i99mD#88mXwGV+H&cPgXzebI zS7vja&P2nAy9XQMXKWJ}tOYTAmLV1h5!mXdZyPI6tuj#7py<(jy~HRtuE6!%eC zVSOK4!$r5HqGqeF@_L@qHZH9?t)dO;v_^HJ4JiYt9Gl=_`ck-<6UV1GtsJ_~8m9`> z@V=`C9ik0-P8_2Q!YtHkvP+A5U$N7g3>b8>WY=zG7^&I$ClxabOzivqN142RvxQG~`u7xC{htCtanS_^n6TsWD0 zD16?zSi&D$aef+OITlysP~7Qn*%Q|E^;qib>dB;Rtgl%9%PoZ-af_v(*PO)bK%zTbVlPge`DQFXE>7zw1D z?{K)tnMgaau6d{;z$bFscl+Mq7UYi)Kg>6*M`~VuA3QTS;kVxCtZ`v#-&>9J+CLA9}h1q@Rx!+5elV9WYiuDPpYQA9YAr>6* zh5F^!0alR_x2qLp(gQNNVO|kj_0i*k%S9WfZZXvanb)RH42^VCnFI-pTp3wB{S2>L zsXuTOJ9dM}-IP5vOQkl>Tj)Z;%KM;AFxpDQBk69Or%2Ojhhye(#uc^Ji5CoftgyM!;WaC`odUUYgAaS?i{U5F8BB0iPPikmmAov>!`TE2ZTDEkyY+Pe)Ebozc{siOyy37 z`lvr6H@^ykDq~prgh<_eW1V1O>0@tL;)~N|8Q(3s$~%s;xFp2o#sH7}i!t}Sp<^Fd zBGu0!@MiL!o0n*2nt7xY*M@})DMaj>ZFBhU7lb*yzG#guTPWJLMjN+G2x!H1D}PEm z@{*w4*-Lbu{?P68ZhD@m6Z-Z?;I?$D;C!a|(ItlIerD47ONTwerNV;p<0dZ;$T8@( z*Xl~N=M%M*6uzyj!TS}x`qGAa z;~Zuj_54rAE^E4FJKpzs;+QU7XE@Alf6}ESj@%mezIoyC`2BfDYg~|*yZ7#Z3X5y! zNP3q%v4#4jqbo7Kj`1C)`IhY_LdteTa*?@O3@2u`6h&>J$PFILz+q#qv zl0h68p=qg9)E-}53x)^o(0VR2E9+dOn)U;P zPy0xGal{Fl6XX7=y6(a1Pp8l3EY_rT}@Zq9IP?lkC^SVgv z@*@S}$&+5X=k!+93A^+sXq<#)48SM9481@(+xVHLn!CF1Bs_W~J=51^*8Lt?#f%MR zExG#%Q}crDhVa?WtQ+y~%hfNNEiX@G-_T8lV$iQeOmbt)^yJ$iEpewB(6{=Mt)dMs z#OB3F>&c(l39sC_yse)i+@j{1u23l0lk;_ZlW5#_wZO+J{mg61fD2h4L&-V|;C|V1 zJm&;F^xjCBhMOAPmCyU)+7hS7BOtV_`X$!Rw9`vUFVXXU*K$XtYm5H(D#>NCn-e#N z)FR)zz&l;kpoQb#&8kI&tvg;=o&P44AP%Ue=5;Nr&3spw@ldQb9d)rTJNC3zFGbu# zHPL`L%u!lcUY#Z)+n{7T{X-WRNk4yvXXS+j-RpaKtEJ?x-%C5oVe=KN$ zexZ@p&thR4Z<6Fb&LP&)AfPRNPlKBym(yMl%fL%}Xa}hvX!47Itf0swV!_-4*8?SwV*gKJ4Yr|1>Vo2}qND@LJ$z;xwM@V=r zhQeQ2hl}kCMcbhbW!K)LEgA7m%^z1I-n=Qaes4S^{hC`^!dFT z4cwdXb5~wZMZR0Xw^^QN!{2fv%I$=Vd9GG79hpI!GCjV@BzNh|H+Ss|i7LivhDW^; z#!ZQvXYWL8LSKHREsu|ZpOe2m{w``2A#B5g?QIZ@2Jc5|r%cpMIfBS_(7hE`;w)Qi z9x43wQq#SUTp_k)p&{DXH+tKpOEIYL+2UU6Zvq(kdN)L;gdL}x%)iEbd;48Ip4~V_ zDfjVwMIo=ds0npF9Lk1L~#%J0UVWc#K^4)(2x?ZK zh|KWoXVQ?C_FrFEX`9`u6cn#HHaHSPE!C98>J>HFB&n{BVusI@7*NBBpvn{rqe40mR+Ctm;b#rsWv#*J)o4Rft>LKq(R!o9@DaUt1|Lob6xzKZj zo6S+N?kZGYRs^~OIryUW5F35)$w2g+8&SAfm!o6*4m*#lG@PzVEI4GfW61^Zx4b*T zfQuTrBhAip!!dZdo`YvzFD45mHKAhV4jv11a_BW^h(Hv*ts%cEwe=_F~;q?}(wChqB)3 zz+tzQ$uEpcq%XiML&LkaNrHXrHkX>ne6QcJWS98Ne$)WIqE2@v+G*v311EdNN_S8l z{f-eyjLb8Ww2$^LjKQaJRI@+)XfF(V{@Nt~kq{aDmY$@T=G$p^sd0tO_24@8VEOEi z-$>zim;>bU;BVKalx5o=E4`Q|h9DsDC`zITNwdWa>(q7Ql&J1$pzu>oWgkq2^D+zONmWc&o>JY5XB8`}*tjGn2g?iSd zMf+E);I}SgrAMtm#C@+{+(TTqK-Ra;9rS1{UF>4oL!GxP;dRVAM)V4%rMyS$=^6MG z6?Nc91_p+=?17X4RKYwWb6LIc{DcfK!AHyiPpFQVyowoHsinWfobgf+hV*@?O%mWf zhVXs3XzG%+kzqmPOAp?ecqAA1)VI|j4SAwg7@F4o+Au$J+n{?h;RWuSFC+znKl?a+ z3uoYGvI_5R(~e%?dWwwd^PH{uURvcWy)rZyT{*c^{ducfyIXXy+IfvRcC!~73m@I; zlZ^HVBXS`>uksl@2>z5s)z$R=TSkhogBuyLiR$R{!IhCl8-A5zifnmLH8HPdL^E!A z%*s|Rt9E=sHxlK!i9=eWI|Vsb^FbAri?^y9yl5eqLH+UL$x6pEEzIsDQone(sc8H_ zm5K}BoIKWXHTqb9&CL>awZ>v4auMTT5soD-&!Iy%A7INPxW5H^ZUfenkEof}IQv(KT6 zJMqkn%d&W4-bo85Km83%t-QnYCw;JP=6zkw*$><-luKUp|09 zjZeJQ6YH})My=SUMR$FivKYsxVwf!CQWy}g7CvWj_&i~O`x{5mSY!6I+MsiXN72IK zxK)VwvKY}^v*gEUg7h8P&7BrDZ?pa~=eY}R(+ZuwF|T}GD2U8t-|{YNyloMq!Jim@ za@BE#LW5V%9Nr>;YGs!0khta2vfMWKLpGi9omv_FXEAqWfihlYipuMe~xTs}o)!}wqQu_Io-RYw{Q?C+D z`M14s_h!rRvBn)~pVsZ1*l;|$?7@rmj^Ozm^O+g_F{B0v-nPOz0|cI4M?T7w{cv8C z>~+LdG8b|o2!czF#rN!v?xg_pq2cbu)mf7tn_8#Fb+UXYYCSfCjV^P!!-GFFODp<) z?x9AD5)+%e5Mwk6m)Ao>V75jU$|u^IgE*8B8SJ(O4Ur!a7cy0FufmLw_$88 zQaO3@(N@qC>j3G#i8rdk#mZlU9t>7w)PY~!c?Dhhks5OG`DyDwOp*OJ?#I!uZCVl} zFIx3Q?)I4(yeP`90}t?|V-G~SUU-)d;1=A)f=l|Wg2zyVwvw)S;~v(F+`X)rc)LSYxLLps^rXs>Rx}JhP^-7 zLw%*0F9*>;Etuod5PsSS$z^Cz7k)P1>UX$}6lg79c^K8*eUIpfI@WvsT@J|`@PWxx zj-2w$Z7-CX>jG|`aep~CUcg5n4n`H-WP6;|x0YWj*iKwZr8Coekui3MwmM^$cqVCL z`;AxK>?%A?(9vpqdon87DdFbSHO_`}8#1GxT;tx+sWc?M&@mRVY*f;hzwRptp2k-C zBLpecr`keWm=VI^iGLWv<;i?Hy7z4cwP%h$57&=PSk0|G^P$o1o2QRhsSu-=-v&_K zV^cG}Ja9LFhs&kQ!7q-9oKQotO`#1n_cEQ$`B&-G2^1YorDx5-H(%Vv<=y-Xt*Ui)*Y9hv7CN9-_c*u1aoi`rN7-?S2qQ&hSF8DeP)`K)GPM{86gavxGZg zx;}T?uRdeGCaXOW<~=y;U)CDQ524U3<_yz>)W;x8y^dT!E>t(HzHmN5)-(O%hz0Sr=t$enlAJd| zy@cPNTRv0$My9U(iZGNx8S0~Th+EMW41~6$?av{1Z11~9&ULs=BCI`&goU-D7PB9u z#t+zzK`7!`O}&G6P2(_oJ%KD2qC=ViXFjZZ$sUF4>NmIJc`>^*nnm4o{e<({ij zF8f+6T(2~tkJPijCAUiyyOV}Z{-i2t{`R;gq5&eP>CzB*Isw5YU{Du$7Ge2&S|ywo zv#j?>FaSd5#Rzph)WYM8jMcu(u2fX4tDW?Wu6z$E8N>M=axxk!)GOEA$}+F%%scmE zGcKkJnHa2X*Q_Vl=JKq9ON+Tatk)%-S}Vt&iHo?G4{ks6p`N3#D{R>X28+NKN#1`n z?PwJ)X>yovfZ$hWd;^VS=KV;T>ge{UXRR_`W?5}_zrKfJaQBnr%iQ|yE9R%9 zttf>ZOxBCeIUPAcaIPr@?;^aiS;Z|F{n}y1$;8*x$K8H z^2-w!r#6=*nqurUQm2w$OI5r#t0c~*TK>n3k29EMjB@r4!>-by80$>9a4l&Cvz z?nRrZIz?m$X;f^`5?9X9JCG%?*oSTB33}1qQ?dSLU$kzGPI*r8owdU?NVTrD>hAli zH>&xc@PenRR5v*+6!Iog$0~?29+E#Se!ULO$#ZFx(}4?ZPZpM4-Q5^J0f{2&&sbx6 zWNee>A@_xQ)Gg=Femr)D%h2Q8=c&vd;gFMq?fJ(fUdS@w?B1uddib=uT{9T!d(&-} zeg9=!ihiteq&g?=%ImRGhj~}@gCUt4v#GfuiEGEtGo`vml-Sg&JeZ(V$ zhlk<&E9~$6&UvKuW+EE{7%+%ncOs&mj9<@EQ7lr(A32p_#qMUudq3>hi8fs8gS=&ydgV>oz+Bl`=IP35?T%Z)o6|o=X!u88!>h9&&x+1uYxpc)%_?iT z?g7sW;fD+GR|&id)kl|GgpMsK@n%FUVlqy`LhN0P4$*RLZ>HSZmRapQ#H~g2*H=i^ zO(~{O7>F-g>wWu>`c#fT?H^T|jr!`T;&cV&V>D{Tyc_4{X_PFltL9s=RW7~{j$2v#tx znAlWcrh&yhrYQXj|ZMh=T$QZ4r3VA3Fj1s49q zpETQ|7FJVTmp3%qUAnQFf(Q&n4@<%ApEW;S=y82`h%pBN9udulqtO!ZuRxcO30ii z!7SO9uYRvfGb`7IiM)(1u$xin<8hg9p`U9ReYs~mCh(GB=V?DW;Mm3l z2SYa+Kc~eg%TX>(-LLDr<)^xJ`+u~MdJKnR`gdY&isvU#6~US{PaTGm&5?K5v}KV; z=kGN=cD5RP)Uyk7sTo7%70&d0-PW8a98St6MvWUmzpoVMo@rXYnh3ZReq__WmiVH? znbcAm@Q6R#bfwEp$@hDK;bAossq?!gVNyK#!ED@uG$0Ul2iQ{11%i&nZP9lkn^ zBW)e48heQ=(=@tOtCGw9IN;9G#(JF13E$VM-N*c87@_F&VMXSF(9?YB2rk_xr-tSwa~bJ4)7a+Fafcn|9sP5GXiOV?sF1=uj3%eKx7yS2j~MK z^dM`Yd=ye+hy=f;NAaT3XfXr=hX?<_@S31-I59jLg+Tu&H{(U(cu@r2-`+y;nqcr~ zF#=u!jW^>(<9SVRD3llyizDF7fbB6zD+@>%P&@|`Q4s&J4DiT(S^*J-`kfzX7Tf9+ zfS^}e-=P-2uVhW`C&74HT~1YGQ|GdbZx4B~L8MJGlxU8O3+aO1TPTj6^n~eR&t5z; zJo-WVSx~Ll#o`c;a!=?FvVa1S@R!&V1I)ShbKFce`o*o>77h-w1yYly-gOoe!)!aQ zT_2!WO`^B^p}`)%n&eiu21TzM3v9~yej0|%rV5>k-#@)vcY=t~_hUI75SG2Et!o~m zURP@`b}>BCjSKF*LG5ivVe#^=9;;N|Y`GY_(YCWJ|IGXB2OVS6nZqyd7Kj+KoP83@ z|I8@%nX1hbH>ziWWp~RMucU&Xqnt|@7mHKhX;HT5#M)OMJ$c1ISJ76j`^bWPzIyP4 zlL{4O=TQ`uwy?Icds3|0hKxX5+1%nlcIsIT(DL^YZ?LvTDW2T zz>~cz<7tRCpt1td-B$vZxL~+a=@|$$HSl8%q@1WtLXHDJPQcEUcC0|e$N=MYP)M;3 z(I>?Nq!b`g#D5(^N(x35NLOLrBec+$KG}fqk<#uHRH(&`?35*V@faXm48{ux+rZ>F z@cUs25{8_et}EJHyXbn^<+`&gFP>1Uvk75>9ogrnP>b)k`$abSFOCR^Vt=Rx6VSx` zlb*v0Y%oEq83_mIGlXCC83M9&kr^6K3i=MRT7*iUvqJ0tA9sdufICBwD2-!>Hu3_x zr~a|yPk`1O^Dti@(A>R?=;P^IKlu--!; zJpbf3D?q^pYyK-JP!hlh8w?DJQYv=XO;S=!DVT6`|2G;?2crSJT|jmcVnU8Y;r2>Y zJ8(xIHhGT+wqKmIfHx;#`g_HRh8&4T>?vSN!8b8*AV~yN*(*?`V1ar-3T%nMXn@Be zu(N<=2t=^-s3?q_bT3bi0cv6}J^FoI_bL(+903wV*sDpF?7!;NU)2ZztC1<-D-Qc| z^jE?9*$({+qu-(#js7=#M0v>2`{}V?3t53>Em#@D!Kgv+l@$Td8wucxHmnM~d0>C` z!K{Uml41613M=3u1LXu(wZYk~@_`DGp)r8G9*l`%-w7ERAVeQ#@((EZJy6;JcLNX( zI1lVNu)qx~B|{ScooR>>;A;Lad=ki?@PYN6iW(5If*ty^zKfCpA$vlwzUz@ACD8lh z%G#^&;9vi_MGy2`gyjGPdI$qR&kkkUgIAXxy&t2$Dn3#I@o&jL&9M)_Ulkwha9n?c(u@c~1096h! zK5#ssNnp$Y#(x%rMgG-ZLIQ;2K%W>C9s#!h!w$HG+T*_dA;W=c5`c*#jGqI8mB1Zz z0gu37u>b(R4aHz_ASbqFPZN(v?`;YG=!k$uf;qaiuZcqtFaW>PUL%|Y0t0CLyu^V{ zT-(3IBM@ld{?AJko&YpB!8D*4JQmDUCTAEw5rZe}P518xkT}%eGl3z1_W~Wz!%H9! zwnbsUSp-Ai_x@0z-x3GWAm9;bus`AfMZzAx_YWC{@Yi&skXZCyf%`)bivVl*0T~*F zLW3dryPgCB2a;OTp_*)Ub57%#KfqDe& zfs8;SaajC;AVeea=)dX+5-)+A;C=~8shZqzF@we=+i~z@s_*IaI{~5-D!FteREDrNm z1Y_~YzdXTVv40_h!x9eG3NR3`e`XViKN^Qe?QKW@Kmq&+jy#AAjzIVeRy+drR{-M? z;K%p_{o!$#z3t8)Ht;wcp7@s#kN9ie@py2JJJ6VbLf{UfML?m^f6H*-tt@{UCZNG3 z^QVmX@4OSxn16)7$?$(cO+fz*D+m_uuL0t)2P-BT+z=dCu!vwi!h_4xpCgn&fYWqf z)SwJ|&^u5DHa^%Ehd#JJ0T&(Y|B-=3fcRHq0{DNJKi~qF670e12rgJ)QXJF+w_yj0 z8K{T*y_j8hwY= Date: Sun, 12 Jul 2026 15:38:06 +0200 Subject: [PATCH 017/137] feat(tenancy): add Season current-season lookup and year name Complete the Season model: - name property renders the start/end years as a "YY-YY" label (e.g. "25-26") via strftime %y, and __str__ now returns it. - get_current(date) returns the active club's season covering the given date (today by default), inclusive of both boundaries, scoped through the tenant queryset so it never crosses clubs. Rename the tenant queryset's current() to current_club() for clarity and update callers/tests. Cover the new behaviour; tenancy modules stay at 100%. Co-Authored-By: Claude Opus 4.8 --- club/models.py | 12 +++++-- club/tests.py | 77 ++++++++++++++++++++++++++++++++++++++++++--- clubmanager/base.py | 2 +- 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/club/models.py b/club/models.py index 8b1c136..9191a8b 100644 --- a/club/models.py +++ b/club/models.py @@ -68,15 +68,21 @@ class Season(ClubScopedModel): end_date = models.DateField(_("end date")) def __str__(self): - return f"{self.start_date} - {self.end_date}" + return self.name class Meta: verbose_name = _("season") verbose_name_plural = _("seasons") + @property + def name(self): + """Short label built from the start/end years, e.g. "25-26".""" + return f"{self.start_date:%y}-{self.end_date:%y}" + @classmethod def get_current(cls, date: datetime.date | None = None): + """Return the current club's season covering ``date`` (today by default).""" if date is None: date = timezone.now().date() - - \ No newline at end of file + + return cls.objects.current_club().filter(start_date__lte=date, end_date__gte=date).first() diff --git a/club/tests.py b/club/tests.py index bed8ac3..fe0e524 100644 --- a/club/tests.py +++ b/club/tests.py @@ -4,6 +4,7 @@ from contextlib import contextmanager from django.db import IntegrityError from django.test import RequestFactory, TestCase, override_settings +from django.utils import timezone from members.models import Member @@ -350,14 +351,82 @@ class TenantScopedModelTests(TestCase): self.assertEqual(list(Season.objects.for_club(self.club)), [mine]) - def test_current_filters_by_active_club(self): + def test_current_club_filters_by_active_club(self): mine = Season.objects.create(club=self.club, **self.dates) Season.objects.create(club=self.other, **self.dates) with with_club(self.club): - self.assertEqual(list(Season.objects.current()), [mine]) + self.assertEqual(list(Season.objects.current_club()), [mine]) - def test_str_shows_date_range(self): + def test_name_is_two_digit_year_range(self): season = Season.objects.create(club=self.club, **self.dates) - self.assertEqual(str(season), "2026-08-01 - 2027-05-31") + self.assertEqual(season.name, "26-27") + + def test_name_zero_pads_years(self): + season = Season.objects.create( + club=self.club, + start_date=datetime.date(2008, 8, 1), + end_date=datetime.date(2009, 5, 31), + ) + + self.assertEqual(season.name, "08-09") + + def test_str_is_the_name(self): + season = Season.objects.create(club=self.club, **self.dates) + + self.assertEqual(str(season), "26-27") + + +class SeasonGetCurrentTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.other = Club.objects.create(name="Rival FC", slug="rival-fc") + self.season = Season.objects.create( + club=self.club, + start_date=datetime.date(2026, 8, 1), + end_date=datetime.date(2027, 5, 31), + ) + + def test_returns_season_covering_the_given_date(self): + with with_club(self.club): + found = Season.get_current(datetime.date(2026, 12, 25)) + + self.assertEqual(found, self.season) + + def test_includes_boundary_dates(self): + with with_club(self.club): + self.assertEqual(Season.get_current(datetime.date(2026, 8, 1)), self.season) + self.assertEqual(Season.get_current(datetime.date(2027, 5, 31)), self.season) + + def test_returns_none_when_no_season_covers_the_date(self): + with with_club(self.club): + self.assertIsNone(Season.get_current(datetime.date(2027, 7, 1))) + + def test_is_scoped_to_the_active_club(self): + # The other club's season covers the same date but must not leak. + Season.objects.create( + club=self.other, + start_date=datetime.date(2026, 8, 1), + end_date=datetime.date(2027, 5, 31), + ) + + with with_club(self.other): + found = Season.get_current(datetime.date(2026, 12, 25)) + + self.assertEqual(found.club, self.other) + + def test_defaults_to_today(self): + today = timezone.now().date() + current = Season.objects.create( + club=self.club, + start_date=today - datetime.timedelta(days=10), + end_date=today + datetime.timedelta(days=10), + ) + + with with_club(self.club): + self.assertEqual(Season.get_current(), current) + + def test_requires_an_active_club(self): + with self.assertRaises(RuntimeError): + Season.get_current(datetime.date(2026, 12, 25)) diff --git a/clubmanager/base.py b/clubmanager/base.py index 0965486..3e607e8 100644 --- a/clubmanager/base.py +++ b/clubmanager/base.py @@ -13,7 +13,7 @@ class TenantQuerySet(models.QuerySet): def for_club(self, club: Club): return self.filter(club=club) - def current(self): + def current_club(self): return self.filter(club=require_current_club()) From dd2e4b216989fa839e65c1a279e3f9e3b732d68e Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 16:47:39 +0200 Subject: [PATCH 018/137] feat: season-scope club memberships and convert to UniqueConstraint ClubMembership is now tied to a Season (plus status / fee_status / sign-up dates) and unique per (club, member, season). The CSV importer attaches each membership to the club's current season (Season.get_current), skipping the row with a clear error when none exists; the now-unreachable "clubs created" bookkeeping is removed. Register SeasonAdmin and rebuild ClubMembershipAdmin for the new fields, and add an admin smoke test that asserts every model in authentication/club/ members/teams is registered and its changelist + add pages load. Convert every unique_together to a Meta UniqueConstraint (Season, ClubMembership, FamilyMembership) per Django's guidance. Regenerate migrations. club, members, and importer stay at 100% coverage. Co-Authored-By: Claude Opus 4.8 --- club/admin.py | 27 ++- ...ership_season_alter_clubmembership_club.py | 25 +++ ...clubmembership_unique_together_and_more.py | 46 +++++ club/models.py | 54 ++++-- club/tests.py | 171 +++++++++++------- .../management/commands/import_members_csv.py | 1 - ...milymembership_unique_together_and_more.py | 21 +++ members/models.py | 4 +- members/services/member_csv_importer.py | 27 ++- members/tests.py | 47 ++++- members/views.py | 1 - 11 files changed, 316 insertions(+), 108 deletions(-) create mode 100644 club/migrations/0007_clubmembership_season_alter_clubmembership_club.py create mode 100644 club/migrations/0008_alter_clubmembership_unique_together_and_more.py create mode 100644 members/migrations/0002_alter_familymembership_unique_together_and_more.py diff --git a/club/admin.py b/club/admin.py index b526846..ea9063e 100644 --- a/club/admin.py +++ b/club/admin.py @@ -1,24 +1,33 @@ from django.contrib import admin from django.utils.translation import gettext_lazy as _ -from .models import Club, ClubMembership +from .models import Club, ClubMembership, Season -# Register your models here. @admin.register(Club) class ClubAdmin(admin.ModelAdmin): - list_display = ["name"] - search_fields = ["name"] + list_display = ["name", "slug"] + search_fields = ["name", "slug"] + prepopulated_fields = {"slug": ["name"]} ordering = ["name"] +@admin.register(Season) +class SeasonAdmin(admin.ModelAdmin): + list_display = ["__str__", "club", "start_date", "end_date"] + list_filter = ["club"] + search_fields = ["club__name"] + ordering = ["club", "-start_date"] + + @admin.register(ClubMembership) class ClubMembershipAdmin(admin.ModelAdmin): - list_display = ["club__name", "member__last_name", "member__first_name", "license"] - search_fields = ["club__name", "member__last_name", "member__first_name"] - list_filter = ["club"] + list_display = ["club__name", "member__last_name", "member__first_name", "season", "status", "fee_status", "license"] + search_fields = ["club__name", "member__last_name", "member__first_name", "license"] + list_filter = ["club", "season", "status", "fee_status"] raw_id_fields = ["member"] fieldsets = [ - [None, {"fields": ["club", "member"]}], - [_("Member informaton"), {"fields": ["license"]}], + [None, {"fields": ["club", "season", "member"]}], + [_("Membership"), {"fields": ["license", "status", "fee_status"]}], + [_("Dates"), {"fields": ["signed_up_at", "activated_at"]}], ] diff --git a/club/migrations/0007_clubmembership_season_alter_clubmembership_club.py b/club/migrations/0007_clubmembership_season_alter_clubmembership_club.py new file mode 100644 index 0000000..425d503 --- /dev/null +++ b/club/migrations/0007_clubmembership_season_alter_clubmembership_club.py @@ -0,0 +1,25 @@ +# Generated by Django 6.0.6 on 2026-07-12 13:43 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0006_club_slug_season'), + ] + + operations = [ + migrations.AddField( + model_name='clubmembership', + name='season', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.PROTECT, related_name='memberships', to='club.season', verbose_name='season'), + preserve_default=False, + ), + migrations.AlterField( + model_name='clubmembership', + name='club', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club'), + ), + ] diff --git a/club/migrations/0008_alter_clubmembership_unique_together_and_more.py b/club/migrations/0008_alter_clubmembership_unique_together_and_more.py new file mode 100644 index 0000000..24e8130 --- /dev/null +++ b/club/migrations/0008_alter_clubmembership_unique_together_and_more.py @@ -0,0 +1,46 @@ +# Generated by Django 6.0.6 on 2026-07-12 14:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0007_clubmembership_season_alter_clubmembership_club'), + ('members', '0002_alter_familymembership_unique_together_and_more'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='clubmembership', + unique_together=set(), + ), + migrations.AddField( + model_name='clubmembership', + name='activated_at', + field=models.DateField(blank=True, null=True, verbose_name='activated at'), + ), + migrations.AddField( + model_name='clubmembership', + name='fee_status', + field=models.CharField(choices=[('unpaid', 'unpaid'), ('paid', 'paid'), ('partially_paid', 'partially paid'), ('waived', 'waived')], default='unpaid', max_length=250, verbose_name='fee status'), + ), + migrations.AddField( + model_name='clubmembership', + name='signed_up_at', + field=models.DateField(blank=True, null=True, verbose_name='signed up at'), + ), + migrations.AddField( + model_name='clubmembership', + name='status', + field=models.CharField(choices=[('active', 'active'), ('pending', 'pending'), ('lapsed', 'lapsed'), ('cancelled', 'cancelled')], default='pending', max_length=250, verbose_name='status'), + ), + migrations.AddConstraint( + model_name='clubmembership', + constraint=models.UniqueConstraint(fields=('club', 'member', 'season'), name='unique_member_per_club_per_season'), + ), + migrations.AddConstraint( + model_name='season', + constraint=models.UniqueConstraint(fields=('club', 'start_date', 'end_date'), name='unique_season_dates_per_club'), + ), + ] diff --git a/club/models.py b/club/models.py index 9191a8b..dca3e0e 100644 --- a/club/models.py +++ b/club/models.py @@ -47,22 +47,6 @@ class Club(UUIDModel): return slug -class ClubMembership(UUIDModel): - member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="member_of", verbose_name=_("member")) - club = models.ForeignKey(Club, on_delete=models.CASCADE, related_name="members", verbose_name=_("club")) - - license = models.CharField(_("license"), max_length=250, blank=True) - - class Meta: - verbose_name = _("club membership") - verbose_name_plural = _("club memberships") - ordering = ["club", "member__last_name", "member__first_name"] - unique_together = ("club", "member") - - def __str__(self): - return f"{self.club} - {self.member}" - - class Season(ClubScopedModel): start_date = models.DateField(_("start date")) end_date = models.DateField(_("end date")) @@ -73,6 +57,9 @@ class Season(ClubScopedModel): class Meta: verbose_name = _("season") verbose_name_plural = _("seasons") + constraints = [ + models.UniqueConstraint(fields=["club", "start_date", "end_date"], name="unique_season_dates_per_club"), + ] @property def name(self): @@ -86,3 +73,38 @@ class Season(ClubScopedModel): date = timezone.now().date() return cls.objects.current_club().filter(start_date__lte=date, end_date__gte=date).first() + + +class ClubMembership(ClubScopedModel): + class StatusChoices(models.TextChoices): + ACTIVE = "active", _("active") + PENDING = "pending", _("pending") + LAPSED = "lapsed", _("lapsed") + CANCELLED = "cancelled", _("cancelled") + + class FeeStatus(models.TextChoices): + UNPAID = "unpaid", _("unpaid") + PAID = "paid", _("paid") + PARTIALLY_PAID = "partially_paid", _("partially paid") + WAIVED = "waived", _("waived") + + member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="member_of", verbose_name=_("member")) + season = models.ForeignKey(Season, on_delete=models.PROTECT, related_name="memberships", verbose_name=_("season")) + + license = models.CharField(_("license"), max_length=250, blank=True) + status = models.CharField(_("status"), max_length=250, choices=StatusChoices.choices, default=StatusChoices.PENDING) + fee_status = models.CharField(_("fee status"), max_length=250, choices=FeeStatus.choices, default=FeeStatus.UNPAID) + + signed_up_at = models.DateField(_("signed up at"), blank=True, null=True) + activated_at = models.DateField(_("activated at"), blank=True, null=True) + + class Meta: + verbose_name = _("club membership") + verbose_name_plural = _("club memberships") + ordering = ["club", "member__last_name", "member__first_name"] + constraints = [ + models.UniqueConstraint(fields=["club", "member", "season"], name="unique_member_per_club_per_season"), + ] + + def __str__(self): + return f"{self.club} - {self.member}" diff --git a/club/tests.py b/club/tests.py index fe0e524..3f64bcf 100644 --- a/club/tests.py +++ b/club/tests.py @@ -2,8 +2,12 @@ import datetime import uuid from contextlib import contextmanager +from django.contrib import admin as django_admin +from django.contrib.auth import get_user_model from django.db import IntegrityError +from django.db.models import ProtectedError from django.test import RequestFactory, TestCase, override_settings +from django.urls import reverse from django.utils import timezone from members.models import Member @@ -54,9 +58,18 @@ class ClubModelTests(TestCase): self.assertEqual(Club._meta.verbose_name_plural, "clubs") +def make_season(club, start_year=2026): + return Season.objects.create( + club=club, + start_date=datetime.date(start_year, 8, 1), + end_date=datetime.date(start_year + 1, 5, 31), + ) + + class ClubMembershipModelTests(TestCase): def setUp(self): self.club = Club.objects.create(name="City Swim Club") + self.season = make_season(self.club) self.member = Member.objects.create( first_name="Jane", last_name="Doe", @@ -64,98 +77,97 @@ class ClubMembershipModelTests(TestCase): ) def test_str_returns_club_and_member(self): - membership = ClubMembership.objects.create( - club=self.club, - member=self.member, - license="LIC-001", - ) + membership = ClubMembership.objects.create(club=self.club, season=self.season, member=self.member, license="LIC-001") self.assertEqual(str(membership), "City Swim Club - Jane Doe") - def test_license_is_optional(self): - membership = ClubMembership.objects.create( - club=self.club, - member=self.member, - ) + def test_defaults_are_pending_and_unpaid(self): + membership = ClubMembership.objects.create(club=self.club, season=self.season, member=self.member) + self.assertEqual(membership.status, ClubMembership.StatusChoices.PENDING) + self.assertEqual(membership.fee_status, ClubMembership.FeeStatus.UNPAID) self.assertEqual(membership.license, "") + self.assertIsNone(membership.signed_up_at) def test_pk_is_uuid(self): - membership = ClubMembership.objects.create( - club=self.club, - member=self.member, - ) + membership = ClubMembership.objects.create(club=self.club, season=self.season, member=self.member) self.assertIsInstance(membership.pk, uuid.UUID) - def test_same_member_can_join_different_clubs(self): - other_club = Club.objects.create(name="Other Swim Club") + def test_club_is_filled_from_active_tenant(self): + with with_club(self.club): + membership = ClubMembership.objects.create(season=self.season, member=self.member) - first_membership = ClubMembership.objects.create( - club=self.club, - member=self.member, - license="LIC-001", - ) - second_membership = ClubMembership.objects.create( - club=other_club, - member=self.member, - license="LIC-002", - ) + self.assertEqual(membership.club, self.club) - self.assertEqual(first_membership.member, self.member) - self.assertEqual(second_membership.member, self.member) - self.assertEqual(self.member.member_of.count(), 2) - - def test_member_is_unique_per_club(self): - ClubMembership.objects.create( - club=self.club, - member=self.member, - license="LIC-001", - ) + def test_member_is_unique_per_club_and_season(self): + ClubMembership.objects.create(club=self.club, season=self.season, member=self.member) with self.assertRaises(IntegrityError): - ClubMembership.objects.create( - club=self.club, - member=self.member, - license="LIC-002", - ) + ClubMembership.objects.create(club=self.club, season=self.season, member=self.member) - def test_deleting_club_deletes_membership_but_keeps_member(self): - ClubMembership.objects.create( - club=self.club, - member=self.member, - license="LIC-001", - ) + def test_same_member_can_join_consecutive_seasons(self): + next_season = make_season(self.club, start_year=2027) + first = ClubMembership.objects.create(club=self.club, season=self.season, member=self.member) + second = ClubMembership.objects.create(club=self.club, season=next_season, member=self.member) + + self.assertEqual(self.member.member_of.count(), 2) + self.assertNotEqual(first.season, second.season) + + def test_same_member_can_join_different_clubs(self): + other_club = Club.objects.create(name="Other Swim Club") + other_season = make_season(other_club) + + ClubMembership.objects.create(club=self.club, season=self.season, member=self.member) + ClubMembership.objects.create(club=other_club, season=other_season, member=self.member) + + self.assertEqual(self.member.member_of.count(), 2) + + def test_deleting_club_is_blocked_while_a_season_has_memberships(self): + # Club -> Season is CASCADE, but ClubMembership -> Season is PROTECT, so + # the club can't be deleted while one of its seasons is still referenced. + ClubMembership.objects.create(club=self.club, season=self.season, member=self.member) + + with self.assertRaises(ProtectedError): + self.club.delete() + + self.assertTrue(ClubMembership.objects.exists()) + + def test_deleting_empty_club_cascades_to_its_seasons(self): self.club.delete() - self.assertFalse(ClubMembership.objects.exists()) - self.assertTrue(Member.objects.filter(pk=self.member.pk).exists()) + self.assertFalse(Club.objects.filter(pk=self.club.pk).exists()) + self.assertFalse(Season.objects.filter(pk=self.season.pk).exists()) def test_deleting_member_deletes_membership_but_keeps_club(self): - ClubMembership.objects.create( - club=self.club, - member=self.member, - license="LIC-001", - ) + ClubMembership.objects.create(club=self.club, season=self.season, member=self.member) self.member.delete() self.assertFalse(ClubMembership.objects.exists()) self.assertTrue(Club.objects.filter(pk=self.club.pk).exists()) + def test_season_is_protected_while_referenced(self): + ClubMembership.objects.create(club=self.club, season=self.season, member=self.member) + + with self.assertRaises(ProtectedError): + self.season.delete() + def test_memberships_are_ordered_by_club_then_member_name(self): alpha_club = Club.objects.create(name="Alpha Club") zulu_club = Club.objects.create(name="Zulu Club") + alpha_season = make_season(alpha_club) + zulu_season = make_season(zulu_club) jane = Member.objects.create(first_name="Jane", last_name="Doe") alice = Member.objects.create(first_name="Alice", last_name="Smith") bob = Member.objects.create(first_name="Bob", last_name="Smith") - ClubMembership.objects.create(club=zulu_club, member=bob) - ClubMembership.objects.create(club=alpha_club, member=bob) - ClubMembership.objects.create(club=alpha_club, member=alice) - ClubMembership.objects.create(club=alpha_club, member=jane) + ClubMembership.objects.create(club=zulu_club, season=zulu_season, member=bob) + ClubMembership.objects.create(club=alpha_club, season=alpha_season, member=bob) + ClubMembership.objects.create(club=alpha_club, season=alpha_season, member=alice) + ClubMembership.objects.create(club=alpha_club, season=alpha_season, member=jane) self.assertEqual( [(membership.club.name, membership.member.last_name, membership.member.first_name) for membership in ClubMembership.objects.all()], @@ -168,14 +180,11 @@ class ClubMembershipModelTests(TestCase): ) def test_reverse_relations(self): - membership = ClubMembership.objects.create( - club=self.club, - member=self.member, - license="LIC-001", - ) + membership = ClubMembership.objects.create(club=self.club, season=self.season, member=self.member) - self.assertEqual(list(self.club.members.all()), [membership]) + self.assertEqual(list(self.club.clubmemberships.all()), [membership]) self.assertEqual(list(self.member.member_of.all()), [membership]) + self.assertEqual(list(self.season.memberships.all()), [membership]) def test_verbose_names(self): self.assertEqual(ClubMembership._meta.verbose_name, "club membership") @@ -430,3 +439,37 @@ class SeasonGetCurrentTests(TestCase): def test_requires_an_active_club(self): with self.assertRaises(RuntimeError): Season.get_current(datetime.date(2026, 12, 25)) + + +class AdminRegistrationSmokeTests(TestCase): + """Every registered model across all apps must have a working admin: load + each changelist and add page to catch bad list_display / search_fields / + fieldsets / autocomplete targets in any app's admin config.""" + + def setUp(self): + self.admin = get_user_model().objects.create_superuser(email="root@club.test", password="pw-secret-123") + self.client.force_login(self.admin) + + def test_every_model_is_registered_in_admin(self): + from django.apps import apps + + registered = set(django_admin.site._registry) + # Concrete, non-auto-created models in these apps should all be registered. + project_apps = {"authentication", "club", "members", "teams"} + for model in apps.get_models(): + if model._meta.app_label not in project_apps or model._meta.auto_created: + continue + with self.subTest(model=model.__name__): + self.assertIn(model, registered, f"{model.__name__} is not registered in the admin") + + def test_all_changelists_load(self): + for model in django_admin.site._registry: + url = reverse(f"admin:{model._meta.app_label}_{model._meta.model_name}_changelist") + with self.subTest(model=model.__name__): + self.assertEqual(self.client.get(url).status_code, 200) + + def test_all_add_pages_load(self): + for model in django_admin.site._registry: + url = reverse(f"admin:{model._meta.app_label}_{model._meta.model_name}_add") + with self.subTest(model=model.__name__): + self.assertEqual(self.client.get(url).status_code, 200) diff --git a/members/management/commands/import_members_csv.py b/members/management/commands/import_members_csv.py index 35de581..2a0e285 100644 --- a/members/management/commands/import_members_csv.py +++ b/members/management/commands/import_members_csv.py @@ -35,7 +35,6 @@ class Command(BaseCommand): f"Members created: {result.created_members}. " f"Members updated: {result.updated_members}. " f"Users created: {result.created_users}. " - f"Clubs created: {result.created_clubs}. " f"Memberships created: {result.created_memberships}. " f"Memberships updated: {result.updated_memberships}. " f"Rows skipped: {result.skipped_rows}." diff --git a/members/migrations/0002_alter_familymembership_unique_together_and_more.py b/members/migrations/0002_alter_familymembership_unique_together_and_more.py new file mode 100644 index 0000000..4b555e9 --- /dev/null +++ b/members/migrations/0002_alter_familymembership_unique_together_and_more.py @@ -0,0 +1,21 @@ +# Generated by Django 6.0.6 on 2026-07-12 14:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0001_initial'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='familymembership', + unique_together=set(), + ), + migrations.AddConstraint( + model_name='familymembership', + constraint=models.UniqueConstraint(fields=('family', 'member'), name='unique_member_per_family'), + ), + ] diff --git a/members/models.py b/members/models.py index 7b2b535..8009e60 100644 --- a/members/models.py +++ b/members/models.py @@ -93,7 +93,9 @@ class FamilyMembership(models.Model): verbose_name = _("family membership") verbose_name_plural = _("family memberships") ordering = ["family", "role", "member__last_name", "member__first_name"] - unique_together = ("family", "member") + constraints = [ + models.UniqueConstraint(fields=["family", "member"], name="unique_member_per_family"), + ] def __str__(self): return f"{self.family} - {self.member} ({self.get_role_display()})" diff --git a/members/services/member_csv_importer.py b/members/services/member_csv_importer.py index 2443c09..45f37b6 100644 --- a/members/services/member_csv_importer.py +++ b/members/services/member_csv_importer.py @@ -7,7 +7,8 @@ from typing import IO, Any from django.contrib.auth import get_user_model from django.db import transaction -from club.models import Club, ClubMembership +from club.models import Club, ClubMembership, Season +from club.tenancy import reset_current_club, set_current_club from members.models import Member REQUIRED_COLUMNS = {"first_name", "last_name", "email", "date_of_birth", "create_account", "club_name", "license_number"} @@ -27,7 +28,6 @@ class MemberImportResult: created_members: int = 0 updated_members: int = 0 created_users: int = 0 - created_clubs: int = 0 created_memberships: int = 0 updated_memberships: int = 0 skipped_rows: int = 0 @@ -42,7 +42,6 @@ class MemberImportResult: class ImportedMemberRowResult: member_created: bool user_created: bool - club_created: bool membership_created: bool @@ -91,9 +90,6 @@ class MemberCsvImporter: if row_result.user_created: result.created_users += 1 - if row_result.club_created: - result.created_clubs += 1 - if row_result.membership_created: result.created_memberships += 1 else: @@ -127,11 +123,13 @@ class MemberCsvImporter: }, ) - club, club_created = Club.objects.get_or_create(name=club_name) + club, _ = Club.objects.get_or_create(name=club_name) + season = self.get_current_season(club) _, membership_created = ClubMembership.objects.update_or_create( club=club, member=member, + season=season, defaults={ "license": license_number, }, @@ -140,10 +138,23 @@ class MemberCsvImporter: return ImportedMemberRowResult( member_created=member_created, user_created=user_created, - club_created=club_created, membership_created=membership_created, ) + def get_current_season(self, club) -> Season: + # Season.get_current() is tenant-scoped, so bind the row's club as the + # active tenant for the lookup. + token = set_current_club(club) + try: + season = Season.get_current() + finally: + reset_current_club(token) + + if season is None: + raise ValueError(f"No current season for club '{club.name}'.") + + return season + def get_or_create_user(self, email) -> tuple[User, bool]: user, created = User.objects.get_or_create( email=email, diff --git a/members/tests.py b/members/tests.py index 021e23d..b3170bb 100644 --- a/members/tests.py +++ b/members/tests.py @@ -1,5 +1,5 @@ import tempfile -from datetime import date +from datetime import date, timedelta from io import StringIO from pathlib import Path @@ -9,9 +9,10 @@ from django.core.management.base import CommandError from django.db import IntegrityError from django.db.models import SET_NULL from django.test import TestCase +from django.utils import timezone from authentication.models import User -from club.models import Club, ClubMembership +from club.models import Club, ClubMembership, Season from members.admin import FamilyAdmin from members.models import Family, FamilyMembership, Member from members.services import MemberImportResult @@ -292,6 +293,17 @@ class AdminSmokeTests(TestCase): class ImportMembersCsvCommandTests(TestCase): + def setUp(self): + # Memberships are season-scoped: the importer attaches each one to the + # club's current season, so the target club needs one covering today. + self.club = Club.objects.create(name="City Swim Club") + today = timezone.localdate() + self.season = Season.objects.create( + club=self.club, + start_date=today - timedelta(days=90), + end_date=today + timedelta(days=275), + ) + def write_csv(self, content): temp_file = tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False, encoding="utf-8") temp_file.write(content) @@ -329,7 +341,6 @@ class ImportMembersCsvCommandTests(TestCase): self.assertIn("Import complete.", stdout) self.assertIn("Members created: 1.", stdout) self.assertIn("Users created: 1.", stdout) - self.assertIn("Clubs created: 1.", stdout) self.assertIn("Memberships created: 1.", stdout) self.assertIn("Rows skipped: 0.", stdout) @@ -341,9 +352,9 @@ class ImportMembersCsvCommandTests(TestCase): self.assertEqual(member.user.email, "jane@example.com") self.assertFalse(member.user.has_usable_password()) - club = Club.objects.get(name="City Swim Club") - membership = ClubMembership.objects.get(club=club, member=member) + membership = ClubMembership.objects.get(club=self.club, member=member) self.assertEqual(membership.license, "LIC-001") + self.assertEqual(membership.season, self.season) def test_import_creates_member_without_user_when_create_account_is_false(self): csv_path = self.write_csv( @@ -366,7 +377,6 @@ class ImportMembersCsvCommandTests(TestCase): self.assertFalse(User.objects.filter(email="john@example.com").exists()) def test_import_updates_existing_member_and_membership(self): - club = Club.objects.create(name="City Swim Club") member = Member.objects.create( first_name="Old", last_name="Name", @@ -374,7 +384,8 @@ class ImportMembersCsvCommandTests(TestCase): date_of_birth=date(2010, 1, 1), ) ClubMembership.objects.create( - club=club, + club=self.club, + season=self.season, member=member, license="OLD-LIC", ) @@ -401,7 +412,7 @@ class ImportMembersCsvCommandTests(TestCase): self.assertEqual(member.last_name, "Doe") self.assertEqual(member.date_of_birth, date(2010, 4, 12)) - membership = ClubMembership.objects.get(club=club, member=member) + membership = ClubMembership.objects.get(club=self.club, member=member) self.assertEqual(membership.license, "LIC-NEW") def test_import_links_existing_user_when_create_account_is_true(self): @@ -539,6 +550,26 @@ class ImportMembersCsvCommandTests(TestCase): self.assertIn("Rows skipped: 1.", stdout) self.assertFalse(Member.objects.filter(email="nofirst@example.com").exists()) + def test_import_skips_row_when_club_has_no_current_season(self): + # "New Club" has no season, so the row is skipped and — because the row + # is atomic — the member and club creation roll back too. + csv_path = self.write_csv( + "\n".join( + [ + "first_name,last_name,email,date_of_birth,create_account,club_name,license_number", + "Jane,Doe,jane@example.com,2010-04-12,false,New Club,LIC-001", + ] + ) + ) + + stdout, stderr = self.call_import_command(csv_path) + + self.assertIn("Row 2 skipped:", stderr) + self.assertIn("No current season for club 'New Club'.", stderr) + self.assertIn("Rows skipped: 1.", stdout) + self.assertFalse(Member.objects.filter(email="jane@example.com").exists()) + self.assertFalse(Club.objects.filter(name="New Club").exists()) + class MemberImportResultTests(TestCase): def test_successful_rows_sums_created_and_updated(self): diff --git a/members/views.py b/members/views.py index b8e4ee0..60f00ef 100644 --- a/members/views.py +++ b/members/views.py @@ -1,2 +1 @@ - # Create your views here. From 076a9cacbc6ecea214dabe9d1085b35bf57fb54e Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 16:47:52 +0200 Subject: [PATCH 019/137] feat(teams): add teams app with roster and staff assignments Introduce the teams app: Team and Position (both club-scoped, with per-club unique names), TeamMembership (season-scoped roster with jersey number + captain flags, unique member and jersey per team/season), and StaffAssignment (coaching/staff, filtered to staff positions). All uniqueness expressed as UniqueConstraints. Register every model in the admin (with roster + staff inlines on Team) and cover the models with tests. Fix two model bugs surfaced by the system check: StaffAssignment.position reused TeamMembership's reverse accessor (Position.team_memberships) and duplicated its constraint name. Co-Authored-By: Claude Opus 4.8 --- clubmanager/settings.py | 1 + teams/__init__.py | 0 teams/admin.py | 56 ++++++++++++++++ teams/apps.py | 5 ++ teams/migrations/0001_initial.py | 100 +++++++++++++++++++++++++++ teams/migrations/__init__.py | 0 teams/models.py | 81 ++++++++++++++++++++++ teams/tests.py | 112 +++++++++++++++++++++++++++++++ teams/views.py | 1 + 9 files changed, 356 insertions(+) create mode 100644 teams/__init__.py create mode 100644 teams/admin.py create mode 100644 teams/apps.py create mode 100644 teams/migrations/0001_initial.py create mode 100644 teams/migrations/__init__.py create mode 100644 teams/models.py create mode 100644 teams/tests.py create mode 100644 teams/views.py diff --git a/clubmanager/settings.py b/clubmanager/settings.py index 05bde7f..b345015 100644 --- a/clubmanager/settings.py +++ b/clubmanager/settings.py @@ -47,6 +47,7 @@ INSTALLED_APPS = [ "club.apps.ClubConfig", "authentication.apps.AuthenticationConfig", "members.apps.MembersConfig", + "teams.apps.TeamsConfig", ] MIDDLEWARE = [ diff --git a/teams/__init__.py b/teams/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/teams/admin.py b/teams/admin.py new file mode 100644 index 0000000..c2deb32 --- /dev/null +++ b/teams/admin.py @@ -0,0 +1,56 @@ +from django.contrib import admin +from django.utils.translation import gettext_lazy as _ + +from .models import Position, StaffAssignment, Team, TeamMembership + + +class TeamMembershipInline(admin.TabularInline): + """Roster shown on the Team page.""" + + model = TeamMembership + extra = 0 + raw_id_fields = ("member",) + + +class StaffAssignmentInline(admin.TabularInline): + """Coaching / staff shown on the Team page.""" + + model = StaffAssignment + extra = 0 + raw_id_fields = ("member",) + + +@admin.register(Team) +class TeamAdmin(admin.ModelAdmin): + list_display = ["name", "short_name", "club"] + list_filter = ["club"] + search_fields = ["name", "short_name"] + inlines = [TeamMembershipInline, StaffAssignmentInline] + + +@admin.register(Position) +class PositionAdmin(admin.ModelAdmin): + list_display = ["name", "short_name", "club", "staff_position", "ordering"] + list_filter = ["club", "staff_position"] + search_fields = ["name", "short_name"] + ordering = ["ordering", "name"] + + +@admin.register(TeamMembership) +class TeamMembershipAdmin(admin.ModelAdmin): + list_display = ["team", "member", "season", "position", "jersey_number", "is_captain", "is_alternate_captain"] + list_filter = ["team", "season", "position", "is_captain"] + search_fields = ["team__name", "member__first_name", "member__last_name"] + raw_id_fields = ["member"] + fieldsets = [ + [None, {"fields": ["team", "season", "member", "position"]}], + [_("Squad"), {"fields": ["jersey_number", "is_captain", "is_alternate_captain"]}], + ] + + +@admin.register(StaffAssignment) +class StaffAssignmentAdmin(admin.ModelAdmin): + list_display = ["team", "member", "season", "position"] + list_filter = ["team", "season", "position"] + search_fields = ["team__name", "member__first_name", "member__last_name"] + raw_id_fields = ["member"] diff --git a/teams/apps.py b/teams/apps.py new file mode 100644 index 0000000..2cbcff7 --- /dev/null +++ b/teams/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class TeamsConfig(AppConfig): + name = "teams" diff --git a/teams/migrations/0001_initial.py b/teams/migrations/0001_initial.py new file mode 100644 index 0000000..a203854 --- /dev/null +++ b/teams/migrations/0001_initial.py @@ -0,0 +1,100 @@ +# Generated by Django 6.0.6 on 2026-07-12 14:43 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ('members', '0002_alter_familymembership_unique_together_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Position', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('short_name', models.CharField(max_length=255, verbose_name='short name')), + ('ordering', models.PositiveSmallIntegerField(default=0, verbose_name='ordering')), + ('staff_position', models.BooleanField(default=False, verbose_name='staff position')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ], + options={ + 'verbose_name': 'position', + 'verbose_name_plural': 'positions', + 'ordering': ['ordering', 'name'], + }, + ), + migrations.CreateModel( + name='Team', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('short_name', models.CharField(max_length=255, verbose_name='short name')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ], + options={ + 'verbose_name': 'team', + 'verbose_name_plural': 'teams', + }, + ), + migrations.CreateModel( + name='StaffAssignment', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='staff_assignments', to='members.member', verbose_name='member')), + ('position', models.ForeignKey(limit_choices_to={'staff_position': True}, on_delete=django.db.models.deletion.PROTECT, related_name='staff_assignments', to='teams.position', verbose_name='position')), + ('season', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='staff_assignments', to='club.season', verbose_name='season')), + ('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='staff_assignments', to='teams.team', verbose_name='team')), + ], + options={ + 'verbose_name': 'staff assignment', + 'verbose_name_plural': 'staff assignments', + 'ordering': ['team', 'member__last_name', 'member__first_name'], + }, + ), + migrations.CreateModel( + name='TeamMembership', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('jersey_number', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='jersey number')), + ('is_captain', models.BooleanField(default=False, verbose_name='is captain')), + ('is_alternate_captain', models.BooleanField(default=False, verbose_name='is alternate captain')), + ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='team_memberships', to='members.member', verbose_name='member')), + ('position', models.ForeignKey(limit_choices_to={'staff_position': False}, on_delete=django.db.models.deletion.PROTECT, related_name='team_memberships', to='teams.position', verbose_name='position')), + ('season', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='team_memberships', to='club.season', verbose_name='season')), + ('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='roster', to='teams.team', verbose_name='team')), + ], + options={ + 'verbose_name': 'team membership', + 'verbose_name_plural': 'team memberships', + 'ordering': ['team', 'member__last_name', 'member__first_name'], + }, + ), + migrations.AddConstraint( + model_name='position', + constraint=models.UniqueConstraint(fields=('club', 'name'), name='unique_position_name_per_club'), + ), + migrations.AddConstraint( + model_name='team', + constraint=models.UniqueConstraint(fields=('club', 'name'), name='unique_team_name_per_club'), + ), + migrations.AddConstraint( + model_name='staffassignment', + constraint=models.UniqueConstraint(fields=('team', 'season', 'member'), name='unique_staff_member_per_team_per_season'), + ), + migrations.AddConstraint( + model_name='teammembership', + constraint=models.UniqueConstraint(fields=('team', 'season', 'member'), name='unique_member_per_team_per_season'), + ), + migrations.AddConstraint( + model_name='teammembership', + constraint=models.UniqueConstraint(fields=('team', 'season', 'jersey_number'), name='unique_jersey_number_per_team_per_season'), + ), + ] diff --git a/teams/migrations/__init__.py b/teams/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/teams/models.py b/teams/models.py new file mode 100644 index 0000000..2b41c16 --- /dev/null +++ b/teams/models.py @@ -0,0 +1,81 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from club.models import Season +from clubmanager.base import ClubScopedModel, UUIDModel +from members.models import Member + + +class Team(ClubScopedModel): + name = models.CharField(_("name"), max_length=255) + short_name = models.CharField(_("short name"), max_length=255) + + class Meta: + verbose_name = _("team") + verbose_name_plural = _("teams") + constraints = [ + models.UniqueConstraint(fields=["club", "name"], name="unique_team_name_per_club"), + ] + + def __str__(self): + return self.name + + +class Position(ClubScopedModel): + name = models.CharField(_("name"), max_length=255) + short_name = models.CharField(_("short name"), max_length=255) + ordering = models.PositiveSmallIntegerField(_("ordering"), default=0) + + staff_position = models.BooleanField(_("staff position"), default=False) + + class Meta: + verbose_name = _("position") + verbose_name_plural = _("positions") + constraints = [ + models.UniqueConstraint(fields=["club", "name"], name="unique_position_name_per_club"), + ] + ordering = ["ordering", "name"] + + def __str__(self): + return self.name + + +class TeamMembership(UUIDModel): + team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="roster", verbose_name=_("team")) + member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="team_memberships", verbose_name=_("member")) + season = models.ForeignKey(Season, on_delete=models.PROTECT, related_name="team_memberships", verbose_name=_("season")) + position = models.ForeignKey(Position, on_delete=models.PROTECT, related_name="team_memberships", verbose_name=_("position"), limit_choices_to={"staff_position": False}) + + jersey_number = models.PositiveSmallIntegerField(_("jersey number"), blank=True, null=True) + is_captain = models.BooleanField(_("is captain"), default=False) + is_alternate_captain = models.BooleanField(_("is alternate captain"), default=False) + + class Meta: + verbose_name = _("team membership") + verbose_name_plural = _("team memberships") + ordering = ["team", "member__last_name", "member__first_name"] + constraints = [ + models.UniqueConstraint(fields=["team", "season", "member"], name="unique_member_per_team_per_season"), + models.UniqueConstraint(fields=["team", "season", "jersey_number"], name="unique_jersey_number_per_team_per_season"), + ] + + def __str__(self): + return f"{self.team} - {self.member}" + + +class StaffAssignment(UUIDModel): + team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="staff_assignments", verbose_name=_("team")) + member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="staff_assignments", verbose_name=_("member")) + season = models.ForeignKey(Season, on_delete=models.PROTECT, related_name="staff_assignments", verbose_name=_("season")) + position = models.ForeignKey(Position, on_delete=models.PROTECT, related_name="staff_assignments", verbose_name=_("position"), limit_choices_to={"staff_position": True}) + + class Meta: + verbose_name = _("staff assignment") + verbose_name_plural = _("staff assignments") + ordering = ["team", "member__last_name", "member__first_name"] + constraints = [ + models.UniqueConstraint(fields=["team", "season", "member"], name="unique_staff_member_per_team_per_season"), + ] + + def __str__(self): + return f"{self.team} - {self.member}" diff --git a/teams/tests.py b/teams/tests.py new file mode 100644 index 0000000..542163b --- /dev/null +++ b/teams/tests.py @@ -0,0 +1,112 @@ +import datetime + +from django.db import IntegrityError +from django.db.models import ProtectedError +from django.test import TestCase + +from club.models import Club, Season +from members.models import Member + +from .models import Position, StaffAssignment, Team, TeamMembership + + +class TeamsTestCase(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.season = Season.objects.create( + club=self.club, + start_date=datetime.date(2026, 8, 1), + end_date=datetime.date(2027, 5, 31), + ) + self.team = Team.objects.create(club=self.club, name="First Team", short_name="1st") + self.forward = Position.objects.create(club=self.club, name="Forward", short_name="FW") + self.coach = Position.objects.create(club=self.club, name="Head Coach", short_name="HC", staff_position=True) + self.member = Member.objects.create(first_name="Jane", last_name="Doe") + + +class TeamModelTests(TeamsTestCase): + def test_str_returns_name(self): + self.assertEqual(str(self.team), "First Team") + + def test_team_name_is_unique_per_club(self): + with self.assertRaises(IntegrityError): + Team.objects.create(club=self.club, name="First Team", short_name="dup") + + def test_same_team_name_allowed_in_other_club(self): + other = Club.objects.create(name="Rival FC", slug="rival-fc") + Team.objects.create(club=other, name="First Team", short_name="1st") + + self.assertEqual(Team.objects.filter(name="First Team").count(), 2) + + +class PositionModelTests(TeamsTestCase): + def test_str_returns_name(self): + self.assertEqual(str(self.forward), "Forward") + + def test_defaults(self): + self.assertFalse(self.forward.staff_position) + self.assertEqual(self.forward.ordering, 0) + + def test_positions_ordered_by_ordering_then_name(self): + keeper = Position.objects.create(club=self.club, name="Keeper", short_name="GK", ordering=1) + + ordered = list(Position.objects.filter(club=self.club).values_list("name", flat=True)) + # ordering=0 entries first (alphabetical), then ordering=1. + self.assertEqual(ordered, ["Forward", "Head Coach", "Keeper"]) + self.assertEqual(keeper.ordering, 1) + + def test_position_name_is_unique_per_club(self): + with self.assertRaises(IntegrityError): + Position.objects.create(club=self.club, name="Forward", short_name="dup") + + +class TeamMembershipModelTests(TeamsTestCase): + def test_can_create_roster_entry(self): + entry = TeamMembership.objects.create(team=self.team, member=self.member, season=self.season, position=self.forward, jersey_number=9) + + self.assertEqual(entry.position, self.forward) + self.assertFalse(entry.is_captain) + self.assertEqual(str(entry), "First Team - Jane Doe") + self.assertEqual(list(self.team.roster.all()), [entry]) + + def test_member_is_unique_per_team_and_season(self): + TeamMembership.objects.create(team=self.team, member=self.member, season=self.season, position=self.forward, jersey_number=9) + + with self.assertRaises(IntegrityError): + TeamMembership.objects.create(team=self.team, member=self.member, season=self.season, position=self.forward, jersey_number=10) + + def test_jersey_number_is_unique_per_team_and_season(self): + TeamMembership.objects.create(team=self.team, member=self.member, season=self.season, position=self.forward, jersey_number=9) + other = Member.objects.create(first_name="John", last_name="Smith") + + with self.assertRaises(IntegrityError): + TeamMembership.objects.create(team=self.team, member=other, season=self.season, position=self.forward, jersey_number=9) + + def test_season_is_protected_while_referenced(self): + TeamMembership.objects.create(team=self.team, member=self.member, season=self.season, position=self.forward) + + with self.assertRaises(ProtectedError): + self.season.delete() + + +class StaffAssignmentModelTests(TeamsTestCase): + def test_can_assign_staff(self): + assignment = StaffAssignment.objects.create(team=self.team, member=self.member, season=self.season, position=self.coach) + + self.assertEqual(assignment.position, self.coach) + self.assertEqual(str(assignment), "First Team - Jane Doe") + self.assertEqual(list(self.team.staff_assignments.all()), [assignment]) + + def test_member_is_unique_per_team_and_season(self): + StaffAssignment.objects.create(team=self.team, member=self.member, season=self.season, position=self.coach) + + with self.assertRaises(IntegrityError): + StaffAssignment.objects.create(team=self.team, member=self.member, season=self.season, position=self.coach) + + def test_roster_and_staff_use_separate_reverse_accessors(self): + TeamMembership.objects.create(team=self.team, member=self.member, season=self.season, position=self.forward) + coach_member = Member.objects.create(first_name="Coach", last_name="Carter") + StaffAssignment.objects.create(team=self.team, member=coach_member, season=self.season, position=self.coach) + + self.assertEqual(self.forward.team_memberships.count(), 1) + self.assertEqual(self.coach.staff_assignments.count(), 1) diff --git a/teams/views.py b/teams/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/teams/views.py @@ -0,0 +1 @@ +# Create your views here. From 866716d1963f4718f0c16ef9ae869b5de60ff8d6 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 17:44:48 +0200 Subject: [PATCH 020/137] feat(events): team/invited audience with attendance sync Rework an event's audience: replace the single team FK with a teams M2M plus invited_members and excluded_members, and add a season FK (derived from the start date when blank) so team rosters resolve correctly. A data migration copies existing team -> teams. Add an attendance sync service: the effective audience is the union of the teams' rosters for the event's season plus invited, minus excluded; sync_event_attendances reconciles Attendance rows for future events only, adding NO_RESPONSE rows for new members and hard-deleting rows for members no longer invited. Signals drive it: editing an event or its audience re-syncs that event, and adding/removing a team-roster member re-syncs that team's future events. Register all events models in the admin (with an attendance inline) and add events to the admin registration smoke test. Add Season.covering() and pillow (Opponent.logo ImageField). Full suite at 100% coverage. Co-Authored-By: Claude Opus 4.8 --- club/models.py | 5 + club/tests.py | 2 +- clubmanager/settings.py | 1 + events/__init__.py | 0 events/admin.py | 48 ++++++ events/apps.py | 8 + events/migrations/0001_initial.py | 87 ++++++++++ events/migrations/0002_add_event_audience.py | 37 +++++ events/migrations/0003_copy_team_to_teams.py | 29 ++++ events/migrations/0004_remove_event_team.py | 17 ++ events/migrations/__init__.py | 0 events/models.py | 97 +++++++++++ events/services/__init__.py | 3 + events/services/attendance.py | 59 +++++++ events/signals.py | 47 ++++++ events/tests.py | 161 +++++++++++++++++++ events/views.py | 1 + pyproject.toml | 1 + uv.lock | 52 ++++++ 19 files changed, 654 insertions(+), 1 deletion(-) create mode 100644 events/__init__.py create mode 100644 events/admin.py create mode 100644 events/apps.py create mode 100644 events/migrations/0001_initial.py create mode 100644 events/migrations/0002_add_event_audience.py create mode 100644 events/migrations/0003_copy_team_to_teams.py create mode 100644 events/migrations/0004_remove_event_team.py create mode 100644 events/migrations/__init__.py create mode 100644 events/models.py create mode 100644 events/services/__init__.py create mode 100644 events/services/attendance.py create mode 100644 events/signals.py create mode 100644 events/tests.py create mode 100644 events/views.py diff --git a/club/models.py b/club/models.py index dca3e0e..8b02009 100644 --- a/club/models.py +++ b/club/models.py @@ -74,6 +74,11 @@ class Season(ClubScopedModel): return cls.objects.current_club().filter(start_date__lte=date, end_date__gte=date).first() + @classmethod + def covering(cls, club, date: datetime.date): + """Return ``club``'s season covering ``date`` (no tenant context needed).""" + return cls.objects.filter(club=club, start_date__lte=date, end_date__gte=date).first() + class ClubMembership(ClubScopedModel): class StatusChoices(models.TextChoices): diff --git a/club/tests.py b/club/tests.py index 3f64bcf..6c699e4 100644 --- a/club/tests.py +++ b/club/tests.py @@ -455,7 +455,7 @@ class AdminRegistrationSmokeTests(TestCase): registered = set(django_admin.site._registry) # Concrete, non-auto-created models in these apps should all be registered. - project_apps = {"authentication", "club", "members", "teams"} + project_apps = {"authentication", "club", "members", "teams", "events"} for model in apps.get_models(): if model._meta.app_label not in project_apps or model._meta.auto_created: continue diff --git a/clubmanager/settings.py b/clubmanager/settings.py index b345015..a547daa 100644 --- a/clubmanager/settings.py +++ b/clubmanager/settings.py @@ -48,6 +48,7 @@ INSTALLED_APPS = [ "authentication.apps.AuthenticationConfig", "members.apps.MembersConfig", "teams.apps.TeamsConfig", + "events.apps.EventsConfig", ] MIDDLEWARE = [ diff --git a/events/__init__.py b/events/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/events/admin.py b/events/admin.py new file mode 100644 index 0000000..1d96b00 --- /dev/null +++ b/events/admin.py @@ -0,0 +1,48 @@ +from django.contrib import admin +from django.utils.translation import gettext_lazy as _ + +from .models import Attendance, Event, Location, Opponent + + +@admin.register(Opponent) +class OpponentAdmin(admin.ModelAdmin): + list_display = ["name", "club"] + list_filter = ["club"] + search_fields = ["name"] + + +@admin.register(Location) +class LocationAdmin(admin.ModelAdmin): + list_display = ["name", "city", "club"] + list_filter = ["club"] + search_fields = ["name", "city"] + + +class AttendanceInline(admin.TabularInline): + model = Attendance + extra = 0 + raw_id_fields = ["member"] + + +@admin.register(Event) +class EventAdmin(admin.ModelAdmin): + list_display = ["title", "kind", "start", "season", "club"] + list_filter = ["kind", "club", "teams"] + search_fields = ["title"] + date_hierarchy = "start" + autocomplete_fields = ["season", "location", "opponent", "teams", "invited_members", "excluded_members"] + inlines = [AttendanceInline] + fieldsets = [ + [None, {"fields": ["title", "kind", "season"]}], + [_("Audience"), {"fields": ["teams", "invited_members", "excluded_members"]}], + [_("When"), {"fields": ["start", "end", "gathering", "deadline"]}], + [_("Where"), {"fields": ["location", "opponent"]}], + ] + + +@admin.register(Attendance) +class AttendanceAdmin(admin.ModelAdmin): + list_display = ["event", "member", "status"] + list_filter = ["status", "event__kind"] + search_fields = ["event__title", "member__first_name", "member__last_name"] + raw_id_fields = ["event", "member"] diff --git a/events/apps.py b/events/apps.py new file mode 100644 index 0000000..01eac98 --- /dev/null +++ b/events/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig + + +class EventsConfig(AppConfig): + name = "events" + + def ready(self): + from . import signals # noqa: F401 diff --git a/events/migrations/0001_initial.py b/events/migrations/0001_initial.py new file mode 100644 index 0000000..5cf0a2f --- /dev/null +++ b/events/migrations/0001_initial.py @@ -0,0 +1,87 @@ +# Generated by Django 6.0.6 on 2026-07-12 15:22 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ('members', '0002_alter_familymembership_unique_together_and_more'), + ('teams', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Location', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('address', models.CharField(max_length=255, verbose_name='address')), + ('city', models.CharField(max_length=255, verbose_name='city')), + ('zip_code', models.CharField(max_length=255, verbose_name='zip code')), + ('country', models.CharField(max_length=255, verbose_name='country')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ], + options={ + 'verbose_name': 'location', + 'verbose_name_plural': 'locations', + 'ordering': ['name'], + }, + ), + migrations.CreateModel( + name='Opponent', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('logo', models.ImageField(blank=True, upload_to='opponents', verbose_name='logo')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ], + options={ + 'verbose_name': 'opponent', + 'verbose_name_plural': 'opponents', + 'ordering': ['name'], + }, + ), + migrations.CreateModel( + name='Event', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('kind', models.CharField(choices=[('training', 'training'), ('match', 'match'), ('tournament', 'tournament'), ('meeting', 'meeting'), ('social', 'social'), ('other', 'other')], default='other', max_length=10, verbose_name='kind')), + ('title', models.CharField(max_length=255, verbose_name='title')), + ('start', models.DateTimeField(verbose_name='start')), + ('end', models.DateTimeField(blank=True, null=True, verbose_name='end')), + ('gathering', models.DateTimeField(blank=True, null=True, verbose_name='gathering')), + ('deadline', models.DateTimeField(blank=True, null=True, verbose_name='deadline')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='events', to='teams.team', verbose_name='team')), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='events', to='events.location', verbose_name='location')), + ('opponent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='events', to='events.opponent', verbose_name='opponent')), + ], + options={ + 'verbose_name': 'event', + 'verbose_name_plural': 'events', + 'ordering': ['-start'], + }, + ), + migrations.CreateModel( + name='Attendance', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('status', models.CharField(choices=[('present', 'present'), ('absent', 'absent'), ('excused', 'excused'), ('selected', 'selected'), ('not_selected', 'not selected'), ('maybe', 'maybe'), ('no_response', 'no response')], default='no_response', max_length=20, verbose_name='status')), + ('note', models.TextField(blank=True, verbose_name='note')), + ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attendances', to='members.member', verbose_name='member')), + ('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attendances', to='events.event', verbose_name='event')), + ], + options={ + 'verbose_name': 'attendance', + 'verbose_name_plural': 'attendances', + 'ordering': ['event', 'member__last_name', 'member__first_name'], + 'constraints': [models.UniqueConstraint(fields=('event', 'member'), name='unique_attendance_per_event_per_member')], + }, + ), + ] diff --git a/events/migrations/0002_add_event_audience.py b/events/migrations/0002_add_event_audience.py new file mode 100644 index 0000000..af3f9f4 --- /dev/null +++ b/events/migrations/0002_add_event_audience.py @@ -0,0 +1,37 @@ +# Generated by Django 6.0.6 on 2026-07-12 15:38 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ('events', '0001_initial'), + ('members', '0002_alter_familymembership_unique_together_and_more'), + ('teams', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='excluded_members', + field=models.ManyToManyField(blank=True, related_name='excluded_from_events', to='members.member', verbose_name='excluded members'), + ), + migrations.AddField( + model_name='event', + name='invited_members', + field=models.ManyToManyField(blank=True, related_name='invited_to_events', to='members.member', verbose_name='invited members'), + ), + migrations.AddField( + model_name='event', + name='season', + field=models.ForeignKey(blank=True, help_text='Season whose team rosters define the audience; derived from the start date when left blank.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='events', to='club.season', verbose_name='season'), + ), + migrations.AddField( + model_name='event', + name='teams', + field=models.ManyToManyField(blank=True, related_name='scheduled_events', to='teams.team', verbose_name='teams'), + ), + ] diff --git a/events/migrations/0003_copy_team_to_teams.py b/events/migrations/0003_copy_team_to_teams.py new file mode 100644 index 0000000..0513bec --- /dev/null +++ b/events/migrations/0003_copy_team_to_teams.py @@ -0,0 +1,29 @@ +# Generated by Django 6.0.6 on 2026-07-12 15:38 + +from django.db import migrations + + +def copy_team_to_teams(apps, schema_editor): + Event = apps.get_model("events", "Event") + for event in Event.objects.exclude(team__isnull=True).iterator(): + event.teams.add(event.team_id) + + +def copy_teams_to_team(apps, schema_editor): + Event = apps.get_model("events", "Event") + for event in Event.objects.iterator(): + first_team = event.teams.first() + if first_team is not None: + event.team = first_team + event.save(update_fields=["team"]) + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0002_add_event_audience'), + ] + + operations = [ + migrations.RunPython(copy_team_to_teams, copy_teams_to_team), + ] diff --git a/events/migrations/0004_remove_event_team.py b/events/migrations/0004_remove_event_team.py new file mode 100644 index 0000000..402fa3a --- /dev/null +++ b/events/migrations/0004_remove_event_team.py @@ -0,0 +1,17 @@ +# Generated by Django 6.0.6 on 2026-07-12 15:38 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0003_copy_team_to_teams'), + ] + + operations = [ + migrations.RemoveField( + model_name='event', + name='team', + ), + ] diff --git a/events/migrations/__init__.py b/events/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/events/models.py b/events/models.py new file mode 100644 index 0000000..b0038a2 --- /dev/null +++ b/events/models.py @@ -0,0 +1,97 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from club.models import Season +from clubmanager.base import ClubScopedModel, UUIDModel +from members.models import Member +from teams.models import Team + + +class Opponent(ClubScopedModel): + name = models.CharField(_("name"), max_length=255) + logo = models.ImageField(_("logo"), upload_to="opponents", blank=True) + + class Meta: + verbose_name = _("opponent") + verbose_name_plural = _("opponents") + ordering = ["name"] + + def __str__(self): + return self.name + + +class Location(ClubScopedModel): + name = models.CharField(_("name"), max_length=255) + address = models.CharField(_("address"), max_length=255) + city = models.CharField(_("city"), max_length=255) + zip_code = models.CharField(_("zip code"), max_length=255) + country = models.CharField(_("country"), max_length=255) + + class Meta: + verbose_name = _("location") + verbose_name_plural = _("locations") + ordering = ["name"] + + def __str__(self): + return self.name + + +class Event(ClubScopedModel): + class EventKind(models.TextChoices): + TRAINING = "training", _("training") + MATCH = "match", _("match") + TOURNAMENT = "tournament", _("tournament") + MEETING = "meeting", _("meeting") + SOCIAL = "social", _("social") + OTHER = "other", _("other") + + teams = models.ManyToManyField(Team, related_name="scheduled_events", blank=True, verbose_name=_("teams")) + invited_members = models.ManyToManyField(Member, related_name="invited_to_events", blank=True, verbose_name=_("invited members")) + excluded_members = models.ManyToManyField(Member, related_name="excluded_from_events", blank=True, verbose_name=_("excluded members")) + season = models.ForeignKey(Season, on_delete=models.SET_NULL, related_name="events", null=True, blank=True, verbose_name=_("season"), help_text=_("Season whose team rosters define the audience; derived from the start date when left blank.")) + + kind = models.CharField(_("kind"), max_length=10, choices=EventKind.choices, default=EventKind.OTHER) + title = models.CharField(_("title"), max_length=255) + + start = models.DateTimeField(_("start")) + end = models.DateTimeField(_("end"), blank=True, null=True) + gathering = models.DateTimeField(_("gathering"), blank=True, null=True) + deadline = models.DateTimeField(_("deadline"), blank=True, null=True) + + location = models.ForeignKey(Location, on_delete=models.SET_NULL, related_name="events", null=True, blank=True, verbose_name=_("location")) + opponent = models.ForeignKey(Opponent, on_delete=models.SET_NULL, related_name="events", null=True, blank=True, verbose_name=_("opponent")) + + class Meta: + verbose_name = _("event") + verbose_name_plural = _("events") + ordering = ["-start"] + + def __str__(self): + return self.title + + +class Attendance(UUIDModel): + class AttendanceStatus(models.TextChoices): + PRESENT = "present", _("present") + ABSENT = "absent", _("absent") + EXCUSED = "excused", _("excused") + SELECTED = "selected", _("selected") + NOT_SELECTED = "not_selected", _("not selected") + MAYBE = "maybe", _("maybe") + NO_RESPONSE = "no_response", _("no response") + + event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="attendances", verbose_name=_("event")) + member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="attendances", verbose_name=_("member")) + status = models.CharField(_("status"), max_length=20, choices=AttendanceStatus.choices, default=AttendanceStatus.NO_RESPONSE) + note = models.TextField(_("note"), blank=True) + + class Meta: + verbose_name = _("attendance") + verbose_name_plural = _("attendances") + ordering = ["event", "member__last_name", "member__first_name"] + constraints = [ + models.UniqueConstraint(fields=["event", "member"], name="unique_attendance_per_event_per_member"), + ] + + def __str__(self): + return f"{self.event} - {self.member}" diff --git a/events/services/__init__.py b/events/services/__init__.py new file mode 100644 index 0000000..fa6cb40 --- /dev/null +++ b/events/services/__init__.py @@ -0,0 +1,3 @@ +from .attendance import effective_members, sync_event_attendances + +__all__ = ["effective_members", "sync_event_attendances"] diff --git a/events/services/attendance.py b/events/services/attendance.py new file mode 100644 index 0000000..004b461 --- /dev/null +++ b/events/services/attendance.py @@ -0,0 +1,59 @@ +"""Keep an event's attendance rows in sync with its effective audience. + +The audience of an event is the union of the current rosters of its ``teams`` +(for the event's season) plus any individually ``invited_members``, minus any +``excluded_members``. Attendance rows are reconciled against that set, but only +for events that are still in the future — history is never rewritten. +""" + +from django.utils import timezone + +from club.models import Season +from events.models import Attendance +from members.models import Member +from teams.models import TeamMembership + + +def resolve_season(event): + """The season whose rosters define the event's audience.""" + if event.season_id is not None: + return event.season + return Season.covering(event.club, event.start.date()) + + +def effective_members(event): + """Return the ``Member`` queryset invited to ``event``.""" + member_ids: set = set() + + season = resolve_season(event) + if season is not None: + team_ids = list(event.teams.values_list("id", flat=True)) + if team_ids: + member_ids.update(TeamMembership.objects.filter(team_id__in=team_ids, season=season).values_list("member_id", flat=True)) + + member_ids.update(event.invited_members.values_list("id", flat=True)) + member_ids.difference_update(event.excluded_members.values_list("id", flat=True)) + + return Member.objects.filter(id__in=member_ids) + + +def sync_event_attendances(event): + """Reconcile ``event``'s attendance rows with its effective audience. + + No-op for events that have already started. New members get a + ``NO_RESPONSE`` row; members no longer invited have their row removed + outright (hard reconcile). + """ + if event.start < timezone.now(): + return + + desired_ids = set(effective_members(event).values_list("id", flat=True)) + existing_ids = set(event.attendances.values_list("member_id", flat=True)) + + to_add = desired_ids - existing_ids + if to_add: + Attendance.objects.bulk_create([Attendance(event=event, member_id=member_id) for member_id in to_add]) + + to_remove = existing_ids - desired_ids + if to_remove: + event.attendances.filter(member_id__in=to_remove).delete() diff --git a/events/signals.py b/events/signals.py new file mode 100644 index 0000000..86a3293 --- /dev/null +++ b/events/signals.py @@ -0,0 +1,47 @@ +"""Signal wiring that keeps attendance in sync with the event audience. + +Registered from ``EventsConfig.ready``. Two triggers: + +* editing an event (its ``start``, or its ``teams`` / ``invited_members`` / + ``excluded_members``) re-syncs that event; +* adding or removing a member from a team roster re-syncs that team's future + events. +""" + +from django.db.models.signals import m2m_changed, post_delete, post_save +from django.dispatch import receiver +from django.utils import timezone + +from events.models import Event +from events.services import sync_event_attendances +from teams.models import TeamMembership + +M2M_SYNC_ACTIONS = {"post_add", "post_remove", "post_clear"} + + +@receiver(post_save, sender=Event) +def sync_on_event_save(sender, instance, **kwargs): + sync_event_attendances(instance) + + +@receiver(m2m_changed, sender=Event.teams.through) +@receiver(m2m_changed, sender=Event.invited_members.through) +@receiver(m2m_changed, sender=Event.excluded_members.through) +def sync_on_audience_change(sender, instance, action, reverse, model, pk_set, **kwargs): + if action not in M2M_SYNC_ACTIONS: + return + + if isinstance(instance, Event): + sync_event_attendances(instance) + else: + # Reverse relation edited (e.g. member.invited_to_events.add(event)). + for event in Event.objects.filter(pk__in=pk_set or []): + sync_event_attendances(event) + + +@receiver(post_save, sender=TeamMembership) +@receiver(post_delete, sender=TeamMembership) +def sync_on_roster_change(sender, instance, **kwargs): + now = timezone.now() + for event in Event.objects.filter(teams=instance.team_id, start__gte=now).distinct(): + sync_event_attendances(event) diff --git a/events/tests.py b/events/tests.py new file mode 100644 index 0000000..7f36a1d --- /dev/null +++ b/events/tests.py @@ -0,0 +1,161 @@ +from datetime import timedelta + +from django.db import IntegrityError +from django.test import TestCase +from django.utils import timezone + +from club.models import Club, Season +from members.models import Member +from teams.models import Position, Team, TeamMembership + +from .models import Attendance, Event, Location, Opponent +from .services import effective_members + + +class EventsTestBase(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + today = timezone.localdate() + self.season = Season.objects.create( + club=self.club, + start_date=today - timedelta(days=30), + end_date=today + timedelta(days=300), + ) + self.team = Team.objects.create(club=self.club, name="First Team", short_name="1st") + self.position = Position.objects.create(club=self.club, name="Forward", short_name="FW") + self.alice = Member.objects.create(first_name="Alice", last_name="Ash") + self.bob = Member.objects.create(first_name="Bob", last_name="Birch") + TeamMembership.objects.create(team=self.team, member=self.alice, season=self.season, position=self.position) + TeamMembership.objects.create(team=self.team, member=self.bob, season=self.season, position=self.position) + self.future = timezone.now() + timedelta(days=7) + + def make_event(self, **kwargs): + kwargs.setdefault("club", self.club) + kwargs.setdefault("title", "Training") + kwargs.setdefault("start", self.future) + if "season" not in kwargs: + kwargs["season"] = self.season + return Event.objects.create(**kwargs) + + def attendee_ids(self, event): + return set(event.attendances.values_list("member_id", flat=True)) + + +class EventModelTests(EventsTestBase): + def test_str_methods(self): + opponent = Opponent.objects.create(club=self.club, name="Rivals FC") + location = Location.objects.create(club=self.club, name="Arena", address="1 St", city="Town", zip_code="1000", country="BE") + event = self.make_event(title="Big Match") + attendance = Attendance.objects.create(event=event, member=self.alice) + + self.assertEqual(str(opponent), "Rivals FC") + self.assertEqual(str(location), "Arena") + self.assertEqual(str(event), "Big Match") + self.assertEqual(str(attendance), "Big Match - Alice Ash") + + def test_attendance_is_unique_per_event_and_member(self): + event = self.make_event() + Attendance.objects.create(event=event, member=self.alice) + + with self.assertRaises(IntegrityError): + Attendance.objects.create(event=event, member=self.alice) + + +class EffectiveMembersTests(EventsTestBase): + def test_union_of_team_invited_minus_excluded(self): + carol = Member.objects.create(first_name="Carol", last_name="Cedar") + event = self.make_event() + event.teams.set([self.team]) + event.invited_members.set([carol]) + event.excluded_members.set([self.bob]) + + self.assertEqual( + set(effective_members(event).values_list("id", flat=True)), + {self.alice.id, carol.id}, + ) + + def test_season_is_derived_from_start_date(self): + event = self.make_event(season=None) + event.teams.set([self.team]) + + self.assertEqual(self.attendee_ids(event), {self.alice.id, self.bob.id}) + + def test_no_covering_season_yields_no_team_members(self): + event = self.make_event(season=None, start=timezone.now() + timedelta(days=5000)) + event.teams.set([self.team]) + + self.assertEqual(event.attendances.count(), 0) + + +class AttendanceSyncTests(EventsTestBase): + def test_setting_teams_creates_attendance_for_roster(self): + event = self.make_event() + event.teams.set([self.team]) + + self.assertEqual(self.attendee_ids(event), {self.alice.id, self.bob.id}) + + def test_invited_member_gets_attendance(self): + carol = Member.objects.create(first_name="Carol", last_name="Cedar") + event = self.make_event() + event.invited_members.set([carol]) + + self.assertEqual(self.attendee_ids(event), {carol.id}) + + def test_reverse_invited_relation_syncs(self): + carol = Member.objects.create(first_name="Carol", last_name="Cedar") + event = self.make_event() + carol.invited_to_events.add(event) + + self.assertIn(carol.id, self.attendee_ids(event)) + + def test_excluding_member_removes_attendance(self): + event = self.make_event() + event.teams.set([self.team]) + event.excluded_members.set([self.alice]) + + self.assertEqual(self.attendee_ids(event), {self.bob.id}) + + def test_hard_reconcile_removes_even_responded_rows(self): + event = self.make_event() + event.teams.set([self.team]) + attendance = event.attendances.get(member=self.alice) + attendance.status = Attendance.AttendanceStatus.PRESENT + attendance.save() + + event.excluded_members.set([self.alice]) + + self.assertFalse(event.attendances.filter(member=self.alice).exists()) + + def test_past_event_is_not_synced(self): + event = self.make_event(start=timezone.now() - timedelta(days=1)) + event.teams.set([self.team]) + + self.assertEqual(event.attendances.count(), 0) + + +class RosterChangeSyncTests(EventsTestBase): + def test_adding_roster_member_syncs_future_events(self): + event = self.make_event() + event.teams.set([self.team]) + dave = Member.objects.create(first_name="Dave", last_name="Dogwood") + + TeamMembership.objects.create(team=self.team, member=dave, season=self.season, position=self.position) + + self.assertIn(dave.id, self.attendee_ids(event)) + + def test_removing_roster_member_syncs_future_events(self): + event = self.make_event() + event.teams.set([self.team]) + + TeamMembership.objects.get(team=self.team, member=self.alice).delete() + + self.assertNotIn(self.alice.id, self.attendee_ids(event)) + + def test_roster_change_leaves_past_events_untouched(self): + past = self.make_event(start=timezone.now() - timedelta(days=1)) + past.teams.add(self.team) + Attendance.objects.create(event=past, member=self.alice) + + TeamMembership.objects.get(team=self.team, member=self.alice).delete() + + self.assertTrue(past.attendances.filter(member=self.alice).exists()) diff --git a/events/views.py b/events/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/events/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/pyproject.toml b/pyproject.toml index d65565b..535339d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ dependencies = [ "dj-database-url>=3.1.2", "django>=6.0.6", "django-phonenumber-field[phonenumbers]>=8.4.0", + "pillow>=12.3.0", "python-decouple>=3.8", ] diff --git a/uv.lock b/uv.lock index f174501..684173e 100644 --- a/uv.lock +++ b/uv.lock @@ -19,6 +19,7 @@ dependencies = [ { name = "dj-database-url" }, { name = "django" }, { name = "django-phonenumber-field", extra = ["phonenumbers"] }, + { name = "pillow" }, { name = "python-decouple" }, ] @@ -33,6 +34,7 @@ requires-dist = [ { name = "dj-database-url", specifier = ">=3.1.2" }, { name = "django", specifier = ">=6.0.6" }, { name = "django-phonenumber-field", extras = ["phonenumbers"], specifier = ">=8.4.0" }, + { name = "pillow", specifier = ">=12.3.0" }, { name = "python-decouple", specifier = ">=3.8" }, ] @@ -133,6 +135,56 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e6/29/f7e30e3dbd3c7e3d9c4a55006112c04ee62b4765a31f21bcc28c253ac3f1/phonenumbers-9.0.33-py2.py3-none-any.whl", hash = "sha256:ba1d0da52711d5fdda6b2b673b2621fe80774fc5d1b2e5a6ef783396b0343186", size = 2595422, upload-time = "2026-06-22T10:23:29.925Z" }, ] +[[package]] +name = "pillow" +version = "12.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/01/001f65b68192f0228cc1dbbc8d2530ab5d58b61037ba0587f946fea607cd/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330", size = 4161736, upload-time = "2026-07-01T11:54:51.156Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d2/0219746d0fd16fc8a84498e79452375be3797d3ce4044596ce565164b84f/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217", size = 4255435, upload-time = "2026-07-01T11:54:53.414Z" }, + { url = "https://files.pythonhosted.org/packages/c8/02/8d0bc62ef0302318c46ff2a512822d2610e81c7aa46c9b3abe6cbaca5ad0/pillow-12.3.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930", size = 3696262, upload-time = "2026-07-01T11:54:55.739Z" }, + { url = "https://files.pythonhosted.org/packages/85/e2/73c77d218410b14f5f2d565e8a998d5317b7b9c75368d29985139f7a46f0/pillow-12.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8", size = 5350344, upload-time = "2026-07-01T11:54:57.657Z" }, + { url = "https://files.pythonhosted.org/packages/c7/da/32c752228ae345f489e3a42499d817b6c3996da7e8a3bc7a04fc806b243b/pillow-12.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0", size = 4780131, upload-time = "2026-07-01T11:54:59.713Z" }, + { url = "https://files.pythonhosted.org/packages/b1/9d/8b2c807dbef61a5197c047afe99823787eb66f63daf9fb2432f91d6f0462/pillow-12.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321", size = 6263757, upload-time = "2026-07-01T11:55:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/5c/44/c85361f65dbe00eea8576ee467c768d25129989efb76e94f205e9ca9bb46/pillow-12.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b", size = 6936962, upload-time = "2026-07-01T11:55:03.93Z" }, + { url = "https://files.pythonhosted.org/packages/18/7e/e483414b35800b86b6f08dbbc7803fb5cd52c4d6f897f47d53ea2c7e6f65/pillow-12.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198", size = 6339171, upload-time = "2026-07-01T11:55:05.989Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f4/68c491844841ede6bed70189546b3ee9731cf9f2cbad396faff5e1ccba45/pillow-12.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130", size = 7048116, upload-time = "2026-07-01T11:55:08.131Z" }, + { url = "https://files.pythonhosted.org/packages/a3/34/77f3f793fed8efc7d243f21b33c5a3f0d1c97ee70346d3db855587e155ff/pillow-12.3.0-cp314-cp314-win32.whl", hash = "sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a", size = 6467209, upload-time = "2026-07-01T11:55:10.408Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e0/492879f69d94f91f60fc8cd05ba03650e9520afebb2fb7aa12777d7c7f38/pillow-12.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d", size = 7237707, upload-time = "2026-07-01T11:55:12.745Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ac/6b11f2875f1c2ac040d84e1bbf9cf22a88038f901ca1037898b280b38365/pillow-12.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838", size = 2565995, upload-time = "2026-07-01T11:55:14.736Z" }, + { url = "https://files.pythonhosted.org/packages/52/69/c2208e56af9bfc1913afb24020297a691eb1d4ef688474c8a04913f65e04/pillow-12.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e", size = 5352503, upload-time = "2026-07-01T11:55:17.076Z" }, + { url = "https://files.pythonhosted.org/packages/07/70/e5686d753e898a45d778ff1718dba8516ead6ab6b95d85fc8c4b70650cf2/pillow-12.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17", size = 4782956, upload-time = "2026-07-01T11:55:19.448Z" }, + { url = "https://files.pythonhosted.org/packages/d5/37/25c6692f06927ee973ff18c8d9ee98ad0b4d84ee67a09610c2dd1447958e/pillow-12.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385", size = 6322855, upload-time = "2026-07-01T11:55:21.613Z" }, + { url = "https://files.pythonhosted.org/packages/cc/91/420637fcb8f1bc11029e403b4538e6694744428d8246118e45719f944556/pillow-12.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c", size = 6989642, upload-time = "2026-07-01T11:55:24.006Z" }, + { url = "https://files.pythonhosted.org/packages/10/08/b94d7811281ccf0d143a1cf768d1c49e1e54af63e7b708ab2ee3eb87face/pillow-12.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d", size = 6391281, upload-time = "2026-07-01T11:55:26.252Z" }, + { url = "https://files.pythonhosted.org/packages/d2/87/24233f785f55474dc02ce3e739c5528a77e3a862e9333d1dd7a25cc31f70/pillow-12.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931", size = 7096716, upload-time = "2026-07-01T11:55:28.318Z" }, + { url = "https://files.pythonhosted.org/packages/23/26/fcb2f6e37175b04f53570b59937867e2b80ee1685e744023153028fc14f9/pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7", size = 6474125, upload-time = "2026-07-01T11:55:30.956Z" }, + { url = "https://files.pythonhosted.org/packages/90/de/3634abee5f1c9e13c56787b7d5517b0ba8d6de51700b95578cf338349c9f/pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c", size = 7242939, upload-time = "2026-07-01T11:55:34.044Z" }, + { url = "https://files.pythonhosted.org/packages/ce/2a/fd13f8eb24de5714a6eb444a3d67e2842c6c576e159a43793adf23051351/pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45", size = 2567506, upload-time = "2026-07-01T11:55:35.988Z" }, + { url = "https://files.pythonhosted.org/packages/5d/dc/8fdce34ec725a33c81c6ba122b904d6b9024e50ea9ac7bede62fab54506c/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139", size = 4162063, upload-time = "2026-07-01T11:55:37.941Z" }, + { url = "https://files.pythonhosted.org/packages/76/66/2044b9a63d3b84ff048228dfcb7cd9bf0df983e8470971bf7d4c57b693de/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402", size = 4255549, upload-time = "2026-07-01T11:55:40.022Z" }, + { url = "https://files.pythonhosted.org/packages/52/7e/1f67e6f4ece6b582ee4b539decbcc9f848dc245a93ed8cd7338bafef72f1/pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c", size = 3696331, upload-time = "2026-07-01T11:55:41.98Z" }, + { url = "https://files.pythonhosted.org/packages/12/40/d306fc2c8e4d45d7f175c77edca7063be7b86fe7fe6e68f4353bf71d808c/pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f", size = 5350370, upload-time = "2026-07-01T11:55:44.028Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/668fb1437e8ce420f62d6106eb66e44a5971602a4d794615bdf79315d82d/pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701", size = 4780147, upload-time = "2026-07-01T11:55:46.073Z" }, + { url = "https://files.pythonhosted.org/packages/0c/08/93fa2e70e30a2d81547e481b6ee2bb9522117221fb1e0ce4b5df70967677/pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace", size = 6273659, upload-time = "2026-07-01T11:55:48.264Z" }, + { url = "https://files.pythonhosted.org/packages/f8/6d/043e96ff814fc31a33077e4cba86082167db520c93632afdf2042febbb0c/pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4", size = 6947439, upload-time = "2026-07-01T11:55:50.503Z" }, + { url = "https://files.pythonhosted.org/packages/af/92/ba71d2ee2ac0edf3fa33bd9d5ee9ee080da70b1766f3ca3934f9938ddac9/pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39", size = 6353577, upload-time = "2026-07-01T11:55:52.697Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ce/e63064e2122923ff687c8ad792d0d736a7b3920a56a46982e81a7fdd25d6/pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71", size = 7060394, upload-time = "2026-07-01T11:55:55.149Z" }, + { url = "https://files.pythonhosted.org/packages/54/76/a09cc3ccc8d773a7283d34c38bec1708f9e3cc932093cbc4c5e71ac4060b/pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827", size = 6467375, upload-time = "2026-07-01T11:55:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/1846c49ba3b1d5550392a4bbd06d6fb4578e1cd91a803198b5c90f5f7d53/pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5", size = 7237048, upload-time = "2026-07-01T11:55:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bb/89f35dcc79610423f9f195504d7def7f0d1416a711541b42867e25fe3412/pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658", size = 2566006, upload-time = "2026-07-01T11:56:02.143Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/707027ba09942dfa2c28759b5c222d769290a41c6d20ea60ec250801941f/pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf", size = 5352509, upload-time = "2026-07-01T11:56:04.2Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6d/00352fa25332c2569cd387851f568cc5a4b75a9adbfb37ac4fbce4c02eec/pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64", size = 4783167, upload-time = "2026-07-01T11:56:06.631Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/9e049dfa21af7c22427275720e2490267ba8138120add5c4c574deb69782/pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e", size = 6329237, upload-time = "2026-07-01T11:56:08.868Z" }, + { url = "https://files.pythonhosted.org/packages/36/16/cf6eeaae8d0fce8dd390a33437cf68c5d5bd73834a2bc6e2f14efda0ab45/pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777", size = 6997047, upload-time = "2026-07-01T11:56:11.379Z" }, + { url = "https://files.pythonhosted.org/packages/1e/69/dbf769bdd55f48bf5733cac28edc6364ffaa072ec9ba336266e4fe66be55/pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1", size = 6400440, upload-time = "2026-07-01T11:56:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e1/ffc9cfc2eea0d178da8018e18e959301ad9d6bc9f3edb7181e748a474b97/pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9", size = 7105895, upload-time = "2026-07-01T11:56:16.575Z" }, + { url = "https://files.pythonhosted.org/packages/18/f0/a5595c1e8c3ae44b9828cb2f0fa8155e5095ef04d6327b8f61cf44a3df85/pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8", size = 6474384, upload-time = "2026-07-01T11:56:18.855Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/62bcd9f844984c5938d3b05264a61d797a29d3e0812341a8204af70bbdee/pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418", size = 7243537, upload-time = "2026-07-01T11:56:21.214Z" }, + { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, +] + [[package]] name = "python-decouple" version = "3.8" From ca34d26a8ea60b055ba5138553c5c5c970232ab6 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 17:51:34 +0200 Subject: [PATCH 021/137] feat(events): recurring event series with occurrence sync Add EventSeries (club-scoped): an RFC-5545 rrule + dtstart + duration and a template (kind/title/location/opponent + audience M2M). Concrete Event rows are materialised occurrences carrying a series FK plus detached/cancelled flags; the series tracks excluded_dates (EXDATEs) and a generated_until horizon watermark. Recurrence service: - occurrence_datetimes expands the rrule (via python-dateutil) up to a horizon, minus EXDATEs. - generate_occurrences materialises missing rows, copies the template + audience (so attendance syncs through the existing signals), and is idempotent. - cancel_occurrence adds an EXDATE and deletes (or soft-cancels) one occurrence so it isn't regenerated; detach_occurrence marks an occurrence as independently edited; propagate_series re-applies the template to non-detached future occurrences. - extend_event_series management command rolls the horizon forward. Register EventSeries in the admin and surface series/detached/cancelled on the Event admin. Add python-dateutil. Full suite at 100% coverage. Co-Authored-By: Claude Opus 4.8 --- events/admin.py | 23 ++- events/management/__init__.py | 0 events/management/commands/__init__.py | 0 .../commands/extend_event_series.py | 20 +++ events/migrations/0005_event_series.py | 57 +++++++ events/models.py | 31 ++++ events/services/__init__.py | 21 ++- events/services/recurrence.py | 104 ++++++++++++ events/tests.py | 153 +++++++++++++++++- pyproject.toml | 1 + uv.lock | 23 +++ 11 files changed, 426 insertions(+), 7 deletions(-) create mode 100644 events/management/__init__.py create mode 100644 events/management/commands/__init__.py create mode 100644 events/management/commands/extend_event_series.py create mode 100644 events/migrations/0005_event_series.py create mode 100644 events/services/recurrence.py diff --git a/events/admin.py b/events/admin.py index 1d96b00..1f6bfb5 100644 --- a/events/admin.py +++ b/events/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from django.utils.translation import gettext_lazy as _ -from .models import Attendance, Event, Location, Opponent +from .models import Attendance, Event, EventSeries, Location, Opponent @admin.register(Opponent) @@ -24,16 +24,31 @@ class AttendanceInline(admin.TabularInline): raw_id_fields = ["member"] +@admin.register(EventSeries) +class EventSeriesAdmin(admin.ModelAdmin): + list_display = ["title", "kind", "rrule", "dtstart", "generated_until", "club"] + list_filter = ["kind", "club"] + search_fields = ["title"] + autocomplete_fields = ["location", "opponent", "teams", "invited_members", "excluded_members"] + fieldsets = [ + [None, {"fields": ["title", "kind"]}], + [_("Recurrence"), {"fields": ["rrule", "dtstart", "duration", "excluded_dates", "generated_until"]}], + [_("Audience"), {"fields": ["teams", "invited_members", "excluded_members"]}], + [_("Where"), {"fields": ["location", "opponent"]}], + ] + + @admin.register(Event) class EventAdmin(admin.ModelAdmin): - list_display = ["title", "kind", "start", "season", "club"] - list_filter = ["kind", "club", "teams"] + list_display = ["title", "kind", "start", "season", "series", "detached", "club"] + list_filter = ["kind", "club", "teams", "detached", "cancelled"] search_fields = ["title"] date_hierarchy = "start" - autocomplete_fields = ["season", "location", "opponent", "teams", "invited_members", "excluded_members"] + autocomplete_fields = ["season", "series", "location", "opponent", "teams", "invited_members", "excluded_members"] inlines = [AttendanceInline] fieldsets = [ [None, {"fields": ["title", "kind", "season"]}], + [_("Series"), {"fields": ["series", "detached", "cancelled"]}], [_("Audience"), {"fields": ["teams", "invited_members", "excluded_members"]}], [_("When"), {"fields": ["start", "end", "gathering", "deadline"]}], [_("Where"), {"fields": ["location", "opponent"]}], diff --git a/events/management/__init__.py b/events/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/events/management/commands/__init__.py b/events/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/events/management/commands/extend_event_series.py b/events/management/commands/extend_event_series.py new file mode 100644 index 0000000..bb33cbc --- /dev/null +++ b/events/management/commands/extend_event_series.py @@ -0,0 +1,20 @@ +from django.core.management.base import BaseCommand + +from events.models import EventSeries +from events.services import generate_occurrences, horizon + + +class Command(BaseCommand): + help = "Materialise recurring event occurrences up to the rolling horizon." + + def handle(self, *args, **options): + until = horizon() + total = 0 + + for series in EventSeries.objects.all(): + created = generate_occurrences(series, until) + total += len(created) + if created: + self.stdout.write(f"{series}: generated {len(created)} occurrence(s).") + + self.stdout.write(self.style.SUCCESS(f"Done. Generated {total} occurrence(s) across {EventSeries.objects.count()} series.")) diff --git a/events/migrations/0005_event_series.py b/events/migrations/0005_event_series.py new file mode 100644 index 0000000..5671fe7 --- /dev/null +++ b/events/migrations/0005_event_series.py @@ -0,0 +1,57 @@ +# Generated by Django 6.0.6 on 2026-07-12 15:47 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ('events', '0004_remove_event_team'), + ('members', '0002_alter_familymembership_unique_together_and_more'), + ('teams', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='cancelled', + field=models.BooleanField(default=False, verbose_name='cancelled'), + ), + migrations.AddField( + model_name='event', + name='detached', + field=models.BooleanField(default=False, help_text='Edited independently; excluded from series-wide updates and regeneration.', verbose_name='detached'), + ), + migrations.CreateModel( + name='EventSeries', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('rrule', models.CharField(help_text='RFC 5545 RRULE, e.g. FREQ=WEEKLY;BYDAY=MO,WE.', max_length=255, verbose_name='recurrence rule')), + ('dtstart', models.DateTimeField(verbose_name='first occurrence')), + ('duration', models.DurationField(blank=True, help_text="Length of each occurrence; sets each event's end.", null=True, verbose_name='duration')), + ('excluded_dates', models.JSONField(blank=True, default=list, help_text='ISO start datetimes of occurrences removed from the series (EXDATEs).', verbose_name='excluded dates')), + ('generated_until', models.DateTimeField(blank=True, help_text='Occurrences have been materialised up to this point.', null=True, verbose_name='generated until')), + ('kind', models.CharField(choices=[('training', 'training'), ('match', 'match'), ('tournament', 'tournament'), ('meeting', 'meeting'), ('social', 'social'), ('other', 'other')], default='other', max_length=10, verbose_name='kind')), + ('title', models.CharField(max_length=255, verbose_name='title')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ('excluded_members', models.ManyToManyField(blank=True, related_name='excluded_from_event_series', to='members.member', verbose_name='excluded members')), + ('invited_members', models.ManyToManyField(blank=True, related_name='invited_to_event_series', to='members.member', verbose_name='invited members')), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='event_series', to='events.location', verbose_name='location')), + ('opponent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='event_series', to='events.opponent', verbose_name='opponent')), + ('teams', models.ManyToManyField(blank=True, related_name='event_series', to='teams.team', verbose_name='teams')), + ], + options={ + 'verbose_name': 'event series', + 'verbose_name_plural': 'event series', + 'ordering': ['title'], + }, + ), + migrations.AddField( + model_name='event', + name='series', + field=models.ForeignKey(blank=True, help_text='The recurring series this occurrence belongs to; blank for one-off events.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='occurrences', to='events.eventseries', verbose_name='series'), + ), + ] diff --git a/events/models.py b/events/models.py index b0038a2..bce13bb 100644 --- a/events/models.py +++ b/events/models.py @@ -45,6 +45,10 @@ class Event(ClubScopedModel): SOCIAL = "social", _("social") OTHER = "other", _("other") + series = models.ForeignKey("EventSeries", on_delete=models.CASCADE, related_name="occurrences", null=True, blank=True, verbose_name=_("series"), help_text=_("The recurring series this occurrence belongs to; blank for one-off events.")) + detached = models.BooleanField(_("detached"), default=False, help_text=_("Edited independently; excluded from series-wide updates and regeneration.")) + cancelled = models.BooleanField(_("cancelled"), default=False) + teams = models.ManyToManyField(Team, related_name="scheduled_events", blank=True, verbose_name=_("teams")) invited_members = models.ManyToManyField(Member, related_name="invited_to_events", blank=True, verbose_name=_("invited members")) excluded_members = models.ManyToManyField(Member, related_name="excluded_from_events", blank=True, verbose_name=_("excluded members")) @@ -70,6 +74,33 @@ class Event(ClubScopedModel): return self.title +class EventSeries(ClubScopedModel): + """A recurring event definition that materialises concrete Event rows.""" + + rrule = models.CharField(_("recurrence rule"), max_length=255, help_text=_("RFC 5545 RRULE, e.g. FREQ=WEEKLY;BYDAY=MO,WE.")) + dtstart = models.DateTimeField(_("first occurrence")) + duration = models.DurationField(_("duration"), null=True, blank=True, help_text=_("Length of each occurrence; sets each event's end.")) + excluded_dates = models.JSONField(_("excluded dates"), default=list, blank=True, help_text=_("ISO start datetimes of occurrences removed from the series (EXDATEs).")) + generated_until = models.DateTimeField(_("generated until"), null=True, blank=True, help_text=_("Occurrences have been materialised up to this point.")) + + # Template copied onto each generated occurrence. + kind = models.CharField(_("kind"), max_length=10, choices=Event.EventKind.choices, default=Event.EventKind.OTHER) + title = models.CharField(_("title"), max_length=255) + location = models.ForeignKey(Location, on_delete=models.SET_NULL, related_name="event_series", null=True, blank=True, verbose_name=_("location")) + opponent = models.ForeignKey(Opponent, on_delete=models.SET_NULL, related_name="event_series", null=True, blank=True, verbose_name=_("opponent")) + teams = models.ManyToManyField(Team, related_name="event_series", blank=True, verbose_name=_("teams")) + invited_members = models.ManyToManyField(Member, related_name="invited_to_event_series", blank=True, verbose_name=_("invited members")) + excluded_members = models.ManyToManyField(Member, related_name="excluded_from_event_series", blank=True, verbose_name=_("excluded members")) + + class Meta: + verbose_name = _("event series") + verbose_name_plural = _("event series") + ordering = ["title"] + + def __str__(self): + return self.title + + class Attendance(UUIDModel): class AttendanceStatus(models.TextChoices): PRESENT = "present", _("present") diff --git a/events/services/__init__.py b/events/services/__init__.py index fa6cb40..8a07cde 100644 --- a/events/services/__init__.py +++ b/events/services/__init__.py @@ -1,3 +1,22 @@ from .attendance import effective_members, sync_event_attendances +from .recurrence import ( + apply_template, + cancel_occurrence, + detach_occurrence, + generate_occurrences, + horizon, + occurrence_datetimes, + propagate_series, +) -__all__ = ["effective_members", "sync_event_attendances"] +__all__ = [ + "apply_template", + "cancel_occurrence", + "detach_occurrence", + "effective_members", + "generate_occurrences", + "horizon", + "occurrence_datetimes", + "propagate_series", + "sync_event_attendances", +] diff --git a/events/services/recurrence.py b/events/services/recurrence.py new file mode 100644 index 0000000..4efc50e --- /dev/null +++ b/events/services/recurrence.py @@ -0,0 +1,104 @@ +"""Materialise concrete Event rows from a recurring EventSeries. + +Occurrences are real Event rows (so attendance attaches directly). The series +holds the RRULE, an anchor ``dtstart``, and a set of ``excluded_dates`` +(EXDATEs). Rows are generated up to a rolling horizon; a single occurrence can +be cancelled (adds an EXDATE + removes the row) or detached (edited +independently, so series-wide updates skip it). +""" + +from datetime import timedelta + +from dateutil.rrule import rrulestr +from django.utils import timezone + +from events.models import Event + +HORIZON_DAYS = 90 + + +def horizon(): + return timezone.now() + timedelta(days=HORIZON_DAYS) + + +def occurrence_datetimes(series, until): + """Expand the series' RRULE from its anchor up to ``until``, minus EXDATEs.""" + rule = rrulestr(series.rrule, dtstart=series.dtstart) + excluded = set(series.excluded_dates) + + result = [] + for occurrence in rule: + if occurrence > until: + break + if occurrence.isoformat() in excluded: + continue + result.append(occurrence) + return result + + +def apply_template(series, event): + """Copy the series template (and audience) onto ``event`` and save it. + + Setting the audience M2M fires the sync signal, so attendance follows. + """ + event.kind = series.kind + event.title = series.title + event.location = series.location + event.opponent = series.opponent + if series.duration is not None: + event.end = event.start + series.duration + event.save() + + event.teams.set(series.teams.all()) + event.invited_members.set(series.invited_members.all()) + event.excluded_members.set(series.excluded_members.all()) + + +def generate_occurrences(series, until=None): + """Materialise any missing occurrences up to ``until`` (default: horizon).""" + until = until or horizon() + existing = set(series.occurrences.values_list("start", flat=True)) + + created = [] + for start in occurrence_datetimes(series, until): + if start in existing: + continue + event = Event(club=series.club, series=series, start=start) + apply_template(series, event) + created.append(event) + + series.generated_until = until + series.save(update_fields=["generated_until"]) + return created + + +def cancel_occurrence(event, *, hard_delete=True): + """Drop a single occurrence and record an EXDATE so it isn't regenerated.""" + series = event.series + if series is not None: + iso = event.start.isoformat() + if iso not in series.excluded_dates: + series.excluded_dates = [*series.excluded_dates, iso] + series.save(update_fields=["excluded_dates"]) + + if hard_delete: + event.delete() + else: + event.cancelled = True + event.save(update_fields=["cancelled"]) + + +def detach_occurrence(event): + """Mark an occurrence as edited independently of the series.""" + event.detached = True + event.save(update_fields=["detached"]) + + +def propagate_series(series): + """Re-apply the series template to its non-detached future occurrences.""" + now = timezone.now() + updated = [] + for event in series.occurrences.filter(detached=False, start__gte=now): + apply_template(series, event) + updated.append(event) + return updated diff --git a/events/tests.py b/events/tests.py index 7f36a1d..f06071f 100644 --- a/events/tests.py +++ b/events/tests.py @@ -1,5 +1,7 @@ from datetime import timedelta +from io import StringIO +from django.core.management import call_command from django.db import IntegrityError from django.test import TestCase from django.utils import timezone @@ -8,8 +10,15 @@ from club.models import Club, Season from members.models import Member from teams.models import Position, Team, TeamMembership -from .models import Attendance, Event, Location, Opponent -from .services import effective_members +from .models import Attendance, Event, EventSeries, Location, Opponent +from .services import ( + cancel_occurrence, + detach_occurrence, + effective_members, + generate_occurrences, + occurrence_datetimes, + propagate_series, +) class EventsTestBase(TestCase): @@ -159,3 +168,143 @@ class RosterChangeSyncTests(EventsTestBase): TeamMembership.objects.get(team=self.team, member=self.alice).delete() self.assertTrue(past.attendances.filter(member=self.alice).exists()) + + +class RecurrenceTestBase(EventsTestBase): + def setUp(self): + super().setUp() + self.anchor = (timezone.now() + timedelta(days=1)).replace(microsecond=0) + + def make_series(self, **kwargs): + kwargs.setdefault("club", self.club) + kwargs.setdefault("title", "Weekly Training") + kwargs.setdefault("kind", Event.EventKind.TRAINING) + kwargs.setdefault("rrule", "FREQ=WEEKLY;COUNT=4") + kwargs.setdefault("dtstart", self.anchor) + kwargs.setdefault("duration", timedelta(hours=2)) + series = EventSeries.objects.create(**kwargs) + series.teams.set([self.team]) + return series + + +class OccurrenceExpansionTests(RecurrenceTestBase): + def test_str(self): + self.assertEqual(str(self.make_series()), "Weekly Training") + + def test_weekly_expansion(self): + series = self.make_series() + + dts = occurrence_datetimes(series, self.anchor + timedelta(days=30)) + + self.assertEqual(dts[0], self.anchor) + self.assertEqual(dts[1], self.anchor + timedelta(weeks=1)) + self.assertEqual(len(dts), 4) + + def test_until_bounds_expansion(self): + series = self.make_series() + + dts = occurrence_datetimes(series, self.anchor + timedelta(days=10)) + + self.assertEqual(len(dts), 2) + + def test_excluded_dates_are_skipped(self): + series = self.make_series() + skipped = self.anchor + timedelta(weeks=1) + series.excluded_dates = [skipped.isoformat()] + series.save() + + dts = occurrence_datetimes(series, self.anchor + timedelta(days=30)) + + self.assertNotIn(skipped, dts) + self.assertEqual(len(dts), 3) + + +class GenerateOccurrencesTests(RecurrenceTestBase): + def test_materialises_occurrences_with_template_and_attendance(self): + series = self.make_series() + + created = generate_occurrences(series, self.anchor + timedelta(days=30)) + + self.assertEqual(len(created), 4) + first = series.occurrences.order_by("start").first() + self.assertEqual(first.start, self.anchor) + self.assertEqual(first.end, self.anchor + timedelta(hours=2)) + self.assertEqual(first.title, "Weekly Training") + self.assertEqual(first.kind, Event.EventKind.TRAINING) + # Audience copied from the series, so attendance follows the roster. + self.assertEqual(set(first.attendances.values_list("member_id", flat=True)), {self.alice.id, self.bob.id}) + series.refresh_from_db() + self.assertIsNotNone(series.generated_until) + + def test_generation_is_idempotent(self): + series = self.make_series() + until = self.anchor + timedelta(days=30) + + generate_occurrences(series, until) + generate_occurrences(series, until) + + self.assertEqual(series.occurrences.count(), 4) + + +class SingleOccurrenceTests(RecurrenceTestBase): + def test_cancel_deletes_and_prevents_regeneration(self): + series = self.make_series() + until = self.anchor + timedelta(days=30) + generate_occurrences(series, until) + target = series.occurrences.order_by("start")[1] + target_start = target.start + + cancel_occurrence(target) + self.assertFalse(series.occurrences.filter(start=target_start).exists()) + + generate_occurrences(series, until) + self.assertFalse(series.occurrences.filter(start=target_start).exists()) + self.assertEqual(series.occurrences.count(), 3) + + def test_cancel_soft_marks_cancelled(self): + series = self.make_series() + generate_occurrences(series, self.anchor + timedelta(days=30)) + target = series.occurrences.order_by("start").first() + + cancel_occurrence(target, hard_delete=False) + + target.refresh_from_db() + self.assertTrue(target.cancelled) + self.assertIn(target.start.isoformat(), series.excluded_dates) + + def test_detached_occurrence_is_left_untouched_by_propagation(self): + series = self.make_series() + generate_occurrences(series, self.anchor + timedelta(days=30)) + detached = series.occurrences.order_by("start").first() + detach_occurrence(detached) + + series.title = "Renamed" + series.save() + propagate_series(series) + + detached.refresh_from_db() + self.assertEqual(detached.title, "Weekly Training") + other = series.occurrences.exclude(pk=detached.pk).order_by("start").first() + self.assertEqual(other.title, "Renamed") + + def test_propagation_updates_audience_and_attendance(self): + series = self.make_series() + generate_occurrences(series, self.anchor + timedelta(days=30)) + carol = Member.objects.create(first_name="Carol", last_name="Cedar") + series.invited_members.set([carol]) + + propagate_series(series) + + event = series.occurrences.order_by("start").first() + self.assertIn(carol.id, set(event.attendances.values_list("member_id", flat=True))) + + +class ExtendSeriesCommandTests(RecurrenceTestBase): + def test_command_generates_occurrences(self): + series = self.make_series() + out = StringIO() + + call_command("extend_event_series", stdout=out) + + self.assertEqual(series.occurrences.count(), 4) + self.assertIn("Done.", out.getvalue()) diff --git a/pyproject.toml b/pyproject.toml index 535339d..7d8cdf5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ dependencies = [ "django>=6.0.6", "django-phonenumber-field[phonenumbers]>=8.4.0", "pillow>=12.3.0", + "python-dateutil>=2.9.0.post0", "python-decouple>=3.8", ] diff --git a/uv.lock b/uv.lock index 684173e..cb93482 100644 --- a/uv.lock +++ b/uv.lock @@ -20,6 +20,7 @@ dependencies = [ { name = "django" }, { name = "django-phonenumber-field", extra = ["phonenumbers"] }, { name = "pillow" }, + { name = "python-dateutil" }, { name = "python-decouple" }, ] @@ -35,6 +36,7 @@ requires-dist = [ { name = "django", specifier = ">=6.0.6" }, { name = "django-phonenumber-field", extras = ["phonenumbers"], specifier = ">=8.4.0" }, { name = "pillow", specifier = ">=12.3.0" }, + { name = "python-dateutil", specifier = ">=2.9.0.post0" }, { name = "python-decouple", specifier = ">=3.8" }, ] @@ -185,6 +187,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + [[package]] name = "python-decouple" version = "3.8" @@ -219,6 +233,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2d/c7/c53e8dbff9c9dc4b7928773421ae294a5d28fcb8dcda1a089579d3a7e510/ruff-0.15.17-py3-none-win_arm64.whl", hash = "sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456", size = 11355275, upload-time = "2026-06-11T17:54:43.635Z" }, ] +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + [[package]] name = "sqlparse" version = "0.5.5" From c535e5dc8c293b212ed5d942ce5668af2397512f Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 18:04:14 +0200 Subject: [PATCH 022/137] feat(events): series-level gathering and deadline offsets EventSeries gains gathering_offset and deadline_offset (durations before the start). Each generated occurrence derives its gathering/deadline from them (and clears them when unset); propagate_series pushes offset changes to non-detached future occurrences. Surface them in the admin. 100% coverage. Co-Authored-By: Claude Opus 4.8 --- events/admin.py | 3 +- .../migrations/0006_event_series_offsets.py | 23 +++++++++++++++ events/models.py | 2 ++ events/services/recurrence.py | 5 ++-- events/tests.py | 29 +++++++++++++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 events/migrations/0006_event_series_offsets.py diff --git a/events/admin.py b/events/admin.py index 1f6bfb5..22979de 100644 --- a/events/admin.py +++ b/events/admin.py @@ -32,7 +32,8 @@ class EventSeriesAdmin(admin.ModelAdmin): autocomplete_fields = ["location", "opponent", "teams", "invited_members", "excluded_members"] fieldsets = [ [None, {"fields": ["title", "kind"]}], - [_("Recurrence"), {"fields": ["rrule", "dtstart", "duration", "excluded_dates", "generated_until"]}], + [_("Recurrence"), {"fields": ["rrule", "dtstart", "excluded_dates", "generated_until"]}], + [_("Timing"), {"fields": ["duration", "gathering_offset", "deadline_offset"]}], [_("Audience"), {"fields": ["teams", "invited_members", "excluded_members"]}], [_("Where"), {"fields": ["location", "opponent"]}], ] diff --git a/events/migrations/0006_event_series_offsets.py b/events/migrations/0006_event_series_offsets.py new file mode 100644 index 0000000..3f3c10f --- /dev/null +++ b/events/migrations/0006_event_series_offsets.py @@ -0,0 +1,23 @@ +# Generated by Django 6.0.6 on 2026-07-12 16:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0005_event_series'), + ] + + operations = [ + migrations.AddField( + model_name='eventseries', + name='deadline_offset', + field=models.DurationField(blank=True, help_text="How long before the start each occurrence's sign-up deadline is.", null=True, verbose_name='deadline offset'), + ), + migrations.AddField( + model_name='eventseries', + name='gathering_offset', + field=models.DurationField(blank=True, help_text="How long before the start each occurrence's gathering time is.", null=True, verbose_name='gathering offset'), + ), + ] diff --git a/events/models.py b/events/models.py index bce13bb..2174ba7 100644 --- a/events/models.py +++ b/events/models.py @@ -80,6 +80,8 @@ class EventSeries(ClubScopedModel): rrule = models.CharField(_("recurrence rule"), max_length=255, help_text=_("RFC 5545 RRULE, e.g. FREQ=WEEKLY;BYDAY=MO,WE.")) dtstart = models.DateTimeField(_("first occurrence")) duration = models.DurationField(_("duration"), null=True, blank=True, help_text=_("Length of each occurrence; sets each event's end.")) + gathering_offset = models.DurationField(_("gathering offset"), null=True, blank=True, help_text=_("How long before the start each occurrence's gathering time is.")) + deadline_offset = models.DurationField(_("deadline offset"), null=True, blank=True, help_text=_("How long before the start each occurrence's sign-up deadline is.")) excluded_dates = models.JSONField(_("excluded dates"), default=list, blank=True, help_text=_("ISO start datetimes of occurrences removed from the series (EXDATEs).")) generated_until = models.DateTimeField(_("generated until"), null=True, blank=True, help_text=_("Occurrences have been materialised up to this point.")) diff --git a/events/services/recurrence.py b/events/services/recurrence.py index 4efc50e..ed05294 100644 --- a/events/services/recurrence.py +++ b/events/services/recurrence.py @@ -45,8 +45,9 @@ def apply_template(series, event): event.title = series.title event.location = series.location event.opponent = series.opponent - if series.duration is not None: - event.end = event.start + series.duration + event.end = event.start + series.duration if series.duration is not None else None + event.gathering = event.start - series.gathering_offset if series.gathering_offset is not None else None + event.deadline = event.start - series.deadline_offset if series.deadline_offset is not None else None event.save() event.teams.set(series.teams.all()) diff --git a/events/tests.py b/events/tests.py index f06071f..5088452 100644 --- a/events/tests.py +++ b/events/tests.py @@ -245,6 +245,24 @@ class GenerateOccurrencesTests(RecurrenceTestBase): self.assertEqual(series.occurrences.count(), 4) + def test_gathering_and_deadline_come_from_offsets(self): + series = self.make_series(gathering_offset=timedelta(minutes=30), deadline_offset=timedelta(days=1)) + + generate_occurrences(series, self.anchor + timedelta(days=30)) + + first = series.occurrences.order_by("start").first() + self.assertEqual(first.gathering, self.anchor - timedelta(minutes=30)) + self.assertEqual(first.deadline, self.anchor - timedelta(days=1)) + + def test_without_offsets_gathering_and_deadline_are_blank(self): + series = self.make_series() + + generate_occurrences(series, self.anchor + timedelta(days=30)) + + first = series.occurrences.order_by("start").first() + self.assertIsNone(first.gathering) + self.assertIsNone(first.deadline) + class SingleOccurrenceTests(RecurrenceTestBase): def test_cancel_deletes_and_prevents_regeneration(self): @@ -298,6 +316,17 @@ class SingleOccurrenceTests(RecurrenceTestBase): event = series.occurrences.order_by("start").first() self.assertIn(carol.id, set(event.attendances.values_list("member_id", flat=True))) + def test_propagation_updates_timing_offsets(self): + series = self.make_series() + generate_occurrences(series, self.anchor + timedelta(days=30)) + series.gathering_offset = timedelta(minutes=45) + series.save() + + propagate_series(series) + + first = series.occurrences.order_by("start").first() + self.assertEqual(first.gathering, self.anchor - timedelta(minutes=45)) + class ExtendSeriesCommandTests(RecurrenceTestBase): def test_command_generates_occurrences(self): From 2653dd6d087cfcc6146d7741a7ada1132baef597 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 19:22:48 +0200 Subject: [PATCH 023/137] feat(events): formal end date for recurring series MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add EventSeries.until: an explicit series end that caps occurrence generation (whichever comes first — it, the generation horizon, or the rule's own COUNT/UNTIL). Blank means open-ended. Surface it in the admin. 100% coverage. Co-Authored-By: Claude Opus 4.8 --- events/admin.py | 4 ++-- events/migrations/0007_event_series_until.py | 18 ++++++++++++++++++ events/models.py | 1 + events/services/recurrence.py | 9 ++++++++- events/tests.py | 16 ++++++++++++++++ 5 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 events/migrations/0007_event_series_until.py diff --git a/events/admin.py b/events/admin.py index 22979de..824cbcc 100644 --- a/events/admin.py +++ b/events/admin.py @@ -26,13 +26,13 @@ class AttendanceInline(admin.TabularInline): @admin.register(EventSeries) class EventSeriesAdmin(admin.ModelAdmin): - list_display = ["title", "kind", "rrule", "dtstart", "generated_until", "club"] + list_display = ["title", "kind", "rrule", "dtstart", "until", "generated_until", "club"] list_filter = ["kind", "club"] search_fields = ["title"] autocomplete_fields = ["location", "opponent", "teams", "invited_members", "excluded_members"] fieldsets = [ [None, {"fields": ["title", "kind"]}], - [_("Recurrence"), {"fields": ["rrule", "dtstart", "excluded_dates", "generated_until"]}], + [_("Recurrence"), {"fields": ["rrule", "dtstart", "until", "excluded_dates", "generated_until"]}], [_("Timing"), {"fields": ["duration", "gathering_offset", "deadline_offset"]}], [_("Audience"), {"fields": ["teams", "invited_members", "excluded_members"]}], [_("Where"), {"fields": ["location", "opponent"]}], diff --git a/events/migrations/0007_event_series_until.py b/events/migrations/0007_event_series_until.py new file mode 100644 index 0000000..299b554 --- /dev/null +++ b/events/migrations/0007_event_series_until.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.6 on 2026-07-12 17:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0006_event_series_offsets'), + ] + + operations = [ + migrations.AddField( + model_name='eventseries', + name='until', + field=models.DateTimeField(blank=True, help_text="Series end: no occurrences are generated after this. Leave blank for open-ended (bounded by the rule's own COUNT/UNTIL, if any).", null=True, verbose_name='until'), + ), + ] diff --git a/events/models.py b/events/models.py index 2174ba7..69e0c68 100644 --- a/events/models.py +++ b/events/models.py @@ -79,6 +79,7 @@ class EventSeries(ClubScopedModel): rrule = models.CharField(_("recurrence rule"), max_length=255, help_text=_("RFC 5545 RRULE, e.g. FREQ=WEEKLY;BYDAY=MO,WE.")) dtstart = models.DateTimeField(_("first occurrence")) + until = models.DateTimeField(_("until"), null=True, blank=True, help_text=_("Series end: no occurrences are generated after this. Leave blank for open-ended (bounded by the rule's own COUNT/UNTIL, if any).")) duration = models.DurationField(_("duration"), null=True, blank=True, help_text=_("Length of each occurrence; sets each event's end.")) gathering_offset = models.DurationField(_("gathering offset"), null=True, blank=True, help_text=_("How long before the start each occurrence's gathering time is.")) deadline_offset = models.DurationField(_("deadline offset"), null=True, blank=True, help_text=_("How long before the start each occurrence's sign-up deadline is.")) diff --git a/events/services/recurrence.py b/events/services/recurrence.py index ed05294..e54973e 100644 --- a/events/services/recurrence.py +++ b/events/services/recurrence.py @@ -22,7 +22,14 @@ def horizon(): def occurrence_datetimes(series, until): - """Expand the series' RRULE from its anchor up to ``until``, minus EXDATEs.""" + """Expand the series' RRULE from its anchor up to ``until``, minus EXDATEs. + + ``until`` is the generation horizon; the series' own ``until`` (its formal + end) further caps it, whichever comes first. + """ + if series.until is not None and series.until < until: + until = series.until + rule = rrulestr(series.rrule, dtstart=series.dtstart) excluded = set(series.excluded_dates) diff --git a/events/tests.py b/events/tests.py index 5088452..8af833a 100644 --- a/events/tests.py +++ b/events/tests.py @@ -218,6 +218,15 @@ class OccurrenceExpansionTests(RecurrenceTestBase): self.assertNotIn(skipped, dts) self.assertEqual(len(dts), 3) + def test_series_until_caps_expansion(self): + series = self.make_series(rrule="FREQ=WEEKLY", until=self.anchor + timedelta(days=10)) + + dts = occurrence_datetimes(series, self.anchor + timedelta(days=90)) + + # Only the anchor and the first weekly repeat fall on/before `until`. + self.assertEqual(dts, [self.anchor, self.anchor + timedelta(weeks=1)]) + self.assertTrue(all(dt <= series.until for dt in dts)) + class GenerateOccurrencesTests(RecurrenceTestBase): def test_materialises_occurrences_with_template_and_attendance(self): @@ -245,6 +254,13 @@ class GenerateOccurrencesTests(RecurrenceTestBase): self.assertEqual(series.occurrences.count(), 4) + def test_generation_stops_at_series_until(self): + series = self.make_series(rrule="FREQ=WEEKLY", until=self.anchor + timedelta(days=10)) + + generate_occurrences(series) + + self.assertEqual(series.occurrences.count(), 2) + def test_gathering_and_deadline_come_from_offsets(self): series = self.make_series(gathering_offset=timedelta(minutes=30), deadline_offset=timedelta(days=1)) From 5dd3715c1fcddd9076ee8b4020cf9b49e25ca79e Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 21:13:16 +0200 Subject: [PATCH 024/137] feat(formbuilder): fix tenancy bugs, add admin + services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix multi-tenancy/integrity bugs in the models: Form.slug and Field.key were globally unique (unique=True), so two clubs couldn't reuse a form slug and two forms couldn't reuse a field key — make slug unique per club (constraint already present) and key unique per form. Add a (submission, field) uniqueness constraint on Answer. Register all four models in the admin (Field inline on Form, Answer inline on Submission) and add formbuilder to the admin registration smoke test. Add a service layer: - submit_form(form, member, data): enforces is_active / login_required / open window / max_submissions, validates required + choice fields, and writes a Submission with Answers atomically (FormSubmissionError carries per-field errors). - build_form(form): a live django.forms.Form built from a Form's active fields, mapping each FieldType to the matching form field. - form_report(form): a tabular overview of every submission's answers plus per-value tallies for choice-type fields. Full suite at 100% coverage. Co-Authored-By: Claude Opus 4.8 --- club/tests.py | 2 +- clubmanager/settings.py | 1 + formbuilder/__init__.py | 0 formbuilder/admin.py | 50 +++ formbuilder/apps.py | 5 + formbuilder/migrations/0001_initial.py | 90 ++++++ ...er_answer_field_alter_submission_member.py | 25 ++ .../migrations/0003_fix_tenant_uniqueness.py | 31 ++ formbuilder/migrations/__init__.py | 0 formbuilder/models.py | 95 ++++++ formbuilder/services/__init__.py | 16 + formbuilder/services/form_factory.py | 35 +++ formbuilder/services/options.py | 23 ++ formbuilder/services/reporting.py | 46 +++ formbuilder/services/submission.py | 84 +++++ formbuilder/tests.py | 286 ++++++++++++++++++ formbuilder/views.py | 1 + shop/migrations/__init__.py | 0 18 files changed, 789 insertions(+), 1 deletion(-) create mode 100644 formbuilder/__init__.py create mode 100644 formbuilder/admin.py create mode 100644 formbuilder/apps.py create mode 100644 formbuilder/migrations/0001_initial.py create mode 100644 formbuilder/migrations/0002_alter_answer_field_alter_submission_member.py create mode 100644 formbuilder/migrations/0003_fix_tenant_uniqueness.py create mode 100644 formbuilder/migrations/__init__.py create mode 100644 formbuilder/models.py create mode 100644 formbuilder/services/__init__.py create mode 100644 formbuilder/services/form_factory.py create mode 100644 formbuilder/services/options.py create mode 100644 formbuilder/services/reporting.py create mode 100644 formbuilder/services/submission.py create mode 100644 formbuilder/tests.py create mode 100644 formbuilder/views.py create mode 100644 shop/migrations/__init__.py diff --git a/club/tests.py b/club/tests.py index 6c699e4..8186d95 100644 --- a/club/tests.py +++ b/club/tests.py @@ -455,7 +455,7 @@ class AdminRegistrationSmokeTests(TestCase): registered = set(django_admin.site._registry) # Concrete, non-auto-created models in these apps should all be registered. - project_apps = {"authentication", "club", "members", "teams", "events"} + project_apps = {"authentication", "club", "members", "teams", "events", "formbuilder"} for model in apps.get_models(): if model._meta.app_label not in project_apps or model._meta.auto_created: continue diff --git a/clubmanager/settings.py b/clubmanager/settings.py index a547daa..63ec28e 100644 --- a/clubmanager/settings.py +++ b/clubmanager/settings.py @@ -49,6 +49,7 @@ INSTALLED_APPS = [ "members.apps.MembersConfig", "teams.apps.TeamsConfig", "events.apps.EventsConfig", + "formbuilder.apps.FormbuilderConfig", ] MIDDLEWARE = [ diff --git a/formbuilder/__init__.py b/formbuilder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/formbuilder/admin.py b/formbuilder/admin.py new file mode 100644 index 0000000..94c6188 --- /dev/null +++ b/formbuilder/admin.py @@ -0,0 +1,50 @@ +from django.contrib import admin + +from .models import Answer, Field, Form, Submission + + +class FieldInline(admin.TabularInline): + model = Field + extra = 0 + fields = ["order", "key", "label", "field_type", "required", "is_active"] + ordering = ["order"] + + +@admin.register(Form) +class FormAdmin(admin.ModelAdmin): + list_display = ["title", "slug", "club", "is_active", "login_required", "opens_at", "closes_at"] + list_filter = ["club", "is_active", "login_required"] + search_fields = ["title", "slug"] + prepopulated_fields = {"slug": ["title"]} + inlines = [FieldInline] + + +@admin.register(Field) +class FieldAdmin(admin.ModelAdmin): + list_display = ["label", "key", "form", "field_type", "order", "required", "is_active"] + list_filter = ["field_type", "required", "is_active"] + search_fields = ["label", "key", "form__title"] + raw_id_fields = ["form"] + + +class AnswerInline(admin.TabularInline): + model = Answer + extra = 0 + raw_id_fields = ["field"] + + +@admin.register(Submission) +class SubmissionAdmin(admin.ModelAdmin): + list_display = ["form", "member", "submitted_at"] + list_filter = ["form"] + search_fields = ["form__title", "member__first_name", "member__last_name"] + raw_id_fields = ["form", "member"] + readonly_fields = ["submitted_at"] + inlines = [AnswerInline] + + +@admin.register(Answer) +class AnswerAdmin(admin.ModelAdmin): + list_display = ["submission", "field", "value"] + search_fields = ["field__label", "field__key"] + raw_id_fields = ["submission", "field"] diff --git a/formbuilder/apps.py b/formbuilder/apps.py new file mode 100644 index 0000000..28e84de --- /dev/null +++ b/formbuilder/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class FormbuilderConfig(AppConfig): + name = "formbuilder" diff --git a/formbuilder/migrations/0001_initial.py b/formbuilder/migrations/0001_initial.py new file mode 100644 index 0000000..ec03e17 --- /dev/null +++ b/formbuilder/migrations/0001_initial.py @@ -0,0 +1,90 @@ +# Generated by Django 6.0.6 on 2026-07-12 19:02 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ('members', '0002_alter_familymembership_unique_together_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Form', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('title', models.CharField(max_length=255, verbose_name='title')), + ('slug', models.SlugField(unique=True, verbose_name='slug')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('is_active', models.BooleanField(default=True, verbose_name='is active?')), + ('login_required', models.BooleanField(default=False, verbose_name='login required?')), + ('opens_at', models.DateTimeField(blank=True, null=True, verbose_name='opens at')), + ('closes_at', models.DateTimeField(blank=True, null=True, verbose_name='closes at')), + ('max_submissions_per_user', models.PositiveIntegerField(blank=True, null=True, verbose_name='max submissions per user')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ], + options={ + 'verbose_name': 'form', + 'verbose_name_plural': 'forms', + 'ordering': ['title'], + }, + ), + migrations.CreateModel( + name='Field', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('key', models.SlugField(unique=True, verbose_name='key')), + ('label', models.CharField(max_length=255, verbose_name='label')), + ('field_type', models.CharField(choices=[('text', 'text'), ('textarea', 'textarea'), ('number', 'number'), ('email', 'email'), ('date', 'date'), ('choice', 'choice'), ('multichoice', 'multichoice'), ('checkbox', 'checkbox'), ('file', 'file')], default='text', max_length=255, verbose_name='field type')), + ('required', models.BooleanField(default=True, verbose_name='required?')), + ('help_text', models.TextField(blank=True, verbose_name='help text')), + ('order', models.PositiveIntegerField(default=0, verbose_name='order')), + ('is_active', models.BooleanField(default=True, verbose_name='is active?')), + ('options', models.JSONField(blank=True, null=True, verbose_name='options')), + ('form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='fields', to='formbuilder.form', verbose_name='form')), + ], + options={ + 'verbose_name': 'field', + 'verbose_name_plural': 'fields', + 'ordering': ['form', 'order'], + }, + ), + migrations.CreateModel( + name='Submission', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('submitted_at', models.DateTimeField(auto_now_add=True, verbose_name='submitted at')), + ('form', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submissions', to='formbuilder.form', verbose_name='form')), + ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submissions', to='members.member', verbose_name='member')), + ], + options={ + 'verbose_name': 'submission', + 'verbose_name_plural': 'submissions', + 'ordering': ['-submitted_at'], + }, + ), + migrations.CreateModel( + name='Answer', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('value', models.JSONField(blank=True, null=True, verbose_name='value')), + ('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='formbuilder.field', verbose_name='field')), + ('submission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='answers', to='formbuilder.submission', verbose_name='submission')), + ], + options={ + 'verbose_name': 'answer', + 'verbose_name_plural': 'answers', + 'ordering': ['submission', 'field'], + }, + ), + migrations.AddConstraint( + model_name='form', + constraint=models.UniqueConstraint(fields=('club', 'slug'), name='unique_form_slug_per_club'), + ), + ] diff --git a/formbuilder/migrations/0002_alter_answer_field_alter_submission_member.py b/formbuilder/migrations/0002_alter_answer_field_alter_submission_member.py new file mode 100644 index 0000000..017078c --- /dev/null +++ b/formbuilder/migrations/0002_alter_answer_field_alter_submission_member.py @@ -0,0 +1,25 @@ +# Generated by Django 6.0.6 on 2026-07-12 19:04 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('formbuilder', '0001_initial'), + ('members', '0002_alter_familymembership_unique_together_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='answer', + name='field', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='answers', to='formbuilder.field', verbose_name='field'), + ), + migrations.AlterField( + model_name='submission', + name='member', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='submissions', to='members.member', verbose_name='member'), + ), + ] diff --git a/formbuilder/migrations/0003_fix_tenant_uniqueness.py b/formbuilder/migrations/0003_fix_tenant_uniqueness.py new file mode 100644 index 0000000..c3749f0 --- /dev/null +++ b/formbuilder/migrations/0003_fix_tenant_uniqueness.py @@ -0,0 +1,31 @@ +# Generated by Django 6.0.6 on 2026-07-12 19:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('formbuilder', '0002_alter_answer_field_alter_submission_member'), + ] + + operations = [ + migrations.AlterField( + model_name='field', + name='key', + field=models.SlugField(verbose_name='key'), + ), + migrations.AlterField( + model_name='form', + name='slug', + field=models.SlugField(verbose_name='slug'), + ), + migrations.AddConstraint( + model_name='answer', + constraint=models.UniqueConstraint(fields=('submission', 'field'), name='unique_answer_per_field_per_submission'), + ), + migrations.AddConstraint( + model_name='field', + constraint=models.UniqueConstraint(fields=('form', 'key'), name='unique_field_key_per_form'), + ), + ] diff --git a/formbuilder/migrations/__init__.py b/formbuilder/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/formbuilder/models.py b/formbuilder/models.py new file mode 100644 index 0000000..2ec8457 --- /dev/null +++ b/formbuilder/models.py @@ -0,0 +1,95 @@ +from django.db import models +from django.db.models import UniqueConstraint +from django.utils.translation import gettext_lazy as _ + +from clubmanager.base import ClubScopedModel, UUIDModel +from members.models import Member + + +class Form(ClubScopedModel): + title = models.CharField(_("title"), max_length=255) + slug = models.SlugField(_("slug")) + description = models.TextField(_("description"), blank=True) + + is_active = models.BooleanField(_("is active?"), default=True) + login_required = models.BooleanField(_("login required?"), default=False) + + opens_at = models.DateTimeField(_("opens at"), blank=True, null=True) + closes_at = models.DateTimeField(_("closes at"), blank=True, null=True) + max_submissions_per_user = models.PositiveIntegerField(_("max submissions per user"), blank=True, null=True) + + class Meta: + verbose_name = _("form") + verbose_name_plural = _("forms") + ordering = ["title"] + constraints = [ + UniqueConstraint(fields=["club", "slug"], name="unique_form_slug_per_club"), + ] + + def __str__(self): + return self.title + + +class Field(UUIDModel): + class FieldType(models.TextChoices): + TEXT = "text", _("text") + TEXTAREA = "textarea", _("textarea") + NUMBER = "number", _("number") + EMAIL = "email", _("email") + DATE = "date", _("date") + CHOICE = "choice", _("choice") + MULTICHOICE = "multichoice", _("multichoice") + CHECKBOX = "checkbox", _("checkbox") + FILE = "file", _("file") + + form = models.ForeignKey(Form, on_delete=models.CASCADE, related_name="fields", verbose_name=_("form")) + key = models.SlugField(_("key")) + label = models.CharField(_("label"), max_length=255) + field_type = models.CharField(_("field type"), max_length=255, choices=FieldType.choices, default=FieldType.TEXT) + required = models.BooleanField(_("required?"), default=True) + help_text = models.TextField(_("help text"), blank=True) + order = models.PositiveIntegerField(_("order"), default=0) + is_active = models.BooleanField(_("is active?"), default=True) + options = models.JSONField(_("options"), blank=True, null=True) + + class Meta: + verbose_name = _("field") + verbose_name_plural = _("fields") + ordering = ["form", "order"] + constraints = [ + UniqueConstraint(fields=["form", "key"], name="unique_field_key_per_form"), + ] + + def __str__(self): + return self.label + + +class Submission(UUIDModel): + form = models.ForeignKey(Form, on_delete=models.CASCADE, related_name="submissions", verbose_name=_("form")) + member = models.ForeignKey(Member, on_delete=models.SET_NULL, related_name="submissions", verbose_name=_("member"), blank=True, null=True) + submitted_at = models.DateTimeField(_("submitted at"), auto_now_add=True) + + class Meta: + verbose_name = _("submission") + verbose_name_plural = _("submissions") + ordering = ["-submitted_at"] + + def __str__(self): + return f"{self.form} - {self.member}" + + +class Answer(UUIDModel): + submission = models.ForeignKey(Submission, on_delete=models.CASCADE, related_name="answers", verbose_name=_("submission")) + field = models.ForeignKey(Field, on_delete=models.PROTECT, related_name="answers", verbose_name=_("field")) + value = models.JSONField(_("value"), blank=True, null=True) + + class Meta: + verbose_name = _("answer") + verbose_name_plural = _("answers") + ordering = ["submission", "field"] + constraints = [ + UniqueConstraint(fields=["submission", "field"], name="unique_answer_per_field_per_submission"), + ] + + def __str__(self): + return f"{self.submission} - {self.field}" diff --git a/formbuilder/services/__init__.py b/formbuilder/services/__init__.py new file mode 100644 index 0000000..0a5c57e --- /dev/null +++ b/formbuilder/services/__init__.py @@ -0,0 +1,16 @@ +from .form_factory import build_form, build_form_class +from .options import allowed_values, field_choices +from .reporting import FormReport, ReportRow, form_report +from .submission import FormSubmissionError, submit_form + +__all__ = [ + "FormReport", + "FormSubmissionError", + "ReportRow", + "allowed_values", + "build_form", + "build_form_class", + "field_choices", + "form_report", + "submit_form", +] diff --git a/formbuilder/services/form_factory.py b/formbuilder/services/form_factory.py new file mode 100644 index 0000000..13b388e --- /dev/null +++ b/formbuilder/services/form_factory.py @@ -0,0 +1,35 @@ +"""Build a live ``django.forms.Form`` class from a Form instance's fields.""" + +from django import forms + +from formbuilder.models import Field + +from .options import field_choices + +FIELD_BUILDERS = { + Field.FieldType.TEXT: lambda field, kw: forms.CharField(**kw), + Field.FieldType.TEXTAREA: lambda field, kw: forms.CharField(widget=forms.Textarea, **kw), + Field.FieldType.NUMBER: lambda field, kw: forms.DecimalField(**kw), + Field.FieldType.EMAIL: lambda field, kw: forms.EmailField(**kw), + Field.FieldType.DATE: lambda field, kw: forms.DateField(**kw), + Field.FieldType.CHOICE: lambda field, kw: forms.ChoiceField(choices=field_choices(field), **kw), + Field.FieldType.MULTICHOICE: lambda field, kw: forms.MultipleChoiceField(choices=field_choices(field), **kw), + Field.FieldType.CHECKBOX: lambda field, kw: forms.BooleanField(**kw), + Field.FieldType.FILE: lambda field, kw: forms.FileField(**kw), +} + + +def _build_field(field): + kwargs = {"required": field.required, "label": field.label, "help_text": field.help_text} + return FIELD_BUILDERS[field.field_type](field, kwargs) + + +def build_form_class(form): + """Return a ``forms.Form`` subclass with a field per active Field, keyed by ``key``.""" + fields = {field.key: _build_field(field) for field in form.fields.filter(is_active=True).order_by("order")} + return type("DynamicForm", (forms.Form,), fields) + + +def build_form(form, data=None, files=None): + """Instantiate the dynamic form, bound to ``data``/``files`` when provided.""" + return build_form_class(form)(data=data, files=files) diff --git a/formbuilder/services/options.py b/formbuilder/services/options.py new file mode 100644 index 0000000..fea825e --- /dev/null +++ b/formbuilder/services/options.py @@ -0,0 +1,23 @@ +"""Helpers for interpreting a Field's ``options`` (choice definitions). + +``options`` is a JSON list, either of plain strings (``["a", "b"]``) or of +``{"value": ..., "label": ...}`` dicts. +""" + + +def field_choices(field): + """Return ``[(value, label), ...]`` for a choice-type field.""" + choices = [] + for option in field.options or []: + if isinstance(option, dict): + value = option.get("value") + label = option.get("label", value) + else: + value = label = option + choices.append((value, label)) + return choices + + +def allowed_values(field): + """Return the set of accepted values for a choice-type field.""" + return {value for value, _ in field_choices(field)} diff --git a/formbuilder/services/reporting.py b/formbuilder/services/reporting.py new file mode 100644 index 0000000..15dc2d0 --- /dev/null +++ b/formbuilder/services/reporting.py @@ -0,0 +1,46 @@ +"""Build a tabular overview of all answers for a Form.""" + +from dataclasses import dataclass + +from formbuilder.models import Field + +CHOICE_TYPES = {Field.FieldType.CHOICE, Field.FieldType.MULTICHOICE, Field.FieldType.CHECKBOX} + + +@dataclass +class ReportRow: + submission: object + values: dict # field id -> answer value + + +@dataclass +class FormReport: + form: object + columns: list # Field instances, in display order + rows: list # ReportRow, newest submission first + summaries: dict # field id -> {stringified value: count} for choice-like fields + count: int + + +def form_report(form): + """Return a FormReport: one column per field, one row per submission.""" + columns = list(form.fields.order_by("order")) + summaries = {column.id: {} for column in columns if column.field_type in CHOICE_TYPES} + + rows = [] + submissions = form.submissions.select_related("member").prefetch_related("answers") + for submission in submissions: + values = {answer.field_id: answer.value for answer in submission.answers.all()} + rows.append(ReportRow(submission=submission, values=values)) + for field_id, bucket in summaries.items(): + if field_id in values: + _tally(bucket, values[field_id]) + + return FormReport(form=form, columns=columns, rows=rows, summaries=summaries, count=len(rows)) + + +def _tally(bucket, value): + items = value if isinstance(value, list) else [value] + for item in items: + key = str(item) + bucket[key] = bucket.get(key, 0) + 1 diff --git a/formbuilder/services/submission.py b/formbuilder/services/submission.py new file mode 100644 index 0000000..eaac775 --- /dev/null +++ b/formbuilder/services/submission.py @@ -0,0 +1,84 @@ +"""Validate and persist a submission against a Form's fields and rules.""" + +from django.db import transaction +from django.utils import timezone + +from formbuilder.models import Answer, Field, Submission + +from .options import allowed_values + + +class FormSubmissionError(Exception): + """Raised when a submission is rejected. ``errors`` maps field key -> message.""" + + def __init__(self, message, *, errors=None): + super().__init__(message) + self.errors = errors or {} + + +def _is_empty(value): + return value is None or value == "" or value == [] + + +@transaction.atomic +def submit_form(form, member, data, *, when=None): + """Create a Submission (with Answers) for ``data`` or raise FormSubmissionError.""" + when = when or timezone.now() + + _check_open(form, member, when) + cleaned = _clean_answers(form, data) + + submission = Submission.objects.create(form=form, member=member) + Answer.objects.bulk_create([Answer(submission=submission, field=field, value=value) for field, value in cleaned]) + return submission + + +def _check_open(form, member, when): + if not form.is_active: + raise FormSubmissionError("This form is not accepting submissions.") + if form.login_required and member is None: + raise FormSubmissionError("You must be signed in to submit this form.") + if form.opens_at is not None and when < form.opens_at: + raise FormSubmissionError("This form is not open yet.") + if form.closes_at is not None and when > form.closes_at: + raise FormSubmissionError("This form has closed.") + if form.max_submissions_per_user is not None and member is not None: + used = form.submissions.filter(member=member).count() + if used >= form.max_submissions_per_user: + raise FormSubmissionError("You have reached the maximum number of submissions for this form.") + + +def _clean_answers(form, data): + errors = {} + cleaned = [] + + for field in form.fields.filter(is_active=True): + raw = data.get(field.key) + if _is_empty(raw): + if field.required: + errors[field.key] = "This field is required." + continue + + message = _validate_choice(field, raw) + if message is not None: + errors[field.key] = message + continue + + cleaned.append((field, raw)) + + if errors: + raise FormSubmissionError("The submission has errors.", errors=errors) + return cleaned + + +def _validate_choice(field, raw): + if field.field_type == Field.FieldType.CHOICE: + allowed = allowed_values(field) + if allowed and raw not in allowed: + return "Select a valid choice." + elif field.field_type == Field.FieldType.MULTICHOICE: + allowed = allowed_values(field) + values = raw if isinstance(raw, list) else [raw] + if allowed and not set(values) <= allowed: + return "Select valid choices." + return None diff --git a/formbuilder/tests.py b/formbuilder/tests.py new file mode 100644 index 0000000..a959654 --- /dev/null +++ b/formbuilder/tests.py @@ -0,0 +1,286 @@ +from datetime import timedelta + +from django import forms +from django.db import IntegrityError +from django.db.models import ProtectedError +from django.test import TestCase +from django.utils import timezone + +from club.models import Club +from members.models import Member + +from .models import Answer, Field, Form, Submission +from .services import ( + FormSubmissionError, + build_form, + build_form_class, + field_choices, + form_report, + submit_form, +) + + +class FormbuilderTestBase(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.form = Form.objects.create(club=self.club, title="Sign-up", slug="sign-up") + self.name = Field.objects.create(form=self.form, key="name", label="Name", field_type=Field.FieldType.TEXT, required=True, order=1) + self.size = Field.objects.create(form=self.form, key="size", label="Shirt size", field_type=Field.FieldType.CHOICE, required=False, order=2, options=["S", "M", "L"]) + self.member = Member.objects.create(first_name="Jane", last_name="Doe") + + +class ModelTests(FormbuilderTestBase): + def test_str_methods(self): + submission = Submission.objects.create(form=self.form, member=self.member) + answer = Answer.objects.create(submission=submission, field=self.name, value="Jane") + + self.assertEqual(str(self.form), "Sign-up") + self.assertEqual(str(self.name), "Name") + self.assertEqual(str(submission), "Sign-up - Jane Doe") + self.assertEqual(str(answer), "Sign-up - Jane Doe - Name") + + def test_slug_is_unique_per_club_not_globally(self): + other_club = Club.objects.create(name="Rival FC", slug="rival-fc") + Form.objects.create(club=other_club, title="Sign-up", slug="sign-up") + + with self.assertRaises(IntegrityError): + Form.objects.create(club=self.club, title="Another", slug="sign-up") + + def test_key_is_unique_per_form_not_globally(self): + other_form = Form.objects.create(club=self.club, title="Other", slug="other") + Field.objects.create(form=other_form, key="name", label="Name", order=1) + + with self.assertRaises(IntegrityError): + Field.objects.create(form=self.form, key="name", label="Duplicate", order=9) + + def test_answer_is_unique_per_field_per_submission(self): + submission = Submission.objects.create(form=self.form, member=self.member) + Answer.objects.create(submission=submission, field=self.name, value="Jane") + + with self.assertRaises(IntegrityError): + Answer.objects.create(submission=submission, field=self.name, value="Again") + + def test_deleting_form_cascades_to_fields_and_submissions(self): + Submission.objects.create(form=self.form, member=self.member) + + self.form.delete() + + self.assertFalse(Field.objects.exists()) + self.assertFalse(Submission.objects.exists()) + + def test_field_referenced_by_answer_is_protected(self): + submission = Submission.objects.create(form=self.form, member=self.member) + Answer.objects.create(submission=submission, field=self.name, value="Jane") + + with self.assertRaises(ProtectedError): + self.name.delete() + + +class FieldChoicesTests(FormbuilderTestBase): + def test_string_options(self): + self.assertEqual(field_choices(self.size), [("S", "S"), ("M", "M"), ("L", "L")]) + + def test_dict_options(self): + field = Field.objects.create(form=self.form, key="tier", label="Tier", field_type=Field.FieldType.CHOICE, order=3, options=[{"value": "a", "label": "Gold"}, {"value": "b", "label": "Silver"}]) + + self.assertEqual(field_choices(field), [("a", "Gold"), ("b", "Silver")]) + + def test_no_options(self): + self.assertEqual(field_choices(self.name), []) + + +class SubmitFormTests(FormbuilderTestBase): + def test_successful_submission_creates_submission_and_answers(self): + submission = submit_form(self.form, self.member, {"name": "Jane", "size": "M"}) + + self.assertEqual(submission.form, self.form) + self.assertEqual(submission.member, self.member) + self.assertEqual(submission.answers.count(), 2) + self.assertEqual(submission.answers.get(field=self.name).value, "Jane") + + def test_optional_field_can_be_omitted(self): + submission = submit_form(self.form, self.member, {"name": "Jane"}) + + self.assertEqual(submission.answers.count(), 1) + + def test_missing_required_field_is_rejected(self): + with self.assertRaises(FormSubmissionError) as ctx: + submit_form(self.form, self.member, {"size": "M"}) + + self.assertIn("name", ctx.exception.errors) + self.assertFalse(Submission.objects.exists()) + + def test_inactive_form_is_rejected(self): + self.form.is_active = False + self.form.save() + + with self.assertRaises(FormSubmissionError): + submit_form(self.form, self.member, {"name": "Jane"}) + + def test_login_required_without_member_is_rejected(self): + self.form.login_required = True + self.form.save() + + with self.assertRaises(FormSubmissionError): + submit_form(self.form, None, {"name": "Jane"}) + + def test_anonymous_submission_allowed_when_login_not_required(self): + submission = submit_form(self.form, None, {"name": "Jane"}) + + self.assertIsNone(submission.member) + + def test_not_open_yet_is_rejected(self): + self.form.opens_at = timezone.now() + timedelta(days=1) + self.form.save() + + with self.assertRaises(FormSubmissionError): + submit_form(self.form, self.member, {"name": "Jane"}) + + def test_closed_form_is_rejected(self): + self.form.closes_at = timezone.now() - timedelta(days=1) + self.form.save() + + with self.assertRaises(FormSubmissionError): + submit_form(self.form, self.member, {"name": "Jane"}) + + def test_open_window_respects_when_argument(self): + self.form.opens_at = timezone.now() + timedelta(days=1) + self.form.save() + + submission = submit_form(self.form, self.member, {"name": "Jane"}, when=timezone.now() + timedelta(days=2)) + + self.assertTrue(Submission.objects.filter(pk=submission.pk).exists()) + + def test_max_submissions_per_user_is_enforced(self): + self.form.max_submissions_per_user = 1 + self.form.save() + submit_form(self.form, self.member, {"name": "Jane"}) + + with self.assertRaises(FormSubmissionError): + submit_form(self.form, self.member, {"name": "Jane"}) + + def test_invalid_choice_is_rejected(self): + with self.assertRaises(FormSubmissionError) as ctx: + submit_form(self.form, self.member, {"name": "Jane", "size": "XL"}) + + self.assertIn("size", ctx.exception.errors) + + def test_multichoice_validation(self): + field = Field.objects.create(form=self.form, key="days", label="Days", field_type=Field.FieldType.MULTICHOICE, required=False, order=3, options=["mon", "tue", "wed"]) + + submit_form(self.form, self.member, {"name": "Jane", "days": ["mon", "wed"]}) + with self.assertRaises(FormSubmissionError): + submit_form(self.form, self.member, {"name": "Joe", "days": ["mon", "sun"]}) + self.assertEqual(Answer.objects.filter(field=field).count(), 1) + + def test_inactive_field_is_ignored(self): + Field.objects.create(form=self.form, key="secret", label="Secret", field_type=Field.FieldType.TEXT, required=True, is_active=False, order=4) + + submission = submit_form(self.form, self.member, {"name": "Jane"}) + + self.assertFalse(submission.answers.filter(field__key="secret").exists()) + + def test_unknown_keys_are_ignored(self): + submission = submit_form(self.form, self.member, {"name": "Jane", "bogus": "x"}) + + self.assertEqual(submission.answers.count(), 1) + + +class BuildFormTests(FormbuilderTestBase): + def make_all_field_types_form(self): + form = Form.objects.create(club=self.club, title="All", slug="all") + specs = [ + ("f_text", Field.FieldType.TEXT, forms.CharField), + ("f_area", Field.FieldType.TEXTAREA, forms.CharField), + ("f_num", Field.FieldType.NUMBER, forms.DecimalField), + ("f_email", Field.FieldType.EMAIL, forms.EmailField), + ("f_date", Field.FieldType.DATE, forms.DateField), + ("f_choice", Field.FieldType.CHOICE, forms.ChoiceField), + ("f_multi", Field.FieldType.MULTICHOICE, forms.MultipleChoiceField), + ("f_check", Field.FieldType.CHECKBOX, forms.BooleanField), + ("f_file", Field.FieldType.FILE, forms.FileField), + ] + for i, (key, field_type, _) in enumerate(specs): + Field.objects.create(form=form, key=key, label=key, field_type=field_type, required=False, order=i, options=["a", "b"]) + return form, specs + + def test_field_classes_map_from_field_type(self): + form, specs = self.make_all_field_types_form() + + instance = build_form_class(form)() + + for key, _, expected in specs: + with self.subTest(key=key): + self.assertIsInstance(instance.fields[key], expected) + + def test_required_and_labels_propagate(self): + instance = build_form_class(self.form)() + + self.assertTrue(instance.fields["name"].required) + self.assertFalse(instance.fields["size"].required) + self.assertEqual(instance.fields["name"].label, "Name") + + def test_choice_field_gets_choices(self): + instance = build_form(self.form) + + self.assertEqual(list(instance.fields["size"].choices), [("S", "S"), ("M", "M"), ("L", "L")]) + + def test_bound_form_validates(self): + bound = build_form(self.form, data={"name": "Jane", "size": "M"}) + + self.assertTrue(bound.is_valid()) + self.assertEqual(bound.cleaned_data["name"], "Jane") + + def test_bound_form_reports_required_error(self): + bound = build_form(self.form, data={"size": "M"}) + + self.assertFalse(bound.is_valid()) + self.assertIn("name", bound.errors) + + def test_inactive_fields_are_excluded(self): + Field.objects.create(form=self.form, key="hidden", label="Hidden", is_active=False, order=9) + + instance = build_form(self.form) + + self.assertNotIn("hidden", instance.fields) + + +class FormReportTests(FormbuilderTestBase): + def test_columns_are_fields_in_order(self): + report = form_report(self.form) + + self.assertEqual(report.columns, [self.name, self.size]) + + def test_rows_carry_answer_values(self): + submit_form(self.form, self.member, {"name": "Jane", "size": "M"}) + + report = form_report(self.form) + + self.assertEqual(report.count, 1) + row = report.rows[0] + self.assertEqual(row.values[self.name.id], "Jane") + self.assertEqual(row.values[self.size.id], "M") + + def test_choice_field_is_summarised(self): + submit_form(self.form, self.member, {"name": "Jane", "size": "M"}) + submit_form(self.form, Member.objects.create(first_name="Joe", last_name="Roe"), {"name": "Joe", "size": "M"}) + submit_form(self.form, Member.objects.create(first_name="Kim", last_name="Ash"), {"name": "Kim", "size": "L"}) + + report = form_report(self.form) + + self.assertEqual(report.summaries[self.size.id], {"M": 2, "L": 1}) + self.assertNotIn(self.name.id, report.summaries) + + def test_multichoice_values_are_tallied_per_option(self): + days = Field.objects.create(form=self.form, key="days", label="Days", field_type=Field.FieldType.MULTICHOICE, required=False, order=3, options=["mon", "tue"]) + submit_form(self.form, self.member, {"name": "Jane", "days": ["mon", "tue"]}) + + report = form_report(self.form) + + self.assertEqual(report.summaries[days.id], {"mon": 1, "tue": 1}) + + def test_empty_form_reports_no_rows(self): + report = form_report(self.form) + + self.assertEqual(report.count, 0) + self.assertEqual(report.rows, []) diff --git a/formbuilder/views.py b/formbuilder/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/formbuilder/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/shop/migrations/__init__.py b/shop/migrations/__init__.py new file mode 100644 index 0000000..e69de29 From 57f20fe5448836ba1d88cdaf99c1c8cc86cbfe65 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 21:13:32 +0200 Subject: [PATCH 025/137] chore(shop): scaffold empty shop app startapp scaffold registered in INSTALLED_APPS; no models yet. Co-Authored-By: Claude Opus 4.8 --- clubmanager/settings.py | 1 + shop/__init__.py | 0 shop/admin.py | 1 + shop/apps.py | 5 +++++ shop/models.py | 1 + shop/tests.py | 1 + shop/views.py | 1 + 7 files changed, 10 insertions(+) create mode 100644 shop/__init__.py create mode 100644 shop/admin.py create mode 100644 shop/apps.py create mode 100644 shop/models.py create mode 100644 shop/tests.py create mode 100644 shop/views.py diff --git a/clubmanager/settings.py b/clubmanager/settings.py index 63ec28e..951cfa5 100644 --- a/clubmanager/settings.py +++ b/clubmanager/settings.py @@ -50,6 +50,7 @@ INSTALLED_APPS = [ "teams.apps.TeamsConfig", "events.apps.EventsConfig", "formbuilder.apps.FormbuilderConfig", + "shop.apps.ShopConfig", ] MIDDLEWARE = [ diff --git a/shop/__init__.py b/shop/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/shop/admin.py b/shop/admin.py new file mode 100644 index 0000000..846f6b4 --- /dev/null +++ b/shop/admin.py @@ -0,0 +1 @@ +# Register your models here. diff --git a/shop/apps.py b/shop/apps.py new file mode 100644 index 0000000..7e18afb --- /dev/null +++ b/shop/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ShopConfig(AppConfig): + name = "shop" diff --git a/shop/models.py b/shop/models.py new file mode 100644 index 0000000..6b20219 --- /dev/null +++ b/shop/models.py @@ -0,0 +1 @@ +# Create your models here. diff --git a/shop/tests.py b/shop/tests.py new file mode 100644 index 0000000..a39b155 --- /dev/null +++ b/shop/tests.py @@ -0,0 +1 @@ +# Create your tests here. diff --git a/shop/views.py b/shop/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/shop/views.py @@ -0,0 +1 @@ +# Create your views here. From 54aace8abbe93ec0b330a985e5297942fa510a85 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Sun, 12 Jul 2026 21:41:59 +0200 Subject: [PATCH 026/137] feat: auto-populate slug fields on save Add clubmanager.base.unique_slugify(instance, value, scope=...): slugify a source value, truncate to the field's max_length, and append -2/-3/... to stay unique within a scope. ClubScopedModel gains a slug_source hook that fills a blank slug (unique per club) on save. Wire it up so every SlugField auto-populates from its natural source when left blank (explicit values are always kept): - shop.Product.slug <- name (per club) - formbuilder.Form.slug <- title (per club) - formbuilder.Field.key <- label (per form) Club.slug already auto-populated; refactor it onto the shared helper. Also fix shop.Product.slug's multi-tenancy bug: it was globally unique (unique=True); make it unique per club like the others. Migrations added. Full suite at 100% coverage. Co-Authored-By: Claude Opus 4.8 --- club/models.py | 15 +------ clubmanager/base.py | 32 +++++++++++++ .../0004_alter_field_key_alter_form_slug.py | 23 ++++++++++ formbuilder/models.py | 13 ++++-- formbuilder/tests.py | 45 +++++++++++++++++++ shop/migrations/0001_initial.py | 29 ++++++++++++ .../0002_alter_product_slug_and_more.py | 23 ++++++++++ shop/models.py | 18 +++++++- shop/tests.py | 40 ++++++++++++++++- 9 files changed, 220 insertions(+), 18 deletions(-) create mode 100644 formbuilder/migrations/0004_alter_field_key_alter_form_slug.py create mode 100644 shop/migrations/0001_initial.py create mode 100644 shop/migrations/0002_alter_product_slug_and_more.py diff --git a/club/models.py b/club/models.py index 8b02009..0a20cb5 100644 --- a/club/models.py +++ b/club/models.py @@ -2,10 +2,9 @@ import datetime from django.db import models from django.utils import timezone -from django.utils.text import slugify from django.utils.translation import gettext_lazy as _ -from clubmanager.base import ClubScopedModel, UUIDModel +from clubmanager.base import ClubScopedModel, UUIDModel, unique_slugify from members.models import Member @@ -33,19 +32,9 @@ class Club(UUIDModel): def save(self, *args, **kwargs): if not self.slug: - self.slug = self._unique_slug() + self.slug = unique_slugify(self, self.name) super().save(*args, **kwargs) - def _unique_slug(self): - base = slugify(self.name) or "club" - slug = base - suffix = 2 - existing = Club.objects.exclude(pk=self.pk) - while existing.filter(slug=slug).exists(): - slug = f"{base}-{suffix}" - suffix += 1 - return slug - class Season(ClubScopedModel): start_date = models.DateField(_("start date")) diff --git a/clubmanager/base.py b/clubmanager/base.py index 3e607e8..a5b2716 100644 --- a/clubmanager/base.py +++ b/clubmanager/base.py @@ -2,6 +2,7 @@ import uuid from typing import TYPE_CHECKING from django.db import models +from django.utils.text import slugify from club.tenancy import require_current_club @@ -9,6 +10,30 @@ if TYPE_CHECKING: from club.models import Club +def unique_slugify(instance, value, *, slug_field="slug", scope=None): + """Return a slug derived from ``value``, unique within ``scope``. + + Truncates to the slug field's ``max_length`` and appends ``-2``, ``-3``, … + on collision. ``scope`` is a dict of field lookups the uniqueness is + checked within (e.g. ``{"club": club}`` for per-club, ``{}``/``None`` for + global). + """ + max_length = instance._meta.get_field(slug_field).max_length + base = slugify(value)[:max_length] or "item" + + queryset = type(instance)._default_manager.exclude(pk=instance.pk) + if scope: + queryset = queryset.filter(**scope) + + slug = base + suffix = 2 + while queryset.filter(**{slug_field: slug}).exists(): + tail = f"-{suffix}" + slug = f"{base[: max_length - len(tail)]}{tail}" + suffix += 1 + return slug + + class TenantQuerySet(models.QuerySet): def for_club(self, club: Club): return self.filter(club=club) @@ -32,6 +57,10 @@ class ClubScopedModel(UUIDModel): club = models.ForeignKey("club.Club", on_delete=models.CASCADE, related_name="%(class)ss") objects = TenantQuerySet.as_manager() + # Subclasses with a ``slug`` field set this to the source field name (e.g. + # "name"/"title") to auto-populate the slug — unique per club — on save. + slug_source = None + class Meta: abstract = True @@ -39,4 +68,7 @@ class ClubScopedModel(UUIDModel): if self.club_id is None: self.club = require_current_club() + if self.slug_source and not self.slug: + self.slug = unique_slugify(self, getattr(self, self.slug_source), scope={"club": self.club}) + super().save(*args, **kwargs) diff --git a/formbuilder/migrations/0004_alter_field_key_alter_form_slug.py b/formbuilder/migrations/0004_alter_field_key_alter_form_slug.py new file mode 100644 index 0000000..2066841 --- /dev/null +++ b/formbuilder/migrations/0004_alter_field_key_alter_form_slug.py @@ -0,0 +1,23 @@ +# Generated by Django 6.0.6 on 2026-07-12 19:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('formbuilder', '0003_fix_tenant_uniqueness'), + ] + + operations = [ + migrations.AlterField( + model_name='field', + name='key', + field=models.SlugField(blank=True, verbose_name='key'), + ), + migrations.AlterField( + model_name='form', + name='slug', + field=models.SlugField(blank=True, verbose_name='slug'), + ), + ] diff --git a/formbuilder/models.py b/formbuilder/models.py index 2ec8457..9f8237e 100644 --- a/formbuilder/models.py +++ b/formbuilder/models.py @@ -2,15 +2,17 @@ from django.db import models from django.db.models import UniqueConstraint from django.utils.translation import gettext_lazy as _ -from clubmanager.base import ClubScopedModel, UUIDModel +from clubmanager.base import ClubScopedModel, UUIDModel, unique_slugify from members.models import Member class Form(ClubScopedModel): title = models.CharField(_("title"), max_length=255) - slug = models.SlugField(_("slug")) + slug = models.SlugField(_("slug"), blank=True) description = models.TextField(_("description"), blank=True) + slug_source = "title" + is_active = models.BooleanField(_("is active?"), default=True) login_required = models.BooleanField(_("login required?"), default=False) @@ -43,7 +45,7 @@ class Field(UUIDModel): FILE = "file", _("file") form = models.ForeignKey(Form, on_delete=models.CASCADE, related_name="fields", verbose_name=_("form")) - key = models.SlugField(_("key")) + key = models.SlugField(_("key"), blank=True) label = models.CharField(_("label"), max_length=255) field_type = models.CharField(_("field type"), max_length=255, choices=FieldType.choices, default=FieldType.TEXT) required = models.BooleanField(_("required?"), default=True) @@ -60,6 +62,11 @@ class Field(UUIDModel): UniqueConstraint(fields=["form", "key"], name="unique_field_key_per_form"), ] + def save(self, *args, **kwargs): + if not self.key: + self.key = unique_slugify(self, self.label, slug_field="key", scope={"form": self.form}) + super().save(*args, **kwargs) + def __str__(self): return self.label diff --git a/formbuilder/tests.py b/formbuilder/tests.py index a959654..d0610ca 100644 --- a/formbuilder/tests.py +++ b/formbuilder/tests.py @@ -76,6 +76,51 @@ class ModelTests(FormbuilderTestBase): self.name.delete() +class FormSlugTests(FormbuilderTestBase): + def test_slug_auto_populated_from_title(self): + form = Form.objects.create(club=self.club, title="Registration Form") + + self.assertEqual(form.slug, "registration-form") + + def test_explicit_slug_is_preserved(self): + form = Form.objects.create(club=self.club, title="Registration", slug="reg") + + self.assertEqual(form.slug, "reg") + + def test_slug_is_unique_per_club_with_suffix(self): + first = Form.objects.create(club=self.club, title="Registration") + second = Form.objects.create(club=self.club, title="Registration") + + self.assertEqual(first.slug, "registration") + self.assertEqual(second.slug, "registration-2") + + +class FieldKeyTests(FormbuilderTestBase): + def test_key_auto_populated_from_label(self): + field = Field.objects.create(form=self.form, label="First Name", order=5) + + self.assertEqual(field.key, "first-name") + + def test_explicit_key_is_preserved(self): + field = Field.objects.create(form=self.form, key="fn", label="First Name", order=5) + + self.assertEqual(field.key, "fn") + + def test_key_is_unique_per_form_with_suffix(self): + first = Field.objects.create(form=self.form, label="First Name", order=5) + second = Field.objects.create(form=self.form, label="First Name", order=6) + + self.assertEqual(first.key, "first-name") + self.assertEqual(second.key, "first-name-2") + + def test_same_key_allowed_in_a_different_form(self): + other_form = Form.objects.create(club=self.club, title="Other", slug="other") + here = Field.objects.create(form=self.form, label="First Name", order=5) + there = Field.objects.create(form=other_form, label="First Name", order=1) + + self.assertEqual(here.key, there.key) + + class FieldChoicesTests(FormbuilderTestBase): def test_string_options(self): self.assertEqual(field_choices(self.size), [("S", "S"), ("M", "M"), ("L", "L")]) diff --git a/shop/migrations/0001_initial.py b/shop/migrations/0001_initial.py new file mode 100644 index 0000000..8a5fd72 --- /dev/null +++ b/shop/migrations/0001_initial.py @@ -0,0 +1,29 @@ +# Generated by Django 6.0.6 on 2026-07-12 19:29 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Product', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('slug', models.SlugField(max_length=255, unique=True, verbose_name='slug')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/shop/migrations/0002_alter_product_slug_and_more.py b/shop/migrations/0002_alter_product_slug_and_more.py new file mode 100644 index 0000000..9b865f4 --- /dev/null +++ b/shop/migrations/0002_alter_product_slug_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 6.0.6 on 2026-07-12 19:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ('shop', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='product', + name='slug', + field=models.SlugField(blank=True, max_length=255, verbose_name='slug'), + ), + migrations.AddConstraint( + model_name='product', + constraint=models.UniqueConstraint(fields=('club', 'slug'), name='unique_product_slug_per_club'), + ), + ] diff --git a/shop/models.py b/shop/models.py index 6b20219..ac6877f 100644 --- a/shop/models.py +++ b/shop/models.py @@ -1 +1,17 @@ -# Create your models here. +from django.db import models +from django.db.models import UniqueConstraint +from django.utils.translation import gettext_lazy as _ + +from clubmanager.base import ClubScopedModel + + +class Product(ClubScopedModel): + name = models.CharField(_("name"), max_length=255) + slug = models.SlugField(_("slug"), max_length=255, blank=True) + + slug_source = "name" + + class Meta: + constraints = [ + UniqueConstraint(fields=["club", "slug"], name="unique_product_slug_per_club"), + ] diff --git a/shop/tests.py b/shop/tests.py index a39b155..bd1b8ec 100644 --- a/shop/tests.py +++ b/shop/tests.py @@ -1 +1,39 @@ -# Create your tests here. +from django.test import TestCase + +from club.models import Club + +from .models import Product + + +class ProductSlugTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + + def test_slug_auto_populated_from_name(self): + product = Product.objects.create(club=self.club, name="Home Jersey") + + self.assertEqual(product.slug, "home-jersey") + + def test_explicit_slug_is_preserved(self): + product = Product.objects.create(club=self.club, name="Home Jersey", slug="custom") + + self.assertEqual(product.slug, "custom") + + def test_slug_is_unique_per_club_with_suffix(self): + first = Product.objects.create(club=self.club, name="Home Jersey") + second = Product.objects.create(club=self.club, name="Home Jersey") + + self.assertEqual(first.slug, "home-jersey") + self.assertEqual(second.slug, "home-jersey-2") + + def test_same_slug_allowed_in_a_different_club(self): + other = Club.objects.create(name="Rival FC", slug="rival-fc") + here = Product.objects.create(club=self.club, name="Home Jersey") + there = Product.objects.create(club=other, name="Home Jersey") + + self.assertEqual(here.slug, there.slug) + + def test_unsluggable_name_falls_back(self): + product = Product.objects.create(club=self.club, name="###") + + self.assertEqual(product.slug, "item") From c320931595f1c147429b95a9d32b7dc3b9e2af91 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 14:41:57 +0200 Subject: [PATCH 027/137] feat(core): add cross-club scope validator Add clubmanager.base.validate_club_scope(instance, owning_club_id, ...): a shared model-clean() helper that rejects FKs leaking across clubs. Club-scoped FKs must share the owning club; Member FKs must have a ClubMembership in it. Unset FKs are skipped. Nothing enforced tenant consistency on the FKs between club-scoped rows, so an order could reference another club's product, an event another club's season, and so on. The following commits wire this into each app's clean(). Co-Authored-By: Claude Opus 4.8 --- club/services/__init__.py | 0 club/services/access.py | 0 clubmanager/base.py | 26 ++++++++++++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 club/services/__init__.py create mode 100644 club/services/access.py diff --git a/club/services/__init__.py b/club/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/club/services/access.py b/club/services/access.py new file mode 100644 index 0000000..e69de29 diff --git a/clubmanager/base.py b/clubmanager/base.py index a5b2716..173b25a 100644 --- a/clubmanager/base.py +++ b/clubmanager/base.py @@ -1,8 +1,10 @@ import uuid from typing import TYPE_CHECKING +from django.core.exceptions import ValidationError from django.db import models from django.utils.text import slugify +from django.utils.translation import gettext_lazy as _ from club.tenancy import require_current_club @@ -10,6 +12,30 @@ if TYPE_CHECKING: from club.models import Club +def validate_club_scope(instance, owning_club_id, *, same_club_fields=(), member_fields=()): + """Reject FKs that leak across clubs. + + ``same_club_fields`` are FKs to club-scoped models that must share + ``owning_club_id``; ``member_fields`` are Member FKs whose target must have + a ClubMembership in that club. Unset (None) FKs are skipped. Call from a + model's ``clean()``. + """ + errors = {} + for field in same_club_fields: + if getattr(instance, f"{field}_id") is not None and getattr(instance, field).club_id != owning_club_id: + errors[field] = _("Must belong to the same club.") + + if member_fields: + from club.models import ClubMembership + + for field in member_fields: + if getattr(instance, f"{field}_id") is not None and not ClubMembership.objects.filter(club_id=owning_club_id, member=getattr(instance, field)).exists(): + errors[field] = _("Must be a member of this club.") + + if errors: + raise ValidationError(errors) + + def unique_slugify(instance, value, *, slug_field="slug", scope=None): """Return a slug derived from ``value``, unique within ``scope``. From c0816a1addc0d66203951423d4904280e00cb951 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 14:42:06 +0200 Subject: [PATCH 028/137] feat(teams): management positions and cross-club validation Position gains management_position, distinguishing a coach/manager from other staff (physio, kit manager). A CheckConstraint enforces that a management position is always a staff position. TeamMembership and StaffAssignment validate that their season and position belong to the same club as the team. Co-Authored-By: Claude Opus 4.8 --- .../0002_position_management_position.py | 18 ++++++++ ...agement_position_implies_staff_position.py | 18 ++++++++ teams/models.py | 14 +++++- teams/tests.py | 43 +++++++++++++++++++ 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 teams/migrations/0002_position_management_position.py create mode 100644 teams/migrations/0003_position_management_position_implies_staff_position.py diff --git a/teams/migrations/0002_position_management_position.py b/teams/migrations/0002_position_management_position.py new file mode 100644 index 0000000..ebf1f0e --- /dev/null +++ b/teams/migrations/0002_position_management_position.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.6 on 2026-07-13 07:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('teams', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='position', + name='management_position', + field=models.BooleanField(default=False, verbose_name='management position'), + ), + ] diff --git a/teams/migrations/0003_position_management_position_implies_staff_position.py b/teams/migrations/0003_position_management_position_implies_staff_position.py new file mode 100644 index 0000000..617266e --- /dev/null +++ b/teams/migrations/0003_position_management_position_implies_staff_position.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.6 on 2026-07-13 07:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0009_clubrole'), + ('teams', '0002_position_management_position'), + ] + + operations = [ + migrations.AddConstraint( + model_name='position', + constraint=models.CheckConstraint(condition=models.Q(('management_position', False), ('staff_position', True), _connector='OR'), name='management_position_implies_staff_position'), + ), + ] diff --git a/teams/models.py b/teams/models.py index 2b41c16..ce43855 100644 --- a/teams/models.py +++ b/teams/models.py @@ -1,8 +1,9 @@ from django.db import models +from django.db.models import Q from django.utils.translation import gettext_lazy as _ from club.models import Season -from clubmanager.base import ClubScopedModel, UUIDModel +from clubmanager.base import ClubScopedModel, UUIDModel, validate_club_scope from members.models import Member @@ -27,12 +28,15 @@ class Position(ClubScopedModel): ordering = models.PositiveSmallIntegerField(_("ordering"), default=0) staff_position = models.BooleanField(_("staff position"), default=False) + management_position = models.BooleanField(_("management position"), default=False) class Meta: verbose_name = _("position") verbose_name_plural = _("positions") constraints = [ models.UniqueConstraint(fields=["club", "name"], name="unique_position_name_per_club"), + # A management position is always a staff position. + models.CheckConstraint(condition=Q(management_position=False) | Q(staff_position=True), name="management_position_implies_staff_position"), ] ordering = ["ordering", "name"] @@ -62,6 +66,10 @@ class TeamMembership(UUIDModel): def __str__(self): return f"{self.team} - {self.member}" + def clean(self): + club_id = self.team.club_id if self.team_id else None + validate_club_scope(self, club_id, same_club_fields=("season", "position")) + class StaffAssignment(UUIDModel): team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="staff_assignments", verbose_name=_("team")) @@ -79,3 +87,7 @@ class StaffAssignment(UUIDModel): def __str__(self): return f"{self.team} - {self.member}" + + def clean(self): + club_id = self.team.club_id if self.team_id else None + validate_club_scope(self, club_id, same_club_fields=("season", "position")) diff --git a/teams/tests.py b/teams/tests.py index 542163b..a16100f 100644 --- a/teams/tests.py +++ b/teams/tests.py @@ -1,5 +1,6 @@ import datetime +from django.core.exceptions import ValidationError from django.db import IntegrityError from django.db.models import ProtectedError from django.test import TestCase @@ -59,6 +60,15 @@ class PositionModelTests(TeamsTestCase): with self.assertRaises(IntegrityError): Position.objects.create(club=self.club, name="Forward", short_name="dup") + def test_management_position_must_also_be_a_staff_position(self): + with self.assertRaises(IntegrityError): + Position.objects.create(club=self.club, name="Bogus", short_name="BG", staff_position=False, management_position=True) + + def test_management_staff_position_is_allowed(self): + position = Position.objects.create(club=self.club, name="Manager", short_name="MG", staff_position=True, management_position=True) + + self.assertTrue(position.management_position) + class TeamMembershipModelTests(TeamsTestCase): def test_can_create_roster_entry(self): @@ -110,3 +120,36 @@ class StaffAssignmentModelTests(TeamsTestCase): self.assertEqual(self.forward.team_memberships.count(), 1) self.assertEqual(self.coach.staff_assignments.count(), 1) + + +class RosterCleanTests(TeamsTestCase): + def setUp(self): + super().setUp() + self.other = Club.objects.create(name="Rival FC", slug="rival-fc") + self.other_season = Season.objects.create(club=self.other, start_date=datetime.date(2026, 8, 1), end_date=datetime.date(2027, 5, 31)) + self.other_position = Position.objects.create(club=self.other, name="Forward", short_name="FW") + self.other_coach = Position.objects.create(club=self.other, name="Coach", short_name="C", staff_position=True) + + def test_teammembership_rejects_cross_club_season(self): + entry = TeamMembership(team=self.team, member=self.member, season=self.other_season, position=self.forward) + with self.assertRaises(ValidationError) as ctx: + entry.full_clean() + self.assertIn("season", ctx.exception.error_dict) + + def test_teammembership_rejects_cross_club_position(self): + entry = TeamMembership(team=self.team, member=self.member, season=self.season, position=self.other_position) + with self.assertRaises(ValidationError) as ctx: + entry.full_clean() + self.assertIn("position", ctx.exception.error_dict) + + def test_teammembership_accepts_same_club(self): + TeamMembership(team=self.team, member=self.member, season=self.season, position=self.forward).full_clean() + + def test_staffassignment_rejects_cross_club_season(self): + assignment = StaffAssignment(team=self.team, member=self.member, season=self.other_season, position=self.coach) + with self.assertRaises(ValidationError) as ctx: + assignment.full_clean() + self.assertIn("season", ctx.exception.error_dict) + + def test_staffassignment_accepts_same_club(self): + StaffAssignment(team=self.team, member=self.member, season=self.season, position=self.coach).full_clean() From 22d971d48c4edced8752f8158e375db54e78723a Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 14:42:17 +0200 Subject: [PATCH 029/137] feat(events): event owner and cross-club validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Event.created_by (the owner, used later by the access service to let an event's creator edit it). Validate that an event's season/location/opponent — and an EventSeries' location/opponent — belong to the event's club. The teams M2M cannot be checked in clean() (M2M rows are written after save), so an m2m_changed pre_add receiver rejects teams from another club. Co-Authored-By: Claude Opus 4.8 --- events/migrations/0008_event_created_by.py | 20 +++++++++ events/models.py | 9 +++- events/signals.py | 17 +++++++- events/tests.py | 48 ++++++++++++++++++++++ 4 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 events/migrations/0008_event_created_by.py diff --git a/events/migrations/0008_event_created_by.py b/events/migrations/0008_event_created_by.py new file mode 100644 index 0000000..a71a4d7 --- /dev/null +++ b/events/migrations/0008_event_created_by.py @@ -0,0 +1,20 @@ +# Generated by Django 6.0.6 on 2026-07-12 21:43 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0007_event_series_until'), + ('members', '0002_alter_familymembership_unique_together_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='created_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_events', to='members.member', verbose_name='created by'), + ), + ] diff --git a/events/models.py b/events/models.py index 69e0c68..c8579c4 100644 --- a/events/models.py +++ b/events/models.py @@ -2,7 +2,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from club.models import Season -from clubmanager.base import ClubScopedModel, UUIDModel +from clubmanager.base import ClubScopedModel, UUIDModel, validate_club_scope from members.models import Member from teams.models import Team @@ -64,6 +64,7 @@ class Event(ClubScopedModel): location = models.ForeignKey(Location, on_delete=models.SET_NULL, related_name="events", null=True, blank=True, verbose_name=_("location")) opponent = models.ForeignKey(Opponent, on_delete=models.SET_NULL, related_name="events", null=True, blank=True, verbose_name=_("opponent")) + created_by = models.ForeignKey(Member, on_delete=models.SET_NULL, related_name="created_events", null=True, blank=True, verbose_name=_("created by")) class Meta: verbose_name = _("event") @@ -73,6 +74,9 @@ class Event(ClubScopedModel): def __str__(self): return self.title + def clean(self): + validate_club_scope(self, self.club_id, same_club_fields=("season", "location", "opponent")) + class EventSeries(ClubScopedModel): """A recurring event definition that materialises concrete Event rows.""" @@ -103,6 +107,9 @@ class EventSeries(ClubScopedModel): def __str__(self): return self.title + def clean(self): + validate_club_scope(self, self.club_id, same_club_fields=("location", "opponent")) + class Attendance(UUIDModel): class AttendanceStatus(models.TextChoices): diff --git a/events/signals.py b/events/signals.py index 86a3293..ab3b015 100644 --- a/events/signals.py +++ b/events/signals.py @@ -8,17 +8,30 @@ Registered from ``EventsConfig.ready``. Two triggers: events. """ +from django.core.exceptions import ValidationError from django.db.models.signals import m2m_changed, post_delete, post_save from django.dispatch import receiver from django.utils import timezone +from django.utils.translation import gettext_lazy as _ -from events.models import Event +from events.models import Event, EventSeries from events.services import sync_event_attendances -from teams.models import TeamMembership +from teams.models import Team, TeamMembership M2M_SYNC_ACTIONS = {"post_add", "post_remove", "post_clear"} +@receiver(m2m_changed, sender=Event.teams.through) +@receiver(m2m_changed, sender=EventSeries.teams.through) +def validate_teams_same_club(sender, instance, action, reverse, pk_set, **kwargs): + # Reject teams from another club before they're attached (forward adds only; + # the reverse direction — team.scheduled_events.add(...) — is not a used path). + if action != "pre_add" or reverse: + return + if Team.objects.filter(pk__in=pk_set).exclude(club_id=instance.club_id).exists(): + raise ValidationError(_("Teams must belong to the same club as the event.")) + + @receiver(post_save, sender=Event) def sync_on_event_save(sender, instance, **kwargs): sync_event_attendances(instance) diff --git a/events/tests.py b/events/tests.py index 8af833a..fcd4c4e 100644 --- a/events/tests.py +++ b/events/tests.py @@ -1,6 +1,7 @@ from datetime import timedelta from io import StringIO +from django.core.exceptions import ValidationError from django.core.management import call_command from django.db import IntegrityError from django.test import TestCase @@ -353,3 +354,50 @@ class ExtendSeriesCommandTests(RecurrenceTestBase): self.assertEqual(series.occurrences.count(), 4) self.assertIn("Done.", out.getvalue()) + + +class EventClubScopeTests(EventsTestBase): + def setUp(self): + super().setUp() + self.other = Club.objects.create(name="Rival FC", slug="rival-fc") + today = timezone.localdate() + self.other_season = Season.objects.create(club=self.other, start_date=today - timedelta(days=30), end_date=today + timedelta(days=300)) + self.other_location = Location.objects.create(club=self.other, name="Arena", address="1 St", city="Town", zip_code="1000", country="BE") + self.other_opponent = Opponent.objects.create(club=self.other, name="Rivals") + self.other_team = Team.objects.create(club=self.other, name="First", short_name="1") + + def test_event_rejects_cross_club_season(self): + event = Event(club=self.club, title="Match", start=self.future, season=self.other_season) + with self.assertRaises(ValidationError) as ctx: + event.full_clean() + self.assertIn("season", ctx.exception.error_dict) + + def test_event_rejects_cross_club_location(self): + event = Event(club=self.club, title="Match", start=self.future, location=self.other_location) + with self.assertRaises(ValidationError) as ctx: + event.full_clean() + self.assertIn("location", ctx.exception.error_dict) + + def test_event_accepts_same_club_fields(self): + Event(club=self.club, title="Match", start=self.future, season=self.season).full_clean() + + def test_event_rejects_cross_club_team(self): + event = Event.objects.create(club=self.club, title="Match", start=self.future, season=self.season) + with self.assertRaises(ValidationError): + event.teams.add(self.other_team) + + def test_event_accepts_same_club_team(self): + event = Event.objects.create(club=self.club, title="Match", start=self.future, season=self.season) + event.teams.add(self.team) + self.assertIn(self.team, event.teams.all()) + + def test_series_rejects_cross_club_opponent(self): + series = EventSeries(club=self.club, title="Weekly", rrule="FREQ=WEEKLY", dtstart=self.future, opponent=self.other_opponent) + with self.assertRaises(ValidationError) as ctx: + series.full_clean() + self.assertIn("opponent", ctx.exception.error_dict) + + def test_series_rejects_cross_club_team(self): + series = EventSeries.objects.create(club=self.club, title="Weekly", rrule="FREQ=WEEKLY", dtstart=self.future) + with self.assertRaises(ValidationError): + series.teams.add(self.other_team) From d43ca0cfa822c71155143ad1c5217f08b2da059f Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 14:42:17 +0200 Subject: [PATCH 030/137] feat(formbuilder): answers must belong to the submission's form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An Answer's field could point at a field of a *different* form than its submission — and since forms are club-scoped, across clubs too. Validate field.form == submission.form in clean(). Co-Authored-By: Claude Opus 4.8 --- formbuilder/models.py | 5 +++++ formbuilder/tests.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/formbuilder/models.py b/formbuilder/models.py index 9f8237e..37601bc 100644 --- a/formbuilder/models.py +++ b/formbuilder/models.py @@ -1,3 +1,4 @@ +from django.core.exceptions import ValidationError from django.db import models from django.db.models import UniqueConstraint from django.utils.translation import gettext_lazy as _ @@ -100,3 +101,7 @@ class Answer(UUIDModel): def __str__(self): return f"{self.submission} - {self.field}" + + def clean(self): + if self.field_id and self.submission_id and self.field.form_id != self.submission.form_id: + raise ValidationError({"field": _("Must belong to the same form as the submission.")}) diff --git a/formbuilder/tests.py b/formbuilder/tests.py index d0610ca..cfa884a 100644 --- a/formbuilder/tests.py +++ b/formbuilder/tests.py @@ -1,6 +1,7 @@ from datetime import timedelta from django import forms +from django.core.exceptions import ValidationError from django.db import IntegrityError from django.db.models import ProtectedError from django.test import TestCase @@ -329,3 +330,20 @@ class FormReportTests(FormbuilderTestBase): self.assertEqual(report.count, 0) self.assertEqual(report.rows, []) + + +class AnswerCleanTests(FormbuilderTestBase): + def test_rejects_field_from_another_form(self): + other_form = Form.objects.create(club=self.club, title="Other", slug="other") + other_field = Field.objects.create(form=other_form, key="x", label="X", order=1) + submission = Submission.objects.create(form=self.form, member=self.member) + answer = Answer(submission=submission, field=other_field, value="v") + + with self.assertRaises(ValidationError) as ctx: + answer.full_clean() + self.assertIn("field", ctx.exception.error_dict) + + def test_accepts_field_from_the_submissions_form(self): + submission = Submission.objects.create(form=self.form, member=self.member) + + Answer(submission=submission, field=self.name, value="v").full_clean() From 78fdcdf1384ce47171c60e83b753c16f8e58b015 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 14:42:29 +0200 Subject: [PATCH 031/137] feat(shop): cart, order, invoice and discounts Build out the shop domain: Cart/CartItem (one open cart per user per club), Order/OrderLine, Discount/AppliedDiscount, Payment and Invoice. Order and Invoice allocate a per-club, per-year sequential number (ORD-- / INV--) via shared helpers, retrying on collision with the (club, number) unique constraint as the source of truth. Fixes found while testing: - Invoice had no number generator, so a second invoice in a club collided on the empty string and could never be created. - AppliedDiscount printed a "%" suffix even for fixed-amount discounts, and had no (order, discount) uniqueness, so a discount could be applied twice. Every model validates its club-scoped FKs (product/team/discount/order/season/ staff_role) against the owning club, and Member FKs against club membership. Register all models in the admin, with FK dropdowns scoped to the owning club. Co-Authored-By: Claude Opus 4.8 --- shop/admin.py | 141 +++++- .../0003_alter_product_options_and_more.py | 59 +++ shop/migrations/0004_cart.py | 32 ++ .../0005_product_staff_role_cartitem_order.py | 59 +++ shop/migrations/0006_alter_order_number.py | 18 + ...ount_invoice_orderline_payment_and_more.py | 113 +++++ .../0008_alter_applieddiscount_applied_by.py | 20 + ...plieddiscount_unique_discount_per_order.py | 18 + shop/models.py | 296 +++++++++++- shop/tests.py | 427 +++++++++++++++++- 10 files changed, 1176 insertions(+), 7 deletions(-) create mode 100644 shop/migrations/0003_alter_product_options_and_more.py create mode 100644 shop/migrations/0004_cart.py create mode 100644 shop/migrations/0005_product_staff_role_cartitem_order.py create mode 100644 shop/migrations/0006_alter_order_number.py create mode 100644 shop/migrations/0007_discount_applieddiscount_invoice_orderline_payment_and_more.py create mode 100644 shop/migrations/0008_alter_applieddiscount_applied_by.py create mode 100644 shop/migrations/0009_applieddiscount_unique_discount_per_order.py diff --git a/shop/admin.py b/shop/admin.py index 846f6b4..2518db7 100644 --- a/shop/admin.py +++ b/shop/admin.py @@ -1 +1,140 @@ -# Register your models here. +from django.contrib import admin + +from .models import ( + AppliedDiscount, + Cart, + CartItem, + Discount, + Invoice, + Order, + OrderLine, + Payment, + Product, +) + + +class ClubScopedFKMixin: + """Restrict club-scoped FK dropdowns to the edited object's (or parent's) club. + + ``scoped_fk_fields`` names FKs whose target is a club-scoped model. The club + is taken from the object being edited (or, for inlines, the parent object). + Enforcement still lives in each model's ``clean()``; this just keeps invalid + options out of the dropdowns. + """ + + scoped_fk_fields = () + + def get_form(self, request, obj=None, **kwargs): + request._club_obj = obj + return super().get_form(request, obj, **kwargs) + + def get_formset(self, request, obj=None, **kwargs): + request._club_obj = obj + return super().get_formset(request, obj, **kwargs) + + def formfield_for_foreignkey(self, db_field, request, **kwargs): + if db_field.name in self.scoped_fk_fields: + club_id = getattr(getattr(request, "_club_obj", None), "club_id", None) + if club_id is not None: + kwargs["queryset"] = db_field.related_model._default_manager.filter(club_id=club_id) + return super().formfield_for_foreignkey(db_field, request, **kwargs) + + +@admin.register(Product) +class ProductAdmin(ClubScopedFKMixin, admin.ModelAdmin): + scoped_fk_fields = ("season", "staff_role") + list_display = ["name", "product_type", "club", "is_active", "is_public"] + list_filter = ["club", "product_type", "is_active", "is_public"] + search_fields = ["name", "slug"] + prepopulated_fields = {"slug": ["name"]} + + +class CartItemInline(ClubScopedFKMixin, admin.TabularInline): + model = CartItem + scoped_fk_fields = ("product", "team") + extra = 0 + raw_id_fields = ["beneficiary"] + + +@admin.register(Cart) +class CartAdmin(admin.ModelAdmin): + list_display = ["user", "status", "club"] + list_filter = ["club", "status"] + search_fields = ["user__email"] + raw_id_fields = ["user"] + inlines = [CartItemInline] + + +@admin.register(CartItem) +class CartItemAdmin(admin.ModelAdmin): + list_display = ["cart", "product", "quantity", "beneficiary"] + search_fields = ["product__name"] + raw_id_fields = ["cart", "product", "beneficiary", "team"] + + +class OrderLineInline(ClubScopedFKMixin, admin.TabularInline): + model = OrderLine + scoped_fk_fields = ("product", "team") + extra = 0 + raw_id_fields = ["beneficiary"] + + +class AppliedDiscountInline(ClubScopedFKMixin, admin.TabularInline): + model = AppliedDiscount + scoped_fk_fields = ("discount",) + extra = 0 + raw_id_fields = ["applied_by"] + + +class PaymentInline(admin.TabularInline): + model = Payment + extra = 0 + + +@admin.register(Order) +class OrderAdmin(admin.ModelAdmin): + list_display = ["number", "purchaser", "status", "total", "club", "created"] + list_filter = ["club", "status"] + search_fields = ["number", "purchaser__first_name", "purchaser__last_name"] + raw_id_fields = ["purchaser"] + readonly_fields = ["number", "created", "modified"] + inlines = [OrderLineInline, AppliedDiscountInline, PaymentInline] + + +@admin.register(OrderLine) +class OrderLineAdmin(admin.ModelAdmin): + list_display = ["order", "product", "quantity", "line_total"] + search_fields = ["product__name", "order__number"] + raw_id_fields = ["order", "product", "beneficiary", "team"] + + +@admin.register(Discount) +class DiscountAdmin(admin.ModelAdmin): + list_display = ["name", "discount_type", "discount_amount", "club", "is_active"] + list_filter = ["club", "discount_type", "is_active"] + search_fields = ["name", "slug"] + prepopulated_fields = {"slug": ["name"]} + + +@admin.register(AppliedDiscount) +class AppliedDiscountAdmin(admin.ModelAdmin): + list_display = ["order", "discount", "discount_type", "discount_amount"] + search_fields = ["order__number", "discount__name"] + raw_id_fields = ["order", "discount", "applied_by"] + + +@admin.register(Payment) +class PaymentAdmin(admin.ModelAdmin): + list_display = ["order", "amount", "method", "status", "paid_at"] + list_filter = ["method", "status"] + search_fields = ["order__number", "reference"] + raw_id_fields = ["order"] + + +@admin.register(Invoice) +class InvoiceAdmin(ClubScopedFKMixin, admin.ModelAdmin): + scoped_fk_fields = ("order",) + list_display = ["number", "order", "club", "issued_at", "due_date"] + list_filter = ["club"] + search_fields = ["number", "order__number"] + readonly_fields = ["number", "issued_at"] diff --git a/shop/migrations/0003_alter_product_options_and_more.py b/shop/migrations/0003_alter_product_options_and_more.py new file mode 100644 index 0000000..524c023 --- /dev/null +++ b/shop/migrations/0003_alter_product_options_and_more.py @@ -0,0 +1,59 @@ +# Generated by Django 6.0.6 on 2026-07-12 19:59 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ('shop', '0002_alter_product_slug_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='product', + options={'ordering': ['name'], 'verbose_name': 'product', 'verbose_name_plural': 'products'}, + ), + migrations.AddField( + model_name='product', + name='early_bird_discount_amount', + field=models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='early bird discount amount'), + ), + migrations.AddField( + model_name='product', + name='early_bird_discount_deadline', + field=models.DateField(blank=True, null=True, verbose_name='early bird discount deadline'), + ), + migrations.AddField( + model_name='product', + name='early_bird_discount_enabled', + field=models.BooleanField(default=False, verbose_name='early bird discount enabled?'), + ), + migrations.AddField( + model_name='product', + name='early_bird_discount_type', + field=models.CharField(choices=[('percentage', 'Percentage'), ('fixed_amount', 'Fixed amount')], default='percentage', max_length=255, verbose_name='early bird discount type'), + ), + migrations.AddField( + model_name='product', + name='is_active', + field=models.BooleanField(default=True, verbose_name='is active?'), + ), + migrations.AddField( + model_name='product', + name='is_public', + field=models.BooleanField(default=True, help_text='Non-public products are only visible to staff members for adding on to an order later on.', verbose_name='is public?'), + ), + migrations.AddField( + model_name='product', + name='product_type', + field=models.CharField(choices=[('membership', 'Membership'), ('event_fee', 'Event fee'), ('merchandise', 'Merchandise'), ('donation', 'Donation')], default='membership', max_length=255, verbose_name='product type'), + ), + migrations.AddField( + model_name='product', + name='season', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='products', to='club.season', verbose_name='season'), + ), + ] diff --git a/shop/migrations/0004_cart.py b/shop/migrations/0004_cart.py new file mode 100644 index 0000000..26305f9 --- /dev/null +++ b/shop/migrations/0004_cart.py @@ -0,0 +1,32 @@ +# Generated by Django 6.0.6 on 2026-07-12 20:02 + +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ('shop', '0003_alter_product_options_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Cart', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('status', models.CharField(choices=[('open', 'Open'), ('checked_out', 'Checked out'), ('abandoned', 'Abandoned')], default='open', max_length=255, verbose_name='status')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='carts', to=settings.AUTH_USER_MODEL, verbose_name='user')), + ], + options={ + 'verbose_name': 'cart', + 'verbose_name_plural': 'carts', + 'constraints': [models.UniqueConstraint(condition=models.Q(('status', 'open')), fields=('club', 'user'), name='unique_open_cart_per_user_per_club')], + }, + ), + ] diff --git a/shop/migrations/0005_product_staff_role_cartitem_order.py b/shop/migrations/0005_product_staff_role_cartitem_order.py new file mode 100644 index 0000000..6e067ed --- /dev/null +++ b/shop/migrations/0005_product_staff_role_cartitem_order.py @@ -0,0 +1,59 @@ +# Generated by Django 6.0.6 on 2026-07-12 20:12 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ('members', '0002_alter_familymembership_unique_together_and_more'), + ('shop', '0004_cart'), + ('teams', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='product', + name='staff_role', + field=models.ForeignKey(blank=True, limit_choices_to={'staff_position': True}, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='staff_products', to='teams.position', verbose_name='staff role'), + ), + migrations.CreateModel( + name='CartItem', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('quantity', models.PositiveSmallIntegerField(default=1, verbose_name='quantity')), + ('unit_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='unit price')), + ('beneficiary', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='cart_items', to='members.member', verbose_name='beneficiary')), + ('cart', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='shop.cart', verbose_name='cart')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='cart_items', to='shop.product', verbose_name='product')), + ('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='cart_items', to='teams.team', verbose_name='team')), + ], + options={ + 'verbose_name': 'cart item', + 'verbose_name_plural': 'cart items', + 'constraints': [models.UniqueConstraint(fields=('cart', 'product', 'beneficiary'), name='unique_product_per_cart_per_beneficiary')], + }, + ), + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('number', models.CharField(max_length=255, verbose_name='number')), + ('status', models.CharField(choices=[('pending', 'Pending'), ('paid', 'Paid'), ('partially_paid', 'Partially paid'), ('cancelled', 'Cancelled'), ('refunded', 'Refunded'), ('delivered', 'Delivered')], default='pending', max_length=255, verbose_name='status')), + ('total', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='total')), + ('created', models.DateTimeField(auto_now_add=True, verbose_name='created at')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='modified')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ('purchaser', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='orders', to='members.member', verbose_name='purchaser')), + ], + options={ + 'verbose_name': 'order', + 'verbose_name_plural': 'orders', + 'ordering': ['-created'], + 'constraints': [models.UniqueConstraint(fields=('club', 'number'), name='unique_order_number_per_club')], + }, + ), + ] diff --git a/shop/migrations/0006_alter_order_number.py b/shop/migrations/0006_alter_order_number.py new file mode 100644 index 0000000..5b80f06 --- /dev/null +++ b/shop/migrations/0006_alter_order_number.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.6 on 2026-07-12 20:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0005_product_staff_role_cartitem_order'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='number', + field=models.CharField(blank=True, max_length=255, verbose_name='number'), + ), + ] diff --git a/shop/migrations/0007_discount_applieddiscount_invoice_orderline_payment_and_more.py b/shop/migrations/0007_discount_applieddiscount_invoice_orderline_payment_and_more.py new file mode 100644 index 0000000..cbffa6a --- /dev/null +++ b/shop/migrations/0007_discount_applieddiscount_invoice_orderline_payment_and_more.py @@ -0,0 +1,113 @@ +# Generated by Django 6.0.6 on 2026-07-12 21:25 + +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ('members', '0002_alter_familymembership_unique_together_and_more'), + ('shop', '0006_alter_order_number'), + ('teams', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Discount', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('slug', models.SlugField(blank=True, max_length=255, verbose_name='slug')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('discount_type', models.CharField(choices=[('percentage', 'Percentage'), ('fixed_amount', 'Fixed amount')], default='percentage', max_length=255, verbose_name='discount type')), + ('discount_amount', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='discount amount')), + ('is_active', models.BooleanField(default=True, verbose_name='is active?')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ], + options={ + 'verbose_name': 'order discount type', + 'verbose_name_plural': 'order discount types', + 'ordering': ['name'], + }, + ), + migrations.CreateModel( + name='AppliedDiscount', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('discount_type', models.CharField(choices=[('percentage', 'Percentage'), ('fixed_amount', 'Fixed amount')], default='percentage', max_length=255, verbose_name='discount type')), + ('discount_amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='discount amount')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('applied_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='applied_discounts', to=settings.AUTH_USER_MODEL, verbose_name='applied by')), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='applied_discounts', to='shop.order', verbose_name='order')), + ('discount', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='applied_discounts', to='shop.discount', verbose_name='discount')), + ], + options={ + 'verbose_name': 'applied discount', + 'verbose_name_plural': 'applied discounts', + }, + ), + migrations.CreateModel( + name='Invoice', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('number', models.CharField(blank=True, max_length=255, verbose_name='number')), + ('issued_at', models.DateTimeField(auto_now_add=True, verbose_name='issued at')), + ('due_date', models.DateField(blank=True, null=True, verbose_name='due date')), + ('billing_snapshot', models.JSONField(blank=True, null=True, verbose_name='billing snapshot')), + ('pdf', models.FileField(blank=True, null=True, upload_to='invoices', verbose_name='PDF')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ('order', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='invoice', to='shop.order', verbose_name='order')), + ], + options={ + 'verbose_name': 'invoice', + 'verbose_name_plural': 'invoices', + 'ordering': ['-issued_at'], + }, + ), + migrations.CreateModel( + name='OrderLine', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('quantity', models.PositiveSmallIntegerField(default=1, verbose_name='quantity')), + ('unit_price', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='unit price')), + ('line_total', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='line total')), + ('beneficiary', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='order_items', to='members.member', verbose_name='beneficiary')), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='order_items', to='shop.order', verbose_name='order')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='order_items', to='shop.product', verbose_name='product')), + ('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='order_items', to='teams.team', verbose_name='team')), + ], + options={ + 'verbose_name': 'order line', + 'verbose_name_plural': 'order lines', + }, + ), + migrations.CreateModel( + name='Payment', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount')), + ('method', models.CharField(choices=[('credit_card', 'Credit card'), ('bank_transfer', 'Bank transfer'), ('cash', 'Cash')], default='credit_card', max_length=255, verbose_name='method')), + ('status', models.CharField(choices=[('pending', 'Pending'), ('confirmed', 'Confirmed'), ('failed', 'Failed'), ('refunded', 'Refunded')], default='pending', max_length=255, verbose_name='status')), + ('reference', models.CharField(blank=True, max_length=255, verbose_name='reference')), + ('paid_at', models.DateTimeField(blank=True, null=True, verbose_name='paid at')), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='shop.order', verbose_name='order')), + ], + options={ + 'verbose_name': 'payment', + 'verbose_name_plural': 'payments', + }, + ), + migrations.AddConstraint( + model_name='discount', + constraint=models.UniqueConstraint(fields=('club', 'slug'), name='unique_order_discount_type_slug_per_club'), + ), + migrations.AddConstraint( + model_name='invoice', + constraint=models.UniqueConstraint(fields=('club', 'number'), name='unique_invoice_number_per_club'), + ), + ] diff --git a/shop/migrations/0008_alter_applieddiscount_applied_by.py b/shop/migrations/0008_alter_applieddiscount_applied_by.py new file mode 100644 index 0000000..60c9ce9 --- /dev/null +++ b/shop/migrations/0008_alter_applieddiscount_applied_by.py @@ -0,0 +1,20 @@ +# Generated by Django 6.0.6 on 2026-07-12 21:31 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0002_alter_familymembership_unique_together_and_more'), + ('shop', '0007_discount_applieddiscount_invoice_orderline_payment_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='applieddiscount', + name='applied_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='applied_discounts', to='members.member', verbose_name='applied by'), + ), + ] diff --git a/shop/migrations/0009_applieddiscount_unique_discount_per_order.py b/shop/migrations/0009_applieddiscount_unique_discount_per_order.py new file mode 100644 index 0000000..5f6fab6 --- /dev/null +++ b/shop/migrations/0009_applieddiscount_unique_discount_per_order.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.6 on 2026-07-12 21:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0002_alter_familymembership_unique_together_and_more'), + ('shop', '0008_alter_applieddiscount_applied_by'), + ] + + operations = [ + migrations.AddConstraint( + model_name='applieddiscount', + constraint=models.UniqueConstraint(fields=('order', 'discount'), name='unique_discount_per_order'), + ), + ] diff --git a/shop/models.py b/shop/models.py index ac6877f..7a7a3f2 100644 --- a/shop/models.py +++ b/shop/models.py @@ -1,17 +1,307 @@ -from django.db import models -from django.db.models import UniqueConstraint +from django.db import IntegrityError, models, transaction +from django.db.models import Q, UniqueConstraint +from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from clubmanager.base import ClubScopedModel +from authentication.models import User +from club.models import Season +from club.tenancy import require_current_club +from clubmanager.base import ClubScopedModel, UUIDModel, validate_club_scope +from members.models import Member +from teams.models import Position, Team + + +def next_scoped_number(instance, code): + """Next per-club sequential number for the current year: ``--``.""" + prefix = f"{code}-{timezone.now().year}-" + model = type(instance) + sequences = [int(suffix) for number in model.objects.filter(club=instance.club, number__startswith=prefix).values_list("number", flat=True) if (suffix := number.removeprefix(prefix)).isdigit()] + return f"{prefix}{max(sequences, default=0) + 1:05d}" + + +def save_with_number(instance, save): + """Assign a unique per-club number (retrying on collision) then save. + + Relies on the model's ``generate_number()`` and its ``(club, number)`` + unique constraint as the source of truth. + """ + if instance.club_id is None: + instance.club = require_current_club() + if instance.number: + return save() + for attempt in range(5): + instance.number = instance.generate_number() + try: + with transaction.atomic(): + return save() + except IntegrityError: + if attempt == 4: + raise class Product(ClubScopedModel): + class ProductType(models.TextChoices): + MEMBERSHIP = "membership", _("Membership") + EVENT_FEE = "event_fee", _("Event fee") + MERCHANDISE = "merchandise", _("Merchandise") + DONATION = "donation", _("Donation") + + class DiscountType(models.TextChoices): + PERCENTAGE = "percentage", _("Percentage") + FIXED_AMOUNT = "fixed_amount", _("Fixed amount") + name = models.CharField(_("name"), max_length=255) slug = models.SlugField(_("slug"), max_length=255, blank=True) + product_type = models.CharField(_("product type"), max_length=255, choices=ProductType.choices, default=ProductType.MEMBERSHIP) + season = models.ForeignKey(Season, on_delete=models.PROTECT, related_name="products", verbose_name=_("season"), blank=True, null=True) + + is_active = models.BooleanField(_("is active?"), default=True) + is_public = models.BooleanField(_("is public?"), default=True, help_text="Non-public products are only visible to staff members for adding on to an order later on.") + staff_role = models.ForeignKey(Position, on_delete=models.PROTECT, related_name="staff_products", verbose_name=_("staff role"), blank=True, null=True, limit_choices_to={"staff_position": True}) + + early_bird_discount_enabled = models.BooleanField(_("early bird discount enabled?"), default=False) + early_bird_discount_deadline = models.DateField(_("early bird discount deadline"), blank=True, null=True) + early_bird_discount_type = models.CharField(_("early bird discount type"), max_length=255, choices=DiscountType.choices, default=DiscountType.PERCENTAGE) + early_bird_discount_amount = models.DecimalField(_("early bird discount amount"), max_digits=10, decimal_places=2, default=0) + slug_source = "name" class Meta: + verbose_name = _("product") + verbose_name_plural = _("products") + ordering = ["name"] constraints = [ UniqueConstraint(fields=["club", "slug"], name="unique_product_slug_per_club"), ] + + def __str__(self): + return self.name + + def clean(self): + validate_club_scope(self, self.club_id, same_club_fields=("season", "staff_role")) + + +class Cart(ClubScopedModel): + class CartStatus(models.TextChoices): + OPEN = "open", _("Open") + CHECKED_OUT = "checked_out", _("Checked out") + ABANDONED = "abandoned", _("Abandoned") + + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="carts", verbose_name=_("user")) + status = models.CharField(_("status"), max_length=255, choices=CartStatus.choices, default=CartStatus.OPEN) + + class Meta: + verbose_name = _("cart") + verbose_name_plural = _("carts") + constraints = [ + # At most one open cart per user per club (CartStatus.OPEN == "open"). + UniqueConstraint(fields=["club", "user"], condition=Q(status="open"), name="unique_open_cart_per_user_per_club"), + ] + + def __str__(self): + return f"{self.user} - {self.status}" + + +class CartItem(UUIDModel): + cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name="items", verbose_name=_("cart")) + product = models.ForeignKey(Product, on_delete=models.PROTECT, related_name="cart_items", verbose_name=_("product")) + + quantity = models.PositiveSmallIntegerField(_("quantity"), default=1) + unit_price = models.DecimalField(_("unit price"), max_digits=10, decimal_places=2) + + beneficiary = models.ForeignKey(Member, on_delete=models.PROTECT, related_name="cart_items", verbose_name=_("beneficiary"), blank=True, null=True) + team = models.ForeignKey(Team, on_delete=models.PROTECT, related_name="cart_items", verbose_name=_("team"), blank=True, null=True) + + class Meta: + verbose_name = _("cart item") + verbose_name_plural = _("cart items") + constraints = [ + UniqueConstraint(fields=["cart", "product", "beneficiary"], name="unique_product_per_cart_per_beneficiary"), + ] + + def __str__(self): + return f"{self.product} - {self.quantity}x" + + def clean(self): + club_id = self.cart.club_id if self.cart_id else None + validate_club_scope(self, club_id, same_club_fields=("product", "team"), member_fields=("beneficiary",)) + + +class Order(ClubScopedModel): + class OrderStatus(models.TextChoices): + PENDING = "pending", _("Pending") + PAID = "paid", _("Paid") + PARTIALLY_PAID = "partially_paid", _("Partially paid") + CANCELLED = "cancelled", _("Cancelled") + REFUNDED = "refunded", _("Refunded") + DELIVERED = "delivered", _("Delivered") + + number = models.CharField(_("number"), max_length=255, blank=True) + purchaser = models.ForeignKey(Member, on_delete=models.PROTECT, related_name="orders", verbose_name=_("purchaser")) + status = models.CharField(_("status"), max_length=255, choices=OrderStatus.choices, default=OrderStatus.PENDING) + total = models.DecimalField(_("total"), max_digits=10, decimal_places=2) + + created = models.DateTimeField(_("created at"), auto_now_add=True) + modified = models.DateTimeField(_("modified"), auto_now=True) + + class Meta: + verbose_name = _("order") + verbose_name_plural = _("orders") + ordering = ["-created"] + constraints = [ + UniqueConstraint(fields=["club", "number"], name="unique_order_number_per_club"), + ] + + def __str__(self): + return self.number + + def clean(self): + validate_club_scope(self, self.club_id, member_fields=("purchaser",)) + + def generate_number(self): + """Next per-club order number for the current year: ``ORD--``.""" + return next_scoped_number(self, "ORD") + + def save(self, *args, **kwargs): + return save_with_number(self, lambda: super(Order, self).save(*args, **kwargs)) + + +class OrderLine(UUIDModel): + order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="order_items", verbose_name=_("order")) + product = models.ForeignKey(Product, on_delete=models.PROTECT, related_name="order_items", verbose_name=_("product")) + + quantity = models.PositiveSmallIntegerField(_("quantity"), default=1) + unit_price = models.DecimalField(_("unit price"), max_digits=10, decimal_places=2) + + beneficiary = models.ForeignKey(Member, on_delete=models.PROTECT, related_name="order_items", verbose_name=_("beneficiary"), blank=True, null=True) + team = models.ForeignKey(Team, on_delete=models.PROTECT, related_name="order_items", verbose_name=_("team"), blank=True, null=True) + line_total = models.DecimalField(_("line total"), max_digits=10, decimal_places=2) + + class Meta: + verbose_name = _("order line") + verbose_name_plural = _("order lines") + + def __str__(self): + return f"{self.product} - {self.quantity}x" + + def clean(self): + club_id = self.order.club_id if self.order_id else None + validate_club_scope(self, club_id, same_club_fields=("product", "team"), member_fields=("beneficiary",)) + + +class Discount(ClubScopedModel): + class DiscountType(models.TextChoices): + PERCENTAGE = "percentage", _("Percentage") + FIXED_AMOUNT = "fixed_amount", _("Fixed amount") + + name = models.CharField(_("name"), max_length=255) + slug = models.SlugField(_("slug"), max_length=255, blank=True) + description = models.TextField(_("description"), blank=True) + + discount_type = models.CharField(_("discount type"), max_length=255, choices=DiscountType.choices, default=DiscountType.PERCENTAGE) + discount_amount = models.DecimalField(_("discount amount"), max_digits=10, decimal_places=2, default=0) + + is_active = models.BooleanField(_("is active?"), default=True) + + slug_source = "name" + + class Meta: + verbose_name = _("order discount type") + verbose_name_plural = _("order discount types") + ordering = ["name"] + constraints = [ + UniqueConstraint(fields=["club", "slug"], name="unique_order_discount_type_slug_per_club"), + ] + + def __str__(self): + return self.name + + +class AppliedDiscount(UUIDModel): + class DiscountType(models.TextChoices): + PERCENTAGE = "percentage", _("Percentage") + FIXED_AMOUNT = "fixed_amount", _("Fixed amount") + + order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="applied_discounts", verbose_name=_("order")) + discount = models.ForeignKey(Discount, on_delete=models.PROTECT, related_name="applied_discounts", verbose_name=_("discount")) + + discount_type = models.CharField(_("discount type"), max_length=255, choices=DiscountType.choices, default=DiscountType.PERCENTAGE) + discount_amount = models.DecimalField(_("discount amount"), max_digits=10, decimal_places=2) + description = models.TextField(_("description"), blank=True) + + applied_by = models.ForeignKey(Member, on_delete=models.PROTECT, related_name="applied_discounts", verbose_name=_("applied by"), blank=True, null=True) + + class Meta: + verbose_name = _("applied discount") + verbose_name_plural = _("applied discounts") + constraints = [ + UniqueConstraint(fields=["order", "discount"], name="unique_discount_per_order"), + ] + + def __str__(self): + suffix = "%" if self.discount_type == self.DiscountType.PERCENTAGE else "" + return f"{self.discount} - {self.discount_amount}{suffix}" + + def clean(self): + club_id = self.order.club_id if self.order_id else None + validate_club_scope(self, club_id, same_club_fields=("discount",), member_fields=("applied_by",)) + + +class Payment(UUIDModel): + class PaymentMethod(models.TextChoices): + CREDIT_CARD = "credit_card", _("Credit card") + BANK_TRANSFER = "bank_transfer", _("Bank transfer") + CASH = "cash", _("Cash") + + class PaymentStatus(models.TextChoices): + PENDING = "pending", _("Pending") + CONFIRMED = "confirmed", _("Confirmed") + FAILED = "failed", _("Failed") + REFUNDED = "refunded", _("Refunded") + + order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="payments", verbose_name=_("order")) + amount = models.DecimalField(_("amount"), max_digits=10, decimal_places=2) + method = models.CharField(_("method"), max_length=255, choices=PaymentMethod.choices, default=PaymentMethod.CREDIT_CARD) + status = models.CharField(_("status"), max_length=255, choices=PaymentStatus.choices, default=PaymentStatus.PENDING) + reference = models.CharField(_("reference"), max_length=255, blank=True) + paid_at = models.DateTimeField(_("paid at"), blank=True, null=True) + + class Meta: + verbose_name = _("payment") + verbose_name_plural = _("payments") + + def __str__(self): + return f"{self.order} - {self.status}" + + +class Invoice(ClubScopedModel): + number = models.CharField(_("number"), max_length=255, blank=True) + order = models.OneToOneField(Order, on_delete=models.CASCADE, related_name="invoice", verbose_name=_("order")) + + issued_at = models.DateTimeField(_("issued at"), auto_now_add=True) + due_date = models.DateField(_("due date"), blank=True, null=True) + + billing_snapshot = models.JSONField(_("billing snapshot"), blank=True, null=True) # Name/address information + pdf = models.FileField(_("PDF"), upload_to="invoices", blank=True, null=True) + + class Meta: + verbose_name = _("invoice") + verbose_name_plural = _("invoices") + ordering = ["-issued_at"] + constraints = [ + UniqueConstraint(fields=["club", "number"], name="unique_invoice_number_per_club"), + ] + + def __str__(self): + return self.number + + def clean(self): + validate_club_scope(self, self.club_id, same_club_fields=("order",)) + + def generate_number(self): + """Next per-club invoice number for the current year: ``INV--``.""" + return next_scoped_number(self, "INV") + + def save(self, *args, **kwargs): + return save_with_number(self, lambda: super(Invoice, self).save(*args, **kwargs)) diff --git a/shop/tests.py b/shop/tests.py index bd1b8ec..f0de9f5 100644 --- a/shop/tests.py +++ b/shop/tests.py @@ -1,8 +1,32 @@ -from django.test import TestCase +from datetime import timedelta +from decimal import Decimal +from unittest.mock import patch -from club.models import Club +from django.contrib.admin.sites import AdminSite +from django.core.exceptions import ValidationError +from django.db import IntegrityError +from django.db.models import ProtectedError +from django.test import RequestFactory, TestCase +from django.utils import timezone -from .models import Product +from authentication.models import User +from club.models import Club, ClubMembership, Season +from club.tenancy import reset_current_club, set_current_club +from members.models import Member +from teams.models import Position, Team + +from .admin import ProductAdmin +from .models import ( + AppliedDiscount, + Cart, + CartItem, + Discount, + Invoice, + Order, + OrderLine, + Payment, + Product, +) class ProductSlugTests(TestCase): @@ -37,3 +61,400 @@ class ProductSlugTests(TestCase): product = Product.objects.create(club=self.club, name="###") self.assertEqual(product.slug, "item") + + def test_str_returns_name(self): + product = Product.objects.create(club=self.club, name="Home Jersey") + + self.assertEqual(str(product), "Home Jersey") + + +class OpenCartConstraintTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.user = User.objects.create_user(email="shopper@example.com", password="pw") + + def test_only_one_open_cart_per_user_per_club(self): + Cart.objects.create(club=self.club, user=self.user) + + with self.assertRaises(IntegrityError): + Cart.objects.create(club=self.club, user=self.user) + + def test_open_and_closed_carts_coexist(self): + Cart.objects.create(club=self.club, user=self.user, status=Cart.CartStatus.CHECKED_OUT) + Cart.objects.create(club=self.club, user=self.user, status=Cart.CartStatus.ABANDONED) + Cart.objects.create(club=self.club, user=self.user) + + self.assertEqual(self.user.carts.count(), 3) + self.assertEqual(self.user.carts.filter(status=Cart.CartStatus.OPEN).count(), 1) + + def test_open_cart_allowed_in_each_club(self): + other = Club.objects.create(name="Rival FC", slug="rival-fc") + Cart.objects.create(club=self.club, user=self.user) + Cart.objects.create(club=other, user=self.user) + + self.assertEqual(self.user.carts.filter(status=Cart.CartStatus.OPEN).count(), 2) + + def test_str(self): + cart = Cart.objects.create(club=self.club, user=self.user) + + self.assertEqual(str(cart), f"{self.user} - open") + + +class CartItemTests(TestCase): + def test_str(self): + club = Club.objects.create(name="Ajax United", slug="ajax-united") + user = User.objects.create_user(email="shopper@example.com", password="pw") + cart = Cart.objects.create(club=club, user=user) + product = Product.objects.create(club=club, name="Home Jersey") + item = CartItem.objects.create(cart=cart, product=product, quantity=2, unit_price=Decimal("25.00")) + + self.assertEqual(str(item), "Home Jersey - 2x") + + +class OrderNumberTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.member = Member.objects.create(first_name="Jane", last_name="Doe") + self.year = timezone.now().year + + def make_order(self, **kwargs): + kwargs.setdefault("club", self.club) + kwargs.setdefault("purchaser", self.member) + kwargs.setdefault("total", Decimal("10.00")) + return Order.objects.create(**kwargs) + + def test_number_is_generated(self): + order = self.make_order() + + self.assertEqual(order.number, f"ORD-{self.year}-00001") + + def test_number_increments_within_club_and_year(self): + first = self.make_order() + second = self.make_order() + + self.assertEqual(first.number, f"ORD-{self.year}-00001") + self.assertEqual(second.number, f"ORD-{self.year}-00002") + + def test_number_is_scoped_per_club(self): + other = Club.objects.create(name="Rival FC", slug="rival-fc") + self.make_order() + + order = self.make_order(club=other) + + self.assertEqual(order.number, f"ORD-{self.year}-00001") + + def test_explicit_number_is_preserved(self): + order = self.make_order(number="CUSTOM-1") + + self.assertEqual(order.number, "CUSTOM-1") + + def test_resaving_keeps_the_number(self): + order = self.make_order() + original = order.number + + order.status = Order.OrderStatus.PAID + order.save() + + order.refresh_from_db() + self.assertEqual(order.number, original) + + def test_non_numeric_suffix_is_ignored(self): + Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("5"), number=f"ORD-{self.year}-oops") + + order = self.make_order() + + self.assertEqual(order.number, f"ORD-{self.year}-00001") + + def test_club_filled_from_tenant_context(self): + token = set_current_club(self.club) + try: + order = Order.objects.create(purchaser=self.member, total=Decimal("5")) + finally: + reset_current_club(token) + + self.assertEqual(order.club, self.club) + + def test_retries_on_collision(self): + taken = self.make_order().number + with patch.object(Order, "generate_number", side_effect=[taken, "ORD-2999-00001"]): + order = Order(club=self.club, purchaser=self.member, total=Decimal("5")) + order.save() + + self.assertEqual(order.number, "ORD-2999-00001") + + def test_gives_up_after_exhausting_retries(self): + taken = self.make_order().number + with patch.object(Order, "generate_number", return_value=taken), self.assertRaises(IntegrityError): + Order(club=self.club, purchaser=self.member, total=Decimal("5")).save() + + def test_str_is_the_number(self): + order = self.make_order() + + self.assertEqual(str(order), order.number) + + +class ShopEntitiesTestBase(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.member = Member.objects.create(first_name="Jane", last_name="Doe") + self.product = Product.objects.create(club=self.club, name="Home Jersey") + self.order = Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("50.00")) + self.year = timezone.now().year + + +class OrderLineTests(ShopEntitiesTestBase): + def make_line(self, **kwargs): + kwargs.setdefault("order", self.order) + kwargs.setdefault("product", self.product) + kwargs.setdefault("quantity", 2) + kwargs.setdefault("unit_price", Decimal("25.00")) + kwargs.setdefault("line_total", Decimal("50.00")) + return OrderLine.objects.create(**kwargs) + + def test_str(self): + self.assertEqual(str(self.make_line()), "Home Jersey - 2x") + + def test_deleting_order_cascades_to_lines(self): + self.make_line() + self.order.delete() + self.assertFalse(OrderLine.objects.exists()) + + def test_product_is_protected_while_referenced(self): + self.make_line() + with self.assertRaises(ProtectedError): + self.product.delete() + + +class DiscountTests(ShopEntitiesTestBase): + def test_slug_and_str(self): + discount = Discount.objects.create(club=self.club, name="Sibling discount") + + self.assertEqual(discount.slug, "sibling-discount") + self.assertEqual(str(discount), "Sibling discount") + + def test_slug_is_unique_per_club(self): + Discount.objects.create(club=self.club, name="Sibling") + second = Discount.objects.create(club=self.club, name="Sibling") + + self.assertEqual(second.slug, "sibling-2") + + +class AppliedDiscountTests(ShopEntitiesTestBase): + def setUp(self): + super().setUp() + self.discount = Discount.objects.create(club=self.club, name="Sibling") + + def apply(self, **kwargs): + kwargs.setdefault("order", self.order) + kwargs.setdefault("discount", self.discount) + kwargs.setdefault("discount_amount", Decimal("10.00")) + return AppliedDiscount.objects.create(**kwargs) + + def test_str_percentage_shows_percent(self): + applied = self.apply(discount_type=AppliedDiscount.DiscountType.PERCENTAGE) + + self.assertEqual(str(applied), "Sibling - 10.00%") + + def test_str_fixed_amount_has_no_percent(self): + applied = self.apply(discount_type=AppliedDiscount.DiscountType.FIXED_AMOUNT) + + self.assertEqual(str(applied), "Sibling - 10.00") + + def test_deleting_order_cascades(self): + self.apply() + self.order.delete() + self.assertFalse(AppliedDiscount.objects.exists()) + + def test_discount_is_protected_while_referenced(self): + self.apply() + with self.assertRaises(ProtectedError): + self.discount.delete() + + +class PaymentTests(ShopEntitiesTestBase): + def test_str(self): + payment = Payment.objects.create(order=self.order, amount=Decimal("50.00")) + + self.assertEqual(str(payment), f"{self.order} - pending") + + def test_deleting_order_cascades(self): + Payment.objects.create(order=self.order, amount=Decimal("50.00")) + self.order.delete() + self.assertFalse(Payment.objects.exists()) + + +class InvoiceTests(ShopEntitiesTestBase): + def test_number_generated_and_str(self): + invoice = Invoice.objects.create(club=self.club, order=self.order) + + self.assertEqual(invoice.number, f"INV-{self.year}-00001") + self.assertEqual(str(invoice), invoice.number) + + def test_number_increments_per_club(self): + second_order = Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("10.00")) + Invoice.objects.create(club=self.club, order=self.order) + second = Invoice.objects.create(club=self.club, order=second_order) + + self.assertEqual(second.number, f"INV-{self.year}-00002") + + def test_one_invoice_per_order(self): + Invoice.objects.create(club=self.club, order=self.order) + + with self.assertRaises(IntegrityError): + Invoice.objects.create(club=self.club, order=self.order) + + def test_deleting_order_cascades_to_invoice(self): + Invoice.objects.create(club=self.club, order=self.order) + self.order.delete() + self.assertFalse(Invoice.objects.exists()) + + +class ClubScopeValidationTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.other = Club.objects.create(name="Rival FC", slug="rival-fc") + today = timezone.localdate() + self.season = Season.objects.create(club=self.club, start_date=today, end_date=today + timedelta(days=300)) + self.other_season = Season.objects.create(club=self.other, start_date=today, end_date=today + timedelta(days=300)) + self.member = Member.objects.create(first_name="Jane", last_name="Doe") + ClubMembership.objects.create(club=self.club, member=self.member, season=self.season) + self.stranger = Member.objects.create(first_name="Stray", last_name="Ger") + + def make_cart(self, club): + user = User.objects.create_user(email=f"u-{club.slug}@example.com", password="pw") + return Cart.objects.create(club=club, user=user) + + def make_order(self, club): + return Order.objects.create(club=club, purchaser=self.member, total=Decimal("10.00")) + + # --- Product --- + def test_product_rejects_cross_club_season(self): + product = Product(club=self.club, name="Jersey", season=self.other_season) + with self.assertRaises(ValidationError) as ctx: + product.full_clean() + self.assertIn("season", ctx.exception.error_dict) + + def test_product_rejects_cross_club_staff_role(self): + position = Position.objects.create(club=self.other, name="Coach", short_name="C", staff_position=True) + product = Product(club=self.club, name="Jersey", staff_role=position) + with self.assertRaises(ValidationError) as ctx: + product.full_clean() + self.assertIn("staff_role", ctx.exception.error_dict) + + def test_product_accepts_same_club_season(self): + Product(club=self.club, name="Jersey", season=self.season).full_clean() + + # --- CartItem --- + def test_cartitem_rejects_cross_club_product(self): + cart = self.make_cart(self.club) + product = Product.objects.create(club=self.other, name="Jersey") + item = CartItem(cart=cart, product=product, unit_price=Decimal("5")) + with self.assertRaises(ValidationError) as ctx: + item.full_clean() + self.assertIn("product", ctx.exception.error_dict) + + def test_cartitem_rejects_cross_club_team(self): + cart = self.make_cart(self.club) + product = Product.objects.create(club=self.club, name="Jersey") + team = Team.objects.create(club=self.other, name="First", short_name="1") + item = CartItem(cart=cart, product=product, team=team, unit_price=Decimal("5")) + with self.assertRaises(ValidationError) as ctx: + item.full_clean() + self.assertIn("team", ctx.exception.error_dict) + + def test_cartitem_rejects_non_member_beneficiary(self): + cart = self.make_cart(self.club) + product = Product.objects.create(club=self.club, name="Jersey") + item = CartItem(cart=cart, product=product, beneficiary=self.stranger, unit_price=Decimal("5")) + with self.assertRaises(ValidationError) as ctx: + item.full_clean() + self.assertIn("beneficiary", ctx.exception.error_dict) + + def test_cartitem_accepts_same_club(self): + cart = self.make_cart(self.club) + product = Product.objects.create(club=self.club, name="Jersey") + CartItem(cart=cart, product=product, beneficiary=self.member, unit_price=Decimal("5")).full_clean() + + def test_cartitem_clean_without_cart_is_noop(self): + CartItem().clean() + + # --- OrderLine --- + def test_orderline_rejects_cross_club_product(self): + order = self.make_order(self.club) + product = Product.objects.create(club=self.other, name="Jersey") + line = OrderLine(order=order, product=product, unit_price=Decimal("5"), line_total=Decimal("5")) + with self.assertRaises(ValidationError) as ctx: + line.full_clean() + self.assertIn("product", ctx.exception.error_dict) + + def test_orderline_clean_without_order_is_noop(self): + OrderLine().clean() + + # --- Order --- + def test_order_rejects_non_member_purchaser(self): + order = Order(club=self.club, purchaser=self.stranger, total=Decimal("10")) + with self.assertRaises(ValidationError) as ctx: + order.full_clean() + self.assertIn("purchaser", ctx.exception.error_dict) + + def test_order_accepts_member_purchaser(self): + Order(club=self.club, purchaser=self.member, total=Decimal("10")).full_clean() + + # --- AppliedDiscount --- + def test_applieddiscount_rejects_cross_club_discount(self): + order = self.make_order(self.club) + discount = Discount.objects.create(club=self.other, name="Sibling") + applied = AppliedDiscount(order=order, discount=discount, discount_amount=Decimal("5")) + with self.assertRaises(ValidationError) as ctx: + applied.full_clean() + self.assertIn("discount", ctx.exception.error_dict) + + def test_applieddiscount_rejects_non_member_applied_by(self): + order = self.make_order(self.club) + discount = Discount.objects.create(club=self.club, name="Sibling") + applied = AppliedDiscount(order=order, discount=discount, discount_amount=Decimal("5"), applied_by=self.stranger) + with self.assertRaises(ValidationError) as ctx: + applied.full_clean() + self.assertIn("applied_by", ctx.exception.error_dict) + + def test_applieddiscount_is_unique_per_order(self): + order = self.make_order(self.club) + discount = Discount.objects.create(club=self.club, name="Sibling") + AppliedDiscount.objects.create(order=order, discount=discount, discount_amount=Decimal("5")) + with self.assertRaises(IntegrityError): + AppliedDiscount.objects.create(order=order, discount=discount, discount_amount=Decimal("5")) + + def test_applieddiscount_clean_without_order_is_noop(self): + AppliedDiscount().clean() + + # --- Invoice --- + def test_invoice_rejects_cross_club_order(self): + order = self.make_order(self.other) + invoice = Invoice(club=self.club, order=order) + with self.assertRaises(ValidationError) as ctx: + invoice.full_clean() + self.assertIn("order", ctx.exception.error_dict) + + def test_invoice_accepts_same_club_order(self): + order = self.make_order(self.club) + Invoice(club=self.club, order=order).full_clean() + + +class AdminScopingTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.other = Club.objects.create(name="Rival FC", slug="rival-fc") + today = timezone.localdate() + self.season = Season.objects.create(club=self.club, start_date=today, end_date=today + timedelta(days=300)) + self.other_season = Season.objects.create(club=self.other, start_date=today, end_date=today + timedelta(days=300)) + + def test_fk_dropdown_scoped_to_object_club(self): + product = Product.objects.create(club=self.club, name="Jersey") + admin_obj = ProductAdmin(Product, AdminSite()) + request = RequestFactory().get("/") + request._club_obj = product + + field = admin_obj.formfield_for_foreignkey(Product._meta.get_field("season"), request) + + self.assertIn(self.season, field.queryset) + self.assertNotIn(self.other_season, field.queryset) From 819700ad0cb8cd74321de115a2c132d9226dad98 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 14:43:15 +0200 Subject: [PATCH 032/137] feat(club): ClubRole, RBAC access service and role sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ClubRole (ADMIN / MEMBER / EDITOR, one per member per club) and complete club/services/access.py — the single module all authorisation routes through: - teams_managed_by / can_edit_event -> authority: a *management* StaffAssignment in the *current season*; ADMIN overrides club-wide. A StaffAssignment is per-season, so a former coach's authority expires with it. - teams_staffed_by -> visibility: *any* staff position, so support staff (physio, kit manager) can see the roster they work with without gaining authority. - members_visible_to -> ADMIN sees everyone linked to the club; otherwise self + children (family graph) + the current-season players and staff of the teams they're staffed on. - can_edit_event -> ADMIN/EDITOR, the event's owner, or a manager of one of its teams for that event's season. - can_manage_shop -> ADMIN. Fix roles_in_club, which called .unique() — not a QuerySet method, so it would have raised AttributeError on first use. Keep ClubRole in sync with membership status: an active ClubMembership grants the MEMBER role and losing it withdraws that role — but an elevated role (ADMIN/EDITOR) is never downgraded or removed, so a lapsed membership or a season rollover can never lock an admin out. Validate ClubMembership.season against the membership's club. Co-Authored-By: Claude Opus 4.8 --- club/admin.py | 10 +- club/apps.py | 3 + club/migrations/0009_clubrole.py | 31 +++ club/models.py | 26 +- club/services/__init__.py | 10 + club/services/access.py | 145 +++++++++++ club/signals.py | 39 +++ club/tests.py | 415 ++++++++++++++++++++++++++++++- 8 files changed, 674 insertions(+), 5 deletions(-) create mode 100644 club/migrations/0009_clubrole.py create mode 100644 club/signals.py diff --git a/club/admin.py b/club/admin.py index ea9063e..0a0f3bd 100644 --- a/club/admin.py +++ b/club/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from django.utils.translation import gettext_lazy as _ -from .models import Club, ClubMembership, Season +from .models import Club, ClubMembership, ClubRole, Season @admin.register(Club) @@ -31,3 +31,11 @@ class ClubMembershipAdmin(admin.ModelAdmin): [_("Membership"), {"fields": ["license", "status", "fee_status"]}], [_("Dates"), {"fields": ["signed_up_at", "activated_at"]}], ] + + +@admin.register(ClubRole) +class ClubRoleAdmin(admin.ModelAdmin): + list_display = ["club__name", "member__last_name", "member__first_name", "role"] + search_fields = ["club__name", "member__last_name", "member__first_name"] + list_filter = ["club", "role"] + raw_id_fields = ["member"] diff --git a/club/apps.py b/club/apps.py index 8e8a26a..48e3b96 100644 --- a/club/apps.py +++ b/club/apps.py @@ -3,3 +3,6 @@ from django.apps import AppConfig class ClubConfig(AppConfig): name = "club" + + def ready(self): + from . import signals # noqa: F401 diff --git a/club/migrations/0009_clubrole.py b/club/migrations/0009_clubrole.py new file mode 100644 index 0000000..1c1d660 --- /dev/null +++ b/club/migrations/0009_clubrole.py @@ -0,0 +1,31 @@ +# Generated by Django 6.0.6 on 2026-07-12 21:43 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0008_alter_clubmembership_unique_together_and_more'), + ('members', '0002_alter_familymembership_unique_together_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='ClubRole', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('role', models.CharField(choices=[('admin', 'admin'), ('member', 'member'), ('editor', 'editor')], default='member', max_length=250, verbose_name='role')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(class)ss', to='club.club')), + ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='roles', to='members.member', verbose_name='member')), + ], + options={ + 'verbose_name': 'club role', + 'verbose_name_plural': 'club roles', + 'ordering': ['club', 'member__last_name', 'member__first_name'], + 'constraints': [models.UniqueConstraint(fields=('club', 'member'), name='unique_member_per_club')], + }, + ), + ] diff --git a/club/models.py b/club/models.py index 0a20cb5..17c9b30 100644 --- a/club/models.py +++ b/club/models.py @@ -4,7 +4,7 @@ from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from clubmanager.base import ClubScopedModel, UUIDModel, unique_slugify +from clubmanager.base import ClubScopedModel, UUIDModel, unique_slugify, validate_club_scope from members.models import Member @@ -102,3 +102,27 @@ class ClubMembership(ClubScopedModel): def __str__(self): return f"{self.club} - {self.member}" + + def clean(self): + validate_club_scope(self, self.club_id, same_club_fields=("season",)) + + +class ClubRole(ClubScopedModel): + class Roles(models.TextChoices): + ADMIN = "admin", _("admin") + MEMBER = "member", _("member") + EDITOR = "editor", _("editor") + + member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name="roles", verbose_name=_("member")) + role = models.CharField(_("role"), max_length=250, choices=Roles.choices, default=Roles.MEMBER) + + class Meta: + verbose_name = _("club role") + verbose_name_plural = _("club roles") + ordering = ["club", "member__last_name", "member__first_name"] + constraints = [ + models.UniqueConstraint(fields=["club", "member"], name="unique_member_per_club"), + ] + + def __str__(self): + return f"{self.club} - {self.member}" diff --git a/club/services/__init__.py b/club/services/__init__.py index e69de29..0c594c5 100644 --- a/club/services/__init__.py +++ b/club/services/__init__.py @@ -0,0 +1,10 @@ +from .access import can_edit_event, can_manage_shop, has_club_role, members_visible_to, roles_in_club, teams_managed_by + +__all__ = [ + "can_edit_event", + "can_manage_shop", + "has_club_role", + "members_visible_to", + "roles_in_club", + "teams_managed_by", +] diff --git a/club/services/access.py b/club/services/access.py index e69de29..145ed81 100644 --- a/club/services/access.py +++ b/club/services/access.py @@ -0,0 +1,145 @@ +"""Per-club access decisions. + +All authorisation goes through this module (ARCHITECTURE §3): stored +``ClubRole`` rows plus object-scoped facts — coach/manager is *derived* from +``StaffAssignment`` (never a ClubRole), and parent/guardian from the family +graph. ADMIN is the club-wide override. + +Two axes, deliberately separate: + +* **Authority** (``teams_managed_by``, ``can_edit_event``) requires a + *management* position, and only for the **current season** — a StaffAssignment + is per-season, so a former coach's authority expires with it. (A ``ClubRole``, + by contrast, is permanent and survives a lapsed membership.) +* **Visibility** (``teams_staffed_by`` → ``members_visible_to``) covers *any* + staff position, so support staff (physio, kit manager) can see the roster they + work with without gaining any authority over it. +""" + +from django.db.models import Q, QuerySet +from django.utils import timezone + +from authentication.models import User +from club.models import Club, ClubRole, Season +from events.models import Event +from members.models import FamilyMembership, Member +from teams.models import StaffAssignment, Team + +#: Derived (never stored) roles. +COACH = "coach" +MANAGER = "manager" +COACH_MANAGER = "coach_manager" + + +def current_season(club: Club) -> Season | None: + """The club's season covering today. Staff authority is scoped to it.""" + return Season.covering(club, timezone.localdate()) + + +def event_season(event: Event) -> Season | None: + """The season an event belongs to (explicit, else derived from its start).""" + return event.season or Season.covering(event.club, event.start.date()) + + +def has_club_role(user: User, club: Club, role: ClubRole.Roles) -> bool: + return ClubRole.objects.filter(member__user=user, club=club, role=role).exists() + + +def is_club_admin(user: User, club: Club) -> bool: + return has_club_role(user, club, ClubRole.Roles.ADMIN) + + +def is_coach_manager(user: User, club: Club) -> bool: + """Derived from a current-season StaffAssignment in a *management* position.""" + return StaffAssignment.objects.filter( + member__user=user, + team__club=club, + position__management_position=True, + season=current_season(club), + ).exists() + + +def roles_in_club(user: User, club: Club) -> set[str]: + """The user's roles in ``club``, including the derived COACH_MANAGER role.""" + roles = set(ClubRole.objects.filter(member__user=user, club=club).values_list("role", flat=True)) + if is_coach_manager(user, club): + roles.add(COACH_MANAGER) + return roles + + +def teams_managed_by(user: User, club: Club) -> QuerySet[Team]: + """Teams the user has authority over: all for an ADMIN, else the ones they + manage *this season* (management position only).""" + if is_club_admin(user, club): + return Team.objects.filter(club=club) + return Team.objects.filter( + club=club, + staff_assignments__member__user=user, + staff_assignments__position__management_position=True, + staff_assignments__season=current_season(club), + ).distinct() + + +def teams_staffed_by(user: User, club: Club) -> QuerySet[Team]: + """Teams the user is on the staff of this season, management or not. + + Visibility only — being a team's physio grants sight of the roster, never + authority over it. + """ + return Team.objects.filter( + club=club, + staff_assignments__member__user=user, + staff_assignments__season=current_season(club), + ).distinct() + + +def members_visible_to(user: User, club: Club) -> QuerySet[Member]: + """Members the user may see. + + ADMIN: everyone linked to the club (membership, roster, staff or role). + Otherwise: themselves, their children, and the current-season players *and* + staff of every team they're staffed on. + """ + if is_club_admin(user, club): + return Member.objects.filter( + Q(member_of__club=club) | Q(team_memberships__team__club=club) | Q(staff_assignments__team__club=club) | Q(roles__club=club) + ).distinct() + + me = Member.objects.filter(user=user).first() + if me is None: + return Member.objects.none() + + children = Member.objects.filter( + family_memberships__role=FamilyMembership.FamilyRole.CHILD, + family_memberships__family__memberships__member=me, + family_memberships__family__memberships__role__in=[FamilyMembership.FamilyRole.PARENT, FamilyMembership.FamilyRole.GUARDIAN], + ) + + season = current_season(club) + teams = teams_staffed_by(user, club) + roster = Member.objects.filter( + Q(team_memberships__team__in=teams, team_memberships__season=season) | Q(staff_assignments__team__in=teams, staff_assignments__season=season) + ) + + visible = {me.pk} | set(children.values_list("pk", flat=True)) | set(roster.values_list("pk", flat=True)) + return Member.objects.filter(pk__in=visible) + + +def can_edit_event(user: User, event: Event) -> bool: + """ADMIN/EDITOR in the club, the event's owner, or a manager of one of its + teams *for that event's season*.""" + club = event.club + if is_club_admin(user, club) or has_club_role(user, club, ClubRole.Roles.EDITOR): + return True + if event.created_by_id is not None and event.created_by.user_id == user.pk: + return True + return StaffAssignment.objects.filter( + member__user=user, + team__in=event.teams.all(), + position__management_position=True, + season=event_season(event), + ).exists() + + +def can_manage_shop(user: User, club: Club) -> bool: + return is_club_admin(user, club) diff --git a/club/signals.py b/club/signals.py new file mode 100644 index 0000000..6a142d6 --- /dev/null +++ b/club/signals.py @@ -0,0 +1,39 @@ +"""Keep ClubRole in sync with membership status. + +An **active** ClubMembership grants the member a ``MEMBER`` ClubRole; when no +active membership remains in that club (status changed away from active, or the +membership was deleted), the ``MEMBER`` role is withdrawn. + +A member holds at most one ClubRole per club (``unique_member_per_club``), so an +elevated role (ADMIN / EDITOR) is never downgraded or removed by this sync — it +simply takes precedence. +""" + +from django.db.models.signals import post_delete, post_save +from django.dispatch import receiver + +from .models import ClubMembership, ClubRole + + +@receiver(post_save, sender=ClubMembership) +@receiver(post_delete, sender=ClubMembership) +def sync_member_role(sender, instance, **kwargs): + has_active = ClubMembership.objects.filter( + club_id=instance.club_id, + member_id=instance.member_id, + status=ClubMembership.StatusChoices.ACTIVE, + ).exists() + + if has_active: + # get_or_create keeps an existing ADMIN/EDITOR role untouched. + ClubRole.objects.get_or_create( + club_id=instance.club_id, + member_id=instance.member_id, + defaults={"role": ClubRole.Roles.MEMBER}, + ) + else: + ClubRole.objects.filter( + club_id=instance.club_id, + member_id=instance.member_id, + role=ClubRole.Roles.MEMBER, + ).delete() diff --git a/club/tests.py b/club/tests.py index 8186d95..7e1890f 100644 --- a/club/tests.py +++ b/club/tests.py @@ -4,15 +4,28 @@ from contextlib import contextmanager from django.contrib import admin as django_admin from django.contrib.auth import get_user_model +from django.core.exceptions import ValidationError from django.db import IntegrityError from django.db.models import ProtectedError from django.test import RequestFactory, TestCase, override_settings from django.urls import reverse from django.utils import timezone -from members.models import Member +from events.models import Event +from members.models import Family, FamilyMembership, Member +from teams.models import Position, StaffAssignment, Team, TeamMembership -from .models import Club, ClubMembership, Season +from .models import Club, ClubMembership, ClubRole, Season +from .services.access import ( + COACH_MANAGER, + can_edit_event, + can_manage_shop, + has_club_role, + members_visible_to, + roles_in_club, + teams_managed_by, + teams_staffed_by, +) from .tenancy import ( ClubTenantMiddleware, get_current_club, @@ -455,7 +468,7 @@ class AdminRegistrationSmokeTests(TestCase): registered = set(django_admin.site._registry) # Concrete, non-auto-created models in these apps should all be registered. - project_apps = {"authentication", "club", "members", "teams", "events", "formbuilder"} + project_apps = {"authentication", "club", "members", "teams", "events", "formbuilder", "shop"} for model in apps.get_models(): if model._meta.app_label not in project_apps or model._meta.auto_created: continue @@ -473,3 +486,399 @@ class AdminRegistrationSmokeTests(TestCase): url = reverse(f"admin:{model._meta.app_label}_{model._meta.model_name}_add") with self.subTest(model=model.__name__): self.assertEqual(self.client.get(url).status_code, 200) + + +class ClubRoleTests(TestCase): + def test_str(self): + club = Club.objects.create(name="Ajax United", slug="ajax-united") + member = Member.objects.create(first_name="Jane", last_name="Doe") + role = ClubRole.objects.create(club=club, member=member) + + self.assertEqual(str(role), f"{club} - {member}") + + +class ClubMembershipCleanTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.other = Club.objects.create(name="Rival FC", slug="rival-fc") + today = timezone.localdate() + self.season = Season.objects.create(club=self.club, start_date=today, end_date=today + datetime.timedelta(days=300)) + self.other_season = Season.objects.create(club=self.other, start_date=today, end_date=today + datetime.timedelta(days=300)) + self.member = Member.objects.create(first_name="Jane", last_name="Doe") + + def test_rejects_cross_club_season(self): + membership = ClubMembership(club=self.club, member=self.member, season=self.other_season) + with self.assertRaises(ValidationError) as ctx: + membership.full_clean() + self.assertIn("season", ctx.exception.error_dict) + + def test_accepts_same_club_season(self): + ClubMembership(club=self.club, member=self.member, season=self.season).full_clean() + + +class AccessServiceTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.other_club = Club.objects.create(name="Rival FC", slug="rival-fc") + today = timezone.localdate() + self.season = Season.objects.create(club=self.club, start_date=today, end_date=today + datetime.timedelta(days=300)) + self.team = Team.objects.create(club=self.club, name="First Team", short_name="1st") + self.second_team = Team.objects.create(club=self.club, name="Second Team", short_name="2nd") + self.forward = Position.objects.create(club=self.club, name="Forward", short_name="FW") + # Management staff (coach / team manager) vs. non-management staff (e.g. physio). + self.coach_position = Position.objects.create(club=self.club, name="Head Coach", short_name="HC", staff_position=True, management_position=True) + self.physio_position = Position.objects.create(club=self.club, name="Physio", short_name="PH", staff_position=True, management_position=False) + + def make_user_member(self, email): + user = get_user_model().objects.create_user(email=email, password="pw") + member = Member.objects.create(user=user, first_name=email.split("@")[0].title(), last_name="Doe") + return user, member + + def grant(self, member, role): + return ClubRole.objects.create(club=self.club, member=member, role=role) + + def make_coach(self, member, team=None): + return StaffAssignment.objects.create(team=team or self.team, member=member, season=self.season, position=self.coach_position) + + def make_support_staff(self, member, team=None): + """Staff on the team, but in a non-management position.""" + return StaffAssignment.objects.create(team=team or self.team, member=member, season=self.season, position=self.physio_position) + + def make_event(self, **kwargs): + kwargs.setdefault("club", self.club) + kwargs.setdefault("title", "Match") + kwargs.setdefault("start", timezone.now() + datetime.timedelta(days=1)) + return Event.objects.create(**kwargs) + + # --- has_club_role / roles_in_club --- + def test_has_club_role_is_scoped_to_role_and_club(self): + user, member = self.make_user_member("admin@example.com") + self.grant(member, ClubRole.Roles.ADMIN) + + self.assertTrue(has_club_role(user, self.club, ClubRole.Roles.ADMIN)) + self.assertFalse(has_club_role(user, self.club, ClubRole.Roles.EDITOR)) + self.assertFalse(has_club_role(user, self.other_club, ClubRole.Roles.ADMIN)) + + def test_roles_in_club_includes_derived_coach_manager(self): + user, member = self.make_user_member("editor@example.com") + self.grant(member, ClubRole.Roles.EDITOR) + self.make_coach(member) + + self.assertEqual(roles_in_club(user, self.club), {ClubRole.Roles.EDITOR, COACH_MANAGER}) + + def test_non_management_staff_gets_no_derived_role(self): + user, member = self.make_user_member("physio@example.com") + self.make_support_staff(member) + + self.assertEqual(roles_in_club(user, self.club), set()) + + def test_roles_in_club_is_empty_for_outsider(self): + user, _ = self.make_user_member("nobody@example.com") + + self.assertEqual(roles_in_club(user, self.club), set()) + + # --- teams_managed_by --- + def test_admin_manages_every_team(self): + user, member = self.make_user_member("admin@example.com") + self.grant(member, ClubRole.Roles.ADMIN) + + self.assertEqual(set(teams_managed_by(user, self.club)), {self.team, self.second_team}) + + def test_coach_manages_only_their_team(self): + user, member = self.make_user_member("coach@example.com") + self.make_coach(member) + + self.assertEqual(list(teams_managed_by(user, self.club)), [self.team]) + + def test_plain_member_manages_no_teams(self): + user, _ = self.make_user_member("plain@example.com") + + self.assertEqual(list(teams_managed_by(user, self.club)), []) + + def test_non_management_staff_manages_no_teams(self): + user, member = self.make_user_member("physio@example.com") + self.make_support_staff(member) + + self.assertEqual(list(teams_managed_by(user, self.club)), []) + + def test_non_management_staff_does_not_inherit_a_managers_team(self): + # The team has BOTH a manager and a non-management staffer. The staffer + # must not pick up the team just because *someone else* manages it. + _, manager = self.make_user_member("coach@example.com") + self.make_coach(manager) + physio_user, physio = self.make_user_member("physio@example.com") + self.make_support_staff(physio) + + self.assertEqual(list(teams_managed_by(physio_user, self.club)), []) + + def make_past_season(self): + return Season.objects.create( + club=self.club, + start_date=self.season.start_date - datetime.timedelta(days=400), + end_date=self.season.start_date - datetime.timedelta(days=1), + ) + + def test_a_former_seasons_coach_no_longer_manages_the_team(self): + # StaffAssignment is per-season: authority expires with it. + user, member = self.make_user_member("coach@example.com") + StaffAssignment.objects.create(team=self.team, member=member, season=self.make_past_season(), position=self.coach_position) + + self.assertEqual(list(teams_managed_by(user, self.club)), []) + self.assertEqual(list(teams_staffed_by(user, self.club)), []) + self.assertFalse(roles_in_club(user, self.club)) + + def test_a_former_seasons_coach_cannot_edit_a_current_event(self): + user, member = self.make_user_member("coach@example.com") + StaffAssignment.objects.create(team=self.team, member=member, season=self.make_past_season(), position=self.coach_position) + event = self.make_event() + event.teams.add(self.team) + + self.assertFalse(can_edit_event(user, event)) + + # --- members_visible_to --- + def test_admin_sees_all_club_members(self): + user, member = self.make_user_member("admin@example.com") + self.grant(member, ClubRole.Roles.ADMIN) + other = Member.objects.create(first_name="Other", last_name="Member") + ClubMembership.objects.create(club=self.club, member=member, season=self.season) + ClubMembership.objects.create(club=self.club, member=other, season=self.season) + + self.assertEqual(set(members_visible_to(user, self.club)), {member, other}) + + def test_coach_sees_self_and_managed_roster(self): + user, member = self.make_user_member("coach@example.com") + self.make_coach(member) + player = Member.objects.create(first_name="Player", last_name="One") + TeamMembership.objects.create(team=self.team, member=player, season=self.season, position=self.forward) + unrelated = Member.objects.create(first_name="Un", last_name="Related") + + visible = set(members_visible_to(user, self.club)) + + self.assertEqual(visible, {member, player}) + self.assertNotIn(unrelated, visible) + + def test_parent_sees_self_and_children(self): + user, parent = self.make_user_member("parent@example.com") + child = Member.objects.create(first_name="Kid", last_name="Doe") + family = Family.objects.create(name="Doe") + FamilyMembership.objects.create(family=family, member=parent, role=FamilyMembership.FamilyRole.PARENT) + FamilyMembership.objects.create(family=family, member=child, role=FamilyMembership.FamilyRole.CHILD) + + self.assertEqual(set(members_visible_to(user, self.club)), {parent, child}) + + def test_user_without_a_member_sees_nobody(self): + user = get_user_model().objects.create_user(email="ghost@example.com", password="pw") + + self.assertEqual(list(members_visible_to(user, self.club)), []) + + def test_non_management_staff_sees_the_roster_but_holds_no_authority(self): + # A physio can see the team they work with, but manages nothing. + user, member = self.make_user_member("physio@example.com") + self.make_support_staff(member) + player = Member.objects.create(first_name="Player", last_name="One") + TeamMembership.objects.create(team=self.team, member=player, season=self.season, position=self.forward) + + self.assertEqual(set(members_visible_to(user, self.club)), {member, player}) + self.assertEqual(list(teams_managed_by(user, self.club)), []) + self.assertEqual(list(teams_staffed_by(user, self.club)), [self.team]) + + def test_manager_also_sees_the_teams_other_staff(self): + user, manager = self.make_user_member("coach@example.com") + self.make_coach(manager) + _, physio = self.make_user_member("physio@example.com") + self.make_support_staff(physio) + + self.assertIn(physio, set(members_visible_to(user, self.club))) + + def test_admin_sees_members_without_a_club_membership(self): + user, admin = self.make_user_member("admin@example.com") + self.grant(admin, ClubRole.Roles.ADMIN) + _, coach = self.make_user_member("coach@example.com") + self.make_coach(coach) # staff, but no ClubMembership + + visible = set(members_visible_to(user, self.club)) + + self.assertIn(coach, visible) + self.assertIn(admin, visible) # the admin sees themselves via their ClubRole + + def test_roster_visibility_is_scoped_to_the_current_season(self): + user, member = self.make_user_member("coach@example.com") + self.make_coach(member) + old_season = Season.objects.create( + club=self.club, + start_date=self.season.start_date - datetime.timedelta(days=400), + end_date=self.season.start_date - datetime.timedelta(days=1), + ) + former_player = Member.objects.create(first_name="Former", last_name="Player") + TeamMembership.objects.create(team=self.team, member=former_player, season=old_season, position=self.forward) + + self.assertNotIn(former_player, set(members_visible_to(user, self.club))) + + # --- can_edit_event --- + def test_admin_can_edit_event(self): + user, member = self.make_user_member("admin@example.com") + self.grant(member, ClubRole.Roles.ADMIN) + + self.assertTrue(can_edit_event(user, self.make_event())) + + def test_editor_can_edit_event(self): + user, member = self.make_user_member("editor@example.com") + self.grant(member, ClubRole.Roles.EDITOR) + + self.assertTrue(can_edit_event(user, self.make_event())) + + def test_owner_can_edit_their_event(self): + user, member = self.make_user_member("owner@example.com") + + self.assertTrue(can_edit_event(user, self.make_event(created_by=member))) + + def test_coach_can_edit_their_teams_event(self): + user, member = self.make_user_member("coach@example.com") + self.make_coach(member) + event = self.make_event() + event.teams.add(self.team) + + self.assertTrue(can_edit_event(user, event)) + + def test_coach_cannot_edit_another_teams_event(self): + user, member = self.make_user_member("coach@example.com") + self.make_coach(member) + event = self.make_event() + event.teams.add(self.second_team) + + self.assertFalse(can_edit_event(user, event)) + + def test_plain_member_cannot_edit_event(self): + user, _ = self.make_user_member("plain@example.com") + + self.assertFalse(can_edit_event(user, self.make_event())) + + def test_non_management_staff_cannot_edit_their_teams_event(self): + user, member = self.make_user_member("physio@example.com") + self.make_support_staff(member) + event = self.make_event() + event.teams.add(self.team) + + self.assertFalse(can_edit_event(user, event)) + + def test_non_management_staff_on_a_managed_team_cannot_edit_its_event(self): + # Same escalation shape as teams_managed_by: a manager exists on the team, + # but the physio must not inherit edit rights from them. + _, manager = self.make_user_member("coach@example.com") + self.make_coach(manager) + physio_user, physio = self.make_user_member("physio@example.com") + self.make_support_staff(physio) + event = self.make_event() + event.teams.add(self.team) + + self.assertFalse(can_edit_event(physio_user, event)) + + # --- can_manage_shop --- + def test_only_admin_can_manage_shop(self): + admin_user, admin_member = self.make_user_member("admin@example.com") + self.grant(admin_member, ClubRole.Roles.ADMIN) + editor_user, editor_member = self.make_user_member("editor@example.com") + self.grant(editor_member, ClubRole.Roles.EDITOR) + + self.assertTrue(can_manage_shop(admin_user, self.club)) + self.assertFalse(can_manage_shop(editor_user, self.club)) + + +class ClubRoleStatusSyncTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + today = timezone.localdate() + self.season = Season.objects.create(club=self.club, start_date=today, end_date=today + datetime.timedelta(days=300)) + self.member = Member.objects.create(first_name="Jane", last_name="Doe") + + def roles(self): + return ClubRole.objects.filter(club=self.club, member=self.member) + + def make_membership(self, status=ClubMembership.StatusChoices.ACTIVE): + return ClubMembership.objects.create(club=self.club, member=self.member, season=self.season, status=status) + + def test_active_membership_grants_member_role(self): + self.make_membership() + + self.assertEqual(self.roles().get().role, ClubRole.Roles.MEMBER) + + def test_pending_membership_grants_no_role(self): + self.make_membership(status=ClubMembership.StatusChoices.PENDING) + + self.assertFalse(self.roles().exists()) + + def test_deactivating_membership_withdraws_member_role(self): + membership = self.make_membership() + self.assertTrue(self.roles().exists()) + + membership.status = ClubMembership.StatusChoices.LAPSED + membership.save() + + self.assertFalse(self.roles().exists()) + + def test_deleting_membership_withdraws_member_role(self): + membership = self.make_membership() + + membership.delete() + + self.assertFalse(self.roles().exists()) + + def test_elevated_role_is_never_downgraded_or_removed(self): + ClubRole.objects.create(club=self.club, member=self.member, role=ClubRole.Roles.ADMIN) + + membership = self.make_membership() + self.assertEqual(self.roles().get().role, ClubRole.Roles.ADMIN) + + membership.status = ClubMembership.StatusChoices.CANCELLED + membership.save() + + self.assertEqual(self.roles().get().role, ClubRole.Roles.ADMIN) + + def test_editor_role_survives_a_lapsed_membership(self): + ClubRole.objects.create(club=self.club, member=self.member, role=ClubRole.Roles.EDITOR) + membership = self.make_membership() + + membership.status = ClubMembership.StatusChoices.LAPSED + membership.save() + + self.assertEqual(self.roles().get().role, ClubRole.Roles.EDITOR) + + def test_elevated_role_survives_membership_deletion(self): + ClubRole.objects.create(club=self.club, member=self.member, role=ClubRole.Roles.ADMIN) + membership = self.make_membership() + + membership.delete() + + self.assertEqual(self.roles().get().role, ClubRole.Roles.ADMIN) + + def test_elevated_role_survives_a_season_rollover(self): + # Last season's membership lapses and the new season's is still pending: + # the admin must not lose their role in the gap. + ClubRole.objects.create(club=self.club, member=self.member, role=ClubRole.Roles.ADMIN) + last_season = self.make_membership() + next_season = Season.objects.create( + club=self.club, + start_date=self.season.end_date + datetime.timedelta(days=1), + end_date=self.season.end_date + datetime.timedelta(days=300), + ) + + last_season.status = ClubMembership.StatusChoices.LAPSED + last_season.save() + ClubMembership.objects.create(club=self.club, member=self.member, season=next_season, status=ClubMembership.StatusChoices.PENDING) + + self.assertEqual(self.roles().get().role, ClubRole.Roles.ADMIN) + + def test_elevated_access_and_login_survive_a_lapsed_membership(self): + # The whole point: a lapsed membership must not lock an admin out. + user = get_user_model().objects.create_user(email="admin@example.com", password="pw") + admin = Member.objects.create(user=user, first_name="Ada", last_name="Min") + ClubRole.objects.create(club=self.club, member=admin, role=ClubRole.Roles.ADMIN) + membership = ClubMembership.objects.create(club=self.club, member=admin, season=self.season, status=ClubMembership.StatusChoices.ACTIVE) + + membership.status = ClubMembership.StatusChoices.CANCELLED + membership.save() + + self.assertTrue(user.is_active) # can still log in + self.assertIn(ClubRole.Roles.ADMIN, roles_in_club(user, self.club)) + self.assertTrue(has_club_role(user, self.club, ClubRole.Roles.ADMIN)) + self.assertTrue(can_manage_shop(user, self.club)) From 10736fd5ee48d9e690b67d2d38958c831e261b98 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 14:43:55 +0200 Subject: [PATCH 033/137] feat(auth): two-factor authentication (TOTP, passkeys, recovery codes) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adopt django-allauth with allauth.mfa, giving TOTP, WebAuthn passkeys and recovery codes — and the signup/password-reset flows we'll need next. There was no login UI at all before this (only /admin/), so this brings the auth stack. The critical piece is authentication/adapters.py. A passkey is bound to a WebAuthn Relying Party ID (a domain), and allauth derives that from the request host — which under our subdomain tenancy would bind a passkey to a *single* club (ajax-united.clubmanager.app) and silently fail at every other one. The adapter pins the RP ID to CLUBMANAGER_BASE_DOMAIN so one passkey works across all clubs. Note this cuts both ways: changing that base domain invalidates every existing passkey. RequireMFAMiddleware makes a second factor mandatory for anyone who can change other people's data — Django staff/superusers and holders of an elevated ClubRole (ADMIN/EDITOR), via the access service — while leaving it optional for regular members. /admin/login/ is routed through allauth, since Django's own admin login knows nothing about second factors. allauth is installed WITHOUT django.contrib.sites (optional since allauth 65), so ARCHITECTURE.md's rejection of the Sites framework stands and no Club.site bridge is needed. Sessions are shared across club subdomains, matching the one-passkey-everywhere model; tenancy still scopes what you can see. Co-Authored-By: Claude Opus 4.8 --- .env.example | 12 +++ authentication/adapters.py | 37 ++++++++ authentication/middleware.py | 47 ++++++++++ authentication/tests.py | 148 +++++++++++++++++++++++++++++- club/tests.py | 4 + clubmanager/settings.py | 57 ++++++++++++ clubmanager/urls.py | 25 ++--- members/tests.py | 3 + pyproject.toml | 1 + uv.lock | 172 +++++++++++++++++++++++++++++++++++ 10 files changed, 490 insertions(+), 16 deletions(-) create mode 100644 authentication/adapters.py create mode 100644 authentication/middleware.py diff --git a/.env.example b/.env.example index 91022cb..adc24fd 100644 --- a/.env.example +++ b/.env.example @@ -15,6 +15,18 @@ DJANGO_ALLOWED_HOSTS=.localhost,127.0.0.1,[::1] # In production set this to your real base domain (e.g. clubmanager.app). CLUBMANAGER_BASE_DOMAIN=localhost +# Two-factor auth. CLUBMANAGER_BASE_DOMAIN doubles as the WebAuthn Relying Party +# ID, so ONE passkey works across every club subdomain. Change it and existing +# passkeys stop validating -- they are cryptographically bound to that domain. +# CLUBMANAGER_RP_NAME is what the browser shows during a passkey prompt. +# CLUBMANAGER_RP_NAME=ClubManager + +# Sessions are shared across club subdomains (log in once, all clubs). Derived +# from CLUBMANAGER_BASE_DOMAIN in production; left host-only on localhost +# because browsers reject a Domain attribute there. Override if needed. +# DJANGO_SESSION_COOKIE_DOMAIN=.clubmanager.app +# DJANGO_CSRF_COOKIE_DOMAIN=.clubmanager.app + # Optional. Defaults to sqlite:///db.sqlite3 for dev; point at Postgres in prod. # DJANGO_DATABASE_URL=postgres://user:pass@localhost:5432/clubmanager diff --git a/authentication/adapters.py b/authentication/adapters.py new file mode 100644 index 0000000..a3c9c53 --- /dev/null +++ b/authentication/adapters.py @@ -0,0 +1,37 @@ +"""allauth adapters. + +The MFA adapter exists for one important reason: WebAuthn credentials are bound +to a **Relying Party ID** (a domain). allauth's default RP ID is the request's +host — which under our subdomain tenancy would be ``ajax-united.clubmanager.app``, +binding a passkey to *one club*. A member of two clubs would then need two +passkeys, and a credential registered at one club would silently fail at another. + +Pinning the RP ID to the registrable parent domain (``clubmanager.app``) makes a +single passkey work across every club subdomain. +""" + +from allauth.mfa.adapter import DefaultMFAAdapter +from django.conf import settings + + +class ClubManagerMFAAdapter(DefaultMFAAdapter): + def get_public_key_credential_rp_entity(self) -> dict[str, str]: + return { + "id": webauthn_rp_id(), + "name": settings.MFA_WEBAUTHN_RP_NAME, + } + + +def webauthn_rp_id() -> str: + """The registrable parent domain that passkeys are bound to. + + Falls back to the request host when no base domain is configured (e.g. a + bare ``localhost`` dev server), which keeps WebAuthn usable there. + """ + base_domain = getattr(settings, "CLUBMANAGER_BASE_DOMAIN", "") + if base_domain: + return base_domain + + from allauth.core import context + + return context.request.get_host().partition(":")[0] diff --git a/authentication/middleware.py b/authentication/middleware.py new file mode 100644 index 0000000..4cfe4af --- /dev/null +++ b/authentication/middleware.py @@ -0,0 +1,47 @@ +"""Force MFA enrolment for privileged users. + +Anyone who can change other people's data must have a second factor: Django +staff/superusers, and anyone holding an elevated ``ClubRole`` (ADMIN or EDITOR) +in *any* club. Regular members may enrol, but aren't forced to. + +Enrolled users are challenged for their second factor by allauth at login; this +middleware only handles the other half — a privileged user who has never +enrolled is redirected to the MFA setup page until they do. +""" + +from allauth.mfa.utils import is_mfa_enabled +from django.conf import settings +from django.shortcuts import redirect +from django.urls import reverse + +from club.models import ClubRole + +#: Paths a not-yet-enrolled user must still reach (to enrol, or to log out). +EXEMPT_PREFIXES = ("/accounts/", "/static/", "/media/") + +ELEVATED_ROLES = (ClubRole.Roles.ADMIN, ClubRole.Roles.EDITOR) + + +def mfa_required_for(user) -> bool: + """Privileged users must hold a second factor.""" + if user.is_staff or user.is_superuser: + return True + return ClubRole.objects.filter(member__user=user, role__in=ELEVATED_ROLES).exists() + + +class RequireMFAMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + if self.needs_enrolment(request): + return redirect(reverse(settings.MFA_ENROLMENT_URL_NAME)) + return self.get_response(request) + + def needs_enrolment(self, request) -> bool: + user = getattr(request, "user", None) + if user is None or not user.is_authenticated: + return False + if request.path.startswith(EXEMPT_PREFIXES): + return False + return mfa_required_for(user) and not is_mfa_enabled(user) diff --git a/authentication/tests.py b/authentication/tests.py index 3b267cb..7b114fc 100644 --- a/authentication/tests.py +++ b/authentication/tests.py @@ -1,14 +1,29 @@ import uuid +from urllib.parse import parse_qs, urlparse +from allauth.core import context +from allauth.mfa.models import Authenticator from django.contrib.auth import get_user_model +from django.contrib.auth.models import AnonymousUser from django.db import IntegrityError -from django.test import TestCase +from django.http import HttpResponse +from django.test import RequestFactory, TestCase, override_settings +from django.urls import reverse +from club.models import Club, ClubRole from members.models import Member +from .adapters import ClubManagerMFAAdapter, webauthn_rp_id +from .middleware import RequireMFAMiddleware, mfa_required_for + User = get_user_model() +def enrol_mfa(user): + """Give ``user`` a second factor (enough for is_mfa_enabled).""" + return Authenticator.objects.create(user=user, type=Authenticator.Type.TOTP, data={"secret": "JBSWY3DPEHPK3PXP"}) + + class UserManagerTests(TestCase): def test_create_user_defaults(self): user = User.objects.create_user(email="alice@example.com", password="secret123") @@ -86,3 +101,134 @@ class UserModelTests(TestCase): self.assertEqual(str(user), "Jane Doe") self.assertEqual(user.get_full_name(), "Jane Doe") self.assertEqual(user.get_short_name(), "Jane") + + +@override_settings( + CLUBMANAGER_BASE_DOMAIN="clubmanager.app", + MFA_WEBAUTHN_RP_NAME="ClubManager", + ALLOWED_HOSTS=[".clubmanager.app", "example.test"], +) +class WebAuthnRelyingPartyTests(TestCase): + """A passkey is bound to a Relying Party ID (a domain). + + allauth's default RP ID is the request host, which under our subdomain + tenancy would bind a passkey to a single club. We pin it to the registrable + parent domain so ONE passkey works across every club. + """ + + def rp_entity(self, host): + request = RequestFactory().get("/", HTTP_HOST=host) + with context.request_context(request): + return ClubManagerMFAAdapter().get_public_key_credential_rp_entity() + + def test_rp_id_is_the_parent_domain_not_the_club_subdomain(self): + self.assertEqual(self.rp_entity("ajax-united.clubmanager.app")["id"], "clubmanager.app") + + def test_rp_id_is_identical_across_clubs(self): + # The whole point: a passkey registered at one club works at the others. + here = self.rp_entity("ajax-united.clubmanager.app") + there = self.rp_entity("rival-fc.clubmanager.app") + + self.assertEqual(here["id"], there["id"]) + + def test_rp_name_comes_from_settings(self): + self.assertEqual(self.rp_entity("ajax-united.clubmanager.app")["name"], "ClubManager") + + @override_settings(CLUBMANAGER_BASE_DOMAIN="") + def test_falls_back_to_the_request_host_without_a_base_domain(self): + request = RequestFactory().get("/", HTTP_HOST="example.test:8000") + + with context.request_context(request): + self.assertEqual(webauthn_rp_id(), "example.test") + + +class MFARequirementTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + + def make_user(self, email, **kwargs): + return User.objects.create_user(email=email, password="pw-secret-123", **kwargs) + + def with_role(self, user, role): + member = Member.objects.create(user=user, first_name="Ada", last_name="Min") + ClubRole.objects.create(club=self.club, member=member, role=role) + return user + + def test_staff_must_have_mfa(self): + self.assertTrue(mfa_required_for(self.make_user("staff@example.com", is_staff=True))) + + def test_superuser_must_have_mfa(self): + self.assertTrue(mfa_required_for(User.objects.create_superuser(email="root@example.com", password="pw-secret-123"))) + + def test_club_admin_must_have_mfa(self): + user = self.with_role(self.make_user("admin@example.com"), ClubRole.Roles.ADMIN) + + self.assertTrue(mfa_required_for(user)) + + def test_editor_must_have_mfa(self): + user = self.with_role(self.make_user("editor@example.com"), ClubRole.Roles.EDITOR) + + self.assertTrue(mfa_required_for(user)) + + def test_plain_member_does_not_need_mfa(self): + user = self.with_role(self.make_user("member@example.com"), ClubRole.Roles.MEMBER) + + self.assertFalse(mfa_required_for(user)) + + def test_user_without_any_role_does_not_need_mfa(self): + self.assertFalse(mfa_required_for(self.make_user("nobody@example.com"))) + + +class RequireMFAMiddlewareTests(TestCase): + def setUp(self): + self.factory = RequestFactory() + self.middleware = RequireMFAMiddleware(lambda request: HttpResponse("ok")) + + def dispatch(self, user, path="/"): + request = self.factory.get(path) + request.user = user + return self.middleware(request) + + def make_staff(self): + return User.objects.create_user(email="staff@example.com", password="pw-secret-123", is_staff=True) + + def test_anonymous_passes_through(self): + self.assertEqual(self.dispatch(AnonymousUser()).content, b"ok") + + def test_unprivileged_user_passes_through(self): + user = User.objects.create_user(email="plain@example.com", password="pw-secret-123") + + self.assertEqual(self.dispatch(user).content, b"ok") + + def test_privileged_user_without_mfa_is_sent_to_enrolment(self): + response = self.dispatch(self.make_staff()) + + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, reverse("mfa_index")) + + def test_privileged_user_can_still_reach_the_enrolment_pages(self): + # Otherwise they'd be redirected in a loop and could never enrol. + response = self.dispatch(self.make_staff(), path="/accounts/2fa/totp/activate/") + + self.assertEqual(response.content, b"ok") + + def test_enrolled_privileged_user_passes_through(self): + staff = self.make_staff() + enrol_mfa(staff) + + self.assertEqual(self.dispatch(staff).content, b"ok") + + +class AdminLoginRoutingTests(TestCase): + def test_admin_login_is_routed_through_allauth(self): + # Django's own admin login knows nothing about second factors. + response = self.client.get("/admin/login/", {"next": "/admin/"}) + + self.assertEqual(response.status_code, 302) + redirect = urlparse(response.url) + self.assertEqual(redirect.path, reverse("account_login")) + # The original destination survives the hop (percent-encoded). + self.assertEqual(parse_qs(redirect.query)["next"], ["/admin/"]) + + def test_allauth_login_page_loads(self): + self.assertEqual(self.client.get(reverse("account_login")).status_code, 200) diff --git a/club/tests.py b/club/tests.py index 7e1890f..403c63b 100644 --- a/club/tests.py +++ b/club/tests.py @@ -2,6 +2,7 @@ import datetime import uuid from contextlib import contextmanager +from allauth.mfa.models import Authenticator from django.contrib import admin as django_admin from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError @@ -461,6 +462,9 @@ class AdminRegistrationSmokeTests(TestCase): def setUp(self): self.admin = get_user_model().objects.create_superuser(email="root@club.test", password="pw-secret-123") + # Staff must hold a second factor (RequireMFAMiddleware), else they are + # redirected to enrolment instead of reaching the admin. + Authenticator.objects.create(user=self.admin, type=Authenticator.Type.TOTP, data={"secret": "JBSWY3DPEHPK3PXP"}) self.client.force_login(self.admin) def test_every_model_is_registered_in_admin(self): diff --git a/clubmanager/settings.py b/clubmanager/settings.py index 951cfa5..e743042 100644 --- a/clubmanager/settings.py +++ b/clubmanager/settings.py @@ -44,6 +44,12 @@ INSTALLED_APPS = [ "django.contrib.messages", "django.contrib.staticfiles", "phonenumber_field", + # Auth: allauth deliberately WITHOUT django.contrib.sites — it is optional in + # allauth 65+, and ARCHITECTURE.md §2.4 rejects the Sites framework (Club is + # the tenant root, not Site). + "allauth", + "allauth.account", + "allauth.mfa", "club.apps.ClubConfig", "authentication.apps.AuthenticationConfig", "members.apps.MembersConfig", @@ -59,11 +65,18 @@ MIDDLEWARE = [ "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", + "allauth.account.middleware.AccountMiddleware", + "authentication.middleware.RequireMFAMiddleware", "club.tenancy.ClubTenantMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] +AUTHENTICATION_BACKENDS = [ + "django.contrib.auth.backends.ModelBackend", + "allauth.account.auth_backends.AuthenticationBackend", +] + # Multi-tenancy: base domain whose subdomains resolve to a club, e.g. # "ajax-united.clubmanager.app" -> the club with slug "ajax-united". Leave # unset to fall back to generic "slug.example.com" (3+ label) resolution. @@ -73,6 +86,50 @@ ROOT_URLCONF = "clubmanager.urls" AUTH_USER_MODEL = "authentication.User" + +# Authentication (django-allauth) + +LOGIN_URL = "account_login" +LOGIN_REDIRECT_URL = "/" + +# The User model logs in by email and has no username field. +ACCOUNT_USER_MODEL_USERNAME_FIELD = None +ACCOUNT_LOGIN_METHODS = {"email"} +ACCOUNT_SIGNUP_FIELDS = ["email*", "password1*", "password2*"] +ACCOUNT_EMAIL_VERIFICATION = "none" + + +# Two-factor authentication (allauth.mfa) + +MFA_SUPPORTED_TYPES = ["totp", "webauthn", "recovery_codes"] + +# Passkeys are a first factor: sign in with Touch ID / a security key alone. +MFA_PASSKEY_LOGIN_ENABLED = True +MFA_PASSKEY_SIGNUP_ENABLED = False + +# WebAuthn needs a secure context. Browsers treat *.localhost as secure, but the +# dev server is plain HTTP, so allow the insecure origin while DEBUG. +MFA_WEBAUTHN_ALLOW_INSECURE_ORIGIN = DEBUG + +# A passkey is bound to a Relying Party ID (a domain). Our adapter pins it to +# CLUBMANAGER_BASE_DOMAIN so that ONE passkey works across every club subdomain +# — allauth's default (the request host) would bind it to a single club. +MFA_ADAPTER = "authentication.adapters.ClubManagerMFAAdapter" +MFA_WEBAUTHN_RP_NAME = config("CLUBMANAGER_RP_NAME", default="ClubManager") + +# Where RequireMFAMiddleware sends privileged users who haven't enrolled yet. +MFA_ENROLMENT_URL_NAME = "mfa_index" + + +# Sessions are shared across club subdomains: log in once and you're authenticated +# on every club (matching the one-passkey-everywhere model). Tenancy still scopes +# what you can *see* — that is the access service's job, not the cookie's. +# Browsers reject a Domain attribute on localhost, so it stays host-only in dev. +SHARED_COOKIE_DOMAIN = f".{CLUBMANAGER_BASE_DOMAIN}" if CLUBMANAGER_BASE_DOMAIN and CLUBMANAGER_BASE_DOMAIN != "localhost" else None + +SESSION_COOKIE_DOMAIN = config("DJANGO_SESSION_COOKIE_DOMAIN", default=SHARED_COOKIE_DOMAIN) +CSRF_COOKIE_DOMAIN = config("DJANGO_CSRF_COOKIE_DOMAIN", default=SHARED_COOKIE_DOMAIN) + TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", diff --git a/clubmanager/urls.py b/clubmanager/urls.py index 7946d6d..59a5a18 100644 --- a/clubmanager/urls.py +++ b/clubmanager/urls.py @@ -1,23 +1,18 @@ -""" -URL configuration for clubmanager project. +"""URL configuration for clubmanager. -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/6.0/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +``/admin/login/`` is deliberately intercepted *before* ``admin.site.urls`` and +redirected to the allauth login, so Django staff go through the same MFA +challenge as everyone else — Django's own admin login form knows nothing about +second factors. ``RequireMFAMiddleware`` then blocks any staff user who has not +enrolled. """ from django.contrib import admin -from django.urls import path +from django.urls import include, path +from django.views.generic import RedirectView urlpatterns = [ + path("admin/login/", RedirectView.as_view(pattern_name="account_login", query_string=True), name="admin_login_redirect"), path("admin/", admin.site.urls), + path("accounts/", include("allauth.urls")), ] diff --git a/members/tests.py b/members/tests.py index b3170bb..fa21870 100644 --- a/members/tests.py +++ b/members/tests.py @@ -3,6 +3,7 @@ from datetime import date, timedelta from io import StringIO from pathlib import Path +from allauth.mfa.models import Authenticator from django.contrib.admin.sites import AdminSite from django.core.management import call_command from django.core.management.base import CommandError @@ -230,6 +231,8 @@ class AdminSmokeTests(TestCase): def setUp(self): self.admin = User.objects.create_superuser(email="root@example.com", password="pw-secret-123") + # Staff must hold a second factor (RequireMFAMiddleware). + Authenticator.objects.create(user=self.admin, type=Authenticator.Type.TOTP, data={"secret": "JBSWY3DPEHPK3PXP"}) self.client.force_login(self.admin) def test_changelists_load(self): diff --git a/pyproject.toml b/pyproject.toml index 7d8cdf5..9910484 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ requires-python = ">=3.14" dependencies = [ "dj-database-url>=3.1.2", "django>=6.0.6", + "django-allauth[mfa]>=65.18.0", "django-phonenumber-field[phonenumbers]>=8.4.0", "pillow>=12.3.0", "python-dateutil>=2.9.0.post0", diff --git a/uv.lock b/uv.lock index cb93482..bb955ee 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,65 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" }, ] +[[package]] +name = "cffi" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d8/f0/81478e482afa03f6d18dc8f2afb5edc45b3080853b634b5ed91961be0998/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2117334c3af3bdcb9a88522b844a2bdb5efdc4f71c6c822df55486ae1c3347a", size = 194142, upload-time = "2026-07-06T21:33:23.657Z" }, + { url = "https://files.pythonhosted.org/packages/7d/95/8de304305cd9204974b0ca051b86d307cafca13aa575a0ef1b44d92c0d8c/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:702c436735fbe99d59ada02a1f65cfc0d31c0ee8b7290912f8fbc5cd1e4b16c3", size = 196819, upload-time = "2026-07-06T21:33:25.007Z" }, + { url = "https://files.pythonhosted.org/packages/20/71/7c8372d30e42415602ed9f268f7cfd66f1b855fed881ecd168bcb45dbc0b/cffi-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1ff3456eab0d889592d1936d6125bbfbc7ae4d3354a700f8bd80450a66445d4d", size = 184965, upload-time = "2026-07-06T21:33:26.605Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5c/584e626835f0375c928176c04137c96927165cb8733cdb3150ec04e5ee5e/cffi-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c4165821e131d6d4ca444347c2b694e2311bcfa3fe5a861cc72968f28867beac", size = 184952, upload-time = "2026-07-06T21:33:27.823Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d2/065fcae1c73979fac8e054462478d0ff8a29c40cdc2ed7ea5676a061df53/cffi-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:276f20fffd7b396e12516ba8edf9509210ac248cbbc5acbc39cd512f9f59ebe6", size = 222353, upload-time = "2026-07-06T21:33:29.178Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a5/e8bbb1ce5b3ac2f53ad6a10bde44318a5a8d99d4f4a000d44a6e39aeb3e4/cffi-2.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7d5980a3433d4b71a5e120f9dd551403d7824e31e2e67124fe2769c404c06913", size = 210051, upload-time = "2026-07-06T21:33:30.534Z" }, + { url = "https://files.pythonhosted.org/packages/28/ed/c127d3ac36e899c965e3361357c3befacd6578c03f40125183e41c3b219e/cffi-2.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6ca4919c6e4f89aa99c42510b42cf54596892c00b3f9077f6bdd1505e24b9c8d", size = 208630, upload-time = "2026-07-06T21:33:31.753Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d7/97d3136f81db489ec8d1d67748c110d6c994268fd7528014aa9f2b085e4e/cffi-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d53d10f7da99ae46f7373b9150393e9c5eab9b224909982b43832668de4779f5", size = 221593, upload-time = "2026-07-06T21:33:33.044Z" }, + { url = "https://files.pythonhosted.org/packages/d3/27/93195977168ee63aed233a1a0993a2178798654d1f4bddcdd321d6fd3b21/cffi-2.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c351efb95e832a853a29361675f33a7ce53de1a109cd73fd47af0712213aa4ce", size = 225146, upload-time = "2026-07-06T21:33:34.224Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c1/6dbd291ee2ae5a50a034aa057207081f545923bbf15dad4511e985aafff5/cffi-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7c7a88e2bac086f06d14577332760bdeecc42bdec8ac4077f6260557d9326", size = 223240, upload-time = "2026-07-06T21:33:35.57Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6f/ade5ce9863a57992a6ea3d0d10d7e29b8749fc127204b3d493d667b2815f/cffi-2.1.0-cp314-cp314-win32.whl", hash = "sha256:1854b724d00f6654c742097d5387569021be12d3a0f770eae1df8f8acfcc6acd", size = 177723, upload-time = "2026-07-06T21:33:51.626Z" }, + { url = "https://files.pythonhosted.org/packages/41/de/92b9eeed4ae4a21d6fd9b2a2c8505cbed573299902ea73981cc13f7ff62c/cffi-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:1b96bfe2c4bd825681b7d311ad6d9b7280a091f43e8f63da5729638083cd3bfb", size = 187937, upload-time = "2026-07-06T21:33:53.403Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1a/cc6ae6c2913a03aab8898eee57963cf1035b8df5872ed8b9115fcc7e2be8/cffi-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:7d28dff1db6764108bc30788d85d61c876beff416d9a49cb9dd7c5a9f34f5804", size = 183001, upload-time = "2026-07-06T21:33:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/14/f0/134c00ce0779ec86dea2aa1aac69339c2741a8045072676763512363a2ea/cffi-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7ea6b3e2c4250ff1de21c630fe72d0f63eb95c2c32ffbf64a358cf4a8836d714", size = 188538, upload-time = "2026-07-06T21:33:36.792Z" }, + { url = "https://files.pythonhosted.org/packages/50/d8/3b86aba791cb610d24e8a3e1b2cd529e71fa15096b04e4d4e360049d4a4c/cffi-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6af371f3767faeffc6ac1ef57cdfd25844403e9d3f476c5537caee499de96376", size = 188230, upload-time = "2026-07-06T21:33:38.011Z" }, + { url = "https://files.pythonhosted.org/packages/14/d0/117dcd9209255ad8571fbc8c92ef32593a1d294dcec91ddc4e4db50606f2/cffi-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb4e8997a49aa2c08a3e43c9045d224448b8941d88e7ac163c7d383e560cbf98", size = 223899, upload-time = "2026-07-06T21:33:39.514Z" }, + { url = "https://files.pythonhosted.org/packages/b6/3d/f20f8b886b254e3ad10e15cd4186d3aed49f3e6a35ab37aab9f8f25f7c03/cffi-2.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf01d8c84cbea96b944c73b22182e6c7c432b3475632b8111dbfdc95ddad6e13", size = 211652, upload-time = "2026-07-06T21:33:40.851Z" }, + { url = "https://files.pythonhosted.org/packages/28/3b/fad54de07260b93ddeef4b96d0131d57ea900675df1d410ae1deee52d7a6/cffi-2.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:33eb1ad83ebe8f313e0df035c406227d55a79456704a863fad9842136af5ad7d", size = 210755, upload-time = "2026-07-06T21:33:42.183Z" }, + { url = "https://files.pythonhosted.org/packages/cc/82/3d5c705acb7abbba9bbd7d79b8e62e0f25b6120eb7ae6ac49f1b721722fe/cffi-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ac0f1a2d0cfa7eea3f2aaf006ab6e70e8feeb16b75d65b7e5939982ca2f11056", size = 223933, upload-time = "2026-07-06T21:33:43.603Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d0/47e338384ab6b1004241002fa616301020cea4fc95f283506565d252f276/cffi-2.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c16914df9fb7f500e440e6875fa23ff5e0b31db01fa9c06af98d59a91f0dc2e4", size = 226749, upload-time = "2026-07-06T21:33:45.046Z" }, + { url = "https://files.pythonhosted.org/packages/70/25/65bd5b58ea4bfdfc15cde02cb5365f89ef8ab8b2adfb8fe5c4bd4233382f/cffi-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5ecbd0499275d57506d397eebe1981cee87b47fcd9ef5c22cab7ed7644a39a94", size = 225703, upload-time = "2026-07-06T21:33:46.374Z" }, + { url = "https://files.pythonhosted.org/packages/dc/78/aa01ac599a8a4322533d45a1f9bc93b338276d2d59dabbe7c6d92a775c81/cffi-2.1.0-cp314-cp314t-win32.whl", hash = "sha256:7d034dcffa09e9a46c93fa3a3be402096cb5354ac6e41ab8e5cc9cd8b642ad76", size = 182857, upload-time = "2026-07-06T21:33:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/b9/26/d00496b22de4d4228f32dde94ad996f350c8aad676d63bcca0743c8dea4d/cffi-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0582a58f3051372229ca8e7f5f589f9e5632678208d8636fea3676711fdf7fe5", size = 194065, upload-time = "2026-07-06T21:33:48.953Z" }, + { url = "https://files.pythonhosted.org/packages/d5/dd/0c7dbf815a579ff005008a2d815a55d6bb047c349eef536d9dc53d3f0a8d/cffi-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:510aeeeac94811b138077451da1fb18b308a5feab47dd2b603af55804155e1c8", size = 186404, upload-time = "2026-07-06T21:33:50.309Z" }, + { url = "https://files.pythonhosted.org/packages/55/c7/8c8c50cb11c6750051daf12164098a9a6f027ac4356967fd4d800a07f242/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:2e9dabb9abcb7ad15938c7196ad5c1718a4e6d33cc79b4c0209bdb64c4a54a5c", size = 194121, upload-time = "2026-07-06T21:33:56.109Z" }, + { url = "https://files.pythonhosted.org/packages/99/e2/67680bf19a6b60d2bb7ff83baefa2a4c3d2d7dc0f3277034b802e1fc504c/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:37f525a7e7e50c017fdebe58b787be310ad59357ae43a053943a6e1a6c526001", size = 196820, upload-time = "2026-07-06T21:33:57.288Z" }, + { url = "https://files.pythonhosted.org/packages/ed/da/4bbe583a3b3a5c8c60892124fe17f3fa3656523faf0d3484eae90f091853/cffi-2.1.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:95f2954c2c9473d892eca6e0409f3568b37ab62a8eedb122461f73cc273476e3", size = 184936, upload-time = "2026-07-06T21:33:58.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/4b/1f4c36ab273980d7aa75bb126ea4f8971f24a96108acad3a0a084028c57b/cffi-2.1.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:cdf2448aab5f661c9315308ec8b93f4e8a1a67a3c733f8631067a2b67d5913dc", size = 185045, upload-time = "2026-07-06T21:34:00.085Z" }, + { url = "https://files.pythonhosted.org/packages/ef/c3/ad299dc38f3583f8d916b299f028af418a9ec98bc695fcbebeae7420691c/cffi-2.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:90bec57cf82089383bd06a605b3eb8daebf7e5a668520beaf6e327a83a947699", size = 222342, upload-time = "2026-07-06T21:34:01.814Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d8/df4543cc087245044ed02ef3ad8e0a26619d0075ac7a77a12dc81177851b/cffi-2.1.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6274dcb2d15cef48daa73ed1be5a40d501d74dccd0cd6db364776d12cb6ba022", size = 210073, upload-time = "2026-07-06T21:34:03.255Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/fac738d73728c6cea2a88a2883dca54892496cbba88a1dc1f2909cb8a6f5/cffi-2.1.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b71d409cccee78310ab5dec549aed052aaea483346e282c7b02362596e01bb0", size = 208551, upload-time = "2026-07-06T21:34:04.433Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3f/0b04a700dd64f465c93020253a793a82c9b4dff9961f48facd0df945d9b8/cffi-2.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d3538f9c0e50670f4deb93dbb696576e60590369cae2faf7de681e597a8a1f1", size = 221649, upload-time = "2026-07-06T21:34:06.157Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7c/b7379a5704c79eda57ce075869ba70a0368d1c850f803b3c0d078d39dcaf/cffi-2.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:8f9ec95b8a043d3dfbc74d9abc6f7baf524dd27a8dc160b0a32ff9cdab650c28", size = 225203, upload-time = "2026-07-06T21:34:07.489Z" }, + { url = "https://files.pythonhosted.org/packages/5a/02/d5e6c43ea85c41bda2a184a3418f195fe7cf602967a8d2b94e085b83deef/cffi-2.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:af5e2915d41fe6c961694d7bfdc8562942638200f3ce2765dfb8b745cf997629", size = 223263, upload-time = "2026-07-06T21:34:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d8/772b8259bf75749adffb1c546828978381fb516f60cf701f6c83daf60c85/cffi-2.1.0-cp315-cp315-win32.whl", hash = "sha256:0a42c688d19fca6e095a53c6a6e2295a5b050a8b289f109adab02a9e61a25de6", size = 177696, upload-time = "2026-07-06T21:34:26.355Z" }, + { url = "https://files.pythonhosted.org/packages/2f/dd/afa2191fc6d57fedd26e5844a2fe2fcc0bbfa00961bbaa5a41e4921e7cca/cffi-2.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:bccbbb5ee76a61f9d99b5bf3846a51d7fca4b6a732fe46f89295610edaf41853", size = 187914, upload-time = "2026-07-06T21:34:27.58Z" }, + { url = "https://files.pythonhosted.org/packages/05/ef/6cd4f8c671517162379dc79cfae5aea9106bc38abb89628d5c16adf6a838/cffi-2.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:8d35c139744adb3e727cd51b1a18324bbe44b8bd41bf8322bca4d41289f48eda", size = 183004, upload-time = "2026-07-06T21:34:28.905Z" }, + { url = "https://files.pythonhosted.org/packages/11/b6/12fc55092817a5faa26fb8c40c7f9d662e11a46ee248c137aafc42517d92/cffi-2.1.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f9912624a0c0b834b7520d7769b3644453aabc0a7e1c839da7359f050750e9bc", size = 188378, upload-time = "2026-07-06T21:34:09.926Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2e/cdac88979f295fde5daa69622c7d2111e56e7ceb94f211357fbe452339e4/cffi-2.1.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:df92f2aba50eb4d96718b68ef76f2e57a57b54f2fa62333496d16c6d585a85ca", size = 188319, upload-time = "2026-07-06T21:34:11.101Z" }, + { url = "https://files.pythonhosted.org/packages/e0/27/1d0b408497e41a74795af122d7b603c418c5fed0171450f899afd04e594f/cffi-2.1.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0520e1f4c35f44e209cbbb421b67eec42e6a157f59444dfb6058874ff3610e5d", size = 223904, upload-time = "2026-07-06T21:34:12.606Z" }, + { url = "https://files.pythonhosted.org/packages/8b/31/e115c985105dd7ffb32444505f18ceb874bb42d992af05d5dced7ecf1980/cffi-2.1.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3681e031db29958a7502f5c0c9d6bbc4c36cb20f7b104086fa642d1799631ff8", size = 211554, upload-time = "2026-07-06T21:34:13.987Z" }, + { url = "https://files.pythonhosted.org/packages/5a/67/9e6e09409336d9e515c58367e7cfcf4f89df06ad25252675595a58eb59d5/cffi-2.1.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:762f99479dcb369f60ab9017ad4ab97a36a1dd7c1ee5a3b15db0f4b8659120cd", size = 210795, upload-time = "2026-07-06T21:34:15.972Z" }, + { url = "https://files.pythonhosted.org/packages/19/e5/d3cc82a4a0be7902af279c04181ad038449c096734464a5ae1de3e1401bd/cffi-2.1.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0611e7ebf90573a535ebdc33ae9da222d037853983e13359f580fab781ca017f", size = 223843, upload-time = "2026-07-06T21:34:17.509Z" }, + { url = "https://files.pythonhosted.org/packages/b9/65/b434abc97ce7cecc2c640fde160507c0ecc7e21544b483ba3325d2e2ea17/cffi-2.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:86cf8755a791f72c85dc287128cc62d4f24d392e3f1e15837245623f4a33cccc", size = 226773, upload-time = "2026-07-06T21:34:19.05Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9f/d4dc66ca651eb1145a133314cda721abf13cfac3d28c4a0402263ae6ad75/cffi-2.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:ba00f661f8ba35d075c937174e27c2c421cec3942fd2e0ea3e66996757c0fdd9", size = 225719, upload-time = "2026-07-06T21:34:20.576Z" }, + { url = "https://files.pythonhosted.org/packages/68/5a/e536c528bc8057496c360c0978559a2dc45653f89dd6151078aa7d8fca1a/cffi-2.1.0-cp315-cp315t-win32.whl", hash = "sha256:cb96698e3c7413d906ce83f8ffd245ec1bd94707541f299d0ce4d6b0193e982b", size = 182760, upload-time = "2026-07-06T21:34:22.059Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0b/0ffe8b82d3875bced5fa1e7986a7a46b748262a40ab7f60b475eb9fb1bb3/cffi-2.1.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f146d154428a2523f9cc7936c02353c2459b8f6cf07d3cd1ee1c0a611109c5d5", size = 193769, upload-time = "2026-07-06T21:34:23.589Z" }, + { url = "https://files.pythonhosted.org/packages/a0/17/1073b53b68c9b5ca6914adf5f8bf55aacc2d3be102418c90700160ea8605/cffi-2.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:cbb7640ce37159548d2147b5b8c241f962143d4c71231431820783f4dc78f210", size = 186405, upload-time = "2026-07-06T21:34:24.857Z" }, +] + [[package]] name = "clubmanager" version = "0.1.0" @@ -18,6 +77,7 @@ source = { virtual = "." } dependencies = [ { name = "dj-database-url" }, { name = "django" }, + { name = "django-allauth", extra = ["mfa"] }, { name = "django-phonenumber-field", extra = ["phonenumbers"] }, { name = "pillow" }, { name = "python-dateutil" }, @@ -34,6 +94,7 @@ dev = [ requires-dist = [ { name = "dj-database-url", specifier = ">=3.1.2" }, { name = "django", specifier = ">=6.0.6" }, + { name = "django-allauth", extras = ["mfa"], specifier = ">=65.18.0" }, { name = "django-phonenumber-field", extras = ["phonenumbers"], specifier = ">=8.4.0" }, { name = "pillow", specifier = ">=12.3.0" }, { name = "python-dateutil", specifier = ">=2.9.0.post0" }, @@ -46,6 +107,15 @@ dev = [ { name = "ruff", specifier = ">=0.15.17" }, ] +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + [[package]] name = "coverage" version = "7.15.0" @@ -85,6 +155,56 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/52/30/21b2ad45959cd50e909e02ebac1e30b4ceb7162e91c11d4c570223a458b7/coverage-7.15.0-py3-none-any.whl", hash = "sha256:56da6a4cbe8f7e9e80bd072ca9cefe67d7106a440a7ec06519ec6507ac94ad19", size = 212632, upload-time = "2026-07-02T13:10:48.641Z" }, ] +[[package]] +name = "cryptography" +version = "49.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", size = 854345, upload-time = "2026-06-12T20:02:30.512Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db", size = 4032100, upload-time = "2026-06-12T20:02:32.143Z" }, + { url = "https://files.pythonhosted.org/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db", size = 4692978, upload-time = "2026-06-12T20:01:21.305Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325", size = 4716422, upload-time = "2026-06-12T20:01:48.566Z" }, + { url = "https://files.pythonhosted.org/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2", size = 4700503, upload-time = "2026-06-12T20:02:47.091Z" }, + { url = "https://files.pythonhosted.org/packages/62/99/a2c95cf8293f07491e9e27c20cc4dcd18176d944e674679adeb1d0173fd6/cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b", size = 5309779, upload-time = "2026-06-12T20:02:08.987Z" }, + { url = "https://files.pythonhosted.org/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6", size = 4749683, upload-time = "2026-06-12T20:02:03.335Z" }, + { url = "https://files.pythonhosted.org/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c", size = 4337874, upload-time = "2026-06-12T20:02:54.323Z" }, + { url = "https://files.pythonhosted.org/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7", size = 4700283, upload-time = "2026-06-12T20:01:34.822Z" }, + { url = "https://files.pythonhosted.org/packages/c4/b6/d7696e4e890d6ae1469935164c9e5215c557671cb78d6e3f458ccceaa632/cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68", size = 5265844, upload-time = "2026-06-12T20:01:24.09Z" }, + { url = "https://files.pythonhosted.org/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9", size = 4749290, upload-time = "2026-06-12T20:01:30.848Z" }, + { url = "https://files.pythonhosted.org/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f", size = 4834612, upload-time = "2026-06-12T20:01:29.246Z" }, + { url = "https://files.pythonhosted.org/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459", size = 4980804, upload-time = "2026-06-12T20:01:42.853Z" }, + { url = "https://files.pythonhosted.org/packages/1f/09/f42b1d190c5ba75f72062a387f8030d1d75f6ab035788f1d9c4b01de6525/cryptography-49.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e", size = 3810026, upload-time = "2026-06-12T20:02:39.262Z" }, + { url = "https://files.pythonhosted.org/packages/ec/9e/db72b3ae7fc9cfad53e630e56c6ae83b9b6ff0bf3718ffb8012d20b3aabf/cryptography-49.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:73a205dce83953d131a4aa1e0fd917a2fd1c5b1eef251e9d7152efefcbf5caf7", size = 4013892, upload-time = "2026-06-12T20:02:10.735Z" }, + { url = "https://files.pythonhosted.org/packages/86/12/c48a424f38db03027be9f7ed5c7dc5de9933dbee992865f98b13727a009d/cryptography-49.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:196ecd6a36e4e9aa10270393bb98d8df88fccee0bf1e5128b91ae4eb4375896d", size = 4678835, upload-time = "2026-06-12T20:02:48.743Z" }, + { url = "https://files.pythonhosted.org/packages/68/28/8a3ad4653662c93fc44dc4e5d8fd374c25c42e07b34bbfbadf49cf57a5a8/cryptography-49.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7abcee80084cda3f7691f3eb1ce480d8df49cec637b429aa35986c1de71738aa", size = 4697239, upload-time = "2026-06-12T20:02:56.03Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b2/2193fc74f81aee4f9b62733133b73b5176718932ed8f2e4b03fa040480a6/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:4ae387c9cb68ea569ca17e490d66d8142b81c3cc814bf179974b7d146e490bbb", size = 4685593, upload-time = "2026-06-12T20:02:50.666Z" }, + { url = "https://files.pythonhosted.org/packages/47/f1/1d3eaa243bfc5de4a187b22aa8c048b3e4980bfbe830ac46e6bac2e66947/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:f37d847238971164fdbc68ade6f6574aecc9c0af714190e2083429ff68f4ce9d", size = 5289961, upload-time = "2026-06-12T20:01:46.468Z" }, + { url = "https://files.pythonhosted.org/packages/58/39/2d51306721330c486495853eda1c567880ff036de15a14c4b74f399934af/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:c2bc30226390d60ea19d9f82b19db005fe0452154a23c1c410c12ea801e43561", size = 4731145, upload-time = "2026-06-12T20:02:16.832Z" }, + { url = "https://files.pythonhosted.org/packages/17/50/983e838c7fd0d87fd8c969bcdd328edaf5f756e38df5281637424c155873/cryptography-49.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:07cab27cc7b7e0fd28e5e26bb9eeedde5c135c868b46de4a27845abe94af6122", size = 4321719, upload-time = "2026-06-12T20:02:52.611Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f5/8f571d7e27c55bce9f76f026143bcb1e040a4233149ecca0bea5fa5dd5f7/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:b20133d204d2bb56ba047642199603876c872026ca53e79c35b83772ab2cc505", size = 4685209, upload-time = "2026-06-12T20:02:07.282Z" }, + { url = "https://files.pythonhosted.org/packages/e7/84/0e27016a6fc5a0886f797018b26aa42f40c09a82332bff77822a451deaaa/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b970c6da94d5bb18629db453d14f2a1300f6bf59b61e9b82377931ef95504866", size = 5246285, upload-time = "2026-06-12T20:01:32.439Z" }, + { url = "https://files.pythonhosted.org/packages/11/2d/5e1fb307cb5931881516b464c98774b3f2c36b5d4bb9a2830253cf553cad/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d8ecde755e2e91bf773fc94e8c9d730cd7f2007004cb492263a794ec3899a1c8", size = 4730441, upload-time = "2026-06-12T20:02:01.469Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c0/bff5a02ee731d207d6a1ed51732549d8c53d2bc8da1d10ec6f2844201d68/cryptography-49.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e3fb64c420688e5319ae25113a354015abbd8dffbfbc41781a1ea66fc7622ac3", size = 4815869, upload-time = "2026-06-12T20:01:36.574Z" }, + { url = "https://files.pythonhosted.org/packages/b9/26/814681d14248d95d73d5c3eea0c39a94eb8302df966f670a2c60de90974b/cryptography-49.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32703d93296f5c1f4b53349ad3a250c2cae0fdecd3a3dd5d47e616d8d616af27", size = 4960948, upload-time = "2026-06-12T20:02:18.688Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/93ecac273d3738939d023612ad12cca9a3740a5345d69fda04134c43fd96/cryptography-49.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:33cd0565932807baddb67b96dbee92f2c374b5c89dee09fd74079aeb8c8dba61", size = 3799153, upload-time = "2026-06-12T20:01:39.059Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a", size = 4025947, upload-time = "2026-06-12T20:01:25.745Z" }, + { url = "https://files.pythonhosted.org/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4", size = 4692429, upload-time = "2026-06-12T20:01:53.628Z" }, + { url = "https://files.pythonhosted.org/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18", size = 4700968, upload-time = "2026-06-12T20:02:45.383Z" }, + { url = "https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69", size = 4697758, upload-time = "2026-06-12T20:01:41.13Z" }, + { url = "https://files.pythonhosted.org/packages/24/01/186c825898477d77e2324d5360fefe622ff1d8d1963ec0554e2cada8ec77/cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64", size = 5298863, upload-time = "2026-06-12T20:02:24.579Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21", size = 4735983, upload-time = "2026-06-12T20:01:50.14Z" }, + { url = "https://files.pythonhosted.org/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9", size = 4334173, upload-time = "2026-06-12T20:01:44.743Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc", size = 4697298, upload-time = "2026-06-12T20:02:20.918Z" }, + { url = "https://files.pythonhosted.org/packages/67/d0/a5fcd3515f0bae49a7b6d0413cc1bdccdcc1fc0047037a0d480642cdc5d6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8", size = 5254338, upload-time = "2026-06-12T20:02:22.737Z" }, + { url = "https://files.pythonhosted.org/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36", size = 4735650, upload-time = "2026-06-12T20:02:41.389Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e", size = 4834820, upload-time = "2026-06-12T20:01:51.847Z" }, + { url = "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", size = 4967968, upload-time = "2026-06-12T20:02:12.524Z" }, + { url = "https://files.pythonhosted.org/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001", size = 3785547, upload-time = "2026-06-12T20:02:26.847Z" }, +] + [[package]] name = "dj-database-url" version = "3.1.2" @@ -111,6 +231,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/50/23f9dc45483419a3cc2085b498b25adfbf10642b2941c73e6d2dfaffc9ab/django-6.0.6-py3-none-any.whl", hash = "sha256:25148b1194c47c2e685e5f5e9c5d59c78b075dfd282cb9618861ba6c1708f4d2", size = 8373354, upload-time = "2026-06-03T13:02:41.72Z" }, ] +[[package]] +name = "django-allauth" +version = "65.18.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/10/2d33c5bc3c0f9d7db1276a1babcaadd147e73eb416ccb1bd9523c6ac925f/django_allauth-65.18.0.tar.gz", hash = "sha256:afb82e2c545b9a5539370ad120468faaed84715e095a7b6e76972dacdc6376fe", size = 2246988, upload-time = "2026-05-29T13:01:26.071Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/75/5a4c2bf050e38ab16549395a7cd751ddcf3a18305bff4852cfd6c0d94998/django_allauth-65.18.0-py3-none-any.whl", hash = "sha256:e508640c83b94eaf1a9b4e9ac5332dc310dcdc83fca40d58a3f670e15bad1e84", size = 2061080, upload-time = "2026-05-29T13:01:16.532Z" }, +] + +[package.optional-dependencies] +mfa = [ + { name = "fido2" }, + { name = "qrcode" }, +] + [[package]] name = "django-phonenumber-field" version = "8.4.0" @@ -128,6 +267,18 @@ phonenumbers = [ { name = "phonenumbers" }, ] +[[package]] +name = "fido2" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/ea/6f08c354b7aeb8019249d46a86c2153f8218499cced4d21bf16b6d49fc16/fido2-2.2.1.tar.gz", hash = "sha256:85787428a94c3f8eaf72f0ff30afba983b559a1b1b795c93318c81b4ad4062c4", size = 327147, upload-time = "2026-06-29T17:41:11.927Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/84/198d99c3312557ef6121cf78c38281efe9b3bc88cba0e2c05446f38a024d/fido2-2.2.1-py3-none-any.whl", hash = "sha256:ed397da981b9ab133da6ead7309e41f924b566b749956129efe286fae097749f", size = 238354, upload-time = "2026-06-29T17:41:09.921Z" }, +] + [[package]] name = "phonenumbers" version = "9.0.33" @@ -187,6 +338,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, ] +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -208,6 +368,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/d4/9193206c4563ec771faf2ccf54815ca7918529fe81f6adb22ee6d0e06622/python_decouple-3.8-py3-none-any.whl", hash = "sha256:d0d45340815b25f4de59c974b855bb38d03151d81b037d9e3f463b0c9f8cbd66", size = 9947, upload-time = "2023-03-01T19:38:36.015Z" }, ] +[[package]] +name = "qrcode" +version = "8.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8f/b2/7fc2931bfae0af02d5f53b174e9cf701adbb35f39d69c2af63d4a39f81a9/qrcode-8.2.tar.gz", hash = "sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c", size = 43317, upload-time = "2025-05-01T15:44:24.726Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dd/b8/d2d6d731733f51684bbf76bf34dab3b70a9148e8f2cef2bb544fccec681a/qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f", size = 45986, upload-time = "2025-05-01T15:44:22.781Z" }, +] + [[package]] name = "ruff" version = "0.15.17" From ebb8bc3db1adfc6573bba895339846333fc57cf1 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 15:29:10 +0200 Subject: [PATCH 034/137] feat(club): archive clubs instead of deleting them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Club.archived_at with active()/archived() managers, archive() and restore(). An archived club stops resolving in ClubTenantMiddleware, so its subdomain behaves as unknown — archiving is a real deactivation, not a cosmetic flag — while every row it owns is retained. There is no hard-delete path, deliberately. A club with any data cannot be deleted anyway (ClubMembership PROTECTs its Season, and the shop chain PROTECTs more), and invoices generally must be kept. Co-Authored-By: Claude Opus 4.8 --- club/migrations/0010_club_archived_at.py | 18 ++++++ club/models.py | 28 +++++++++ club/services/access.py | 8 +-- club/tenancy.py | 3 +- club/tests.py | 74 ++++++++++++++++++++++++ 5 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 club/migrations/0010_club_archived_at.py diff --git a/club/migrations/0010_club_archived_at.py b/club/migrations/0010_club_archived_at.py new file mode 100644 index 0000000..a07d5ec --- /dev/null +++ b/club/migrations/0010_club_archived_at.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.6 on 2026-07-13 13:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0009_clubrole'), + ] + + operations = [ + migrations.AddField( + model_name='club', + name='archived_at', + field=models.DateTimeField(blank=True, help_text='Archived clubs stop resolving on their subdomain, but their data is retained.', null=True, verbose_name='archived at'), + ), + ] diff --git a/club/models.py b/club/models.py index 17c9b30..c61557d 100644 --- a/club/models.py +++ b/club/models.py @@ -15,11 +15,19 @@ class ClubManager(models.Manager): return get_current_club() + def active(self): + return self.filter(archived_at__isnull=True) + + def archived(self): + return self.filter(archived_at__isnull=False) + class Club(UUIDModel): name = models.CharField(_("name"), max_length=255) slug = models.SlugField(_("slug"), max_length=255, unique=True, blank=True, help_text=_("Drives subdomain / path resolution (e.g. ajax-united.clubmanager.app).")) + archived_at = models.DateTimeField(_("archived at"), null=True, blank=True, help_text=_("Archived clubs stop resolving on their subdomain, but their data is retained.")) + objects = ClubManager() class Meta: @@ -35,6 +43,26 @@ class Club(UUIDModel): self.slug = unique_slugify(self, self.name) super().save(*args, **kwargs) + @property + def is_archived(self) -> bool: + return self.archived_at is not None + + def archive(self): + """Soft-delete: the club stops resolving, but nothing is destroyed. + + Clubs are never hard-deleted — a club with any data cannot be removed + anyway (ClubMembership PROTECTs its Season), and financial records must + be retained. + """ + if not self.is_archived: + self.archived_at = timezone.now() + self.save(update_fields=["archived_at"]) + + def restore(self): + if self.is_archived: + self.archived_at = None + self.save(update_fields=["archived_at"]) + class Season(ClubScopedModel): start_date = models.DateField(_("start date")) diff --git a/club/services/access.py b/club/services/access.py index 145ed81..495e247 100644 --- a/club/services/access.py +++ b/club/services/access.py @@ -101,9 +101,7 @@ def members_visible_to(user: User, club: Club) -> QuerySet[Member]: staff of every team they're staffed on. """ if is_club_admin(user, club): - return Member.objects.filter( - Q(member_of__club=club) | Q(team_memberships__team__club=club) | Q(staff_assignments__team__club=club) | Q(roles__club=club) - ).distinct() + return Member.objects.filter(Q(member_of__club=club) | Q(team_memberships__team__club=club) | Q(staff_assignments__team__club=club) | Q(roles__club=club)).distinct() me = Member.objects.filter(user=user).first() if me is None: @@ -117,9 +115,7 @@ def members_visible_to(user: User, club: Club) -> QuerySet[Member]: season = current_season(club) teams = teams_staffed_by(user, club) - roster = Member.objects.filter( - Q(team_memberships__team__in=teams, team_memberships__season=season) | Q(staff_assignments__team__in=teams, staff_assignments__season=season) - ) + roster = Member.objects.filter(Q(team_memberships__team__in=teams, team_memberships__season=season) | Q(staff_assignments__team__in=teams, staff_assignments__season=season)) visible = {me.pk} | set(children.values_list("pk", flat=True)) | set(roster.values_list("pk", flat=True)) return Member.objects.filter(pk__in=visible) diff --git a/club/tenancy.py b/club/tenancy.py index a519f29..3df0a62 100644 --- a/club/tenancy.py +++ b/club/tenancy.py @@ -67,7 +67,8 @@ class ClubTenantMiddleware: if not subdomain: return None - return Club.objects.filter(slug=subdomain).first() + # Archived clubs stop resolving: their subdomain behaves as unknown. + return Club.objects.active().filter(slug=subdomain).first() @staticmethod def get_subdomain(request) -> str | None: diff --git a/club/tests.py b/club/tests.py index 403c63b..e39c0ed 100644 --- a/club/tests.py +++ b/club/tests.py @@ -492,6 +492,80 @@ class AdminRegistrationSmokeTests(TestCase): self.assertEqual(self.client.get(url).status_code, 200) +class ClubArchivingTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + + def test_a_new_club_is_active(self): + self.assertFalse(self.club.is_archived) + self.assertIn(self.club, Club.objects.active()) + self.assertNotIn(self.club, Club.objects.archived()) + + def test_archive_and_restore(self): + self.club.archive() + + self.assertTrue(self.club.is_archived) + self.assertIn(self.club, Club.objects.archived()) + self.assertNotIn(self.club, Club.objects.active()) + + self.club.restore() + + self.assertFalse(self.club.is_archived) + self.assertIn(self.club, Club.objects.active()) + + def test_archiving_twice_keeps_the_original_timestamp(self): + self.club.archive() + first = self.club.archived_at + + self.club.archive() + + self.assertEqual(self.club.archived_at, first) + + def test_restoring_an_active_club_is_a_no_op(self): + self.club.restore() + + self.assertFalse(self.club.is_archived) + + def test_archiving_destroys_nothing(self): + season = make_season(self.club) + member = Member.objects.create(first_name="Jane", last_name="Doe") + ClubMembership.objects.create(club=self.club, member=member, season=season) + + self.club.archive() + + self.assertTrue(ClubMembership.objects.filter(club=self.club).exists()) + self.assertTrue(Season.objects.filter(club=self.club).exists()) + + +@override_settings(CLUBMANAGER_BASE_DOMAIN="clubmanager.app", ALLOWED_HOSTS=[".clubmanager.app"]) +class ArchivedClubTenancyTests(TestCase): + """An archived club's subdomain must stop resolving — that is what makes + archiving a real deactivation rather than a cosmetic flag.""" + + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.middleware = ClubTenantMiddleware(lambda request: "response") + + def resolve(self): + request = RequestFactory().get("/", HTTP_HOST="ajax-united.clubmanager.app") + self.middleware(request) + return request.club + + def test_active_club_resolves(self): + self.assertEqual(self.resolve(), self.club) + + def test_archived_club_stops_resolving(self): + self.club.archive() + + self.assertIsNone(self.resolve()) + + def test_restored_club_resolves_again(self): + self.club.archive() + self.club.restore() + + self.assertEqual(self.resolve(), self.club) + + class ClubRoleTests(TestCase): def test_str(self): club = Club.objects.create(name="Ajax United", slug="ajax-united") From 848a8578de55aba6079c60fd60abf61d922b2ad0 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 15:29:10 +0200 Subject: [PATCH 035/137] build(ui): add Tailwind v4 + daisyUI 5 pipeline daisyUI is an npm plugin, so the standalone Tailwind CLI can't load it; this adds a real build. Themes are configured as `light --default, dark --prefersdark`, so dark follows the OS with no JavaScript, and an explicit data-theme (set by the toggle) overrides it. The built CSS is committed, so `uv run manage.py runserver` remains all a Python dev needs; `npm run watch` is only for those touching styles. Co-Authored-By: Claude Opus 4.8 --- .gitignore | 4 +- package-lock.json | 1181 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 14 + static/src/app.css | 12 + 4 files changed, 1210 insertions(+), 1 deletion(-) create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 static/src/app.css diff --git a/.gitignore b/.gitignore index dc2ed57..ae60515 100644 --- a/.gitignore +++ b/.gitignore @@ -376,4 +376,6 @@ poetry.toml # LSP config files pyrightconfig.json -# End of https://www.toptal.com/developers/gitignore/api/python,pycharm,django% \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/python,pycharm,django% +# Node +node_modules/ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ed1dee7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1181 @@ +{ + "name": "clubmanager", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "clubmanager", + "devDependencies": { + "@tailwindcss/cli": "^4.1.0", + "daisyui": "^5.0.0", + "tailwindcss": "^4.1.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/cli": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.3.2.tgz", + "integrity": "sha512-Fzt+HrIZHDlkRYKdLMBeufaroaPvwCBG70sMLdmurdeadNMO/LxbmT8Sbb+P83ep0iAlAImettb7Y+rO+37rXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/watcher": "2.5.1", + "@tailwindcss/node": "4.3.2", + "@tailwindcss/oxide": "4.3.2", + "enhanced-resolve": "5.21.6", + "mri": "^1.2.0", + "picocolors": "^1.1.1", + "tailwindcss": "4.3.2" + }, + "bin": { + "tailwindcss": "dist/index.mjs" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.2.tgz", + "integrity": "sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "5.21.6", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.2.tgz", + "integrity": "sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.2", + "@tailwindcss/oxide-darwin-arm64": "4.3.2", + "@tailwindcss/oxide-darwin-x64": "4.3.2", + "@tailwindcss/oxide-freebsd-x64": "4.3.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.2", + "@tailwindcss/oxide-linux-x64-musl": "4.3.2", + "@tailwindcss/oxide-wasm32-wasi": "4.3.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.2.tgz", + "integrity": "sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.2.tgz", + "integrity": "sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.2.tgz", + "integrity": "sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.2.tgz", + "integrity": "sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.2.tgz", + "integrity": "sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.2.tgz", + "integrity": "sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.2.tgz", + "integrity": "sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.2.tgz", + "integrity": "sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.2.tgz", + "integrity": "sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.2.tgz", + "integrity": "sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz", + "integrity": "sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.2.tgz", + "integrity": "sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/daisyui": { + "version": "5.6.18", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.6.18.tgz", + "integrity": "sha512-6y9rboRQl8fosnusy/5pg11yp+H0LtK3YdtfS4Vf2QVnaYOKasNdYm6hDxeiT6bwZPdl4WRPzVIlIApdo2ssBg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/saadeghi/daisyui?sponsor=1" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.21.6", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", + "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.2.tgz", + "integrity": "sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..78f1820 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "clubmanager", + "private": true, + "type": "module", + "scripts": { + "build": "tailwindcss -i ./static/src/app.css -o ./static/css/app.css --minify", + "watch": "tailwindcss -i ./static/src/app.css -o ./static/css/app.css --watch" + }, + "devDependencies": { + "@tailwindcss/cli": "^4.1.0", + "daisyui": "^5.0.0", + "tailwindcss": "^4.1.0" + } +} diff --git a/static/src/app.css b/static/src/app.css new file mode 100644 index 0000000..bc76a63 --- /dev/null +++ b/static/src/app.css @@ -0,0 +1,12 @@ +@import "tailwindcss"; + +/* Scan Django templates for utility classes (Tailwind's auto-detection doesn't + know about our template dirs). */ +@source "../../templates"; +@source "../../controlpanel"; + +/* daisyUI: light is the default, dark applies automatically when the OS asks + for it. An explicit data-theme on (set by the toggle) overrides both. */ +@plugin "daisyui" { + themes: light --default, dark --prefersdark; +} From 6f66df3ba41041c34228f00fdb0399ea090bd0df Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 15:29:24 +0200 Subject: [PATCH 036/137] feat(ui): platform control panel and styled auth screens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the `controlpanel` app: a platform-wide (not club-scoped) admin panel for creating clubs, archiving/restoring them, managing club admins, and per-club statistics (members, teams & staff, events, shop). Statistics are annotated in one query so the club list cannot fan out into N+1, and are returned as stat *groups* so growing the domain means adding one entry. Two access rules, both enforced by PlatformStaffRequiredMixin: - staff only (is_staff/is_superuser); anonymous are sent to login, signed-in non-staff get a 403. Staff already need a second factor, so the panel is 2FA-protected for free. - base domain only: the panel manages *all* clubs, so it 404s if the tenant middleware resolved a club from the subdomain. Granting admin to an unknown email creates the account (unusable password — they set one via password reset) and the Member behind it, since a ClubRole hangs off a Member. A member who already holds a role is promoted in place, because there is only one role per member per club. UI is Tailwind + daisyUI. allauth ships an element system, so overriding allauth/layouts/base.html plus ~13 element partials restyles *every* auth and 2FA screen at once — login, signup, password reset, the 2FA challenge, TOTP enrolment, passkeys and recovery codes — rather than templating 20+ pages. Co-Authored-By: Claude Opus 4.8 --- clubmanager/settings.py | 1 + clubmanager/urls.py | 1 + controlpanel/__init__.py | 0 controlpanel/admin.py | 1 + controlpanel/apps.py | 5 + controlpanel/forms.py | 37 +++ controlpanel/migrations/__init__.py | 0 controlpanel/mixins.py | 27 ++ controlpanel/models.py | 1 + controlpanel/services/__init__.py | 0 controlpanel/services/admins.py | 47 ++++ controlpanel/services/statistics.py | 92 +++++++ controlpanel/templates/controlpanel/base.html | 24 ++ .../controlpanel/club_admin_form.html | 36 +++ .../templates/controlpanel/club_detail.html | 89 ++++++ .../templates/controlpanel/club_form.html | 33 +++ .../templates/controlpanel/club_list.html | 61 +++++ .../templates/controlpanel/dashboard.html | 64 +++++ controlpanel/templatetags/__init__.py | 0 controlpanel/templatetags/ui.py | 30 +++ controlpanel/tests.py | 255 ++++++++++++++++++ controlpanel/urls.py | 17 ++ controlpanel/views.py | 128 +++++++++ static/css/app.css | 2 + .../allauth/controlpanel/services/__init__.py | 0 .../controlpanel/templatetags/__init__.py | 0 templates/allauth/elements/alert.html | 4 + templates/allauth/elements/badge.html | 1 + templates/allauth/elements/button.html | 13 + templates/allauth/elements/button_group.html | 4 + templates/allauth/elements/details.html | 5 + templates/allauth/elements/fields.html | 24 ++ templates/allauth/elements/form.html | 9 + templates/allauth/elements/h1.html | 1 + templates/allauth/elements/h2.html | 1 + templates/allauth/elements/hr.html | 1 + templates/allauth/elements/p.html | 1 + templates/allauth/elements/panel.html | 8 + templates/allauth/elements/table.html | 6 + templates/allauth/layouts/base.html | 15 ++ templates/base.html | 86 ++++++ 41 files changed, 1130 insertions(+) create mode 100644 controlpanel/__init__.py create mode 100644 controlpanel/admin.py create mode 100644 controlpanel/apps.py create mode 100644 controlpanel/forms.py create mode 100644 controlpanel/migrations/__init__.py create mode 100644 controlpanel/mixins.py create mode 100644 controlpanel/models.py create mode 100644 controlpanel/services/__init__.py create mode 100644 controlpanel/services/admins.py create mode 100644 controlpanel/services/statistics.py create mode 100644 controlpanel/templates/controlpanel/base.html create mode 100644 controlpanel/templates/controlpanel/club_admin_form.html create mode 100644 controlpanel/templates/controlpanel/club_detail.html create mode 100644 controlpanel/templates/controlpanel/club_form.html create mode 100644 controlpanel/templates/controlpanel/club_list.html create mode 100644 controlpanel/templates/controlpanel/dashboard.html create mode 100644 controlpanel/templatetags/__init__.py create mode 100644 controlpanel/templatetags/ui.py create mode 100644 controlpanel/tests.py create mode 100644 controlpanel/urls.py create mode 100644 controlpanel/views.py create mode 100644 static/css/app.css create mode 100644 templates/allauth/controlpanel/services/__init__.py create mode 100644 templates/allauth/controlpanel/templatetags/__init__.py create mode 100644 templates/allauth/elements/alert.html create mode 100644 templates/allauth/elements/badge.html create mode 100644 templates/allauth/elements/button.html create mode 100644 templates/allauth/elements/button_group.html create mode 100644 templates/allauth/elements/details.html create mode 100644 templates/allauth/elements/fields.html create mode 100644 templates/allauth/elements/form.html create mode 100644 templates/allauth/elements/h1.html create mode 100644 templates/allauth/elements/h2.html create mode 100644 templates/allauth/elements/hr.html create mode 100644 templates/allauth/elements/p.html create mode 100644 templates/allauth/elements/panel.html create mode 100644 templates/allauth/elements/table.html create mode 100644 templates/allauth/layouts/base.html create mode 100644 templates/base.html diff --git a/clubmanager/settings.py b/clubmanager/settings.py index e743042..a187986 100644 --- a/clubmanager/settings.py +++ b/clubmanager/settings.py @@ -57,6 +57,7 @@ INSTALLED_APPS = [ "events.apps.EventsConfig", "formbuilder.apps.FormbuilderConfig", "shop.apps.ShopConfig", + "controlpanel.apps.ControlpanelConfig", ] MIDDLEWARE = [ diff --git a/clubmanager/urls.py b/clubmanager/urls.py index 59a5a18..16c6dbe 100644 --- a/clubmanager/urls.py +++ b/clubmanager/urls.py @@ -15,4 +15,5 @@ urlpatterns = [ path("admin/login/", RedirectView.as_view(pattern_name="account_login", query_string=True), name="admin_login_redirect"), path("admin/", admin.site.urls), path("accounts/", include("allauth.urls")), + path("controlpanel/", include("controlpanel.urls")), ] diff --git a/controlpanel/__init__.py b/controlpanel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/controlpanel/admin.py b/controlpanel/admin.py new file mode 100644 index 0000000..846f6b4 --- /dev/null +++ b/controlpanel/admin.py @@ -0,0 +1 @@ +# Register your models here. diff --git a/controlpanel/apps.py b/controlpanel/apps.py new file mode 100644 index 0000000..36fad22 --- /dev/null +++ b/controlpanel/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ControlpanelConfig(AppConfig): + name = "controlpanel" diff --git a/controlpanel/forms.py b/controlpanel/forms.py new file mode 100644 index 0000000..bf63cb7 --- /dev/null +++ b/controlpanel/forms.py @@ -0,0 +1,37 @@ +from django import forms +from django.utils.translation import gettext_lazy as _ + +from club.models import Club + +from .services.admins import find_member_by_email + + +class ClubForm(forms.ModelForm): + class Meta: + model = Club + fields = ["name", "slug"] + help_texts = {"slug": _("Drives the club's subdomain. Left blank, it is derived from the name.")} + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields["slug"].required = False + + +class ClubAdminForm(forms.Form): + """Grant club-admin rights to an email address, creating the person if new.""" + + email = forms.EmailField(label=_("Email address"), help_text=_("If this email has no account yet, one is created and they set a password via the reset link.")) + first_name = forms.CharField(label=_("First name"), required=False) + last_name = forms.CharField(label=_("Last name"), required=False) + + def clean(self): + cleaned = super().clean() + email = cleaned.get("email") + + # Only a brand-new person needs a name; an existing member already has one. + if email and find_member_by_email(email) is None: + for field in ("first_name", "last_name"): + if not cleaned.get(field): + self.add_error(field, _("Required: this email has no account yet.")) + + return cleaned diff --git a/controlpanel/migrations/__init__.py b/controlpanel/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/controlpanel/mixins.py b/controlpanel/mixins.py new file mode 100644 index 0000000..01bdcf0 --- /dev/null +++ b/controlpanel/mixins.py @@ -0,0 +1,27 @@ +from django.contrib.auth.mixins import UserPassesTestMixin +from django.http import Http404 + + +class PlatformStaffRequiredMixin(UserPassesTestMixin): + """Gate for the platform control panel. + + Two rules: + + * **Staff only.** ``is_staff`` or ``is_superuser``. Anonymous visitors are + sent to the login page; signed-in non-staff get a 403 (Django's + AccessMixin already distinguishes those two cases). Staff must also hold a + second factor — ``RequireMFAMiddleware`` enforces that, so the panel is + 2FA-protected for free. + * **Base domain only.** The panel manages *all* clubs, so it must not be + reachable from inside one. If the tenant middleware resolved a club from + the subdomain, the panel does not exist here. + """ + + def dispatch(self, request, *args, **kwargs): + if getattr(request, "club", None) is not None: + raise Http404("The control panel is not available on a club subdomain.") + return super().dispatch(request, *args, **kwargs) + + def test_func(self): + user = self.request.user + return user.is_staff or user.is_superuser diff --git a/controlpanel/models.py b/controlpanel/models.py new file mode 100644 index 0000000..6b20219 --- /dev/null +++ b/controlpanel/models.py @@ -0,0 +1 @@ +# Create your models here. diff --git a/controlpanel/services/__init__.py b/controlpanel/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/controlpanel/services/admins.py b/controlpanel/services/admins.py new file mode 100644 index 0000000..89ba187 --- /dev/null +++ b/controlpanel/services/admins.py @@ -0,0 +1,47 @@ +"""Granting and revoking club-admin rights from the platform panel.""" + +from django.contrib.auth import get_user_model +from django.db import transaction + +from club.models import ClubRole +from members.models import Member + +User = get_user_model() + + +def find_member_by_email(email): + """The Member behind a login email, if that account exists at all.""" + return Member.objects.filter(user__email__iexact=email).first() + + +@transaction.atomic +def grant_club_admin(club, email, first_name="", last_name=""): + """Make the holder of ``email`` an ADMIN of ``club``, creating them if new. + + A ClubRole hangs off a Member, and a Member optionally links to a User — so + an admin who has never existed needs both. The account is created without a + usable password; they set one via the password-reset flow. + """ + email = email.lower() + user, created_user = User.objects.get_or_create(email=email, defaults={"is_active": True}) + if created_user: + user.set_unusable_password() + user.save(update_fields=["password"]) + + member, _ = Member.objects.get_or_create( + user=user, + defaults={"first_name": first_name, "last_name": last_name}, + ) + + # One role per member per club, so promote rather than add a second row. + role, created_role = ClubRole.objects.get_or_create(club=club, member=member, defaults={"role": ClubRole.Roles.ADMIN}) + if not created_role and role.role != ClubRole.Roles.ADMIN: + role.role = ClubRole.Roles.ADMIN + role.save(update_fields=["role"]) + + return role + + +def revoke_club_admin(role): + """Remove admin rights. The membership-status sync never re-adds ADMIN.""" + role.delete() diff --git a/controlpanel/services/statistics.py b/controlpanel/services/statistics.py new file mode 100644 index 0000000..c63961a --- /dev/null +++ b/controlpanel/services/statistics.py @@ -0,0 +1,92 @@ +"""Platform and per-club statistics. + +``club_statistics`` returns a list of stat *groups*, so growing the model later +means adding an entry here and nothing else. ``clubs_with_totals`` annotates in +a single query — the club list must not fan out into N+1. +""" + +from decimal import Decimal + +from django.db.models import Count, Q, Sum +from django.utils import timezone + +from club.models import Club, ClubMembership, ClubRole, Season +from events.models import Event +from members.models import Member +from shop.models import Cart, Order +from teams.models import StaffAssignment, Team, TeamMembership + +ZERO = Decimal("0.00") + +PAID_STATUSES = (Order.OrderStatus.PAID, Order.OrderStatus.DELIVERED) +OWED_STATUSES = (Order.OrderStatus.PENDING, Order.OrderStatus.PARTIALLY_PAID) + + +def clubs_with_totals(queryset=None): + """Clubs annotated with headline counts (one query, no N+1).""" + clubs = Club.objects.all() if queryset is None else queryset + return clubs.annotate( + member_count=Count("clubmemberships__member", distinct=True), + team_count=Count("teams", distinct=True), + event_count=Count("events", distinct=True), + admin_count=Count("clubroles", filter=Q(clubroles__role=ClubRole.Roles.ADMIN), distinct=True), + ) + + +def platform_totals(): + return { + "clubs": Club.objects.active().count(), + "archived_clubs": Club.objects.archived().count(), + "members": Member.objects.count(), + "admins": ClubRole.objects.filter(role=ClubRole.Roles.ADMIN).count(), + } + + +def _money(queryset): + return queryset.aggregate(total=Sum("total"))["total"] or ZERO + + +def club_statistics(club): + """Stat groups for one club. Add new groups here as the domain grows.""" + season = Season.covering(club, timezone.localdate()) + now = timezone.now() + + memberships = ClubMembership.objects.filter(club=club) + events = Event.objects.filter(club=club) + orders = Order.objects.filter(club=club) + + return [ + { + "title": "Members", + "stats": [ + ("Members", memberships.values("member").distinct().count()), + ("Active this season", memberships.filter(season=season, status=ClubMembership.StatusChoices.ACTIVE).count() if season else 0), + ("Pending", memberships.filter(status=ClubMembership.StatusChoices.PENDING).count()), + ("Lapsed", memberships.filter(status=ClubMembership.StatusChoices.LAPSED).count()), + ], + }, + { + "title": "Teams & staff", + "stats": [ + ("Teams", Team.objects.filter(club=club).count()), + ("Players this season", TeamMembership.objects.filter(team__club=club, season=season).count() if season else 0), + ("Staff this season", StaffAssignment.objects.filter(team__club=club, season=season).count() if season else 0), + ], + }, + { + "title": "Events", + "stats": [ + ("Upcoming", events.filter(start__gte=now).count()), + ("This season", events.filter(season=season).count() if season else 0), + ], + }, + { + "title": "Shop", + "stats": [ + ("Orders", orders.count()), + ("Revenue", _money(orders.filter(status__in=PAID_STATUSES))), + ("Outstanding", _money(orders.filter(status__in=OWED_STATUSES))), + ("Open carts", Cart.objects.filter(club=club, status=Cart.CartStatus.OPEN).count()), + ], + }, + ] diff --git a/controlpanel/templates/controlpanel/base.html b/controlpanel/templates/controlpanel/base.html new file mode 100644 index 0000000..d11241c --- /dev/null +++ b/controlpanel/templates/controlpanel/base.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} + +{% block title %} + {% block panel_title %}Control panel{% endblock panel_title %} · ClubManager +{% endblock title %} + +{% block main %} +

+ + {% block panel %}{% endblock panel %} +{% endblock main %} diff --git a/controlpanel/templates/controlpanel/club_admin_form.html b/controlpanel/templates/controlpanel/club_admin_form.html new file mode 100644 index 0000000..71413fc --- /dev/null +++ b/controlpanel/templates/controlpanel/club_admin_form.html @@ -0,0 +1,36 @@ +{% extends "controlpanel/base.html" %} +{% load ui %} + +{% block heading %}Add an admin to {{ club }}{% endblock heading %} + +{% block panel %} +
+
+
+ A club admin can manage everything in this club. They will be required to set up two-factor authentication before they can sign in. +
+
+ {% csrf_token %} + {% for error in form.non_field_errors %} +
+ {{ error }} +
+ {% endfor %} + {% for field in form %} +
+ + {{ field|daisy }} + {% if field.help_text %}{{ field.help_text }}{% endif %} + {% for error in field.errors %}{{ error }}{% endfor %} +
+ {% endfor %} +
+ Cancel + +
+
+
+
+{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/club_detail.html b/controlpanel/templates/controlpanel/club_detail.html new file mode 100644 index 0000000..33582e5 --- /dev/null +++ b/controlpanel/templates/controlpanel/club_detail.html @@ -0,0 +1,89 @@ +{% extends "controlpanel/base.html" %} + +{% block heading %}{{ club.name }}{% endblock heading %} + +{% block subheading %} +

+ {{ club.slug }} + {% if club.is_archived %} + Archived + {% endif %} +

+{% endblock subheading %} + +{% block actions %} + Edit + {% if club.is_archived %} +
+ {% csrf_token %} + +
+ {% else %} +
+ {% csrf_token %} + +
+ {% endif %} +{% endblock actions %} + +{% block panel %} + {% if club.is_archived %} +
+ This club is archived: its subdomain no longer resolves. Nothing has been deleted — restore it to bring it back. +
+ {% endif %} +
+ {% for group in groups %} +
+
+

{{ group.title }}

+
+ {% for label, value in group.stats %} +
+
{{ label }}
+
{{ value }}
+
+ {% endfor %} +
+
+
+ {% endfor %} +
+
+
+
+

Club admins

+ Add admin +
+
+ + + + + + + + + + {% for role in admins %} + + + + + + {% empty %} + + + + {% endfor %} + +
NameEmail
{{ role.member }}{{ role.member.user.email|default:"—" }} +
+ {% csrf_token %} + +
+
No admins yet.
+
+
+
+{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/club_form.html b/controlpanel/templates/controlpanel/club_form.html new file mode 100644 index 0000000..b2e976b --- /dev/null +++ b/controlpanel/templates/controlpanel/club_form.html @@ -0,0 +1,33 @@ +{% extends "controlpanel/base.html" %} +{% load ui %} + +{% block heading %}{% if object %}Edit {{ object }}{% else %}New club{% endif %}{% endblock heading %} + +{% block panel %} +
+
+
+ {% csrf_token %} + {% for error in form.non_field_errors %} +
+ {{ error }} +
+ {% endfor %} + {% for field in form %} +
+ + {{ field|daisy }} + {% if field.help_text %}{{ field.help_text }}{% endif %} + {% for error in field.errors %}{{ error }}{% endfor %} +
+ {% endfor %} +
+ Cancel + +
+
+
+
+{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/club_list.html b/controlpanel/templates/controlpanel/club_list.html new file mode 100644 index 0000000..2e94e32 --- /dev/null +++ b/controlpanel/templates/controlpanel/club_list.html @@ -0,0 +1,61 @@ +{% extends "controlpanel/base.html" %} + +{% block heading %}{% if show_archived %}Archived clubs{% else %}Clubs{% endif %}{% endblock heading %} + +{% block actions %} + {% if show_archived %} + Active clubs + {% else %} + Archived + {% endif %} + New club +{% endblock actions %} + +{% block panel %} +
+ {% if show_archived %}{% endif %} + + +
+
+
+
+ + + + + + + + + + + + {% for club in clubs %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
ClubMembersTeamsEventsAdmins
+ {{ club.name }} +
{{ club.slug }}
+
{{ club.member_count }}{{ club.team_count }}{{ club.event_count }}{{ club.admin_count }}
+ {% if show_archived %}No archived clubs.{% else %}No clubs yet.{% endif %} +
+
+
+
+{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/dashboard.html b/controlpanel/templates/controlpanel/dashboard.html new file mode 100644 index 0000000..a9f8bbd --- /dev/null +++ b/controlpanel/templates/controlpanel/dashboard.html @@ -0,0 +1,64 @@ +{% extends "controlpanel/base.html" %} + +{% block heading %}Platform overview{% endblock heading %} + +{% block actions %} + New club +{% endblock actions %} + +{% block panel %} +
+
+
Active clubs
+
{{ totals.clubs }}
+
+
+
Archived
+
{{ totals.archived_clubs }}
+
+
+
Members
+
{{ totals.members }}
+
+
+
Club admins
+
{{ totals.admins }}
+
+
+
+
+

Clubs

+
+ + + + + + + + + + + + {% for club in clubs %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
ClubMembersTeamsEventsAdmins
+ {{ club.name }} +
{{ club.slug }}
+
{{ club.member_count }}{{ club.team_count }}{{ club.event_count }}{{ club.admin_count }}
No clubs yet.
+
+
+
+{% endblock panel %} diff --git a/controlpanel/templatetags/__init__.py b/controlpanel/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/controlpanel/templatetags/ui.py b/controlpanel/templatetags/ui.py new file mode 100644 index 0000000..ca04f3d --- /dev/null +++ b/controlpanel/templatetags/ui.py @@ -0,0 +1,30 @@ +"""Template helpers for the daisyUI-based UI.""" + +from django import forms, template + +register = template.Library() + +#: daisyUI class per widget kind. Django renders widgets unstyled, so this is +#: what makes every allauth and control-panel form field look right. +WIDGET_CLASSES = ( + (forms.CheckboxInput, "checkbox"), + (forms.RadioSelect, "radio"), + (forms.Select, "select select-bordered w-full"), + (forms.Textarea, "textarea textarea-bordered w-full"), +) +DEFAULT_WIDGET_CLASS = "input input-bordered w-full" + + +@register.filter +def daisy(field): + """Render a bound form field with the right daisyUI classes.""" + widget = field.field.widget + css = next((css for widget_type, css in WIDGET_CLASSES if isinstance(widget, widget_type)), DEFAULT_WIDGET_CLASS) + + classes = [widget.attrs.get("class", ""), css] + if field.errors: + classes.append(f"{css.split()[0]}-error") + + attrs = dict(widget.attrs) + attrs["class"] = " ".join(part for part in classes if part) + return field.as_widget(attrs=attrs) diff --git a/controlpanel/tests.py b/controlpanel/tests.py new file mode 100644 index 0000000..f50ea00 --- /dev/null +++ b/controlpanel/tests.py @@ -0,0 +1,255 @@ +from decimal import Decimal + +from allauth.mfa.models import Authenticator +from django import forms +from django.contrib.auth import get_user_model +from django.test import TestCase, override_settings +from django.urls import reverse +from django.utils import timezone + +from club.models import Club, ClubMembership, ClubRole, Season +from members.models import Member +from shop.models import Order +from teams.models import Position, Team, TeamMembership + +from .services.admins import grant_club_admin +from .services.statistics import club_statistics, clubs_with_totals, platform_totals +from .templatetags.ui import daisy + +User = get_user_model() + + +def enrol_mfa(user): + return Authenticator.objects.create(user=user, type=Authenticator.Type.TOTP, data={"secret": "JBSWY3DPEHPK3PXP"}) + + +class ControlPanelTestBase(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United") + self.staff = User.objects.create_user(email="root@example.com", password="pw-secret-123", is_staff=True) + # Staff must hold a second factor, else RequireMFAMiddleware redirects. + enrol_mfa(self.staff) + self.client.force_login(self.staff) + + +class AccessTests(ControlPanelTestBase): + def test_staff_can_reach_the_panel(self): + self.assertEqual(self.client.get(reverse("controlpanel:dashboard")).status_code, 200) + + def test_anonymous_is_sent_to_login(self): + self.client.logout() + + response = self.client.get(reverse("controlpanel:dashboard")) + + self.assertEqual(response.status_code, 302) + self.assertIn(reverse("account_login"), response.url) + + def test_signed_in_non_staff_gets_403(self): + self.client.force_login(User.objects.create_user(email="member@example.com", password="pw-secret-123")) + + self.assertEqual(self.client.get(reverse("controlpanel:dashboard")).status_code, 403) + + def test_superuser_can_reach_the_panel(self): + root = User.objects.create_superuser(email="super@example.com", password="pw-secret-123") + enrol_mfa(root) + self.client.force_login(root) + + self.assertEqual(self.client.get(reverse("controlpanel:dashboard")).status_code, 200) + + @override_settings(CLUBMANAGER_BASE_DOMAIN="clubmanager.app", ALLOWED_HOSTS=[".clubmanager.app"]) + def test_panel_does_not_exist_on_a_club_subdomain(self): + # It manages *all* clubs, so it must not be reachable from inside one. + Club.objects.create(name="Rival FC", slug="rival-fc") + + response = self.client.get(reverse("controlpanel:dashboard"), headers={"host": "rival-fc.clubmanager.app"}) + + self.assertEqual(response.status_code, 404) + + def test_staff_without_a_second_factor_is_sent_to_enrolment(self): + self.client.force_login(User.objects.create_user(email="nomfa@example.com", password="pw-secret-123", is_staff=True)) + + response = self.client.get(reverse("controlpanel:dashboard")) + + self.assertEqual(response.status_code, 302) + self.assertEqual(response.url, reverse("mfa_index")) + + +class ClubManagementTests(ControlPanelTestBase): + def test_dashboard_lists_clubs(self): + self.assertContains(self.client.get(reverse("controlpanel:dashboard")), "Ajax United") + + def test_create_club_derives_the_slug(self): + response = self.client.post(reverse("controlpanel:club_create"), {"name": "New Club", "slug": ""}) + + club = Club.objects.get(name="New Club") + self.assertEqual(club.slug, "new-club") + self.assertRedirects(response, reverse("controlpanel:club_detail", args=[club.pk])) + + def test_update_club(self): + self.client.post(reverse("controlpanel:club_update", args=[self.club.pk]), {"name": "Renamed", "slug": self.club.slug}) + + self.club.refresh_from_db() + self.assertEqual(self.club.name, "Renamed") + + def test_club_detail_shows_statistics(self): + response = self.client.get(reverse("controlpanel:club_detail", args=[self.club.pk])) + + self.assertContains(response, "Members") + self.assertContains(response, "Teams & staff") + self.assertContains(response, "Shop") + + def test_archive_then_restore(self): + self.client.post(reverse("controlpanel:club_archive", args=[self.club.pk])) + self.club.refresh_from_db() + self.assertTrue(self.club.is_archived) + + self.client.post(reverse("controlpanel:club_restore", args=[self.club.pk])) + self.club.refresh_from_db() + self.assertFalse(self.club.is_archived) + + def test_list_separates_active_from_archived(self): + Club.objects.create(name="Gone FC").archive() + + active = self.client.get(reverse("controlpanel:club_list")) + self.assertContains(active, "Ajax United") + self.assertNotContains(active, "Gone FC") + + archived = self.client.get(reverse("controlpanel:club_list"), {"archived": "1"}) + self.assertContains(archived, "Gone FC") + self.assertNotContains(archived, "Ajax United") + + def test_list_search(self): + Club.objects.create(name="Rival FC") + + response = self.client.get(reverse("controlpanel:club_list"), {"q": "Ajax"}) + + self.assertContains(response, "Ajax United") + self.assertNotContains(response, "Rival FC") + + +class ClubAdminManagementTests(ControlPanelTestBase): + def add_admin(self, **data): + return self.client.post(reverse("controlpanel:club_admin_add", args=[self.club.pk]), data) + + def test_granting_admin_to_a_new_email_creates_the_account(self): + self.add_admin(email="New.Admin@Example.com", first_name="Ada", last_name="Min") + + user = User.objects.get(email="new.admin@example.com") + self.assertFalse(user.has_usable_password()) # they set one via password reset + self.assertEqual(ClubRole.objects.get(club=self.club, member__user=user).role, ClubRole.Roles.ADMIN) + + def test_a_new_email_must_come_with_a_name(self): + response = self.add_admin(email="nameless@example.com", first_name="", last_name="") + + self.assertEqual(response.status_code, 200) + self.assertFalse(ClubRole.objects.exists()) + self.assertFormError(response.context["form"], "first_name", "Required: this email has no account yet.") + + def test_an_existing_member_is_promoted_rather_than_duplicated(self): + user = User.objects.create_user(email="existing@example.com", password="pw-secret-123") + member = Member.objects.create(user=user, first_name="Ex", last_name="Isting") + ClubRole.objects.create(club=self.club, member=member, role=ClubRole.Roles.MEMBER) + + self.add_admin(email="existing@example.com") + + # One role per member per club, so the MEMBER role is upgraded in place. + self.assertEqual(ClubRole.objects.get(club=self.club, member=member).role, ClubRole.Roles.ADMIN) + self.assertEqual(Member.objects.filter(user=user).count(), 1) + + def test_granting_twice_is_idempotent(self): + self.add_admin(email="ada@example.com", first_name="Ada", last_name="Min") + self.add_admin(email="ada@example.com", first_name="Ada", last_name="Min") + + self.assertEqual(ClubRole.objects.filter(club=self.club).count(), 1) + + def test_remove_admin(self): + role = grant_club_admin(self.club, "ada@example.com", "Ada", "Min") + + self.client.post(reverse("controlpanel:club_admin_remove", args=[self.club.pk, role.pk])) + + self.assertFalse(ClubRole.objects.filter(pk=role.pk).exists()) + + def test_admins_are_listed_on_the_club(self): + grant_club_admin(self.club, "ada@example.com", "Ada", "Min") + + response = self.client.get(reverse("controlpanel:club_detail", args=[self.club.pk])) + + self.assertContains(response, "Ada Min") + self.assertContains(response, "ada@example.com") + + +class StatisticsTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United") + today = timezone.localdate() + self.season = Season.objects.create(club=self.club, start_date=today, end_date=today) + self.member = Member.objects.create(first_name="Jane", last_name="Doe") + + def groups_for(self, club): + return {group["title"]: dict(group["stats"]) for group in club_statistics(club)} + + def test_platform_totals_split_active_and_archived(self): + Club.objects.create(name="Gone FC").archive() + + totals = platform_totals() + + self.assertEqual(totals["clubs"], 1) + self.assertEqual(totals["archived_clubs"], 1) + + def test_club_totals_are_annotated(self): + Team.objects.create(club=self.club, name="First", short_name="1st") + ClubMembership.objects.create(club=self.club, member=self.member, season=self.season) + + club = clubs_with_totals().get(pk=self.club.pk) + + self.assertEqual(club.member_count, 1) + self.assertEqual(club.team_count, 1) + self.assertEqual(club.admin_count, 0) + + def test_club_statistics_count_members_teams_and_money(self): + ClubMembership.objects.create(club=self.club, member=self.member, season=self.season, status=ClubMembership.StatusChoices.ACTIVE) + team = Team.objects.create(club=self.club, name="First", short_name="1st") + position = Position.objects.create(club=self.club, name="Forward", short_name="FW") + TeamMembership.objects.create(team=team, member=self.member, season=self.season, position=position) + Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("50.00"), status=Order.OrderStatus.PAID) + Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("20.00"), status=Order.OrderStatus.PENDING) + + groups = self.groups_for(self.club) + + self.assertEqual(groups["Members"]["Active this season"], 1) + self.assertEqual(groups["Teams & staff"]["Players this season"], 1) + self.assertEqual(groups["Shop"]["Revenue"], Decimal("50.00")) + self.assertEqual(groups["Shop"]["Outstanding"], Decimal("20.00")) + + def test_statistics_cope_with_no_current_season(self): + # A brand-new club has no season covering today; it must not blow up. + groups = self.groups_for(Club.objects.create(name="Seasonless FC")) + + self.assertEqual(groups["Members"]["Active this season"], 0) + self.assertEqual(groups["Teams & staff"]["Players this season"], 0) + self.assertEqual(groups["Events"]["This season"], 0) + self.assertEqual(groups["Shop"]["Revenue"], Decimal("0.00")) + + +class SampleForm(forms.Form): + text = forms.CharField() + choice = forms.ChoiceField(choices=[("a", "A")]) + note = forms.CharField(widget=forms.Textarea) + agree = forms.BooleanField() + + +class DaisyFilterTests(TestCase): + def rendered(self, field_name, data=None): + form = SampleForm(data) + if data is not None: + form.is_valid() + return str(daisy(form[field_name])) + + def test_widgets_get_the_right_daisyui_class(self): + self.assertIn("input input-bordered", self.rendered("text")) + self.assertIn("select select-bordered", self.rendered("choice")) + self.assertIn("textarea textarea-bordered", self.rendered("note")) + self.assertIn("checkbox", self.rendered("agree")) + + def test_invalid_fields_get_an_error_class(self): + self.assertIn("input-error", self.rendered("text", data={})) diff --git a/controlpanel/urls.py b/controlpanel/urls.py new file mode 100644 index 0000000..5662b50 --- /dev/null +++ b/controlpanel/urls.py @@ -0,0 +1,17 @@ +from django.urls import path + +from . import views + +app_name = "controlpanel" + +urlpatterns = [ + path("", views.DashboardView.as_view(), name="dashboard"), + path("clubs/", views.ClubListView.as_view(), name="club_list"), + path("clubs/new/", views.ClubCreateView.as_view(), name="club_create"), + path("clubs//", views.ClubDetailView.as_view(), name="club_detail"), + path("clubs//edit/", views.ClubUpdateView.as_view(), name="club_update"), + path("clubs//archive/", views.ClubArchiveView.as_view(), name="club_archive"), + path("clubs//restore/", views.ClubRestoreView.as_view(), name="club_restore"), + path("clubs//admins/add/", views.ClubAdminAddView.as_view(), name="club_admin_add"), + path("clubs//admins//remove/", views.ClubAdminRemoveView.as_view(), name="club_admin_remove"), +] diff --git a/controlpanel/views.py b/controlpanel/views.py new file mode 100644 index 0000000..54f44db --- /dev/null +++ b/controlpanel/views.py @@ -0,0 +1,128 @@ +from django.contrib import messages +from django.shortcuts import get_object_or_404, redirect +from django.urls import reverse +from django.views.generic import CreateView, DetailView, FormView, ListView, TemplateView, UpdateView, View + +from club.models import Club, ClubRole + +from .forms import ClubAdminForm, ClubForm +from .mixins import PlatformStaffRequiredMixin +from .services.admins import grant_club_admin, revoke_club_admin +from .services.statistics import club_statistics, clubs_with_totals, platform_totals + + +class DashboardView(PlatformStaffRequiredMixin, TemplateView): + template_name = "controlpanel/dashboard.html" + + def get_context_data(self, **kwargs): + return super().get_context_data( + nav="dashboard", + totals=platform_totals(), + clubs=clubs_with_totals(Club.objects.active()), + **kwargs, + ) + + +class ClubListView(PlatformStaffRequiredMixin, ListView): + template_name = "controlpanel/club_list.html" + context_object_name = "clubs" + + @property + def show_archived(self): + return self.request.GET.get("archived") == "1" + + def get_queryset(self): + clubs = Club.objects.archived() if self.show_archived else Club.objects.active() + search = self.request.GET.get("q", "").strip() + if search: + clubs = clubs.filter(name__icontains=search) + return clubs_with_totals(clubs) + + def get_context_data(self, **kwargs): + return super().get_context_data(nav="clubs", show_archived=self.show_archived, search=self.request.GET.get("q", ""), **kwargs) + + +class ClubCreateView(PlatformStaffRequiredMixin, CreateView): + model = Club + form_class = ClubForm + template_name = "controlpanel/club_form.html" + + def form_valid(self, form): + response = super().form_valid(form) + messages.success(self.request, f"Club “{self.object}” created.") + return response + + def get_success_url(self): + return reverse("controlpanel:club_detail", args=[self.object.pk]) + + +class ClubUpdateView(PlatformStaffRequiredMixin, UpdateView): + model = Club + form_class = ClubForm + template_name = "controlpanel/club_form.html" + + def form_valid(self, form): + response = super().form_valid(form) + messages.success(self.request, f"Club “{self.object}” updated.") + return response + + def get_success_url(self): + return reverse("controlpanel:club_detail", args=[self.object.pk]) + + +class ClubDetailView(PlatformStaffRequiredMixin, DetailView): + model = Club + template_name = "controlpanel/club_detail.html" + context_object_name = "club" + + def get_context_data(self, **kwargs): + return super().get_context_data( + nav="clubs", + groups=club_statistics(self.object), + admins=ClubRole.objects.filter(club=self.object, role=ClubRole.Roles.ADMIN).select_related("member", "member__user"), + **kwargs, + ) + + +class ClubArchiveView(PlatformStaffRequiredMixin, View): + """Clubs are archived, never destroyed — their data (and invoices) are kept.""" + + def post(self, request, pk): + club = get_object_or_404(Club, pk=pk) + club.archive() + messages.warning(request, f"Club “{club}” archived. Its subdomain no longer resolves.") + return redirect("controlpanel:club_detail", pk=club.pk) + + +class ClubRestoreView(PlatformStaffRequiredMixin, View): + def post(self, request, pk): + club = get_object_or_404(Club, pk=pk) + club.restore() + messages.success(request, f"Club “{club}” restored.") + return redirect("controlpanel:club_detail", pk=club.pk) + + +class ClubAdminAddView(PlatformStaffRequiredMixin, FormView): + form_class = ClubAdminForm + template_name = "controlpanel/club_admin_form.html" + + @property + def club(self): + return get_object_or_404(Club, pk=self.kwargs["pk"]) + + def get_context_data(self, **kwargs): + return super().get_context_data(nav="clubs", club=self.club, **kwargs) + + def form_valid(self, form): + role = grant_club_admin(self.club, **form.cleaned_data) + messages.success(self.request, f"{role.member} is now an admin of {role.club}. They must set up two-factor authentication before they can sign in.") + return redirect("controlpanel:club_detail", pk=self.kwargs["pk"]) + + +class ClubAdminRemoveView(PlatformStaffRequiredMixin, View): + def post(self, request, pk, role_pk): + role = get_object_or_404(ClubRole, pk=role_pk, club_id=pk, role=ClubRole.Roles.ADMIN) + member = role.member + revoke_club_admin(role) + messages.warning(request, f"{member} is no longer an admin of this club.") + return redirect("controlpanel:club_detail", pk=pk) diff --git a/static/css/app.css b/static/css/app.css new file mode 100644 index 0000000..2e13205 --- /dev/null +++ b/static/css/app.css @@ -0,0 +1,2 @@ +/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */ +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax:"";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tab:is(.tabs>.tab){cursor:pointer;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:is(.tabs>.tab):hover{color:var(--color-base-content)}}.tab:is(.tabs>.tab){--tab-p:.75rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));--tab-radius-limit:min(var(--radius-field), var(--tab-radius-min));--tab-radius-grad:#0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + .25px), var(--tab-border-color) 69%, var(--tab-bg) calc(69% + .25px);order:var(--tab-order);height:var(--tab-height);padding-inline:var(--tab-p);border-color:#0000;font-size:.875rem}.tab:is(.tabs>.tab):is(input[type=radio]){min-width:fit-content}.tab:is(.tabs>.tab):is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:is(.tabs>.tab):is(label){position:relative}.tab:is(.tabs>.tab):is(label) input{cursor:pointer;appearance:none;opacity:0;position:absolute;inset:0}:is(.tab:is(.tabs>.tab):checked,.tab:is(.tabs>.tab):is(label:has(:checked)),.tab:is(.tabs>.tab):is(.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]))+.tab-content{display:block}.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.tab:is(.tabs>.tab):not(input):empty{cursor:default;flex-grow:1}.tab:is(.tabs>.tab):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:is(.tabs>.tab):focus{outline-offset:2px;outline:2px solid #0000}}.tab:is(.tabs>.tab):focus-visible,.tab:is(.tabs>.tab):is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab:is(.tabs>.tab)[disabled]{pointer-events:none;opacity:.4}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem) * 10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}}@layer daisyui.l1.l2{.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.btn-circle{width:var(--size);height:var(--size);border-radius:3.40282e38px;padding-inline:0}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.visible{visibility:visible}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.static{position:static}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.table{display:table}.h-5{height:calc(var(--spacing) * 5)}.min-h-screen{min-height:100vh}.w-5{width:calc(var(--spacing) * 5)}.w-56{width:calc(var(--spacing) * 56)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-5xl{max-width:var(--container-5xl)}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.flex-none{flex:none}.cursor-pointer{cursor:pointer}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.overflow-x-auto{overflow-x:auto}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.border{border-style:var(--tw-border-style);border-width:1px}.border-base-300{border-color:var(--color-base-300)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-right{text-align:right}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file diff --git a/templates/allauth/controlpanel/services/__init__.py b/templates/allauth/controlpanel/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/templates/allauth/controlpanel/templatetags/__init__.py b/templates/allauth/controlpanel/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/templates/allauth/elements/alert.html b/templates/allauth/elements/alert.html new file mode 100644 index 0000000..e57a824 --- /dev/null +++ b/templates/allauth/elements/alert.html @@ -0,0 +1,4 @@ +{% load allauth %} +
+ {% slot message %}{% endslot %} +
diff --git a/templates/allauth/elements/badge.html b/templates/allauth/elements/badge.html new file mode 100644 index 0000000..432e80c --- /dev/null +++ b/templates/allauth/elements/badge.html @@ -0,0 +1 @@ +{% load allauth %}{% slot %}{% endslot %} diff --git a/templates/allauth/elements/button.html b/templates/allauth/elements/button.html new file mode 100644 index 0000000..e4a04e9 --- /dev/null +++ b/templates/allauth/elements/button.html @@ -0,0 +1,13 @@ +{% load allauth %} +{% comment %} djlint:off {% endcomment %} +<{% if attrs.href %}a href="{{ attrs.href }}"{% else %}button{% endif %} +class="btn {% if attrs.tags and 'danger' in attrs.tags %}btn-error{% elif attrs.tags and 'secondary' in attrs.tags %}btn-ghost{% elif attrs.tags and 'link' in attrs.tags %}btn-link{% else %}btn-primary{% endif %}" +{% if attrs.form %}form="{{ attrs.form }}"{% endif %} +{% if attrs.id %}id="{{ attrs.id }}"{% endif %} +{% if attrs.name %}name="{{ attrs.name }}"{% endif %} +{% if attrs.value %}value="{{ attrs.value }}"{% endif %} +{% if attrs.type %}type="{{ attrs.type }}"{% endif %} +> +{% slot %} +{% endslot %} + diff --git a/templates/allauth/elements/button_group.html b/templates/allauth/elements/button_group.html new file mode 100644 index 0000000..f0ed9cc --- /dev/null +++ b/templates/allauth/elements/button_group.html @@ -0,0 +1,4 @@ +{% load allauth %} +
+ {% slot %}{% endslot %} +
diff --git a/templates/allauth/elements/details.html b/templates/allauth/elements/details.html new file mode 100644 index 0000000..ceab2e8 --- /dev/null +++ b/templates/allauth/elements/details.html @@ -0,0 +1,5 @@ +{% load allauth %} +
+ {% slot summary %}{% endslot %} +
{% slot body %}{% endslot %}
+
diff --git a/templates/allauth/elements/fields.html b/templates/allauth/elements/fields.html new file mode 100644 index 0000000..4ea96bd --- /dev/null +++ b/templates/allauth/elements/fields.html @@ -0,0 +1,24 @@ +{% load ui %} +{% for field in attrs.form.hidden_fields %}{{ field }}{% endfor %} +{% for error in attrs.form.non_field_errors %} +
+ {{ error }} +
+{% endfor %} +{% for field in attrs.form.visible_fields %} +
+ {% if field.field.widget.input_type == "checkbox" %} + + {% else %} + + {{ field|daisy }} + {% endif %} + {% if field.help_text %}{{ field.help_text }}{% endif %} + {% for error in field.errors %}{{ error }}{% endfor %} +
+{% endfor %} diff --git a/templates/allauth/elements/form.html b/templates/allauth/elements/form.html new file mode 100644 index 0000000..38ea765 --- /dev/null +++ b/templates/allauth/elements/form.html @@ -0,0 +1,9 @@ +{% load allauth %} +
+ {% slot body %}{% endslot %} +
+ {% slot actions %}{% endslot %} +
+
diff --git a/templates/allauth/elements/h1.html b/templates/allauth/elements/h1.html new file mode 100644 index 0000000..a3feddd --- /dev/null +++ b/templates/allauth/elements/h1.html @@ -0,0 +1 @@ +{% comment %} djlint:off {% endcomment %}{% load allauth %}

{% slot %}{% endslot %}

diff --git a/templates/allauth/elements/h2.html b/templates/allauth/elements/h2.html new file mode 100644 index 0000000..0018ab0 --- /dev/null +++ b/templates/allauth/elements/h2.html @@ -0,0 +1 @@ +{% comment %} djlint:off {% endcomment %}{% load allauth %}

{% slot %}{% endslot %}

diff --git a/templates/allauth/elements/hr.html b/templates/allauth/elements/hr.html new file mode 100644 index 0000000..31684e3 --- /dev/null +++ b/templates/allauth/elements/hr.html @@ -0,0 +1 @@ +{% comment %} djlint:off {% endcomment %}{% load allauth %}
diff --git a/templates/allauth/elements/p.html b/templates/allauth/elements/p.html new file mode 100644 index 0000000..04cd76c --- /dev/null +++ b/templates/allauth/elements/p.html @@ -0,0 +1 @@ +{% comment %} djlint:off {% endcomment %}{% load allauth %}

{% slot %}{% endslot %}

diff --git a/templates/allauth/elements/panel.html b/templates/allauth/elements/panel.html new file mode 100644 index 0000000..f295669 --- /dev/null +++ b/templates/allauth/elements/panel.html @@ -0,0 +1,8 @@ +{% load allauth %} +
+
+ {% if slots.title %}

{% slot title %}{% endslot %}

{% endif %} +
{% slot body %}{% endslot %}
+ {% if slots.actions %}
{% slot actions %}{% endslot %}
{% endif %} +
+
diff --git a/templates/allauth/elements/table.html b/templates/allauth/elements/table.html new file mode 100644 index 0000000..bf01d82 --- /dev/null +++ b/templates/allauth/elements/table.html @@ -0,0 +1,6 @@ +{% load allauth %} +
+ + {% slot %}{% endslot %} +
+
diff --git a/templates/allauth/layouts/base.html b/templates/allauth/layouts/base.html new file mode 100644 index 0000000..e731b7b --- /dev/null +++ b/templates/allauth/layouts/base.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block title %} + {% block head_title %}{% endblock head_title %} · ClubManager +{% endblock title %} + +{% block main %} +
+
+
+ {% block content %}{% endblock content %} +
+
+
+{% endblock main %} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..42602f9 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,86 @@ +{% load static %} + + + + + + + {% block title %}ClubManager{% endblock title %} + + {# Apply the stored theme before first paint, otherwise the page flashes + the wrong colours. With no stored preference we set nothing, so + daisyUI's `dark --prefersdark` follows the OS. #} + + + {% block extra_head %}{% endblock extra_head %} + + + + {% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+ {% endif %} +
+ {% block main %}{% endblock main %} +
+ + + From eace903f05a07844af4407a80582d5d86b061600 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 15:42:20 +0200 Subject: [PATCH 037/137] chore: rebrand ClubManager -> RosterChief, add lucide icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The clubmanager.app domain was taken, so the platform is now RosterChief (rosterchief.app). Renames the Django project package clubmanager/ -> rosterchief/ (git tracks it as a move, so history follows), every `from rosterchief.base import ...`, the settings/wsgi/asgi module paths, env vars (ROSTERCHIEF_BASE_DOMAIN / ROSTERCHIEF_RP_NAME), the MFA adapter (RosterChiefMFAAdapter), brand text, and the docs. Two things were deliberately NOT swept: - club.models.ClubManager stays: it is the Django manager *for Club*, not the brand. A blind rename would have silently broken it. - Migrations are untouched (history is not rewritten). The only reference was a cosmetic help_text, so a normal AlterField migration carries the new domain. Note the WebAuthn RP ID is the base domain, so moving to rosterchief.app cryptographically invalidates any passkey enrolled under the old one; they cannot be migrated and must be re-enrolled. Nothing is in production, so the real cost is zero. Add django-lucide (from bsiebens/lucide) for icons: the theme toggle now swaps sun/moon against the effective theme, and the control panel gets icons on its tabs, actions and stat groups. Its classifiers stop at Django 5.0, but that is stale metadata — verified rendering on Django 6 / Python 3.14. Also add formbuilder, shop and controlpanel to ruff's known-first-party list, which had drifted behind the apps that landed. Co-Authored-By: Claude Opus 4.8 --- .env.example | 20 ++--- ARCHITECTURE.md | 26 +++--- CLAUDE.md | 8 +- authentication/adapters.py | 8 +- authentication/tests.py | 20 ++--- club/migrations/0011_alter_club_slug.py | 18 +++++ club/models.py | 4 +- club/tenancy.py | 4 +- club/tests.py | 30 +++---- controlpanel/services/statistics.py | 4 + controlpanel/templates/controlpanel/base.html | 7 +- .../templates/controlpanel/club_detail.html | 13 +-- .../templates/controlpanel/club_list.html | 5 +- .../templates/controlpanel/dashboard.html | 3 +- controlpanel/tests.py | 4 +- events/models.py | 2 +- formbuilder/models.py | 2 +- manage.py | 2 +- members/models.py | 2 +- package.json | 2 +- pyproject.toml | 10 ++- {clubmanager => rosterchief}/__init__.py | 0 {clubmanager => rosterchief}/asgi.py | 4 +- {clubmanager => rosterchief}/base.py | 0 {clubmanager => rosterchief}/settings.py | 19 ++--- {clubmanager => rosterchief}/urls.py | 2 +- {clubmanager => rosterchief}/wsgi.py | 4 +- shop/models.py | 2 +- static/css/app.css | 2 +- teams/models.py | 2 +- templates/allauth/layouts/base.html | 2 +- templates/base.html | 61 ++++++++++---- uv.lock | 81 ++++++++++--------- 33 files changed, 220 insertions(+), 153 deletions(-) create mode 100644 club/migrations/0011_alter_club_slug.py rename {clubmanager => rosterchief}/__init__.py (100%) rename {clubmanager => rosterchief}/asgi.py (74%) rename {clubmanager => rosterchief}/base.py (100%) rename {clubmanager => rosterchief}/settings.py (91%) rename {clubmanager => rosterchief}/urls.py (95%) rename {clubmanager => rosterchief}/wsgi.py (74%) diff --git a/.env.example b/.env.example index adc24fd..746e21e 100644 --- a/.env.example +++ b/.env.example @@ -12,26 +12,26 @@ DJANGO_ALLOWED_HOSTS=.localhost,127.0.0.1,[::1] # Multi-tenancy: subdomains of this base domain resolve to a club by slug, # e.g. http://ajax-united.localhost:8000/ -> club with slug "ajax-united". -# In production set this to your real base domain (e.g. clubmanager.app). -CLUBMANAGER_BASE_DOMAIN=localhost +# In production set this to your real base domain (e.g. rosterchief.app). +ROSTERCHIEF_BASE_DOMAIN=localhost -# Two-factor auth. CLUBMANAGER_BASE_DOMAIN doubles as the WebAuthn Relying Party +# Two-factor auth. ROSTERCHIEF_BASE_DOMAIN doubles as the WebAuthn Relying Party # ID, so ONE passkey works across every club subdomain. Change it and existing # passkeys stop validating -- they are cryptographically bound to that domain. -# CLUBMANAGER_RP_NAME is what the browser shows during a passkey prompt. -# CLUBMANAGER_RP_NAME=ClubManager +# ROSTERCHIEF_RP_NAME is what the browser shows during a passkey prompt. +# ROSTERCHIEF_RP_NAME=RosterChief # Sessions are shared across club subdomains (log in once, all clubs). Derived -# from CLUBMANAGER_BASE_DOMAIN in production; left host-only on localhost +# from ROSTERCHIEF_BASE_DOMAIN in production; left host-only on localhost # because browsers reject a Domain attribute there. Override if needed. -# DJANGO_SESSION_COOKIE_DOMAIN=.clubmanager.app -# DJANGO_CSRF_COOKIE_DOMAIN=.clubmanager.app +# DJANGO_SESSION_COOKIE_DOMAIN=.rosterchief.app +# DJANGO_CSRF_COOKIE_DOMAIN=.rosterchief.app # Optional. Defaults to sqlite:///db.sqlite3 for dev; point at Postgres in prod. -# DJANGO_DATABASE_URL=postgres://user:pass@localhost:5432/clubmanager +# DJANGO_DATABASE_URL=postgres://user:pass@localhost:5432/rosterchief # Optional. CSRF trusted origins (needed for subdomains in prod), comma-separated. -# DJANGO_CSRF_TRUSTED_ORIGINS=https://*.clubmanager.app +# DJANGO_CSRF_TRUSTED_ORIGINS=https://*.rosterchief.app # Optional. # DJANGO_TIME_ZONE=Europe/Brussels diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 7fb2fca..385ab52 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,10 +1,10 @@ -# ClubManager — Model & Domain Architecture +# RosterChief — Model & Domain Architecture Baseline reference for implementing the domain models. This describes the **intended shape** of the data model: what exists today, what is planned, and the conventions every app should follow. It is a living document — update it when the model changes. -> **Tenancy: multi-tenant (row-based / shared-schema).** ClubManager is designed as a +> **Tenancy: multi-tenant (row-based / shared-schema).** RosterChief is designed as a > **multi-tenant platform** — one deployment serves many clubs, with **`Club` as the tenant > root**. Isolation is **row-based**: a shared database and schema where every club-owned > row carries a `club` FK (via `ClubScopedModel`), and *all* access is scoped to the @@ -54,9 +54,9 @@ only if the app grows unwieldy. The roadmap `members` name is reserved either wa These are already established in code — every new model follows them. -- **UUID primary keys.** Inherit `clubmanager.base.UUIDModel` (`id = UUIDField(default=uuid4)`). +- **UUID primary keys.** Inherit `rosterchief.base.UUIDModel` (`id = UUIDField(default=uuid4)`). Never expose sequential integer PKs. -- **`ClubScopedModel`** (`clubmanager.base`) adds the tenant `club` FK +- **`ClubScopedModel`** (`rosterchief.base`) adds the tenant `club` FK (`related_name="%(class)ss"`) and, under multi-tenancy, a tenant-aware manager + auto club-stamping `save()` (§2.4). **Every aggregate-root model inherits it**; leaf rows reachable only via a scoped parent (e.g. `Attendance` via `Event`) may inherit scope from @@ -105,12 +105,12 @@ several clubs; a `Member` is that person *within one club*. So: current club** (via the tenant context below), falling back to email. **Tenant resolution → `request.club`.** A `ClubTenantMiddleware` resolves the active club -per request (recommended: **subdomain**, `ajax-united.clubmanager.app`; path-prefix +per request (recommended: **subdomain**, `ajax-united.rosterchief.app`; path-prefix `/c//` is the alternative) and stores it on `request.club` *and* in a context variable so non-request code (services, management commands) can read it: ``` -# clubmanager/tenancy.py +# rosterchief/tenancy.py from contextvars import ContextVar _current_club: ContextVar = ContextVar("current_club", default=None) @@ -740,7 +740,7 @@ Legend: `───<` one-to-many, `>───<` many-to-many via a through model rows with the current season and re-scopes `unique_together`. 3. ✅ **Full multi-tenancy** — adopt **row-based multi-tenancy**, `Club` as tenant root (§2.4). Requires: `ClubScopedModel` on every aggregate root, `Member.user` → - `ForeignKey` (+`unique(club, user)`), tenant middleware + `clubmanager/tenancy.py` + `ForeignKey` (+`unique(club, user)`), tenant middleware + `rosterchief/tenancy.py` context, tenant-aware manager, per-club uniqueness, per-club roles (§3). **Supersedes `CLAUDE.md`.** 4. ✅ **Jersey uniqueness** — unique **within a team** via a partial `UniqueConstraint` @@ -822,10 +822,10 @@ Setup: ### 8.3 Tenancy runtime config (needed once `ClubTenantMiddleware` lands) -- **Hosts:** wildcard `ALLOWED_HOSTS` for the chosen base domain (e.g. `.clubmanager.app`) +- **Hosts:** wildcard `ALLOWED_HOSTS` for the chosen base domain (e.g. `.rosterchief.app`) if using subdomain resolution; add `DJANGO_ALLOWED_HOSTS` accordingly. - **CSRF:** `CSRF_TRUSTED_ORIGINS` must cover the wildcard scheme+host set - (`https://*.clubmanager.app`). + (`https://*.rosterchief.app`). - **Cookies:** to share login across club subdomains, set `SESSION_COOKIE_DOMAIN` / `CSRF_COOKIE_DOMAIN` to the base domain; otherwise keep per-subdomain sessions (decide with the "cross-club users" question in §7). @@ -842,8 +842,8 @@ Setup: --- -*Conventions cross-reference:* `clubmanager/base.py` (`UUIDModel`, `ClubScopedModel`), -`clubmanager/tenancy.py` (*to add* — tenant context/middleware, §2.4), +*Conventions cross-reference:* `rosterchief/base.py` (`UUIDModel`, `ClubScopedModel`), +`rosterchief/tenancy.py` (*to add* — tenant context/middleware, §2.4), `authentication/managers.py` (`UserManager`), `authentication/services/` (service-layer pattern). @@ -852,10 +852,10 @@ pattern). > ### ⚠️ Banner: supersedes `CLAUDE.md` > > This architecture adopts **full multi-tenancy** (§2.4), which **directly contradicts** -> the current `CLAUDE.md` ("ClubManager is a **single-club** app … deliberately *not* +> the current `CLAUDE.md` ("RosterChief is a **single-club** app … deliberately *not* > multi-tenant — there is no `club_id` tenancy") and the project memory > (`project_overview` — "Single-club (NOT multi-tenant)"). > > **Action required** before/alongside implementation: update `CLAUDE.md` and the memory -> to describe ClubManager as a **multi-tenant platform (row-based, `Club` = tenant root)**. +> to describe RosterChief as a **multi-tenant platform (row-based, `Club` = tenant root)**. > Until that is done, where the two disagree **this document is authoritative**. diff --git a/CLAUDE.md b/CLAUDE.md index 26c853a..2c1a765 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,11 +4,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## What this is -ClubManager is a sport club management app + public website, built on **Django 6.0** (Python 3.14+). As of **2026-07-11 it is designed as a multi-tenant platform** (row-based / shared-schema): one deployment serves many clubs, with `Club` as the tenant root. Every club-owned model carries a `club` FK (via `ClubScopedModel`); `User` is the only global model. This **reverses** the project's earlier single-club stance — treat older "single-club / no `club_id` tenancy" notes (in git history or memory) as obsolete. +RosterChief is a sport club management app + public website, built on **Django 6.0** (Python 3.14+). As of **2026-07-11 it is designed as a multi-tenant platform** (row-based / shared-schema): one deployment serves many clubs, with `Club` as the tenant root. Every club-owned model carries a `club` FK (via `ClubScopedModel`); `User` is the only global model. This **reverses** the project's earlier single-club stance — treat older "single-club / no `club_id` tenancy" notes (in git history or memory) as obsolete. **`ARCHITECTURE.md` at the repo root is the authoritative model & domain design** — the tenancy mechanics, the RBAC design, and per-app model sketches all live there. Consult and update it when adding domain models. -The repo is an early build: `authentication` and `club` apps exist (`User`, `Member`, `Family`, `FamilyMembership`, `Club`, `ClubMembership`); the remaining domain apps and the tenancy plumbing (`clubmanager/tenancy.py`, tenant middleware, `ClubScopedModel` upgrade) are **planned, not yet on disk**. Verify against the actual tree before assuming a module exists. +The repo is an early build: `authentication` and `club` apps exist (`User`, `Member`, `Family`, `FamilyMembership`, `Club`, `ClubMembership`); the remaining domain apps and the tenancy plumbing (`rosterchief/tenancy.py`, tenant middleware, `ClubScopedModel` upgrade) are **planned, not yet on disk**. Verify against the actual tree before assuming a module exists. ## Commands @@ -34,7 +34,7 @@ uv run ruff format . # format ## Configuration -Settings live in a single `clubmanager/settings.py` and read from the environment via **python-decouple** (`config(...)`), with a local `.env` file for dev. Key vars: `DJANGO_SECRET_KEY` (required), `DJANGO_DEBUG`, `DJANGO_ALLOWED_HOSTS`, `DJANGO_CSRF_TRUSTED_ORIGINS`, `DJANGO_DATABASE_URL`, `DJANGO_TIME_ZONE`. +Settings live in a single `rosterchief/settings.py` and read from the environment via **python-decouple** (`config(...)`), with a local `.env` file for dev. Key vars: `DJANGO_SECRET_KEY` (required), `DJANGO_DEBUG`, `DJANGO_ALLOWED_HOSTS`, `DJANGO_CSRF_TRUSTED_ORIGINS`, `DJANGO_DATABASE_URL`, `DJANGO_TIME_ZONE`. The database is configured through a single `DJANGO_DATABASE_URL` (parsed by **dj-database-url**), defaulting to `sqlite:///db.sqlite3` for dev; production is intended to point at PostgreSQL via that URL. Don't hardcode DB settings — go through the env var. @@ -52,4 +52,4 @@ Domain notes (drive modeling decisions): ## Conventions - Ruff config anticipates a Wagtail-style codebase (`DJ` Django rules; `RUF012`/`RUF005` ignored for framework idioms; `line-length = 250`). Migrations are excluded from linting — don't hand-edit them to satisfy ruff. -- Settings files are exempt from `F403/F405/E501` (star imports allowed) under `clubmanager/settings/*` — note the config expects a settings *package*, though the current code is a single `settings.py`. If you split settings, match that path. +- Settings files are exempt from `F403/F405/E501` (star imports allowed) under `rosterchief/settings/*` — note the config expects a settings *package*, though the current code is a single `settings.py`. If you split settings, match that path. diff --git a/authentication/adapters.py b/authentication/adapters.py index a3c9c53..d9857e9 100644 --- a/authentication/adapters.py +++ b/authentication/adapters.py @@ -2,11 +2,11 @@ The MFA adapter exists for one important reason: WebAuthn credentials are bound to a **Relying Party ID** (a domain). allauth's default RP ID is the request's -host — which under our subdomain tenancy would be ``ajax-united.clubmanager.app``, +host — which under our subdomain tenancy would be ``ajax-united.rosterchief.app``, binding a passkey to *one club*. A member of two clubs would then need two passkeys, and a credential registered at one club would silently fail at another. -Pinning the RP ID to the registrable parent domain (``clubmanager.app``) makes a +Pinning the RP ID to the registrable parent domain (``rosterchief.app``) makes a single passkey work across every club subdomain. """ @@ -14,7 +14,7 @@ from allauth.mfa.adapter import DefaultMFAAdapter from django.conf import settings -class ClubManagerMFAAdapter(DefaultMFAAdapter): +class RosterChiefMFAAdapter(DefaultMFAAdapter): def get_public_key_credential_rp_entity(self) -> dict[str, str]: return { "id": webauthn_rp_id(), @@ -28,7 +28,7 @@ def webauthn_rp_id() -> str: Falls back to the request host when no base domain is configured (e.g. a bare ``localhost`` dev server), which keeps WebAuthn usable there. """ - base_domain = getattr(settings, "CLUBMANAGER_BASE_DOMAIN", "") + base_domain = getattr(settings, "ROSTERCHIEF_BASE_DOMAIN", "") if base_domain: return base_domain diff --git a/authentication/tests.py b/authentication/tests.py index 7b114fc..871d356 100644 --- a/authentication/tests.py +++ b/authentication/tests.py @@ -13,7 +13,7 @@ from django.urls import reverse from club.models import Club, ClubRole from members.models import Member -from .adapters import ClubManagerMFAAdapter, webauthn_rp_id +from .adapters import RosterChiefMFAAdapter, webauthn_rp_id from .middleware import RequireMFAMiddleware, mfa_required_for User = get_user_model() @@ -104,9 +104,9 @@ class UserModelTests(TestCase): @override_settings( - CLUBMANAGER_BASE_DOMAIN="clubmanager.app", - MFA_WEBAUTHN_RP_NAME="ClubManager", - ALLOWED_HOSTS=[".clubmanager.app", "example.test"], + ROSTERCHIEF_BASE_DOMAIN="rosterchief.app", + MFA_WEBAUTHN_RP_NAME="RosterChief", + ALLOWED_HOSTS=[".rosterchief.app", "example.test"], ) class WebAuthnRelyingPartyTests(TestCase): """A passkey is bound to a Relying Party ID (a domain). @@ -119,22 +119,22 @@ class WebAuthnRelyingPartyTests(TestCase): def rp_entity(self, host): request = RequestFactory().get("/", HTTP_HOST=host) with context.request_context(request): - return ClubManagerMFAAdapter().get_public_key_credential_rp_entity() + return RosterChiefMFAAdapter().get_public_key_credential_rp_entity() def test_rp_id_is_the_parent_domain_not_the_club_subdomain(self): - self.assertEqual(self.rp_entity("ajax-united.clubmanager.app")["id"], "clubmanager.app") + self.assertEqual(self.rp_entity("ajax-united.rosterchief.app")["id"], "rosterchief.app") def test_rp_id_is_identical_across_clubs(self): # The whole point: a passkey registered at one club works at the others. - here = self.rp_entity("ajax-united.clubmanager.app") - there = self.rp_entity("rival-fc.clubmanager.app") + here = self.rp_entity("ajax-united.rosterchief.app") + there = self.rp_entity("rival-fc.rosterchief.app") self.assertEqual(here["id"], there["id"]) def test_rp_name_comes_from_settings(self): - self.assertEqual(self.rp_entity("ajax-united.clubmanager.app")["name"], "ClubManager") + self.assertEqual(self.rp_entity("ajax-united.rosterchief.app")["name"], "RosterChief") - @override_settings(CLUBMANAGER_BASE_DOMAIN="") + @override_settings(ROSTERCHIEF_BASE_DOMAIN="") def test_falls_back_to_the_request_host_without_a_base_domain(self): request = RequestFactory().get("/", HTTP_HOST="example.test:8000") diff --git a/club/migrations/0011_alter_club_slug.py b/club/migrations/0011_alter_club_slug.py new file mode 100644 index 0000000..f00b4e8 --- /dev/null +++ b/club/migrations/0011_alter_club_slug.py @@ -0,0 +1,18 @@ +# Generated by Django 6.0.6 on 2026-07-13 13:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0010_club_archived_at'), + ] + + operations = [ + migrations.AlterField( + model_name='club', + name='slug', + field=models.SlugField(blank=True, help_text='Drives subdomain / path resolution (e.g. ajax-united.rosterchief.app).', max_length=255, unique=True, verbose_name='slug'), + ), + ] diff --git a/club/models.py b/club/models.py index c61557d..79db905 100644 --- a/club/models.py +++ b/club/models.py @@ -4,8 +4,8 @@ from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from clubmanager.base import ClubScopedModel, UUIDModel, unique_slugify, validate_club_scope from members.models import Member +from rosterchief.base import ClubScopedModel, UUIDModel, unique_slugify, validate_club_scope class ClubManager(models.Manager): @@ -24,7 +24,7 @@ class ClubManager(models.Manager): class Club(UUIDModel): name = models.CharField(_("name"), max_length=255) - slug = models.SlugField(_("slug"), max_length=255, unique=True, blank=True, help_text=_("Drives subdomain / path resolution (e.g. ajax-united.clubmanager.app).")) + slug = models.SlugField(_("slug"), max_length=255, unique=True, blank=True, help_text=_("Drives subdomain / path resolution (e.g. ajax-united.rosterchief.app).")) archived_at = models.DateTimeField(_("archived at"), null=True, blank=True, help_text=_("Archived clubs stop resolving on their subdomain, but their data is retained.")) diff --git a/club/tenancy.py b/club/tenancy.py index 3df0a62..94f0e0b 100644 --- a/club/tenancy.py +++ b/club/tenancy.py @@ -58,7 +58,7 @@ class ClubTenantMiddleware: reset_current_club(token) def get_club(self, request) -> Club | None: - # Imported lazily: club.models imports clubmanager.base, which imports + # Imported lazily: club.models imports rosterchief.base, which imports # this module, so a top-level import would be circular. from .models import Club @@ -78,7 +78,7 @@ class ClubTenantMiddleware: if not host: return None - base_domain = getattr(settings, "CLUBMANAGER_BASE_DOMAIN", "").lower().strip(".") + base_domain = getattr(settings, "ROSTERCHIEF_BASE_DOMAIN", "").lower().strip(".") if base_domain: # Only hosts under the configured base domain carry a tenant slug. diff --git a/club/tests.py b/club/tests.py index e39c0ed..34405d8 100644 --- a/club/tests.py +++ b/club/tests.py @@ -231,8 +231,8 @@ class ClubSlugTests(TestCase): @override_settings( - CLUBMANAGER_BASE_DOMAIN="clubmanager.app", - ALLOWED_HOSTS=[".clubmanager.app", ".example.com", ".example.org"], + ROSTERCHIEF_BASE_DOMAIN="rosterchief.app", + ALLOWED_HOSTS=[".rosterchief.app", ".example.com", ".example.org"], ) class ClubTenantMiddlewareTests(TestCase): def setUp(self): @@ -252,29 +252,29 @@ class ClubTenantMiddlewareTests(TestCase): return request, response def test_subdomain_resolves_to_club(self): - request, response = self._run("ajax-united.clubmanager.app") + request, response = self._run("ajax-united.rosterchief.app") self.assertEqual(response, "response") self.assertEqual(request.club, self.club) self.assertEqual(self.captured["context_club"], self.club) def test_subdomain_resolution_ignores_port(self): - request, _ = self._run("ajax-united.clubmanager.app:8000") + request, _ = self._run("ajax-united.rosterchief.app:8000") self.assertEqual(request.club, self.club) def test_unknown_subdomain_sets_none(self): - request, _ = self._run("unknown-club.clubmanager.app") + request, _ = self._run("unknown-club.rosterchief.app") self.assertIsNone(request.club) def test_bare_base_domain_has_no_club(self): - request, _ = self._run("clubmanager.app") + request, _ = self._run("rosterchief.app") self.assertIsNone(request.club) def test_www_is_treated_as_no_club(self): - request, _ = self._run("www.clubmanager.app") + request, _ = self._run("www.rosterchief.app") self.assertIsNone(request.club) @@ -284,17 +284,17 @@ class ClubTenantMiddlewareTests(TestCase): self.assertIsNone(request.club) def test_context_var_is_reset_after_request(self): - self._run("ajax-united.clubmanager.app") + self._run("ajax-united.rosterchief.app") self.assertIsNone(get_current_club()) - @override_settings(CLUBMANAGER_BASE_DOMAIN="") + @override_settings(ROSTERCHIEF_BASE_DOMAIN="") def test_generic_host_resolution_without_base_domain(self): request, _ = self._run("ajax-united.example.com") self.assertEqual(request.club, self.club) - @override_settings(CLUBMANAGER_BASE_DOMAIN="") + @override_settings(ROSTERCHIEF_BASE_DOMAIN="") def test_two_label_host_has_no_club_without_base_domain(self): request, _ = self._run("example.com") @@ -309,7 +309,7 @@ class _FakeRequest: return self._host -@override_settings(CLUBMANAGER_BASE_DOMAIN="clubmanager.app") +@override_settings(ROSTERCHIEF_BASE_DOMAIN="rosterchief.app") class GetSubdomainTests(TestCase): def subdomain(self, host): return ClubTenantMiddleware.get_subdomain(_FakeRequest(host)) @@ -318,10 +318,10 @@ class GetSubdomainTests(TestCase): self.assertIsNone(self.subdomain("")) def test_trailing_dot_is_stripped(self): - self.assertEqual(self.subdomain("ajax-united.clubmanager.app."), "ajax-united") + self.assertEqual(self.subdomain("ajax-united.rosterchief.app."), "ajax-united") def test_nested_subdomain_uses_leftmost_label(self): - self.assertEqual(self.subdomain("a.b.clubmanager.app"), "a") + self.assertEqual(self.subdomain("a.b.rosterchief.app"), "a") class TenantContextTests(TestCase): @@ -537,7 +537,7 @@ class ClubArchivingTests(TestCase): self.assertTrue(Season.objects.filter(club=self.club).exists()) -@override_settings(CLUBMANAGER_BASE_DOMAIN="clubmanager.app", ALLOWED_HOSTS=[".clubmanager.app"]) +@override_settings(ROSTERCHIEF_BASE_DOMAIN="rosterchief.app", ALLOWED_HOSTS=[".rosterchief.app"]) class ArchivedClubTenancyTests(TestCase): """An archived club's subdomain must stop resolving — that is what makes archiving a real deactivation rather than a cosmetic flag.""" @@ -547,7 +547,7 @@ class ArchivedClubTenancyTests(TestCase): self.middleware = ClubTenantMiddleware(lambda request: "response") def resolve(self): - request = RequestFactory().get("/", HTTP_HOST="ajax-united.clubmanager.app") + request = RequestFactory().get("/", HTTP_HOST="ajax-united.rosterchief.app") self.middleware(request) return request.club diff --git a/controlpanel/services/statistics.py b/controlpanel/services/statistics.py index c63961a..3600aa5 100644 --- a/controlpanel/services/statistics.py +++ b/controlpanel/services/statistics.py @@ -58,6 +58,7 @@ def club_statistics(club): return [ { "title": "Members", + "icon": "users", "stats": [ ("Members", memberships.values("member").distinct().count()), ("Active this season", memberships.filter(season=season, status=ClubMembership.StatusChoices.ACTIVE).count() if season else 0), @@ -67,6 +68,7 @@ def club_statistics(club): }, { "title": "Teams & staff", + "icon": "shield", "stats": [ ("Teams", Team.objects.filter(club=club).count()), ("Players this season", TeamMembership.objects.filter(team__club=club, season=season).count() if season else 0), @@ -75,6 +77,7 @@ def club_statistics(club): }, { "title": "Events", + "icon": "calendar-days", "stats": [ ("Upcoming", events.filter(start__gte=now).count()), ("This season", events.filter(season=season).count() if season else 0), @@ -82,6 +85,7 @@ def club_statistics(club): }, { "title": "Shop", + "icon": "shopping-cart", "stats": [ ("Orders", orders.count()), ("Revenue", _money(orders.filter(status__in=PAID_STATUSES))), diff --git a/controlpanel/templates/controlpanel/base.html b/controlpanel/templates/controlpanel/base.html index d11241c..900eb06 100644 --- a/controlpanel/templates/controlpanel/base.html +++ b/controlpanel/templates/controlpanel/base.html @@ -1,7 +1,8 @@ {% extends "base.html" %} +{% load lucide %} {% block title %} - {% block panel_title %}Control panel{% endblock panel_title %} · ClubManager + {% block panel_title %}Control panel{% endblock panel_title %} · RosterChief {% endblock title %} {% block main %} @@ -17,8 +18,8 @@ {% block panel %}{% endblock panel %} {% endblock main %} diff --git a/controlpanel/templates/controlpanel/club_detail.html b/controlpanel/templates/controlpanel/club_detail.html index 33582e5..db8d54d 100644 --- a/controlpanel/templates/controlpanel/club_detail.html +++ b/controlpanel/templates/controlpanel/club_detail.html @@ -1,4 +1,5 @@ {% extends "controlpanel/base.html" %} +{% load lucide %} {% block heading %}{{ club.name }}{% endblock heading %} @@ -12,16 +13,16 @@ {% endblock subheading %} {% block actions %} - Edit + {% lucide "pencil" size=16 %} Edit {% if club.is_archived %}
{% csrf_token %} - +
{% else %}
{% csrf_token %} - +
{% endif %} {% endblock actions %} @@ -36,7 +37,7 @@ {% for group in groups %}
-

{{ group.title }}

+

{% lucide group.icon size=18 %} {{ group.title }}

{% for label, value in group.stats %}
@@ -53,7 +54,7 @@
@@ -72,7 +73,7 @@ diff --git a/controlpanel/templates/controlpanel/club_list.html b/controlpanel/templates/controlpanel/club_list.html index 2e94e32..5d4f740 100644 --- a/controlpanel/templates/controlpanel/club_list.html +++ b/controlpanel/templates/controlpanel/club_list.html @@ -1,4 +1,5 @@ {% extends "controlpanel/base.html" %} +{% load lucide %} {% block heading %}{% if show_archived %}Archived clubs{% else %}Clubs{% endif %}{% endblock heading %} @@ -8,7 +9,7 @@ {% else %} Archived {% endif %} - New club + {% lucide "plus" size=16 %} New club {% endblock actions %} {% block panel %} @@ -19,7 +20,7 @@ value="{{ search }}" placeholder="Search clubs…" class="input input-bordered w-full max-w-xs"> - +
diff --git a/controlpanel/templates/controlpanel/dashboard.html b/controlpanel/templates/controlpanel/dashboard.html index a9f8bbd..ed95e7c 100644 --- a/controlpanel/templates/controlpanel/dashboard.html +++ b/controlpanel/templates/controlpanel/dashboard.html @@ -1,9 +1,10 @@ {% extends "controlpanel/base.html" %} +{% load lucide %} {% block heading %}Platform overview{% endblock heading %} {% block actions %} - New club + {% lucide "plus" size=16 %} New club {% endblock actions %} {% block panel %} diff --git a/controlpanel/tests.py b/controlpanel/tests.py index f50ea00..02ebbf4 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -56,12 +56,12 @@ class AccessTests(ControlPanelTestBase): self.assertEqual(self.client.get(reverse("controlpanel:dashboard")).status_code, 200) - @override_settings(CLUBMANAGER_BASE_DOMAIN="clubmanager.app", ALLOWED_HOSTS=[".clubmanager.app"]) + @override_settings(ROSTERCHIEF_BASE_DOMAIN="rosterchief.app", ALLOWED_HOSTS=[".rosterchief.app"]) def test_panel_does_not_exist_on_a_club_subdomain(self): # It manages *all* clubs, so it must not be reachable from inside one. Club.objects.create(name="Rival FC", slug="rival-fc") - response = self.client.get(reverse("controlpanel:dashboard"), headers={"host": "rival-fc.clubmanager.app"}) + response = self.client.get(reverse("controlpanel:dashboard"), headers={"host": "rival-fc.rosterchief.app"}) self.assertEqual(response.status_code, 404) diff --git a/events/models.py b/events/models.py index c8579c4..607e248 100644 --- a/events/models.py +++ b/events/models.py @@ -2,8 +2,8 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from club.models import Season -from clubmanager.base import ClubScopedModel, UUIDModel, validate_club_scope from members.models import Member +from rosterchief.base import ClubScopedModel, UUIDModel, validate_club_scope from teams.models import Team diff --git a/formbuilder/models.py b/formbuilder/models.py index 37601bc..7eb2c5a 100644 --- a/formbuilder/models.py +++ b/formbuilder/models.py @@ -3,8 +3,8 @@ from django.db import models from django.db.models import UniqueConstraint from django.utils.translation import gettext_lazy as _ -from clubmanager.base import ClubScopedModel, UUIDModel, unique_slugify from members.models import Member +from rosterchief.base import ClubScopedModel, UUIDModel, unique_slugify class Form(ClubScopedModel): diff --git a/manage.py b/manage.py index e08fcbb..86cc41a 100755 --- a/manage.py +++ b/manage.py @@ -7,7 +7,7 @@ import sys def main(): """Run administrative tasks.""" - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "clubmanager.settings") + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rosterchief.settings") try: from django.core.management import execute_from_command_line except ImportError as exc: diff --git a/members/models.py b/members/models.py index 8009e60..d9499b3 100644 --- a/members/models.py +++ b/members/models.py @@ -3,7 +3,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField -from clubmanager.base import UUIDModel +from rosterchief.base import UUIDModel class Family(UUIDModel): diff --git a/package.json b/package.json index 78f1820..4009fb9 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "clubmanager", + "name": "rosterchief", "private": true, "type": "module", "scripts": { diff --git a/pyproject.toml b/pyproject.toml index 9910484..69b8739 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,12 @@ [project] -name = "clubmanager" +name = "rosterchief" version = "0.1.0" requires-python = ">=3.14" dependencies = [ "dj-database-url>=3.1.2", "django>=6.0.6", "django-allauth[mfa]>=65.18.0", + "django-lucide", "django-phonenumber-field[phonenumbers]>=8.4.0", "pillow>=12.3.0", "python-dateutil>=2.9.0.post0", @@ -43,7 +44,10 @@ ignore = [ [tool.ruff.lint.per-file-ignores] # Settings legitimately use star imports and long generated values. -"clubmanager/settings/*" = ["F403", "F405", "E501"] +"rosterchief/settings/*" = ["F403", "F405", "E501"] [tool.ruff.lint.isort] -known-first-party = ["authentication", "club", "members", "teams", "events", "news", "pages", "home", "search", "clubmanager"] +known-first-party = ["authentication", "club", "members", "teams", "events", "formbuilder", "shop", "controlpanel", "news", "pages", "home", "search", "rosterchief"] + +[tool.uv.sources] +django-lucide = { git = "https://github.com/bsiebens/lucide" } diff --git a/clubmanager/__init__.py b/rosterchief/__init__.py similarity index 100% rename from clubmanager/__init__.py rename to rosterchief/__init__.py diff --git a/clubmanager/asgi.py b/rosterchief/asgi.py similarity index 74% rename from clubmanager/asgi.py rename to rosterchief/asgi.py index eb47138..7352af8 100644 --- a/clubmanager/asgi.py +++ b/rosterchief/asgi.py @@ -1,5 +1,5 @@ """ -ASGI config for clubmanager project. +ASGI config for rosterchief project. It exposes the ASGI callable as a module-level variable named ``application``. @@ -11,6 +11,6 @@ import os from django.core.asgi import get_asgi_application -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "clubmanager.settings") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rosterchief.settings") application = get_asgi_application() diff --git a/clubmanager/base.py b/rosterchief/base.py similarity index 100% rename from clubmanager/base.py rename to rosterchief/base.py diff --git a/clubmanager/settings.py b/rosterchief/settings.py similarity index 91% rename from clubmanager/settings.py rename to rosterchief/settings.py index a187986..e6d61e7 100644 --- a/clubmanager/settings.py +++ b/rosterchief/settings.py @@ -1,5 +1,5 @@ """ -Django settings for clubmanager project. +Django settings for rosterchief project. Generated by 'django-admin startproject' using Django 6.0.6. @@ -44,6 +44,7 @@ INSTALLED_APPS = [ "django.contrib.messages", "django.contrib.staticfiles", "phonenumber_field", + "lucide", # Auth: allauth deliberately WITHOUT django.contrib.sites — it is optional in # allauth 65+, and ARCHITECTURE.md §2.4 rejects the Sites framework (Club is # the tenant root, not Site). @@ -79,11 +80,11 @@ AUTHENTICATION_BACKENDS = [ ] # Multi-tenancy: base domain whose subdomains resolve to a club, e.g. -# "ajax-united.clubmanager.app" -> the club with slug "ajax-united". Leave +# "ajax-united.rosterchief.app" -> the club with slug "ajax-united". Leave # unset to fall back to generic "slug.example.com" (3+ label) resolution. -CLUBMANAGER_BASE_DOMAIN = config("CLUBMANAGER_BASE_DOMAIN", default="") +ROSTERCHIEF_BASE_DOMAIN = config("ROSTERCHIEF_BASE_DOMAIN", default="") -ROOT_URLCONF = "clubmanager.urls" +ROOT_URLCONF = "rosterchief.urls" AUTH_USER_MODEL = "authentication.User" @@ -113,10 +114,10 @@ MFA_PASSKEY_SIGNUP_ENABLED = False MFA_WEBAUTHN_ALLOW_INSECURE_ORIGIN = DEBUG # A passkey is bound to a Relying Party ID (a domain). Our adapter pins it to -# CLUBMANAGER_BASE_DOMAIN so that ONE passkey works across every club subdomain +# ROSTERCHIEF_BASE_DOMAIN so that ONE passkey works across every club subdomain # — allauth's default (the request host) would bind it to a single club. -MFA_ADAPTER = "authentication.adapters.ClubManagerMFAAdapter" -MFA_WEBAUTHN_RP_NAME = config("CLUBMANAGER_RP_NAME", default="ClubManager") +MFA_ADAPTER = "authentication.adapters.RosterChiefMFAAdapter" +MFA_WEBAUTHN_RP_NAME = config("ROSTERCHIEF_RP_NAME", default="RosterChief") # Where RequireMFAMiddleware sends privileged users who haven't enrolled yet. MFA_ENROLMENT_URL_NAME = "mfa_index" @@ -126,7 +127,7 @@ MFA_ENROLMENT_URL_NAME = "mfa_index" # on every club (matching the one-passkey-everywhere model). Tenancy still scopes # what you can *see* — that is the access service's job, not the cookie's. # Browsers reject a Domain attribute on localhost, so it stays host-only in dev. -SHARED_COOKIE_DOMAIN = f".{CLUBMANAGER_BASE_DOMAIN}" if CLUBMANAGER_BASE_DOMAIN and CLUBMANAGER_BASE_DOMAIN != "localhost" else None +SHARED_COOKIE_DOMAIN = f".{ROSTERCHIEF_BASE_DOMAIN}" if ROSTERCHIEF_BASE_DOMAIN and ROSTERCHIEF_BASE_DOMAIN != "localhost" else None SESSION_COOKIE_DOMAIN = config("DJANGO_SESSION_COOKIE_DOMAIN", default=SHARED_COOKIE_DOMAIN) CSRF_COOKIE_DOMAIN = config("DJANGO_CSRF_COOKIE_DOMAIN", default=SHARED_COOKIE_DOMAIN) @@ -146,7 +147,7 @@ TEMPLATES = [ }, ] -WSGI_APPLICATION = "clubmanager.wsgi.application" +WSGI_APPLICATION = "rosterchief.wsgi.application" # Database diff --git a/clubmanager/urls.py b/rosterchief/urls.py similarity index 95% rename from clubmanager/urls.py rename to rosterchief/urls.py index 16c6dbe..15fb04f 100644 --- a/clubmanager/urls.py +++ b/rosterchief/urls.py @@ -1,4 +1,4 @@ -"""URL configuration for clubmanager. +"""URL configuration for rosterchief. ``/admin/login/`` is deliberately intercepted *before* ``admin.site.urls`` and redirected to the allauth login, so Django staff go through the same MFA diff --git a/clubmanager/wsgi.py b/rosterchief/wsgi.py similarity index 74% rename from clubmanager/wsgi.py rename to rosterchief/wsgi.py index ceeeca2..958f022 100644 --- a/clubmanager/wsgi.py +++ b/rosterchief/wsgi.py @@ -1,5 +1,5 @@ """ -WSGI config for clubmanager project. +WSGI config for rosterchief project. It exposes the WSGI callable as a module-level variable named ``application``. @@ -11,6 +11,6 @@ import os from django.core.wsgi import get_wsgi_application -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "clubmanager.settings") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rosterchief.settings") application = get_wsgi_application() diff --git a/shop/models.py b/shop/models.py index 7a7a3f2..9e4a30f 100644 --- a/shop/models.py +++ b/shop/models.py @@ -6,8 +6,8 @@ from django.utils.translation import gettext_lazy as _ from authentication.models import User from club.models import Season from club.tenancy import require_current_club -from clubmanager.base import ClubScopedModel, UUIDModel, validate_club_scope from members.models import Member +from rosterchief.base import ClubScopedModel, UUIDModel, validate_club_scope from teams.models import Position, Team diff --git a/static/css/app.css b/static/css/app.css index 2e13205..e59aa20 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -1,2 +1,2 @@ /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax:"";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tab:is(.tabs>.tab){cursor:pointer;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:is(.tabs>.tab):hover{color:var(--color-base-content)}}.tab:is(.tabs>.tab){--tab-p:.75rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));--tab-radius-limit:min(var(--radius-field), var(--tab-radius-min));--tab-radius-grad:#0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + .25px), var(--tab-border-color) 69%, var(--tab-bg) calc(69% + .25px);order:var(--tab-order);height:var(--tab-height);padding-inline:var(--tab-p);border-color:#0000;font-size:.875rem}.tab:is(.tabs>.tab):is(input[type=radio]){min-width:fit-content}.tab:is(.tabs>.tab):is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:is(.tabs>.tab):is(label){position:relative}.tab:is(.tabs>.tab):is(label) input{cursor:pointer;appearance:none;opacity:0;position:absolute;inset:0}:is(.tab:is(.tabs>.tab):checked,.tab:is(.tabs>.tab):is(label:has(:checked)),.tab:is(.tabs>.tab):is(.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]))+.tab-content{display:block}.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.tab:is(.tabs>.tab):not(input):empty{cursor:default;flex-grow:1}.tab:is(.tabs>.tab):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:is(.tabs>.tab):focus{outline-offset:2px;outline:2px solid #0000}}.tab:is(.tabs>.tab):focus-visible,.tab:is(.tabs>.tab):is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab:is(.tabs>.tab)[disabled]{pointer-events:none;opacity:.4}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem) * 10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}}@layer daisyui.l1.l2{.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.btn-circle{width:var(--size);height:var(--size);border-radius:3.40282e38px;padding-inline:0}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.visible{visibility:visible}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.static{position:static}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.table{display:table}.h-5{height:calc(var(--spacing) * 5)}.min-h-screen{min-height:100vh}.w-5{width:calc(var(--spacing) * 5)}.w-56{width:calc(var(--spacing) * 56)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-5xl{max-width:var(--container-5xl)}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.flex-none{flex:none}.cursor-pointer{cursor:pointer}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.overflow-x-auto{overflow-x:auto}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.border{border-style:var(--tw-border-style);border-width:1px}.border-base-300{border-color:var(--color-base-300)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-right{text-align:right}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax:"";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tab:is(.tabs>.tab){cursor:pointer;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:is(.tabs>.tab):hover{color:var(--color-base-content)}}.tab:is(.tabs>.tab){--tab-p:.75rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));--tab-radius-limit:min(var(--radius-field), var(--tab-radius-min));--tab-radius-grad:#0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + .25px), var(--tab-border-color) 69%, var(--tab-bg) calc(69% + .25px);order:var(--tab-order);height:var(--tab-height);padding-inline:var(--tab-p);border-color:#0000;font-size:.875rem}.tab:is(.tabs>.tab):is(input[type=radio]){min-width:fit-content}.tab:is(.tabs>.tab):is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:is(.tabs>.tab):is(label){position:relative}.tab:is(.tabs>.tab):is(label) input{cursor:pointer;appearance:none;opacity:0;position:absolute;inset:0}:is(.tab:is(.tabs>.tab):checked,.tab:is(.tabs>.tab):is(label:has(:checked)),.tab:is(.tabs>.tab):is(.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]))+.tab-content{display:block}.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.tab:is(.tabs>.tab):not(input):empty{cursor:default;flex-grow:1}.tab:is(.tabs>.tab):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:is(.tabs>.tab):focus{outline-offset:2px;outline:2px solid #0000}}.tab:is(.tabs>.tab):focus-visible,.tab:is(.tabs>.tab):is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab:is(.tabs>.tab)[disabled]{pointer-events:none;opacity:.4}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem) * 10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}}@layer daisyui.l1.l2{.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.btn-circle{width:var(--size);height:var(--size);border-radius:3.40282e38px;padding-inline:0}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}.input-error,.input-error:focus,.input-error:focus-within{--input-color:var(--color-error)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.visible{visibility:visible}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.static{position:static}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.table{display:table}.min-h-screen{min-height:100vh}.w-56{width:calc(var(--spacing) * 56)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-5xl{max-width:var(--container-5xl)}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.flex-none{flex:none}.cursor-pointer{cursor:pointer}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.overflow-x-auto{overflow-x:auto}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.border{border-style:var(--tw-border-style);border-width:1px}.border-base-300{border-color:var(--color-base-300)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-right{text-align:right}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file diff --git a/teams/models.py b/teams/models.py index ce43855..f643ef0 100644 --- a/teams/models.py +++ b/teams/models.py @@ -3,8 +3,8 @@ from django.db.models import Q from django.utils.translation import gettext_lazy as _ from club.models import Season -from clubmanager.base import ClubScopedModel, UUIDModel, validate_club_scope from members.models import Member +from rosterchief.base import ClubScopedModel, UUIDModel, validate_club_scope class Team(ClubScopedModel): diff --git a/templates/allauth/layouts/base.html b/templates/allauth/layouts/base.html index e731b7b..dbef0b3 100644 --- a/templates/allauth/layouts/base.html +++ b/templates/allauth/layouts/base.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block title %} - {% block head_title %}{% endblock head_title %} · ClubManager + {% block head_title %}{% endblock head_title %} · RosterChief {% endblock title %} {% block main %} diff --git a/templates/base.html b/templates/base.html index 42602f9..c4b5820 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,11 +1,11 @@ -{% load static %} +{% load lucide static %} - {% block title %}ClubManager{% endblock title %} + {% block title %}RosterChief{% endblock title %} {# Apply the stored theme before first paint, otherwise the page flashes the wrong colours. With no stored preference we set nothing, so @@ -22,18 +22,25 @@ @@ -69,18 +88,28 @@ {% block main %}{% endblock main %} diff --git a/uv.lock b/uv.lock index bb955ee..aa0eebb 100644 --- a/uv.lock +++ b/uv.lock @@ -70,43 +70,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/17/1073b53b68c9b5ca6914adf5f8bf55aacc2d3be102418c90700160ea8605/cffi-2.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:cbb7640ce37159548d2147b5b8c241f962143d4c71231431820783f4dc78f210", size = 186405, upload-time = "2026-07-06T21:34:24.857Z" }, ] -[[package]] -name = "clubmanager" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "dj-database-url" }, - { name = "django" }, - { name = "django-allauth", extra = ["mfa"] }, - { name = "django-phonenumber-field", extra = ["phonenumbers"] }, - { name = "pillow" }, - { name = "python-dateutil" }, - { name = "python-decouple" }, -] - -[package.dev-dependencies] -dev = [ - { name = "coverage" }, - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "dj-database-url", specifier = ">=3.1.2" }, - { name = "django", specifier = ">=6.0.6" }, - { name = "django-allauth", extras = ["mfa"], specifier = ">=65.18.0" }, - { name = "django-phonenumber-field", extras = ["phonenumbers"], specifier = ">=8.4.0" }, - { name = "pillow", specifier = ">=12.3.0" }, - { name = "python-dateutil", specifier = ">=2.9.0.post0" }, - { name = "python-decouple", specifier = ">=3.8" }, -] - -[package.metadata.requires-dev] -dev = [ - { name = "coverage", specifier = ">=7.15.0" }, - { name = "ruff", specifier = ">=0.15.17" }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -250,6 +213,11 @@ mfa = [ { name = "qrcode" }, ] +[[package]] +name = "django-lucide" +version = "1.3.1" +source = { git = "https://github.com/bsiebens/lucide#d2cdc609c30ad09701c9d044427adef5cec87500" } + [[package]] name = "django-phonenumber-field" version = "8.4.0" @@ -380,6 +348,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/b8/d2d6d731733f51684bbf76bf34dab3b70a9148e8f2cef2bb544fccec681a/qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f", size = 45986, upload-time = "2025-05-01T15:44:22.781Z" }, ] +[[package]] +name = "rosterchief" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "dj-database-url" }, + { name = "django" }, + { name = "django-allauth", extra = ["mfa"] }, + { name = "django-lucide" }, + { name = "django-phonenumber-field", extra = ["phonenumbers"] }, + { name = "pillow" }, + { name = "python-dateutil" }, + { name = "python-decouple" }, +] + +[package.dev-dependencies] +dev = [ + { name = "coverage" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "dj-database-url", specifier = ">=3.1.2" }, + { name = "django", specifier = ">=6.0.6" }, + { name = "django-allauth", extras = ["mfa"], specifier = ">=65.18.0" }, + { name = "django-lucide", git = "https://github.com/bsiebens/lucide" }, + { name = "django-phonenumber-field", extras = ["phonenumbers"], specifier = ">=8.4.0" }, + { name = "pillow", specifier = ">=12.3.0" }, + { name = "python-dateutil", specifier = ">=2.9.0.post0" }, + { name = "python-decouple", specifier = ">=3.8" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "coverage", specifier = ">=7.15.0" }, + { name = "ruff", specifier = ">=0.15.17" }, +] + [[package]] name = "ruff" version = "0.15.17" From fed24bfee3ef1870eea85555d6705d0ff61f1ea5 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 16:42:22 +0200 Subject: [PATCH 038/137] Add club-scoped feature flags on django-waffle Adds a `features` app with a swappable waffle Flag (WAFFLE_FLAG_MODEL) that gains a m2m to Club, so a feature can be rolled out club by club. Two things worth calling out: - `everyone` keeps waffle's contract of overriding *all* other targeting, so club targeting is only consulted when `everyone is None`. This keeps `everyone = False` usable as a hard kill-switch. - m2m edits don't call save(), so waffle's per-flag cache would go stale when clubs are added or removed. A m2m_changed receiver flushes it from both directions, and get_flush_keys() drops the club-set key alongside waffle's own. Note for future flag tests: waffle's cache is not rolled back with the test transaction, so tests touching flags must clear it (see features/tests.py). Co-Authored-By: Claude Opus 4.8 --- features/__init__.py | 0 features/apps.py | 8 +++ features/migrations/0001_initial.py | 45 ++++++++++++++ features/migrations/__init__.py | 0 features/models.py | 71 +++++++++++++++++++++ features/signals.py | 26 ++++++++ features/tests.py | 96 +++++++++++++++++++++++++++++ pyproject.toml | 1 + rosterchief/settings.py | 7 +++ uv.lock | 14 +++++ 10 files changed, 268 insertions(+) create mode 100644 features/__init__.py create mode 100644 features/apps.py create mode 100644 features/migrations/0001_initial.py create mode 100644 features/migrations/__init__.py create mode 100644 features/models.py create mode 100644 features/signals.py create mode 100644 features/tests.py diff --git a/features/__init__.py b/features/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/features/apps.py b/features/apps.py new file mode 100644 index 0000000..1060681 --- /dev/null +++ b/features/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig + + +class FeaturesConfig(AppConfig): + name = "features" + + def ready(self): + from . import signals # noqa: F401 diff --git a/features/migrations/0001_initial.py b/features/migrations/0001_initial.py new file mode 100644 index 0000000..c7344f4 --- /dev/null +++ b/features/migrations/0001_initial.py @@ -0,0 +1,45 @@ +# Generated by Django 6.0.6 on 2026-07-13 14:29 + +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ('club', '0011_alter_club_slug'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Flag', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='The human/computer readable name.', max_length=100, unique=True, verbose_name='Name')), + ('everyone', models.BooleanField(blank=True, help_text='Flip this flag on (Yes) or off (No) for everyone, overriding all other settings. Leave as Unknown to use normally.', null=True, verbose_name='Everyone')), + ('percent', models.DecimalField(blank=True, decimal_places=1, help_text='A number between 0.0 and 99.9 to indicate a percentage of users for whom this flag will be active.', max_digits=3, null=True, verbose_name='Percent')), + ('testing', models.BooleanField(default=False, help_text='Allow this flag to be set for a session for user testing', verbose_name='Testing')), + ('superusers', models.BooleanField(default=True, help_text='Flag always active for superusers?', verbose_name='Superusers')), + ('staff', models.BooleanField(default=False, help_text='Flag always active for staff?', verbose_name='Staff')), + ('authenticated', models.BooleanField(default=False, help_text='Flag always active for authenticated users?', verbose_name='Authenticated')), + ('languages', models.TextField(blank=True, default='', help_text='Activate this flag for users with one of these languages (comma-separated list)', verbose_name='Languages')), + ('rollout', models.BooleanField(default=False, help_text='Activate roll-out mode?', verbose_name='Rollout')), + ('note', models.TextField(blank=True, help_text='Note where this Flag is used.', verbose_name='Note')), + ('created', models.DateTimeField(db_index=True, default=django.utils.timezone.now, help_text='Date when this Flag was created.', verbose_name='Created')), + ('modified', models.DateTimeField(default=django.utils.timezone.now, help_text='Date when this Flag was last modified.', verbose_name='Modified')), + ('clubs', models.ManyToManyField(blank=True, help_text='Activate this flag for these clubs.', related_name='flags', to='club.club', verbose_name='Clubs')), + ('groups', models.ManyToManyField(blank=True, help_text='Activate this flag for these user groups.', to='auth.group', verbose_name='Groups')), + ('users', models.ManyToManyField(blank=True, help_text='Activate this flag for these users.', to=settings.AUTH_USER_MODEL, verbose_name='Users')), + ], + options={ + 'verbose_name': 'Flag', + 'verbose_name_plural': 'Flags', + 'abstract': False, + }, + ), + ] diff --git a/features/migrations/__init__.py b/features/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/features/models.py b/features/models.py new file mode 100644 index 0000000..c3385a4 --- /dev/null +++ b/features/models.py @@ -0,0 +1,71 @@ +"""Club-scoped feature flags. + +waffle's Flag model is swappable (``WAFFLE_FLAG_MODEL``, like ``AUTH_USER_MODEL``), +so we subclass it to add the one dimension this platform actually needs: which +*clubs* a feature is on for. The tenant middleware already puts ``request.club`` +on every request, so a flag resolves with a plain ``flag_is_active(request, "shop")`` +— no call site has to know about clubs. + +Everything waffle already offers (``everyone`` / ``percent`` / ``staff`` / +``superusers`` / per-user / per-group) keeps working untouched. +""" + +from django.db import models +from django.utils.translation import gettext_lazy as _ +from waffle.models import CACHE_EMPTY, AbstractUserFlag +from waffle.utils import get_cache, keyfmt + +#: Cache key template for a flag's club ids, mirroring waffle's own +#: FLAG_USERS_CACHE_KEY / FLAG_GROUPS_CACHE_KEY. +FLAG_CLUBS_CACHE_KEY = "flag:%s:clubs" + + +class Flag(AbstractUserFlag): + clubs = models.ManyToManyField( + "club.Club", + blank=True, + related_name="flags", + help_text=_("Activate this flag for these clubs."), + verbose_name=_("Clubs"), + ) + + def get_flush_keys(self, flush_keys=None): + flush_keys = super().get_flush_keys(flush_keys) + flush_keys.append(keyfmt(FLAG_CLUBS_CACHE_KEY, self.name)) + return flush_keys + + def _get_club_ids(self) -> set: + """Club ids this flag is on for, cached the way waffle caches its own M2Ms.""" + cache = get_cache() + cache_key = keyfmt(FLAG_CLUBS_CACHE_KEY, self.name) + + cached = cache.get(cache_key) + if cached == CACHE_EMPTY: + return set() + if cached: + return cached + + club_ids = set(self.clubs.values_list("pk", flat=True)) + if not club_ids: + cache.add(cache_key, CACHE_EMPTY) + return set() + + cache.add(cache_key, club_ids) + return club_ids + + def is_active(self, request, read_only=False): + # waffle's contract: `everyone` overrides *all* other settings. So a flag + # explicitly switched off for everyone stays off even for a targeted club, + # and club targeting only applies while `everyone` is left Unknown (None). + if self.everyone is None: + club = getattr(request, "club", None) + if club is not None and club.pk in self._get_club_ids(): + return True + + return super().is_active(request, read_only=read_only) + + def is_active_for_club(self, club) -> bool: + """Explicit check for code that holds a club but no request.""" + if self.everyone is not None: + return self.everyone + return club.pk in self._get_club_ids() diff --git a/features/signals.py b/features/signals.py new file mode 100644 index 0000000..d1ce6e7 --- /dev/null +++ b/features/signals.py @@ -0,0 +1,26 @@ +"""Keep waffle's flag cache honest when club targeting changes. + +waffle caches a flag's M2M ids and only flushes on ``save()``. Editing an M2M +does not call ``save()``, so adding or removing a club would otherwise leave a +stale cached set and the flag would keep answering with the old value. +""" + +from django.db.models.signals import m2m_changed +from django.dispatch import receiver + +from .models import Flag + +FLUSH_ACTIONS = {"post_add", "post_remove", "post_clear"} + + +@receiver(m2m_changed, sender=Flag.clubs.through) +def flush_flag_club_cache(sender, instance, action, reverse, pk_set, **kwargs): + if action not in FLUSH_ACTIONS: + return + + if isinstance(instance, Flag): + instance.flush() + else: + # Reverse edit (club.flags.add(flag)): flush each flag touched. + for flag in Flag.objects.filter(pk__in=pk_set or []): + flag.flush() diff --git a/features/tests.py b/features/tests.py new file mode 100644 index 0000000..6e447df --- /dev/null +++ b/features/tests.py @@ -0,0 +1,96 @@ +from django.core.cache import cache +from django.test import RequestFactory, TestCase +from waffle import flag_is_active, get_waffle_flag_model + +from club.models import Club + +Flag = get_waffle_flag_model() + + +class ClubScopedFlagTests(TestCase): + def setUp(self): + # waffle caches flags by name, and its cache is NOT rolled back with the + # test transaction -- a flag row recreated under the same name in the next + # test would otherwise be shadowed by the previous test's cached object. + cache.clear() + self.addCleanup(cache.clear) + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.other = Club.objects.create(name="Rival FC", slug="rival-fc") + self.flag = Flag.objects.create(name="shop") + + def request_for(self, club): + request = RequestFactory().get("/") + request.club = club + request.user = None + return request + + def active_for(self, club): + return flag_is_active(self.request_for(club), "shop") + + def test_off_for_every_club_by_default(self): + self.assertFalse(self.active_for(self.club)) + self.assertFalse(self.active_for(self.other)) + + def test_on_only_for_the_targeted_club(self): + self.flag.clubs.add(self.club) + + self.assertTrue(self.active_for(self.club)) + self.assertFalse(self.active_for(self.other)) + + def test_removing_a_club_turns_it_off_again(self): + self.flag.clubs.add(self.club) + self.flag.clubs.remove(self.club) + + self.assertFalse(self.active_for(self.club)) + + def test_everyone_true_overrides_club_targeting(self): + self.flag.everyone = True + self.flag.save() + + self.assertTrue(self.active_for(self.other)) # not targeted, still on + + def test_everyone_false_beats_club_targeting(self): + # waffle's contract: `everyone` overrides ALL other settings, so a flag + # switched off for everyone must stay off even for a targeted club. + self.flag.clubs.add(self.club) + self.flag.everyone = False + self.flag.save() + + self.assertFalse(self.active_for(self.club)) + + def test_no_club_on_the_request_is_not_active(self): + # e.g. the base domain / control panel, where there is no tenant. + self.flag.clubs.add(self.club) + + self.assertFalse(flag_is_active(self.request_for(None), "shop")) + + def test_is_active_for_club_without_a_request(self): + self.flag.clubs.add(self.club) + + self.assertTrue(self.flag.is_active_for_club(self.club)) + self.assertFalse(self.flag.is_active_for_club(self.other)) + + def test_is_active_for_club_respects_everyone(self): + self.flag.everyone = False + self.flag.save() + self.assertFalse(self.flag.is_active_for_club(self.club)) + + self.flag.everyone = True + self.flag.save() + self.assertTrue(self.flag.is_active_for_club(self.club)) + + def test_cache_is_flushed_when_club_targeting_changes(self): + # The M2M does not call save(), so without the flush signal waffle would + # keep answering from a stale cached set. + self.assertFalse(self.active_for(self.club)) # primes the cache + + self.flag.clubs.add(self.club) + + self.assertTrue(self.active_for(self.club)) + + def test_cache_is_flushed_on_reverse_edit(self): + self.assertFalse(self.active_for(self.club)) + + self.club.flags.add(self.flag) + + self.assertTrue(self.active_for(self.club)) diff --git a/pyproject.toml b/pyproject.toml index 69b8739..fbfc6cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ dependencies = [ "django-allauth[mfa]>=65.18.0", "django-lucide", "django-phonenumber-field[phonenumbers]>=8.4.0", + "django-waffle>=5.0.0", "pillow>=12.3.0", "python-dateutil>=2.9.0.post0", "python-decouple>=3.8", diff --git a/rosterchief/settings.py b/rosterchief/settings.py index e6d61e7..fb1411c 100644 --- a/rosterchief/settings.py +++ b/rosterchief/settings.py @@ -58,9 +58,16 @@ INSTALLED_APPS = [ "events.apps.EventsConfig", "formbuilder.apps.FormbuilderConfig", "shop.apps.ShopConfig", + "waffle", + "features.apps.FeaturesConfig", "controlpanel.apps.ControlpanelConfig", ] +# Feature flags (django-waffle). The Flag model is swappable, like AUTH_USER_MODEL: +# ours adds a `clubs` M2M so a feature can be turned on per tenant. Because the +# tenant middleware sets request.club, `flag_is_active(request, "x")` just works. +WAFFLE_FLAG_MODEL = "features.Flag" + MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", diff --git a/uv.lock b/uv.lock index aa0eebb..476e066 100644 --- a/uv.lock +++ b/uv.lock @@ -235,6 +235,18 @@ phonenumbers = [ { name = "phonenumbers" }, ] +[[package]] +name = "django-waffle" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/e1/6f533da0d4ac89f427dfd9410e39bfc14ae3a23335ecd549d76be4b2a834/django_waffle-5.0.0.tar.gz", hash = "sha256:62f9d00eedf68dafb82657beab56e601bddedc1ea1ccfef91d83df8658708509", size = 37761, upload-time = "2025-06-12T07:38:54.895Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/d2/6f0d664bd35a3fdd0403655c7c32ec290704923f11541ef356b180cd8fbf/django_waffle-5.0.0-py3-none-any.whl", hash = "sha256:3312851d9d926b76b9e90712355781700a383b82b5bf2b61e1f1be97532c0f3d", size = 48137, upload-time = "2025-06-12T07:38:53.698Z" }, +] + [[package]] name = "fido2" version = "2.2.1" @@ -358,6 +370,7 @@ dependencies = [ { name = "django-allauth", extra = ["mfa"] }, { name = "django-lucide" }, { name = "django-phonenumber-field", extra = ["phonenumbers"] }, + { name = "django-waffle" }, { name = "pillow" }, { name = "python-dateutil" }, { name = "python-decouple" }, @@ -376,6 +389,7 @@ requires-dist = [ { name = "django-allauth", extras = ["mfa"], specifier = ">=65.18.0" }, { name = "django-lucide", git = "https://github.com/bsiebens/lucide" }, { name = "django-phonenumber-field", extras = ["phonenumbers"], specifier = ">=8.4.0" }, + { name = "django-waffle", specifier = ">=5.0.0" }, { name = "pillow", specifier = ">=12.3.0" }, { name = "python-dateutil", specifier = ">=2.9.0.post0" }, { name = "python-decouple", specifier = ">=3.8" }, From 268cbe1e0600e08b562fc8d56b3436ec88de9964 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 16:42:23 +0200 Subject: [PATCH 039/137] Manage platform admins and feature flags from the control panel Features tab: create/edit flags, flip global switches, and toggle a flag per club from the club detail page. Where `everyone` is set the per-club toggle is replaced by a badge, because a toggle there would have no effect and so would lie about what is on. Admins tab: grant, promote, demote and revoke platform access. Gated on is_superuser, not is_staff -- the panel itself is staff-accessible, so letting staff grant is_superuser would collapse the two levels into one and stop is_superuser being a boundary we can later hang anything on. Two guardrails, enforced in the service so they hold regardless of caller: you cannot strip your own access (you would lose the panel mid-click), and the last superuser can never be demoted (the platform would be locked out of itself). Granted users get an unusable password and must enrol 2FA before they can sign in. Co-Authored-By: Claude Opus 4.8 --- controlpanel/forms.py | 17 + controlpanel/mixins.py | 13 + controlpanel/services/platform_admins.py | 76 + .../templates/controlpanel/admin_form.html | 39 + .../templates/controlpanel/admins.html | 73 + controlpanel/templates/controlpanel/base.html | 4 + .../templates/controlpanel/club_detail.html | 39 + .../templates/controlpanel/features.html | 87 + .../templates/controlpanel/flag_form.html | 33 + controlpanel/tests.py | 170 + controlpanel/urls.py | 12 + controlpanel/views.py | 155 +- static/css/app.css | 3170 ++++++++++++++++- templates/base.html | 54 +- 14 files changed, 3913 insertions(+), 29 deletions(-) create mode 100644 controlpanel/services/platform_admins.py create mode 100644 controlpanel/templates/controlpanel/admin_form.html create mode 100644 controlpanel/templates/controlpanel/admins.html create mode 100644 controlpanel/templates/controlpanel/features.html create mode 100644 controlpanel/templates/controlpanel/flag_form.html diff --git a/controlpanel/forms.py b/controlpanel/forms.py index bf63cb7..7b76202 100644 --- a/controlpanel/forms.py +++ b/controlpanel/forms.py @@ -1,5 +1,6 @@ from django import forms from django.utils.translation import gettext_lazy as _ +from waffle import get_waffle_flag_model from club.models import Club @@ -35,3 +36,19 @@ class ClubAdminForm(forms.Form): self.add_error(field, _("Required: this email has no account yet.")) return cleaned + + +class PlatformAdminForm(forms.Form): + """Grant platform access to an email address, creating the account if new.""" + + email = forms.EmailField(label=_("Email address"), help_text=_("If this email has no account yet, one is created and they set a password via the reset link.")) + is_superuser = forms.BooleanField(label=_("Superuser"), required=False, help_text=_("Superusers can manage platform admins. Everyone granted access is staff.")) + + +class FlagForm(forms.ModelForm): + class Meta: + model = get_waffle_flag_model() + fields = ["name", "note", "everyone", "superusers", "staff", "percent"] + help_texts = { + "everyone": _("Yes = on for all clubs, No = off everywhere (overrides club targeting). Leave unknown to target clubs."), + } diff --git a/controlpanel/mixins.py b/controlpanel/mixins.py index 01bdcf0..d573a09 100644 --- a/controlpanel/mixins.py +++ b/controlpanel/mixins.py @@ -25,3 +25,16 @@ class PlatformStaffRequiredMixin(UserPassesTestMixin): def test_func(self): user = self.request.user return user.is_staff or user.is_superuser + + +class PlatformSuperuserRequiredMixin(PlatformStaffRequiredMixin): + """Superusers only. + + Managing platform admins is the one thing staff may not do. The panel is + gated on ``is_staff or is_superuser``, so if a staff member could grant + themselves ``is_superuser`` the two would collapse into the same thing and + ``is_superuser`` would stop being a security boundary. + """ + + def test_func(self): + return self.request.user.is_superuser diff --git a/controlpanel/services/platform_admins.py b/controlpanel/services/platform_admins.py new file mode 100644 index 0000000..1bf55f0 --- /dev/null +++ b/controlpanel/services/platform_admins.py @@ -0,0 +1,76 @@ +"""Granting and revoking platform access (is_staff / is_superuser). + +The guardrails matter more than the plumbing here: it must be impossible to lock +the platform out of itself. Two rules are enforced for every change: + +* you cannot strip your **own** access (you would lose the panel mid-click); +* the **last superuser** can never be demoted, or nobody could administer the + platform again without shell access. +""" + +from django.contrib.auth import get_user_model +from django.db import transaction +from django.db.models import Q + +User = get_user_model() + + +class PlatformAdminError(Exception): + """A change that would leave the platform unadministrable.""" + + +def platform_admins(): + return User.objects.filter(Q(is_staff=True) | Q(is_superuser=True)).order_by("email") + + +def is_last_superuser(user) -> bool: + return user.is_superuser and not User.objects.filter(is_superuser=True).exclude(pk=user.pk).exists() + + +def check_access_change(actor, user, *, is_staff: bool, is_superuser: bool) -> None: + """Raise PlatformAdminError if this change would lock someone out.""" + losing_access = not (is_staff or is_superuser) + + if actor.pk == user.pk and losing_access: + raise PlatformAdminError("You cannot remove your own platform access.") + + if actor.pk == user.pk and user.is_superuser and not is_superuser: + raise PlatformAdminError("You cannot remove your own superuser rights.") + + if user.is_superuser and not is_superuser and is_last_superuser(user): + raise PlatformAdminError("At least one superuser must remain.") + + +@transaction.atomic +def set_platform_access(actor, user, *, is_staff: bool, is_superuser: bool): + check_access_change(actor, user, is_staff=is_staff, is_superuser=is_superuser) + + # A superuser without is_staff cannot reach the panel, which is a confusing + # half-state; superuser implies staff. + user.is_staff = is_staff or is_superuser + user.is_superuser = is_superuser + user.save(update_fields=["is_staff", "is_superuser"]) + return user + + +def revoke_platform_access(actor, user): + return set_platform_access(actor, user, is_staff=False, is_superuser=False) + + +@transaction.atomic +def grant_platform_access(email, *, is_superuser: bool = False): + """Give ``email`` platform access, creating the account if it is new. + + New accounts get an unusable password — they set one through the password + reset flow — and, being staff, must enrol a second factor before they can + sign in at all. + """ + email = email.lower() + user, created = User.objects.get_or_create(email=email, defaults={"is_active": True}) + if created: + user.set_unusable_password() + + user.is_staff = True + user.is_superuser = is_superuser + user.save() + return user diff --git a/controlpanel/templates/controlpanel/admin_form.html b/controlpanel/templates/controlpanel/admin_form.html new file mode 100644 index 0000000..896aafb --- /dev/null +++ b/controlpanel/templates/controlpanel/admin_form.html @@ -0,0 +1,39 @@ +{% extends "controlpanel/base.html" %} +{% load lucide ui %} + +{% block heading %}Grant platform access{% endblock heading %} + +{% block panel %} +
+
+
+ Platform admins can manage every club. They must set up two-factor authentication before they can sign in. +
+
+ {% csrf_token %} + {% for error in form.non_field_errors %} +
{{ error }}
+ {% endfor %} + {% for field in form %} +
+ {% if field.field.widget.input_type == "checkbox" %} + + {% else %} + + {{ field|daisy }} + {% endif %} + {% if field.help_text %}{{ field.help_text }}{% endif %} + {% for error in field.errors %}{{ error }}{% endfor %} +
+ {% endfor %} +
+ Cancel + +
+ +
+
+{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/admins.html b/controlpanel/templates/controlpanel/admins.html new file mode 100644 index 0000000..70472d0 --- /dev/null +++ b/controlpanel/templates/controlpanel/admins.html @@ -0,0 +1,73 @@ +{% extends "controlpanel/base.html" %} +{% load lucide %} + +{% block heading %}Platform admins{% endblock heading %} + +{% block subheading %} +

Staff run the panel. Superusers additionally manage this list.

+{% endblock subheading %} + +{% block actions %} + {% lucide "user-plus" size=16 %} Grant access +{% endblock actions %} + +{% block panel %} +
+
+
+
{% csrf_token %} - +
+ + + + + + + + + + + {% for admin in admins %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
UserStaffSuperuserLast login
+
{{ admin.email }}
+ {% if admin.pk == user.pk %}
That's you
{% endif %} +
+
+ {% csrf_token %} + + + +
+
+
+ {% csrf_token %} + + + +
+
{{ admin.last_login|date:"j M Y"|default:"Never" }} +
+ {% csrf_token %} + +
+
No platform admins.
+
+
+
+{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/base.html b/controlpanel/templates/controlpanel/base.html index 900eb06..f79004d 100644 --- a/controlpanel/templates/controlpanel/base.html +++ b/controlpanel/templates/controlpanel/base.html @@ -20,6 +20,10 @@ {% block panel %}{% endblock panel %} {% endblock main %} diff --git a/controlpanel/templates/controlpanel/club_detail.html b/controlpanel/templates/controlpanel/club_detail.html index db8d54d..cca65d6 100644 --- a/controlpanel/templates/controlpanel/club_detail.html +++ b/controlpanel/templates/controlpanel/club_detail.html @@ -50,6 +50,45 @@
{% endfor %}
+
+
+
+

{% lucide "toggle-right" size=18 %} Features

+ Manage features +
+
+ + + {% for entry in flags %} + + + + + + {% empty %} + + + + {% endfor %} + +
{{ entry.flag.name }}{{ entry.flag.note|default:"—" }} + {% if entry.overridden %} + {# `everyone` overrides club targeting, so a per-club toggle would be a lie. #} + + {% if entry.flag.everyone %}On for all clubs{% else %}Off everywhere{% endif %} + + {% else %} +
+ {% csrf_token %} + +
+ {% endif %} +
No features defined yet.
+
+
+
diff --git a/controlpanel/templates/controlpanel/features.html b/controlpanel/templates/controlpanel/features.html new file mode 100644 index 0000000..7c931df --- /dev/null +++ b/controlpanel/templates/controlpanel/features.html @@ -0,0 +1,87 @@ +{% extends "controlpanel/base.html" %} +{% load lucide %} + +{% block heading %}Features{% endblock heading %} + +{% block actions %} + {% lucide "plus" size=16 %} New feature +{% endblock actions %} + +{% block panel %} +
+
+

{% lucide "flag" size=18 %} Flags

+

+ Flags are turned on per club. Setting Everyone to Yes or No overrides club targeting entirely. +

+
+ + + + + + + + + + + + {% for flag in flags %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
NameEveryoneClubsNote
{{ flag.name }} + {% if flag.everyone is True %} + On for all + {% elif flag.everyone is False %} + Off everywhere + {% else %} + Per club + {% endif %} + {{ flag.clubs.count }}{{ flag.note|default:"—" }} + {% lucide "pencil" size=14 %} Edit +
No features yet.
+
+
+
+
+
+

{% lucide "power" size=18 %} Switches

+

Global on/off for the whole platform — kill-switches, maintenance, infra rollouts.

+
+ + + {% for switch in switches %} + + + + + + {% empty %} + + + + {% endfor %} + +
{{ switch.name }}{{ switch.note|default:"—" }} +
+ {% csrf_token %} + +
+
No switches yet — add one in the Django admin.
+
+
+
+{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/flag_form.html b/controlpanel/templates/controlpanel/flag_form.html new file mode 100644 index 0000000..56c4f23 --- /dev/null +++ b/controlpanel/templates/controlpanel/flag_form.html @@ -0,0 +1,33 @@ +{% extends "controlpanel/base.html" %} +{% load ui %} + +{% block heading %}{% if object %}Edit {{ object.name }}{% else %}New feature{% endif %}{% endblock heading %} + +{% block panel %} +
+
+
+ {% csrf_token %} + {% for error in form.non_field_errors %} +
+ {{ error }} +
+ {% endfor %} + {% for field in form %} +
+ + {{ field|daisy }} + {% if field.help_text %}{{ field.help_text }}{% endif %} + {% for error in field.errors %}{{ error }}{% endfor %} +
+ {% endfor %} +
+ Cancel + +
+
+
+
+{% endblock panel %} diff --git a/controlpanel/tests.py b/controlpanel/tests.py index 02ebbf4..c2c5338 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -3,9 +3,11 @@ from decimal import Decimal from allauth.mfa.models import Authenticator from django import forms from django.contrib.auth import get_user_model +from django.core.cache import cache from django.test import TestCase, override_settings from django.urls import reverse from django.utils import timezone +from waffle import get_waffle_flag_model, get_waffle_switch_model from club.models import Club, ClubMembership, ClubRole, Season from members.models import Member @@ -13,10 +15,13 @@ from shop.models import Order from teams.models import Position, Team, TeamMembership from .services.admins import grant_club_admin +from .services.platform_admins import PlatformAdminError, is_last_superuser, set_platform_access from .services.statistics import club_statistics, clubs_with_totals, platform_totals from .templatetags.ui import daisy User = get_user_model() +Flag = get_waffle_flag_model() +Switch = get_waffle_switch_model() def enrol_mfa(user): @@ -253,3 +258,168 @@ class DaisyFilterTests(TestCase): def test_invalid_fields_get_an_error_class(self): self.assertIn("input-error", self.rendered("text", data={})) + + +class PlatformAdminAccessTests(ControlPanelTestBase): + """Managing platform admins is superuser-only: the panel is gated on + is_staff OR is_superuser, so letting staff grant superuser would collapse + the two into one privilege level.""" + + def test_staff_cannot_reach_the_admins_section(self): + self.assertEqual(self.client.get(reverse("controlpanel:admins")).status_code, 403) + + def test_staff_cannot_grant_platform_access(self): + self.assertEqual(self.client.get(reverse("controlpanel:admin_add")).status_code, 403) + + def test_the_admins_tab_is_hidden_from_staff(self): + self.assertNotContains(self.client.get(reverse("controlpanel:dashboard")), reverse("controlpanel:admins")) + + +class PlatformAdminTests(TestCase): + def setUp(self): + self.root = User.objects.create_superuser(email="root@example.com", password="pw-secret-123") + enrol_mfa(self.root) + self.client.force_login(self.root) + + def test_superuser_sees_the_admins_section(self): + self.assertEqual(self.client.get(reverse("controlpanel:admins")).status_code, 200) + + def test_the_grant_form_renders(self): + self.assertEqual(self.client.get(reverse("controlpanel:admin_add")).status_code, 200) + + def test_grant_access_to_a_new_email_creates_a_staff_account(self): + self.client.post(reverse("controlpanel:admin_add"), {"email": "New.Admin@Example.com"}) + + user = User.objects.get(email="new.admin@example.com") + self.assertTrue(user.is_staff) + self.assertFalse(user.is_superuser) + self.assertFalse(user.has_usable_password()) # set via password reset + + def test_grant_superuser(self): + self.client.post(reverse("controlpanel:admin_add"), {"email": "boss@example.com", "is_superuser": "1"}) + + user = User.objects.get(email="boss@example.com") + self.assertTrue(user.is_superuser) + self.assertTrue(user.is_staff) # superuser implies staff, else they can't reach the panel + + def test_promote_and_demote_another_admin(self): + other = User.objects.create_user(email="other@example.com", password="pw-secret-123", is_staff=True) + + self.client.post(reverse("controlpanel:admin_update", args=[other.pk]), {"is_staff": "1", "is_superuser": "1"}) + other.refresh_from_db() + self.assertTrue(other.is_superuser) + + self.client.post(reverse("controlpanel:admin_update", args=[other.pk]), {"is_staff": "1", "is_superuser": "0"}) + other.refresh_from_db() + self.assertFalse(other.is_superuser) + + def test_revoke_another_admins_access(self): + other = User.objects.create_user(email="other@example.com", password="pw-secret-123", is_staff=True) + + self.client.post(reverse("controlpanel:admin_revoke", args=[other.pk])) + + other.refresh_from_db() + self.assertFalse(other.is_staff) + self.assertFalse(other.is_superuser) + + # --- guardrails: it must be impossible to lock the platform out of itself --- + def test_cannot_revoke_your_own_access(self): + response = self.client.post(reverse("controlpanel:admin_revoke", args=[self.root.pk]), follow=True) + + self.root.refresh_from_db() + self.assertTrue(self.root.is_superuser) + self.assertContains(response, "cannot remove your own platform access") + + def test_cannot_remove_your_own_superuser_rights(self): + # Keep another superuser around so this is blocked by the self-rule, not + # by the last-superuser rule. + User.objects.create_superuser(email="spare@example.com", password="pw-secret-123") + + response = self.client.post(reverse("controlpanel:admin_update", args=[self.root.pk]), {"is_staff": "1", "is_superuser": "0"}, follow=True) + + self.root.refresh_from_db() + self.assertTrue(self.root.is_superuser) + self.assertContains(response, "cannot remove your own superuser rights") + + def test_the_last_superuser_cannot_be_demoted(self): + other = User.objects.create_superuser(email="other@example.com", password="pw-secret-123") + # Now demote self is blocked by the self-rule; demote `other` is fine... + self.client.post(reverse("controlpanel:admin_update", args=[other.pk]), {"is_staff": "1", "is_superuser": "0"}) + other.refresh_from_db() + self.assertFalse(other.is_superuser) + + # ...leaving root as the last superuser, who now cannot be demoted by anyone. + self.assertTrue(is_last_superuser(self.root)) + with self.assertRaises(PlatformAdminError): + set_platform_access(other, self.root, is_staff=True, is_superuser=False) + + def test_last_superuser_rule_is_enforced_for_other_actors_too(self): + response = self.client.post(reverse("controlpanel:admin_revoke", args=[self.root.pk]), follow=True) + + self.root.refresh_from_db() + self.assertTrue(self.root.is_superuser) + self.assertContains(response, "cannot remove your own platform access") + + +class FeatureViewTests(ControlPanelTestBase): + def setUp(self): + super().setUp() + cache.clear() + self.addCleanup(cache.clear) + self.flag = Flag.objects.create(name="shop") + + def test_features_page_lists_flags_and_switches(self): + Switch.objects.create(name="maintenance", active=False) + + response = self.client.get(reverse("controlpanel:features")) + + self.assertContains(response, "shop") + self.assertContains(response, "maintenance") + + def test_the_flag_forms_render(self): + self.assertEqual(self.client.get(reverse("controlpanel:flag_create")).status_code, 200) + self.assertEqual(self.client.get(reverse("controlpanel:flag_update", args=[self.flag.pk])).status_code, 200) + + def test_create_a_flag(self): + self.client.post(reverse("controlpanel:flag_create"), {"name": "news", "note": "News module", "percent": "", "everyone": ""}) + + self.assertTrue(Flag.objects.filter(name="news").exists()) + + def test_edit_a_flag(self): + self.client.post(reverse("controlpanel:flag_update", args=[self.flag.pk]), {"name": "shop", "note": "Webshop", "percent": "", "everyone": ""}) + + self.flag.refresh_from_db() + self.assertEqual(self.flag.note, "Webshop") + + def test_toggle_a_feature_on_and_off_for_a_club(self): + url = reverse("controlpanel:club_feature_toggle", args=[self.club.pk, self.flag.pk]) + + self.client.post(url) + self.assertTrue(self.flag.clubs.filter(pk=self.club.pk).exists()) + + self.client.post(url) + self.assertFalse(self.flag.clubs.filter(pk=self.club.pk).exists()) + + def test_toggle_a_switch(self): + switch = Switch.objects.create(name="maintenance", active=False) + + self.client.post(reverse("controlpanel:switch_toggle", args=[switch.pk])) + + switch.refresh_from_db() + self.assertTrue(switch.active) + + def test_club_detail_offers_a_toggle_per_feature(self): + response = self.client.get(reverse("controlpanel:club_detail", args=[self.club.pk])) + + self.assertContains(response, "shop") + self.assertContains(response, reverse("controlpanel:club_feature_toggle", args=[self.club.pk, self.flag.pk])) + + def test_an_everyone_flag_shows_a_badge_instead_of_a_club_toggle(self): + # `everyone` overrides club targeting, so offering a per-club toggle would lie. + self.flag.everyone = True + self.flag.save() + + response = self.client.get(reverse("controlpanel:club_detail", args=[self.club.pk])) + + self.assertContains(response, "On for all clubs") + self.assertNotContains(response, reverse("controlpanel:club_feature_toggle", args=[self.club.pk, self.flag.pk])) diff --git a/controlpanel/urls.py b/controlpanel/urls.py index 5662b50..dc1e25d 100644 --- a/controlpanel/urls.py +++ b/controlpanel/urls.py @@ -6,6 +6,7 @@ app_name = "controlpanel" urlpatterns = [ path("", views.DashboardView.as_view(), name="dashboard"), + # Clubs path("clubs/", views.ClubListView.as_view(), name="club_list"), path("clubs/new/", views.ClubCreateView.as_view(), name="club_create"), path("clubs//", views.ClubDetailView.as_view(), name="club_detail"), @@ -14,4 +15,15 @@ urlpatterns = [ path("clubs//restore/", views.ClubRestoreView.as_view(), name="club_restore"), path("clubs//admins/add/", views.ClubAdminAddView.as_view(), name="club_admin_add"), path("clubs//admins//remove/", views.ClubAdminRemoveView.as_view(), name="club_admin_remove"), + path("clubs//features//toggle/", views.ClubFeatureToggleView.as_view(), name="club_feature_toggle"), + # Features + path("features/", views.FeatureListView.as_view(), name="features"), + path("features/flags/new/", views.FlagCreateView.as_view(), name="flag_create"), + path("features/flags//edit/", views.FlagUpdateView.as_view(), name="flag_update"), + path("features/switches//toggle/", views.SwitchToggleView.as_view(), name="switch_toggle"), + # Platform admins (superusers only) + path("admins/", views.PlatformAdminListView.as_view(), name="admins"), + path("admins/add/", views.PlatformAdminAddView.as_view(), name="admin_add"), + path("admins//update/", views.PlatformAdminUpdateView.as_view(), name="admin_update"), + path("admins//revoke/", views.PlatformAdminRevokeView.as_view(), name="admin_revoke"), ] diff --git a/controlpanel/views.py b/controlpanel/views.py index 54f44db..289237c 100644 --- a/controlpanel/views.py +++ b/controlpanel/views.py @@ -1,15 +1,27 @@ from django.contrib import messages +from django.contrib.auth import get_user_model from django.shortcuts import get_object_or_404, redirect from django.urls import reverse from django.views.generic import CreateView, DetailView, FormView, ListView, TemplateView, UpdateView, View +from waffle import get_waffle_flag_model, get_waffle_switch_model from club.models import Club, ClubRole -from .forms import ClubAdminForm, ClubForm -from .mixins import PlatformStaffRequiredMixin +from .forms import ClubAdminForm, ClubForm, FlagForm, PlatformAdminForm +from .mixins import PlatformStaffRequiredMixin, PlatformSuperuserRequiredMixin from .services.admins import grant_club_admin, revoke_club_admin +from .services.platform_admins import ( + PlatformAdminError, + grant_platform_access, + platform_admins, + revoke_platform_access, + set_platform_access, +) from .services.statistics import club_statistics, clubs_with_totals, platform_totals +Flag = get_waffle_flag_model() +Switch = get_waffle_switch_model() + class DashboardView(PlatformStaffRequiredMixin, TemplateView): template_name = "controlpanel/dashboard.html" @@ -80,6 +92,7 @@ class ClubDetailView(PlatformStaffRequiredMixin, DetailView): nav="clubs", groups=club_statistics(self.object), admins=ClubRole.objects.filter(club=self.object, role=ClubRole.Roles.ADMIN).select_related("member", "member__user"), + flags=flags_for_club(self.object), **kwargs, ) @@ -126,3 +139,141 @@ class ClubAdminRemoveView(PlatformStaffRequiredMixin, View): revoke_club_admin(role) messages.warning(request, f"{member} is no longer an admin of this club.") return redirect("controlpanel:club_detail", pk=pk) + + +def flags_for_club(club): + """Every flag, annotated with whether it is on for this club and why.""" + enabled_ids = set(club.flags.values_list("pk", flat=True)) + return [ + { + "flag": flag, + "enabled": flag.pk in enabled_ids, + # `everyone` overrides club targeting, so the per-club toggle is moot. + "overridden": flag.everyone is not None, + } + for flag in Flag.objects.order_by("name") + ] + + +class ClubFeatureToggleView(PlatformStaffRequiredMixin, View): + """Turn a feature on or off for one club.""" + + def post(self, request, pk, flag_pk): + club = get_object_or_404(Club, pk=pk) + flag = get_object_or_404(Flag, pk=flag_pk) + + if flag.clubs.filter(pk=club.pk).exists(): + flag.clubs.remove(club) + messages.warning(request, f"“{flag.name}” turned off for {club}.") + else: + flag.clubs.add(club) + messages.success(request, f"“{flag.name}” turned on for {club}.") + + return redirect("controlpanel:club_detail", pk=club.pk) + + +class FeatureListView(PlatformStaffRequiredMixin, TemplateView): + template_name = "controlpanel/features.html" + + def get_context_data(self, **kwargs): + return super().get_context_data( + nav="features", + flags=Flag.objects.prefetch_related("clubs").order_by("name"), + switches=Switch.objects.order_by("name"), + **kwargs, + ) + + +class FlagCreateView(PlatformStaffRequiredMixin, CreateView): + model = Flag + form_class = FlagForm + template_name = "controlpanel/flag_form.html" + success_url = None + + def get_context_data(self, **kwargs): + return super().get_context_data(nav="features", **kwargs) + + def form_valid(self, form): + response = super().form_valid(form) + messages.success(self.request, f"Feature “{self.object.name}” created.") + return response + + def get_success_url(self): + return reverse("controlpanel:features") + + +class FlagUpdateView(PlatformStaffRequiredMixin, UpdateView): + model = Flag + form_class = FlagForm + template_name = "controlpanel/flag_form.html" + + def get_context_data(self, **kwargs): + return super().get_context_data(nav="features", **kwargs) + + def form_valid(self, form): + response = super().form_valid(form) + messages.success(self.request, f"Feature “{self.object.name}” updated.") + return response + + def get_success_url(self): + return reverse("controlpanel:features") + + +class SwitchToggleView(PlatformStaffRequiredMixin, View): + """Global kill-switch: on or off for the whole platform.""" + + def post(self, request, pk): + switch = get_object_or_404(Switch, pk=pk) + switch.active = not switch.active + switch.save() + messages.success(request, f"Switch “{switch.name}” is now {'on' if switch.active else 'off'}.") + return redirect("controlpanel:features") + + +class PlatformAdminListView(PlatformSuperuserRequiredMixin, TemplateView): + template_name = "controlpanel/admins.html" + + def get_context_data(self, **kwargs): + return super().get_context_data(nav="admins", admins=platform_admins(), **kwargs) + + +class PlatformAdminAddView(PlatformSuperuserRequiredMixin, FormView): + form_class = PlatformAdminForm + template_name = "controlpanel/admin_form.html" + + def get_context_data(self, **kwargs): + return super().get_context_data(nav="admins", **kwargs) + + def form_valid(self, form): + user = grant_platform_access(form.cleaned_data["email"], is_superuser=form.cleaned_data["is_superuser"]) + messages.success(self.request, f"{user.email} now has platform access. They must set up two-factor authentication before they can sign in.") + return redirect("controlpanel:admins") + + +class PlatformAdminUpdateView(PlatformSuperuserRequiredMixin, View): + def post(self, request, pk): + user = get_object_or_404(get_user_model(), pk=pk) + try: + set_platform_access( + request.user, + user, + is_staff=request.POST.get("is_staff") == "1", + is_superuser=request.POST.get("is_superuser") == "1", + ) + except PlatformAdminError as error: + messages.error(request, str(error)) + else: + messages.success(request, f"Updated platform access for {user.email}.") + return redirect("controlpanel:admins") + + +class PlatformAdminRevokeView(PlatformSuperuserRequiredMixin, View): + def post(self, request, pk): + user = get_object_or_404(get_user_model(), pk=pk) + try: + revoke_platform_access(request.user, user) + except PlatformAdminError as error: + messages.error(request, str(error)) + else: + messages.warning(request, f"{user.email} no longer has platform access.") + return redirect("controlpanel:admins") diff --git a/static/css/app.css b/static/css/app.css index e59aa20..b421c9f 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -1,2 +1,3170 @@ /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--container-5xl:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax:"";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tab:is(.tabs>.tab){cursor:pointer;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:is(.tabs>.tab):hover{color:var(--color-base-content)}}.tab:is(.tabs>.tab){--tab-p:.75rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));--tab-radius-limit:min(var(--radius-field), var(--tab-radius-min));--tab-radius-grad:#0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + .25px), var(--tab-border-color) 69%, var(--tab-bg) calc(69% + .25px);order:var(--tab-order);height:var(--tab-height);padding-inline:var(--tab-p);border-color:#0000;font-size:.875rem}.tab:is(.tabs>.tab):is(input[type=radio]){min-width:fit-content}.tab:is(.tabs>.tab):is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:is(.tabs>.tab):is(label){position:relative}.tab:is(.tabs>.tab):is(label) input{cursor:pointer;appearance:none;opacity:0;position:absolute;inset:0}:is(.tab:is(.tabs>.tab):checked,.tab:is(.tabs>.tab):is(label:has(:checked)),.tab:is(.tabs>.tab):is(.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]))+.tab-content{display:block}.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.tab:is(.tabs>.tab):not(input):empty{cursor:default;flex-grow:1}.tab:is(.tabs>.tab):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:is(.tabs>.tab):focus{outline-offset:2px;outline:2px solid #0000}}.tab:is(.tabs>.tab):focus-visible,.tab:is(.tabs>.tab):is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab:is(.tabs>.tab)[disabled]{pointer-events:none;opacity:.4}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem) * 10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}}@layer daisyui.l1.l2{.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.btn-circle{width:var(--size);height:var(--size);border-radius:3.40282e38px;padding-inline:0}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}.input-error,.input-error:focus,.input-error:focus-within{--input-color:var(--color-error)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.visible{visibility:visible}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.static{position:static}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.table{display:table}.min-h-screen{min-height:100vh}.w-56{width:calc(var(--spacing) * 56)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-5xl{max-width:var(--container-5xl)}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.flex-none{flex:none}.cursor-pointer{cursor:pointer}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.overflow-x-auto{overflow-x:auto}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.border{border-style:var(--tw-border-style);border-width:1px}.border-base-300{border-color:var(--color-base-300)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-right{text-align:right}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file +@layer properties; +@layer theme, base, components, utilities; +@layer theme { + :root, :host { + --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + "Courier New", monospace; + --color-black: #000; + --spacing: 0.25rem; + --container-xs: 20rem; + --container-xl: 36rem; + --container-5xl: 64rem; + --text-xs: 0.75rem; + --text-xs--line-height: calc(1 / 0.75); + --text-sm: 0.875rem; + --text-sm--line-height: calc(1.25 / 0.875); + --text-base: 1rem; + --text-base--line-height: calc(1.5 / 1); + --text-lg: 1.125rem; + --text-lg--line-height: calc(1.75 / 1.125); + --text-xl: 1.25rem; + --text-xl--line-height: calc(1.75 / 1.25); + --text-2xl: 1.5rem; + --text-2xl--line-height: calc(2 / 1.5); + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + --default-font-family: var(--font-sans); + --default-mono-font-family: var(--font-mono); + } +} +@layer base { + *, ::after, ::before, ::backdrop, ::file-selector-button { + box-sizing: border-box; + margin: 0; + padding: 0; + border: 0 solid; + } + html, :host { + line-height: 1.5; + -webkit-text-size-adjust: 100%; + tab-size: 4; + font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); + font-feature-settings: var(--default-font-feature-settings, normal); + font-variation-settings: var(--default-font-variation-settings, normal); + -webkit-tap-highlight-color: transparent; + } + hr { + height: 0; + color: inherit; + border-top-width: 1px; + } + abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + } + h1, h2, h3, h4, h5, h6 { + font-size: inherit; + font-weight: inherit; + } + a { + color: inherit; + -webkit-text-decoration: inherit; + text-decoration: inherit; + } + b, strong { + font-weight: bolder; + } + code, kbd, samp, pre { + font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); + font-feature-settings: var(--default-mono-font-feature-settings, normal); + font-variation-settings: var(--default-mono-font-variation-settings, normal); + font-size: 1em; + } + small { + font-size: 80%; + } + sub, sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; + } + sub { + bottom: -0.25em; + } + sup { + top: -0.5em; + } + table { + text-indent: 0; + border-color: inherit; + border-collapse: collapse; + } + :-moz-focusring { + outline: auto; + } + progress { + vertical-align: baseline; + } + summary { + display: list-item; + } + ol, ul, menu { + list-style: none; + } + img, svg, video, canvas, audio, iframe, embed, object { + display: block; + vertical-align: middle; + } + img, video { + max-width: 100%; + height: auto; + } + button, input, select, optgroup, textarea, ::file-selector-button { + font: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + letter-spacing: inherit; + color: inherit; + border-radius: 0; + background-color: transparent; + opacity: 1; + } + :where(select:is([multiple], [size])) optgroup { + font-weight: bolder; + } + :where(select:is([multiple], [size])) optgroup option { + padding-inline-start: 20px; + } + ::file-selector-button { + margin-inline-end: 4px; + } + ::placeholder { + opacity: 1; + } + @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) { + ::placeholder { + color: currentcolor; + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, currentcolor 50%, transparent); + } + } + } + textarea { + resize: vertical; + } + ::-webkit-search-decoration { + -webkit-appearance: none; + } + ::-webkit-date-and-time-value { + min-height: 1lh; + text-align: inherit; + } + ::-webkit-datetime-edit { + display: inline-flex; + } + ::-webkit-datetime-edit-fields-wrapper { + padding: 0; + } + ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { + padding-block: 0; + } + ::-webkit-calendar-picker-indicator { + line-height: 1; + } + :-moz-ui-invalid { + box-shadow: none; + } + button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button { + appearance: button; + } + ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { + height: auto; + } + [hidden]:where(:not([hidden="until-found"])) { + display: none !important; + } +} +@layer utilities { + .tab { + @layer daisyui.l1.l2.l3 { + &:is(.tabs > .tab) { + position: relative; + display: inline-flex; + cursor: pointer; + appearance: none; + flex-wrap: wrap; + align-items: center; + justify-content: center; + text-align: center; + webkit-user-select: none; + user-select: none; + &:hover { + @media (hover: hover) { + color: var(--color-base-content); + } + } + --tab-p: 0.75rem; + --tab-bg: var(--color-base-100); + --tab-border-color: var(--color-base-300); + --tab-radius-ss: 0; + --tab-radius-se: 0; + --tab-radius-es: 0; + --tab-radius-ee: 0; + --tab-order: 0; + --tab-radius-min: calc(0.75rem - var(--border)); + --tab-radius-limit: min(var(--radius-field), var(--tab-radius-min)); + --tab-radius-grad: #0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + 0.25px), + var(--tab-border-color) 69%, var(--tab-bg) calc(69% + 0.25px); + border-color: #0000; + order: var(--tab-order); + height: var(--tab-height); + font-size: 0.875rem; + padding-inline: var(--tab-p); + &:is(input[type="radio"]) { + min-width: fit-content; + &:after { + --tw-content: attr(aria-label); + content: var(--tw-content); + } + } + &:is(label) { + position: relative; + input { + position: absolute; + inset: 0; + cursor: pointer; + appearance: none; + opacity: 0%; + } + } + &:checked, &:is(label:has(:checked)), &:is(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]) { + & + .tab-content { + display: block; + } + } + &:not(:checked, label:has(:checked), :hover, .tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"] ) { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 50%, transparent); + } + } + &:not(input):empty { + flex-grow: 1; + cursor: default; + } + &:focus { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &:focus-visible, &:is(label:has(:checked:focus-visible)) { + outline: 2px solid currentColor; + outline-offset: -5px; + } + &[disabled] { + pointer-events: none; + opacity: 40%; + } + } + } + } + .menu { + @layer daisyui.l1.l2.l3 { + display: flex; + width: fit-content; + flex-direction: column; + flex-wrap: wrap; + padding: calc(0.25rem * 2); + --menu-active-fg: var(--color-neutral-content); + --menu-active-bg: var(--color-neutral); + font-size: 0.875rem; + :where(li ul, li menu) { + position: relative; + margin-inline-start: calc(0.25rem * 4); + padding-inline-start: calc(0.25rem * 2); + white-space: nowrap; + &:before { + position: absolute; + inset-inline-start: calc(0.25rem * 0); + top: calc(0.25rem * 3); + bottom: calc(0.25rem * 3); + background-color: var(--color-base-content); + opacity: 10%; + width: var(--border); + content: ""; + } + } + :where(li > .menu-dropdown:not(.menu-dropdown-show)) { + display: none; + } + :where(li:not(.menu-title) > *:not(ul, menu, details, .menu-title, .btn)), :where(li:not(.menu-title) > details > summary:not(.menu-title)) { + display: grid; + grid-auto-flow: column; + align-content: flex-start; + align-items: center; + gap: calc(0.25rem * 2); + border-radius: var(--radius-field); + padding-inline: calc(0.25rem * 3); + padding-block: calc(0.25rem * 1.5); + text-align: start; + transition-property: color, background-color, box-shadow; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + grid-auto-columns: minmax(auto, max-content) auto max-content; + user-select: none; + } + :where(li > details > summary) { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + &::-webkit-details-marker { + display: none; + } + } + :where(li > details > summary), :where(li > .menu-dropdown-toggle) { + &:after { + justify-self: flex-end; + display: block; + height: 0.375rem; + width: 0.375rem; + rotate: -135deg; + translate: 0 -1px; + transition-property: rotate, translate; + transition-duration: 0.2s; + content: ""; + transform-origin: 50% 50%; + box-shadow: 2px 2px inset; + pointer-events: none; + } + } + details { + overflow: hidden; + interpolate-size: allow-keywords; + } + details::details-content { + block-size: 0; + @media (prefers-reduced-motion: no-preference) { + transition-behavior: allow-discrete; + transition-property: block-size, content-visibility; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + } + } + details[open]::details-content { + block-size: auto; + } + :where(li > details[open] > summary):after, :where(li > .menu-dropdown-toggle.menu-dropdown-show):after { + rotate: 45deg; + translate: 0 1px; + } + :where(li:not(.menu-title, .disabled) > *:not(ul, menu, details, .menu-title), li:not(.menu-title, .disabled) > details > summary:not(.menu-title) ):not(.menu-active, :active, .btn) { + &.menu-focus, &:focus-visible { + cursor: pointer; + background-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); + } + color: var(--color-base-content); + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + } + :where(li:not(.menu-title, .disabled) > *:not(ul, menu, details, .menu-title):not(.menu-active, :active, .btn):hover, li:not(.menu-title, .disabled) > details > summary:not(.menu-title):not(.menu-active, :active, .btn):hover ) { + cursor: pointer; + background-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); + } + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + box-shadow: 0 1px oklch(0% 0 0 / 0.01) inset, 0 -1px oklch(100% 0 0 / 0.01) inset; + } + :where(li:empty) { + background-color: var(--color-base-content); + opacity: 10%; + margin: 0.5rem 1rem; + height: 1px; + } + :where(li) { + position: relative; + display: flex; + flex-shrink: 0; + flex-direction: column; + flex-wrap: wrap; + align-items: stretch; + .badge { + justify-self: flex-end; + } + & > *:not(ul, menu, .menu-title, details, .btn):active, & > *:not(ul, menu, .menu-title, details, .btn).menu-active, & > details > summary:active { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + color: var(--menu-active-fg); + background-color: var(--menu-active-bg); + background-size: auto, calc(var(--noise) * 100%); + background-image: none, var(--fx-noise); + &:not(&:active) { + box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg); + } + } + &.menu-disabled, [disabled] { + pointer-events: none; + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 20%, transparent); + } + } + } + .dropdown:focus-within { + .menu-dropdown-toggle:after { + rotate: 45deg; + translate: 0 1px; + } + } + .dropdown-content { + margin-top: calc(0.25rem * 2); + padding: calc(0.25rem * 2); + &:before { + display: none; + } + } + } + } + .collapse-arrow { + @layer daisyui.l1.l2 { + > .collapse-title:after { + position: absolute; + display: block; + height: 0.5rem; + width: 0.5rem; + transform: translateY(-100%) rotate(45deg); + @media (prefers-reduced-motion: no-preference) { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 0.2s; + } + top: 50%; + inset-inline-end: 1.4rem; + content: ""; + transform-origin: 75% 75%; + box-shadow: 2px 2px; + pointer-events: none; + } + } + } + .dropdown { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-block; + position-area: var(--anchor-v, bottom) var(--anchor-h, span-right); + & > *:not(:has( ~ [class*="dropdown-content"])):focus { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + .dropdown-content { + position: absolute; + } + &.dropdown-close .dropdown-content, &:not(details, .dropdown-open, .dropdown-hover:hover, :focus-within) .dropdown-content, &.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible) ~ .dropdown-content { + display: none; + transform-origin: top; + opacity: 0%; + scale: 95%; + } + &[popover], .dropdown-content { + z-index: 999; + @media (prefers-reduced-motion: no-preference) { + animation: dropdown 0.2s; + transition-property: opacity, scale, display, overlay; + transition-behavior: allow-discrete; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + } + } + @starting-style { + &[popover], .dropdown-content { + scale: 95%; + opacity: 0; + } + } + &:not(.dropdown-close) { + &.dropdown-open, &:not(.dropdown-hover):focus, &:focus-within { + > [tabindex]:first-child { + pointer-events: none; + } + .dropdown-content { + opacity: 100%; + scale: 100%; + } + } + &.dropdown-hover:hover { + .dropdown-content { + opacity: 100%; + scale: 100%; + } + } + } + &:is(details) { + overflow: revert-layer; + summary { + &::-webkit-details-marker { + display: none; + } + } + } + &:where([popover]) { + background: #0000; + } + &[popover] { + position: fixed; + color: inherit; + @supports not (position-area: bottom) { + margin: auto; + &.dropdown-close, &.dropdown-open:not(:popover-open) { + display: none; + transform-origin: top; + opacity: 0%; + scale: 95%; + } + &::backdrop { + background-color: color-mix(in oklab, #000 30%, #0000); + } + } + &.dropdown-close, &:not(.dropdown-open, :popover-open) { + display: none; + transform-origin: top; + opacity: 0%; + scale: 95%; + } + } + } + } + .btn { + :where(&) { + @layer daisyui.l1.l2.l3 { + width: unset; + } + } + .prose :where(a&:not(.btn-link)):not(:where([class~="not-prose"], [class~="not-prose"] *)) { + text-decoration-line: none; + } + @layer daisyui.l1.l2.l3 { + --size: calc(var(--size-field, 0.25rem) * 10); + --btn-p: 1rem; + --btn-fg: var(--color-base-content); + display: inline-flex; + flex-shrink: 0; + cursor: pointer; + flex-wrap: nowrap; + align-items: center; + justify-content: center; + gap: calc(0.25rem * 1.5); + text-align: center; + vertical-align: middle; + outline-offset: 2px; + webkit-user-select: none; + user-select: none; + font-weight: 600; + border-start-start-radius: var(--join-ss, var(--radius-field)); + border-start-end-radius: var(--join-se, var(--radius-field)); + border-end-start-radius: var(--join-es, var(--radius-field)); + border-end-end-radius: var(--join-ee, var(--radius-field)); + border-width: var(--border); + touch-action: manipulation; + transition-property: color, background-color, border-color, box-shadow, transform; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-duration: 0.2s; + --btn-bg: var(--btn-color, var(--color-base-200)); + --btn-border: var(--btn-color, var(--color-base-200)); + @supports (color: color-mix(in lab, red, red)) { + --btn-border: color-mix( + in oklab, + var(--btn-color, var(--color-base-200)), + #000 calc(var(--depth) * 5%) + ); + } + --btn-soft-bg: initial; + --btn-shadow: 0 3px 2px -2px var(--btn-bg), + 0 4px 3px -2px var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), + 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); + } + --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); + height: var(--size); + padding-inline: var(--btn-p); + font-size: var(--fontsize, 0.875rem); + background-color: var(--btn-bg); + color: var(--btn-fg); + border-color: var(--btn-border); + border-style: var(--btn-border-style, solid); + outline-color: var(--btn-color, var(--color-base-content)); + --tw-prose-links: var(--btn-fg); + background-image: none, var(--fx-noise); + background-size: auto, calc(var(--noise, 0) * 100%); + text-shadow: 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 0.15)); + box-shadow: var(--btn-inset) inset, var(--btn-shadow); + &:is([type="checkbox"], [type="radio"]) { + appearance: none; + &[aria-label]::after { + --tw-content: attr(aria-label); + content: var(--tw-content); + } + } + } + @layer daisyui.l1 { + &:where(:checked:not(.filter [type="radio"].btn)) { + --btn-color: var(--color-primary); + --btn-fg: var(--color-primary-content); + } + @media (hover: hover) { + &:hover { + --btn-bg: var(--btn-color, var(--color-base-200)); + @supports (color: color-mix(in lab, red, red)) { + --btn-bg: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 7%); + } + color: var(--btn-fg); + --btn-border: var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-border: color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%)); + } + --btn-border-style: solid; + --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); + --btn-shadow: 0 3px 2px -2px var(--btn-bg), + 0 4px 3px -2px var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), + 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); + } + } + } + &:active:not(.btn-active) { + translate: 0 0.5px; + --btn-bg: var(--btn-color, var(--color-base-200)); + @supports (color: color-mix(in lab, red, red)) { + --btn-bg: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 5%); + } + color: var(--btn-fg, var(--color-base-content)); + --btn-border: var(--btn-color, var(--color-base-200)); + @supports (color: color-mix(in lab, red, red)) { + --btn-border: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 7%); + } + --btn-border-style: solid; + --btn-inset: 0 0 0 0 oklch(0% 0 0/0); + --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); + } + &:where(:checked:not(.filter [type="radio"].btn), :not([type="radio"], [type="checkbox"]):focus-visible ) { + --btn-bg: var(--btn-color, var(--color-base-200)); + color: var(--btn-fg, var(--color-base-content)); + --btn-border: var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-border: color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%)); + } + --btn-border-style: solid; + --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); + --btn-shadow: 0 3px 2px -2px var(--btn-bg), + 0 4px 3px -2px var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), + 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); + } + isolation: isolate; + } + &:focus-visible, &:has(:focus-visible) { + outline-width: 2px; + outline-style: solid; + isolation: isolate; + } + } + &:is(:disabled, [disabled], [aria-disabled="true"]) { + @layer daisyui { + pointer-events: none; + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklch, var(--color-base-content) 20%, #0000); + } + --btn-bg: #0000; + --btn-border: #0000; + background-image: none; + --btn-inset: 0 0 0 0 oklch(0% 0 0/0); + --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); + &:not(.btn-link, .btn-ghost) { + background-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); + } + } + } + } + } + .collapse { + &:not(td, tr, colgroup) { + visibility: revert-layer; + } + @layer daisyui.l1.l2.l3 { + display: grid; + position: relative; + border-radius: var(--radius-box, 1rem); + width: 100%; + grid-template-rows: max-content 0fr; + grid-template-columns: minmax(0, 1fr); + isolation: isolate; + @media (prefers-reduced-motion: no-preference) { + transition: grid-template-rows 0.2s; + } + > input:is([type="checkbox"], [type="radio"]) { + grid-column-start: 1; + grid-row-start: 1; + appearance: none; + opacity: 0; + z-index: 1; + width: 100%; + padding: 1rem; + padding-inline-end: 3rem; + min-height: 1lh; + transition: background-color 0.2s ease-out; + } + &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ), &:not(.collapse-close):has( > input:is([type="checkbox"], [type="radio"]):checked) { + grid-template-rows: max-content 1fr; + } + &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ) > .collapse-content, &:not(.collapse-close) > :where(input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-content) { + --overflow-delay: 0.2s; + overflow: revert-layer; + content-visibility: visible; + min-height: fit-content; + @supports not (content-visibility: visible) { + visibility: visible; + } + } + &:focus-visible, &:has( > input:is([type="checkbox"], [type="radio"]):focus-visible), &:has(summary:focus-visible) { + outline-color: var(--color-base-content); + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; + } + &:not(.collapse-close) { + > input[type="checkbox"], > input[type="radio"]:not(:checked), > .collapse-title { + cursor: pointer; + } + } + &[tabindex]:focus:not(.collapse-close, .collapse[open]), &[tabindex]:focus-within:not(.collapse-close, .collapse[open]) { + > .collapse-title { + cursor: unset; + } + } + &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ) > :where(.collapse-content), &:not(.collapse-close) > :where(input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-content) { + padding-bottom: 1rem; + } + &:is(details) { + width: 100%; + &::details-content { + --overflow-delay: 0s; + overflow: clip; + height: 0; + } + &:where([open])::details-content { + overflow: revert-layer; + height: auto; + } + @media (prefers-reduced-motion: no-preference) { + &::details-content { + transition: overflow 0.2s allow-discrete var(--overflow-delay), content-visibility 0.2s allow-discrete, visibility 0.2s allow-discrete, min-height 0.2s ease-out allow-discrete, padding 0.1s ease-out 20ms, background-color 0.2s ease-out, height 0.2s; + interpolate-size: allow-keywords; + } + &:where([open])::details-content { + --overflow-delay: 0.2s; + } + } + > summary { + position: relative; + display: block; + outline: none; + &::-webkit-details-marker { + display: none; + } + } + } + } + @layer daisyui.l1.l2 { + &:is([open]) { + &.collapse-arrow { + > .collapse-title:after { + @media (prefers-reduced-motion: no-preference) { + transform: translateY(-50%) rotate(225deg); + } + } + } + } + &.collapse-open { + &.collapse-arrow { + > .collapse-title:after { + @media (prefers-reduced-motion: no-preference) { + transform: translateY(-50%) rotate(225deg); + } + } + } + &.collapse-plus { + > .collapse-title:after { + --tw-content: "−"; + content: var(--tw-content); + } + } + } + &[tabindex].collapse-arrow:focus:not(.collapse-close), &.collapse-arrow[tabindex]:focus-within:not(.collapse-close) { + > .collapse-title:after { + transform: translateY(-50%) rotate(225deg); + } + } + &.collapse-arrow:not(.collapse-close) { + > input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-title:after { + transform: translateY(-50%) rotate(225deg); + } + } + &[open] { + &.collapse-plus { + > .collapse-title:after { + --tw-content: "−"; + content: var(--tw-content); + } + } + } + &[tabindex].collapse-plus:focus:not(.collapse-close) { + > .collapse-title:after { + --tw-content: "−"; + content: var(--tw-content); + } + } + &.collapse-plus:not(.collapse-close) { + > input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-title:after { + --tw-content: "−"; + content: var(--tw-content); + } + } + } + } + .collapse-content { + @layer daisyui.l1.l2.l3 { + --overflow-delay: 0s; + content-visibility: hidden; + overflow: clip; + grid-column-start: 1; + grid-row-start: 2; + min-height: 0; + padding-left: 1rem; + padding-right: 1rem; + cursor: unset; + @supports not (content-visibility: hidden) { + visibility: hidden; + } + @media (prefers-reduced-motion: no-preference) { + transition: overflow 0.2s allow-discrete var(--overflow-delay), content-visibility 0.2s allow-discrete, visibility 0.2s allow-discrete, min-height 0.2s ease-out allow-discrete, padding 0.1s ease-out 20ms, background-color 0.2s ease-out; + } + details > & { + content-visibility: visible; + } + } + } + .filter { + @layer daisyui.l1.l2.l3 { + display: flex; + flex-wrap: wrap; + [type="radio"] { + width: auto; + } + input { + overflow: hidden; + opacity: 100%; + scale: 1; + transition: visibility 0.1s allow-discrete, margin 0.1s, opacity 0.3s, padding 0.3s, border-width 0.1s; + &.filter-reset { + aspect-ratio: 1 / 1; + &::after { + --tw-content: "×"; + content: var(--tw-content); + } + } + } + > input:not(:last-child), > :not(:last-child) input { + margin-inline-end: 0.25rem; + } + } + @layer daisyui.l1 { + &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible) { + visibility: hidden; + } + &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible), &:not(:has(:focus-visible)):has(:checked:not(.filter-reset, [type="checkbox"])) :is(input, button):not(:checked, .filter-reset, [type="reset"]) { + margin-inline: 0; + width: 0; + padding-inline: 0; + opacity: 0%; + scale: 0; + border-width: 0; + } + } + } + .collapse { + visibility: collapse; + } + .visible { + visibility: visible; + } + .list { + @layer daisyui.l1.l2.l3 { + display: flex; + flex-direction: column; + font-size: 0.875rem; + .list-row { + --list-grid-cols: minmax(0, auto) 1fr; + position: relative; + display: grid; + grid-auto-flow: column; + gap: calc(0.25rem * 4); + border-radius: var(--radius-box); + padding: calc(0.25rem * 4); + word-break: break-word; + grid-template-columns: var(--list-grid-cols); + } + & > :not(:last-child) { + &.list-row, .list-row { + &:after { + content: ""; + border-bottom: var(--border) solid; + inset-inline: var(--radius-box); + position: absolute; + bottom: 0; + border-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + border-color: color-mix(in oklab, var(--color-base-content) 5%, transparent); + } + } + } + } + } + @layer daisyui.l1.l2 { + .list-row { + &:has(.list-col-grow:nth-child(1)) { + --list-grid-cols: 1fr; + } + &:has(.list-col-grow:nth-child(2)) { + --list-grid-cols: minmax(0, auto) 1fr; + } + &:has(.list-col-grow:nth-child(3)) { + --list-grid-cols: minmax(0, auto) minmax(0, auto) 1fr; + } + &:has(.list-col-grow:nth-child(4)) { + --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr; + } + &:has(.list-col-grow:nth-child(5)) { + --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr; + } + &:has(.list-col-grow:nth-child(6)) { + --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) + minmax(0, auto) 1fr; + } + > * { + grid-row-start: 1; + } + } + } + } + .toggle { + @layer daisyui.l1.l2.l3 { + border: var(--border) solid currentColor; + color: var(--input-color); + position: relative; + display: inline-grid; + flex-shrink: 0; + cursor: pointer; + appearance: none; + place-content: center; + vertical-align: middle; + webkit-user-select: none; + user-select: none; + grid-template-columns: 0fr 1fr 1fr; + --radius-selector-max: calc( + var(--radius-selector) + var(--radius-selector) + var(--radius-selector) + ); + border-radius: calc( var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)) ); + padding: var(--toggle-p); + box-shadow: 0 1px currentColor inset; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset; + } + transition: color 0.3s, grid-template-columns 0.2s; + --input-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --input-color: color-mix(in oklab, var(--color-base-content) 50%, #0000); + } + --toggle-p: calc(var(--size) * 0.125); + --size: calc(var(--size-selector, 0.25rem) * 6); + width: calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2); + height: var(--size); + > * { + z-index: 1; + grid-column: span 1 / span 1; + grid-column-start: 2; + grid-row-start: 1; + height: 100%; + cursor: pointer; + appearance: none; + background-color: transparent; + padding: calc(0.25rem * 0.5); + transition: opacity 0.2s, rotate 0.4s; + border: none; + &:focus { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &:nth-child(2) { + color: var(--color-base-100); + rotate: 0deg; + } + &:nth-child(3) { + color: var(--color-base-100); + opacity: 0%; + rotate: -15deg; + } + } + &:has(:checked) { + > :nth-child(2) { + opacity: 0%; + rotate: 15deg; + } + > :nth-child(3) { + opacity: 100%; + rotate: 0deg; + } + } + &:before { + position: relative; + inset-inline-start: calc(0.25rem * 0); + grid-column-start: 2; + grid-row-start: 1; + aspect-ratio: 1 / 1; + height: 100%; + width: 100%; + border-radius: var(--radius-selector); + background-color: currentcolor; + translate: 0; + --tw-content: ""; + content: var(--tw-content); + transition: background-color 0.1s, translate 0.2s, inset-inline-start 0.2s; + box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px currentColor; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000); + } + background-size: auto, calc(var(--noise) * 100%); + background-image: none, var(--fx-noise); + } + @media (forced-colors: active) { + &:before { + outline-style: var(--tw-outline-style); + outline-width: 1px; + outline-offset: calc(1px * -1); + } + } + @media print { + &:before { + outline: 0.25rem solid; + outline-offset: -1rem; + } + } + &:focus-visible, &:has(:focus-visible) { + outline: 2px solid currentColor; + outline-offset: 2px; + } + &:checked, &[aria-checked="true"], &:has( > input:checked) { + grid-template-columns: 1fr 1fr 0fr; + background-color: var(--color-base-100); + --input-color: var(--color-base-content); + &:before { + background-color: currentcolor; + } + @starting-style { + &:before { + opacity: 0; + } + } + } + &:indeterminate { + grid-template-columns: 0.5fr 1fr 0.5fr; + } + &:disabled { + cursor: not-allowed; + opacity: 30%; + &:before { + background-color: transparent; + border: var(--border) solid currentColor; + } + } + } + } + .input { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-flex; + flex-shrink: 1; + appearance: none; + align-items: center; + gap: calc(0.25rem * 2); + background-color: var(--color-base-100); + padding-inline: calc(0.25rem * 3); + vertical-align: middle; + white-space: nowrap; + --size: calc(var(--size-field, 0.25rem) * var(--in-size-mul)); + --input-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + cursor: text; + width: clamp(3rem, 20rem, 100%); + height: var(--size); + font-size: max(var(--font-size, 0rem), var(--font-size-min)); + touch-action: manipulation; + border-start-start-radius: var(--join-ss, var(--radius-field)); + border-start-end-radius: var(--join-se, var(--radius-field)); + border-end-start-radius: var(--join-es, var(--radius-field)); + border-end-end-radius: var(--join-ee, var(--radius-field)); + border: var(--border) solid var(--input-color, #0000); + box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + } + input { + height: 100%; + width: 100%; + appearance: none; + background-color: transparent; + border: none; + &::placeholder { + color: var(--color-base-content); + opacity: 50%; + } + &:focus, &:focus-within { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &::-webkit-calendar-picker-indicator { + inset-inline-end: -0.15em; + } + } + &::-webkit-inner-spin-button { + margin-inline-end: -10px; + } + &::-webkit-calendar-picker-indicator { + inset-inline-end: 0.75em; + } + input&, input { + position: relative; + display: inline-flex; + &[type="url"], &[type="email"] { + text-align: left; + } + &::-webkit-datetime-edit, &::-webkit-date-and-time-value { + display: grid; + min-height: 100%; + align-items: center; + text-align: inherit; + } + &::-webkit-inner-spin-button { + margin-block: calc(0.25rem * var(--spin-my)); + } + &::-webkit-calendar-picker-indicator { + position: absolute; + width: 1em; + height: 1em; + cursor: pointer; + } + &::-webkit-color-swatch-wrapper { + padding-block: 0.25rem; + } + } + &:focus, &:focus-within { + --input-color: var(--color-base-content); + box-shadow: 0 1px var(--input-color); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); + } + outline: 2px solid var(--input-color); + outline-offset: 2px; + isolation: isolate; + } + @media (pointer: coarse) { + @supports (-webkit-touch-callout: none) { + &:focus, &:focus-within { + --font-size: 1rem; + } + } + } + &:has( > input[disabled]), &:is(:disabled, [disabled]), fieldset:disabled & { + cursor: not-allowed; + border-color: var(--color-base-200); + background-color: var(--color-base-200); + &:is(input), :is(input) { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 40%, transparent); + } + } + box-shadow: none; + &::placeholder, ::placeholder { + color: var(--color-base-content); + opacity: 20%; + } + } + &:has( > input[disabled]) > input[disabled] { + cursor: not-allowed; + } + } + @layer daisyui.l1.l2 { + --in-size-mul: 10; + --font-size-min: 0.875rem; + --spin-my: -3; + .floating-label:has(&) { + --top-mul: 5; + --font-size: 0.875rem; + } + } + } + .table { + @layer daisyui.l1.l2.l3 { + font-size: 0.875rem; + position: relative; + width: 100%; + border-collapse: separate; + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); + border-radius: var(--radius-box); + text-align: left; + &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) { + text-align: right; + } + tr.row-hover { + &, &:nth-child(even) { + &:hover { + @media (hover: hover) { + background-color: var(--color-base-200); + } + } + } + } + :where(th, td) { + padding-inline: calc(0.25rem * 4); + padding-block: calc(0.25rem * 3); + vertical-align: middle; + } + :where(thead, tfoot) { + white-space: nowrap; + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 60%, transparent); + } + font-size: 0.875rem; + font-weight: 600; + } + :where(tfoot tr:first-child :is(td, th)) { + border-top: var(--border) solid var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + border-top: var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000); + } + } + :where(.table-pin-rows thead tr) { + position: sticky; + top: 0; + z-index: 1; + background-color: var(--color-base-100); + } + :where(.table-pin-rows tfoot tr) { + position: sticky; + bottom: 0; + z-index: 1; + background-color: var(--color-base-100); + } + :where(.table-pin-cols tr th) { + position: sticky; + right: 0; + left: 0; + background-color: var(--color-base-100); + } + :where(thead tr :is(td, th), tbody tr:not(:last-child) :is(td, th)) { + border-bottom: var(--border) solid var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + border-bottom: var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000); + } + } + } + } + .select { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-flex; + flex-shrink: 1; + appearance: none; + align-items: center; + gap: calc(0.25rem * 1.5); + background-color: var(--color-base-100); + padding-inline-start: calc(0.25rem * 3); + padding-inline-end: calc(0.25rem * 7); + vertical-align: middle; + --size: calc(var(--size-field, 0.25rem) * var(--sl-size-mul)); + --input-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + width: clamp(3rem, 20rem, 100%); + height: var(--size); + font-size: max(var(--font-size, 0rem), var(--font-size-min)); + touch-action: manipulation; + border-start-start-radius: var(--join-ss, var(--radius-field)); + border-start-end-radius: var(--join-se, var(--radius-field)); + border-end-start-radius: var(--join-es, var(--radius-field)); + border-end-end-radius: var(--join-ee, var(--radius-field)); + background-image: linear-gradient(45deg, #0000 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, #0000 50%); + background-position: calc(100% - 20px) calc(1px + 50%), calc(100% - 16.1px) calc(1px + 50%); + background-size: 4px 4px, 4px 4px; + background-repeat: no-repeat; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + border: var(--border) solid var(--input-color, #0000); + box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + } + [dir="rtl"] & { + background-position: calc(0% + 12px) calc(1px + 50%), calc(0% + 16px) calc(1px + 50%); + &::picker(select), select::picker(select) { + translate: 0.5rem 0; + } + } + &[multiple] { + height: auto; + overflow: auto; + padding-block: calc(0.25rem * 3); + padding-inline-end: calc(0.25rem * 3); + background-image: none; + } + select { + margin-inline-start: calc(0.25rem * -3); + margin-inline-end: calc(0.25rem * -7); + width: calc(100% + 2.75rem); + appearance: none; + padding-inline-start: calc(0.25rem * 3); + padding-inline-end: calc(0.25rem * 7); + height: calc(100% - calc(var(--border) * 2)); + align-items: center; + background: inherit; + border-radius: inherit; + border-style: none; + &::placeholder { + color: var(--color-base-content); + opacity: 50%; + } + &:focus, &:focus-within { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &:not(:last-child) { + margin-inline-end: calc(0.25rem * -5.5); + background-image: none; + } + } + &:focus, &:focus-within, &:open { + --input-color: var(--color-base-content); + box-shadow: 0 1px var(--input-color); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); + } + outline: 2px solid var(--input-color); + outline-offset: 2px; + isolation: isolate; + background-image: linear-gradient(135deg, #0000 50%, currentColor 50%), linear-gradient(45deg, currentColor 50%, #0000 50%); + } + &:has( > select[disabled]), &:is(:disabled, [disabled]), fieldset:disabled & { + cursor: not-allowed; + border-color: var(--color-base-200); + background-color: var(--color-base-200); + &:is(select), :is(select) { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 40%, transparent); + } + } + box-shadow: none; + &::placeholder, ::placeholder { + color: var(--color-base-content); + opacity: 20%; + } + } + &:has( > select[disabled]) > select[disabled] { + cursor: not-allowed; + } + &, & select { + @supports (appearance: base-select) { + &, &::picker(select) { + appearance: base-select; + } + } + &::picker(select) { + color: inherit; + max-height: min(24rem, 70dvh); + margin-inline: 0.5rem; + translate: -0.5rem 0; + border: var(--border) solid var(--color-base-200); + margin-block: calc(0.25rem * 2); + border-radius: var(--radius-box); + padding: calc(0.25rem * 2); + background-color: inherit; + box-shadow: 0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/0.2); + box-shadow: 0 20px 25px -5px rgb(0 0 0 / calc(var(--depth) * 0.1)), 0 8px 10px -6px rgb(0 0 0 / calc(var(--depth) * 0.1)); + } + &::picker-icon { + display: none; + } + selectedcontent { + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + optgroup { + padding-top: 0.5em; + option { + &:nth-child(1) { + margin-top: 0.5em; + } + } + } + option { + border-radius: var(--radius-field); + padding-block: calc(0.25rem * 1.5); + padding-inline: calc(0.25rem * var(--option-px)); + transition-property: color, background-color; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + white-space: normal; + &:not(:disabled) { + &:hover, &:focus-visible { + cursor: pointer; + background-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); + } + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &:active { + background-color: var(--color-neutral); + color: var(--color-neutral-content); + box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--color-neutral); + } + } + } + } + } + @layer daisyui.l1.l2 { + --sl-size-mul: 10; + --font-size-min: 0.875rem; + --option-px: 3; + .floating-label:has(&) { + --top-mul: 5; + --font-size: 0.875rem; + } + } + } + .collapse-title { + @layer daisyui.l1.l2.l3 { + grid-column-start: 1; + grid-row-start: 1; + position: relative; + width: 100%; + padding: 1rem; + padding-inline-end: 3rem; + min-height: 1lh; + transition: background-color 0.2s ease-out; + } + } + .checkbox { + @layer daisyui.l1.l2.l3 { + border: var(--border) solid var(--input-color, var(--color-base-content)); + @supports (color: color-mix(in lab, red, red)) { + border: var(--border) solid var(--input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000)); + } + position: relative; + display: inline-block; + flex-shrink: 0; + cursor: pointer; + appearance: none; + border-radius: var(--radius-selector); + padding: 0.25rem; + vertical-align: middle; + color: var(--color-base-content); + box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 0 #0000 inset, 0 0 #0000; + transition: background-color 0.2s, box-shadow 0.2s; + --size: calc(var(--size-selector, 0.25rem) * 6); + width: var(--size); + height: var(--size); + background-size: auto, calc(var(--noise) * 100%); + background-image: none, var(--fx-noise); + &:before { + --tw-content: ""; + content: var(--tw-content); + display: block; + width: 100%; + height: 100%; + rotate: 45deg; + background-color: currentcolor; + opacity: 0%; + transition: clip-path 0.3s, opacity 0.1s, rotate 0.3s, translate 0.3s; + transition-delay: 0.1s; + clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 70% 80%, 70% 100%); + box-shadow: 0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + font-size: 1rem; + line-height: 0.75; + } + &:focus-visible { + outline: 2px solid var(--input-color, currentColor); + outline-offset: 2px; + } + &:checked, &[aria-checked="true"] { + background-color: var(--input-color, #0000); + box-shadow: 0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); + &:before { + clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 0%, 70% 0%, 70% 100%); + opacity: 100%; + } + @media (forced-colors: active) { + &:before { + rotate: 0deg; + background-color: transparent; + --tw-content: "✔︎"; + clip-path: none; + } + } + @media print { + &:before { + rotate: 0deg; + background-color: transparent; + --tw-content: "✔︎"; + clip-path: none; + } + } + } + &:indeterminate { + background-color: var(--input-color, var(--color-base-content) ); + @supports (color: color-mix(in lab, red, red)) { + background-color: var(--input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000) ); + } + &:before { + rotate: 0deg; + opacity: 100%; + translate: 0 -35%; + clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 80% 80%, 80% 100%); + } + } + } + &:disabled { + @layer daisyui.l1.l2 { + cursor: not-allowed; + opacity: 20%; + } + } + } + .radio { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-block; + flex-shrink: 0; + cursor: pointer; + appearance: none; + border-radius: calc(infinity * 1px); + padding: 0.25rem; + vertical-align: middle; + border: var(--border) solid var(--input-color, currentColor); + @supports (color: color-mix(in lab, red, red)) { + border: var(--border) solid var(--input-color, color-mix(in srgb, currentColor 20%, #0000)); + } + box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset; + --size: calc(var(--size-selector, 0.25rem) * 6); + width: var(--size); + height: var(--size); + color: var(--input-color, currentColor); + &:before { + display: block; + width: 100%; + height: 100%; + border-radius: calc(infinity * 1px); + --tw-content: ""; + content: var(--tw-content); + background-size: auto, calc(var(--noise) * 100%); + background-image: none, var(--fx-noise); + } + &:focus-visible { + outline: 2px solid currentColor; + } + &:checked, &[aria-checked="true"] { + border-color: currentcolor; + background-color: var(--color-base-100); + @media (prefers-reduced-motion: no-preference) { + animation: radio 0.2s ease-out; + } + &:before { + background-color: currentcolor; + box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); + } + @media (forced-colors: active) { + &:before { + outline-style: var(--tw-outline-style); + outline-width: 1px; + outline-offset: calc(1px * -1); + } + } + @media print { + &:before { + outline: 0.25rem solid; + outline-offset: -1rem; + } + } + } + } + &:disabled { + @layer daisyui.l1.l2 { + cursor: not-allowed; + opacity: 20%; + } + } + } + .navbar { + @layer daisyui.l1.l2.l3 { + display: flex; + width: 100%; + align-items: center; + padding: 0.5rem; + min-height: 4rem; + } + :where(&) { + @layer daisyui.l1.l2 { + position: relative; + } + } + } + .card { + @layer daisyui.l1.l2.l3 { + position: relative; + display: flex; + flex-direction: column; + border-radius: var(--radius-box); + transition: outline 0.2s ease-in-out; + outline: 2px solid #0000; + outline-offset: 2px; + &:focus-visible, &[aria-checked="true"], &:has( > :checked, > :is([type="checkbox"], [type="radio"]):focus-visible) { + outline-color: currentColor; + } + &:has( > :checked:focus-visible), &[aria-checked="true"]:focus-visible, &[aria-checked="true"]:has( > :is([type="checkbox"], [type="radio"]):focus-visible) { + outline-width: 4px; + } + &:has( > :is([type="checkbox"], [type="radio"])) { + cursor: pointer; + user-select: none; + } + > :is([type="checkbox"], [type="radio"]) { + appearance: none; + } + } + @layer daisyui.l1.l2.l3.l4 { + figure:first-child { + overflow: hidden; + border-start-start-radius: inherit; + border-start-end-radius: inherit; + border-end-start-radius: unset; + border-end-end-radius: unset; + } + figure:last-child { + overflow: hidden; + border-start-start-radius: unset; + border-start-end-radius: unset; + border-end-start-radius: inherit; + border-end-end-radius: inherit; + } + figure { + display: flex; + align-items: center; + justify-content: center; + } + } + } + .stats { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-grid; + grid-auto-flow: column; + overflow-x: auto; + border-radius: var(--radius-box); + } + } + .static { + position: static; + } + .dropdown-end { + @layer daisyui.l1.l2 { + --anchor-h: span-left; + :where(.dropdown-content) { + inset-inline-end: calc(0.25rem * 0); + translate: 0 0; + [dir="rtl"] & { + translate: 0 0; + } + } + &.dropdown-left { + --anchor-h: left; + --anchor-v: span-top; + .dropdown-content { + top: auto; + bottom: 0; + } + } + &.dropdown-right { + --anchor-h: right; + --anchor-v: span-top; + .dropdown-content { + top: auto; + bottom: 0; + } + } + } + } + .textarea { + @layer daisyui.l1.l2.l3 { + min-height: calc(0.25rem * 20); + flex-shrink: 1; + appearance: none; + border-radius: var(--radius-field); + background-color: var(--color-base-100); + padding-inline: calc(0.25rem * 3); + padding-block: calc(0.25rem * 2); + vertical-align: middle; + --input-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + width: clamp(3rem, 20rem, 100%); + font-size: max(var(--font-size, 0rem), var(--font-size-min)); + touch-action: manipulation; + border: var(--border) solid var(--input-color, #0000); + box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + } + textarea { + appearance: none; + background-color: transparent; + border: none; + &::placeholder { + color: var(--color-base-content); + opacity: 50%; + } + &:focus, &:focus-within { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + } + &:focus, &:focus-within { + --input-color: var(--color-base-content); + box-shadow: 0 1px var(--input-color); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); + } + outline: 2px solid var(--input-color); + outline-offset: 2px; + isolation: isolate; + } + @media (pointer: coarse) { + @supports (-webkit-touch-callout: none) { + &:focus, &:focus-within { + --font-size: 1rem; + } + } + } + &:has( > textarea[disabled]), &:is(:disabled, [disabled]) { + cursor: not-allowed; + border-color: var(--color-base-200); + background-color: var(--color-base-200); + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 40%, transparent); + } + box-shadow: none; + &::placeholder, ::placeholder { + color: var(--color-base-content); + opacity: 20%; + } + } + &:has( > textarea[disabled]) > textarea[disabled] { + cursor: not-allowed; + } + } + @layer daisyui.l1.l2 { + --font-size-min: 0.875rem; + .floating-label:has(&) { + --top-mul: 5; + --font-size: 0.875rem; + } + } + } + .stack { + @layer daisyui.l1.l2.l3 { + display: inline-grid; + grid-template-columns: 3px 4px 1fr 4px 3px; + grid-template-rows: 3px 4px 1fr 4px 3px; + & > * { + height: 100%; + width: 100%; + &:nth-child(n + 2) { + width: 100%; + opacity: 70%; + } + &:nth-child(2) { + z-index: 2; + opacity: 90%; + } + &:nth-child(1) { + z-index: 3; + width: 100%; + } + } + } + @layer daisyui.l1.l2 { + &, &.stack-bottom { + > * { + grid-column: 3 / 4; + grid-row: 3 / 6; + &:nth-child(2) { + grid-column: 2 / 5; + grid-row: 2 / 5; + } + &:nth-child(1) { + grid-column: 1 / 6; + grid-row: 1 / 4; + } + } + } + &.stack-top { + > * { + grid-column: 3 / 4; + grid-row: 1 / 4; + &:nth-child(2) { + grid-column: 2 / 5; + grid-row: 2 / 5; + } + &:nth-child(1) { + grid-column: 1 / 6; + grid-row: 3 / 6; + } + } + } + &.stack-start { + > * { + grid-column: 1 / 4; + grid-row: 3 / 4; + &:nth-child(2) { + grid-column: 2 / 5; + grid-row: 2 / 5; + } + &:nth-child(1) { + grid-column: 3 / 6; + grid-row: 1 / 6; + } + } + } + &.stack-end { + > * { + grid-column: 3 / 6; + grid-row: 3 / 4; + &:nth-child(2) { + grid-column: 2 / 5; + grid-row: 2 / 5; + } + &:nth-child(1) { + grid-column: 1 / 4; + grid-row: 1 / 6; + } + } + } + } + } + .z-10 { + z-index: 10; + } + .hero { + @layer daisyui.l1.l2.l3 { + display: grid; + width: 100%; + place-items: center; + background-size: cover; + background-position: center; + & > * { + grid-column-start: 1; + grid-row-start: 1; + } + } + } + .stat-value { + @layer daisyui.l1.l2.l3 { + grid-column-start: 1; + white-space: nowrap; + font-size: 2rem; + font-weight: 800; + } + } + .stat-title { + @layer daisyui.l1.l2.l3 { + grid-column-start: 1; + white-space: nowrap; + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 60%, transparent); + } + font-size: 0.75rem; + } + } + .container { + width: 100%; + @media (width >= 40rem) { + max-width: 40rem; + } + @media (width >= 48rem) { + max-width: 48rem; + } + @media (width >= 64rem) { + max-width: 64rem; + } + @media (width >= 80rem) { + max-width: 80rem; + } + @media (width >= 96rem) { + max-width: 96rem; + } + } + .divider { + @layer daisyui.l1.l2.l3 { + display: flex; + height: calc(0.25rem * 4); + flex-direction: row; + align-items: center; + align-self: stretch; + white-space: nowrap; + margin: var(--divider-m, 1rem 0); + --divider-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --divider-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); + } + &:before, &:after { + content: ""; + height: calc(0.25rem * 0.5); + width: 100%; + flex-grow: 1; + background-color: var(--divider-color); + } + @media print { + &:before, &:after { + border: 0.5px solid; + } + } + &:not(:empty) { + gap: calc(0.25rem * 4); + } + } + } + .mx-auto { + margin-inline: auto; + } + .my-2 { + margin-block: calc(var(--spacing) * 2); + } + .my-3 { + margin-block: calc(var(--spacing) * 3); + } + .my-4 { + margin-block: calc(var(--spacing) * 4); + } + .label { + @layer daisyui.l1.l2.l3 { + display: inline-flex; + align-items: center; + gap: calc(0.25rem * 1.5); + white-space: nowrap; + color: currentcolor; + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, currentcolor 60%, transparent); + } + &:has(input) { + cursor: pointer; + } + &:is(.input > *, .select > *) { + display: flex; + height: calc(100% - 0.5rem); + align-items: center; + padding-inline: calc(0.25rem * 3); + white-space: nowrap; + font-size: inherit; + &:first-child { + margin-inline-start: calc(0.25rem * -3); + margin-inline-end: calc(0.25rem * 3); + border-inline-end: var(--border) solid currentColor; + @supports (color: color-mix(in lab, red, red)) { + border-inline-end: var(--border) solid color-mix(in oklab, currentColor 10%, #0000); + } + } + &:last-child { + margin-inline-start: calc(0.25rem * 3); + margin-inline-end: calc(0.25rem * -3); + border-inline-start: var(--border) solid currentColor; + @supports (color: color-mix(in lab, red, red)) { + border-inline-start: var(--border) solid color-mix(in oklab, currentColor 10%, #0000); + } + } + } + } + } + .mt-1 { + margin-top: var(--spacing); + } + .mt-2 { + margin-top: calc(var(--spacing) * 2); + } + .mt-4 { + margin-top: calc(var(--spacing) * 4); + } + .mb-4 { + margin-bottom: calc(var(--spacing) * 4); + } + .mb-6 { + margin-bottom: calc(var(--spacing) * 6); + } + .ml-2 { + margin-left: calc(var(--spacing) * 2); + } + .status { + @layer daisyui.l1.l2.l3 { + display: inline-block; + aspect-ratio: 1 / 1; + width: calc(0.25rem * 2); + height: calc(0.25rem * 2); + border-radius: var(--radius-selector); + background-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-base-content) 20%, transparent); + } + background-position: center; + background-repeat: no-repeat; + vertical-align: middle; + color: color-mix(in srgb, #000 30%, transparent); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-black) 30%, transparent); + } + background-image: radial-gradient( circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * 0.5)), #0000 ); + box-shadow: 0 2px 3px -1px currentColor; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000); + } + } + } + .badge { + @layer daisyui.l1.l2.l3 { + display: inline-flex; + align-items: center; + justify-content: center; + gap: calc(0.25rem * 2); + border-radius: var(--radius-selector); + vertical-align: middle; + color: var(--badge-fg); + border: var(--border) solid var(--badge-color, var(--color-base-200)); + font-size: 0.875rem; + width: fit-content; + background-size: auto, calc(var(--noise) * 100%); + background-image: none, var(--fx-noise); + background-color: var(--badge-bg); + --badge-bg: var(--badge-color, var(--color-base-100)); + --badge-fg: var(--color-base-content); + --size: calc(var(--size-selector, 0.25rem) * 6); + height: var(--size); + padding-inline: calc(var(--size) / 2 - var(--border)); + } + } + .tabs { + @layer daisyui.l1.l2.l3 { + display: flex; + flex-wrap: wrap; + --tabs-height: auto; + --tabs-direction: row; + --tab-height: calc(var(--size-field, 0.25rem) * 10); + height: var(--tabs-height); + flex-direction: var(--tabs-direction); + } + } + .stat { + @layer daisyui.l1.l2.l3 { + display: inline-grid; + width: 100%; + column-gap: calc(0.25rem * 4); + padding-inline: calc(0.25rem * 6); + padding-block: calc(0.25rem * 4); + grid-template-columns: repeat(1, 1fr); + &:not(:last-child) { + border-inline-end: var(--border) dashed currentColor; + @supports (color: color-mix(in lab, red, red)) { + border-inline-end: var(--border) dashed color-mix(in oklab, currentColor 10%, #0000); + } + border-block-end: none; + } + } + } + .card-body { + @layer daisyui.l1.l2.l3 { + display: flex; + flex: auto; + flex-direction: column; + gap: calc(0.25rem * 2); + padding: var(--card-p, 1.5rem); + font-size: var(--card-fs, 0.875rem); + } + @layer daisyui.l1.l2.l3.l4 { + p { + flex-grow: 1; + } + } + } + .alert { + border-width: var(--border); + border-color: var(--alert-border-color, var(--color-base-200)); + @layer daisyui.l1.l2.l3 { + border-style: solid; + --alert-border-color: var(--color-base-200); + display: grid; + align-items: center; + gap: calc(0.25rem * 4); + border-radius: var(--radius-box); + padding-inline: calc(0.25rem * 4); + padding-block: calc(0.25rem * 3); + color: var(--color-base-content); + background-color: var(--alert-color, var(--color-base-200)); + justify-content: start; + justify-items: start; + grid-auto-flow: column; + grid-template-columns: auto; + text-align: start; + font-size: 0.875rem; + line-height: 1.25rem; + background-size: auto, calc(var(--noise) * 33%); + background-image: none, var(--fx-noise); + box-shadow: 0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * 0.08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * 0.08)); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * 0.08)) inset, 0 1px color-mix( in oklab, color-mix(in oklab, #000 20%, var(--alert-color, var(--color-base-200))) calc(var(--depth) * 20%), #0000 ), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * 0.08)); + } + &:has(:nth-child(2)) { + grid-template-columns: auto minmax(auto, 1fr); + } + } + } + .card-actions { + @layer daisyui.l1.l2.l3 { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: calc(0.25rem * 2); + } + } + .card-title { + @layer daisyui.l1.l2.l3 { + display: flex; + align-items: center; + gap: calc(0.25rem * 2); + font-size: var(--cardtitle-fs, 1.125rem); + font-weight: 600; + } + } + .prose { + :root & { + --tw-prose-body: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-body: color-mix(in oklab, var(--color-base-content) 80%, #0000); + } + --tw-prose-headings: var(--color-base-content); + --tw-prose-lead: var(--color-base-content); + --tw-prose-links: var(--color-base-content); + --tw-prose-bold: var(--color-base-content); + --tw-prose-counters: var(--color-base-content); + --tw-prose-bullets: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-bullets: color-mix(in oklab, var(--color-base-content) 50%, #0000); + } + --tw-prose-hr: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-hr: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + --tw-prose-quotes: var(--color-base-content); + --tw-prose-quote-borders: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-quote-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + --tw-prose-captions: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-captions: color-mix(in oklab, var(--color-base-content) 50%, #0000); + } + --tw-prose-code: var(--color-base-content); + --tw-prose-pre-code: var(--color-neutral-content); + --tw-prose-pre-bg: var(--color-neutral); + --tw-prose-th-borders: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-th-borders: color-mix(in oklab, var(--color-base-content) 50%, #0000); + } + --tw-prose-td-borders: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-td-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + --tw-prose-kbd: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-kbd: color-mix(in oklab, var(--color-base-content) 80%, #0000); + } + :where(code):not(pre > code) { + background-color: var(--color-base-200); + border-radius: var(--radius-selector); + border: var(--border) solid var(--color-base-300); + padding-inline: 0.5em; + padding-block: 0.2em; + font-weight: inherit; + &:before, &:after { + display: none; + } + } + } + } + .block { + display: block; + } + .flex { + display: flex; + } + .grid { + display: grid; + } + .hidden { + display: none; + } + .table { + display: table; + } + .btn-circle { + @layer daisyui.l1.l2 { + border-radius: calc(infinity * 1px); + padding-inline: 0; + width: var(--size); + height: var(--size); + } + } + .min-h-screen { + min-height: 100vh; + } + .w-56 { + width: calc(var(--spacing) * 56); + } + .w-fit { + width: fit-content; + } + .w-full { + width: 100%; + } + .max-w-5xl { + max-width: var(--container-5xl); + } + .max-w-none { + max-width: none; + } + .max-w-xl { + max-width: var(--container-xl); + } + .max-w-xs { + max-width: var(--container-xs); + } + .flex-1 { + flex: 1; + } + .flex-none { + flex: none; + } + .link { + @layer daisyui.l1.l2.l3 { + cursor: pointer; + text-decoration-line: underline; + &:focus { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; + } + } + } + .cursor-pointer { + cursor: pointer; + } + .flex-wrap { + flex-wrap: wrap; + } + .items-center { + align-items: center; + } + .justify-between { + justify-content: space-between; + } + .justify-center { + justify-content: center; + } + .justify-end { + justify-content: flex-end; + } + .justify-start { + justify-content: flex-start; + } + .gap-1 { + gap: var(--spacing); + } + .gap-2 { + gap: calc(var(--spacing) * 2); + } + .gap-3 { + gap: calc(var(--spacing) * 3); + } + .gap-4 { + gap: calc(var(--spacing) * 4); + } + .space-y-2 { + :where(& > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse))); + } + } + .divide-y { + :where(& > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(1px * var(--tw-divide-y-reverse)); + border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + } + } + .divide-base-200 { + :where(& > :not(:last-child)) { + border-color: var(--color-base-200); + } + } + .truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .overflow-x-auto { + overflow-x: auto; + } + .rounded-box { + border-radius: var(--radius-box); + } + .rounded-box { + border-radius: var(--radius-box); + } + .border { + border-style: var(--tw-border-style); + border-width: 1px; + } + .badge-ghost { + @layer daisyui.l1.l2 { + border-color: var(--color-base-200); + background-color: var(--color-base-200); + color: var(--color-base-content); + background-image: none; + } + } + .border-base-300 { + border-color: var(--color-base-300); + } + .bg-base-100 { + background-color: var(--color-base-100); + } + .bg-base-200 { + background-color: var(--color-base-200); + } + .btn-link { + @layer daisyui { + text-decoration-line: underline; + --btn-bg: #0000; + color: var(--btn-color, var(--color-primary)); + --btn-border: #0000; + background-image: none; + --btn-inset: 0 0 0 0 oklch(0% 0 0/0); + --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); + } + } + .btn-ghost { + @layer daisyui.l1.l2.l3 { + --btn-bg: #0000; + color: var(--btn-rest-fg, var(--btn-color, var(--color-base-content, currentColor))); + --btn-border: #0000; + background-image: none; + --btn-inset: 0 0 0 0 oklch(0% 0 0/0); + --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); + } + } + .p-2 { + padding: calc(var(--spacing) * 2); + } + .p-4 { + padding: calc(var(--spacing) * 4); + } + .px-4 { + padding-inline: calc(var(--spacing) * 4); + } + .py-2 { + padding-block: calc(var(--spacing) * 2); + } + .pt-2 { + padding-top: calc(var(--spacing) * 2); + } + .pt-3 { + padding-top: calc(var(--spacing) * 3); + } + .text-center { + text-align: center; + } + .text-right { + text-align: right; + } + .font-mono { + font-family: var(--font-mono); + } + .text-2xl { + font-size: var(--text-2xl); + line-height: var(--tw-leading, var(--text-2xl--line-height)); + } + .text-base { + font-size: var(--text-base); + line-height: var(--tw-leading, var(--text-base--line-height)); + } + .text-lg { + font-size: var(--text-lg); + line-height: var(--tw-leading, var(--text-lg--line-height)); + } + .text-sm { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } + .text-xl { + font-size: var(--text-xl); + line-height: var(--tw-leading, var(--text-xl--line-height)); + } + .text-xs { + font-size: var(--text-xs); + line-height: var(--tw-leading, var(--text-xs--line-height)); + } + .badge-sm { + @layer daisyui.l1.l2 { + --size: calc(var(--size-selector, 0.25rem) * 5); + font-size: 0.75rem; + } + } + .font-bold { + --tw-font-weight: var(--font-weight-bold); + font-weight: var(--font-weight-bold); + } + .font-medium { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } + .font-semibold { + --tw-font-weight: var(--font-weight-semibold); + font-weight: var(--font-weight-semibold); + } + .alert-error { + @layer daisyui.l1.l2 { + color: var(--color-error-content); + --alert-border-color: var(--color-error); + --alert-color: var(--color-error); + } + } + .alert-info { + @layer daisyui.l1.l2 { + color: var(--color-info-content); + --alert-border-color: var(--color-info); + --alert-color: var(--color-info); + } + } + .alert-success { + @layer daisyui.l1.l2 { + color: var(--color-success-content); + --alert-border-color: var(--color-success); + --alert-color: var(--color-success); + } + } + .alert-warning { + @layer daisyui.l1.l2 { + color: var(--color-warning-content); + --alert-border-color: var(--color-warning); + --alert-color: var(--color-warning); + } + } + .text-base-content\/70 { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 70%, transparent); + } + } + .text-base-content\/80 { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 80%, transparent); + } + } + .text-error { + color: var(--color-error); + } + .tabular-nums { + --tw-numeric-spacing: tabular-nums; + font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,); + } + .link-hover { + @layer daisyui.l1.l2 { + text-decoration-line: none; + &:hover { + @media (hover: hover) { + text-decoration-line: underline; + } + } + } + } + .prose { + & :where(a.btn:not(.btn-link)):not(:where([class~="not-prose"], [class~="not-prose"] *)) { + text-decoration-line: none; + } + } + .opacity-60 { + opacity: 60%; + } + .opacity-70 { + opacity: 70%; + } + .shadow { + --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + .shadow-sm { + --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + .filter { + filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); + } + .btn-error { + @layer daisyui.l1.l2 { + --btn-color: var(--color-error); + --btn-fg: var(--color-error-content); + --btn-soft-bg: initial; + } + } + .btn-primary { + @layer daisyui.l1.l2 { + --btn-color: var(--color-primary); + --btn-fg: var(--color-primary-content); + --btn-soft-bg: initial; + } + } + .btn-sm { + @layer daisyui.l1.l2 { + --fontsize: 0.75rem; + --btn-p: 0.75rem; + --size: calc(var(--size-field, 0.25rem) * 8); + } + } + .btn-success { + @layer daisyui.l1.l2 { + --btn-color: var(--color-success); + --btn-fg: var(--color-success-content); + --btn-soft-bg: initial; + } + } + .btn-warning { + @layer daisyui.l1.l2 { + --btn-color: var(--color-warning); + --btn-fg: var(--color-warning-content); + --btn-soft-bg: initial; + } + } + .btn-xs { + @layer daisyui.l1.l2 { + --fontsize: 0.6875rem; + --btn-p: 0.5rem; + --size: calc(var(--size-field, 0.25rem) * 6); + } + } + .badge-error { + @layer daisyui.l1.l2 { + --badge-color: var(--color-error); + --badge-fg: var(--color-error-content); + } + } + .badge-neutral { + @layer daisyui.l1.l2 { + --badge-color: var(--color-neutral); + --badge-fg: var(--color-neutral-content); + } + } + .badge-success { + @layer daisyui.l1.l2 { + --badge-color: var(--color-success); + --badge-fg: var(--color-success-content); + } + } + .badge-warning { + @layer daisyui.l1.l2 { + --badge-color: var(--color-warning); + --badge-fg: var(--color-warning-content); + } + } + .input-error { + @layer daisyui.l1.l2 { + &, &:focus, &:focus-within { + --input-color: var(--color-error); + } + } + } + .md\:grid-cols-2 { + @media (width >= 48rem) { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + } +} +@layer base { + :where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] { + color-scheme: light; + --color-base-100: oklch(100% 0 0); + --color-base-200: oklch(98% 0 0); + --color-base-300: oklch(95% 0 0); + --color-base-content: oklch(21% 0.006 285.885); + --color-primary: oklch(45% 0.24 277.023); + --color-primary-content: oklch(93% 0.034 272.788); + --color-secondary: oklch(65% 0.241 354.308); + --color-secondary-content: oklch(94% 0.028 342.258); + --color-accent: oklch(77% 0.152 181.912); + --color-accent-content: oklch(38% 0.063 188.416); + --color-neutral: oklch(14% 0.005 285.823); + --color-neutral-content: oklch(92% 0.004 286.32); + --color-info: oklch(74% 0.16 232.661); + --color-info-content: oklch(29% 0.066 243.157); + --color-success: oklch(76% 0.177 163.223); + --color-success-content: oklch(37% 0.077 168.94); + --color-warning: oklch(82% 0.189 84.429); + --color-warning-content: oklch(41% 0.112 45.904); + --color-error: oklch(71% 0.194 13.428); + --color-error-content: oklch(27% 0.105 12.094); + --radius-selector: 0.5rem; + --radius-field: 0.25rem; + --radius-box: 0.5rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 1; + --noise: 0; + } +} +@layer base { + @media (prefers-color-scheme: dark) { + :root:not([data-theme]) { + color-scheme: dark; + --color-base-100: oklch(25.33% 0.016 252.42); + --color-base-200: oklch(23.26% 0.014 253.1); + --color-base-300: oklch(21.15% 0.012 254.09); + --color-base-content: oklch(97.807% 0.029 256.847); + --color-primary: oklch(58% 0.233 277.117); + --color-primary-content: oklch(96% 0.018 272.314); + --color-secondary: oklch(65% 0.241 354.308); + --color-secondary-content: oklch(94% 0.028 342.258); + --color-accent: oklch(77% 0.152 181.912); + --color-accent-content: oklch(38% 0.063 188.416); + --color-neutral: oklch(14% 0.005 285.823); + --color-neutral-content: oklch(92% 0.004 286.32); + --color-info: oklch(74% 0.16 232.661); + --color-info-content: oklch(29% 0.066 243.157); + --color-success: oklch(76% 0.177 163.223); + --color-success-content: oklch(37% 0.077 168.94); + --color-warning: oklch(82% 0.189 84.429); + --color-warning-content: oklch(41% 0.112 45.904); + --color-error: oklch(71% 0.194 13.428); + --color-error-content: oklch(27% 0.105 12.094); + --radius-selector: 0.5rem; + --radius-field: 0.25rem; + --radius-box: 0.5rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 1; + --noise: 0; + } + } +} +@layer base { + :root:has(input.theme-controller[value=light]:checked),[data-theme=light] { + color-scheme: light; + --color-base-100: oklch(100% 0 0); + --color-base-200: oklch(98% 0 0); + --color-base-300: oklch(95% 0 0); + --color-base-content: oklch(21% 0.006 285.885); + --color-primary: oklch(45% 0.24 277.023); + --color-primary-content: oklch(93% 0.034 272.788); + --color-secondary: oklch(65% 0.241 354.308); + --color-secondary-content: oklch(94% 0.028 342.258); + --color-accent: oklch(77% 0.152 181.912); + --color-accent-content: oklch(38% 0.063 188.416); + --color-neutral: oklch(14% 0.005 285.823); + --color-neutral-content: oklch(92% 0.004 286.32); + --color-info: oklch(74% 0.16 232.661); + --color-info-content: oklch(29% 0.066 243.157); + --color-success: oklch(76% 0.177 163.223); + --color-success-content: oklch(37% 0.077 168.94); + --color-warning: oklch(82% 0.189 84.429); + --color-warning-content: oklch(41% 0.112 45.904); + --color-error: oklch(71% 0.194 13.428); + --color-error-content: oklch(27% 0.105 12.094); + --radius-selector: 0.5rem; + --radius-field: 0.25rem; + --radius-box: 0.5rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 1; + --noise: 0; + } +} +@layer base { + :root:has(input.theme-controller[value=dark]:checked),[data-theme=dark] { + color-scheme: dark; + --color-base-100: oklch(25.33% 0.016 252.42); + --color-base-200: oklch(23.26% 0.014 253.1); + --color-base-300: oklch(21.15% 0.012 254.09); + --color-base-content: oklch(97.807% 0.029 256.847); + --color-primary: oklch(58% 0.233 277.117); + --color-primary-content: oklch(96% 0.018 272.314); + --color-secondary: oklch(65% 0.241 354.308); + --color-secondary-content: oklch(94% 0.028 342.258); + --color-accent: oklch(77% 0.152 181.912); + --color-accent-content: oklch(38% 0.063 188.416); + --color-neutral: oklch(14% 0.005 285.823); + --color-neutral-content: oklch(92% 0.004 286.32); + --color-info: oklch(74% 0.16 232.661); + --color-info-content: oklch(29% 0.066 243.157); + --color-success: oklch(76% 0.177 163.223); + --color-success-content: oklch(37% 0.077 168.94); + --color-warning: oklch(82% 0.189 84.429); + --color-warning-content: oklch(41% 0.112 45.904); + --color-error: oklch(71% 0.194 13.428); + --color-error-content: oklch(27% 0.105 12.094); + --radius-selector: 0.5rem; + --radius-field: 0.25rem; + --radius-box: 0.5rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 1; + --noise: 0; + } +} +@layer base { + @property --radialprogress { + syntax: ""; + inherits: true; + initial-value: 0%; + } + @property --aura-angle { + syntax: ""; + inherits: false; + initial-value: 0deg; + } +} +@layer base { + :root { + scrollbar-color: currentColor #0000; + @supports (color: color-mix(in lab, red, red)) { + scrollbar-color: color-mix(in oklch, currentColor 35%, #0000) #0000; + } + } +} +@layer base { + :root { + --fx-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E"); + } +} +@layer base { + :root { + --page-scroll-lock: initial; + --page-overflow: var(--page-scroll-lock) hidden; + } + :root:not(span) { + overflow: var(--page-overflow); + } +} +@layer base { + :root { + --page-has-backdrop: var(--page-scroll-lock) 1; + --page-scroll-bg: var(--page-scroll-lock) + var(--root-bg, #0000); + @supports (color: color-mix(in lab, red, red)) { + --page-scroll-bg: var(--page-scroll-lock) + color-mix(in srgb, var(--root-bg, #0000), oklch(0% 0 0) calc(var(--page-has-backdrop, 0) * 40%)); + } + background-image: var(--page-scroll-lock) linear-gradient(var(--root-bg, #0000), var(--root-bg, #0000)); + transition: var(--page-scroll-lock) background-color 0.3s ease-out; + animation: var(--page-scroll-lock) set-page-has-scroll forwards; + animation-timeline: var(--page-scroll-lock) scroll(); + --page-has-scroll: initial; + scrollbar-gutter: var(--page-has-scroll) var(--page-scroll-lock) stable; + } + @keyframes set-page-has-scroll { + 0%, to { + --page-has-scroll: ; + } + } +} +@layer base { + :root, [data-theme] { + background-color: var(--root-bg); + color: var(--color-base-content); + } + :root { + background-color: var(--page-scroll-bg, var(--root-bg)); + } + :where(:root, [data-theme]) { + --root-bg: var(--color-base-100); + } +} +@keyframes dropdown { + 0% { + opacity: 0; + } +} +@keyframes rotator { + 89.9999%, 100% { + --first-item-position: 0 0%; + } + 90%, 99.9999% { + --first-item-position: 0 calc(var(--items) * 100%); + } + 100% { + translate: 0 -100%; + } +} +@keyframes radio { + 0% { + padding: 5px; + } + 50% { + padding: 3px; + } +} +@keyframes toast { + 0% { + scale: 0.9; + opacity: 0; + } + 100% { + scale: 1; + opacity: 1; + } +} +@keyframes skeleton { + 0% { + background-position: 150%; + } + 100% { + background-position: -50%; + } +} +@keyframes progress { + 50% { + background-position-x: -115%; + } +} +@keyframes rating { + 0%, 40% { + scale: 1.1; + filter: brightness(1.05) contrast(1.05); + } +} +@keyframes aura { + to { + --aura-angle: 360deg; + transform: translateZ(1px); + } +} +@keyframes aura-glow { + 20%, 80% { + opacity: 0.7; + filter: blur(0.25rem); + } + 50% { + opacity: 1; + filter: blur(0.75rem); + } +} +@keyframes aura-glow-after { + 20%, 80% { + opacity: 0.3; + filter: blur(1rem); + } + 50% { + opacity: 0.6; + filter: blur(1.5rem); + } +} +@keyframes menu { + 0% { + opacity: 0; + } +} +@property --tw-space-y-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-divide-y-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-border-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} +@property --tw-font-weight { + syntax: "*"; + inherits: false; +} +@property --tw-ordinal { + syntax: "*"; + inherits: false; +} +@property --tw-slashed-zero { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-figure { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-spacing { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-fraction { + syntax: "*"; + inherits: false; +} +@property --tw-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-inset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-inset-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-inset-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-ring-color { + syntax: "*"; + inherits: false; +} +@property --tw-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-inset-ring-color { + syntax: "*"; + inherits: false; +} +@property --tw-inset-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-ring-inset { + syntax: "*"; + inherits: false; +} +@property --tw-ring-offset-width { + syntax: ""; + inherits: false; + initial-value: 0px; +} +@property --tw-ring-offset-color { + syntax: "*"; + inherits: false; + initial-value: #fff; +} +@property --tw-ring-offset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-blur { + syntax: "*"; + inherits: false; +} +@property --tw-brightness { + syntax: "*"; + inherits: false; +} +@property --tw-contrast { + syntax: "*"; + inherits: false; +} +@property --tw-grayscale { + syntax: "*"; + inherits: false; +} +@property --tw-hue-rotate { + syntax: "*"; + inherits: false; +} +@property --tw-invert { + syntax: "*"; + inherits: false; +} +@property --tw-opacity { + syntax: "*"; + inherits: false; +} +@property --tw-saturate { + syntax: "*"; + inherits: false; +} +@property --tw-sepia { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-drop-shadow-size { + syntax: "*"; + inherits: false; +} +@layer properties { + @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { + *, ::before, ::after, ::backdrop { + --tw-space-y-reverse: 0; + --tw-divide-y-reverse: 0; + --tw-border-style: solid; + --tw-font-weight: initial; + --tw-ordinal: initial; + --tw-slashed-zero: initial; + --tw-numeric-figure: initial; + --tw-numeric-spacing: initial; + --tw-numeric-fraction: initial; + --tw-shadow: 0 0 #0000; + --tw-shadow-color: initial; + --tw-shadow-alpha: 100%; + --tw-inset-shadow: 0 0 #0000; + --tw-inset-shadow-color: initial; + --tw-inset-shadow-alpha: 100%; + --tw-ring-color: initial; + --tw-ring-shadow: 0 0 #0000; + --tw-inset-ring-color: initial; + --tw-inset-ring-shadow: 0 0 #0000; + --tw-ring-inset: initial; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-offset-shadow: 0 0 #0000; + --tw-blur: initial; + --tw-brightness: initial; + --tw-contrast: initial; + --tw-grayscale: initial; + --tw-hue-rotate: initial; + --tw-invert: initial; + --tw-opacity: initial; + --tw-saturate: initial; + --tw-sepia: initial; + --tw-drop-shadow: initial; + --tw-drop-shadow-color: initial; + --tw-drop-shadow-alpha: 100%; + --tw-drop-shadow-size: initial; + } + } +} diff --git a/templates/base.html b/templates/base.html index c4b5820..fc75bef 100644 --- a/templates/base.html +++ b/templates/base.html @@ -7,14 +7,16 @@ {% block title %}RosterChief{% endblock title %} - {# Apply the stored theme before first paint, otherwise the page flashes - the wrong colours. With no stored preference we set nothing, so - daisyUI's `dark --prefersdark` follows the OS. #} + {% comment %} + Apply the stored theme before first paint, otherwise the page flashes the wrong + colours. With no stored preference we set nothing, so daisyUI's `dark --prefersdark` + follows the OS. + {% endcomment %} {% block extra_head %}{% endblock extra_head %} @@ -88,28 +90,28 @@ {% block main %}{% endblock main %} From 334c706aec9c5f8995a4d482622d8ce7285df399 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 18:32:19 +0200 Subject: [PATCH 040/137] Reload the browser on template and static changes in dev Adds django-browser-reload: runserver already restarts on Python changes, but the browser had to be refreshed by hand for every template or CSS edit. It also watches static/, so a Tailwind rebuild now refreshes the page on its own. Mounted only under DEBUG -- it injects a script into every HTML response and serves an open event stream, neither of which belongs in production; a test holds that line. Its endpoint is exempt from RequireMFAMiddleware, otherwise a not-yet-enrolled staff user has the stream redirected away and live reload dies on the MFA enrolment page, which is exactly a page we are restyling. Co-Authored-By: Claude Opus 4.8 --- .DS_Store | Bin 0 -> 6148 bytes authentication/middleware.py | 4 +- pyproject.toml | 1 + rosterchief/settings.py | 7 + rosterchief/tests.py | 30 ++ rosterchief/urls.py | 4 + static/.DS_Store | Bin 0 -> 6148 bytes static/css/app.css | 358 ++++++++++++++++++ static/images/rosterchief-dark.svg | 6 + static/images/rosterchief-white.svg | 6 + static/src/app.css | 10 +- templates/_controlpanel_base.html | 33 ++ .../allauth/controlpanel/services/__init__.py | 0 .../controlpanel/templatetags/__init__.py | 0 templates/allauth/layouts/base.html | 2 +- uv.lock | 15 + 16 files changed, 473 insertions(+), 3 deletions(-) create mode 100644 .DS_Store create mode 100644 rosterchief/tests.py create mode 100644 static/.DS_Store create mode 100644 static/images/rosterchief-dark.svg create mode 100644 static/images/rosterchief-white.svg create mode 100644 templates/_controlpanel_base.html delete mode 100644 templates/allauth/controlpanel/services/__init__.py delete mode 100644 templates/allauth/controlpanel/templatetags/__init__.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3ba24ff755e2d6d4124ab235b082f25de93b7339 GIT binary patch literal 6148 zcmeHK&2G~`5T0p6Nn1*tG$|Euh{&ZA zILAnYK{Q>z#k7gq3q0zwhqgHW#C_9fZyE(NFW7|bn5-Z51nBcB+$MawGV>ybtcVsBzS~wC%&T8^Z z9`E@wYQ$n3h~ri&UZu_8HHC^lCG3mtlT+8h$&kjBP918}d$ADFqkbH8>%Cqg($)`l zo2&V;48w*@f;5m(`%qO+#b7V$Nvgx3-91Pe1y;N6~5sLuH!Y_!s~bwZ=s7jxQ`F9g`Uoyqpz=$s|F3=b26sx z?zkC8p^W icB~BEiZ@7=P^`iRp=&VHNFEaVBcN!ojb-4kGVmSMmXTKg literal 0 HcmV?d00001 diff --git a/authentication/middleware.py b/authentication/middleware.py index 4cfe4af..8cbe9ac 100644 --- a/authentication/middleware.py +++ b/authentication/middleware.py @@ -17,7 +17,9 @@ from django.urls import reverse from club.models import ClubRole #: Paths a not-yet-enrolled user must still reach (to enrol, or to log out). -EXEMPT_PREFIXES = ("/accounts/", "/static/", "/media/") +#: ``/__reload__/`` is django-browser-reload's event stream, which only exists +#: under DEBUG — without it, live reload dies on the enrolment page itself. +EXEMPT_PREFIXES = ("/accounts/", "/static/", "/media/", "/__reload__/") ELEVATED_ROLES = (ClubRole.Roles.ADMIN, ClubRole.Roles.EDITOR) diff --git a/pyproject.toml b/pyproject.toml index fbfc6cc..7fa16ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ dependencies = [ [dependency-groups] dev = [ "coverage>=7.15.0", + "django-browser-reload>=1.21.0", "ruff>=0.15.17", ] diff --git a/rosterchief/settings.py b/rosterchief/settings.py index fb1411c..fdd3375 100644 --- a/rosterchief/settings.py +++ b/rosterchief/settings.py @@ -81,6 +81,13 @@ MIDDLEWARE = [ "django.middleware.clickjacking.XFrameOptionsMiddleware", ] +# Reload the browser when templates, static files or Python change. Dev only: it +# injects a script tag into every HTML response and serves an open event stream, +# neither of which belongs in production. +if DEBUG: + INSTALLED_APPS += ["django_browser_reload"] + MIDDLEWARE += ["django_browser_reload.middleware.BrowserReloadMiddleware"] + AUTHENTICATION_BACKENDS = [ "django.contrib.auth.backends.ModelBackend", "allauth.account.auth_backends.AuthenticationBackend", diff --git a/rosterchief/tests.py b/rosterchief/tests.py new file mode 100644 index 0000000..2dcf681 --- /dev/null +++ b/rosterchief/tests.py @@ -0,0 +1,30 @@ +import importlib + +from django.test import SimpleTestCase, override_settings +from django.urls import Resolver404, clear_url_caches, resolve + +from . import urls + + +class BrowserReloadUrlTests(SimpleTestCase): + """django-browser-reload serves an open event stream and injects a script into + every HTML response, so it must never be mounted outside DEBUG.""" + + def reload_urlconf(self): + importlib.reload(urls) + clear_url_caches() + + def tearDown(self): + self.reload_urlconf() # restore the real (DEBUG=False) urlconf + + def test_the_reload_endpoint_is_mounted_in_debug(self): + with override_settings(DEBUG=True): + self.reload_urlconf() + + self.assertEqual(resolve("/__reload__/events/").view_name, "django_browser_reload:events") + + def test_the_reload_endpoint_is_absent_without_debug(self): + self.reload_urlconf() + + with self.assertRaises(Resolver404): + resolve("/__reload__/events/") diff --git a/rosterchief/urls.py b/rosterchief/urls.py index 15fb04f..c743dff 100644 --- a/rosterchief/urls.py +++ b/rosterchief/urls.py @@ -7,6 +7,7 @@ second factors. ``RequireMFAMiddleware`` then blocks any staff user who has not enrolled. """ +from django.conf import settings from django.contrib import admin from django.urls import include, path from django.views.generic import RedirectView @@ -17,3 +18,6 @@ urlpatterns = [ path("accounts/", include("allauth.urls")), path("controlpanel/", include("controlpanel.urls")), ] + +if settings.DEBUG: + urlpatterns += [path("__reload__/", include("django_browser_reload.urls"))] diff --git a/static/.DS_Store b/static/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..bff4bec45682bb98a49c4f1d2813076da976b4fa GIT binary patch literal 6148 zcmeHKPfrs;6n_JiE+Dc%i}>fTu@@6atRfiV!KGjf@c>~7lmOPOEGU@cya(RsNirB>}GfC&HwHjbGg z?9LGG=dDNjl%*e$h%qR{UOR4j0rPsIwLvqW8Ti{6;NR{5_|O0s>d^iDzVn)aA1B4) zC(_rSIX93svf1J6p7VsYoy1LAamkH#_|pbsftTv8x9QV%#mp_SFme5mMk2wlQ4HDL z^1~Wymsl&T#Uj^{4r9Y>_{$cMD3&!(A{H8?bUe&oWaIW|5qmK)3G^OG0zQ{$Jf zRLlv_tsc}PzT71iu&5NYXvG|jvFMd53)dogh{cu@`aE&`7w8)QC1IV`9-W#tj+z*g zJ2i+wA4HUfJN3w~t?uu~G%5e2Z}Zuz5etGf7W;|M!tGr#dqNv4;XcC|`3hE>+!b%q zC`RnbDQ~+`~giMlaWSY#7IdX^C zWRa|sEm9_~%ijOjb1a3n!j44rpb(jgC{u~LVi1{*c1y+C3Of>I zIuLdFAZli!ZYV^|j^kS*9EdGZ_nHCCKsN*Y-yg-~=l}iR-~UCB?lc3Mfq#<$l38|^ zOIVvaTYIYHXRU?pF*Ytdt|L*TV5g2_q41-43Y!r0SzJN16?P<|1x5b|2pV*!8Tg|N F`~Yf<$u9r^ literal 0 HcmV?d00001 diff --git a/static/css/app.css b/static/css/app.css index b421c9f..55fecd1 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -27,6 +27,10 @@ --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; + --ease-out: cubic-bezier(0, 0, 0.2, 1); + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --default-transition-duration: 150ms; + --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); --default-font-family: var(--font-sans); --default-mono-font-family: var(--font-mono); } @@ -464,6 +468,26 @@ } } } + .collapse-plus { + @layer daisyui.l1.l2 { + > .collapse-title:after { + position: absolute; + display: block; + height: 0.5rem; + width: 0.5rem; + @media (prefers-reduced-motion: no-preference) { + transition-property: all; + transition-duration: 300ms; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + } + top: 0.9rem; + inset-inline-end: 1.4rem; + --tw-content: "+"; + content: var(--tw-content); + pointer-events: none; + } + } + } .dropdown { @layer daisyui.l1.l2.l3 { position: relative; @@ -917,6 +941,21 @@ } } } + .collapse-open { + @layer daisyui.l1.l2 { + grid-template-rows: max-content 1fr; + > .collapse-content { + --overflow-delay: 0.2s; + overflow: revert-layer; + content-visibility: visible; + min-height: fit-content; + padding-bottom: 1rem; + @supports not (content-visibility: visible) { + visibility: visible; + } + } + } + } .collapse { visibility: collapse; } @@ -982,6 +1021,27 @@ } } } + .toast { + @layer daisyui.l1.l2.l3 { + position: fixed; + inset-inline-start: auto; + inset-inline-end: calc(0.25rem * 4); + top: auto; + bottom: calc(0.25rem * 4); + display: flex; + flex-direction: column; + gap: calc(0.25rem * 2); + background-color: transparent; + translate: var(--toast-x, 0) var(--toast-y, 0); + width: max-content; + max-width: calc(100vw - 2rem); + & > * { + @media (prefers-reduced-motion: no-preference) { + animation: toast 0.25s ease-out; + } + } + } + } .toggle { @layer daisyui.l1.l2.l3 { border: var(--border) solid currentColor; @@ -1318,6 +1378,51 @@ } } } + .aura { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-block; + --aura-padding: 0.125rem; + padding: var(--aura-padding); + border-radius: calc(var(--aura-padding) + var(--aura-radius, var(--radius-box))); + animation: aura var(--tw-duration, 6s) linear infinite; + background-image: conic-gradient(from var(--aura-angle), transparent 225deg, currentColor); + &:has( > .card, > .alert) { + --aura-radius: var(--radius-box); + } + &:has( > .btn, > .input, > .select) { + --aura-radius: var(--radius-field); + } + &:has( > .checkbox, > .toggle, > .badge) { + --aura-radius: var(--radius-selector); + } + &:before, &:after { + animation: inherit; + background-color: inherit; + background-image: inherit; + border-radius: inherit; + position: absolute; + top: calc(1 / 2 * 100%); + left: calc(1 / 2 * 100%); + z-index: 0; + display: block; + opacity: 70%; + filter: blur(0.25rem); + translate: -50% -50%; + width: 100%; + height: 100%; + content: ""; + } + &:after { + opacity: 30%; + filter: blur(1rem); + } + & > * { + position: relative; + z-index: 1; + } + } + } .select { @layer daisyui.l1.l2.l3 { position: relative; @@ -1666,6 +1771,48 @@ } } } + .rating { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-flex; + vertical-align: middle; + --size: var(--size-selector, 0.25rem) * 6; + input { + cursor: pointer; + appearance: none; + } + * { + border-radius: 0; + background-color: var(--color-base-content); + opacity: 20%; + width: calc(var(--size) * 1); + height: calc(var(--size)); + @media (prefers-reduced-motion: no-preference) { + animation: rating 0.25s ease-out; + } + } + .rating-hidden { + width: calc(0.25rem * 2); + background-color: transparent; + } + :checked, [aria-checked="true"], [aria-current="true"], :has( ~ :checked, ~ [aria-checked="true"], ~ [aria-current="true"]) { + opacity: 100%; + } + :focus-visible { + scale: 1.1; + @media (prefers-reduced-motion: no-preference) { + transition: scale 0.2s ease-out; + } + } + :active:focus { + animation: none; + scale: 1.1; + } + } + @layer daisyui.l1.l2 { + --size: var(--size-selector, 0.25rem) * 6; + } + } .navbar { @layer daisyui.l1.l2.l3 { display: flex; @@ -1734,9 +1881,89 @@ border-radius: var(--radius-box); } } + .progress { + @layer daisyui.l1.l2.l3 { + position: relative; + height: calc(0.25rem * 2); + width: 100%; + appearance: none; + overflow: hidden; + border-radius: var(--radius-box); + background-color: currentcolor; + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, currentcolor 20%, transparent); + } + color: var(--color-base-content); + &:indeterminate { + background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); + background-size: 200%; + background-position-x: 15%; + @media (prefers-reduced-motion: no-preference) { + animation: progress 5s ease-in-out infinite; + } + @supports (-moz-appearance: none) { + &::-moz-progress-bar { + background-color: transparent; + @media (prefers-reduced-motion: no-preference) { + animation: progress 5s ease-in-out infinite; + background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); + background-size: 200%; + background-position-x: 15%; + } + } + } + } + @supports (-moz-appearance: none) { + &::-moz-progress-bar { + border-radius: var(--radius-box); + background-color: currentcolor; + @media (prefers-reduced-motion: no-preference) { + transition: inline-size 0.3s ease; + } + } + } + @supports (-webkit-appearance: none) { + &::-webkit-progress-bar { + border-radius: var(--radius-box); + background-color: transparent; + } + &::-webkit-progress-value { + border-radius: var(--radius-box); + background-color: currentColor; + @media (prefers-reduced-motion: no-preference) { + transition: inline-size 0.3s ease; + } + } + } + } + } .static { position: static; } + .dropdown-right { + @layer daisyui.l1.l2 { + --anchor-h: right; + --anchor-v: span-bottom; + .dropdown-content { + inset-inline-start: 100%; + top: 0; + bottom: auto; + transform-origin: 0; + } + } + } + .dropdown-left { + @layer daisyui.l1.l2 { + --anchor-h: left; + --anchor-v: span-bottom; + .dropdown-content { + inset-inline-end: 100%; + top: 0; + bottom: auto; + transform-origin: 100%; + } + } + } .dropdown-end { @layer daisyui.l1.l2 { --anchor-h: span-left; @@ -1931,6 +2158,26 @@ .z-10 { z-index: 10; } + .tab-content { + @layer daisyui.l1.l2.l3 { + order: var(--tabcontent-order); + display: none; + border-color: transparent; + --tabcontent-radius-ss: var(--radius-box); + --tabcontent-radius-se: var(--radius-box); + --tabcontent-radius-es: var(--radius-box); + --tabcontent-radius-ee: var(--radius-box); + --tabcontent-order: 1; + width: 100%; + height: calc(100% - var(--tab-height) + var(--border)); + margin: var(--tabcontent-margin); + border-width: var(--border); + border-start-start-radius: var(--tabcontent-radius-ss); + border-start-end-radius: var(--tabcontent-radius-se); + border-end-start-radius: var(--tabcontent-radius-es); + border-end-end-radius: var(--tabcontent-radius-ee); + } + } .hero { @layer daisyui.l1.l2.l3 { display: grid; @@ -2328,6 +2575,46 @@ .flex-none { flex: none; } + .flex-shrink { + flex-shrink: 1; + } + .flex-grow { + flex-grow: 1; + } + .border-collapse { + border-collapse: collapse; + } + .transform { + transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); + } + .skeleton { + @layer daisyui.l1.l2.l3 { + border-radius: var(--radius-box); + background-color: var(--color-base-300); + @media (prefers-reduced-motion: reduce) { + transition-duration: 15s; + } + will-change: background-position; + background-image: linear-gradient( 105deg, #0000 0% 40%, var(--color-base-100) 50%, #0000 60% 100% ); + background-size: 200% auto; + background-position-x: -50%; + @media (prefers-reduced-motion: no-preference) { + animation: skeleton 1.8s ease-in-out infinite; + } + } + } + .aura-glow { + @layer daisyui.l1.l2 { + animation: none; + background-image: radial-gradient(closest-corner at center, currentColor 0%, transparent 90%); + &:before { + animation: aura-glow var(--tw-duration, 6s) ease-out infinite; + } + &:after { + animation: aura-glow-after var(--tw-duration, 6s) ease-out infinite; + } + } + } .link { @layer daisyui.l1.l2.l3 { cursor: pointer; @@ -2349,6 +2636,9 @@ .cursor-pointer { cursor: pointer; } + .resize { + resize: both; + } .flex-wrap { flex-wrap: wrap; } @@ -2435,6 +2725,9 @@ .bg-base-200 { background-color: var(--color-base-200); } + .bg-base-300 { + background-color: var(--color-base-300); + } .btn-link { @layer daisyui { text-decoration-line: underline; @@ -2553,6 +2846,9 @@ --alert-color: var(--color-warning); } } + .text-base-content { + color: var(--color-base-content); + } .text-base-content\/70 { color: var(--color-base-content); @supports (color: color-mix(in lab, red, red)) { @@ -2587,6 +2883,9 @@ text-decoration-line: none; } } + .underline { + text-decoration-line: underline; + } .opacity-60 { opacity: 60%; } @@ -2601,9 +2900,26 @@ --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); } + .outline { + outline-style: var(--tw-outline-style); + outline-width: 1px; + } .filter { filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); } + .transition { + transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events; + transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); + transition-duration: var(--tw-duration, var(--default-transition-duration)); + } + .ease-in-out { + --tw-ease: var(--ease-in-out); + transition-timing-function: var(--ease-in-out); + } + .ease-out { + --tw-ease: var(--ease-out); + transition-timing-function: var(--ease-out); + } .btn-error { @layer daisyui.l1.l2 { --btn-color: var(--color-error); @@ -2683,6 +2999,12 @@ } } } +[data-theme="light"] .logo { + content: var(--logo-dark); +} +[data-theme="dark"] .logo { + content: var(--logo-light); +} @layer base { :where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] { color-scheme: light; @@ -2969,6 +3291,26 @@ opacity: 0; } } +@property --tw-rotate-x { + syntax: "*"; + inherits: false; +} +@property --tw-rotate-y { + syntax: "*"; + inherits: false; +} +@property --tw-rotate-z { + syntax: "*"; + inherits: false; +} +@property --tw-skew-x { + syntax: "*"; + inherits: false; +} +@property --tw-skew-y { + syntax: "*"; + inherits: false; +} @property --tw-space-y-reverse { syntax: "*"; inherits: false; @@ -3073,6 +3415,11 @@ inherits: false; initial-value: 0 0 #0000; } +@property --tw-outline-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} @property --tw-blur { syntax: "*"; inherits: false; @@ -3126,9 +3473,18 @@ syntax: "*"; inherits: false; } +@property --tw-ease { + syntax: "*"; + inherits: false; +} @layer properties { @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { *, ::before, ::after, ::backdrop { + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; --tw-space-y-reverse: 0; --tw-divide-y-reverse: 0; --tw-border-style: solid; @@ -3152,6 +3508,7 @@ --tw-ring-offset-width: 0px; --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; + --tw-outline-style: solid; --tw-blur: initial; --tw-brightness: initial; --tw-contrast: initial; @@ -3165,6 +3522,7 @@ --tw-drop-shadow-color: initial; --tw-drop-shadow-alpha: 100%; --tw-drop-shadow-size: initial; + --tw-ease: initial; } } } diff --git a/static/images/rosterchief-dark.svg b/static/images/rosterchief-dark.svg new file mode 100644 index 0000000..d58f99f --- /dev/null +++ b/static/images/rosterchief-dark.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/static/images/rosterchief-white.svg b/static/images/rosterchief-white.svg new file mode 100644 index 0000000..12f0bfb --- /dev/null +++ b/static/images/rosterchief-white.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/static/src/app.css b/static/src/app.css index bc76a63..3389bfe 100644 --- a/static/src/app.css +++ b/static/src/app.css @@ -8,5 +8,13 @@ /* daisyUI: light is the default, dark applies automatically when the OS asks for it. An explicit data-theme on (set by the toggle) overrides both. */ @plugin "daisyui" { - themes: light --default, dark --prefersdark; + themes: light --default, dark --prefersdark; +} + +[data-theme="light"] .logo { + content: var(--logo-dark); +} + +[data-theme="dark"] .logo { + content: var(--logo-light); } diff --git a/templates/_controlpanel_base.html b/templates/_controlpanel_base.html new file mode 100644 index 0000000..02581df --- /dev/null +++ b/templates/_controlpanel_base.html @@ -0,0 +1,33 @@ +{% load static lucide %} + + + + + + + + RosterChief - Control Panel + + + + + + + + {% block extra %}{% endblock extra %} + + + + {% block main %}{% endblock main %} + + \ No newline at end of file diff --git a/templates/allauth/controlpanel/services/__init__.py b/templates/allauth/controlpanel/services/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/templates/allauth/controlpanel/templatetags/__init__.py b/templates/allauth/controlpanel/templatetags/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/templates/allauth/layouts/base.html b/templates/allauth/layouts/base.html index dbef0b3..07dfacd 100644 --- a/templates/allauth/layouts/base.html +++ b/templates/allauth/layouts/base.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "_controlpanel_base.html" %} {% block title %} {% block head_title %}{% endblock head_title %} · RosterChief diff --git a/uv.lock b/uv.lock index 476e066..cecb875 100644 --- a/uv.lock +++ b/uv.lock @@ -213,6 +213,19 @@ mfa = [ { name = "qrcode" }, ] +[[package]] +name = "django-browser-reload" +version = "1.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/ef/ab407c1a2f14e13c75a6419a2d124954aa0364f62580ad95a3d31dc6c73d/django_browser_reload-1.21.0.tar.gz", hash = "sha256:3335ad3d107eb657f623d1a8e680dfbcab8a83ae1f94df1895e069dddf5604ba", size = 15800, upload-time = "2025-09-22T17:00:35.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/61/1b4a8c589652859995bcab87682286443eb9fdf2d7fd584975b9ffc1db33/django_browser_reload-1.21.0-py3-none-any.whl", hash = "sha256:0b2a86ab460774fa9bb142a121c70e75a72f18109f51a4f6de409cd633d3a70d", size = 12852, upload-time = "2025-09-22T17:00:33.479Z" }, +] + [[package]] name = "django-lucide" version = "1.3.1" @@ -379,6 +392,7 @@ dependencies = [ [package.dev-dependencies] dev = [ { name = "coverage" }, + { name = "django-browser-reload" }, { name = "ruff" }, ] @@ -398,6 +412,7 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "coverage", specifier = ">=7.15.0" }, + { name = "django-browser-reload", specifier = ">=1.21.0" }, { name = "ruff", specifier = ">=0.15.17" }, ] From fc51e1903c078ea37f892f968560b71de165f0d9 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 22:30:02 +0200 Subject: [PATCH 041/137] Self-host Ubuntu, JetBrains Mono, Roboto and Tourney Ubuntu becomes the default sans and JetBrains Mono the default mono (via --font-sans / --font-mono, which Tailwind wires to the body and defaults). Roboto and Tourney are opt-in utilities: `font-roboto`, and `font-tourney` for display numerals like jersey numbers. Self-hosted rather than linked from Google's CDN: a CDN sends every visitor's IP to a third party on each page load, which is an avoidable GDPR liability for an EU club platform, and it puts someone else's uptime in our render path. Files come from the @fontsource packages. Two subsets each, with unicode-range: plain `latin` cannot render the Polish, Czech or Turkish letters that turn up in member names, and the range means latin-ext is only fetched by pages that actually contain those glyphs. Tourney is variable on two axes, so font-stretch is declared as a range next to the weight range -- declare only the weight and the browser clamps the width axis to its default, making `font-stretch: 125%` (a wide shirt number) silently do nothing. Co-Authored-By: Claude Opus 4.8 --- package-lock.json | 48 +- package.json | 4 + static/css/app.css | 3528 +---------------- ...jetbrains-mono-latin-ext-wght-normal.woff2 | Bin 0 -> 15196 bytes .../jetbrains-mono-latin-wght-normal.woff2 | Bin 0 -> 40404 bytes .../roboto-latin-ext-standard-normal.woff2 | Bin 0 -> 45720 bytes .../fonts/roboto-latin-standard-normal.woff2 | Bin 0 -> 66544 bytes .../tourney-latin-ext-standard-normal.woff2 | Bin 0 -> 27064 bytes .../fonts/tourney-latin-standard-normal.woff2 | Bin 0 -> 28572 bytes static/fonts/ubuntu-latin-400-italic.woff2 | Bin 0 -> 36468 bytes static/fonts/ubuntu-latin-400-normal.woff2 | Bin 0 -> 34924 bytes static/fonts/ubuntu-latin-500-normal.woff2 | Bin 0 -> 30508 bytes static/fonts/ubuntu-latin-700-normal.woff2 | Bin 0 -> 29844 bytes .../fonts/ubuntu-latin-ext-400-italic.woff2 | Bin 0 -> 49776 bytes .../fonts/ubuntu-latin-ext-400-normal.woff2 | Bin 0 -> 46980 bytes .../fonts/ubuntu-latin-ext-500-normal.woff2 | Bin 0 -> 42480 bytes .../fonts/ubuntu-latin-ext-700-normal.woff2 | Bin 0 -> 38020 bytes static/src/app.css | 155 + 18 files changed, 206 insertions(+), 3529 deletions(-) create mode 100644 static/fonts/jetbrains-mono-latin-ext-wght-normal.woff2 create mode 100644 static/fonts/jetbrains-mono-latin-wght-normal.woff2 create mode 100644 static/fonts/roboto-latin-ext-standard-normal.woff2 create mode 100644 static/fonts/roboto-latin-standard-normal.woff2 create mode 100644 static/fonts/tourney-latin-ext-standard-normal.woff2 create mode 100644 static/fonts/tourney-latin-standard-normal.woff2 create mode 100644 static/fonts/ubuntu-latin-400-italic.woff2 create mode 100644 static/fonts/ubuntu-latin-400-normal.woff2 create mode 100644 static/fonts/ubuntu-latin-500-normal.woff2 create mode 100644 static/fonts/ubuntu-latin-700-normal.woff2 create mode 100644 static/fonts/ubuntu-latin-ext-400-italic.woff2 create mode 100644 static/fonts/ubuntu-latin-ext-400-normal.woff2 create mode 100644 static/fonts/ubuntu-latin-ext-500-normal.woff2 create mode 100644 static/fonts/ubuntu-latin-ext-700-normal.woff2 diff --git a/package-lock.json b/package-lock.json index ed1dee7..4af8852 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,60 @@ { - "name": "clubmanager", + "name": "rosterchief", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "clubmanager", + "name": "rosterchief", "devDependencies": { + "@fontsource-variable/jetbrains-mono": "^5.2.8", + "@fontsource-variable/roboto": "^5.2.10", + "@fontsource-variable/tourney": "^5.2.8", + "@fontsource/ubuntu": "^5.2.8", "@tailwindcss/cli": "^4.1.0", "daisyui": "^5.0.0", "tailwindcss": "^4.1.0" } }, + "node_modules/@fontsource-variable/jetbrains-mono": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource-variable/jetbrains-mono/-/jetbrains-mono-5.2.8.tgz", + "integrity": "sha512-WBA9elru6Jdp5df2mES55wuOO0WIrn3kpXnI4+W2ek5u3ZgLS9XS4gmIlcQhiZOWEKl95meYdvK7xI+ETLCq/Q==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource-variable/roboto": { + "version": "5.2.10", + "resolved": "https://registry.npmjs.org/@fontsource-variable/roboto/-/roboto-5.2.10.tgz", + "integrity": "sha512-LJ0iLg6aHbLzN515gyHzmdTqJzd9NlI95cCg1DW0F5G7KkFqRLBBKBbqEJx4nSu4aby3IKmw3ZH6Fe928IfaSQ==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource-variable/tourney": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource-variable/tourney/-/tourney-5.2.8.tgz", + "integrity": "sha512-jCKjAoSZGylpe8a3GWL5f94pUGDJf2wwvNCleLOPXNE63HOQz6wWis/YJs9sT6Qm0dgLLlBT5712mOLFj0YsSw==", + "dev": true, + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/ubuntu": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/ubuntu/-/ubuntu-5.2.8.tgz", + "integrity": "sha512-esSJCpgigwWOm4Ug9f7AMozUyBq5Iobea5C9QYsl2OlCb62wZABBUbXahHLdFv5Ru4lJCD9tUX6xEAeLwUTNBg==", + "dev": true, + "license": "UFL-1.0", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", diff --git a/package.json b/package.json index 4009fb9..d68246f 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,10 @@ "watch": "tailwindcss -i ./static/src/app.css -o ./static/css/app.css --watch" }, "devDependencies": { + "@fontsource-variable/jetbrains-mono": "^5.2.8", + "@fontsource-variable/roboto": "^5.2.10", + "@fontsource-variable/tourney": "^5.2.8", + "@fontsource/ubuntu": "^5.2.8", "@tailwindcss/cli": "^4.1.0", "daisyui": "^5.0.0", "tailwindcss": "^4.1.0" diff --git a/static/css/app.css b/static/css/app.css index 55fecd1..b82cc20 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -1,3528 +1,2 @@ /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */ -@layer properties; -@layer theme, base, components, utilities; -@layer theme { - :root, :host { - --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", - "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", - "Courier New", monospace; - --color-black: #000; - --spacing: 0.25rem; - --container-xs: 20rem; - --container-xl: 36rem; - --container-5xl: 64rem; - --text-xs: 0.75rem; - --text-xs--line-height: calc(1 / 0.75); - --text-sm: 0.875rem; - --text-sm--line-height: calc(1.25 / 0.875); - --text-base: 1rem; - --text-base--line-height: calc(1.5 / 1); - --text-lg: 1.125rem; - --text-lg--line-height: calc(1.75 / 1.125); - --text-xl: 1.25rem; - --text-xl--line-height: calc(1.75 / 1.25); - --text-2xl: 1.5rem; - --text-2xl--line-height: calc(2 / 1.5); - --font-weight-medium: 500; - --font-weight-semibold: 600; - --font-weight-bold: 700; - --ease-out: cubic-bezier(0, 0, 0.2, 1); - --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); - --default-transition-duration: 150ms; - --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - --default-font-family: var(--font-sans); - --default-mono-font-family: var(--font-mono); - } -} -@layer base { - *, ::after, ::before, ::backdrop, ::file-selector-button { - box-sizing: border-box; - margin: 0; - padding: 0; - border: 0 solid; - } - html, :host { - line-height: 1.5; - -webkit-text-size-adjust: 100%; - tab-size: 4; - font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); - font-feature-settings: var(--default-font-feature-settings, normal); - font-variation-settings: var(--default-font-variation-settings, normal); - -webkit-tap-highlight-color: transparent; - } - hr { - height: 0; - color: inherit; - border-top-width: 1px; - } - abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - } - h1, h2, h3, h4, h5, h6 { - font-size: inherit; - font-weight: inherit; - } - a { - color: inherit; - -webkit-text-decoration: inherit; - text-decoration: inherit; - } - b, strong { - font-weight: bolder; - } - code, kbd, samp, pre { - font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); - font-feature-settings: var(--default-mono-font-feature-settings, normal); - font-variation-settings: var(--default-mono-font-variation-settings, normal); - font-size: 1em; - } - small { - font-size: 80%; - } - sub, sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; - } - sub { - bottom: -0.25em; - } - sup { - top: -0.5em; - } - table { - text-indent: 0; - border-color: inherit; - border-collapse: collapse; - } - :-moz-focusring { - outline: auto; - } - progress { - vertical-align: baseline; - } - summary { - display: list-item; - } - ol, ul, menu { - list-style: none; - } - img, svg, video, canvas, audio, iframe, embed, object { - display: block; - vertical-align: middle; - } - img, video { - max-width: 100%; - height: auto; - } - button, input, select, optgroup, textarea, ::file-selector-button { - font: inherit; - font-feature-settings: inherit; - font-variation-settings: inherit; - letter-spacing: inherit; - color: inherit; - border-radius: 0; - background-color: transparent; - opacity: 1; - } - :where(select:is([multiple], [size])) optgroup { - font-weight: bolder; - } - :where(select:is([multiple], [size])) optgroup option { - padding-inline-start: 20px; - } - ::file-selector-button { - margin-inline-end: 4px; - } - ::placeholder { - opacity: 1; - } - @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) { - ::placeholder { - color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, currentcolor 50%, transparent); - } - } - } - textarea { - resize: vertical; - } - ::-webkit-search-decoration { - -webkit-appearance: none; - } - ::-webkit-date-and-time-value { - min-height: 1lh; - text-align: inherit; - } - ::-webkit-datetime-edit { - display: inline-flex; - } - ::-webkit-datetime-edit-fields-wrapper { - padding: 0; - } - ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { - padding-block: 0; - } - ::-webkit-calendar-picker-indicator { - line-height: 1; - } - :-moz-ui-invalid { - box-shadow: none; - } - button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button { - appearance: button; - } - ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { - height: auto; - } - [hidden]:where(:not([hidden="until-found"])) { - display: none !important; - } -} -@layer utilities { - .tab { - @layer daisyui.l1.l2.l3 { - &:is(.tabs > .tab) { - position: relative; - display: inline-flex; - cursor: pointer; - appearance: none; - flex-wrap: wrap; - align-items: center; - justify-content: center; - text-align: center; - webkit-user-select: none; - user-select: none; - &:hover { - @media (hover: hover) { - color: var(--color-base-content); - } - } - --tab-p: 0.75rem; - --tab-bg: var(--color-base-100); - --tab-border-color: var(--color-base-300); - --tab-radius-ss: 0; - --tab-radius-se: 0; - --tab-radius-es: 0; - --tab-radius-ee: 0; - --tab-order: 0; - --tab-radius-min: calc(0.75rem - var(--border)); - --tab-radius-limit: min(var(--radius-field), var(--tab-radius-min)); - --tab-radius-grad: #0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + 0.25px), - var(--tab-border-color) 69%, var(--tab-bg) calc(69% + 0.25px); - border-color: #0000; - order: var(--tab-order); - height: var(--tab-height); - font-size: 0.875rem; - padding-inline: var(--tab-p); - &:is(input[type="radio"]) { - min-width: fit-content; - &:after { - --tw-content: attr(aria-label); - content: var(--tw-content); - } - } - &:is(label) { - position: relative; - input { - position: absolute; - inset: 0; - cursor: pointer; - appearance: none; - opacity: 0%; - } - } - &:checked, &:is(label:has(:checked)), &:is(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]) { - & + .tab-content { - display: block; - } - } - &:not(:checked, label:has(:checked), :hover, .tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"] ) { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 50%, transparent); - } - } - &:not(input):empty { - flex-grow: 1; - cursor: default; - } - &:focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:focus-visible, &:is(label:has(:checked:focus-visible)) { - outline: 2px solid currentColor; - outline-offset: -5px; - } - &[disabled] { - pointer-events: none; - opacity: 40%; - } - } - } - } - .menu { - @layer daisyui.l1.l2.l3 { - display: flex; - width: fit-content; - flex-direction: column; - flex-wrap: wrap; - padding: calc(0.25rem * 2); - --menu-active-fg: var(--color-neutral-content); - --menu-active-bg: var(--color-neutral); - font-size: 0.875rem; - :where(li ul, li menu) { - position: relative; - margin-inline-start: calc(0.25rem * 4); - padding-inline-start: calc(0.25rem * 2); - white-space: nowrap; - &:before { - position: absolute; - inset-inline-start: calc(0.25rem * 0); - top: calc(0.25rem * 3); - bottom: calc(0.25rem * 3); - background-color: var(--color-base-content); - opacity: 10%; - width: var(--border); - content: ""; - } - } - :where(li > .menu-dropdown:not(.menu-dropdown-show)) { - display: none; - } - :where(li:not(.menu-title) > *:not(ul, menu, details, .menu-title, .btn)), :where(li:not(.menu-title) > details > summary:not(.menu-title)) { - display: grid; - grid-auto-flow: column; - align-content: flex-start; - align-items: center; - gap: calc(0.25rem * 2); - border-radius: var(--radius-field); - padding-inline: calc(0.25rem * 3); - padding-block: calc(0.25rem * 1.5); - text-align: start; - transition-property: color, background-color, box-shadow; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - grid-auto-columns: minmax(auto, max-content) auto max-content; - user-select: none; - } - :where(li > details > summary) { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - &::-webkit-details-marker { - display: none; - } - } - :where(li > details > summary), :where(li > .menu-dropdown-toggle) { - &:after { - justify-self: flex-end; - display: block; - height: 0.375rem; - width: 0.375rem; - rotate: -135deg; - translate: 0 -1px; - transition-property: rotate, translate; - transition-duration: 0.2s; - content: ""; - transform-origin: 50% 50%; - box-shadow: 2px 2px inset; - pointer-events: none; - } - } - details { - overflow: hidden; - interpolate-size: allow-keywords; - } - details::details-content { - block-size: 0; - @media (prefers-reduced-motion: no-preference) { - transition-behavior: allow-discrete; - transition-property: block-size, content-visibility; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - } - } - details[open]::details-content { - block-size: auto; - } - :where(li > details[open] > summary):after, :where(li > .menu-dropdown-toggle.menu-dropdown-show):after { - rotate: 45deg; - translate: 0 1px; - } - :where(li:not(.menu-title, .disabled) > *:not(ul, menu, details, .menu-title), li:not(.menu-title, .disabled) > details > summary:not(.menu-title) ):not(.menu-active, :active, .btn) { - &.menu-focus, &:focus-visible { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - color: var(--color-base-content); - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - } - :where(li:not(.menu-title, .disabled) > *:not(ul, menu, details, .menu-title):not(.menu-active, :active, .btn):hover, li:not(.menu-title, .disabled) > details > summary:not(.menu-title):not(.menu-active, :active, .btn):hover ) { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - box-shadow: 0 1px oklch(0% 0 0 / 0.01) inset, 0 -1px oklch(100% 0 0 / 0.01) inset; - } - :where(li:empty) { - background-color: var(--color-base-content); - opacity: 10%; - margin: 0.5rem 1rem; - height: 1px; - } - :where(li) { - position: relative; - display: flex; - flex-shrink: 0; - flex-direction: column; - flex-wrap: wrap; - align-items: stretch; - .badge { - justify-self: flex-end; - } - & > *:not(ul, menu, .menu-title, details, .btn):active, & > *:not(ul, menu, .menu-title, details, .btn).menu-active, & > details > summary:active { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - color: var(--menu-active-fg); - background-color: var(--menu-active-bg); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - &:not(&:active) { - box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg); - } - } - &.menu-disabled, [disabled] { - pointer-events: none; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - } - } - .dropdown:focus-within { - .menu-dropdown-toggle:after { - rotate: 45deg; - translate: 0 1px; - } - } - .dropdown-content { - margin-top: calc(0.25rem * 2); - padding: calc(0.25rem * 2); - &:before { - display: none; - } - } - } - } - .collapse-arrow { - @layer daisyui.l1.l2 { - > .collapse-title:after { - position: absolute; - display: block; - height: 0.5rem; - width: 0.5rem; - transform: translateY(-100%) rotate(45deg); - @media (prefers-reduced-motion: no-preference) { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 0.2s; - } - top: 50%; - inset-inline-end: 1.4rem; - content: ""; - transform-origin: 75% 75%; - box-shadow: 2px 2px; - pointer-events: none; - } - } - } - .collapse-plus { - @layer daisyui.l1.l2 { - > .collapse-title:after { - position: absolute; - display: block; - height: 0.5rem; - width: 0.5rem; - @media (prefers-reduced-motion: no-preference) { - transition-property: all; - transition-duration: 300ms; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - top: 0.9rem; - inset-inline-end: 1.4rem; - --tw-content: "+"; - content: var(--tw-content); - pointer-events: none; - } - } - } - .dropdown { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - position-area: var(--anchor-v, bottom) var(--anchor-h, span-right); - & > *:not(:has( ~ [class*="dropdown-content"])):focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - .dropdown-content { - position: absolute; - } - &.dropdown-close .dropdown-content, &:not(details, .dropdown-open, .dropdown-hover:hover, :focus-within) .dropdown-content, &.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible) ~ .dropdown-content { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - &[popover], .dropdown-content { - z-index: 999; - @media (prefers-reduced-motion: no-preference) { - animation: dropdown 0.2s; - transition-property: opacity, scale, display, overlay; - transition-behavior: allow-discrete; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - } - @starting-style { - &[popover], .dropdown-content { - scale: 95%; - opacity: 0; - } - } - &:not(.dropdown-close) { - &.dropdown-open, &:not(.dropdown-hover):focus, &:focus-within { - > [tabindex]:first-child { - pointer-events: none; - } - .dropdown-content { - opacity: 100%; - scale: 100%; - } - } - &.dropdown-hover:hover { - .dropdown-content { - opacity: 100%; - scale: 100%; - } - } - } - &:is(details) { - overflow: revert-layer; - summary { - &::-webkit-details-marker { - display: none; - } - } - } - &:where([popover]) { - background: #0000; - } - &[popover] { - position: fixed; - color: inherit; - @supports not (position-area: bottom) { - margin: auto; - &.dropdown-close, &.dropdown-open:not(:popover-open) { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - &::backdrop { - background-color: color-mix(in oklab, #000 30%, #0000); - } - } - &.dropdown-close, &:not(.dropdown-open, :popover-open) { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - } - } - } - .btn { - :where(&) { - @layer daisyui.l1.l2.l3 { - width: unset; - } - } - .prose :where(a&:not(.btn-link)):not(:where([class~="not-prose"], [class~="not-prose"] *)) { - text-decoration-line: none; - } - @layer daisyui.l1.l2.l3 { - --size: calc(var(--size-field, 0.25rem) * 10); - --btn-p: 1rem; - --btn-fg: var(--color-base-content); - display: inline-flex; - flex-shrink: 0; - cursor: pointer; - flex-wrap: nowrap; - align-items: center; - justify-content: center; - gap: calc(0.25rem * 1.5); - text-align: center; - vertical-align: middle; - outline-offset: 2px; - webkit-user-select: none; - user-select: none; - font-weight: 600; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - border-width: var(--border); - touch-action: manipulation; - transition-property: color, background-color, border-color, box-shadow, transform; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-duration: 0.2s; - --btn-bg: var(--btn-color, var(--color-base-200)); - --btn-border: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix( - in oklab, - var(--btn-color, var(--color-base-200)), - #000 calc(var(--depth) * 5%) - ); - } - --btn-soft-bg: initial; - --btn-shadow: 0 3px 2px -2px var(--btn-bg), - 0 4px 3px -2px var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), - 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); - } - --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); - height: var(--size); - padding-inline: var(--btn-p); - font-size: var(--fontsize, 0.875rem); - background-color: var(--btn-bg); - color: var(--btn-fg); - border-color: var(--btn-border); - border-style: var(--btn-border-style, solid); - outline-color: var(--btn-color, var(--color-base-content)); - --tw-prose-links: var(--btn-fg); - background-image: none, var(--fx-noise); - background-size: auto, calc(var(--noise, 0) * 100%); - text-shadow: 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 0.15)); - box-shadow: var(--btn-inset) inset, var(--btn-shadow); - &:is([type="checkbox"], [type="radio"]) { - appearance: none; - &[aria-label]::after { - --tw-content: attr(aria-label); - content: var(--tw-content); - } - } - } - @layer daisyui.l1 { - &:where(:checked:not(.filter [type="radio"].btn)) { - --btn-color: var(--color-primary); - --btn-fg: var(--color-primary-content); - } - @media (hover: hover) { - &:hover { - --btn-bg: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-bg: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 7%); - } - color: var(--btn-fg); - --btn-border: var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%)); - } - --btn-border-style: solid; - --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); - --btn-shadow: 0 3px 2px -2px var(--btn-bg), - 0 4px 3px -2px var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), - 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); - } - } - } - &:active:not(.btn-active) { - translate: 0 0.5px; - --btn-bg: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-bg: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 5%); - } - color: var(--btn-fg, var(--color-base-content)); - --btn-border: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 7%); - } - --btn-border-style: solid; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - } - &:where(:checked:not(.filter [type="radio"].btn), :not([type="radio"], [type="checkbox"]):focus-visible ) { - --btn-bg: var(--btn-color, var(--color-base-200)); - color: var(--btn-fg, var(--color-base-content)); - --btn-border: var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%)); - } - --btn-border-style: solid; - --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); - --btn-shadow: 0 3px 2px -2px var(--btn-bg), - 0 4px 3px -2px var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), - 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); - } - isolation: isolate; - } - &:focus-visible, &:has(:focus-visible) { - outline-width: 2px; - outline-style: solid; - isolation: isolate; - } - } - &:is(:disabled, [disabled], [aria-disabled="true"]) { - @layer daisyui { - pointer-events: none; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklch, var(--color-base-content) 20%, #0000); - } - --btn-bg: #0000; - --btn-border: #0000; - background-image: none; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - &:not(.btn-link, .btn-ghost) { - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - } - } - } - } - .collapse { - &:not(td, tr, colgroup) { - visibility: revert-layer; - } - @layer daisyui.l1.l2.l3 { - display: grid; - position: relative; - border-radius: var(--radius-box, 1rem); - width: 100%; - grid-template-rows: max-content 0fr; - grid-template-columns: minmax(0, 1fr); - isolation: isolate; - @media (prefers-reduced-motion: no-preference) { - transition: grid-template-rows 0.2s; - } - > input:is([type="checkbox"], [type="radio"]) { - grid-column-start: 1; - grid-row-start: 1; - appearance: none; - opacity: 0; - z-index: 1; - width: 100%; - padding: 1rem; - padding-inline-end: 3rem; - min-height: 1lh; - transition: background-color 0.2s ease-out; - } - &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ), &:not(.collapse-close):has( > input:is([type="checkbox"], [type="radio"]):checked) { - grid-template-rows: max-content 1fr; - } - &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ) > .collapse-content, &:not(.collapse-close) > :where(input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-content) { - --overflow-delay: 0.2s; - overflow: revert-layer; - content-visibility: visible; - min-height: fit-content; - @supports not (content-visibility: visible) { - visibility: visible; - } - } - &:focus-visible, &:has( > input:is([type="checkbox"], [type="radio"]):focus-visible), &:has(summary:focus-visible) { - outline-color: var(--color-base-content); - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; - } - &:not(.collapse-close) { - > input[type="checkbox"], > input[type="radio"]:not(:checked), > .collapse-title { - cursor: pointer; - } - } - &[tabindex]:focus:not(.collapse-close, .collapse[open]), &[tabindex]:focus-within:not(.collapse-close, .collapse[open]) { - > .collapse-title { - cursor: unset; - } - } - &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ) > :where(.collapse-content), &:not(.collapse-close) > :where(input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-content) { - padding-bottom: 1rem; - } - &:is(details) { - width: 100%; - &::details-content { - --overflow-delay: 0s; - overflow: clip; - height: 0; - } - &:where([open])::details-content { - overflow: revert-layer; - height: auto; - } - @media (prefers-reduced-motion: no-preference) { - &::details-content { - transition: overflow 0.2s allow-discrete var(--overflow-delay), content-visibility 0.2s allow-discrete, visibility 0.2s allow-discrete, min-height 0.2s ease-out allow-discrete, padding 0.1s ease-out 20ms, background-color 0.2s ease-out, height 0.2s; - interpolate-size: allow-keywords; - } - &:where([open])::details-content { - --overflow-delay: 0.2s; - } - } - > summary { - position: relative; - display: block; - outline: none; - &::-webkit-details-marker { - display: none; - } - } - } - } - @layer daisyui.l1.l2 { - &:is([open]) { - &.collapse-arrow { - > .collapse-title:after { - @media (prefers-reduced-motion: no-preference) { - transform: translateY(-50%) rotate(225deg); - } - } - } - } - &.collapse-open { - &.collapse-arrow { - > .collapse-title:after { - @media (prefers-reduced-motion: no-preference) { - transform: translateY(-50%) rotate(225deg); - } - } - } - &.collapse-plus { - > .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - } - &[tabindex].collapse-arrow:focus:not(.collapse-close), &.collapse-arrow[tabindex]:focus-within:not(.collapse-close) { - > .collapse-title:after { - transform: translateY(-50%) rotate(225deg); - } - } - &.collapse-arrow:not(.collapse-close) { - > input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-title:after { - transform: translateY(-50%) rotate(225deg); - } - } - &[open] { - &.collapse-plus { - > .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - } - &[tabindex].collapse-plus:focus:not(.collapse-close) { - > .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - &.collapse-plus:not(.collapse-close) { - > input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - } - } - .collapse-content { - @layer daisyui.l1.l2.l3 { - --overflow-delay: 0s; - content-visibility: hidden; - overflow: clip; - grid-column-start: 1; - grid-row-start: 2; - min-height: 0; - padding-left: 1rem; - padding-right: 1rem; - cursor: unset; - @supports not (content-visibility: hidden) { - visibility: hidden; - } - @media (prefers-reduced-motion: no-preference) { - transition: overflow 0.2s allow-discrete var(--overflow-delay), content-visibility 0.2s allow-discrete, visibility 0.2s allow-discrete, min-height 0.2s ease-out allow-discrete, padding 0.1s ease-out 20ms, background-color 0.2s ease-out; - } - details > & { - content-visibility: visible; - } - } - } - .filter { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-wrap: wrap; - [type="radio"] { - width: auto; - } - input { - overflow: hidden; - opacity: 100%; - scale: 1; - transition: visibility 0.1s allow-discrete, margin 0.1s, opacity 0.3s, padding 0.3s, border-width 0.1s; - &.filter-reset { - aspect-ratio: 1 / 1; - &::after { - --tw-content: "×"; - content: var(--tw-content); - } - } - } - > input:not(:last-child), > :not(:last-child) input { - margin-inline-end: 0.25rem; - } - } - @layer daisyui.l1 { - &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible) { - visibility: hidden; - } - &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible), &:not(:has(:focus-visible)):has(:checked:not(.filter-reset, [type="checkbox"])) :is(input, button):not(:checked, .filter-reset, [type="reset"]) { - margin-inline: 0; - width: 0; - padding-inline: 0; - opacity: 0%; - scale: 0; - border-width: 0; - } - } - } - .collapse-open { - @layer daisyui.l1.l2 { - grid-template-rows: max-content 1fr; - > .collapse-content { - --overflow-delay: 0.2s; - overflow: revert-layer; - content-visibility: visible; - min-height: fit-content; - padding-bottom: 1rem; - @supports not (content-visibility: visible) { - visibility: visible; - } - } - } - } - .collapse { - visibility: collapse; - } - .visible { - visibility: visible; - } - .list { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-direction: column; - font-size: 0.875rem; - .list-row { - --list-grid-cols: minmax(0, auto) 1fr; - position: relative; - display: grid; - grid-auto-flow: column; - gap: calc(0.25rem * 4); - border-radius: var(--radius-box); - padding: calc(0.25rem * 4); - word-break: break-word; - grid-template-columns: var(--list-grid-cols); - } - & > :not(:last-child) { - &.list-row, .list-row { - &:after { - content: ""; - border-bottom: var(--border) solid; - inset-inline: var(--radius-box); - position: absolute; - bottom: 0; - border-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-base-content) 5%, transparent); - } - } - } - } - } - @layer daisyui.l1.l2 { - .list-row { - &:has(.list-col-grow:nth-child(1)) { - --list-grid-cols: 1fr; - } - &:has(.list-col-grow:nth-child(2)) { - --list-grid-cols: minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(3)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(4)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(5)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(6)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) - minmax(0, auto) 1fr; - } - > * { - grid-row-start: 1; - } - } - } - } - .toast { - @layer daisyui.l1.l2.l3 { - position: fixed; - inset-inline-start: auto; - inset-inline-end: calc(0.25rem * 4); - top: auto; - bottom: calc(0.25rem * 4); - display: flex; - flex-direction: column; - gap: calc(0.25rem * 2); - background-color: transparent; - translate: var(--toast-x, 0) var(--toast-y, 0); - width: max-content; - max-width: calc(100vw - 2rem); - & > * { - @media (prefers-reduced-motion: no-preference) { - animation: toast 0.25s ease-out; - } - } - } - } - .toggle { - @layer daisyui.l1.l2.l3 { - border: var(--border) solid currentColor; - color: var(--input-color); - position: relative; - display: inline-grid; - flex-shrink: 0; - cursor: pointer; - appearance: none; - place-content: center; - vertical-align: middle; - webkit-user-select: none; - user-select: none; - grid-template-columns: 0fr 1fr 1fr; - --radius-selector-max: calc( - var(--radius-selector) + var(--radius-selector) + var(--radius-selector) - ); - border-radius: calc( var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)) ); - padding: var(--toggle-p); - box-shadow: 0 1px currentColor inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset; - } - transition: color 0.3s, grid-template-columns 0.2s; - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --toggle-p: calc(var(--size) * 0.125); - --size: calc(var(--size-selector, 0.25rem) * 6); - width: calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2); - height: var(--size); - > * { - z-index: 1; - grid-column: span 1 / span 1; - grid-column-start: 2; - grid-row-start: 1; - height: 100%; - cursor: pointer; - appearance: none; - background-color: transparent; - padding: calc(0.25rem * 0.5); - transition: opacity 0.2s, rotate 0.4s; - border: none; - &:focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:nth-child(2) { - color: var(--color-base-100); - rotate: 0deg; - } - &:nth-child(3) { - color: var(--color-base-100); - opacity: 0%; - rotate: -15deg; - } - } - &:has(:checked) { - > :nth-child(2) { - opacity: 0%; - rotate: 15deg; - } - > :nth-child(3) { - opacity: 100%; - rotate: 0deg; - } - } - &:before { - position: relative; - inset-inline-start: calc(0.25rem * 0); - grid-column-start: 2; - grid-row-start: 1; - aspect-ratio: 1 / 1; - height: 100%; - width: 100%; - border-radius: var(--radius-selector); - background-color: currentcolor; - translate: 0; - --tw-content: ""; - content: var(--tw-content); - transition: background-color 0.1s, translate 0.2s, inset-inline-start 0.2s; - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px currentColor; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000); - } - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - } - @media (forced-colors: active) { - &:before { - outline-style: var(--tw-outline-style); - outline-width: 1px; - outline-offset: calc(1px * -1); - } - } - @media print { - &:before { - outline: 0.25rem solid; - outline-offset: -1rem; - } - } - &:focus-visible, &:has(:focus-visible) { - outline: 2px solid currentColor; - outline-offset: 2px; - } - &:checked, &[aria-checked="true"], &:has( > input:checked) { - grid-template-columns: 1fr 1fr 0fr; - background-color: var(--color-base-100); - --input-color: var(--color-base-content); - &:before { - background-color: currentcolor; - } - @starting-style { - &:before { - opacity: 0; - } - } - } - &:indeterminate { - grid-template-columns: 0.5fr 1fr 0.5fr; - } - &:disabled { - cursor: not-allowed; - opacity: 30%; - &:before { - background-color: transparent; - border: var(--border) solid currentColor; - } - } - } - } - .input { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - flex-shrink: 1; - appearance: none; - align-items: center; - gap: calc(0.25rem * 2); - background-color: var(--color-base-100); - padding-inline: calc(0.25rem * 3); - vertical-align: middle; - white-space: nowrap; - --size: calc(var(--size-field, 0.25rem) * var(--in-size-mul)); - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - cursor: text; - width: clamp(3rem, 20rem, 100%); - height: var(--size); - font-size: max(var(--font-size, 0rem), var(--font-size-min)); - touch-action: manipulation; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - border: var(--border) solid var(--input-color, #0000); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - input { - height: 100%; - width: 100%; - appearance: none; - background-color: transparent; - border: none; - &::placeholder { - color: var(--color-base-content); - opacity: 50%; - } - &:focus, &:focus-within { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &::-webkit-calendar-picker-indicator { - inset-inline-end: -0.15em; - } - } - &::-webkit-inner-spin-button { - margin-inline-end: -10px; - } - &::-webkit-calendar-picker-indicator { - inset-inline-end: 0.75em; - } - input&, input { - position: relative; - display: inline-flex; - &[type="url"], &[type="email"] { - text-align: left; - } - &::-webkit-datetime-edit, &::-webkit-date-and-time-value { - display: grid; - min-height: 100%; - align-items: center; - text-align: inherit; - } - &::-webkit-inner-spin-button { - margin-block: calc(0.25rem * var(--spin-my)); - } - &::-webkit-calendar-picker-indicator { - position: absolute; - width: 1em; - height: 1em; - cursor: pointer; - } - &::-webkit-color-swatch-wrapper { - padding-block: 0.25rem; - } - } - &:focus, &:focus-within { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - } - @media (pointer: coarse) { - @supports (-webkit-touch-callout: none) { - &:focus, &:focus-within { - --font-size: 1rem; - } - } - } - &:has( > input[disabled]), &:is(:disabled, [disabled]), fieldset:disabled & { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - &:is(input), :is(input) { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 40%, transparent); - } - } - box-shadow: none; - &::placeholder, ::placeholder { - color: var(--color-base-content); - opacity: 20%; - } - } - &:has( > input[disabled]) > input[disabled] { - cursor: not-allowed; - } - } - @layer daisyui.l1.l2 { - --in-size-mul: 10; - --font-size-min: 0.875rem; - --spin-my: -3; - .floating-label:has(&) { - --top-mul: 5; - --font-size: 0.875rem; - } - } - } - .table { - @layer daisyui.l1.l2.l3 { - font-size: 0.875rem; - position: relative; - width: 100%; - border-collapse: separate; - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); - border-radius: var(--radius-box); - text-align: left; - &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) { - text-align: right; - } - tr.row-hover { - &, &:nth-child(even) { - &:hover { - @media (hover: hover) { - background-color: var(--color-base-200); - } - } - } - } - :where(th, td) { - padding-inline: calc(0.25rem * 4); - padding-block: calc(0.25rem * 3); - vertical-align: middle; - } - :where(thead, tfoot) { - white-space: nowrap; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 60%, transparent); - } - font-size: 0.875rem; - font-weight: 600; - } - :where(tfoot tr:first-child :is(td, th)) { - border-top: var(--border) solid var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-top: var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000); - } - } - :where(.table-pin-rows thead tr) { - position: sticky; - top: 0; - z-index: 1; - background-color: var(--color-base-100); - } - :where(.table-pin-rows tfoot tr) { - position: sticky; - bottom: 0; - z-index: 1; - background-color: var(--color-base-100); - } - :where(.table-pin-cols tr th) { - position: sticky; - right: 0; - left: 0; - background-color: var(--color-base-100); - } - :where(thead tr :is(td, th), tbody tr:not(:last-child) :is(td, th)) { - border-bottom: var(--border) solid var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-bottom: var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000); - } - } - } - } - .aura { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - --aura-padding: 0.125rem; - padding: var(--aura-padding); - border-radius: calc(var(--aura-padding) + var(--aura-radius, var(--radius-box))); - animation: aura var(--tw-duration, 6s) linear infinite; - background-image: conic-gradient(from var(--aura-angle), transparent 225deg, currentColor); - &:has( > .card, > .alert) { - --aura-radius: var(--radius-box); - } - &:has( > .btn, > .input, > .select) { - --aura-radius: var(--radius-field); - } - &:has( > .checkbox, > .toggle, > .badge) { - --aura-radius: var(--radius-selector); - } - &:before, &:after { - animation: inherit; - background-color: inherit; - background-image: inherit; - border-radius: inherit; - position: absolute; - top: calc(1 / 2 * 100%); - left: calc(1 / 2 * 100%); - z-index: 0; - display: block; - opacity: 70%; - filter: blur(0.25rem); - translate: -50% -50%; - width: 100%; - height: 100%; - content: ""; - } - &:after { - opacity: 30%; - filter: blur(1rem); - } - & > * { - position: relative; - z-index: 1; - } - } - } - .select { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - flex-shrink: 1; - appearance: none; - align-items: center; - gap: calc(0.25rem * 1.5); - background-color: var(--color-base-100); - padding-inline-start: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 7); - vertical-align: middle; - --size: calc(var(--size-field, 0.25rem) * var(--sl-size-mul)); - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - width: clamp(3rem, 20rem, 100%); - height: var(--size); - font-size: max(var(--font-size, 0rem), var(--font-size-min)); - touch-action: manipulation; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - background-image: linear-gradient(45deg, #0000 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, #0000 50%); - background-position: calc(100% - 20px) calc(1px + 50%), calc(100% - 16.1px) calc(1px + 50%); - background-size: 4px 4px, 4px 4px; - background-repeat: no-repeat; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - border: var(--border) solid var(--input-color, #0000); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - [dir="rtl"] & { - background-position: calc(0% + 12px) calc(1px + 50%), calc(0% + 16px) calc(1px + 50%); - &::picker(select), select::picker(select) { - translate: 0.5rem 0; - } - } - &[multiple] { - height: auto; - overflow: auto; - padding-block: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 3); - background-image: none; - } - select { - margin-inline-start: calc(0.25rem * -3); - margin-inline-end: calc(0.25rem * -7); - width: calc(100% + 2.75rem); - appearance: none; - padding-inline-start: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 7); - height: calc(100% - calc(var(--border) * 2)); - align-items: center; - background: inherit; - border-radius: inherit; - border-style: none; - &::placeholder { - color: var(--color-base-content); - opacity: 50%; - } - &:focus, &:focus-within { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:not(:last-child) { - margin-inline-end: calc(0.25rem * -5.5); - background-image: none; - } - } - &:focus, &:focus-within, &:open { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - background-image: linear-gradient(135deg, #0000 50%, currentColor 50%), linear-gradient(45deg, currentColor 50%, #0000 50%); - } - &:has( > select[disabled]), &:is(:disabled, [disabled]), fieldset:disabled & { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - &:is(select), :is(select) { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 40%, transparent); - } - } - box-shadow: none; - &::placeholder, ::placeholder { - color: var(--color-base-content); - opacity: 20%; - } - } - &:has( > select[disabled]) > select[disabled] { - cursor: not-allowed; - } - &, & select { - @supports (appearance: base-select) { - &, &::picker(select) { - appearance: base-select; - } - } - &::picker(select) { - color: inherit; - max-height: min(24rem, 70dvh); - margin-inline: 0.5rem; - translate: -0.5rem 0; - border: var(--border) solid var(--color-base-200); - margin-block: calc(0.25rem * 2); - border-radius: var(--radius-box); - padding: calc(0.25rem * 2); - background-color: inherit; - box-shadow: 0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/0.2); - box-shadow: 0 20px 25px -5px rgb(0 0 0 / calc(var(--depth) * 0.1)), 0 8px 10px -6px rgb(0 0 0 / calc(var(--depth) * 0.1)); - } - &::picker-icon { - display: none; - } - selectedcontent { - width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - optgroup { - padding-top: 0.5em; - option { - &:nth-child(1) { - margin-top: 0.5em; - } - } - } - option { - border-radius: var(--radius-field); - padding-block: calc(0.25rem * 1.5); - padding-inline: calc(0.25rem * var(--option-px)); - transition-property: color, background-color; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - white-space: normal; - &:not(:disabled) { - &:hover, &:focus-visible { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:active { - background-color: var(--color-neutral); - color: var(--color-neutral-content); - box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--color-neutral); - } - } - } - } - } - @layer daisyui.l1.l2 { - --sl-size-mul: 10; - --font-size-min: 0.875rem; - --option-px: 3; - .floating-label:has(&) { - --top-mul: 5; - --font-size: 0.875rem; - } - } - } - .collapse-title { - @layer daisyui.l1.l2.l3 { - grid-column-start: 1; - grid-row-start: 1; - position: relative; - width: 100%; - padding: 1rem; - padding-inline-end: 3rem; - min-height: 1lh; - transition: background-color 0.2s ease-out; - } - } - .checkbox { - @layer daisyui.l1.l2.l3 { - border: var(--border) solid var(--input-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - border: var(--border) solid var(--input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000)); - } - position: relative; - display: inline-block; - flex-shrink: 0; - cursor: pointer; - appearance: none; - border-radius: var(--radius-selector); - padding: 0.25rem; - vertical-align: middle; - color: var(--color-base-content); - box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 0 #0000 inset, 0 0 #0000; - transition: background-color 0.2s, box-shadow 0.2s; - --size: calc(var(--size-selector, 0.25rem) * 6); - width: var(--size); - height: var(--size); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - &:before { - --tw-content: ""; - content: var(--tw-content); - display: block; - width: 100%; - height: 100%; - rotate: 45deg; - background-color: currentcolor; - opacity: 0%; - transition: clip-path 0.3s, opacity 0.1s, rotate 0.3s, translate 0.3s; - transition-delay: 0.1s; - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 70% 80%, 70% 100%); - box-shadow: 0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - font-size: 1rem; - line-height: 0.75; - } - &:focus-visible { - outline: 2px solid var(--input-color, currentColor); - outline-offset: 2px; - } - &:checked, &[aria-checked="true"] { - background-color: var(--input-color, #0000); - box-shadow: 0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); - &:before { - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 0%, 70% 0%, 70% 100%); - opacity: 100%; - } - @media (forced-colors: active) { - &:before { - rotate: 0deg; - background-color: transparent; - --tw-content: "✔︎"; - clip-path: none; - } - } - @media print { - &:before { - rotate: 0deg; - background-color: transparent; - --tw-content: "✔︎"; - clip-path: none; - } - } - } - &:indeterminate { - background-color: var(--input-color, var(--color-base-content) ); - @supports (color: color-mix(in lab, red, red)) { - background-color: var(--input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000) ); - } - &:before { - rotate: 0deg; - opacity: 100%; - translate: 0 -35%; - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 80% 80%, 80% 100%); - } - } - } - &:disabled { - @layer daisyui.l1.l2 { - cursor: not-allowed; - opacity: 20%; - } - } - } - .radio { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - flex-shrink: 0; - cursor: pointer; - appearance: none; - border-radius: calc(infinity * 1px); - padding: 0.25rem; - vertical-align: middle; - border: var(--border) solid var(--input-color, currentColor); - @supports (color: color-mix(in lab, red, red)) { - border: var(--border) solid var(--input-color, color-mix(in srgb, currentColor 20%, #0000)); - } - box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset; - --size: calc(var(--size-selector, 0.25rem) * 6); - width: var(--size); - height: var(--size); - color: var(--input-color, currentColor); - &:before { - display: block; - width: 100%; - height: 100%; - border-radius: calc(infinity * 1px); - --tw-content: ""; - content: var(--tw-content); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - } - &:focus-visible { - outline: 2px solid currentColor; - } - &:checked, &[aria-checked="true"] { - border-color: currentcolor; - background-color: var(--color-base-100); - @media (prefers-reduced-motion: no-preference) { - animation: radio 0.2s ease-out; - } - &:before { - background-color: currentcolor; - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); - } - @media (forced-colors: active) { - &:before { - outline-style: var(--tw-outline-style); - outline-width: 1px; - outline-offset: calc(1px * -1); - } - } - @media print { - &:before { - outline: 0.25rem solid; - outline-offset: -1rem; - } - } - } - } - &:disabled { - @layer daisyui.l1.l2 { - cursor: not-allowed; - opacity: 20%; - } - } - } - .rating { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - vertical-align: middle; - --size: var(--size-selector, 0.25rem) * 6; - input { - cursor: pointer; - appearance: none; - } - * { - border-radius: 0; - background-color: var(--color-base-content); - opacity: 20%; - width: calc(var(--size) * 1); - height: calc(var(--size)); - @media (prefers-reduced-motion: no-preference) { - animation: rating 0.25s ease-out; - } - } - .rating-hidden { - width: calc(0.25rem * 2); - background-color: transparent; - } - :checked, [aria-checked="true"], [aria-current="true"], :has( ~ :checked, ~ [aria-checked="true"], ~ [aria-current="true"]) { - opacity: 100%; - } - :focus-visible { - scale: 1.1; - @media (prefers-reduced-motion: no-preference) { - transition: scale 0.2s ease-out; - } - } - :active:focus { - animation: none; - scale: 1.1; - } - } - @layer daisyui.l1.l2 { - --size: var(--size-selector, 0.25rem) * 6; - } - } - .navbar { - @layer daisyui.l1.l2.l3 { - display: flex; - width: 100%; - align-items: center; - padding: 0.5rem; - min-height: 4rem; - } - :where(&) { - @layer daisyui.l1.l2 { - position: relative; - } - } - } - .card { - @layer daisyui.l1.l2.l3 { - position: relative; - display: flex; - flex-direction: column; - border-radius: var(--radius-box); - transition: outline 0.2s ease-in-out; - outline: 2px solid #0000; - outline-offset: 2px; - &:focus-visible, &[aria-checked="true"], &:has( > :checked, > :is([type="checkbox"], [type="radio"]):focus-visible) { - outline-color: currentColor; - } - &:has( > :checked:focus-visible), &[aria-checked="true"]:focus-visible, &[aria-checked="true"]:has( > :is([type="checkbox"], [type="radio"]):focus-visible) { - outline-width: 4px; - } - &:has( > :is([type="checkbox"], [type="radio"])) { - cursor: pointer; - user-select: none; - } - > :is([type="checkbox"], [type="radio"]) { - appearance: none; - } - } - @layer daisyui.l1.l2.l3.l4 { - figure:first-child { - overflow: hidden; - border-start-start-radius: inherit; - border-start-end-radius: inherit; - border-end-start-radius: unset; - border-end-end-radius: unset; - } - figure:last-child { - overflow: hidden; - border-start-start-radius: unset; - border-start-end-radius: unset; - border-end-start-radius: inherit; - border-end-end-radius: inherit; - } - figure { - display: flex; - align-items: center; - justify-content: center; - } - } - } - .stats { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-grid; - grid-auto-flow: column; - overflow-x: auto; - border-radius: var(--radius-box); - } - } - .progress { - @layer daisyui.l1.l2.l3 { - position: relative; - height: calc(0.25rem * 2); - width: 100%; - appearance: none; - overflow: hidden; - border-radius: var(--radius-box); - background-color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, currentcolor 20%, transparent); - } - color: var(--color-base-content); - &:indeterminate { - background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); - background-size: 200%; - background-position-x: 15%; - @media (prefers-reduced-motion: no-preference) { - animation: progress 5s ease-in-out infinite; - } - @supports (-moz-appearance: none) { - &::-moz-progress-bar { - background-color: transparent; - @media (prefers-reduced-motion: no-preference) { - animation: progress 5s ease-in-out infinite; - background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); - background-size: 200%; - background-position-x: 15%; - } - } - } - } - @supports (-moz-appearance: none) { - &::-moz-progress-bar { - border-radius: var(--radius-box); - background-color: currentcolor; - @media (prefers-reduced-motion: no-preference) { - transition: inline-size 0.3s ease; - } - } - } - @supports (-webkit-appearance: none) { - &::-webkit-progress-bar { - border-radius: var(--radius-box); - background-color: transparent; - } - &::-webkit-progress-value { - border-radius: var(--radius-box); - background-color: currentColor; - @media (prefers-reduced-motion: no-preference) { - transition: inline-size 0.3s ease; - } - } - } - } - } - .static { - position: static; - } - .dropdown-right { - @layer daisyui.l1.l2 { - --anchor-h: right; - --anchor-v: span-bottom; - .dropdown-content { - inset-inline-start: 100%; - top: 0; - bottom: auto; - transform-origin: 0; - } - } - } - .dropdown-left { - @layer daisyui.l1.l2 { - --anchor-h: left; - --anchor-v: span-bottom; - .dropdown-content { - inset-inline-end: 100%; - top: 0; - bottom: auto; - transform-origin: 100%; - } - } - } - .dropdown-end { - @layer daisyui.l1.l2 { - --anchor-h: span-left; - :where(.dropdown-content) { - inset-inline-end: calc(0.25rem * 0); - translate: 0 0; - [dir="rtl"] & { - translate: 0 0; - } - } - &.dropdown-left { - --anchor-h: left; - --anchor-v: span-top; - .dropdown-content { - top: auto; - bottom: 0; - } - } - &.dropdown-right { - --anchor-h: right; - --anchor-v: span-top; - .dropdown-content { - top: auto; - bottom: 0; - } - } - } - } - .textarea { - @layer daisyui.l1.l2.l3 { - min-height: calc(0.25rem * 20); - flex-shrink: 1; - appearance: none; - border-radius: var(--radius-field); - background-color: var(--color-base-100); - padding-inline: calc(0.25rem * 3); - padding-block: calc(0.25rem * 2); - vertical-align: middle; - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - width: clamp(3rem, 20rem, 100%); - font-size: max(var(--font-size, 0rem), var(--font-size-min)); - touch-action: manipulation; - border: var(--border) solid var(--input-color, #0000); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - textarea { - appearance: none; - background-color: transparent; - border: none; - &::placeholder { - color: var(--color-base-content); - opacity: 50%; - } - &:focus, &:focus-within { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - } - &:focus, &:focus-within { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - } - @media (pointer: coarse) { - @supports (-webkit-touch-callout: none) { - &:focus, &:focus-within { - --font-size: 1rem; - } - } - } - &:has( > textarea[disabled]), &:is(:disabled, [disabled]) { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 40%, transparent); - } - box-shadow: none; - &::placeholder, ::placeholder { - color: var(--color-base-content); - opacity: 20%; - } - } - &:has( > textarea[disabled]) > textarea[disabled] { - cursor: not-allowed; - } - } - @layer daisyui.l1.l2 { - --font-size-min: 0.875rem; - .floating-label:has(&) { - --top-mul: 5; - --font-size: 0.875rem; - } - } - } - .stack { - @layer daisyui.l1.l2.l3 { - display: inline-grid; - grid-template-columns: 3px 4px 1fr 4px 3px; - grid-template-rows: 3px 4px 1fr 4px 3px; - & > * { - height: 100%; - width: 100%; - &:nth-child(n + 2) { - width: 100%; - opacity: 70%; - } - &:nth-child(2) { - z-index: 2; - opacity: 90%; - } - &:nth-child(1) { - z-index: 3; - width: 100%; - } - } - } - @layer daisyui.l1.l2 { - &, &.stack-bottom { - > * { - grid-column: 3 / 4; - grid-row: 3 / 6; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 1 / 6; - grid-row: 1 / 4; - } - } - } - &.stack-top { - > * { - grid-column: 3 / 4; - grid-row: 1 / 4; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 1 / 6; - grid-row: 3 / 6; - } - } - } - &.stack-start { - > * { - grid-column: 1 / 4; - grid-row: 3 / 4; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 3 / 6; - grid-row: 1 / 6; - } - } - } - &.stack-end { - > * { - grid-column: 3 / 6; - grid-row: 3 / 4; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 1 / 4; - grid-row: 1 / 6; - } - } - } - } - } - .z-10 { - z-index: 10; - } - .tab-content { - @layer daisyui.l1.l2.l3 { - order: var(--tabcontent-order); - display: none; - border-color: transparent; - --tabcontent-radius-ss: var(--radius-box); - --tabcontent-radius-se: var(--radius-box); - --tabcontent-radius-es: var(--radius-box); - --tabcontent-radius-ee: var(--radius-box); - --tabcontent-order: 1; - width: 100%; - height: calc(100% - var(--tab-height) + var(--border)); - margin: var(--tabcontent-margin); - border-width: var(--border); - border-start-start-radius: var(--tabcontent-radius-ss); - border-start-end-radius: var(--tabcontent-radius-se); - border-end-start-radius: var(--tabcontent-radius-es); - border-end-end-radius: var(--tabcontent-radius-ee); - } - } - .hero { - @layer daisyui.l1.l2.l3 { - display: grid; - width: 100%; - place-items: center; - background-size: cover; - background-position: center; - & > * { - grid-column-start: 1; - grid-row-start: 1; - } - } - } - .stat-value { - @layer daisyui.l1.l2.l3 { - grid-column-start: 1; - white-space: nowrap; - font-size: 2rem; - font-weight: 800; - } - } - .stat-title { - @layer daisyui.l1.l2.l3 { - grid-column-start: 1; - white-space: nowrap; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 60%, transparent); - } - font-size: 0.75rem; - } - } - .container { - width: 100%; - @media (width >= 40rem) { - max-width: 40rem; - } - @media (width >= 48rem) { - max-width: 48rem; - } - @media (width >= 64rem) { - max-width: 64rem; - } - @media (width >= 80rem) { - max-width: 80rem; - } - @media (width >= 96rem) { - max-width: 96rem; - } - } - .divider { - @layer daisyui.l1.l2.l3 { - display: flex; - height: calc(0.25rem * 4); - flex-direction: row; - align-items: center; - align-self: stretch; - white-space: nowrap; - margin: var(--divider-m, 1rem 0); - --divider-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --divider-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - &:before, &:after { - content: ""; - height: calc(0.25rem * 0.5); - width: 100%; - flex-grow: 1; - background-color: var(--divider-color); - } - @media print { - &:before, &:after { - border: 0.5px solid; - } - } - &:not(:empty) { - gap: calc(0.25rem * 4); - } - } - } - .mx-auto { - margin-inline: auto; - } - .my-2 { - margin-block: calc(var(--spacing) * 2); - } - .my-3 { - margin-block: calc(var(--spacing) * 3); - } - .my-4 { - margin-block: calc(var(--spacing) * 4); - } - .label { - @layer daisyui.l1.l2.l3 { - display: inline-flex; - align-items: center; - gap: calc(0.25rem * 1.5); - white-space: nowrap; - color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, currentcolor 60%, transparent); - } - &:has(input) { - cursor: pointer; - } - &:is(.input > *, .select > *) { - display: flex; - height: calc(100% - 0.5rem); - align-items: center; - padding-inline: calc(0.25rem * 3); - white-space: nowrap; - font-size: inherit; - &:first-child { - margin-inline-start: calc(0.25rem * -3); - margin-inline-end: calc(0.25rem * 3); - border-inline-end: var(--border) solid currentColor; - @supports (color: color-mix(in lab, red, red)) { - border-inline-end: var(--border) solid color-mix(in oklab, currentColor 10%, #0000); - } - } - &:last-child { - margin-inline-start: calc(0.25rem * 3); - margin-inline-end: calc(0.25rem * -3); - border-inline-start: var(--border) solid currentColor; - @supports (color: color-mix(in lab, red, red)) { - border-inline-start: var(--border) solid color-mix(in oklab, currentColor 10%, #0000); - } - } - } - } - } - .mt-1 { - margin-top: var(--spacing); - } - .mt-2 { - margin-top: calc(var(--spacing) * 2); - } - .mt-4 { - margin-top: calc(var(--spacing) * 4); - } - .mb-4 { - margin-bottom: calc(var(--spacing) * 4); - } - .mb-6 { - margin-bottom: calc(var(--spacing) * 6); - } - .ml-2 { - margin-left: calc(var(--spacing) * 2); - } - .status { - @layer daisyui.l1.l2.l3 { - display: inline-block; - aspect-ratio: 1 / 1; - width: calc(0.25rem * 2); - height: calc(0.25rem * 2); - border-radius: var(--radius-selector); - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - background-position: center; - background-repeat: no-repeat; - vertical-align: middle; - color: color-mix(in srgb, #000 30%, transparent); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-black) 30%, transparent); - } - background-image: radial-gradient( circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * 0.5)), #0000 ); - box-shadow: 0 2px 3px -1px currentColor; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000); - } - } - } - .badge { - @layer daisyui.l1.l2.l3 { - display: inline-flex; - align-items: center; - justify-content: center; - gap: calc(0.25rem * 2); - border-radius: var(--radius-selector); - vertical-align: middle; - color: var(--badge-fg); - border: var(--border) solid var(--badge-color, var(--color-base-200)); - font-size: 0.875rem; - width: fit-content; - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - background-color: var(--badge-bg); - --badge-bg: var(--badge-color, var(--color-base-100)); - --badge-fg: var(--color-base-content); - --size: calc(var(--size-selector, 0.25rem) * 6); - height: var(--size); - padding-inline: calc(var(--size) / 2 - var(--border)); - } - } - .tabs { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-wrap: wrap; - --tabs-height: auto; - --tabs-direction: row; - --tab-height: calc(var(--size-field, 0.25rem) * 10); - height: var(--tabs-height); - flex-direction: var(--tabs-direction); - } - } - .stat { - @layer daisyui.l1.l2.l3 { - display: inline-grid; - width: 100%; - column-gap: calc(0.25rem * 4); - padding-inline: calc(0.25rem * 6); - padding-block: calc(0.25rem * 4); - grid-template-columns: repeat(1, 1fr); - &:not(:last-child) { - border-inline-end: var(--border) dashed currentColor; - @supports (color: color-mix(in lab, red, red)) { - border-inline-end: var(--border) dashed color-mix(in oklab, currentColor 10%, #0000); - } - border-block-end: none; - } - } - } - .card-body { - @layer daisyui.l1.l2.l3 { - display: flex; - flex: auto; - flex-direction: column; - gap: calc(0.25rem * 2); - padding: var(--card-p, 1.5rem); - font-size: var(--card-fs, 0.875rem); - } - @layer daisyui.l1.l2.l3.l4 { - p { - flex-grow: 1; - } - } - } - .alert { - border-width: var(--border); - border-color: var(--alert-border-color, var(--color-base-200)); - @layer daisyui.l1.l2.l3 { - border-style: solid; - --alert-border-color: var(--color-base-200); - display: grid; - align-items: center; - gap: calc(0.25rem * 4); - border-radius: var(--radius-box); - padding-inline: calc(0.25rem * 4); - padding-block: calc(0.25rem * 3); - color: var(--color-base-content); - background-color: var(--alert-color, var(--color-base-200)); - justify-content: start; - justify-items: start; - grid-auto-flow: column; - grid-template-columns: auto; - text-align: start; - font-size: 0.875rem; - line-height: 1.25rem; - background-size: auto, calc(var(--noise) * 33%); - background-image: none, var(--fx-noise); - box-shadow: 0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * 0.08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * 0.08)); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * 0.08)) inset, 0 1px color-mix( in oklab, color-mix(in oklab, #000 20%, var(--alert-color, var(--color-base-200))) calc(var(--depth) * 20%), #0000 ), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * 0.08)); - } - &:has(:nth-child(2)) { - grid-template-columns: auto minmax(auto, 1fr); - } - } - } - .card-actions { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-wrap: wrap; - align-items: flex-start; - gap: calc(0.25rem * 2); - } - } - .card-title { - @layer daisyui.l1.l2.l3 { - display: flex; - align-items: center; - gap: calc(0.25rem * 2); - font-size: var(--cardtitle-fs, 1.125rem); - font-weight: 600; - } - } - .prose { - :root & { - --tw-prose-body: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-body: color-mix(in oklab, var(--color-base-content) 80%, #0000); - } - --tw-prose-headings: var(--color-base-content); - --tw-prose-lead: var(--color-base-content); - --tw-prose-links: var(--color-base-content); - --tw-prose-bold: var(--color-base-content); - --tw-prose-counters: var(--color-base-content); - --tw-prose-bullets: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-bullets: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-hr: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-hr: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-quotes: var(--color-base-content); - --tw-prose-quote-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-quote-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-captions: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-captions: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-code: var(--color-base-content); - --tw-prose-pre-code: var(--color-neutral-content); - --tw-prose-pre-bg: var(--color-neutral); - --tw-prose-th-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-th-borders: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-td-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-td-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-kbd: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-kbd: color-mix(in oklab, var(--color-base-content) 80%, #0000); - } - :where(code):not(pre > code) { - background-color: var(--color-base-200); - border-radius: var(--radius-selector); - border: var(--border) solid var(--color-base-300); - padding-inline: 0.5em; - padding-block: 0.2em; - font-weight: inherit; - &:before, &:after { - display: none; - } - } - } - } - .block { - display: block; - } - .flex { - display: flex; - } - .grid { - display: grid; - } - .hidden { - display: none; - } - .table { - display: table; - } - .btn-circle { - @layer daisyui.l1.l2 { - border-radius: calc(infinity * 1px); - padding-inline: 0; - width: var(--size); - height: var(--size); - } - } - .min-h-screen { - min-height: 100vh; - } - .w-56 { - width: calc(var(--spacing) * 56); - } - .w-fit { - width: fit-content; - } - .w-full { - width: 100%; - } - .max-w-5xl { - max-width: var(--container-5xl); - } - .max-w-none { - max-width: none; - } - .max-w-xl { - max-width: var(--container-xl); - } - .max-w-xs { - max-width: var(--container-xs); - } - .flex-1 { - flex: 1; - } - .flex-none { - flex: none; - } - .flex-shrink { - flex-shrink: 1; - } - .flex-grow { - flex-grow: 1; - } - .border-collapse { - border-collapse: collapse; - } - .transform { - transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); - } - .skeleton { - @layer daisyui.l1.l2.l3 { - border-radius: var(--radius-box); - background-color: var(--color-base-300); - @media (prefers-reduced-motion: reduce) { - transition-duration: 15s; - } - will-change: background-position; - background-image: linear-gradient( 105deg, #0000 0% 40%, var(--color-base-100) 50%, #0000 60% 100% ); - background-size: 200% auto; - background-position-x: -50%; - @media (prefers-reduced-motion: no-preference) { - animation: skeleton 1.8s ease-in-out infinite; - } - } - } - .aura-glow { - @layer daisyui.l1.l2 { - animation: none; - background-image: radial-gradient(closest-corner at center, currentColor 0%, transparent 90%); - &:before { - animation: aura-glow var(--tw-duration, 6s) ease-out infinite; - } - &:after { - animation: aura-glow-after var(--tw-duration, 6s) ease-out infinite; - } - } - } - .link { - @layer daisyui.l1.l2.l3 { - cursor: pointer; - text-decoration-line: underline; - &:focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:focus-visible { - outline: 2px solid currentColor; - outline-offset: 2px; - } - } - } - .cursor-pointer { - cursor: pointer; - } - .resize { - resize: both; - } - .flex-wrap { - flex-wrap: wrap; - } - .items-center { - align-items: center; - } - .justify-between { - justify-content: space-between; - } - .justify-center { - justify-content: center; - } - .justify-end { - justify-content: flex-end; - } - .justify-start { - justify-content: flex-start; - } - .gap-1 { - gap: var(--spacing); - } - .gap-2 { - gap: calc(var(--spacing) * 2); - } - .gap-3 { - gap: calc(var(--spacing) * 3); - } - .gap-4 { - gap: calc(var(--spacing) * 4); - } - .space-y-2 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse))); - } - } - .divide-y { - :where(& > :not(:last-child)) { - --tw-divide-y-reverse: 0; - border-bottom-style: var(--tw-border-style); - border-top-style: var(--tw-border-style); - border-top-width: calc(1px * var(--tw-divide-y-reverse)); - border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - } - } - .divide-base-200 { - :where(& > :not(:last-child)) { - border-color: var(--color-base-200); - } - } - .truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .overflow-x-auto { - overflow-x: auto; - } - .rounded-box { - border-radius: var(--radius-box); - } - .rounded-box { - border-radius: var(--radius-box); - } - .border { - border-style: var(--tw-border-style); - border-width: 1px; - } - .badge-ghost { - @layer daisyui.l1.l2 { - border-color: var(--color-base-200); - background-color: var(--color-base-200); - color: var(--color-base-content); - background-image: none; - } - } - .border-base-300 { - border-color: var(--color-base-300); - } - .bg-base-100 { - background-color: var(--color-base-100); - } - .bg-base-200 { - background-color: var(--color-base-200); - } - .bg-base-300 { - background-color: var(--color-base-300); - } - .btn-link { - @layer daisyui { - text-decoration-line: underline; - --btn-bg: #0000; - color: var(--btn-color, var(--color-primary)); - --btn-border: #0000; - background-image: none; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - } - } - .btn-ghost { - @layer daisyui.l1.l2.l3 { - --btn-bg: #0000; - color: var(--btn-rest-fg, var(--btn-color, var(--color-base-content, currentColor))); - --btn-border: #0000; - background-image: none; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - } - } - .p-2 { - padding: calc(var(--spacing) * 2); - } - .p-4 { - padding: calc(var(--spacing) * 4); - } - .px-4 { - padding-inline: calc(var(--spacing) * 4); - } - .py-2 { - padding-block: calc(var(--spacing) * 2); - } - .pt-2 { - padding-top: calc(var(--spacing) * 2); - } - .pt-3 { - padding-top: calc(var(--spacing) * 3); - } - .text-center { - text-align: center; - } - .text-right { - text-align: right; - } - .font-mono { - font-family: var(--font-mono); - } - .text-2xl { - font-size: var(--text-2xl); - line-height: var(--tw-leading, var(--text-2xl--line-height)); - } - .text-base { - font-size: var(--text-base); - line-height: var(--tw-leading, var(--text-base--line-height)); - } - .text-lg { - font-size: var(--text-lg); - line-height: var(--tw-leading, var(--text-lg--line-height)); - } - .text-sm { - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - } - .text-xl { - font-size: var(--text-xl); - line-height: var(--tw-leading, var(--text-xl--line-height)); - } - .text-xs { - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - } - .badge-sm { - @layer daisyui.l1.l2 { - --size: calc(var(--size-selector, 0.25rem) * 5); - font-size: 0.75rem; - } - } - .font-bold { - --tw-font-weight: var(--font-weight-bold); - font-weight: var(--font-weight-bold); - } - .font-medium { - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - } - .font-semibold { - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - } - .alert-error { - @layer daisyui.l1.l2 { - color: var(--color-error-content); - --alert-border-color: var(--color-error); - --alert-color: var(--color-error); - } - } - .alert-info { - @layer daisyui.l1.l2 { - color: var(--color-info-content); - --alert-border-color: var(--color-info); - --alert-color: var(--color-info); - } - } - .alert-success { - @layer daisyui.l1.l2 { - color: var(--color-success-content); - --alert-border-color: var(--color-success); - --alert-color: var(--color-success); - } - } - .alert-warning { - @layer daisyui.l1.l2 { - color: var(--color-warning-content); - --alert-border-color: var(--color-warning); - --alert-color: var(--color-warning); - } - } - .text-base-content { - color: var(--color-base-content); - } - .text-base-content\/70 { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 70%, transparent); - } - } - .text-base-content\/80 { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 80%, transparent); - } - } - .text-error { - color: var(--color-error); - } - .tabular-nums { - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,); - } - .link-hover { - @layer daisyui.l1.l2 { - text-decoration-line: none; - &:hover { - @media (hover: hover) { - text-decoration-line: underline; - } - } - } - } - .prose { - & :where(a.btn:not(.btn-link)):not(:where([class~="not-prose"], [class~="not-prose"] *)) { - text-decoration-line: none; - } - } - .underline { - text-decoration-line: underline; - } - .opacity-60 { - opacity: 60%; - } - .opacity-70 { - opacity: 70%; - } - .shadow { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .shadow-sm { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .outline { - outline-style: var(--tw-outline-style); - outline-width: 1px; - } - .filter { - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); - } - .transition { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .ease-in-out { - --tw-ease: var(--ease-in-out); - transition-timing-function: var(--ease-in-out); - } - .ease-out { - --tw-ease: var(--ease-out); - transition-timing-function: var(--ease-out); - } - .btn-error { - @layer daisyui.l1.l2 { - --btn-color: var(--color-error); - --btn-fg: var(--color-error-content); - --btn-soft-bg: initial; - } - } - .btn-primary { - @layer daisyui.l1.l2 { - --btn-color: var(--color-primary); - --btn-fg: var(--color-primary-content); - --btn-soft-bg: initial; - } - } - .btn-sm { - @layer daisyui.l1.l2 { - --fontsize: 0.75rem; - --btn-p: 0.75rem; - --size: calc(var(--size-field, 0.25rem) * 8); - } - } - .btn-success { - @layer daisyui.l1.l2 { - --btn-color: var(--color-success); - --btn-fg: var(--color-success-content); - --btn-soft-bg: initial; - } - } - .btn-warning { - @layer daisyui.l1.l2 { - --btn-color: var(--color-warning); - --btn-fg: var(--color-warning-content); - --btn-soft-bg: initial; - } - } - .btn-xs { - @layer daisyui.l1.l2 { - --fontsize: 0.6875rem; - --btn-p: 0.5rem; - --size: calc(var(--size-field, 0.25rem) * 6); - } - } - .badge-error { - @layer daisyui.l1.l2 { - --badge-color: var(--color-error); - --badge-fg: var(--color-error-content); - } - } - .badge-neutral { - @layer daisyui.l1.l2 { - --badge-color: var(--color-neutral); - --badge-fg: var(--color-neutral-content); - } - } - .badge-success { - @layer daisyui.l1.l2 { - --badge-color: var(--color-success); - --badge-fg: var(--color-success-content); - } - } - .badge-warning { - @layer daisyui.l1.l2 { - --badge-color: var(--color-warning); - --badge-fg: var(--color-warning-content); - } - } - .input-error { - @layer daisyui.l1.l2 { - &, &:focus, &:focus-within { - --input-color: var(--color-error); - } - } - } - .md\:grid-cols-2 { - @media (width >= 48rem) { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } -} -[data-theme="light"] .logo { - content: var(--logo-dark); -} -[data-theme="dark"] .logo { - content: var(--logo-light); -} -@layer base { - :where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] { - color-scheme: light; - --color-base-100: oklch(100% 0 0); - --color-base-200: oklch(98% 0 0); - --color-base-300: oklch(95% 0 0); - --color-base-content: oklch(21% 0.006 285.885); - --color-primary: oklch(45% 0.24 277.023); - --color-primary-content: oklch(93% 0.034 272.788); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } -} -@layer base { - @media (prefers-color-scheme: dark) { - :root:not([data-theme]) { - color-scheme: dark; - --color-base-100: oklch(25.33% 0.016 252.42); - --color-base-200: oklch(23.26% 0.014 253.1); - --color-base-300: oklch(21.15% 0.012 254.09); - --color-base-content: oklch(97.807% 0.029 256.847); - --color-primary: oklch(58% 0.233 277.117); - --color-primary-content: oklch(96% 0.018 272.314); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } - } -} -@layer base { - :root:has(input.theme-controller[value=light]:checked),[data-theme=light] { - color-scheme: light; - --color-base-100: oklch(100% 0 0); - --color-base-200: oklch(98% 0 0); - --color-base-300: oklch(95% 0 0); - --color-base-content: oklch(21% 0.006 285.885); - --color-primary: oklch(45% 0.24 277.023); - --color-primary-content: oklch(93% 0.034 272.788); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } -} -@layer base { - :root:has(input.theme-controller[value=dark]:checked),[data-theme=dark] { - color-scheme: dark; - --color-base-100: oklch(25.33% 0.016 252.42); - --color-base-200: oklch(23.26% 0.014 253.1); - --color-base-300: oklch(21.15% 0.012 254.09); - --color-base-content: oklch(97.807% 0.029 256.847); - --color-primary: oklch(58% 0.233 277.117); - --color-primary-content: oklch(96% 0.018 272.314); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } -} -@layer base { - @property --radialprogress { - syntax: ""; - inherits: true; - initial-value: 0%; - } - @property --aura-angle { - syntax: ""; - inherits: false; - initial-value: 0deg; - } -} -@layer base { - :root { - scrollbar-color: currentColor #0000; - @supports (color: color-mix(in lab, red, red)) { - scrollbar-color: color-mix(in oklch, currentColor 35%, #0000) #0000; - } - } -} -@layer base { - :root { - --fx-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E"); - } -} -@layer base { - :root { - --page-scroll-lock: initial; - --page-overflow: var(--page-scroll-lock) hidden; - } - :root:not(span) { - overflow: var(--page-overflow); - } -} -@layer base { - :root { - --page-has-backdrop: var(--page-scroll-lock) 1; - --page-scroll-bg: var(--page-scroll-lock) - var(--root-bg, #0000); - @supports (color: color-mix(in lab, red, red)) { - --page-scroll-bg: var(--page-scroll-lock) - color-mix(in srgb, var(--root-bg, #0000), oklch(0% 0 0) calc(var(--page-has-backdrop, 0) * 40%)); - } - background-image: var(--page-scroll-lock) linear-gradient(var(--root-bg, #0000), var(--root-bg, #0000)); - transition: var(--page-scroll-lock) background-color 0.3s ease-out; - animation: var(--page-scroll-lock) set-page-has-scroll forwards; - animation-timeline: var(--page-scroll-lock) scroll(); - --page-has-scroll: initial; - scrollbar-gutter: var(--page-has-scroll) var(--page-scroll-lock) stable; - } - @keyframes set-page-has-scroll { - 0%, to { - --page-has-scroll: ; - } - } -} -@layer base { - :root, [data-theme] { - background-color: var(--root-bg); - color: var(--color-base-content); - } - :root { - background-color: var(--page-scroll-bg, var(--root-bg)); - } - :where(:root, [data-theme]) { - --root-bg: var(--color-base-100); - } -} -@keyframes dropdown { - 0% { - opacity: 0; - } -} -@keyframes rotator { - 89.9999%, 100% { - --first-item-position: 0 0%; - } - 90%, 99.9999% { - --first-item-position: 0 calc(var(--items) * 100%); - } - 100% { - translate: 0 -100%; - } -} -@keyframes radio { - 0% { - padding: 5px; - } - 50% { - padding: 3px; - } -} -@keyframes toast { - 0% { - scale: 0.9; - opacity: 0; - } - 100% { - scale: 1; - opacity: 1; - } -} -@keyframes skeleton { - 0% { - background-position: 150%; - } - 100% { - background-position: -50%; - } -} -@keyframes progress { - 50% { - background-position-x: -115%; - } -} -@keyframes rating { - 0%, 40% { - scale: 1.1; - filter: brightness(1.05) contrast(1.05); - } -} -@keyframes aura { - to { - --aura-angle: 360deg; - transform: translateZ(1px); - } -} -@keyframes aura-glow { - 20%, 80% { - opacity: 0.7; - filter: blur(0.25rem); - } - 50% { - opacity: 1; - filter: blur(0.75rem); - } -} -@keyframes aura-glow-after { - 20%, 80% { - opacity: 0.3; - filter: blur(1rem); - } - 50% { - opacity: 0.6; - filter: blur(1.5rem); - } -} -@keyframes menu { - 0% { - opacity: 0; - } -} -@property --tw-rotate-x { - syntax: "*"; - inherits: false; -} -@property --tw-rotate-y { - syntax: "*"; - inherits: false; -} -@property --tw-rotate-z { - syntax: "*"; - inherits: false; -} -@property --tw-skew-x { - syntax: "*"; - inherits: false; -} -@property --tw-skew-y { - syntax: "*"; - inherits: false; -} -@property --tw-space-y-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-divide-y-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-border-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} -@property --tw-font-weight { - syntax: "*"; - inherits: false; -} -@property --tw-ordinal { - syntax: "*"; - inherits: false; -} -@property --tw-slashed-zero { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-figure { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-spacing { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-fraction { - syntax: "*"; - inherits: false; -} -@property --tw-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-inset-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-inset-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-inset-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-ring-color { - syntax: "*"; - inherits: false; -} -@property --tw-ring-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-inset-ring-color { - syntax: "*"; - inherits: false; -} -@property --tw-inset-ring-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-ring-inset { - syntax: "*"; - inherits: false; -} -@property --tw-ring-offset-width { - syntax: ""; - inherits: false; - initial-value: 0px; -} -@property --tw-ring-offset-color { - syntax: "*"; - inherits: false; - initial-value: #fff; -} -@property --tw-ring-offset-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-outline-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} -@property --tw-blur { - syntax: "*"; - inherits: false; -} -@property --tw-brightness { - syntax: "*"; - inherits: false; -} -@property --tw-contrast { - syntax: "*"; - inherits: false; -} -@property --tw-grayscale { - syntax: "*"; - inherits: false; -} -@property --tw-hue-rotate { - syntax: "*"; - inherits: false; -} -@property --tw-invert { - syntax: "*"; - inherits: false; -} -@property --tw-opacity { - syntax: "*"; - inherits: false; -} -@property --tw-saturate { - syntax: "*"; - inherits: false; -} -@property --tw-sepia { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-drop-shadow-size { - syntax: "*"; - inherits: false; -} -@property --tw-ease { - syntax: "*"; - inherits: false; -} -@layer properties { - @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { - *, ::before, ::after, ::backdrop { - --tw-rotate-x: initial; - --tw-rotate-y: initial; - --tw-rotate-z: initial; - --tw-skew-x: initial; - --tw-skew-y: initial; - --tw-space-y-reverse: 0; - --tw-divide-y-reverse: 0; - --tw-border-style: solid; - --tw-font-weight: initial; - --tw-ordinal: initial; - --tw-slashed-zero: initial; - --tw-numeric-figure: initial; - --tw-numeric-spacing: initial; - --tw-numeric-fraction: initial; - --tw-shadow: 0 0 #0000; - --tw-shadow-color: initial; - --tw-shadow-alpha: 100%; - --tw-inset-shadow: 0 0 #0000; - --tw-inset-shadow-color: initial; - --tw-inset-shadow-alpha: 100%; - --tw-ring-color: initial; - --tw-ring-shadow: 0 0 #0000; - --tw-inset-ring-color: initial; - --tw-inset-ring-shadow: 0 0 #0000; - --tw-ring-inset: initial; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-offset-shadow: 0 0 #0000; - --tw-outline-style: solid; - --tw-blur: initial; - --tw-brightness: initial; - --tw-contrast: initial; - --tw-grayscale: initial; - --tw-hue-rotate: initial; - --tw-invert: initial; - --tw-opacity: initial; - --tw-saturate: initial; - --tw-sepia: initial; - --tw-drop-shadow: initial; - --tw-drop-shadow-color: initial; - --tw-drop-shadow-alpha: 100%; - --tw-drop-shadow-size: initial; - --tw-ease: initial; - } - } -} +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:"Ubuntu", ui-sans-serif, system-ui, sans-serif;--font-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, monospace;--color-sky-500:oklch(68.5% .169 237.323);--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-roboto:"Roboto", ui-sans-serif, system-ui, sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax:"";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tab:is(.tabs>.tab){cursor:pointer;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:is(.tabs>.tab):hover{color:var(--color-base-content)}}.tab:is(.tabs>.tab){--tab-p:.75rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));--tab-radius-limit:min(var(--radius-field), var(--tab-radius-min));--tab-radius-grad:#0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + .25px), var(--tab-border-color) 69%, var(--tab-bg) calc(69% + .25px);order:var(--tab-order);height:var(--tab-height);padding-inline:var(--tab-p);border-color:#0000;font-size:.875rem}.tab:is(.tabs>.tab):is(input[type=radio]){min-width:fit-content}.tab:is(.tabs>.tab):is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:is(.tabs>.tab):is(label){position:relative}.tab:is(.tabs>.tab):is(label) input{cursor:pointer;appearance:none;opacity:0;position:absolute;inset:0}:is(.tab:is(.tabs>.tab):checked,.tab:is(.tabs>.tab):is(label:has(:checked)),.tab:is(.tabs>.tab):is(.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]))+.tab-content{display:block}.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.tab:is(.tabs>.tab):not(input):empty{cursor:default;flex-grow:1}.tab:is(.tabs>.tab):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:is(.tabs>.tab):focus{outline-offset:2px;outline:2px solid #0000}}.tab:is(.tabs>.tab):focus-visible,.tab:is(.tabs>.tab):is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab:is(.tabs>.tab)[disabled]{pointer-events:none;opacity:.4}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.avatar{vertical-align:middle;display:inline-flex;position:relative}.avatar>div{aspect-ratio:1;display:block;overflow:hidden}.avatar img{object-fit:cover;width:100%;height:100%}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.file-input{cursor:pointer;cursor:pointer;border:var(--border) solid #0000;appearance:none;background-color:var(--color-base-100);vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;width:clamp(3rem,20rem,100%);height:var(--size);border-color:var(--input-color);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));align-items:center;padding-inline-end:.75rem;font-size:.875rem;line-height:2;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.file-input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.file-input{--size:calc(var(--size-field,.25rem) * 10);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.file-input::file-selector-button{cursor:pointer;webkit-user-select:none;-webkit-user-select:none;user-select:none;height:calc(100% + var(--border) * 2);margin-inline-end:1rem;margin-block:calc(var(--border) * -1);color:var(--btn-fg);border-width:var(--border);border-style:solid;border-color:var(--btn-border);background-color:var(--btn-bg);background-size:calc(var(--noise) * 100%);background-image:var(--fx-noise);text-shadow:0 .5px oklch(1 0 0 / calc(var(--depth) * .15));box-shadow:0 .5px 0 .5px white inset, var(--btn-shadow);border-start-start-radius:calc(var(--join-ss,var(--radius-field) - var(--border)));border-end-start-radius:calc(var(--join-es,var(--radius-field) - var(--border)));margin-inline-start:calc(var(--border) * -1);padding-inline:1rem;font-size:.875rem;font-weight:600}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{box-shadow:0 .5px 0 .5px color-mix(in oklab, color-mix(in oklab, white 30%, var(--btn-bg)) calc(var(--depth) * 20%), #0000) inset, var(--btn-shadow)}}.file-input::file-selector-button{--size:calc(var(--size-field,.25rem) * 10);--btn-bg:var(--btn-color,var(--color-base-200));--btn-fg:var(--color-base-content);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-border:color-mix(in oklab, var(--btn-bg), #000 5%)}}.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000)}}.file-input:focus{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.file-input:focus{box-shadow:0 1px color-mix(in oklab, var(--input-color) 10%, #0000)}}.file-input:focus{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){box-shadow:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);--btn-border:#0000;--btn-fg:var(--color-base-content);background-image:none}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{--btn-fg:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem) * 10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}}@layer daisyui.l1.l2{.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.avatar-placeholder>div{justify-content:center;align-items:center;display:flex}.badge-ghost{border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content);background-image:none}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-error{--badge-color:var(--color-error);--badge-fg:var(--color-error-content)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-success{--badge-color:var(--color-success);--badge-fg:var(--color-success-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}.input-error,.input-error:focus,.input-error:focus-within{--input-color:var(--color-error)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.visible{visibility:visible}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.fixed{position:fixed}.static{position:static}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-16{height:calc(var(--spacing) * 16)}.min-h-screen{min-height:100vh}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-60{width:calc(var(--spacing) * 60)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-base-300{border-color:var(--color-base-300)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.bg-primary{background-color:var(--color-primary)}.object-contain{object-fit:contain}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-roboto{font-family:var(--font-roboto)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-base-content\/50{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/50{color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.text-primary-content{color:var(--color-primary-content)}.text-sky-500{color:var(--color-sky-500)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-italic.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-italic.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-ext-500-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-ext-700-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}[data-theme=light] .logo{content:var(--logo-dark)}[data-theme=dark] .logo{content:var(--logo-light)}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file diff --git a/static/fonts/jetbrains-mono-latin-ext-wght-normal.woff2 b/static/fonts/jetbrains-mono-latin-ext-wght-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..01769d9e63d89d35d21547fdcfe84382a68cfe7c GIT binary patch literal 15196 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 WDe3x{Jh1bgDcAe|Ep*;>2LJ$kxGXII literal 0 HcmV?d00001 diff --git a/static/fonts/jetbrains-mono-latin-wght-normal.woff2 b/static/fonts/jetbrains-mono-latin-wght-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..cd5102a44c763f1cfe9f2b199fd5d7257491c0dd GIT binary patch literal 40404 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 literal 0 HcmV?d00001 diff --git a/static/fonts/roboto-latin-standard-normal.woff2 b/static/fonts/roboto-latin-standard-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..20c3f4a3bf6f9461a3673e37d894efcf7c5bafe9 GIT binary patch literal 66544 zcmV({K+?Z=Pew8T0RR910R!*=761SM0t-k00Rwda0RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/static/fonts/tourney-latin-standard-normal.woff2 b/static/fonts/tourney-latin-standard-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..5f5badfefd023a64e7f6fde581bdcb7172d693e5 GIT binary patch literal 28572 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%0DZ_t*b%^(j<*bi5vl_n@}OP_KvJ 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 literal 0 HcmV?d00001 diff --git a/static/fonts/ubuntu-latin-400-italic.woff2 b/static/fonts/ubuntu-latin-400-italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2b557a758045f81a928d70f75c686b7e811b8c1f GIT binary patch literal 36468 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 literal 0 HcmV?d00001 diff --git a/static/fonts/ubuntu-latin-400-normal.woff2 b/static/fonts/ubuntu-latin-400-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..8cb3d3243506e76a69887b935376def60a8f1522 GIT binary patch literal 34924 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 z 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 literal 0 HcmV?d00001 diff --git a/static/fonts/ubuntu-latin-700-normal.woff2 b/static/fonts/ubuntu-latin-700-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..8eaed1f3d73de58956b56736f0b3a283c5a10489 GIT binary patch literal 29844 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 literal 0 HcmV?d00001 diff --git a/static/fonts/ubuntu-latin-ext-500-normal.woff2 b/static/fonts/ubuntu-latin-ext-500-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..d11b8f9ec621eee83c8b8118e29a63bfe0e72757 GIT binary patch literal 42480 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≻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 literal 0 HcmV?d00001 diff --git a/static/fonts/ubuntu-latin-ext-700-normal.woff2 b/static/fonts/ubuntu-latin-ext-700-normal.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a0d4e55a47cd092c67855fc04dde39a4a4f019a7 GIT binary patch literal 38020 zcmV(|K+(T~j3W^c3WED!g3)aYhGYN|-#7s_0we>CL<6ml-sU^+rdS+EEZH9p+R>+9ClZ=QQ80h|9^T? zkulp0l64!PL-AJ;`8~aI&LFbMml_dG(_EIgaK82$R#vOGV&&d*7$YOb#*o|$sw>@X zNzFA-odAu96+eDtYO=>~dG>z2SWRA-EG)dbM*m{k<_Pm!q%D@6?zj*uu5ZnO^eERJWw&y{H>KH)iz{o4kK z;op$&{_vBP5$l9sj#h-)ki?O!2W%HV6gAH51tHA&=|7*F-?=YDAy^3#RBAwtucj+C zTTMG^o&ngVV51Te5=tnDf~bT6ib#~Almd#8sB?-@wdua=l5OT(q&2s`i$|^c`@Zdq zf02*bNjo8`QB7d+X) ztpKRvFm@Qfitg5F#IYUbAdE3_o)^`Rd491agRfs_@;DH0O-f9|wvB(tQp~{nYmvXJF8uaV`ns-RExLlGRTxs4={@=7d7 zvLLWfvj|=u^^X1YE8oS~5C#|qx~NHiW%p|RzZ=Z6-y;zQoWCms7z|tZ$WHRqnJ54fiU33VR6itWA*JfWus#_tG?}q03*gg zlD4b&Vi7R^HPiaPz6wq;;MUT@d1)_#m@i#ntgg7LDef( zRf$S~4blf1lE%?=l%5{j%0F${90Z#MfnEjZPl(v?&#X)RdBl~SFx zK4wU_@UoJ2i#@dd?^DHxr9L5bYC5T`&-yA6gCG%u;Bv`@KmpPE{QIe8>H7da1%PCk z6nlY6UclZ&(Z@QHmz=8h@ljXgC5W~l_xty~|MCC({}aFi$pSto6$w&h5@feXP*nwj zsxkr5WsoIzL-HoIE0DaZysrc#l}nV$mPn}zIr2|Ea9 zvI!@iXo7Gi-J+Q|m@r+|aQb!(VAwCIEH6m7k(j6s0uV|Ieb7 zR6BNKhKzN1+LHp;5@7RZT9pP|va|E^n9}OR5=YhaL>Vhz{1Th}U z`;apW(r2r|_EX$`dS+&OoB{=_;{{lgf=GcbXyGXz5Izjd7lvW@IAWPSEq~hO%`9b? zT2$*ogFCrS{#I_NRY*u86z<|K?n`Wl|G8DccNpZyZVnsROe=0BnuM~U<~^AxrSW>w z7}6MGzscVJJd?ibep;|dkxP*hL_!E*7=~dsxFX{Fbno|yUDl+UU?5_RU@IsfyL+v? zN=LTg6y)#d5T{wSR@{{GMB9pnI%XNfcLIBV*;I;bFheR=9H%rw6QBl2XjjF)>)$j# z_NwL}9b4C-bSyX-TY#R3Pt!B?$6w!yK>$U29qdoO($yKp=@nFCNatkxCWB-GzzDE+ zPnrvGXP@et4Ey1yyb1{r{!`J$2#;XQ%fIULgbjbn_dq@Ptl4JK-4a%~BSu$%qHg2J zKVvJy(~oZ_eK_rs#W)k>=YRR^ZXf;0Yqc|C8KZimHPY?SYhJ_B;z!1)#%n^(^@xMc z5+tVwI-nKeef)>lq+_qD@4960e3w${Ul7JJGQYeF-d;Uj_~PW?&j+6^$balb4%~ zXr#Lf>zq=wTx8eqwObBdlGqHl$LljKQ#0J<^Bb^^tr+#wO`*Q}$gqSw|5)22+8c;X zI9`i5vct6t^uJYt#!d*yVv;q1bi0ffhWY{L!$+trte}a7b^l2%8KvS$?o8eGL|rMYhsaXT>JbO72Vgo= zpAOOLV8M*S9=xC!rs8@WfpN+t!Iqd5URIT%(2XyM1PN;yXW8XYpyGg)1YF5rDh2p5 zgHRR_%LY=}k*f_{wZ*l%;#SjeuUUX=Baqqzv~~b1+rxDi70a?gU29n-1Ze*@7ohFe zwK|abdtMsk?z~8JO(!{D8YzSi9%6k4gstskl?AZW17?uL{!#HW!wNK~y24mf1iyhW#+_qA_*@2YEl@z4{$ z`@BgZGZ&My1X_ohHPN(2B2H)++pV)JB=JX1L#$bUgP8g zU>}3{oU<=LeGBF%E`9;>Cl7xgz{?>)r6pGFlU>bAcD4K-(0jMsd{=U>Ey=YGu{{Ow zVApYufYU+wV|H};vK^Gq4iGlI^-ginm&34;+sso??OB<~XVzk3;H8OAW%DBZ3!w_& zXRW-a9twnFFlJB9tZA^pO*x|( zZK=G?>DPGBP{D3s$QEHjBexgK)1KyNPHpPDzvm{lyilU#7wg%PmP4fq{$RelF4R!D zZoe7Qbvt%8PsQQhLa2kp4uwq{CHjV~=M4Lika&UGxkhGixR`fdypJ#EBkQ#n;iHm` z-E92rxSO70~917C#>6Ffq;Bw9+Sxi(xmQJ#r8(Tw75zp##heKmYt=vZD_3(CH7mc_}tO* zJ$D&PXSI{6FzU_eH5F8bjsM$mIMH!SjQ7p?5}YQ!Pas9QZCLOVkaTphz;r90d9p>U zyNu;-LS9~QCwJ^yKM!2V{#x?HX^bJ9FlbEzuMaZW9$fi;lxUG?eocI|;U&^;+9+&0 zIGv2`U(~-cbAq(ZxFel*q6J%e@)%psGfySG*!rM zPWO|T^5{1~Y>HX8oWx=M9?Q|U_Dn7mK3SVOd?I(fEY?&MZdc#k8P2{xzqd#tSor_ zW!eDZXE7P3>S+upAT=Y?T3`11^E43JU>HN6v4*qS+jI2_vC%;ahha6R)WZJx?ITVq zciwmE{D2l$w7NOAx#O;Ozv}dxE|0%jw`X4H@zQJkKKNvf&sI#vnMCX!&*IoKVI^>D zL?v=!B&Be2Vk^ONY0BX1GL^|sl~RR%uGLlSm#V00zgBIvcwBAO=FjS?J}+x|w##Vov{dGwsg-J7Y3W-;+;#{&A5lc8~P}DG1|Vl{7dxzI?md ziC+pMUXs037hyp_>Q(h=j)>Lh<&=y3$>gz@ZI;3dp=*`~)XeeRRgJQjW(Z0tPdy|r ztyv*10bCEjbf!L4Hd-AlC@Px0a#sqBFcsJ12#ixaosANUfULe%+nC%+0Y^rH1Zr-_ z;n>orW40&M_F=Bh9dMOLqjnJAGeoE>OdJ42NTWx|WHE|zzr#2)gxNx2f(2fR)l0K^ z8TR_K9F66iMo}&(Qz9isuo4k%uXbK1ak)FvD6lWl}DduWO>%n zD3M-i4@e9W#dDVyWp%}%HYO|#QpyT(>8Kdj7f7VTh{{H*g9Ygc^SDBuE0;DNE%64T z@|X@_+a$l-!*6VWD!YbqNoOdw?Yn+*#%zeGFTTAMBM^7*fYeWVy6^>YF$?}vej89o zm0G=2X<{3)$1bhICCB?6AA9CMD;L$0*S~!Wds8q>LotI7))1eRE4nM%0wD=J5i-F$ zuEau&P6i1g{_Q+SfMoo6P4AePgKUGO!`@u4?h_oLwC{nX*wPP0->CD?XRzfY6DKf^ znN9$-*{6Tpjz8@@LxSiWdly52JEojG0&x6M0I(I=T>wOI2#>F@BQFTVbKKi^faFh2 z9ax6D!t(5Rz*kFu!-YBj0lm1gaHEDhFe?b#2xZS_Fpls5S2iNJ@-HL}iaR_i)Z8 ztCU#1wJN?RN|h;5&Keaem8~lB9;=?L8uA{gokX2_H5$}vRHsS(wpqjdrbVkJZJMK9 zhnCg(G4G+SiEizBbm-N&`pErN|0D(s>WejNEx8X2O$-}s>-sOgu*Q08eQ7vGj9Rxg zkbB?8N!!%#*=)oXqqf?>W|M6;+ir^;wzjc;*Erjn=;huqIWc92X=B@+#`m@vyG+_` zD)!iG=h{c^E&C@9*xe5Hn-1CIu)SvOGiSef2irowVbLK+9JXXOj*iF7?{&v5I^js1 zY^VA)r!6_-SbSyKiFI}$_o{P~IKM8Cx#H_dd~@1_{)3S6fA{lH^Qrq@TiG4^9Z0HN zUCd2&xodT$z(oPkyrAU()1f?4QSd?(A`FoF6{-O|4KK9#FvboGqB>OmuvUPlXwDT~Pl2u$!^#T6v4Wpe$bn)`M-9v4 zR8eeCb8_JZt)@PEX4Vd4Sj4Nn63!BIetD5nO z+MDfGcOcsoHyfLh79`e=WsA~IXS1@Fvq^auvQb6fYgP7BHmK@yMpbt`BWk*nFV%KG z>(%vJ)~WAzUPI3_tg%-a($w3m)!Zj+>7NX0?SHQ=J7Yk5PDa0uT#a6xLAAODCiAU3w#?Q(wO6&>uDJ2BPC&!D+Q7c6V(NX)#n(nhhsNlXZ#H=!@iOus&7leVHb; zMoOecGo{9cQd4bX8In!qq{`+BQfW)8Q(~1D`_OvIt_I5Bi_BEGm`#X{>2RfEa2Roe% zhdP^dhr5t8vwcs^^>b3qcO@wnx-MVrPLdtzK}?o<7^9;-iNUd6B+2n!;)&iy?_{5N zs?X6n-T!}zI5X@Gjjx8Yp|(8Sj09%~)QER(Afj@9U=ruT@Jg(&2O&z|404ozMsxsR z|HEgZEmv*I+D(?-_$1qU@s`Fy_?>~27r@Q)+igR_El+74k$~M70MPB zWkg-N^J2I4L2dqDHTz=gEXX`<&ayI4DgHG^3te~i!)lZ)NwnhvH6IoT5%$|zOJY4| zXEW(kG7*oht*$Ibc_bVPT1$%y^K-K^(^HcZLxTbTS)XwlP7QRmH8(ZZ*Va^5eO&}A zafm90Lxp8NSXl}DGYQUmI`qL}81)ht)wHzL%@QnEtULO;fVIJzLYvq;nV(GNqqnm^ zHNc8!BO^BQ!gBbu(u*C`V2=fzkN#!AVK`MqfJ zs`5^MxNC|{B(Kc6J=k-$1eQ84mUjtv-RIbB;shc#ed}++5Z_1tF3Qn_$X&54^HtgWX&pQnDVew}*ls0(lMtjQwv5yjAaB@BsAw;fI0MIf^6`ejGdfbDAPV6q>wzMF$=3isV{6jV($P4l(vUV9WbHkxRgj831VL^ zPy(9B`>FZDksTmcrG`cVsY``X#YBl#Di=ySC0Vo|htNEVD!{Rrw51_0pccv9YakZ3`Z;(pa2Bwd(Ft(I3KRcPm_s=4f4d7G4Kie5LC7-7DEW*002X(nJFBphwtGA-o7G_$3h8M7gl*$@k@ z8%TBTnNby=F4ynVh{Ke@s7doP|8gu*R>}xj<)o0g5@NR7s|%?~uBZPpBpHDgF!y20 zcs53(5LL!>#A=#Y)OCs{JiDG{xfi%Pn(ZUqTivrGwwTvg%V))1?N;)1BNC&rTqxl8 zFbR1;Ut~OszOXmM_a;Kv3q|id8j^;e>pFrM(0QEgDW>3#JaU!P>v8BnY?va76c!0$ zTERml6?Iqx`ys4_P?>Kju;i;Cl~jp+9ob%+sSlJeW|Wl>voI}L2X_(x?F|sv0)XVj z0D$s2fbc#*-#>}(3`s(U#__2z`;@H5i6Qps|Nkm+rumbr1MVpk;G=jD5j!i^V1E|W zM}9^pv?20tJ|HWzwdS#i40MWRe=3o>4X0QFn`a|ECgBqkPXs}+iklYk%9*RpN=;cu zwCIdCo12ug#Eem5`pLeFl*#N`PZKZ=L1%5~`$8w10FG$IBn?goN$P8B3-LBAW|M## z8%%3~Z3d|E8C#3Vnc2NhU`YH55x@o}Ejj;gL>Wf6P~U2t-wF#&T_conR=58xylD6O zPIT;Bv7?Mjx_Q9WDuuQ?{*@wL#F^4p%#3DLZuH z?b*sBo9sHSbGKEbD&f1UpOA}?0q zN%%hvisR5hhOP-Bf9b|3nZkHRr4#;9mFm{#W{1$S}29CL;2&maQM zl{kG|VkzXwtk1c^ND|$%Chi#bZt(m~Y*2n<3cU$j$@;!E?WE!M^mjG_3U-)HSONqa z8%r(aeA+kVrlYS;XN@X8LP;O6VoIakAq^_=4ew`y<2{ozJb>Lb%IZ!eG$j&n|FUU zoN9~LbQQtrYnyYY61P#X&5=~7IY6-ukY4Eb7%~@FNzC1m=Do?0w zn!TQGwQbmw6ju1TBLPlbvaRz}S`>)ncDDUzunz-J8VHix2cmaTcylP+y z@FGpf9f7v*l_|F0XR2t&5*~|ViO85^P4P051KJlUiEzJ$#ldoN6*wQyhO-hx;?KgC z(I9ltjpkwCYZO6k9b=k-<8Bbx`n^g%A^QMgEX=E7Ds7h+XFO%KTGH94^UnHy@u+*M z;La4%&Z)Gv((Rzed8js<_~Lrfo~8LJZb$PqS!ZG2+pn0sv6iS&XoAfr!K|kwaGv^ zyTy`9`;9@{Tef`{9AUFs0Cgqy=GEY|4r=>{`Ap-gL8Xb%h_Vvdyco2P`y>UBgX^NQ z&1;!So~3dO^gvbmVdcM=*eVB~s%7b>q_{}KLHS*@;@tnRUW-EnQgYJiPl1^?D zu{HeBf@;WX+ErBosz;))KUwnu)6+Ww{|1^-=#?$2;Xk4kI*f*3d`5*CX@S6$FcG6oNDWk za0}f9CsHaED9C6GpkD3FjKuO^(LeA6fv)ZIL z5Xs+p+|_8(2JVzpM^~SKUtrj3nu*NJsn2^h1dU@gZ=r;I+B?^s{6A7V$!I(@ z5+F#^#9oS2B=JeGABsfvpd`nUBQr2$%|1DhdOw%49AYcT4#Ylex_; z=i@0b=2L-2O4=tXaCxWG2dzg5l!#l+$53a0s}3?%l5`1TAcgyy1mPHqWp@}0I+h=H zmw9KS$J4H{cqwvo3MEZOckK7}=!?h$+6Ovej=fRsY7n`kuW7AlJu$kjBqH9p#Odc_ zn%qA7Cs#CYYaWRd*Ec@Vz}X_3;S9vziavPce(+@xWf3ezmyKL!Jlj{gyaeo-oVx7m z%AU?CSX_?R7J-v~NFs>w-5HPhdIU&rX-P^>#df_R_MSlkrwt~_0y}lwnSVMkbp)jc zf?x!xL6nhkky(TewV=Qdwwf|F>g7R!eM_$#`aSwy`RgGaHAmd0ZiGS^$K;+?Y1<;p z62|Pm*sT3AdAl>5V`dhwf1GM<1)S?_C$b}y3JQ*Sm4_rO9UD&(FUGOH1Tylja;2AQ zxJEbzGM^VrEQNEYK$TmB-l}Q+KfY)!@;%7#sptX|oXPZE!K9%Vhr;2&#O*6Nqan!K z%N!rW z?E1jA@2UTlrXJ5z7Iz3i2KDp@4p^9mWke5#HM?uO4`$4cX+hdPfw_`&VAh@-*5L~1Z@TWXp3$~6E(;8_QY2m7YI$alhtx=XGC8iTbX|IaqprwKb-9&J9%_ilGd zKg@T^hT-ez%LW6&5};$$yVAgbMlMwQ*%Q{Bz&)Cd7$rwu_l{xQ;zA$LMJS=SelxGk z9FljovqQ6lIWaWD7_`?hoZy~|5Z3#yL9^r5Fj;mST*#J9ApgV)c;;15eX!%9$C(<0 z4hgq$1my`?EoT>40=+Q95Dg?TM7?#|*C}uSx#d9XSSei?2>2!>1WL&K`48=*e2(rd z=OgU>P+T9_eO+Ic?_d61Q7A#wRXMd&X5eHYEepcfX$b0Ub^je+CpJH=RsUZJP`Jku z6f8b-BZM`cr_jU%da_a0oy>NtLLlSO1U2l}aG8T%!b@ z2cSf^MZyHY(!1!lQ%98l5xr`&(3l9Nfmq1bLh1iwn&yj!jSiKz#2lp#qeu!OgO?)NiePBK8jj3sgKE)O0JpwsxN$#YsZM}`bVu2|%T z>na4iz!LJzvbhAJ4l7Y6Z+8L?QT&6J$&n$=L(>$-iata+$?9E zr@1b@oq37QyXwBR&k^KA3?Jg{N+C(L8La7%o{>3hgKoSB54fV7Q~>cx>#Wun2C8J)lsWjTW#~I}zL4;F!spX0nub=pB`#e8($Z@%>+yCkAok`H_kZU2& z2Z05Unt0U+?YFXH&6IMlTpO;BUXFg2@Y*k9@KdyL#HG@9-7CkK7L|ig;8>2PM^7Q5 z+zQ6o3RiJ%3IShSsOxjpy#2#@@t798(p~U$!D6K0+KCh37Cjm+?`WuNnW?mOh!g6( ztO;0krfHw@@y57$jcUQ_AQ#x1#)ns60*6DyMd#8M;x8H!YHEYUAZxIaUsE7Ecy)no z#I0Cazus7l1@@obi#A$|i!6EiU{oqB!@T^f?B9)mD-KV>F)iR3Hm63DV|>c{PG1&d z_0q+a;V7gi(c$GR2%g-7j4<=H3Gw=Joa;(!+)znyZINN1QRwl= zUnd_G8?*$~<^S5aA{tD_M+mu@zhzb2vX(VeGJM@od6b$;nFi%X*C}#!dTIh#aGe00 zg|dUMc_|8Ky9d;KB_3iM8H^kbma!#w)l2qWr-~4XPxdJvLdw6o$N4ctr8+g_;|ZTi z5am|-IB{2hVD!!grPZH4p8Ti!$f~B*pO8bzmVI@fey;7O>ibOmgX0p+iR7~daR7>071gYe(Z10rx*!ig#5M+L2C??L z3B)t=K?thMvVQ~p$s~SzF`+UxOZdG*Pt$nB!sU$|56ytGm+M}73@`>r_klXML6D*Xq;Mb9 z&FyfvFor3VVa9%penTBJM5&=5QbZelj0fS24BUfk3zIzrF(k* zcXc*e&B00ty3gl7<8?R*=bJ(B?nB4-8^e0i)7nGg847nTtBTS5Itd+4k)oxxQ9zLpsNqGjw1vbwCguw->L$#r1G@UWy{F1D?RUEYbUj>PY6J35|GaYNhq^es&Qc{`SMpIaK zdRj|}lBITU6IC&qUh6S}H}kdy8`X(AN!BGRiOJ`+(EW4FhpU<%vUE)oQ(T%Sm~~8A5l@&kME?FCTHu*56I} z7q%?40yLTWVqI0(=B*3b%VrKn_gq3`1WJj|F**)xr++Rb1~Me1_2^-&l+d13b4yUH zqB$tt9M+SP648^9+7u+qNb0}sgK;Np)7I)(vH4kSkeH#QsZj@gsImZ2Lqx)Vw!O4s zXHJ*L_@NVwWL01oC!U_?UgNW8{brc&=LD>mgplDQW-`g~qOf%DD7gg>WHkIF_hfS+ zBm~xBY*V6!D#6Ow*}X;m!LhEx?Kc7&ADD4Q-F){Yrj2>2+dl&wyDC7cB;`dPv`x_I z4zIj4wm)^ACGhA0c@;#fkS_?)XOIx!jvABW!j9$lpE z4~y)b@)BA6WO7PgjF6Zgr7-$&iEaZgN6;FEot$1MlbOlPvi81iFu_fqpb8Vx)Q|y0 z-9n>FwYQ>>CRLJh(<|&ISNq_w%*J!RY3I<*7Ifo{m*ba_QM?4XM|($8KQj}y7TXjQ z4`m4z3`7H+P*T}e6-myJN^&VNUR|~HuzdOJ5%lLrPd$Z6z9C3cIBgWIRI!oNE9)e8 zMj@l%jWjidqNeR{br5n1VyQ*cJ?cb|j|O}i{kPDP>nzEv{4N*Y)|h^qExC9&KF@mW zc1&E9hNS`Q{d?#>t@Fv~>k;%U8YBd0u`gj_x?}_$jm&iX@0KY0*IWx1OOkzKP-9<6 zYAB3-X~QU&)`VXW1An3s-YE5o(z%Ld)TV zYq=f$p=Kf^Z~gkwx6Q>Ei8~zWIq!G3h(<)ikDXx*))=U9C59}f3APzqx#>r$+#i(g z{jv`D_SIi~)K!|*@-#FWQr$jUt;C^K3^`UjrKiFESd z_Dp1s5S0?{H;PbZra#)dgAgjlC`MIp@eTwu_$9AG{OHE6ytMYPqoZ3e^&;Ur82vULubdNSv zZuqY8yN#8!?5=Q8ZhUS$S7ht+f>oc(klas1!=K;14MT1&Q-(YZedlp1Dh$0fg?N)p zzDe9ypLu+zM8aJa=Zi6R-%Slj_-n_B&nEB6SSHlo`rDU`=^gJo{reo+9A*Ta*9ae5 z>Cyn1+cw)rNdFQ>S}I$X8L)n`pRQ8p2JL(FznKB?TVR`!`MkI!0Y9NQut^7tW4Y?U z2&Lonk*X#}^O#my@sG5d4Xp34lr78oTWn!JZnB*h5uNqVWt4%$3OD#h!zY#}w!<%N zlnMJNE-)7+KDY<<_L=tLLq1I>AY;*PXs%C z`}&NQ3F*UJ-`25d9T)me2P zZQf4awvW1ZPDxG%k3)i(5wxkTGU4d+aHB4h?W0Ko6AeTP6>gKsNMkLFt+9yYh0ZZg~zDx%`q@vyyr+y3?Mdf;z6g+N>v zS68Vuf@gbCAI;3$W4yrW`T76*k)Xb!Q1%EKASW|YD|z3;@c^;`p@(= zf4zxx@hVqE7OJolzhF-UH=hYo+=zd(uXVx)E0%ewJH~x{CL;b={;7V$s52!O z=4lXBP!i0w=jlh*TGzVS+=hFOTbU^YZO|qxEoWOMYJWDKTv3EX43y;FB)iWLCf^J-?!s z3g&~w3%5E9lIQ}4yG8pLzt6(O!&HIpE@%(NW&d)8KN!fl-+r!|vl3hseQr>$QI5jv&2WojU!wY&vmjs<;?DE8HH#RuyW7DJ_Ov%Fs z3~x}P3+a^cw1+b&{I^clKq`@I@aM_!Igxxyj!bT$ioB+JBf}(uA&n?h5r+Q$CU6{% zBwTy)AqnCtQY&hF`kkWUYT$`MjJSwIO^65+VhAtUUb2l>=JW%-GZXFT6jKe2Zp{65 z4p@$n8~nqWiU3J=O}z3djs8Z!s7QRotl%Wnq4i;b20`V{vaR$YP}=klU4P9IYp1@% z6RHMlWT3V{-vq5Av@JkEHk7E;9+jx!>H)=K@2$TXJ}fS*os zgk_!<^W7O(!tDBCI;{vq-@3D`QV?j+ht;7II2F;e$OmJmf&)G+R1@%DPSx@m0Dd!@ zCpS>J#MFUx#K!s8aucUop|xG~GWBR(~x&uFe;C}gF9 z8e?F)(M^fl(&{LWRST&FN|}Irg_=RRzTAlq(fWzKQt(69g6iXPUr9maH8xAB5AxaY z>dgUhR5mqE#>~WUm1TrFR)bWBiD2n`F)`%dy;iATz~l@e{$?M$*cti-yc5op!C&h! zEDQcv%GX_!;s(=?>pk{KTDn{Wt-FKrQ;vq@2j`|74=k98?rC4)|Mdiw-(}HKw(6p`P#$T*vVNZnL0mXp7atv@tC_tl-b<$1w^1 zc9F9+RB8`ISa56*iC%hOj!}NlOKqp+6;fMkiFk>7({_SRYndZxr8(2QLBikY^V;Zh zO=ST@W%P8|7;W9<9UCwixNI-;*(*ddDcd_^!)C_jQfF4qPvrpweF5dvT{QU1514Yl zNHSl~aH8d zMsYB@?_N=gW$rj5v@o+rLpN z;Gu6=I$akf~T~H6n)*Sadd3DyLXtqVc&rg~6X^HM}sYJuc(xdC0s;5cn}u z<9~c~M@1PZwNiAJur#s^caRoQ7-iv_=$GWOReeV2)Jf9@ZcX4%PuzpR#vAR!v97`W zy+zqQxE?e&T6QcoW!OJ5vyhUN;cqY?FoFO52C15}M!ZxqpBX=tD z*T6C`b?7v-e07yZ?enYhEwJvTcRI*lPW^EcwsNxx#>^@)#8C}3gN!rB;AmpP#$$72 z&6lQ|@%YMbPtpSk!YB#SFG5B76PXgZvXCz8$tZp^0XK)lEMuW}cMHg_LU%jetooP^ zxW0t~iqsc@G9+uX;Lrm2s;d=n+(*2u&GfgU3S9+jiYoXmPpU(~;%i#5ZTW++eV}5HJLFYySA-Da7Mx$~(Zy z26{>&yI!m0G*W8YL#j4h#v|f_v(qg)olRs1Z$AZsE^nv`X|F9P8q%`sQxY5K#oJEw zHyoZzVRR>^)G%Tds{-V4zJ;P`+ooW(-veCW&#^EaC)`Ef_a_xC1Gm4s8afs=vdze) z2FTDbp=D1K9GN}oCzN(mL?D~9bX7gz_tvf+)?7UVn)_3%3x8pNL)HJYUf-fY6mK!pa`Cl zt>e-4>M$!-LXUO7^yo%VBOuQwQXCt=#CKnKN@W@6SS;$(3*C5TY=Ag2j{pcd-uUfe ztQ%cgf>npn^*milb`iWF0=45*`r{6-UmH&D^Hui@g)3C7(BR($h`E`@kE9ysmqnxA zO>0W5be#C|L*BeDXE=YR>G>r_dOR=*WTCA4RqWZnczmTYg6+~NK+&d`A;i1Ko;A(n z-y`Ma5etuuN~s~>=c(TNcK3U@HpJGUzlbut{hu0rMq@q1K587JMKwRb`8fMwA8xNr zwVq!w*1m)?BsA~;tA5`dTd-6gs*EvvO?W9Y!55iCoiwV5q#`^mJ~=|@*lY9(r{Hh= zGpAv+0kYTqv3oZ08RXCD%z<$D{Em`a=%1 zAdV=FUMJWgUZ4~@p4tdH>}m-o;kgiSE*zc%hU7$_*cJ2dc;ZR`6#dza!y!JaF#h|0 z@+e_4liejgeGKYDg3#Sp)X7sONAQ@xa{M)j`|QT40!(CNHdYZHVB~W+DZwIwup~w= zvRWly;B`*uv9At;de@bgAw`G0I7Y+^vN*&)C^oHAVBkqb<}&UEY!bi)JTgHl>|F|0 zBSCxP>QypYAYmO{wurBvqiuXFhaoi6|8~N4`jSAg+Phd}u$dU~G9eL1hDX?fP~Fo* zc+%+ZZ8V0r5GO|Z@pU0Zv09ZvXC6a<91w?VKl>`)s5W9h$!iXUUBKJTnWo?{={?z|u6y83?x~A4e8z`U}1Ml&yLav`#D#-BR@iKg*0zWfX z$g|!Hq#6Z*F522PR{Iou)JbX$YkL%rCtq5$QP(MilfmHsiSJKSAQjGzGsMch0tkKprWKK%B`CZ~oA zsQEH9^XK>9Eg25oZ*wyAn8N@guRlg{$<@^Va&SC~Q-H}VH6I`76#xj2@>PHXKoc!z zJ!dyumLJ?z@3@FlLVXFfqC#n1C@!F^dVNbYB~PZb_=|j$&;Tb{IEwLspK`u^5WBJn z61it)60JJlr~89T$E_I82}mw*%ly^?ah`nQOv1wZtSl|68Q8@@Hg z9=G@IIbzNH-=XOQxF?42*yP7pZGtCOC-I4HPlO--j_7jH8LiZy{*{v^FKhocanxea zZk?OWt>4gEKhGcWGz?%+mrc?@0nvXPjDFH?Q^2&k(xWF77_D`jdtudgYZi zE)FC4egIeZ{d;ThSVTrA94f1t)FSsEY$r(&wIfmTVo#)L`&?MPaa92I#MUO7f zybfRktk}Iet4^fJ_QvVGIgxJlk2&irIO) zGjVr6r}yMR?AD($IIq{9Ob#LDViMFCwBkfeV+PpE?KUB^hfIsp0p9Va{$=q-SCVmw z@mNfPgedBG)JZOJ&^qAsJDyOUq5gP{OWd7`cz72j+4tR)&Z<*~3fx+Gy|o@@i-gEc zUypt*O_$y%wB%-Crzc!7{e9fkS|W-;WYS_J^>V#OPqZ<}oEo}VZUpQ#6$G`h?`M=x8Xa0EpSz>KFC$PMAu-VxO z@~^cf=qI_;hG}K-4`7^%x0WdNkp3NZI-X2x9bElv3=9G7~O@a zbeGy*#(K>Je+7<@-=rn*FFkLX$v=&4nKbhrUdD;()-@$f-lIh?ok=wx8)qhwg5QEe zRV+n#((*gs1u**b(>O#Tv38G`J-D$#_Wj~SklegIF@KCF1_Ch-OYuk~f5`zugiqq9 z{En=GLb~KdbwQw2;oJJ~I-HjXj~992bjQ7kjy68|YDsGyv<{4a`u`pJYR%6U<9em6 zX7|gUO#nGS#=k#KWZTJQC=`ug`plORpQM(D*N-TCV$I`E*H{b6RAwo$>sup@d*Xxp z0Z6xN%O;42p=CASdzx?vZQA{)KyG&jGw0QCvQ zB7eqHv@a@nfY~41926a9$aQQWY9~tc=_jP(h~{{JasS73YnUhl7jF5lGkASUiT+pR z_Ed-1PWR%!%A1mPu7&}q1+h1m3eRbO*{g=&7wCYQ=bKaMhUO$! zj};8s8D2&ugN4Jx%fr5bf-~*P)hOnw+XziBDZKD7Lw1>zB3NR`F8Etv>_a#L<@1Gd z1mbfqx+rrQT(Iygou32CdNIG5#GF#`llyK%9@(5}_%wYe_c9N?^2ft`oiKA)SE~ew zJV%RI%>)&9?15S2-^{HXqWg*wCSiLi$#d_=DV$qYwl(flE1KYil@B~$v>a5|`@>%z zC&5>Xl3M3Ws|4B0;5Q{^GY0@vwmxg(@x7?xMqg{2OF8SgW+D!7t)bD zN6Q`pnBeKJWyEsZTV4@xPZd;JA%6Pd&IDb5ugiea213(CWlCTp({`#{z!)>`ydM)Z zsD0O0D%%Hret4WMrKJLI*L%T+9~0DH#(3A~ys=x~u}!dA0$m?PWkhO7?EmmNWU95% zX&^mz@cG}Fw`U(S4!~SRf6u@~T}bTPW~thRxo8J79*YNis`igQ>Ju*BF$uE6M&0hY zB^vhz=hz<|(OD+#?KNy>cI0`2PM9`SQG5O8gf3kzmN~sm=JNHKGuxiN7*1^?Av^X^ zFYNzFPud$!9+WrpQmpN2>8zPA0i^K2Hw@WF)`0aEo1qJf{;8-2l_~ytZK5u#s@#^z zPWc1x^M50Jx5almv~AF?Y_L?QO>M5^1K02&Y*4WWz;ihXVMb0lXL7|T4L^Tj=e`mG*C>I z@D6;#q@5!tz-P+7_fE7jWgB@7xy;&1Uc(M%?L?UkU^69Q)7%jnBmR&+oB0Aj4pZYN zCJUEF)3Yv3oV~KX>jBIsF)xP~aFI#7gq~oV&$!;+LJf!SrfY_4F?4_ae^$Qxn-`7E z%GvLCRQLNA7$5?3ZsmXf#o{t2_k69i5khe?Z-+1z`CnhBP7D+t*xh42S75IVD{?;c zw6D$8(adM;C7o5{w4mMB2NCML3JHv~_WQu%`xz*G@6#OI`rTX`E>IdB>yxwqZ0lh~ zV!S@;q>iee)e-f5U~$esDdycY*U&BD+Nx^QLp-zQ(0kfPUYV1&h#a9F{9;AW!D-mnz)aoCTxAKj;fynU!?{S&P^p+>49i(p9G=mZ|st~ z=%TEpo_2|(&SxNSjVcH2D7yD;A1>(ZHIGerk8x(dsaEwhbyWQ~u+Yj?`M>fIgP8k_ z+#_QTSKua`)!NhNIYu#9DRFO?ae9J00`v28ZjCTRpvW*jv0Nbs=z?wZQC^d6d$AYuKEF`js&cjKo@(`qh%e))vgM z1RdP#lKlX*TQL6zX(zqaEdAb_d>$Clb1OHwK(BJzI6_Vp^NJEmoONBCB206K9l@QB%BQEk!_tT-DQmsnn0Pz*uzo^2@)2?1($cXlUcb( z1A}L(dQ2Oq@mrs}GWzIp?;ieM`D*SEIsGNG>U}C-+q`2WCHs4`$oHybs0-{2zR*P3 z0`2>LSV@vpew9EyZyWDHZ6g+ZoQQux;R!l;{K88v(%ZKohOn}BkyV#jK>@i;BiO0T zd{HG;*mN9mtn;U-y-Gq(?`g8pA>t~# zWxGMugybUIXPy@(pdxi-== zTw+Nmvva%IfN?rN9~7pX-k?%wQ)t=f?GAREdB&0RdLV0Pz8Oyywl~I`ITc?fIdRzUh*b#KacY)(->_`w1vJ^8(rKZqOxN3NE63$6fXayDXHxlo{EI*DW{vDzu5Jil$nit5VYccNeu6?sLd89Ih=uNG^uB9WkyIh8ltf5j@R5|KYG^KxHsiVwYhdU*OIeFJ9pupxqs{)Tu0?X@sZCXDx{_~>s`X6Xw0sR zbnFusLQ_MY{`<#JevAKgK0!WG8u^;(E8{1EKm3C5dd0B!Waz|#f1lmXFDKR=>l-a% zGMIU>cgxKT*R_Jvi~^?41Ip(CY4d>R<~^G1^5+4i^MFC~fY^CJH?1mG0UB4yr`V}h z`98f!_MBCKzN-Kcs{nzCLajoiDB&n19F+nE_Lu=QpnW<6_{%@NmH00JlW*&=hC}cwIlC$k|XwoAY>hDct^c6$gjY!wNj@pdOL< z)foRto3^W@D=!8fRZy%QTih~DxxS`A8&DEJJLyY(9t3a3CIAFrYrd5ii(6p=Ur*=Jq_Bw@^m6; zjdvonm#Io>A*fXSn1B>`Wa;#Fb4I|MH~sp>dQE?{^`v-G3yYg8GVm2s{uQtU&^~Z_ zEAUuR5m43vR(vgN*Mf<#;L=Mq_G9!0Q!lM!1cVYmF4Y%@s|Ox!e&V=4wk3cIO7<^aOAnG2`g^PxklK@wJ)EI*Ydk~{Ri8**2i zb-qE}srXCr7Got-YlPKhZ4yKG7>-e@ZLC62b}Jd3gE^@cw%b|p{Q@6GsLgDuj@O|& zr1SKbQ*qrmr6fGIj!By^LT_7j$jR1*LNEL`TuDn<4Mx6W!4|%2IeClsbspvd$w>c) zkmfpA<{b*`j0Q_^*M7|aL1JZL+O2UB8AyAM$N^vJQ#)pk073~$Su& zOLAj+4Hv`MWh_v!CE$au-f}>jjFy~%-M6QDBdUdJy^hRxt0S_=n#p`WW2OJ}AHq(wtJ)*a_qee} z{eHC3?BaK^`sIkdA0j zVG6V9B{(U4j%OhQ?{%Z%UjR%I&M+cSue=CMly2_rf_~-#3dYBy)@>M{ob^u*XSfa| zzX5!oj~&|gJQhr87+*|b#EEPwphGn3=WWvB&CiAr#7&958I$H2B%y;!Dp!!wUshT~ zsazBJ4V>l*JudA%Im?TcL`3KUoPqZM0vKlS>^x(r9wxwGiEc0uNn)7?Kr6U0&tngH z%7w9-t0)W@A(0*L9Dvuhv0+AF-NjG%5x|CD%;bVhRF|!eLAy(laXrN%IrJgAYzz+j9deY}wqu5!sLdJa z*B4-4D6Izz#Yg^rR4fb_kdjR+ooJ=1$|}UFNc&82VWI_81~j0k|LX=hC}uAb zwm{qPasGyzm7O~;uchPLjvt&<>-O=LmuRTcVgOGEptuH~J5SAa5x@X*$tE<5?}A^x z<K~wmaPYng8|= zua_G{Egv;Y$r^pm=lmIe)y$31>HTs@b2!S2vFrlXR)^{>t!8NUkZ4!NV zVq72UzK{E9<>mIvlw4IW6)(+RLNBSdd&lZD&ljignNwxnjE)~`m=?n7#q*aa=%@Ml zAddUvnJ{^IxzI`%t&Hm2hGNhs+A;bpiGbBg+ef(HdFf8ZX~(_RK<#%-Nvo;VoORSm z$4Qj>dQ-m*@awE&?Tbz6%{V`BcD1nmbQK```nf$!=e=L0E&3u{Xes$)LQjZ?Tw+M|qy4|1jqEIoWE9%&p(%Y zQ;#Y=esKaHYzzb^Nwi6oHoyQH>dSJ8oo$dtUb^%9d)vF)W{|kq25#8W4ly3MXJZzf zcQRAzFPkq#+k;Ldw)VOmXPvQ{byS06weun8t-%LNd&T%SV4-Lud$MER8^-#Wl#xIb zXaKx_ll4ex0X~ImbnL%h^XSrc!jOywO`MoqL?Mt(Zi z^qrHb2_i_bFf;NU3Br+iuFZxYys3|N;peHeNhU3i|3NRK~EsupV?1CrM8S{kpZ z?EeFYA^aVD6Z&A(a9jC&6m!ObZ18~^Y%zk?MxSIc4q5{}dXWUk`VETP|978ZliPo6 z^?fc_^0Pnt5k0X~A@o(MZb6s9(6a6bfgyV)V5K(PP=bIZ00JPx>a<}-%qh8fRzSwo zm>>wu`y_Z~s%Nj9gHLZQDevzAn-BTGK-1IQ-7U-)Nn)W28&gX`(hIlPDvunrDvgJn0SO5b6%k z0!UZO*kjRdn$;ob4_f>V!~~%ugN7mWg4m48MB~D+iShispm$Ldx^lQ-9VMUmljh&c zjh&{Z4m)^cxwU^r8}~>kJJ_!k!O7x+0u^?bLHfYqFR5X#01b&pQ%VJsFv`r3_zZMxyOk?!$yFaGjJ%c~6Vc4{riVxh6$)ZO@I9?lY!NO63_brFY zHF7(#Nc3};F@sp4I7gHiATsusy#N((ns|=!p}-6FIr**!Y_LKSsC>i@&KJXOo`FlWLdfw-d47N_S4tv(?XWa%pog>C`C4p&d*X7@ z(i3DEh2D$%vpH(=rUY~AZxh-DZ~Yu}@vwQw0tpDO@8WTLakGSMj|r>;YxQf2NJ_ts ze7@iSGq$aH9^i}!L6}jj205$Zdt8Ww_4LxZwFaM5J@xY4OwK63S0Qyn0?YR@2$B%w zf7eE>x}ZM%tdhVNzF?q)e{WfRQ8_p<=8KkfmHjUlmbJg6=S;vnf@Dp&BD20+!%P@F zPxB+I*R9$TRgELOB)}?$!jdI7To{7kPsj^Hbptl@++8_Vtwb|2CBNA7o}5V3$E~T+ zG$K~>X|L(T9kGv2`s4mX42Ow~Caw_Fw_5aI*)LQ4&Fcy2^hUk94XS%BYJ&0!pGvgp z7MnfoT0F@61{r1L7g&x@mb!FU@8cly*D&bCmvT$9Tq9uq5T&{mKTZmeZ7Q`WUyzlNq zd6LY{0vsh-rP-5Z0pFI=sa9`VGuY~8l0r@rZE&UcSA=r z;f4X1MrDgGGd&q=umJmEd(PQ-fa~MR!`&bX3@9H(-kQ-g#q+Qqx$BJG$^i96y)u`I z!(qk9V5wv4cc_4le5D72=6kLB#^&449tQI!90sZNV47L6wG;{J+KT6K%vt*Z?Lz@E zTJPYzTdo1ADZcEUllBxqIG)Z=p?fAirQ~ECA1CLifuiG0hFr9fjbG5l;=%-n!G9%T z;12F;uHlY$1YY^*#4cFo(j_p&!1DE+pcz7h2)s5@taZzXklSr~w4Tx^E#m#42SCuOJfqpA3D7xU0ka0t6hB?Z?zr6sfcx(LXc4qy=AyPg8uzQLak$I@ z0wnDW`RdXo_1xw2%l>##N$&NK#~tV8K{0b;RjHZkoaNQlS-R9B6rQ9n3Cyk&9+cwm zHTrtzg7WwRcB&a_H&bE3*Kly- zk7}dniP0h5%+{Z&9e4GG8d@6SE=N-TFzf(A;p~O z{AoL8GBX7k&sSpC>vATclNA zJ-N#DHVlN&dH&n(17!ePVWX*Wa@I;hFHO1LJe_}kq#aTUd)@`zFS)q!StJulP4>6} z&ONSP2C0&AKbo+DcviT*lUKItASF7%zxzXF(E?almZ_;?Du#Eo)X+2&3M<~mQNnO! zZNyA$$BAP?v`!ps8bjBDnbi<&NE%GUhrG~SBh_dIpn(wwKgJ9je*#^qT{)BCTg;Kg z>>*9={x%pp->+`GOI8HO?%c8~vl~@ZjWRgK6~$CHP^bB1;@a@|km~wrq2>8waOYn8 zucX|C5aN)8x5Lj~-Z46^t)g8}=Sr$D{7)NG5oLTWKA59Cn(Ku8&SRuieR@058$3A!N&K(sHkT4jiSSf=24^aJ(--?EPK}I zU0jWoy-krdw{IKHhfLKu^Ki`{Pg`nZV>>i)$h&<`-1B?z*n(RN(3tx@aKZK6HMoYa zk!xtQ;P=ngxqez{DZ1Ybkkdw>FIa+u%riM4@2vq$f>n&ultXd1fOSJ$BZ((#{x~b; zPRup(!;OMY&!fBy>!3A^9@eqT_7+zYm^|vf+(mN}#Z?!&%atv6?h!pE%V`bPY~SSs z)GJlBVhLoiJ?ljA&)}wC{HR(_Q<(P28z%TjY^~zMpe2ubo3Ipyjg1sQp|_lH=bBNo zL`H$qjO&wZ$QT>dN4o3S58V*TFtqf!v1{^h8MT1UnX)=SgE9!i;X=Hv_d(!RVTA}>)|A^1s03FY#!CEzFhEFyp0_ttJI0z2 zO4}qSR231?rSQDYn!kBr3%HgSLIPBn3zW0OmcEhFHnVBv4~O+(fB5Mkac5BCfqcrS zlTIf}b*2SmSnN6j9mqZn2OVU4mcBmC4k5tvaG&(46G{6Ud6+8gUmNJ$&QPC(rnW)a zs2E%>bYh$l@PZ(e*{An6YpOmsNStm-rgIUSE|rl^ITM;N5{5iF!`D~3ZnLg6>Y+_^ z$nbR!yl~@YjN)7EH8VzlMa<}-%O|GvMlKg3HZ^@NyJKCK=<+(ioCpoyA}yG&r4u>& zq9&7R?y7SXsc_eU4(U*kKz`JjIX9^h>4*gy4`IKd7z%nq!Uogp5FE03Hp{*uBd!l+ zedQe*B}mCeO@v^^-T*+>*v!z-D537RLFbeIbQ8nNtn4oi*srkfwZtmfRvJGOe0M+^ zgzHX2*A{U%PpR&M`kgOa(4&AXLu}>R;DpA4>_8myU=QIP1q++DalbzcI%%quiR=h3uz61 z=4IUmuY&)$#!kpp@`w;LclqD1HpIU6``YT4EB1ywkH^-O6I5j&yVIzQMuYszF8g4N zyo1aCM!eSlyI*!!uf}{B0=LsdN8L9A!{rLl2O~z&@}y(E%Y=2Bu$H4ls^x z6-O=V%_C1p4z_xbbT6VM=d~DtC0WRckxV+lI}i+8d`tp1xMh%p3sLSEhQf@fa}!n< zZKScKJ(b?%VGq=&#~=w_YFLmpi>izZMG8y6%m+bqUNG_bR3>G2fJy*Rlmg7WKc;%& zLsy3~(i}%BQOpFm4%xW_lDam!REkEzqgu*OS5b5qs;?9SsORcbrNRe2H-)mn3ZYYP z0_0JfZN!zIMeJJ(42vj-zBdu1Bgc2G6I8;u6qZ!sxWjZ3(JN;=cMxvCb5f!NB1Vi> zHEFjZf%ViPdu+O998!|2fQ;TjmpHX~x2G46mS$+j! zgi{M@L0EVUwnjnKy%Uv@gy+7~{Jm}7~}(S#4En@?lkM@)Lf-F6o?*SeE!ZpIOL99tMcIcn39vW;^%kE;$C3M_aZKJz^^ z{x>Cm^eZ*R=kY7)bMfukcyflkvop%;Txi~5_TWa~2|jl#=$5y+yUv$$wo502J~Yl1 za}`F#3)4V%o_jqA#yI++dV>zbvgv&r5YOtIx4y0Iy_vw5rn;#rCAjJ0fqkb^d$;Be&b#YdFU z`ed!-u|vuH(II4Gbo*emF(Y#}Mz_teDUUa46B>W3g|DG;9fZfxVNPfW?1VYrX8tej zOa>tL2?0III)Nc-cT=aIlYwD@MiVBIx@|H9xx z+AptOYGmB(f`iJFZI%%lAa5+VuDB3lf4YNlf{1^zc8*tWt>xo`^No&1SqB_$d}VS3 z3M{$E@}^V^M>p}$<_A~Y35kly(8vAaoZQZPZCf2jUkMUqTTOa|t6^x(rp?(2X&wx& zG;ZIouXG0HuibHwDVgxhn83}hokpEPyy6vl@-cQv`;?Jf@4=1N5lP+)n%NgddrhjO8ic?GF z*1^c0;dE)A^hYCz$*|iafV}FVz6YVi9+h~R_vfBM2@RkU5O6Cljo7>P$1!b&(Lp0k z2wVM(gtjy`RWGUjRjP*1#@sVkjM1UjYAyqg$w$e5ejN#+6_k~?!!fhH_nQ7X^7qI1 zcr4q^jti&dG|$ZS{4Mk?#NSG-RY`9??%?pqQ-aN1?w;!3ox>eZnt7-3kSFu+d8PH> zd)u6EAa3Ony!pwfI3Gw5j)sg|5IsC3yLbbGv_C3?8u#!7Sp(l9C+eWZE|rJeDMaW< zEP)Q9s22G}k|;9k0yap`Tj`XC&NY`vP~>*U6k1?1h-ejf5zh%>Qf=2ut+bXXi%iwclTxbLsDNN7wVPUjuD`tc zq#M(=h8J<0z8LI}XO?}Gifdy^Q1vpsvP(0|)=OE`Kpt0x(tA;BB+pS(vTQ9hTVzc$ zb37fWtv)voVe>p_0Cj`?L1WVbXa=GHm#F-I5bis`h@@-~hSwC5qvo3L&jvlcg5S=) zt5r5k=H|{yIUQ7hoL^i6O*njqUJF}8UBlUGJUF}pA9Bn361pYA!?jY6{oF^g{%G27 zGH1o6QNb6k0(4J`u&PRvjU}F?Lr}uriva=R;o=2gQTlU%T!^}}P1_q>4LPN(l;lDu ze0Co!@@#5R*@G98Nw1eLy7LwsUSBAUIG|+X!ObKD)#)=`>UQNSwYH*9ox13m1L0qw zHMfQ}=n<`!`8)h|7;~}_Pqd|HTD3k~MH1 zTnv`j@+9L2@Wj*kUd%8kO*-{9+NtHa+rAkAe;Hs;`=QCK6O#}TBMaJaH^NGM5y-G_ zE_(^H>7gy?2TJ254V$h7O%1*!h(c*Ivhd@-ZkbOT|3cNI{_P?tyf#I||ET(OOX#DZ}qZhiOo z>&AQSE}`4#7DLyvf^HXR`ac|TZ{JtYesT5xw`N&rF+j#7p}jzQZ4ZO);9azoPwW+cBbBtK)e% z3%??KHFPxdwUWd}ZXlUFy4s8wcK?`qw22!*>Oy|+sD67huuiG;-pJ23HsA~UX{=jv zqRya`_@))Fdv;bhcx*zkVG!o_KZq_h%HntMu*w$<(PuFTR408xxe5xXe0Fmm5tD2MfD7;C zp%707%1+{EAURLLcX{7lyU1FsBt{mpk*>{#`MFc^qB$|l4%-EWa+#J$w>O+Ai6_v8 z^J$ePtRRZT$Muok(v+>JbO(ZdCCwRRHt%--{@Fqcnu~3R>)jt!N6_7acYudI;vrc& z;hZ6IcQ^V{kEyxy7QAQOdb)O4ooRcpJagXTe7p0+v7CeB${kQF1w1FPS^@>zq`XUl z(Jho}Wq)T|07d7GEwysMu|eKsRz{26;HIP2kT)sWLaV$+?fRw_*&JoG(wa>bm8t}$ zoNB=53(9o5SfalF?G%Vh`xNl3l1NIyBh@ky7Fg)#R*cCon8pQE(9HKC{{4e~x^BP9Kh{5p?Ge9{+uUb9EmLx!RtNAj8{6TY$V=nHX2l zTR7Ck^riDoLLm^Yve2+pA;a0i=DzZ=GKMrWEJ|j?nfs`?->%>~D?Pg4LN4GMv2xDd z^V9oA;2k`sR3<*Bq_Le6SLt@)v)A`2x5OKSr7)Yo*+VIUbAB!%&$B?bMFOqvGG|*y ztxBQuREpOwi(6ugwpU(e7}6S>yDgf^v`b%+X^6A9wSkr-fm&B6a|0}`<(!8tW_aD5 zzac2Tn@2}@8vX)COLSWP<@h(_N;5-yo)>-1@izzQ^%{3Fl4NL{^&HRFc33a^=HsQw zDCh-u{K)XNS9@sc6VaH=$0BYyrt0@l1V$sX9mkHTb@y-$;BSz|wcaELCrC5pbgCb$ zoYAG{;vMKwWp_zum z77*ASNiF>#zapLs-}4f{3lC;*j3I1yJsz!Cw!{@&0^t?Sq(Y1n0TlC1&ETj!a#Rnl zRf3RT_4}1(Hhpj20th4Izqi#tEffE|-}(7E%s!gCdNDp}S&5$}qu-L4DtREpn(Zg9 z<<49!#0dOlLC!QBubv`TotUFi(~e>y?Rk7_SIYy@S43PqJ$%{44$f);A_!re@ifB$ zf^{Tj3&|&K#N%tRRG8PL>1dXkb_jt62+_c*=>|Y`zQ-%rID-~(u-l7q3x_Gg5NLiN zMu2GrzOx@O>yF&h+}!o32(|5lIB{N6lKpXXh@|E7TI|SP8--tK=drXUzY1f(*L69U zSc75Suttx;)0TRz%zq7FNl?1 zcw*!+P3aY>PiDPT;M>NheV7*2AYxYdU*(&v$|V1v-hd>*$qD%%P>&HQb}6+KMttrcm9oD zMy*`z9Vz$nBTA~~ymx1C*dXolRUO5~Uygat?Qb9B+uqL%(CN^1#eQ3m3mP%yEV2X^ zuG`*4d~)jZRHz*=!?b5}a-d0>G?@B*Mk>-+ZQ59b+obb;DETTC1vb(VY-p@2PGjRM zjtz^nzm(^`_?9U}rNO2E!phcWoLSd$Q956$SgSD>)qRSM%*-t;Cgh&No~#kE{~anP z41U*?f_FLT+$I6P*yB{^g|Vt$SUu45RIL{2LyR zLAZG=+6~vJtfC2!j76V8TmuW6YZISAc4!|&Bj+4|9N(m6l6y5jKKF+ zH{^Kj^|cSzcD~7FrApShp9jm=2~gn(>Rmmp2wQRmrLreB%_7&_uGgy5lceH+O z97~zp_P69~7rl7wN$-37D9jqj9aT*Es}BG)06>31uypG63(uOK>VDTiR}GVdPG+(B zo=7AzMJFpGX%obI{H*~KG@|6>M{_Vxb|2NwPQRUON<@%D)q8UhtOcl{Nf^ByyGUu! zJlah{6!8+fOXT~{X0-`b|Fkz6c@dgrv$|%GIVkiaQ-VOKmy;=RnToHd0JmJf&)VoC z&f?wC7_&k^-luizK?7$r%)iBJk4?Gv*L#Q%x+{Q$kJP z4{Qa!@Cg|;h)?da@2b;fsoCedO+fL6qtiQ1PNP&i-aKLEvwgtnTiGFHhTTGyMp1SpFo9qwJ8B1z(-a>Fjg~EA+ z7P6Qj44PEXi_h#{qMqxmiK1ON1FJM5eAJj;Uagqo>lck?h7b=JgQgY2Qb9gIqm zrkz9W0Btc*VBgPEIGP7#sl`yu3QA5lq&0W|C%(8S8g&B(4^}II!a|De41e>=R!K~3 zE+#{7#7q;em%iUb=bV3`@oUcIMB%!33P&dX$sLXva3aD_0aQ2C6EnGSGp6Fo3k|D8 z3-hXKnRrlQVR(cv4A_Fjy_&YoVOH5VC~IT%icKPYY~NV;_x95x?0*cK{>ml!%Y*%%ud1g1xa0qql@8aI3xIc~zv zh>CH{T|S7sFl3#yPwWNz z0RY7@+TgpFd7nQ-x=l@_i*Vj;M<38lus&xQ?P6kh+ayswkq139lDumj4eV3$h?I=I z%ApJG+nq~1p;zd4D)dIZph{a;OiTe48&Uc9m=pXvN>H1dn!)xx`4fM<(U8{QZ-dib z5tWh#3=0E<*f@}4sHXr9?zO;umeRJDJ9JM=cB(>#*VxYmeCgEJ`Cwwr_IsMEb2oRM z@LEX}$y6!B)}-@k)YxR_7x(dlh|!z-oISQ!UFF=?x#P!}E2)w#u#Y$IqiYl1DVw`6 zh8cJ=k<|ivu|tx4lD!p8M%OCu!}EMCI@UQ4cc)A93*Y>oPHpJW7}}AG_OLf@l?b(* z|DTVe?7!8a&g;|+CirbUAXjNNoCy7m;|!;P4p0Mzg<0^58wYPWC^j6y#WU-L$4q9= zHkrJ7BgwTJ=SNQ0h&`&8Exi(VnRRaK=zd-#?pLHDw%z9P^9i@P*+o--@`L3le&)w< z_gq)*d8XWXdTsl-^4NXuZfjb6tZum``dV0JTlKZ@Q9?5wzG*2w)$=Fp-J|<^PWVFZ zO47%zpY*5vr)#ihJb=*M4aV56yQz%7Wdyh7f>2$l8EkejLV4B5MsFF=B)Zck5$Lc6 z48oG838Laaa*Lr?hfejW$ij&2Uqd*Vp%!LVDF}a_>uP~Oo4pub)_1q?MQ7Ja9N>uq zS$Z#GV-PJ2g!6{1F1-RH={w-y=Tf(G)wr85@WS*W%!w1(+hilb1e&&O|D!>rkX6~hvX$K$PGCC&IXM9x zpau+|joKh84kS4Fwt1;tb?(uGc?i++SI~<#0eM9)=%H|#=?$PgR`5$FIlU|5)C2}0sP z;-IVxxzXI$Q#wOa(d8SX3ACSZN{N}pu2-}X;ko{nRxiQ{5!n-gMbr^5oW|P-2_n?qCn&JpcUjQ)0*j%$z##)QKVRG=Rl_wgj$bje z2ZoLgY?rg0bBymf2>*T)8G(yJ~4gOuZNj@lpBIv#T14AC%RlNI=7nn`G4e!f;VE z!y^nCTdGwf#S$Qa#gxS3F=~6eABW>}1q0P9HG`EizS9}GW-#`^0>Oh7owkXrXc`+Z z*fa?~Bu#2a?qa6bj7;GU(=5>nO@c$HTDxu#f*nVMi=ASmw!uJiA<;ulXM)>n>$RM-`G%7`bRwqk^P0GWEBN(hz<5RD zhez_=eO#THxW)K5CuhoE2wx_SCMI`6F5T85WUvWq<(hE4YK#ns&1(%5_|Vy`UJqdw zG4weLrY0+Pj^KL+Hy&`Kc9-z+c31>ZJH7AE_s1jo^O`j$PR&T3iDYZGn$Q9h#wY3I zl)<)xi47PQmhVm$aUgL}5oZpCCs@lABCmj$uFSZdS({u=;M|^`ap|aya|@K|Ru|Ok z_CsA(SQJB9{1Vv+aVgQ>SQ=>s+r2BH$!Cly#Woh^`?!S&bIiU12jPyz?5ldWbHPJQ zSR2=b+ZtzMUBIcoG!ggW=|u6-vLjVQ)hVW?%NG}RUu|Dp=nNS{9&?B?ksywvf`zaV zp?@XcAJ6CHulsb`wzH%bjGDcsLm}6U;7cI%lEF?Yh(HIZ0fVpvK?H3`K?zP6T`wF1 zv0PH^7%og(ifzD+Y8E07H*-*UJjA11$3_|}5tYIko8?N%l#UpK6^ahwd}Vn>uAUea z3XfH;Kid~Fq)?YDQ*pKmn6>r+9Tej0%f6qoL(+t;hZ(~PySRvJG;zJ)v&)r&sS<+| z%7k9YXC%5T6Op(M5l62F?{p2xl;;{^jM8DDhK_K#yXC};IChruc_p9JSB1sfK)JZ*^6PLUUlx&ec*BtwE2aUi+LGn6s$IJ)9IKw@PgxrBHsc#cHp zLBQ&3)9k{xZlzl|!e>v7Q8^^1tB;3U5mLU&Qs%DO33W16?5>S`<5oU$o?2{UVLmP+ z)wuC&o1v+-?06S0p(=a#Cag7ev!%|k_pnmfahIVujpHknXg;rqUpIPqpPY5(D~USG zRo#c=Ij>8H$rvZrlQwxvn^WJYb~uJ8X{SfN?979A@S$X|Lzda9 zYv+5WZR9czM_;&JjC9%(4mO`e*WqTvDf61ATAF5F_|8+Otz{oqm_ftm26uefX~8=3dIG?!f4WN`(Q|d2HJE z?{z-B*v7(qpOwqqY|C_I&9k$grX|g+LicXsb@}F4euf8dO}N4KhVAx#40RFBC(6^C zd9nODj5kzoWz;*);XcOH^_W-fD{Rd>_KP+BSbm=VeLsAJX7G$p(~dda#oTf+74~Xc z{{8Fw`|kgIJ*L+3NG%LiemuU*v|Oc2!_4%r?>#uFAOanr1`G?c*ei}Wc-woT7APJ3 z_O?)AU&8DB%j9ec%s%h|0+mFsA zlSmw@l?($Ui$VQ8uV)ODzEs|F%rin=`;IuH*_rQd`}A1;3c)q{8D{f1;|fm-hxl`s z6}vx<9POg}a{CTkE6R_f7%tE9m%bitlEk={DU+5_}L_qdA?J z2pF}ur%om!57o)D!x7LZIMC%kgF^Mi7a`hX$KOH>ekZK`laz`N!oR*L88? zQN5m~8WO1mN1a(b3N2K&Qm}>vS}e+NCj}xan#KkU3rh^R5fld!oZwRd8)-}lCF6Pt zmC4w2v_kO$&RT!dfHN)lu7^?p57Hh1Os@cX$C!6Dr5%}H!K-0Yg zE>BLrNIB_M7<;aX)FbUevRn0XN-gU~(AEsI78DN=-^}4uVhe1aSO(U9U-goU;+l(W}%#qIzq#Py%wz zD83J*P40qDI;kK69iRpb!YHOKrgy0(2~KcB&6%G{A>^Rv_)2Jvl;Rj94hDF4nJvbr z{F7}vzUe_43A-y_><(%Ujsm*Sf>kkDZTx1ikpi{W%|W*sN6ywtJgsng-E(u0eRB<| zVzG@$J``NQaf4O2P0$hBX2)5>!D_AAy}bf`95Qhu6PmCc`6?t0qg}l2wj~27MCX%- z=1L*gvMrgY0lDA*4TPBxiM$MzP-8$9{K9&tJ9&$k5 zUO$Vil|HtaJPl96^QX6hIYn!wwMro>Fg=>)=jfBqm3}%LWbE2Xsf9tE`8+3z6tb08 zHKa}MGZf>bf(Ueg8ZZb;Bjl2w6Im(23BHC7VV0Lm@yZZLDMHRf*e(>NfQpH39EA`= zKMZ37q)fQ`ko(}^_FYzE^`&+u)3PP&$}9Mmw0F*RC)+zOlyn>ELEvPW3XNgyN8N5d zYG2fLo0_ZlPA&VYTU>WP`(n*0TT4B3XM}E8+>89;HXlwRY!x2`*xoRsuPxAM7lTin zo+?W>gLnt_0-0Nu-$DKyy^nFGdW+uq+1q)(i=o(o-WI=Y{+rwClT}X02(E^8&fe>Z zcFQcGas#>3AL6L1SO5E!yi(U~KDBVDnUC9imf7YiJvO9G?rU0{lL{ix0cyY?jAGVe zt}eZn-~>Ngfz+z+_a&iqN^3~9dPm*aXORde7#NdMj`a;=sXDv+Miqk7>D#SraoI*& z{rNhFxP0c=I}<|)eH+FQC8}_7W{_3{j`?JH|IK!N#m!MmG23i)i!rFnHd{EyvET9G zZCgX|WB18ZVCP^a3WSnJTGw$7(o>sw{%%+iY6r@?+C--WB>0$7;_N+xn6E= z?5{Dk2&kEFUhCPe*=7>8fwal})E)#U6-1x|)PO-)+T&L8mnB9c!3kb1buD2i0%a}u* z>&&{D&x`Rif0V#Y(6jXCF3IuN@zJ--Bb$~jZ1cE2mt}6b)qRioRT<6xGRN*Z&xJ#G zSz;Bh*<6x*UnqPr;o0*UTyvXg^v92LdE^B56?KD>y5r^@tamQyR*G3~JJh;u_x${} zczG=4asLkC$NG?eDZB6Q%U`F5^+InwNE_AX`u4c3OIVf|OAdbKr40YCVqUb4_J|1%IZ@Jc(8%4;u!436PoWK_~E{q_BwV%#Exb|qs@(EP} z(jB56kaep`Af5O1hJY78zvq)%C@bVmh@`wGkr12GJJWe%mv}-EdO$*^i6udiXE?f# zSjDT-*Pq{5K^_{XLOlSTlaKmA^2)Slb^d%qHb_=6Ky&D%lL|p;;ipAKf;FILWfEs~ ze~18U!4Aw`PTXR!Ma3ymu~xAxB$^XU+qwkVWA+w-e#aR7`FHvt`yzHz>>|g^W!(d_b?BXe+_!s5L7^ZQ=N7gN z&okzcH%D6$yS}eys$~0n=JC2d+L)8CiJ?Pcify4`@u0Ak*Zp|vfO;&T6A^1XH)YNa zfllx#d6Afky45-&-d9ilo@0-Z63gq&7t!{}v=fxB<7x(_0j0j;-|^&LOb#o&NTJp- zZ5pZXbDA-QpeZq-)kagv-+;jfanD-Gz4oBuGgUMn>wjo0@GwgM!sE=_=1pqa>?^|$ zm2C@No;`Rgl+|mbgr@w=9T@hnSi(TX8Y=pJR`GKzpX|RX{~yY0|ENG$&MzLyvy;nK z@44^SFn)%1AxrN64R#v^=u}?X$i2YHu=1SxFaNFsI>Xy2Xq5fC)wL1D&Jt8_EDsAY zKh^)2T|LaXPjKz;Ec-Ioa+YC?P^VRSjx7V&+uGThS0dCGl^RHWwsm}J8M36js}6xJ zy!sCLRGEmS5>RDW-3z`$*wkDeVJQbYYc*SmHnHc@4pv*ZV(h971K?Mh{LK*oM8nym zDH*^n6?iGJnEX;kGWw;QoZ6R~xP>gfuevQ^Uk?~0M>VUe@~aQhUa4Yvn5vZ2EZi|^ zb_*SeBoOJ~IgF_smSk10OuY)L6exV1PEwRt$X29dWm~3{mCjt*io5nyB~LKkEM{4H znM#(uP0DHIRiz@cP@$G0)l52r$`kQA1xZ1bJZAXFj2qOcr6p&6VroDA9THN~GIGjQ zs8pp|K~bH04N978sg(|-QhXj?|4G>=VERogDvi!yve+Chk6$FNAQXuaiBvtEXQA zav2)Q6%LG*4jnmm;);pWm>%C;bKMP7GXR8O1jTTIq-ciactMnu%4*Bx3Z+V|(dzUD zqseTs+WyNF&9EFVhzZ1!tf-o9L^s3W2%u#VPSPwd$_kKJ zNTuth?fPMy=4IXX4s_9j_Z+LpT8eQHgN$`X*6V= z4>9F3*Ja(#m+S5Rc)s4=KR&;F{pQ>6zW?FJp8!OdP{xH++F0j9Owueb%BpVKt{+CR z01iy$?JeIhFhFoE6T@LQ%J08e@)UxEdW7{+*b$9y4v!bVKpEqUDz1qOkA<-!v3wA| zGn2TGIP{b zQT=vd^d~)~+IX#x9`f{1Z4Yip?&qSqujqs#CF{p^DO4p;cNDd$^?>r#a`gtMsPRVs zJ=DVzO^z08PuK@tr~iy6h6L1tV#KFNzdbC`YErFlDRAZZ_35D&vHXzU_n}VLfVop= zCo|CTn`S%|AF>@?ifozkWmDo|w}iWbt5%@vw}z5kIq=i8uOTpW2>hh=tPc&w54bxG zc5(v)zuSj~9sFKxe5k@69y93O?}~>(`lT%$e+tBC7JYtT90=608U)7vK!(Skz@7{T z`@@rCnRW{L@p!h~z)pHjtB;R8=RMASXd#{dQWINf?uLs9f2euKc!}Y3_4Js`yFVZg z$%-W5BiE}@V~fYjPwanGd{upe6diA!>B#=$qoY%z(62FVAbPy%L-Shpd3&lTh>G&_ zjvoJMJ^2N6A9d3yr=#=D*lp8R-N z9el=n8vqd|lyM=IHa1uA07RHjrj5-NJ^&FWlyM=IHa1uE07RHjE~L`N=87MH2ouV< zkV+eyn}H>Pxa@nY=!|a{dytaE2ZU&EU6zO$s=ZBn(@xFVdVDh$?vzC zpRhsfMEsz4vZIwJy<}GkDX)&F+(Y7ii5z9~#aU&)I7;=4PDO$}}i1ym5XghqiZg`}K2)HP(A#Rau(2Lse_XzE)g%?2ji5M5`uE+Casi}UK_C} zON5FRi$X;9E^2OR!!*4OQ+Bzh$O|Xy8=JzT^sRjQ<_!t;#jwv7_@Pgd^5C&ZpO=x4G)-a_ c>7C0I>?<#prU#ZpE?>E9TJ%cqa#zi+9nc&4-2eap literal 0 HcmV?d00001 diff --git a/static/src/app.css b/static/src/app.css index 3389bfe..93185a9 100644 --- a/static/src/app.css +++ b/static/src/app.css @@ -11,6 +11,161 @@ themes: light --default, dark --prefersdark; } +/* Fonts are self-hosted (files copied from the @fontsource packages into + static/fonts/, paths are relative to the built css at /static/css/app.css). + Google's CDN would leak every visitor's IP to a third party on page load, + which we don't want to inherit for an EU club platform. + + Two subsets each: `latin` covers western europe, `latin-ext` carries the + polish/czech/turkish letters that turn up in member names. The unicode-range + means a browser only fetches latin-ext when a page actually uses those glyphs. + + Ubuntu is static (it has no variable version); its real weights are 400/500/700, + so `font-semibold` (600) is synthesised up to 700 by the browser. JetBrains Mono + and Roboto are variable: one file covers the whole weight axis. */ +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("../fonts/ubuntu-latin-400-normal.woff2") format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("../fonts/ubuntu-latin-ext-400-normal.woff2") format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: "Ubuntu"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url("../fonts/ubuntu-latin-400-italic.woff2") format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "Ubuntu"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url("../fonts/ubuntu-latin-ext-400-italic.woff2") format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url("../fonts/ubuntu-latin-500-normal.woff2") format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url("../fonts/ubuntu-latin-ext-500-normal.woff2") format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url("../fonts/ubuntu-latin-700-normal.woff2") format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url("../fonts/ubuntu-latin-ext-700-normal.woff2") format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 100 800; + font-display: swap; + src: url("../fonts/jetbrains-mono-latin-wght-normal.woff2") format("woff2-variations"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 100 800; + font-display: swap; + src: url("../fonts/jetbrains-mono-latin-ext-wght-normal.woff2") format("woff2-variations"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: "Roboto"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url("../fonts/roboto-latin-standard-normal.woff2") format("woff2-variations"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "Roboto"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url("../fonts/roboto-latin-ext-standard-normal.woff2") format("woff2-variations"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* Tourney is a display face for jersey numbers and the like. Its variable file + carries two axes -- weight 100-900 and width 75-125 -- so font-stretch has to be + declared as a range too, otherwise the browser clamps to the default width and + `font-stretch: 125%` (a wide shirt number) silently does nothing. */ +@font-face { + font-family: "Tourney"; + font-style: normal; + font-weight: 100 900; + font-stretch: 75% 125%; + font-display: swap; + src: url("../fonts/tourney-latin-standard-normal.woff2") format("woff2-variations"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +@font-face { + font-family: "Tourney"; + font-style: normal; + font-weight: 100 900; + font-stretch: 75% 125%; + font-display: swap; + src: url("../fonts/tourney-latin-ext-standard-normal.woff2") format("woff2-variations"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +/* Setting --font-sans / --font-mono changes the body and defaults; every + --font-* also generates a utility, so --font-roboto gives us `font-roboto` to + opt into Roboto where we want it. */ +@theme { + --font-sans: "Ubuntu", ui-sans-serif, system-ui, sans-serif; + --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace; + --font-ubuntu: "Ubuntu", ui-sans-serif, system-ui, sans-serif; + --font-roboto: "Roboto", ui-sans-serif, system-ui, sans-serif; + --font-tourney: "Tourney", ui-sans-serif, system-ui, sans-serif; +} + [data-theme="light"] .logo { content: var(--logo-dark); } From 1a2bf257da3443dbecbc6534059824feed7e6a3e Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 22:30:38 +0200 Subject: [PATCH 042/137] Brand the auth screens per tenant A club member signing in at ajax-united.rosterchief.app now sees their club's logo, name and colours; the base domain keeps the RosterChief skin for the control panel and Django admin. The mechanism is `{% extends base_template %}` -- Django lets the parent be a context variable, so the `branding` context processor picks the skin from request.club and *every* auth screen allauth ships (login, password reset, MFA, passkeys, and whatever it adds next) follows the tenant without a single one of them knowing that clubs exist. Templates split three ways: _base.html is the skeleton with no branding, and _platform_base.html / _club_base.html dress it. The control panel extends the platform base *explicitly* rather than through the variable, so a bug in branding resolution can never dress the panel up as a club. Club gains an optional logo and primary_color. Notes on both: - No logo falls back to the club's initials, never the RosterChief mark, which would pass our branding off as theirs. - Club colours land in an inline :root. daisyUI declares its theme variables inside `@layer base`, and unlayered styles beat every layered rule regardless of specificity, so this needs no !important. --color-primary-content is derived from WCAG relative luminance, so a club that picks pale yellow gets black text instead of invisible white. - primary_color is a text input, not : a colour picker cannot express "no colour", so every club that never touched it would submit #000000 and silently get a black theme. "/" now resolves per tenant (club home, or hand off to the control panel), which is why LOGIN_REDIRECT_URL can stay "/" and allauth needs no redirect adapter. Also folds in the theme toggle gaining a third "auto" state and the logo switching from `content:` to background-image (content-replacement on a real element is not supported in Firefox), both of which lived in the base template this commit replaces. Co-Authored-By: Claude Opus 4.8 --- club/context_processors.py | 23 ++++ .../0012_club_logo_club_primary_color.py | 25 ++++ club/models.py | 39 ++++++ club/templates/club/home.html | 18 +++ club/tests.py | 110 +++++++++++++++- club/views.py | 23 ++++ controlpanel/forms.py | 6 +- controlpanel/templates/controlpanel/base.html | 4 +- .../templates/controlpanel/club_form.html | 2 +- controlpanel/templatetags/ui.py | 1 + rosterchief/settings.py | 1 + rosterchief/urls.py | 8 ++ static/css/app.css | 2 +- static/src/app.css | 23 +++- templates/_base.html | 119 ++++++++++++++++++ templates/_club_base.html | 46 +++++++ templates/_controlpanel_base.html | 33 ----- templates/_platform_base.html | 35 ++++++ templates/allauth/layouts/base.html | 13 +- templates/base.html | 117 ----------------- 20 files changed, 487 insertions(+), 161 deletions(-) create mode 100644 club/context_processors.py create mode 100644 club/migrations/0012_club_logo_club_primary_color.py create mode 100644 club/templates/club/home.html create mode 100644 club/views.py create mode 100644 templates/_base.html create mode 100644 templates/_club_base.html delete mode 100644 templates/_controlpanel_base.html create mode 100644 templates/_platform_base.html delete mode 100644 templates/base.html diff --git a/club/context_processors.py b/club/context_processors.py new file mode 100644 index 0000000..8d2f0bc --- /dev/null +++ b/club/context_processors.py @@ -0,0 +1,23 @@ +"""Tenant-aware page branding. + +Every page inherits its chrome from ``base_template``. On a club subdomain that +resolves to the club-branded skin, on the base domain to the RosterChief one, so +the auth screens (login, password reset, MFA, passkeys — anything allauth ships, +now or later) follow the tenant without a single template of their own knowing +that clubs exist. + +The control panel deliberately does *not* use this: it hardcodes the platform +base, so no branding bug can ever dress the platform panel up as a club. +""" + +PLATFORM_BASE_TEMPLATE = "_platform_base.html" +CLUB_BASE_TEMPLATE = "_club_base.html" + + +def branding(request): + club = getattr(request, "club", None) # set by ClubTenantMiddleware + + return { + "club": club, + "base_template": CLUB_BASE_TEMPLATE if club else PLATFORM_BASE_TEMPLATE, + } diff --git a/club/migrations/0012_club_logo_club_primary_color.py b/club/migrations/0012_club_logo_club_primary_color.py new file mode 100644 index 0000000..90a7b79 --- /dev/null +++ b/club/migrations/0012_club_logo_club_primary_color.py @@ -0,0 +1,25 @@ +# Generated by Django 6.0.6 on 2026-07-13 17:33 + +import club.models +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0011_alter_club_slug'), + ] + + operations = [ + migrations.AddField( + model_name='club', + name='logo', + field=models.ImageField(blank=True, help_text="Shown on the club's own pages. Without one, the club's initials are used.", upload_to=club.models.club_logo_path, verbose_name='logo'), + ), + migrations.AddField( + model_name='club', + name='primary_color', + field=models.CharField(blank=True, help_text="Hex colour for buttons and links on the club's pages, e.g. #1e40af.", max_length=7, validators=[django.core.validators.RegexValidator('^#[0-9a-fA-F]{6}$', 'Enter a colour as a hex value, e.g. #1e40af.')], verbose_name='primary colour'), + ), + ] diff --git a/club/models.py b/club/models.py index 79db905..0a3a5db 100644 --- a/club/models.py +++ b/club/models.py @@ -1,5 +1,6 @@ import datetime +from django.core.validators import RegexValidator from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ @@ -22,10 +23,23 @@ class ClubManager(models.Manager): return self.filter(archived_at__isnull=False) +def club_logo_path(instance: Club, filename: str) -> str: + return f"clubs/{instance.slug}/{filename}" + + class Club(UUIDModel): name = models.CharField(_("name"), max_length=255) slug = models.SlugField(_("slug"), max_length=255, unique=True, blank=True, help_text=_("Drives subdomain / path resolution (e.g. ajax-united.rosterchief.app).")) + logo = models.ImageField(_("logo"), upload_to=club_logo_path, blank=True, help_text=_("Shown on the club's own pages. Without one, the club's initials are used.")) + primary_color = models.CharField( + _("primary colour"), + max_length=7, + blank=True, + validators=[RegexValidator(r"^#[0-9a-fA-F]{6}$", _("Enter a colour as a hex value, e.g. #1e40af."))], + help_text=_("Hex colour for buttons and links on the club's pages, e.g. #1e40af."), + ) + archived_at = models.DateTimeField(_("archived at"), null=True, blank=True, help_text=_("Archived clubs stop resolving on their subdomain, but their data is retained.")) objects = ClubManager() @@ -47,6 +61,31 @@ class Club(UUIDModel): def is_archived(self) -> bool: return self.archived_at is not None + @property + def initials(self) -> str: + """Stand-in for a missing logo. Never the RosterChief mark — that would + pass our branding off as the club's own.""" + return "".join(word[0] for word in self.name.split()[:2]).upper() + + @property + def primary_content_color(self) -> str: + """Readable text colour to sit *on* ``primary_color``. + + A club picking a pale yellow would otherwise get white-on-yellow buttons. + Relative luminance per WCAG, with its 0.179 threshold for black vs white. + """ + if not self.primary_color: + return "" + + def channel(value: int) -> float: + fraction = value / 255 + return fraction / 12.92 if fraction <= 0.04045 else ((fraction + 0.055) / 1.055) ** 2.4 + + red, green, blue = (channel(int(self.primary_color[index : index + 2], 16)) for index in (1, 3, 5)) + luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue + + return "#000000" if luminance > 0.179 else "#ffffff" + def archive(self): """Soft-delete: the club stops resolving, but nothing is destroyed. diff --git a/club/templates/club/home.html b/club/templates/club/home.html new file mode 100644 index 0000000..f6ce595 --- /dev/null +++ b/club/templates/club/home.html @@ -0,0 +1,18 @@ +{% extends "_club_base.html" %} +{% load lucide %} + +{% block head_title %}Home{% endblock head_title %} + +{% block main %} +

+{% endblock main %} diff --git a/club/tests.py b/club/tests.py index 34405d8..ba62d86 100644 --- a/club/tests.py +++ b/club/tests.py @@ -16,7 +16,7 @@ from events.models import Event from members.models import Family, FamilyMembership, Member from teams.models import Position, StaffAssignment, Team, TeamMembership -from .models import Club, ClubMembership, ClubRole, Season +from .models import Club, ClubMembership, ClubRole, Season, club_logo_path from .services.access import ( COACH_MANAGER, can_edit_event, @@ -960,3 +960,111 @@ class ClubRoleStatusSyncTests(TestCase): self.assertIn(ClubRole.Roles.ADMIN, roles_in_club(user, self.club)) self.assertTrue(has_club_role(user, self.club, ClubRole.Roles.ADMIN)) self.assertTrue(can_manage_shop(user, self.club)) + + +@override_settings( + ROSTERCHIEF_BASE_DOMAIN="rosterchief.app", + ALLOWED_HOSTS=["rosterchief.app", "ajax-united.rosterchief.app", "testserver"], +) +class BrandingTests(TestCase): + """The auth screens are shared; only the skin they inherit differs per tenant.""" + + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + + def login_page(self, host): + return self.client.get(reverse("account_login"), HTTP_HOST=host) + + def test_the_base_domain_gets_the_platform_skin(self): + response = self.login_page("rosterchief.app") + + self.assertTemplateUsed(response, "_platform_base.html") + self.assertTemplateNotUsed(response, "_club_base.html") + self.assertContains(response, "Club & Team Management") + self.assertIsNone(response.context["club"]) + + def test_a_club_subdomain_gets_the_club_skin(self): + response = self.login_page("ajax-united.rosterchief.app") + + self.assertTemplateUsed(response, "_club_base.html") + self.assertTemplateNotUsed(response, "_platform_base.html") + self.assertContains(response, "Ajax United") + self.assertEqual(response.context["club"], self.club) + + def test_an_archived_club_falls_back_to_the_platform_skin(self): + # The subdomain stops resolving, so there is no club to brand with. + self.club.archive() + + self.assertTemplateUsed(self.login_page("ajax-united.rosterchief.app"), "_platform_base.html") + + def test_a_club_without_a_logo_shows_its_initials_not_our_mark(self): + response = self.login_page("ajax-united.rosterchief.app") + + self.assertContains(response, "AU") + self.assertNotContains(response, "rosterchief-dark.svg") + + def test_a_club_logo_is_rendered_when_set(self): + self.club.logo = "clubs/ajax-united/crest.png" + self.club.save() + + self.assertContains(self.login_page("ajax-united.rosterchief.app"), "clubs/ajax-united/crest.png") + + def test_a_club_colour_overrides_the_theme(self): + self.club.primary_color = "#1e40af" + self.club.save() + + self.assertContains(self.login_page("ajax-united.rosterchief.app"), "--color-primary: #1e40af") + + def test_no_colour_means_no_override(self): + self.assertNotContains(self.login_page("ajax-united.rosterchief.app"), "--color-primary") + + +class ClubBrandingModelTests(TestCase): + def test_initials_use_the_first_two_words(self): + self.assertEqual(Club(name="Ajax United Football Club").initials, "AU") + self.assertEqual(Club(name="Ajax").initials, "A") + + def test_text_on_a_pale_colour_is_black_and_on_a_dark_one_white(self): + # A club picking pale yellow must not get white-on-yellow buttons. + self.assertEqual(Club(primary_color="#fef08a").primary_content_color, "#000000") + self.assertEqual(Club(primary_color="#1e40af").primary_content_color, "#ffffff") + + def test_no_colour_means_no_contrast_colour(self): + self.assertEqual(Club(primary_color="").primary_content_color, "") + + def test_a_colour_must_be_a_hex_value(self): + club = Club(name="Ajax United", primary_color="blue") + + with self.assertRaises(ValidationError): + club.full_clean() + + def test_logos_are_stored_per_club(self): + self.assertEqual(club_logo_path(Club(slug="ajax-united"), "crest.png"), "clubs/ajax-united/crest.png") + + +@override_settings( + ROSTERCHIEF_BASE_DOMAIN="rosterchief.app", + ALLOWED_HOSTS=["rosterchief.app", "ajax-united.rosterchief.app", "testserver"], +) +class RootViewTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.user = get_user_model().objects.create_user(email="member@example.com", password="pw-secret-123") + + def test_the_base_domain_hands_off_to_the_control_panel(self): + response = self.client.get("/", HTTP_HOST="rosterchief.app") + + self.assertRedirects(response, reverse("controlpanel:dashboard"), fetch_redirect_response=False) + + def test_a_club_subdomain_lands_on_the_club_home(self): + self.client.force_login(self.user) + + response = self.client.get("/", HTTP_HOST="ajax-united.rosterchief.app") + + self.assertTemplateUsed(response, "club/home.html") + self.assertContains(response, "Ajax United") + + def test_the_club_home_requires_a_login(self): + response = self.client.get("/", HTTP_HOST="ajax-united.rosterchief.app") + + self.assertRedirects(response, f"{reverse('account_login')}?next=/", fetch_redirect_response=False) diff --git a/club/views.py b/club/views.py new file mode 100644 index 0000000..3a5a18b --- /dev/null +++ b/club/views.py @@ -0,0 +1,23 @@ +from django.contrib.auth.mixins import LoginRequiredMixin +from django.shortcuts import redirect +from django.views.generic import TemplateView + + +class ClubHomeView(LoginRequiredMixin, TemplateView): + """Placeholder landing page for a club subdomain — where members land after + signing in, until the club-facing site is built.""" + + template_name = "club/home.html" + + +def root(request): + """``/`` means different things per tenant. + + This is why allauth needs no login-redirect adapter: LOGIN_REDIRECT_URL is "/", + and "/" resolves itself — a club subdomain lands on the club, the base domain + hands off to the platform control panel. + """ + if request.club is None: + return redirect("controlpanel:dashboard") + + return ClubHomeView.as_view()(request) diff --git a/controlpanel/forms.py b/controlpanel/forms.py index 7b76202..0bddd5a 100644 --- a/controlpanel/forms.py +++ b/controlpanel/forms.py @@ -10,8 +10,12 @@ from .services.admins import find_member_by_email class ClubForm(forms.ModelForm): class Meta: model = Club - fields = ["name", "slug"] + fields = ["name", "slug", "logo", "primary_color"] help_texts = {"slug": _("Drives the club's subdomain. Left blank, it is derived from the name.")} + # Deliberately a text input, not : a colour picker cannot + # express "no colour" -- it would submit #000000 for every club that never + # touched it, and every club would silently get a black theme. + widgets = {"primary_color": forms.TextInput(attrs={"placeholder": "#1e40af"})} def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/controlpanel/templates/controlpanel/base.html b/controlpanel/templates/controlpanel/base.html index f79004d..ece8afd 100644 --- a/controlpanel/templates/controlpanel/base.html +++ b/controlpanel/templates/controlpanel/base.html @@ -1,4 +1,6 @@ -{% extends "base.html" %} +{# Hardcoded, not `base_template`: the panel is platform-only, and a branding bug + must never be able to dress it up as a club. #} +{% extends "_platform_base.html" %} {% load lucide %} {% block title %} diff --git a/controlpanel/templates/controlpanel/club_form.html b/controlpanel/templates/controlpanel/club_form.html index b2e976b..e52f492 100644 --- a/controlpanel/templates/controlpanel/club_form.html +++ b/controlpanel/templates/controlpanel/club_form.html @@ -6,7 +6,7 @@ {% block panel %}
-
+ {% csrf_token %} {% for error in form.non_field_errors %}
diff --git a/controlpanel/templatetags/ui.py b/controlpanel/templatetags/ui.py index ca04f3d..82a7a56 100644 --- a/controlpanel/templatetags/ui.py +++ b/controlpanel/templatetags/ui.py @@ -8,6 +8,7 @@ register = template.Library() #: what makes every allauth and control-panel form field look right. WIDGET_CLASSES = ( (forms.CheckboxInput, "checkbox"), + (forms.FileInput, "file-input file-input-bordered w-full"), (forms.RadioSelect, "radio"), (forms.Select, "select select-bordered w-full"), (forms.Textarea, "textarea textarea-bordered w-full"), diff --git a/rosterchief/settings.py b/rosterchief/settings.py index fdd3375..110057e 100644 --- a/rosterchief/settings.py +++ b/rosterchief/settings.py @@ -156,6 +156,7 @@ TEMPLATES = [ "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", + "club.context_processors.branding", ], }, }, diff --git a/rosterchief/urls.py b/rosterchief/urls.py index c743dff..581bff8 100644 --- a/rosterchief/urls.py +++ b/rosterchief/urls.py @@ -8,16 +8,24 @@ enrolled. """ from django.conf import settings +from django.conf.urls.static import static from django.contrib import admin from django.urls import include, path from django.views.generic import RedirectView +from club.views import root + urlpatterns = [ path("admin/login/", RedirectView.as_view(pattern_name="account_login", query_string=True), name="admin_login_redirect"), path("admin/", admin.site.urls), path("accounts/", include("allauth.urls")), path("controlpanel/", include("controlpanel.urls")), + # "/" resolves per tenant: a club subdomain lands on the club, the base domain + # hands off to the control panel. This is why LOGIN_REDIRECT_URL can stay "/". + path("", root, name="root"), ] if settings.DEBUG: urlpatterns += [path("__reload__/", include("django_browser_reload.urls"))] + # Club logos are uploads: runserver has to serve MEDIA_ROOT itself. + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/static/css/app.css b/static/css/app.css index b82cc20..aabb488 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -1,2 +1,2 @@ /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:"Ubuntu", ui-sans-serif, system-ui, sans-serif;--font-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, monospace;--color-sky-500:oklch(68.5% .169 237.323);--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-roboto:"Roboto", ui-sans-serif, system-ui, sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax:"";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tab:is(.tabs>.tab){cursor:pointer;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:is(.tabs>.tab):hover{color:var(--color-base-content)}}.tab:is(.tabs>.tab){--tab-p:.75rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));--tab-radius-limit:min(var(--radius-field), var(--tab-radius-min));--tab-radius-grad:#0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + .25px), var(--tab-border-color) 69%, var(--tab-bg) calc(69% + .25px);order:var(--tab-order);height:var(--tab-height);padding-inline:var(--tab-p);border-color:#0000;font-size:.875rem}.tab:is(.tabs>.tab):is(input[type=radio]){min-width:fit-content}.tab:is(.tabs>.tab):is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:is(.tabs>.tab):is(label){position:relative}.tab:is(.tabs>.tab):is(label) input{cursor:pointer;appearance:none;opacity:0;position:absolute;inset:0}:is(.tab:is(.tabs>.tab):checked,.tab:is(.tabs>.tab):is(label:has(:checked)),.tab:is(.tabs>.tab):is(.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]))+.tab-content{display:block}.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.tab:is(.tabs>.tab):not(input):empty{cursor:default;flex-grow:1}.tab:is(.tabs>.tab):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:is(.tabs>.tab):focus{outline-offset:2px;outline:2px solid #0000}}.tab:is(.tabs>.tab):focus-visible,.tab:is(.tabs>.tab):is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab:is(.tabs>.tab)[disabled]{pointer-events:none;opacity:.4}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.avatar{vertical-align:middle;display:inline-flex;position:relative}.avatar>div{aspect-ratio:1;display:block;overflow:hidden}.avatar img{object-fit:cover;width:100%;height:100%}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.file-input{cursor:pointer;cursor:pointer;border:var(--border) solid #0000;appearance:none;background-color:var(--color-base-100);vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;width:clamp(3rem,20rem,100%);height:var(--size);border-color:var(--input-color);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));align-items:center;padding-inline-end:.75rem;font-size:.875rem;line-height:2;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.file-input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.file-input{--size:calc(var(--size-field,.25rem) * 10);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.file-input::file-selector-button{cursor:pointer;webkit-user-select:none;-webkit-user-select:none;user-select:none;height:calc(100% + var(--border) * 2);margin-inline-end:1rem;margin-block:calc(var(--border) * -1);color:var(--btn-fg);border-width:var(--border);border-style:solid;border-color:var(--btn-border);background-color:var(--btn-bg);background-size:calc(var(--noise) * 100%);background-image:var(--fx-noise);text-shadow:0 .5px oklch(1 0 0 / calc(var(--depth) * .15));box-shadow:0 .5px 0 .5px white inset, var(--btn-shadow);border-start-start-radius:calc(var(--join-ss,var(--radius-field) - var(--border)));border-end-start-radius:calc(var(--join-es,var(--radius-field) - var(--border)));margin-inline-start:calc(var(--border) * -1);padding-inline:1rem;font-size:.875rem;font-weight:600}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{box-shadow:0 .5px 0 .5px color-mix(in oklab, color-mix(in oklab, white 30%, var(--btn-bg)) calc(var(--depth) * 20%), #0000) inset, var(--btn-shadow)}}.file-input::file-selector-button{--size:calc(var(--size-field,.25rem) * 10);--btn-bg:var(--btn-color,var(--color-base-200));--btn-fg:var(--color-base-content);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-border:color-mix(in oklab, var(--btn-bg), #000 5%)}}.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000)}}.file-input:focus{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.file-input:focus{box-shadow:0 1px color-mix(in oklab, var(--input-color) 10%, #0000)}}.file-input:focus{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){box-shadow:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);--btn-border:#0000;--btn-fg:var(--color-base-content);background-image:none}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{--btn-fg:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem) * 10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}}@layer daisyui.l1.l2{.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.avatar-placeholder>div{justify-content:center;align-items:center;display:flex}.badge-ghost{border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content);background-image:none}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-error{--badge-color:var(--color-error);--badge-fg:var(--color-error-content)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-success{--badge-color:var(--color-success);--badge-fg:var(--color-success-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}.input-error,.input-error:focus,.input-error:focus-within{--input-color:var(--color-error)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.visible{visibility:visible}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.fixed{position:fixed}.static{position:static}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-16{height:calc(var(--spacing) * 16)}.min-h-screen{min-height:100vh}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-60{width:calc(var(--spacing) * 60)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-base-300{border-color:var(--color-base-300)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.bg-primary{background-color:var(--color-primary)}.object-contain{object-fit:contain}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-roboto{font-family:var(--font-roboto)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-base-content\/50{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/50{color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.text-primary-content{color:var(--color-primary-content)}.text-sky-500{color:var(--color-sky-500)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-italic.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-italic.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-ext-500-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-ext-700-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}[data-theme=light] .logo{content:var(--logo-dark)}[data-theme=dark] .logo{content:var(--logo-light)}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:"Ubuntu", ui-sans-serif, system-ui, sans-serif;--font-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, monospace;--color-sky-500:oklch(68.5% .169 237.323);--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-roboto:"Roboto", ui-sans-serif, system-ui, sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax:"";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tab:is(.tabs>.tab){cursor:pointer;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:is(.tabs>.tab):hover{color:var(--color-base-content)}}.tab:is(.tabs>.tab){--tab-p:.75rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));--tab-radius-limit:min(var(--radius-field), var(--tab-radius-min));--tab-radius-grad:#0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + .25px), var(--tab-border-color) 69%, var(--tab-bg) calc(69% + .25px);order:var(--tab-order);height:var(--tab-height);padding-inline:var(--tab-p);border-color:#0000;font-size:.875rem}.tab:is(.tabs>.tab):is(input[type=radio]){min-width:fit-content}.tab:is(.tabs>.tab):is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:is(.tabs>.tab):is(label){position:relative}.tab:is(.tabs>.tab):is(label) input{cursor:pointer;appearance:none;opacity:0;position:absolute;inset:0}:is(.tab:is(.tabs>.tab):checked,.tab:is(.tabs>.tab):is(label:has(:checked)),.tab:is(.tabs>.tab):is(.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]))+.tab-content{display:block}.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.tab:is(.tabs>.tab):not(input):empty{cursor:default;flex-grow:1}.tab:is(.tabs>.tab):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:is(.tabs>.tab):focus{outline-offset:2px;outline:2px solid #0000}}.tab:is(.tabs>.tab):focus-visible,.tab:is(.tabs>.tab):is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab:is(.tabs>.tab)[disabled]{pointer-events:none;opacity:.4}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.avatar{vertical-align:middle;display:inline-flex;position:relative}.avatar>div{aspect-ratio:1;display:block;overflow:hidden}.avatar img{object-fit:cover;width:100%;height:100%}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.file-input{cursor:pointer;cursor:pointer;border:var(--border) solid #0000;appearance:none;background-color:var(--color-base-100);vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;width:clamp(3rem,20rem,100%);height:var(--size);border-color:var(--input-color);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));align-items:center;padding-inline-end:.75rem;font-size:.875rem;line-height:2;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.file-input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.file-input{--size:calc(var(--size-field,.25rem) * 10);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.file-input::file-selector-button{cursor:pointer;webkit-user-select:none;-webkit-user-select:none;user-select:none;height:calc(100% + var(--border) * 2);margin-inline-end:1rem;margin-block:calc(var(--border) * -1);color:var(--btn-fg);border-width:var(--border);border-style:solid;border-color:var(--btn-border);background-color:var(--btn-bg);background-size:calc(var(--noise) * 100%);background-image:var(--fx-noise);text-shadow:0 .5px oklch(1 0 0 / calc(var(--depth) * .15));box-shadow:0 .5px 0 .5px white inset, var(--btn-shadow);border-start-start-radius:calc(var(--join-ss,var(--radius-field) - var(--border)));border-end-start-radius:calc(var(--join-es,var(--radius-field) - var(--border)));margin-inline-start:calc(var(--border) * -1);padding-inline:1rem;font-size:.875rem;font-weight:600}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{box-shadow:0 .5px 0 .5px color-mix(in oklab, color-mix(in oklab, white 30%, var(--btn-bg)) calc(var(--depth) * 20%), #0000) inset, var(--btn-shadow)}}.file-input::file-selector-button{--size:calc(var(--size-field,.25rem) * 10);--btn-bg:var(--btn-color,var(--color-base-200));--btn-fg:var(--color-base-content);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-border:color-mix(in oklab, var(--btn-bg), #000 5%)}}.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000)}}.file-input:focus{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.file-input:focus{box-shadow:0 1px color-mix(in oklab, var(--input-color) 10%, #0000)}}.file-input:focus{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){box-shadow:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);--btn-border:#0000;--btn-fg:var(--color-base-content);background-image:none}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{--btn-fg:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem) * 10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}}@layer daisyui.l1.l2{.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.avatar-placeholder>div{justify-content:center;align-items:center;display:flex}.badge-ghost{border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content);background-image:none}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-error{--badge-color:var(--color-error);--badge-fg:var(--color-error-content)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-success{--badge-color:var(--color-success);--badge-fg:var(--color-success-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}.input-error,.input-error:focus,.input-error:focus-within{--input-color:var(--color-error)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.visible{visibility:visible}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.fixed{position:fixed}.static{position:static}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-16{height:calc(var(--spacing) * 16)}.min-h-screen{min-height:100vh}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-60{width:calc(var(--spacing) * 60)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-base-300{border-color:var(--color-base-300)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.bg-primary{background-color:var(--color-primary)}.object-contain{object-fit:contain}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-roboto{font-family:var(--font-roboto)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-base-content\/50{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/50{color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.text-primary-content{color:var(--color-primary-content)}.text-sky-500{color:var(--color-sky-500)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-italic.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-italic.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-ext-500-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-ext-700-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}.logo{background-image:var(--logo-dark);background-position:50%;background-repeat:no-repeat;background-size:contain}@media (prefers-color-scheme:dark){.logo{background-image:var(--logo-light)}}[data-theme=light] .logo{background-image:var(--logo-dark)}[data-theme=dark] .logo{background-image:var(--logo-light)}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file diff --git a/static/src/app.css b/static/src/app.css index 93185a9..bb45435 100644 --- a/static/src/app.css +++ b/static/src/app.css @@ -166,10 +166,29 @@ --font-tourney: "Tourney", ui-sans-serif, system-ui, sans-serif; } +/* The logo is a background image, not `content:` -- content-replacement on a real + element (rather than ::before/::after) isn't supported in Firefox. + + Default = dark-ink logo, for a light background. The media query covers "auto", + where the toggle deliberately sets no data-theme at all; the attribute selectors + are more specific, so an explicit choice always beats the OS. */ +.logo { + background-image: var(--logo-dark); + background-position: center; + background-repeat: no-repeat; + background-size: contain; +} + +@media (prefers-color-scheme: dark) { + .logo { + background-image: var(--logo-light); + } +} + [data-theme="light"] .logo { - content: var(--logo-dark); + background-image: var(--logo-dark); } [data-theme="dark"] .logo { - content: var(--logo-light); + background-image: var(--logo-light); } diff --git a/templates/_base.html b/templates/_base.html new file mode 100644 index 0000000..e295c79 --- /dev/null +++ b/templates/_base.html @@ -0,0 +1,119 @@ +{% load lucide static %} + +{% comment %} + The page skeleton, with no branding of its own. `_platform_base.html` dresses it + as RosterChief, `_club_base.html` as a club; the `branding` context processor + picks between them per tenant. +{% endcomment %} + + + + + + + + {% block title %}RosterChief{% endblock title %} + + + {% comment %} + Apply the stored theme before first paint, otherwise the page flashes the wrong + colours. Nothing stored means "auto": we set no attribute at all, so daisyUI's + `dark --prefersdark` follows the OS. + {% endcomment %} + + + + + {# After the stylesheet: brand overrides (logo urls, club colours) must win. #} + {% block extra %}{% endblock extra %} + + + + + + {% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+ {% endif %} + +
+ {% block main %}{% endblock main %} +
+ + + + diff --git a/templates/_club_base.html b/templates/_club_base.html new file mode 100644 index 0000000..40f659b --- /dev/null +++ b/templates/_club_base.html @@ -0,0 +1,46 @@ +{% extends "_base.html" %} + +{% comment %} + A club's skin, used on its subdomain. `club` comes from the branding context + processor, which reads the tenant resolved by ClubTenantMiddleware. +{% endcomment %} + +{% block title %} + {% block head_title %}{% endblock head_title %} · {{ club.name }} +{% endblock title %} + +{% block extra %} + {% if club.primary_color %} + {% comment %} + daisyUI declares its theme variables inside `@layer base`, and unlayered styles + beat every layered rule regardless of specificity -- so this plain :root wins + without any !important or selector games. primary_content_color is computed from + the club's colour so a pale brand doesn't end up with white-on-yellow buttons. + {% endcomment %} + + {% endif %} +{% endblock extra %} + +{% block brand %} + + {% if club.logo %} + {{ club.name }} + {% else %} + {# Never the RosterChief mark: that would pass our branding off as the club's own. #} +
+
+ {{ club.initials }} +
+
+ {% endif %} +
+
{{ club.name }}
+
Powered by RosterChief
+
+
+{% endblock brand %} diff --git a/templates/_controlpanel_base.html b/templates/_controlpanel_base.html deleted file mode 100644 index 02581df..0000000 --- a/templates/_controlpanel_base.html +++ /dev/null @@ -1,33 +0,0 @@ -{% load static lucide %} - - - - - - - - RosterChief - Control Panel - - - - - - - - {% block extra %}{% endblock extra %} - - - - {% block main %}{% endblock main %} - - \ No newline at end of file diff --git a/templates/_platform_base.html b/templates/_platform_base.html new file mode 100644 index 0000000..74daa0d --- /dev/null +++ b/templates/_platform_base.html @@ -0,0 +1,35 @@ +{% extends "_base.html" %} +{% load static %} + +{% comment %} + RosterChief's own skin: the platform control panel, Django-admin-adjacent pages, + and every auth screen served on the base domain. +{% endcomment %} + +{% block title %} + {% block head_title %}{% endblock head_title %} · RosterChief +{% endblock title %} + +{% block extra %} + {% comment %} + Named after the logo's own ink, not the theme it is used on: the white logo goes + on dark backgrounds and vice versa. They live here rather than in app.css because + only Django knows the {% static %} URL. + {% endcomment %} + +{% endblock extra %} + +{% block brand %} + + +
+
RosterChief
+
Club & Team Management
+
+
+{% endblock brand %} diff --git a/templates/allauth/layouts/base.html b/templates/allauth/layouts/base.html index 07dfacd..010645f 100644 --- a/templates/allauth/layouts/base.html +++ b/templates/allauth/layouts/base.html @@ -1,8 +1,13 @@ -{% extends "_controlpanel_base.html" %} +{% extends base_template %} -{% block title %} - {% block head_title %}{% endblock head_title %} · RosterChief -{% endblock title %} +{% comment %} + Not a fixed parent: `base_template` is resolved per request by the branding context + processor, so every auth screen allauth ships -- login, password reset, MFA, passkeys, + and whatever it adds next -- picks up the club's skin on a club subdomain and the + RosterChief one on the base domain, with no template of its own knowing clubs exist. + + The title is left to the chosen base, which suffixes the club or the platform name. +{% endcomment %} {% block main %}
diff --git a/templates/base.html b/templates/base.html deleted file mode 100644 index fc75bef..0000000 --- a/templates/base.html +++ /dev/null @@ -1,117 +0,0 @@ -{% load lucide static %} - - - - - - - {% block title %}RosterChief{% endblock title %} - - {% comment %} - Apply the stored theme before first paint, otherwise the page flashes the wrong - colours. With no stored preference we set nothing, so daisyUI's `dark --prefersdark` - follows the OS. - {% endcomment %} - - - {% block extra_head %}{% endblock extra_head %} - - - - {% if messages %} -
- {% for message in messages %} -
- {{ message }} -
- {% endfor %} -
- {% endif %} -
- {% block main %}{% endblock main %} -
- - - From e44933330dbd2d249238a9b13d039e965f2a9234 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 22:35:06 +0200 Subject: [PATCH 043/137] Give messages an icon, a bold title and soft styling Each level now renders as a daisyUI soft alert: an icon, a bold heading and the message text. Django messages carry a level and a string -- there is no title field -- so the heading comes from the level ("Done", "Careful", "Something went wrong"), and a call site that wants a specific one passes it as extra_tags: messages.success(request, f"{club} is live.", extra_tags="Club created") The lookup is keyed on level_tag, not tags. `tags` is extra_tags and level_tag joined, so the old `message.tags == "error"` test would have stopped matching the moment any message carried a custom title, and every alert would have quietly rendered as blue info. Co-Authored-By: Claude Opus 4.8 --- controlpanel/templatetags/ui.py | 30 +++++++++++++++++++++++++++++ controlpanel/tests.py | 34 ++++++++++++++++++++++++++++++++- static/css/app.css | 2 +- templates/_base.html | 14 ++++++++++---- 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/controlpanel/templatetags/ui.py b/controlpanel/templatetags/ui.py index 82a7a56..b7afef7 100644 --- a/controlpanel/templatetags/ui.py +++ b/controlpanel/templatetags/ui.py @@ -16,6 +16,36 @@ WIDGET_CLASSES = ( DEFAULT_WIDGET_CLASS = "input input-bordered w-full" +#: Icon, default heading and daisyUI colour per message level. +MESSAGE_ALERTS = { + "debug": ("bug", "Debug", "alert-info"), + "info": ("info", "Heads up", "alert-info"), + "success": ("circle-check", "Done", "alert-success"), + "warning": ("triangle-alert", "Careful", "alert-warning"), + "error": ("circle-x", "Something went wrong", "alert-error"), +} +DEFAULT_MESSAGE_ALERT = MESSAGE_ALERTS["info"] + + +@register.filter +def as_alert(message): + """Presentation for one Django message: icon, bold title, body, colour. + + Django messages carry a level and a string — there is no title field — so the + title comes from the level, and a call site that wants a specific one passes it + as ``extra_tags``:: + + messages.success(request, f"{club} is live.", extra_tags="Club created") + + Keyed on ``level_tag``, never ``tags``: ``tags`` is extra_tags and level_tag + joined, so a message carrying a custom title would stop matching its own level + and quietly render as info. + """ + icon, title, css = MESSAGE_ALERTS.get(message.level_tag, DEFAULT_MESSAGE_ALERT) + + return {"icon": icon, "title": message.extra_tags or title, "body": message.message, "css": css} + + @register.filter def daisy(field): """Render a bound form field with the right daisyUI classes.""" diff --git a/controlpanel/tests.py b/controlpanel/tests.py index c2c5338..b29f422 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -2,7 +2,9 @@ from decimal import Decimal from allauth.mfa.models import Authenticator from django import forms +from django.contrib import messages from django.contrib.auth import get_user_model +from django.contrib.messages.storage.base import Message from django.core.cache import cache from django.test import TestCase, override_settings from django.urls import reverse @@ -17,7 +19,7 @@ from teams.models import Position, Team, TeamMembership from .services.admins import grant_club_admin from .services.platform_admins import PlatformAdminError, is_last_superuser, set_platform_access from .services.statistics import club_statistics, clubs_with_totals, platform_totals -from .templatetags.ui import daisy +from .templatetags.ui import as_alert, daisy User = get_user_model() Flag = get_waffle_flag_model() @@ -423,3 +425,33 @@ class FeatureViewTests(ControlPanelTestBase): self.assertContains(response, "On for all clubs") self.assertNotContains(response, reverse("controlpanel:club_feature_toggle", args=[self.club.pk, self.flag.pk])) + + +class MessageAlertTests(TestCase): + def alert(self, level, text, extra_tags=None): + return as_alert(Message(level, text, extra_tags=extra_tags)) + + def test_each_level_gets_its_own_icon_title_and_colour(self): + self.assertEqual(self.alert(messages.SUCCESS, "Saved.")["icon"], "circle-check") + self.assertEqual(self.alert(messages.WARNING, "Careful.")["css"], "alert-warning") + self.assertEqual(self.alert(messages.ERROR, "Boom.")["title"], "Something went wrong") + self.assertEqual(self.alert(messages.INFO, "FYI.")["css"], "alert-info") + + def test_extra_tags_override_the_title(self): + alert = self.alert(messages.SUCCESS, "Ajax United is live.", extra_tags="Club created") + + self.assertEqual(alert["title"], "Club created") + self.assertEqual(alert["body"], "Ajax United is live.") + self.assertEqual(alert["css"], "alert-success") # a custom title must not change the level + + def test_an_unknown_level_falls_back_to_info(self): + self.assertEqual(self.alert(999, "Odd.")["css"], "alert-info") + + +class MessageRenderingTests(ControlPanelTestBase): + def test_a_message_renders_as_a_soft_alert_with_icon_and_title(self): + response = self.client.post(reverse("controlpanel:club_archive", args=[self.club.pk]), follow=True) + + self.assertContains(response, "alert alert-soft alert-warning") + self.assertContains(response, '
Careful
', html=False) + self.assertContains(response, "";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tab:is(.tabs>.tab){cursor:pointer;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:is(.tabs>.tab):hover{color:var(--color-base-content)}}.tab:is(.tabs>.tab){--tab-p:.75rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));--tab-radius-limit:min(var(--radius-field), var(--tab-radius-min));--tab-radius-grad:#0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + .25px), var(--tab-border-color) 69%, var(--tab-bg) calc(69% + .25px);order:var(--tab-order);height:var(--tab-height);padding-inline:var(--tab-p);border-color:#0000;font-size:.875rem}.tab:is(.tabs>.tab):is(input[type=radio]){min-width:fit-content}.tab:is(.tabs>.tab):is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:is(.tabs>.tab):is(label){position:relative}.tab:is(.tabs>.tab):is(label) input{cursor:pointer;appearance:none;opacity:0;position:absolute;inset:0}:is(.tab:is(.tabs>.tab):checked,.tab:is(.tabs>.tab):is(label:has(:checked)),.tab:is(.tabs>.tab):is(.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]))+.tab-content{display:block}.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.tab:is(.tabs>.tab):not(input):empty{cursor:default;flex-grow:1}.tab:is(.tabs>.tab):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:is(.tabs>.tab):focus{outline-offset:2px;outline:2px solid #0000}}.tab:is(.tabs>.tab):focus-visible,.tab:is(.tabs>.tab):is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab:is(.tabs>.tab)[disabled]{pointer-events:none;opacity:.4}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.avatar{vertical-align:middle;display:inline-flex;position:relative}.avatar>div{aspect-ratio:1;display:block;overflow:hidden}.avatar img{object-fit:cover;width:100%;height:100%}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.file-input{cursor:pointer;cursor:pointer;border:var(--border) solid #0000;appearance:none;background-color:var(--color-base-100);vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;width:clamp(3rem,20rem,100%);height:var(--size);border-color:var(--input-color);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));align-items:center;padding-inline-end:.75rem;font-size:.875rem;line-height:2;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.file-input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.file-input{--size:calc(var(--size-field,.25rem) * 10);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.file-input::file-selector-button{cursor:pointer;webkit-user-select:none;-webkit-user-select:none;user-select:none;height:calc(100% + var(--border) * 2);margin-inline-end:1rem;margin-block:calc(var(--border) * -1);color:var(--btn-fg);border-width:var(--border);border-style:solid;border-color:var(--btn-border);background-color:var(--btn-bg);background-size:calc(var(--noise) * 100%);background-image:var(--fx-noise);text-shadow:0 .5px oklch(1 0 0 / calc(var(--depth) * .15));box-shadow:0 .5px 0 .5px white inset, var(--btn-shadow);border-start-start-radius:calc(var(--join-ss,var(--radius-field) - var(--border)));border-end-start-radius:calc(var(--join-es,var(--radius-field) - var(--border)));margin-inline-start:calc(var(--border) * -1);padding-inline:1rem;font-size:.875rem;font-weight:600}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{box-shadow:0 .5px 0 .5px color-mix(in oklab, color-mix(in oklab, white 30%, var(--btn-bg)) calc(var(--depth) * 20%), #0000) inset, var(--btn-shadow)}}.file-input::file-selector-button{--size:calc(var(--size-field,.25rem) * 10);--btn-bg:var(--btn-color,var(--color-base-200));--btn-fg:var(--color-base-content);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-border:color-mix(in oklab, var(--btn-bg), #000 5%)}}.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000)}}.file-input:focus{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.file-input:focus{box-shadow:0 1px color-mix(in oklab, var(--input-color) 10%, #0000)}}.file-input:focus{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){box-shadow:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);--btn-border:#0000;--btn-fg:var(--color-base-content);background-image:none}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{--btn-fg:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem) * 10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}}@layer daisyui.l1.l2{.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.avatar-placeholder>div{justify-content:center;align-items:center;display:flex}.badge-ghost{border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content);background-image:none}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-error{--badge-color:var(--color-error);--badge-fg:var(--color-error-content)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-success{--badge-color:var(--color-success);--badge-fg:var(--color-success-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}.input-error,.input-error:focus,.input-error:focus-within{--input-color:var(--color-error)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.visible{visibility:visible}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.fixed{position:fixed}.static{position:static}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-16{height:calc(var(--spacing) * 16)}.min-h-screen{min-height:100vh}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-60{width:calc(var(--spacing) * 60)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-base-300{border-color:var(--color-base-300)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.bg-primary{background-color:var(--color-primary)}.object-contain{object-fit:contain}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-roboto{font-family:var(--font-roboto)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-base-content\/50{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/50{color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.text-primary-content{color:var(--color-primary-content)}.text-sky-500{color:var(--color-sky-500)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-italic.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-italic.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-ext-500-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-ext-700-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}.logo{background-image:var(--logo-dark);background-position:50%;background-repeat:no-repeat;background-size:contain}@media (prefers-color-scheme:dark){.logo{background-image:var(--logo-light)}}[data-theme=light] .logo{background-image:var(--logo-dark)}[data-theme=dark] .logo{background-image:var(--logo-light)}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:"Ubuntu", ui-sans-serif, system-ui, sans-serif;--font-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, monospace;--color-sky-500:oklch(68.5% .169 237.323);--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-roboto:"Roboto", ui-sans-serif, system-ui, sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax:"";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tab:is(.tabs>.tab){cursor:pointer;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:is(.tabs>.tab):hover{color:var(--color-base-content)}}.tab:is(.tabs>.tab){--tab-p:.75rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));--tab-radius-limit:min(var(--radius-field), var(--tab-radius-min));--tab-radius-grad:#0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + .25px), var(--tab-border-color) 69%, var(--tab-bg) calc(69% + .25px);order:var(--tab-order);height:var(--tab-height);padding-inline:var(--tab-p);border-color:#0000;font-size:.875rem}.tab:is(.tabs>.tab):is(input[type=radio]){min-width:fit-content}.tab:is(.tabs>.tab):is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:is(.tabs>.tab):is(label){position:relative}.tab:is(.tabs>.tab):is(label) input{cursor:pointer;appearance:none;opacity:0;position:absolute;inset:0}:is(.tab:is(.tabs>.tab):checked,.tab:is(.tabs>.tab):is(label:has(:checked)),.tab:is(.tabs>.tab):is(.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]))+.tab-content{display:block}.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.tab:is(.tabs>.tab):not(input):empty{cursor:default;flex-grow:1}.tab:is(.tabs>.tab):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:is(.tabs>.tab):focus{outline-offset:2px;outline:2px solid #0000}}.tab:is(.tabs>.tab):focus-visible,.tab:is(.tabs>.tab):is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab:is(.tabs>.tab)[disabled]{pointer-events:none;opacity:.4}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.avatar{vertical-align:middle;display:inline-flex;position:relative}.avatar>div{aspect-ratio:1;display:block;overflow:hidden}.avatar img{object-fit:cover;width:100%;height:100%}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.file-input{cursor:pointer;cursor:pointer;border:var(--border) solid #0000;appearance:none;background-color:var(--color-base-100);vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;width:clamp(3rem,20rem,100%);height:var(--size);border-color:var(--input-color);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));align-items:center;padding-inline-end:.75rem;font-size:.875rem;line-height:2;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.file-input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.file-input{--size:calc(var(--size-field,.25rem) * 10);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.file-input::file-selector-button{cursor:pointer;webkit-user-select:none;-webkit-user-select:none;user-select:none;height:calc(100% + var(--border) * 2);margin-inline-end:1rem;margin-block:calc(var(--border) * -1);color:var(--btn-fg);border-width:var(--border);border-style:solid;border-color:var(--btn-border);background-color:var(--btn-bg);background-size:calc(var(--noise) * 100%);background-image:var(--fx-noise);text-shadow:0 .5px oklch(1 0 0 / calc(var(--depth) * .15));box-shadow:0 .5px 0 .5px white inset, var(--btn-shadow);border-start-start-radius:calc(var(--join-ss,var(--radius-field) - var(--border)));border-end-start-radius:calc(var(--join-es,var(--radius-field) - var(--border)));margin-inline-start:calc(var(--border) * -1);padding-inline:1rem;font-size:.875rem;font-weight:600}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{box-shadow:0 .5px 0 .5px color-mix(in oklab, color-mix(in oklab, white 30%, var(--btn-bg)) calc(var(--depth) * 20%), #0000) inset, var(--btn-shadow)}}.file-input::file-selector-button{--size:calc(var(--size-field,.25rem) * 10);--btn-bg:var(--btn-color,var(--color-base-200));--btn-fg:var(--color-base-content);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-border:color-mix(in oklab, var(--btn-bg), #000 5%)}}.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000)}}.file-input:focus{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.file-input:focus{box-shadow:0 1px color-mix(in oklab, var(--input-color) 10%, #0000)}}.file-input:focus{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){box-shadow:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);--btn-border:#0000;--btn-fg:var(--color-base-content);background-image:none}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{--btn-fg:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem) * 10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}}@layer daisyui.l1.l2{.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.avatar-placeholder>div{justify-content:center;align-items:center;display:flex}.badge-ghost{border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content);background-image:none}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-error{--badge-color:var(--color-error);--badge-fg:var(--color-error-content)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-success{--badge-color:var(--color-success);--badge-fg:var(--color-success-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}.input-error,.input-error:focus,.input-error:focus-within{--input-color:var(--color-error)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}.alert-soft{color:var(--alert-color,var(--color-base-content));background:var(--alert-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.alert-soft{background:color-mix(in oklab, var(--alert-color,var(--color-base-content)) 8%, var(--color-base-100))}}.alert-soft{--alert-border-color:var(--alert-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.alert-soft{--alert-border-color:color-mix(in oklab, var(--alert-color,var(--color-base-content)) 10%, var(--color-base-100))}}.alert-soft{box-shadow:none;background-image:none}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.visible{visibility:visible}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.fixed{position:fixed}.static{position:static}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-16{height:calc(var(--spacing) * 16)}.min-h-screen{min-height:100vh}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-60{width:calc(var(--spacing) * 60)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-base-300{border-color:var(--color-base-300)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.bg-primary{background-color:var(--color-primary)}.object-contain{object-fit:contain}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-roboto{font-family:var(--font-roboto)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-base-content\/50{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/50{color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.text-primary-content{color:var(--color-primary-content)}.text-sky-500{color:var(--color-sky-500)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-italic.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-italic.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-ext-500-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-ext-700-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}.logo{background-image:var(--logo-dark);background-position:50%;background-repeat:no-repeat;background-size:contain}@media (prefers-color-scheme:dark){.logo{background-image:var(--logo-light)}}[data-theme=light] .logo{background-image:var(--logo-dark)}[data-theme=dark] .logo{background-image:var(--logo-light)}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file diff --git a/templates/_base.html b/templates/_base.html index e295c79..69895a1 100644 --- a/templates/_base.html +++ b/templates/_base.html @@ -1,4 +1,4 @@ -{% load lucide static %} +{% load lucide static ui %} {% comment %} The page skeleton, with no branding of its own. `_platform_base.html` dresses it @@ -68,9 +68,15 @@ {% if messages %}
{% for message in messages %} -
- {{ message }} -
+ {% with alert=message|as_alert %} + + {% endwith %} {% endfor %}
{% endif %} From 7b18b39f497ed1bd0dd5b40166e5ed2017817675 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 22:43:44 +0200 Subject: [PATCH 044/137] Put icons in the login fields and drop their labels The entrance forms lose their visible field labels and gain an icon inside each field (mail, lock), and allauth's "Forgot your password?" link -- which is the password field's help_text -- is spaced away from the input instead of sitting flush against it. Three things this depends on: - allauth already passes `unlabeled=True` on the entrance forms and already sets a placeholder on every field there, so the visible label was redundant. The label is still emitted sr-only: a placeholder is not a label, and it vanishes as soon as you type. - daisyUI's icon-in-field layout puts the `input` class on the *wrapping label*, so the input itself must carry only `grow` -- hence the optional css override on the daisy filter. `input` on both draws a box inside a box, and the error state belongs on the wrapper for the same reason. - The help text now carries id="_helptext". Django points the input's aria-describedby at exactly that id, so without it the reference dangled and a screen reader never announced the password-reset link. Co-Authored-By: Claude Opus 4.8 --- controlpanel/templatetags/ui.py | 42 +- controlpanel/tests.py | 60 +- static/css/app.css | 3897 +++++++++++++++++++++++- templates/allauth/elements/fields.html | 42 +- templates/allauth/layouts/base.html | 2 +- 5 files changed, 4025 insertions(+), 18 deletions(-) diff --git a/controlpanel/templatetags/ui.py b/controlpanel/templatetags/ui.py index b7afef7..49cfafa 100644 --- a/controlpanel/templatetags/ui.py +++ b/controlpanel/templatetags/ui.py @@ -46,16 +46,40 @@ def as_alert(message): return {"icon": icon, "title": message.extra_tags or title, "body": message.message, "css": css} -@register.filter -def daisy(field): - """Render a bound form field with the right daisyUI classes.""" - widget = field.field.widget - css = next((css for widget_type, css in WIDGET_CLASSES if isinstance(widget, widget_type)), DEFAULT_WIDGET_CLASS) +#: Icon shown inside the field, by form field name. Anything unlisted gets none. +FIELD_ICONS = { + "login": "mail", + "email": "mail", + "email2": "mail", + "oldpassword": "lock-keyhole", + "password": "lock-keyhole", + "password1": "lock-keyhole", + "password2": "lock-keyhole", + "code": "shield-check", +} - classes = [widget.attrs.get("class", ""), css] - if field.errors: - classes.append(f"{css.split()[0]}-error") + +@register.filter +def field_icon(field): + return FIELD_ICONS.get(field.name, "") + + +@register.filter +def daisy(field, css=None): + """Render a bound form field with the right daisyUI classes. + + Pass ``css`` to override them — the icon-in-field layout wraps the input in a + ``label.input``, and there the input itself must NOT carry the ``input`` class + (daisyUI styles the wrapper instead), so it is rendered with ``grow``. The error + state then belongs on the wrapper too, which is why an override skips it here. + """ + widget = field.field.widget + + if css is None: + css = next((css for widget_type, css in WIDGET_CLASSES if isinstance(widget, widget_type)), DEFAULT_WIDGET_CLASS) + if field.errors: + css = f"{css} {css.split()[0]}-error" attrs = dict(widget.attrs) - attrs["class"] = " ".join(part for part in classes if part) + attrs["class"] = " ".join(part for part in [widget.attrs.get("class", ""), css] if part) return field.as_widget(attrs=attrs) diff --git a/controlpanel/tests.py b/controlpanel/tests.py index b29f422..1aaba5e 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -19,7 +19,7 @@ from teams.models import Position, Team, TeamMembership from .services.admins import grant_club_admin from .services.platform_admins import PlatformAdminError, is_last_superuser, set_platform_access from .services.statistics import club_statistics, clubs_with_totals, platform_totals -from .templatetags.ui import as_alert, daisy +from .templatetags.ui import as_alert, daisy, field_icon User = get_user_model() Flag = get_waffle_flag_model() @@ -455,3 +455,61 @@ class MessageRenderingTests(ControlPanelTestBase): self.assertContains(response, "alert alert-soft alert-warning") self.assertContains(response, '
Careful
', html=False) self.assertContains(response, "Email') + self.assertContains(self.response, 'class="sr-only">Password') + self.assertNotContains(self.response, 'Email') + self.assertContains(self.response, 'placeholder="Email address"') + + def test_the_checkbox_keeps_its_visible_label(self): + self.assertContains(self.response, 'Remember Me') + + def test_the_password_reset_link_is_spaced_and_addressable(self): + # The input's aria-describedby points here; without the id it dangles. + self.assertContains(self.response, 'id="id_password_helptext"') + self.assertContains(self.response, "mt-3") + self.assertContains(self.response, "Forgot your password?") diff --git a/static/css/app.css b/static/css/app.css index 3af89f0..5f2d35b 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -1,2 +1,3897 @@ /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:"Ubuntu", ui-sans-serif, system-ui, sans-serif;--font-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, monospace;--color-sky-500:oklch(68.5% .169 237.323);--color-black:#000;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-roboto:"Roboto", ui-sans-serif, system-ui, sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax:"";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tab:is(.tabs>.tab){cursor:pointer;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:is(.tabs>.tab):hover{color:var(--color-base-content)}}.tab:is(.tabs>.tab){--tab-p:.75rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));--tab-radius-limit:min(var(--radius-field), var(--tab-radius-min));--tab-radius-grad:#0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + .25px), var(--tab-border-color) 69%, var(--tab-bg) calc(69% + .25px);order:var(--tab-order);height:var(--tab-height);padding-inline:var(--tab-p);border-color:#0000;font-size:.875rem}.tab:is(.tabs>.tab):is(input[type=radio]){min-width:fit-content}.tab:is(.tabs>.tab):is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:is(.tabs>.tab):is(label){position:relative}.tab:is(.tabs>.tab):is(label) input{cursor:pointer;appearance:none;opacity:0;position:absolute;inset:0}:is(.tab:is(.tabs>.tab):checked,.tab:is(.tabs>.tab):is(label:has(:checked)),.tab:is(.tabs>.tab):is(.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]))+.tab-content{display:block}.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.tab:is(.tabs>.tab):not(input):empty{cursor:default;flex-grow:1}.tab:is(.tabs>.tab):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:is(.tabs>.tab):focus{outline-offset:2px;outline:2px solid #0000}}.tab:is(.tabs>.tab):focus-visible,.tab:is(.tabs>.tab):is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab:is(.tabs>.tab)[disabled]{pointer-events:none;opacity:.4}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.avatar{vertical-align:middle;display:inline-flex;position:relative}.avatar>div{aspect-ratio:1;display:block;overflow:hidden}.avatar img{object-fit:cover;width:100%;height:100%}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.file-input{cursor:pointer;cursor:pointer;border:var(--border) solid #0000;appearance:none;background-color:var(--color-base-100);vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;width:clamp(3rem,20rem,100%);height:var(--size);border-color:var(--input-color);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));align-items:center;padding-inline-end:.75rem;font-size:.875rem;line-height:2;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.file-input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.file-input{--size:calc(var(--size-field,.25rem) * 10);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.file-input::file-selector-button{cursor:pointer;webkit-user-select:none;-webkit-user-select:none;user-select:none;height:calc(100% + var(--border) * 2);margin-inline-end:1rem;margin-block:calc(var(--border) * -1);color:var(--btn-fg);border-width:var(--border);border-style:solid;border-color:var(--btn-border);background-color:var(--btn-bg);background-size:calc(var(--noise) * 100%);background-image:var(--fx-noise);text-shadow:0 .5px oklch(1 0 0 / calc(var(--depth) * .15));box-shadow:0 .5px 0 .5px white inset, var(--btn-shadow);border-start-start-radius:calc(var(--join-ss,var(--radius-field) - var(--border)));border-end-start-radius:calc(var(--join-es,var(--radius-field) - var(--border)));margin-inline-start:calc(var(--border) * -1);padding-inline:1rem;font-size:.875rem;font-weight:600}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{box-shadow:0 .5px 0 .5px color-mix(in oklab, color-mix(in oklab, white 30%, var(--btn-bg)) calc(var(--depth) * 20%), #0000) inset, var(--btn-shadow)}}.file-input::file-selector-button{--size:calc(var(--size-field,.25rem) * 10);--btn-bg:var(--btn-color,var(--color-base-200));--btn-fg:var(--color-base-content);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-border:color-mix(in oklab, var(--btn-bg), #000 5%)}}.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000)}}.file-input:focus{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.file-input:focus{box-shadow:0 1px color-mix(in oklab, var(--input-color) 10%, #0000)}}.file-input:focus{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){box-shadow:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);--btn-border:#0000;--btn-fg:var(--color-base-content);background-image:none}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{--btn-fg:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem) * 10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}}@layer daisyui.l1.l2{.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.avatar-placeholder>div{justify-content:center;align-items:center;display:flex}.badge-ghost{border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content);background-image:none}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-error{--badge-color:var(--color-error);--badge-fg:var(--color-error-content)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-success{--badge-color:var(--color-success);--badge-fg:var(--color-success-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}.input-error,.input-error:focus,.input-error:focus-within{--input-color:var(--color-error)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}.alert-soft{color:var(--alert-color,var(--color-base-content));background:var(--alert-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.alert-soft{background:color-mix(in oklab, var(--alert-color,var(--color-base-content)) 8%, var(--color-base-100))}}.alert-soft{--alert-border-color:var(--alert-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.alert-soft{--alert-border-color:color-mix(in oklab, var(--alert-color,var(--color-base-content)) 10%, var(--color-base-100))}}.alert-soft{box-shadow:none;background-image:none}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.visible{visibility:visible}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.fixed{position:fixed}.static{position:static}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-16{height:calc(var(--spacing) * 16)}.min-h-screen{min-height:100vh}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-60{width:calc(var(--spacing) * 60)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-base-300{border-color:var(--color-base-300)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.bg-primary{background-color:var(--color-primary)}.object-contain{object-fit:contain}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-roboto{font-family:var(--font-roboto)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-base-content\/50{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/50{color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.text-primary-content{color:var(--color-primary-content)}.text-sky-500{color:var(--color-sky-500)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-italic.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-italic.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-ext-500-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-ext-700-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}.logo{background-image:var(--logo-dark);background-position:50%;background-repeat:no-repeat;background-size:contain}@media (prefers-color-scheme:dark){.logo{background-image:var(--logo-light)}}[data-theme=light] .logo{background-image:var(--logo-dark)}[data-theme=dark] .logo{background-image:var(--logo-light)}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file +@layer properties; +@layer theme, base, components, utilities; +@layer theme { + :root, :host { + --font-sans: "Ubuntu", ui-sans-serif, system-ui, sans-serif; + --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace; + --color-sky-500: oklch(68.5% 0.169 237.323); + --color-black: #000; + --spacing: 0.25rem; + --container-xs: 20rem; + --container-xl: 36rem; + --text-xs: 0.75rem; + --text-xs--line-height: calc(1 / 0.75); + --text-sm: 0.875rem; + --text-sm--line-height: calc(1.25 / 0.875); + --text-base: 1rem; + --text-base--line-height: calc(1.5 / 1); + --text-lg: 1.125rem; + --text-lg--line-height: calc(1.75 / 1.125); + --text-xl: 1.25rem; + --text-xl--line-height: calc(1.75 / 1.25); + --text-2xl: 1.5rem; + --text-2xl--line-height: calc(2 / 1.5); + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + --tracking-wider: 0.05em; + --ease-out: cubic-bezier(0, 0, 0.2, 1); + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --default-transition-duration: 150ms; + --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + --default-font-family: var(--font-sans); + --default-mono-font-family: var(--font-mono); + --font-roboto: "Roboto", ui-sans-serif, system-ui, sans-serif; + } +} +@layer base { + *, ::after, ::before, ::backdrop, ::file-selector-button { + box-sizing: border-box; + margin: 0; + padding: 0; + border: 0 solid; + } + html, :host { + line-height: 1.5; + -webkit-text-size-adjust: 100%; + tab-size: 4; + font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); + font-feature-settings: var(--default-font-feature-settings, normal); + font-variation-settings: var(--default-font-variation-settings, normal); + -webkit-tap-highlight-color: transparent; + } + hr { + height: 0; + color: inherit; + border-top-width: 1px; + } + abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; + } + h1, h2, h3, h4, h5, h6 { + font-size: inherit; + font-weight: inherit; + } + a { + color: inherit; + -webkit-text-decoration: inherit; + text-decoration: inherit; + } + b, strong { + font-weight: bolder; + } + code, kbd, samp, pre { + font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); + font-feature-settings: var(--default-mono-font-feature-settings, normal); + font-variation-settings: var(--default-mono-font-variation-settings, normal); + font-size: 1em; + } + small { + font-size: 80%; + } + sub, sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; + } + sub { + bottom: -0.25em; + } + sup { + top: -0.5em; + } + table { + text-indent: 0; + border-color: inherit; + border-collapse: collapse; + } + :-moz-focusring { + outline: auto; + } + progress { + vertical-align: baseline; + } + summary { + display: list-item; + } + ol, ul, menu { + list-style: none; + } + img, svg, video, canvas, audio, iframe, embed, object { + display: block; + vertical-align: middle; + } + img, video { + max-width: 100%; + height: auto; + } + button, input, select, optgroup, textarea, ::file-selector-button { + font: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + letter-spacing: inherit; + color: inherit; + border-radius: 0; + background-color: transparent; + opacity: 1; + } + :where(select:is([multiple], [size])) optgroup { + font-weight: bolder; + } + :where(select:is([multiple], [size])) optgroup option { + padding-inline-start: 20px; + } + ::file-selector-button { + margin-inline-end: 4px; + } + ::placeholder { + opacity: 1; + } + @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) { + ::placeholder { + color: currentcolor; + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, currentcolor 50%, transparent); + } + } + } + textarea { + resize: vertical; + } + ::-webkit-search-decoration { + -webkit-appearance: none; + } + ::-webkit-date-and-time-value { + min-height: 1lh; + text-align: inherit; + } + ::-webkit-datetime-edit { + display: inline-flex; + } + ::-webkit-datetime-edit-fields-wrapper { + padding: 0; + } + ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { + padding-block: 0; + } + ::-webkit-calendar-picker-indicator { + line-height: 1; + } + :-moz-ui-invalid { + box-shadow: none; + } + button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button { + appearance: button; + } + ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { + height: auto; + } + [hidden]:where(:not([hidden="until-found"])) { + display: none !important; + } +} +@layer utilities { + .tab { + @layer daisyui.l1.l2.l3 { + &:is(.tabs > .tab) { + position: relative; + display: inline-flex; + cursor: pointer; + appearance: none; + flex-wrap: wrap; + align-items: center; + justify-content: center; + text-align: center; + webkit-user-select: none; + user-select: none; + &:hover { + @media (hover: hover) { + color: var(--color-base-content); + } + } + --tab-p: 0.75rem; + --tab-bg: var(--color-base-100); + --tab-border-color: var(--color-base-300); + --tab-radius-ss: 0; + --tab-radius-se: 0; + --tab-radius-es: 0; + --tab-radius-ee: 0; + --tab-order: 0; + --tab-radius-min: calc(0.75rem - var(--border)); + --tab-radius-limit: min(var(--radius-field), var(--tab-radius-min)); + --tab-radius-grad: #0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + 0.25px), + var(--tab-border-color) 69%, var(--tab-bg) calc(69% + 0.25px); + border-color: #0000; + order: var(--tab-order); + height: var(--tab-height); + font-size: 0.875rem; + padding-inline: var(--tab-p); + &:is(input[type="radio"]) { + min-width: fit-content; + &:after { + --tw-content: attr(aria-label); + content: var(--tw-content); + } + } + &:is(label) { + position: relative; + input { + position: absolute; + inset: 0; + cursor: pointer; + appearance: none; + opacity: 0%; + } + } + &:checked, &:is(label:has(:checked)), &:is(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]) { + & + .tab-content { + display: block; + } + } + &:not(:checked, label:has(:checked), :hover, .tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"] ) { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 50%, transparent); + } + } + &:not(input):empty { + flex-grow: 1; + cursor: default; + } + &:focus { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &:focus-visible, &:is(label:has(:checked:focus-visible)) { + outline: 2px solid currentColor; + outline-offset: -5px; + } + &[disabled] { + pointer-events: none; + opacity: 40%; + } + } + } + } + .menu { + @layer daisyui.l1.l2.l3 { + display: flex; + width: fit-content; + flex-direction: column; + flex-wrap: wrap; + padding: calc(0.25rem * 2); + --menu-active-fg: var(--color-neutral-content); + --menu-active-bg: var(--color-neutral); + font-size: 0.875rem; + :where(li ul, li menu) { + position: relative; + margin-inline-start: calc(0.25rem * 4); + padding-inline-start: calc(0.25rem * 2); + white-space: nowrap; + &:before { + position: absolute; + inset-inline-start: calc(0.25rem * 0); + top: calc(0.25rem * 3); + bottom: calc(0.25rem * 3); + background-color: var(--color-base-content); + opacity: 10%; + width: var(--border); + content: ""; + } + } + :where(li > .menu-dropdown:not(.menu-dropdown-show)) { + display: none; + } + :where(li:not(.menu-title) > *:not(ul, menu, details, .menu-title, .btn)), :where(li:not(.menu-title) > details > summary:not(.menu-title)) { + display: grid; + grid-auto-flow: column; + align-content: flex-start; + align-items: center; + gap: calc(0.25rem * 2); + border-radius: var(--radius-field); + padding-inline: calc(0.25rem * 3); + padding-block: calc(0.25rem * 1.5); + text-align: start; + transition-property: color, background-color, box-shadow; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + grid-auto-columns: minmax(auto, max-content) auto max-content; + user-select: none; + } + :where(li > details > summary) { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + &::-webkit-details-marker { + display: none; + } + } + :where(li > details > summary), :where(li > .menu-dropdown-toggle) { + &:after { + justify-self: flex-end; + display: block; + height: 0.375rem; + width: 0.375rem; + rotate: -135deg; + translate: 0 -1px; + transition-property: rotate, translate; + transition-duration: 0.2s; + content: ""; + transform-origin: 50% 50%; + box-shadow: 2px 2px inset; + pointer-events: none; + } + } + details { + overflow: hidden; + interpolate-size: allow-keywords; + } + details::details-content { + block-size: 0; + @media (prefers-reduced-motion: no-preference) { + transition-behavior: allow-discrete; + transition-property: block-size, content-visibility; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + } + } + details[open]::details-content { + block-size: auto; + } + :where(li > details[open] > summary):after, :where(li > .menu-dropdown-toggle.menu-dropdown-show):after { + rotate: 45deg; + translate: 0 1px; + } + :where(li:not(.menu-title, .disabled) > *:not(ul, menu, details, .menu-title), li:not(.menu-title, .disabled) > details > summary:not(.menu-title) ):not(.menu-active, :active, .btn) { + &.menu-focus, &:focus-visible { + cursor: pointer; + background-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); + } + color: var(--color-base-content); + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + } + :where(li:not(.menu-title, .disabled) > *:not(ul, menu, details, .menu-title):not(.menu-active, :active, .btn):hover, li:not(.menu-title, .disabled) > details > summary:not(.menu-title):not(.menu-active, :active, .btn):hover ) { + cursor: pointer; + background-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); + } + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + box-shadow: 0 1px oklch(0% 0 0 / 0.01) inset, 0 -1px oklch(100% 0 0 / 0.01) inset; + } + :where(li:empty) { + background-color: var(--color-base-content); + opacity: 10%; + margin: 0.5rem 1rem; + height: 1px; + } + :where(li) { + position: relative; + display: flex; + flex-shrink: 0; + flex-direction: column; + flex-wrap: wrap; + align-items: stretch; + .badge { + justify-self: flex-end; + } + & > *:not(ul, menu, .menu-title, details, .btn):active, & > *:not(ul, menu, .menu-title, details, .btn).menu-active, & > details > summary:active { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + color: var(--menu-active-fg); + background-color: var(--menu-active-bg); + background-size: auto, calc(var(--noise) * 100%); + background-image: none, var(--fx-noise); + &:not(&:active) { + box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg); + } + } + &.menu-disabled, [disabled] { + pointer-events: none; + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 20%, transparent); + } + } + } + .dropdown:focus-within { + .menu-dropdown-toggle:after { + rotate: 45deg; + translate: 0 1px; + } + } + .dropdown-content { + margin-top: calc(0.25rem * 2); + padding: calc(0.25rem * 2); + &:before { + display: none; + } + } + } + } + .collapse-arrow { + @layer daisyui.l1.l2 { + > .collapse-title:after { + position: absolute; + display: block; + height: 0.5rem; + width: 0.5rem; + transform: translateY(-100%) rotate(45deg); + @media (prefers-reduced-motion: no-preference) { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 0.2s; + } + top: 50%; + inset-inline-end: 1.4rem; + content: ""; + transform-origin: 75% 75%; + box-shadow: 2px 2px; + pointer-events: none; + } + } + } + .collapse-plus { + @layer daisyui.l1.l2 { + > .collapse-title:after { + position: absolute; + display: block; + height: 0.5rem; + width: 0.5rem; + @media (prefers-reduced-motion: no-preference) { + transition-property: all; + transition-duration: 300ms; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + } + top: 0.9rem; + inset-inline-end: 1.4rem; + --tw-content: "+"; + content: var(--tw-content); + pointer-events: none; + } + } + } + .dropdown { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-block; + position-area: var(--anchor-v, bottom) var(--anchor-h, span-right); + & > *:not(:has( ~ [class*="dropdown-content"])):focus { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + .dropdown-content { + position: absolute; + } + &.dropdown-close .dropdown-content, &:not(details, .dropdown-open, .dropdown-hover:hover, :focus-within) .dropdown-content, &.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible) ~ .dropdown-content { + display: none; + transform-origin: top; + opacity: 0%; + scale: 95%; + } + &[popover], .dropdown-content { + z-index: 999; + @media (prefers-reduced-motion: no-preference) { + animation: dropdown 0.2s; + transition-property: opacity, scale, display, overlay; + transition-behavior: allow-discrete; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + } + } + @starting-style { + &[popover], .dropdown-content { + scale: 95%; + opacity: 0; + } + } + &:not(.dropdown-close) { + &.dropdown-open, &:not(.dropdown-hover):focus, &:focus-within { + > [tabindex]:first-child { + pointer-events: none; + } + .dropdown-content { + opacity: 100%; + scale: 100%; + } + } + &.dropdown-hover:hover { + .dropdown-content { + opacity: 100%; + scale: 100%; + } + } + } + &:is(details) { + overflow: revert-layer; + summary { + &::-webkit-details-marker { + display: none; + } + } + } + &:where([popover]) { + background: #0000; + } + &[popover] { + position: fixed; + color: inherit; + @supports not (position-area: bottom) { + margin: auto; + &.dropdown-close, &.dropdown-open:not(:popover-open) { + display: none; + transform-origin: top; + opacity: 0%; + scale: 95%; + } + &::backdrop { + background-color: color-mix(in oklab, #000 30%, #0000); + } + } + &.dropdown-close, &:not(.dropdown-open, :popover-open) { + display: none; + transform-origin: top; + opacity: 0%; + scale: 95%; + } + } + } + } + .btn { + :where(&) { + @layer daisyui.l1.l2.l3 { + width: unset; + } + } + .prose :where(a&:not(.btn-link)):not(:where([class~="not-prose"], [class~="not-prose"] *)) { + text-decoration-line: none; + } + @layer daisyui.l1.l2.l3 { + --size: calc(var(--size-field, 0.25rem) * 10); + --btn-p: 1rem; + --btn-fg: var(--color-base-content); + display: inline-flex; + flex-shrink: 0; + cursor: pointer; + flex-wrap: nowrap; + align-items: center; + justify-content: center; + gap: calc(0.25rem * 1.5); + text-align: center; + vertical-align: middle; + outline-offset: 2px; + webkit-user-select: none; + user-select: none; + font-weight: 600; + border-start-start-radius: var(--join-ss, var(--radius-field)); + border-start-end-radius: var(--join-se, var(--radius-field)); + border-end-start-radius: var(--join-es, var(--radius-field)); + border-end-end-radius: var(--join-ee, var(--radius-field)); + border-width: var(--border); + touch-action: manipulation; + transition-property: color, background-color, border-color, box-shadow, transform; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-duration: 0.2s; + --btn-bg: var(--btn-color, var(--color-base-200)); + --btn-border: var(--btn-color, var(--color-base-200)); + @supports (color: color-mix(in lab, red, red)) { + --btn-border: color-mix( + in oklab, + var(--btn-color, var(--color-base-200)), + #000 calc(var(--depth) * 5%) + ); + } + --btn-soft-bg: initial; + --btn-shadow: 0 3px 2px -2px var(--btn-bg), + 0 4px 3px -2px var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), + 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); + } + --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); + height: var(--size); + padding-inline: var(--btn-p); + font-size: var(--fontsize, 0.875rem); + background-color: var(--btn-bg); + color: var(--btn-fg); + border-color: var(--btn-border); + border-style: var(--btn-border-style, solid); + outline-color: var(--btn-color, var(--color-base-content)); + --tw-prose-links: var(--btn-fg); + background-image: none, var(--fx-noise); + background-size: auto, calc(var(--noise, 0) * 100%); + text-shadow: 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 0.15)); + box-shadow: var(--btn-inset) inset, var(--btn-shadow); + &:is([type="checkbox"], [type="radio"]) { + appearance: none; + &[aria-label]::after { + --tw-content: attr(aria-label); + content: var(--tw-content); + } + } + } + @layer daisyui.l1 { + &:where(:checked:not(.filter [type="radio"].btn)) { + --btn-color: var(--color-primary); + --btn-fg: var(--color-primary-content); + } + @media (hover: hover) { + &:hover { + --btn-bg: var(--btn-color, var(--color-base-200)); + @supports (color: color-mix(in lab, red, red)) { + --btn-bg: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 7%); + } + color: var(--btn-fg); + --btn-border: var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-border: color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%)); + } + --btn-border-style: solid; + --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); + --btn-shadow: 0 3px 2px -2px var(--btn-bg), + 0 4px 3px -2px var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), + 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); + } + } + } + &:active:not(.btn-active) { + translate: 0 0.5px; + --btn-bg: var(--btn-color, var(--color-base-200)); + @supports (color: color-mix(in lab, red, red)) { + --btn-bg: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 5%); + } + color: var(--btn-fg, var(--color-base-content)); + --btn-border: var(--btn-color, var(--color-base-200)); + @supports (color: color-mix(in lab, red, red)) { + --btn-border: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 7%); + } + --btn-border-style: solid; + --btn-inset: 0 0 0 0 oklch(0% 0 0/0); + --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); + } + &:where(:checked:not(.filter [type="radio"].btn), :not([type="radio"], [type="checkbox"]):focus-visible ) { + --btn-bg: var(--btn-color, var(--color-base-200)); + color: var(--btn-fg, var(--color-base-content)); + --btn-border: var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-border: color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%)); + } + --btn-border-style: solid; + --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); + --btn-shadow: 0 3px 2px -2px var(--btn-bg), + 0 4px 3px -2px var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), + 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); + } + isolation: isolate; + } + &:focus-visible, &:has(:focus-visible) { + outline-width: 2px; + outline-style: solid; + isolation: isolate; + } + } + &:is(:disabled, [disabled], [aria-disabled="true"]) { + @layer daisyui { + pointer-events: none; + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklch, var(--color-base-content) 20%, #0000); + } + --btn-bg: #0000; + --btn-border: #0000; + background-image: none; + --btn-inset: 0 0 0 0 oklch(0% 0 0/0); + --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); + &:not(.btn-link, .btn-ghost) { + background-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); + } + } + } + } + } + .collapse { + &:not(td, tr, colgroup) { + visibility: revert-layer; + } + @layer daisyui.l1.l2.l3 { + display: grid; + position: relative; + border-radius: var(--radius-box, 1rem); + width: 100%; + grid-template-rows: max-content 0fr; + grid-template-columns: minmax(0, 1fr); + isolation: isolate; + @media (prefers-reduced-motion: no-preference) { + transition: grid-template-rows 0.2s; + } + > input:is([type="checkbox"], [type="radio"]) { + grid-column-start: 1; + grid-row-start: 1; + appearance: none; + opacity: 0; + z-index: 1; + width: 100%; + padding: 1rem; + padding-inline-end: 3rem; + min-height: 1lh; + transition: background-color 0.2s ease-out; + } + &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ), &:not(.collapse-close):has( > input:is([type="checkbox"], [type="radio"]):checked) { + grid-template-rows: max-content 1fr; + } + &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ) > .collapse-content, &:not(.collapse-close) > :where(input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-content) { + --overflow-delay: 0.2s; + overflow: revert-layer; + content-visibility: visible; + min-height: fit-content; + @supports not (content-visibility: visible) { + visibility: visible; + } + } + &:focus-visible, &:has( > input:is([type="checkbox"], [type="radio"]):focus-visible), &:has(summary:focus-visible) { + outline-color: var(--color-base-content); + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; + } + &:not(.collapse-close) { + > input[type="checkbox"], > input[type="radio"]:not(:checked), > .collapse-title { + cursor: pointer; + } + } + &[tabindex]:focus:not(.collapse-close, .collapse[open]), &[tabindex]:focus-within:not(.collapse-close, .collapse[open]) { + > .collapse-title { + cursor: unset; + } + } + &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ) > :where(.collapse-content), &:not(.collapse-close) > :where(input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-content) { + padding-bottom: 1rem; + } + &:is(details) { + width: 100%; + &::details-content { + --overflow-delay: 0s; + overflow: clip; + height: 0; + } + &:where([open])::details-content { + overflow: revert-layer; + height: auto; + } + @media (prefers-reduced-motion: no-preference) { + &::details-content { + transition: overflow 0.2s allow-discrete var(--overflow-delay), content-visibility 0.2s allow-discrete, visibility 0.2s allow-discrete, min-height 0.2s ease-out allow-discrete, padding 0.1s ease-out 20ms, background-color 0.2s ease-out, height 0.2s; + interpolate-size: allow-keywords; + } + &:where([open])::details-content { + --overflow-delay: 0.2s; + } + } + > summary { + position: relative; + display: block; + outline: none; + &::-webkit-details-marker { + display: none; + } + } + } + } + @layer daisyui.l1.l2 { + &:is([open]) { + &.collapse-arrow { + > .collapse-title:after { + @media (prefers-reduced-motion: no-preference) { + transform: translateY(-50%) rotate(225deg); + } + } + } + } + &.collapse-open { + &.collapse-arrow { + > .collapse-title:after { + @media (prefers-reduced-motion: no-preference) { + transform: translateY(-50%) rotate(225deg); + } + } + } + &.collapse-plus { + > .collapse-title:after { + --tw-content: "−"; + content: var(--tw-content); + } + } + } + &[tabindex].collapse-arrow:focus:not(.collapse-close), &.collapse-arrow[tabindex]:focus-within:not(.collapse-close) { + > .collapse-title:after { + transform: translateY(-50%) rotate(225deg); + } + } + &.collapse-arrow:not(.collapse-close) { + > input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-title:after { + transform: translateY(-50%) rotate(225deg); + } + } + &[open] { + &.collapse-plus { + > .collapse-title:after { + --tw-content: "−"; + content: var(--tw-content); + } + } + } + &[tabindex].collapse-plus:focus:not(.collapse-close) { + > .collapse-title:after { + --tw-content: "−"; + content: var(--tw-content); + } + } + &.collapse-plus:not(.collapse-close) { + > input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-title:after { + --tw-content: "−"; + content: var(--tw-content); + } + } + } + } + .collapse-content { + @layer daisyui.l1.l2.l3 { + --overflow-delay: 0s; + content-visibility: hidden; + overflow: clip; + grid-column-start: 1; + grid-row-start: 2; + min-height: 0; + padding-left: 1rem; + padding-right: 1rem; + cursor: unset; + @supports not (content-visibility: hidden) { + visibility: hidden; + } + @media (prefers-reduced-motion: no-preference) { + transition: overflow 0.2s allow-discrete var(--overflow-delay), content-visibility 0.2s allow-discrete, visibility 0.2s allow-discrete, min-height 0.2s ease-out allow-discrete, padding 0.1s ease-out 20ms, background-color 0.2s ease-out; + } + details > & { + content-visibility: visible; + } + } + } + .filter { + @layer daisyui.l1.l2.l3 { + display: flex; + flex-wrap: wrap; + [type="radio"] { + width: auto; + } + input { + overflow: hidden; + opacity: 100%; + scale: 1; + transition: visibility 0.1s allow-discrete, margin 0.1s, opacity 0.3s, padding 0.3s, border-width 0.1s; + &.filter-reset { + aspect-ratio: 1 / 1; + &::after { + --tw-content: "×"; + content: var(--tw-content); + } + } + } + > input:not(:last-child), > :not(:last-child) input { + margin-inline-end: 0.25rem; + } + } + @layer daisyui.l1 { + &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible) { + visibility: hidden; + } + &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible), &:not(:has(:focus-visible)):has(:checked:not(.filter-reset, [type="checkbox"])) :is(input, button):not(:checked, .filter-reset, [type="reset"]) { + margin-inline: 0; + width: 0; + padding-inline: 0; + opacity: 0%; + scale: 0; + border-width: 0; + } + } + } + .collapse-open { + @layer daisyui.l1.l2 { + grid-template-rows: max-content 1fr; + > .collapse-content { + --overflow-delay: 0.2s; + overflow: revert-layer; + content-visibility: visible; + min-height: fit-content; + padding-bottom: 1rem; + @supports not (content-visibility: visible) { + visibility: visible; + } + } + } + } + .collapse { + visibility: collapse; + } + .visible { + visibility: visible; + } + .list { + @layer daisyui.l1.l2.l3 { + display: flex; + flex-direction: column; + font-size: 0.875rem; + .list-row { + --list-grid-cols: minmax(0, auto) 1fr; + position: relative; + display: grid; + grid-auto-flow: column; + gap: calc(0.25rem * 4); + border-radius: var(--radius-box); + padding: calc(0.25rem * 4); + word-break: break-word; + grid-template-columns: var(--list-grid-cols); + } + & > :not(:last-child) { + &.list-row, .list-row { + &:after { + content: ""; + border-bottom: var(--border) solid; + inset-inline: var(--radius-box); + position: absolute; + bottom: 0; + border-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + border-color: color-mix(in oklab, var(--color-base-content) 5%, transparent); + } + } + } + } + } + @layer daisyui.l1.l2 { + .list-row { + &:has(.list-col-grow:nth-child(1)) { + --list-grid-cols: 1fr; + } + &:has(.list-col-grow:nth-child(2)) { + --list-grid-cols: minmax(0, auto) 1fr; + } + &:has(.list-col-grow:nth-child(3)) { + --list-grid-cols: minmax(0, auto) minmax(0, auto) 1fr; + } + &:has(.list-col-grow:nth-child(4)) { + --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr; + } + &:has(.list-col-grow:nth-child(5)) { + --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr; + } + &:has(.list-col-grow:nth-child(6)) { + --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) + minmax(0, auto) 1fr; + } + > * { + grid-row-start: 1; + } + } + } + } + .toast { + @layer daisyui.l1.l2.l3 { + position: fixed; + inset-inline-start: auto; + inset-inline-end: calc(0.25rem * 4); + top: auto; + bottom: calc(0.25rem * 4); + display: flex; + flex-direction: column; + gap: calc(0.25rem * 2); + background-color: transparent; + translate: var(--toast-x, 0) var(--toast-y, 0); + width: max-content; + max-width: calc(100vw - 2rem); + & > * { + @media (prefers-reduced-motion: no-preference) { + animation: toast 0.25s ease-out; + } + } + } + } + .toggle { + @layer daisyui.l1.l2.l3 { + border: var(--border) solid currentColor; + color: var(--input-color); + position: relative; + display: inline-grid; + flex-shrink: 0; + cursor: pointer; + appearance: none; + place-content: center; + vertical-align: middle; + webkit-user-select: none; + user-select: none; + grid-template-columns: 0fr 1fr 1fr; + --radius-selector-max: calc( + var(--radius-selector) + var(--radius-selector) + var(--radius-selector) + ); + border-radius: calc( var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)) ); + padding: var(--toggle-p); + box-shadow: 0 1px currentColor inset; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset; + } + transition: color 0.3s, grid-template-columns 0.2s; + --input-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --input-color: color-mix(in oklab, var(--color-base-content) 50%, #0000); + } + --toggle-p: calc(var(--size) * 0.125); + --size: calc(var(--size-selector, 0.25rem) * 6); + width: calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2); + height: var(--size); + > * { + z-index: 1; + grid-column: span 1 / span 1; + grid-column-start: 2; + grid-row-start: 1; + height: 100%; + cursor: pointer; + appearance: none; + background-color: transparent; + padding: calc(0.25rem * 0.5); + transition: opacity 0.2s, rotate 0.4s; + border: none; + &:focus { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &:nth-child(2) { + color: var(--color-base-100); + rotate: 0deg; + } + &:nth-child(3) { + color: var(--color-base-100); + opacity: 0%; + rotate: -15deg; + } + } + &:has(:checked) { + > :nth-child(2) { + opacity: 0%; + rotate: 15deg; + } + > :nth-child(3) { + opacity: 100%; + rotate: 0deg; + } + } + &:before { + position: relative; + inset-inline-start: calc(0.25rem * 0); + grid-column-start: 2; + grid-row-start: 1; + aspect-ratio: 1 / 1; + height: 100%; + width: 100%; + border-radius: var(--radius-selector); + background-color: currentcolor; + translate: 0; + --tw-content: ""; + content: var(--tw-content); + transition: background-color 0.1s, translate 0.2s, inset-inline-start 0.2s; + box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px currentColor; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000); + } + background-size: auto, calc(var(--noise) * 100%); + background-image: none, var(--fx-noise); + } + @media (forced-colors: active) { + &:before { + outline-style: var(--tw-outline-style); + outline-width: 1px; + outline-offset: calc(1px * -1); + } + } + @media print { + &:before { + outline: 0.25rem solid; + outline-offset: -1rem; + } + } + &:focus-visible, &:has(:focus-visible) { + outline: 2px solid currentColor; + outline-offset: 2px; + } + &:checked, &[aria-checked="true"], &:has( > input:checked) { + grid-template-columns: 1fr 1fr 0fr; + background-color: var(--color-base-100); + --input-color: var(--color-base-content); + &:before { + background-color: currentcolor; + } + @starting-style { + &:before { + opacity: 0; + } + } + } + &:indeterminate { + grid-template-columns: 0.5fr 1fr 0.5fr; + } + &:disabled { + cursor: not-allowed; + opacity: 30%; + &:before { + background-color: transparent; + border: var(--border) solid currentColor; + } + } + } + } + .input { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-flex; + flex-shrink: 1; + appearance: none; + align-items: center; + gap: calc(0.25rem * 2); + background-color: var(--color-base-100); + padding-inline: calc(0.25rem * 3); + vertical-align: middle; + white-space: nowrap; + --size: calc(var(--size-field, 0.25rem) * var(--in-size-mul)); + --input-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + cursor: text; + width: clamp(3rem, 20rem, 100%); + height: var(--size); + font-size: max(var(--font-size, 0rem), var(--font-size-min)); + touch-action: manipulation; + border-start-start-radius: var(--join-ss, var(--radius-field)); + border-start-end-radius: var(--join-se, var(--radius-field)); + border-end-start-radius: var(--join-es, var(--radius-field)); + border-end-end-radius: var(--join-ee, var(--radius-field)); + border: var(--border) solid var(--input-color, #0000); + box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + } + input { + height: 100%; + width: 100%; + appearance: none; + background-color: transparent; + border: none; + &::placeholder { + color: var(--color-base-content); + opacity: 50%; + } + &:focus, &:focus-within { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &::-webkit-calendar-picker-indicator { + inset-inline-end: -0.15em; + } + } + &::-webkit-inner-spin-button { + margin-inline-end: -10px; + } + &::-webkit-calendar-picker-indicator { + inset-inline-end: 0.75em; + } + input&, input { + position: relative; + display: inline-flex; + &[type="url"], &[type="email"] { + text-align: left; + } + &::-webkit-datetime-edit, &::-webkit-date-and-time-value { + display: grid; + min-height: 100%; + align-items: center; + text-align: inherit; + } + &::-webkit-inner-spin-button { + margin-block: calc(0.25rem * var(--spin-my)); + } + &::-webkit-calendar-picker-indicator { + position: absolute; + width: 1em; + height: 1em; + cursor: pointer; + } + &::-webkit-color-swatch-wrapper { + padding-block: 0.25rem; + } + } + &:focus, &:focus-within { + --input-color: var(--color-base-content); + box-shadow: 0 1px var(--input-color); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); + } + outline: 2px solid var(--input-color); + outline-offset: 2px; + isolation: isolate; + } + @media (pointer: coarse) { + @supports (-webkit-touch-callout: none) { + &:focus, &:focus-within { + --font-size: 1rem; + } + } + } + &:has( > input[disabled]), &:is(:disabled, [disabled]), fieldset:disabled & { + cursor: not-allowed; + border-color: var(--color-base-200); + background-color: var(--color-base-200); + &:is(input), :is(input) { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 40%, transparent); + } + } + box-shadow: none; + &::placeholder, ::placeholder { + color: var(--color-base-content); + opacity: 20%; + } + } + &:has( > input[disabled]) > input[disabled] { + cursor: not-allowed; + } + } + @layer daisyui.l1.l2 { + --in-size-mul: 10; + --font-size-min: 0.875rem; + --spin-my: -3; + .floating-label:has(&) { + --top-mul: 5; + --font-size: 0.875rem; + } + } + } + .table { + @layer daisyui.l1.l2.l3 { + font-size: 0.875rem; + position: relative; + width: 100%; + border-collapse: separate; + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); + border-radius: var(--radius-box); + text-align: left; + &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) { + text-align: right; + } + tr.row-hover { + &, &:nth-child(even) { + &:hover { + @media (hover: hover) { + background-color: var(--color-base-200); + } + } + } + } + :where(th, td) { + padding-inline: calc(0.25rem * 4); + padding-block: calc(0.25rem * 3); + vertical-align: middle; + } + :where(thead, tfoot) { + white-space: nowrap; + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 60%, transparent); + } + font-size: 0.875rem; + font-weight: 600; + } + :where(tfoot tr:first-child :is(td, th)) { + border-top: var(--border) solid var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + border-top: var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000); + } + } + :where(.table-pin-rows thead tr) { + position: sticky; + top: 0; + z-index: 1; + background-color: var(--color-base-100); + } + :where(.table-pin-rows tfoot tr) { + position: sticky; + bottom: 0; + z-index: 1; + background-color: var(--color-base-100); + } + :where(.table-pin-cols tr th) { + position: sticky; + right: 0; + left: 0; + background-color: var(--color-base-100); + } + :where(thead tr :is(td, th), tbody tr:not(:last-child) :is(td, th)) { + border-bottom: var(--border) solid var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + border-bottom: var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000); + } + } + } + } + .aura { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-block; + --aura-padding: 0.125rem; + padding: var(--aura-padding); + border-radius: calc(var(--aura-padding) + var(--aura-radius, var(--radius-box))); + animation: aura var(--tw-duration, 6s) linear infinite; + background-image: conic-gradient(from var(--aura-angle), transparent 225deg, currentColor); + &:has( > .card, > .alert) { + --aura-radius: var(--radius-box); + } + &:has( > .btn, > .input, > .select) { + --aura-radius: var(--radius-field); + } + &:has( > .checkbox, > .toggle, > .badge) { + --aura-radius: var(--radius-selector); + } + &:before, &:after { + animation: inherit; + background-color: inherit; + background-image: inherit; + border-radius: inherit; + position: absolute; + top: calc(1 / 2 * 100%); + left: calc(1 / 2 * 100%); + z-index: 0; + display: block; + opacity: 70%; + filter: blur(0.25rem); + translate: -50% -50%; + width: 100%; + height: 100%; + content: ""; + } + &:after { + opacity: 30%; + filter: blur(1rem); + } + & > * { + position: relative; + z-index: 1; + } + } + } + .select { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-flex; + flex-shrink: 1; + appearance: none; + align-items: center; + gap: calc(0.25rem * 1.5); + background-color: var(--color-base-100); + padding-inline-start: calc(0.25rem * 3); + padding-inline-end: calc(0.25rem * 7); + vertical-align: middle; + --size: calc(var(--size-field, 0.25rem) * var(--sl-size-mul)); + --input-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + width: clamp(3rem, 20rem, 100%); + height: var(--size); + font-size: max(var(--font-size, 0rem), var(--font-size-min)); + touch-action: manipulation; + border-start-start-radius: var(--join-ss, var(--radius-field)); + border-start-end-radius: var(--join-se, var(--radius-field)); + border-end-start-radius: var(--join-es, var(--radius-field)); + border-end-end-radius: var(--join-ee, var(--radius-field)); + background-image: linear-gradient(45deg, #0000 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, #0000 50%); + background-position: calc(100% - 20px) calc(1px + 50%), calc(100% - 16.1px) calc(1px + 50%); + background-size: 4px 4px, 4px 4px; + background-repeat: no-repeat; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + border: var(--border) solid var(--input-color, #0000); + box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + } + [dir="rtl"] & { + background-position: calc(0% + 12px) calc(1px + 50%), calc(0% + 16px) calc(1px + 50%); + &::picker(select), select::picker(select) { + translate: 0.5rem 0; + } + } + &[multiple] { + height: auto; + overflow: auto; + padding-block: calc(0.25rem * 3); + padding-inline-end: calc(0.25rem * 3); + background-image: none; + } + select { + margin-inline-start: calc(0.25rem * -3); + margin-inline-end: calc(0.25rem * -7); + width: calc(100% + 2.75rem); + appearance: none; + padding-inline-start: calc(0.25rem * 3); + padding-inline-end: calc(0.25rem * 7); + height: calc(100% - calc(var(--border) * 2)); + align-items: center; + background: inherit; + border-radius: inherit; + border-style: none; + &::placeholder { + color: var(--color-base-content); + opacity: 50%; + } + &:focus, &:focus-within { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &:not(:last-child) { + margin-inline-end: calc(0.25rem * -5.5); + background-image: none; + } + } + &:focus, &:focus-within, &:open { + --input-color: var(--color-base-content); + box-shadow: 0 1px var(--input-color); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); + } + outline: 2px solid var(--input-color); + outline-offset: 2px; + isolation: isolate; + background-image: linear-gradient(135deg, #0000 50%, currentColor 50%), linear-gradient(45deg, currentColor 50%, #0000 50%); + } + &:has( > select[disabled]), &:is(:disabled, [disabled]), fieldset:disabled & { + cursor: not-allowed; + border-color: var(--color-base-200); + background-color: var(--color-base-200); + &:is(select), :is(select) { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 40%, transparent); + } + } + box-shadow: none; + &::placeholder, ::placeholder { + color: var(--color-base-content); + opacity: 20%; + } + } + &:has( > select[disabled]) > select[disabled] { + cursor: not-allowed; + } + &, & select { + @supports (appearance: base-select) { + &, &::picker(select) { + appearance: base-select; + } + } + &::picker(select) { + color: inherit; + max-height: min(24rem, 70dvh); + margin-inline: 0.5rem; + translate: -0.5rem 0; + border: var(--border) solid var(--color-base-200); + margin-block: calc(0.25rem * 2); + border-radius: var(--radius-box); + padding: calc(0.25rem * 2); + background-color: inherit; + box-shadow: 0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/0.2); + box-shadow: 0 20px 25px -5px rgb(0 0 0 / calc(var(--depth) * 0.1)), 0 8px 10px -6px rgb(0 0 0 / calc(var(--depth) * 0.1)); + } + &::picker-icon { + display: none; + } + selectedcontent { + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + optgroup { + padding-top: 0.5em; + option { + &:nth-child(1) { + margin-top: 0.5em; + } + } + } + option { + border-radius: var(--radius-field); + padding-block: calc(0.25rem * 1.5); + padding-inline: calc(0.25rem * var(--option-px)); + transition-property: color, background-color; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + white-space: normal; + &:not(:disabled) { + &:hover, &:focus-visible { + cursor: pointer; + background-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); + } + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &:active { + background-color: var(--color-neutral); + color: var(--color-neutral-content); + box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--color-neutral); + } + } + } + } + } + @layer daisyui.l1.l2 { + --sl-size-mul: 10; + --font-size-min: 0.875rem; + --option-px: 3; + .floating-label:has(&) { + --top-mul: 5; + --font-size: 0.875rem; + } + } + } + .collapse-title { + @layer daisyui.l1.l2.l3 { + grid-column-start: 1; + grid-row-start: 1; + position: relative; + width: 100%; + padding: 1rem; + padding-inline-end: 3rem; + min-height: 1lh; + transition: background-color 0.2s ease-out; + } + } + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; + border-width: 0; + } + .avatar { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-flex; + vertical-align: middle; + & > div { + display: block; + aspect-ratio: 1 / 1; + overflow: hidden; + } + img { + height: 100%; + width: 100%; + object-fit: cover; + } + } + } + .checkbox { + @layer daisyui.l1.l2.l3 { + border: var(--border) solid var(--input-color, var(--color-base-content)); + @supports (color: color-mix(in lab, red, red)) { + border: var(--border) solid var(--input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000)); + } + position: relative; + display: inline-block; + flex-shrink: 0; + cursor: pointer; + appearance: none; + border-radius: var(--radius-selector); + padding: 0.25rem; + vertical-align: middle; + color: var(--color-base-content); + box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 0 #0000 inset, 0 0 #0000; + transition: background-color 0.2s, box-shadow 0.2s; + --size: calc(var(--size-selector, 0.25rem) * 6); + width: var(--size); + height: var(--size); + background-size: auto, calc(var(--noise) * 100%); + background-image: none, var(--fx-noise); + &:before { + --tw-content: ""; + content: var(--tw-content); + display: block; + width: 100%; + height: 100%; + rotate: 45deg; + background-color: currentcolor; + opacity: 0%; + transition: clip-path 0.3s, opacity 0.1s, rotate 0.3s, translate 0.3s; + transition-delay: 0.1s; + clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 70% 80%, 70% 100%); + box-shadow: 0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + font-size: 1rem; + line-height: 0.75; + } + &:focus-visible { + outline: 2px solid var(--input-color, currentColor); + outline-offset: 2px; + } + &:checked, &[aria-checked="true"] { + background-color: var(--input-color, #0000); + box-shadow: 0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); + &:before { + clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 0%, 70% 0%, 70% 100%); + opacity: 100%; + } + @media (forced-colors: active) { + &:before { + rotate: 0deg; + background-color: transparent; + --tw-content: "✔︎"; + clip-path: none; + } + } + @media print { + &:before { + rotate: 0deg; + background-color: transparent; + --tw-content: "✔︎"; + clip-path: none; + } + } + } + &:indeterminate { + background-color: var(--input-color, var(--color-base-content) ); + @supports (color: color-mix(in lab, red, red)) { + background-color: var(--input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000) ); + } + &:before { + rotate: 0deg; + opacity: 100%; + translate: 0 -35%; + clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 80% 80%, 80% 100%); + } + } + } + &:disabled { + @layer daisyui.l1.l2 { + cursor: not-allowed; + opacity: 20%; + } + } + } + .radio { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-block; + flex-shrink: 0; + cursor: pointer; + appearance: none; + border-radius: calc(infinity * 1px); + padding: 0.25rem; + vertical-align: middle; + border: var(--border) solid var(--input-color, currentColor); + @supports (color: color-mix(in lab, red, red)) { + border: var(--border) solid var(--input-color, color-mix(in srgb, currentColor 20%, #0000)); + } + box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset; + --size: calc(var(--size-selector, 0.25rem) * 6); + width: var(--size); + height: var(--size); + color: var(--input-color, currentColor); + &:before { + display: block; + width: 100%; + height: 100%; + border-radius: calc(infinity * 1px); + --tw-content: ""; + content: var(--tw-content); + background-size: auto, calc(var(--noise) * 100%); + background-image: none, var(--fx-noise); + } + &:focus-visible { + outline: 2px solid currentColor; + } + &:checked, &[aria-checked="true"] { + border-color: currentcolor; + background-color: var(--color-base-100); + @media (prefers-reduced-motion: no-preference) { + animation: radio 0.2s ease-out; + } + &:before { + background-color: currentcolor; + box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); + } + @media (forced-colors: active) { + &:before { + outline-style: var(--tw-outline-style); + outline-width: 1px; + outline-offset: calc(1px * -1); + } + } + @media print { + &:before { + outline: 0.25rem solid; + outline-offset: -1rem; + } + } + } + } + &:disabled { + @layer daisyui.l1.l2 { + cursor: not-allowed; + opacity: 20%; + } + } + } + .rating { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-flex; + vertical-align: middle; + --size: var(--size-selector, 0.25rem) * 6; + input { + cursor: pointer; + appearance: none; + } + * { + border-radius: 0; + background-color: var(--color-base-content); + opacity: 20%; + width: calc(var(--size) * 1); + height: calc(var(--size)); + @media (prefers-reduced-motion: no-preference) { + animation: rating 0.25s ease-out; + } + } + .rating-hidden { + width: calc(0.25rem * 2); + background-color: transparent; + } + :checked, [aria-checked="true"], [aria-current="true"], :has( ~ :checked, ~ [aria-checked="true"], ~ [aria-current="true"]) { + opacity: 100%; + } + :focus-visible { + scale: 1.1; + @media (prefers-reduced-motion: no-preference) { + transition: scale 0.2s ease-out; + } + } + :active:focus { + animation: none; + scale: 1.1; + } + } + @layer daisyui.l1.l2 { + --size: var(--size-selector, 0.25rem) * 6; + } + } + .navbar { + @layer daisyui.l1.l2.l3 { + display: flex; + width: 100%; + align-items: center; + padding: 0.5rem; + min-height: 4rem; + } + :where(&) { + @layer daisyui.l1.l2 { + position: relative; + } + } + } + .card { + @layer daisyui.l1.l2.l3 { + position: relative; + display: flex; + flex-direction: column; + border-radius: var(--radius-box); + transition: outline 0.2s ease-in-out; + outline: 2px solid #0000; + outline-offset: 2px; + &:focus-visible, &[aria-checked="true"], &:has( > :checked, > :is([type="checkbox"], [type="radio"]):focus-visible) { + outline-color: currentColor; + } + &:has( > :checked:focus-visible), &[aria-checked="true"]:focus-visible, &[aria-checked="true"]:has( > :is([type="checkbox"], [type="radio"]):focus-visible) { + outline-width: 4px; + } + &:has( > :is([type="checkbox"], [type="radio"])) { + cursor: pointer; + user-select: none; + } + > :is([type="checkbox"], [type="radio"]) { + appearance: none; + } + } + @layer daisyui.l1.l2.l3.l4 { + figure:first-child { + overflow: hidden; + border-start-start-radius: inherit; + border-start-end-radius: inherit; + border-end-start-radius: unset; + border-end-end-radius: unset; + } + figure:last-child { + overflow: hidden; + border-start-start-radius: unset; + border-start-end-radius: unset; + border-end-start-radius: inherit; + border-end-end-radius: inherit; + } + figure { + display: flex; + align-items: center; + justify-content: center; + } + } + } + .stats { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-grid; + grid-auto-flow: column; + overflow-x: auto; + border-radius: var(--radius-box); + } + } + .progress { + @layer daisyui.l1.l2.l3 { + position: relative; + height: calc(0.25rem * 2); + width: 100%; + appearance: none; + overflow: hidden; + border-radius: var(--radius-box); + background-color: currentcolor; + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, currentcolor 20%, transparent); + } + color: var(--color-base-content); + &:indeterminate { + background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); + background-size: 200%; + background-position-x: 15%; + @media (prefers-reduced-motion: no-preference) { + animation: progress 5s ease-in-out infinite; + } + @supports (-moz-appearance: none) { + &::-moz-progress-bar { + background-color: transparent; + @media (prefers-reduced-motion: no-preference) { + animation: progress 5s ease-in-out infinite; + background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); + background-size: 200%; + background-position-x: 15%; + } + } + } + } + @supports (-moz-appearance: none) { + &::-moz-progress-bar { + border-radius: var(--radius-box); + background-color: currentcolor; + @media (prefers-reduced-motion: no-preference) { + transition: inline-size 0.3s ease; + } + } + } + @supports (-webkit-appearance: none) { + &::-webkit-progress-bar { + border-radius: var(--radius-box); + background-color: transparent; + } + &::-webkit-progress-value { + border-radius: var(--radius-box); + background-color: currentColor; + @media (prefers-reduced-motion: no-preference) { + transition: inline-size 0.3s ease; + } + } + } + } + } + .fixed { + position: fixed; + } + .static { + position: static; + } + .dropdown-right { + @layer daisyui.l1.l2 { + --anchor-h: right; + --anchor-v: span-bottom; + .dropdown-content { + inset-inline-start: 100%; + top: 0; + bottom: auto; + transform-origin: 0; + } + } + } + .dropdown-left { + @layer daisyui.l1.l2 { + --anchor-h: left; + --anchor-v: span-bottom; + .dropdown-content { + inset-inline-end: 100%; + top: 0; + bottom: auto; + transform-origin: 100%; + } + } + } + .dropdown-end { + @layer daisyui.l1.l2 { + --anchor-h: span-left; + :where(.dropdown-content) { + inset-inline-end: calc(0.25rem * 0); + translate: 0 0; + [dir="rtl"] & { + translate: 0 0; + } + } + &.dropdown-left { + --anchor-h: left; + --anchor-v: span-top; + .dropdown-content { + top: auto; + bottom: 0; + } + } + &.dropdown-right { + --anchor-h: right; + --anchor-v: span-top; + .dropdown-content { + top: auto; + bottom: 0; + } + } + } + } + .file-input { + @layer daisyui.l1.l2.l3 { + cursor: pointer; + cursor: pointer; + border: var(--border) solid #0000; + display: inline-flex; + appearance: none; + align-items: center; + background-color: var(--color-base-100); + vertical-align: middle; + webkit-user-select: none; + user-select: none; + width: clamp(3rem, 20rem, 100%); + height: var(--size); + padding-inline-end: 0.75rem; + font-size: 0.875rem; + line-height: 2; + border-start-start-radius: var(--join-ss, var(--radius-field)); + border-start-end-radius: var(--join-se, var(--radius-field)); + border-end-start-radius: var(--join-es, var(--radius-field)); + border-end-end-radius: var(--join-ee, var(--radius-field)); + border-color: var(--input-color); + box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + } + --size: calc(var(--size-field, 0.25rem) * 10); + --input-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + &::file-selector-button { + margin-inline-end: calc(0.25rem * 4); + cursor: pointer; + padding-inline: calc(0.25rem * 4); + webkit-user-select: none; + user-select: none; + height: calc(100% + var(--border) * 2); + margin-block: calc(var(--border) * -1); + margin-inline-start: calc(var(--border) * -1); + font-size: 0.875rem; + color: var(--btn-fg); + border-width: var(--border); + border-style: solid; + border-color: var(--btn-border); + border-start-start-radius: calc(var(--join-ss, var(--radius-field) - var(--border))); + border-end-start-radius: calc(var(--join-es, var(--radius-field) - var(--border))); + font-weight: 600; + background-color: var(--btn-bg); + background-size: calc(var(--noise) * 100%); + background-image: var(--fx-noise); + text-shadow: 0 0.5px oklch(1 0 0 / calc(var(--depth) * 0.15)); + box-shadow: 0 0.5px 0 0.5px white inset, var(--btn-shadow); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 0.5px 0 0.5px color-mix( in oklab, color-mix(in oklab, white 30%, var(--btn-bg)) calc(var(--depth) * 20%), #0000 ) inset, var(--btn-shadow); + } + --size: calc(var(--size-field, 0.25rem) * 10); + --btn-bg: var(--btn-color, var(--color-base-200)); + --btn-fg: var(--color-base-content); + --btn-border: var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-border: color-mix(in oklab, var(--btn-bg), #000 5%); + } + --btn-shadow: 0 3px 2px -2px var(--btn-bg), + 0 4px 3px -2px var(--btn-bg); + @supports (color: color-mix(in lab, red, red)) { + --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000), + 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000); + } + } + &:focus { + --input-color: var(--color-base-content); + box-shadow: 0 1px var(--input-color); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) 10%, #0000); + } + outline: 2px solid var(--input-color); + outline-offset: 2px; + isolation: isolate; + } + &:has( > input[disabled]), &:is(:disabled, [disabled]) { + cursor: not-allowed; + border-color: var(--color-base-200); + background-color: var(--color-base-200); + &::placeholder { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 20%, transparent); + } + } + box-shadow: none; + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklch, var(--color-base-content) 20%, #0000); + } + &::file-selector-button { + cursor: not-allowed; + border-color: var(--color-base-200); + background-color: var(--color-base-200); + --btn-border: #0000; + background-image: none; + --btn-fg: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --btn-fg: color-mix(in oklch, var(--color-base-content) 20%, #0000); + } + } + } + } + } + .textarea { + @layer daisyui.l1.l2.l3 { + min-height: calc(0.25rem * 20); + flex-shrink: 1; + appearance: none; + border-radius: var(--radius-field); + background-color: var(--color-base-100); + padding-inline: calc(0.25rem * 3); + padding-block: calc(0.25rem * 2); + vertical-align: middle; + --input-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + width: clamp(3rem, 20rem, 100%); + font-size: max(var(--font-size, 0rem), var(--font-size-min)); + touch-action: manipulation; + border: var(--border) solid var(--input-color, #0000); + box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; + } + textarea { + appearance: none; + background-color: transparent; + border: none; + &::placeholder { + color: var(--color-base-content); + opacity: 50%; + } + &:focus, &:focus-within { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + } + &:focus, &:focus-within { + --input-color: var(--color-base-content); + box-shadow: 0 1px var(--input-color); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); + } + outline: 2px solid var(--input-color); + outline-offset: 2px; + isolation: isolate; + } + @media (pointer: coarse) { + @supports (-webkit-touch-callout: none) { + &:focus, &:focus-within { + --font-size: 1rem; + } + } + } + &:has( > textarea[disabled]), &:is(:disabled, [disabled]) { + cursor: not-allowed; + border-color: var(--color-base-200); + background-color: var(--color-base-200); + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 40%, transparent); + } + box-shadow: none; + &::placeholder, ::placeholder { + color: var(--color-base-content); + opacity: 20%; + } + } + &:has( > textarea[disabled]) > textarea[disabled] { + cursor: not-allowed; + } + } + @layer daisyui.l1.l2 { + --font-size-min: 0.875rem; + .floating-label:has(&) { + --top-mul: 5; + --font-size: 0.875rem; + } + } + } + .stack { + @layer daisyui.l1.l2.l3 { + display: inline-grid; + grid-template-columns: 3px 4px 1fr 4px 3px; + grid-template-rows: 3px 4px 1fr 4px 3px; + & > * { + height: 100%; + width: 100%; + &:nth-child(n + 2) { + width: 100%; + opacity: 70%; + } + &:nth-child(2) { + z-index: 2; + opacity: 90%; + } + &:nth-child(1) { + z-index: 3; + width: 100%; + } + } + } + @layer daisyui.l1.l2 { + &, &.stack-bottom { + > * { + grid-column: 3 / 4; + grid-row: 3 / 6; + &:nth-child(2) { + grid-column: 2 / 5; + grid-row: 2 / 5; + } + &:nth-child(1) { + grid-column: 1 / 6; + grid-row: 1 / 4; + } + } + } + &.stack-top { + > * { + grid-column: 3 / 4; + grid-row: 1 / 4; + &:nth-child(2) { + grid-column: 2 / 5; + grid-row: 2 / 5; + } + &:nth-child(1) { + grid-column: 1 / 6; + grid-row: 3 / 6; + } + } + } + &.stack-start { + > * { + grid-column: 1 / 4; + grid-row: 3 / 4; + &:nth-child(2) { + grid-column: 2 / 5; + grid-row: 2 / 5; + } + &:nth-child(1) { + grid-column: 3 / 6; + grid-row: 1 / 6; + } + } + } + &.stack-end { + > * { + grid-column: 3 / 6; + grid-row: 3 / 4; + &:nth-child(2) { + grid-column: 2 / 5; + grid-row: 2 / 5; + } + &:nth-child(1) { + grid-column: 1 / 4; + grid-row: 1 / 6; + } + } + } + } + } + .z-10 { + z-index: 10; + } + .tab-content { + @layer daisyui.l1.l2.l3 { + order: var(--tabcontent-order); + display: none; + border-color: transparent; + --tabcontent-radius-ss: var(--radius-box); + --tabcontent-radius-se: var(--radius-box); + --tabcontent-radius-es: var(--radius-box); + --tabcontent-radius-ee: var(--radius-box); + --tabcontent-order: 1; + width: 100%; + height: calc(100% - var(--tab-height) + var(--border)); + margin: var(--tabcontent-margin); + border-width: var(--border); + border-start-start-radius: var(--tabcontent-radius-ss); + border-start-end-radius: var(--tabcontent-radius-se); + border-end-start-radius: var(--tabcontent-radius-es); + border-end-end-radius: var(--tabcontent-radius-ee); + } + } + .hero { + @layer daisyui.l1.l2.l3 { + display: grid; + width: 100%; + place-items: center; + background-size: cover; + background-position: center; + & > * { + grid-column-start: 1; + grid-row-start: 1; + } + } + } + .stat-value { + @layer daisyui.l1.l2.l3 { + grid-column-start: 1; + white-space: nowrap; + font-size: 2rem; + font-weight: 800; + } + } + .stat-title { + @layer daisyui.l1.l2.l3 { + grid-column-start: 1; + white-space: nowrap; + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 60%, transparent); + } + font-size: 0.75rem; + } + } + .container { + width: 100%; + @media (width >= 40rem) { + max-width: 40rem; + } + @media (width >= 48rem) { + max-width: 48rem; + } + @media (width >= 64rem) { + max-width: 64rem; + } + @media (width >= 80rem) { + max-width: 80rem; + } + @media (width >= 96rem) { + max-width: 96rem; + } + } + .divider { + @layer daisyui.l1.l2.l3 { + display: flex; + height: calc(0.25rem * 4); + flex-direction: row; + align-items: center; + align-self: stretch; + white-space: nowrap; + margin: var(--divider-m, 1rem 0); + --divider-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --divider-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); + } + &:before, &:after { + content: ""; + height: calc(0.25rem * 0.5); + width: 100%; + flex-grow: 1; + background-color: var(--divider-color); + } + @media print { + &:before, &:after { + border: 0.5px solid; + } + } + &:not(:empty) { + gap: calc(0.25rem * 4); + } + } + } + .mx-auto { + margin-inline: auto; + } + .my-2 { + margin-block: calc(var(--spacing) * 2); + } + .my-3 { + margin-block: calc(var(--spacing) * 3); + } + .my-4 { + margin-block: calc(var(--spacing) * 4); + } + .label { + @layer daisyui.l1.l2.l3 { + display: inline-flex; + align-items: center; + gap: calc(0.25rem * 1.5); + white-space: nowrap; + color: currentcolor; + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, currentcolor 60%, transparent); + } + &:has(input) { + cursor: pointer; + } + &:is(.input > *, .select > *) { + display: flex; + height: calc(100% - 0.5rem); + align-items: center; + padding-inline: calc(0.25rem * 3); + white-space: nowrap; + font-size: inherit; + &:first-child { + margin-inline-start: calc(0.25rem * -3); + margin-inline-end: calc(0.25rem * 3); + border-inline-end: var(--border) solid currentColor; + @supports (color: color-mix(in lab, red, red)) { + border-inline-end: var(--border) solid color-mix(in oklab, currentColor 10%, #0000); + } + } + &:last-child { + margin-inline-start: calc(0.25rem * 3); + margin-inline-end: calc(0.25rem * -3); + border-inline-start: var(--border) solid currentColor; + @supports (color: color-mix(in lab, red, red)) { + border-inline-start: var(--border) solid color-mix(in oklab, currentColor 10%, #0000); + } + } + } + } + } + .mt-1 { + margin-top: var(--spacing); + } + .mt-2 { + margin-top: calc(var(--spacing) * 2); + } + .mt-3 { + margin-top: calc(var(--spacing) * 3); + } + .mt-4 { + margin-top: calc(var(--spacing) * 4); + } + .mb-4 { + margin-bottom: calc(var(--spacing) * 4); + } + .mb-6 { + margin-bottom: calc(var(--spacing) * 6); + } + .ml-2 { + margin-left: calc(var(--spacing) * 2); + } + .status { + @layer daisyui.l1.l2.l3 { + display: inline-block; + aspect-ratio: 1 / 1; + width: calc(0.25rem * 2); + height: calc(0.25rem * 2); + border-radius: var(--radius-selector); + background-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-base-content) 20%, transparent); + } + background-position: center; + background-repeat: no-repeat; + vertical-align: middle; + color: color-mix(in srgb, #000 30%, transparent); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-black) 30%, transparent); + } + background-image: radial-gradient( circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * 0.5)), #0000 ); + box-shadow: 0 2px 3px -1px currentColor; + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000); + } + } + } + .badge { + @layer daisyui.l1.l2.l3 { + display: inline-flex; + align-items: center; + justify-content: center; + gap: calc(0.25rem * 2); + border-radius: var(--radius-selector); + vertical-align: middle; + color: var(--badge-fg); + border: var(--border) solid var(--badge-color, var(--color-base-200)); + font-size: 0.875rem; + width: fit-content; + background-size: auto, calc(var(--noise) * 100%); + background-image: none, var(--fx-noise); + background-color: var(--badge-bg); + --badge-bg: var(--badge-color, var(--color-base-100)); + --badge-fg: var(--color-base-content); + --size: calc(var(--size-selector, 0.25rem) * 6); + height: var(--size); + padding-inline: calc(var(--size) / 2 - var(--border)); + } + } + .tabs { + @layer daisyui.l1.l2.l3 { + display: flex; + flex-wrap: wrap; + --tabs-height: auto; + --tabs-direction: row; + --tab-height: calc(var(--size-field, 0.25rem) * 10); + height: var(--tabs-height); + flex-direction: var(--tabs-direction); + } + } + .stat { + @layer daisyui.l1.l2.l3 { + display: inline-grid; + width: 100%; + column-gap: calc(0.25rem * 4); + padding-inline: calc(0.25rem * 6); + padding-block: calc(0.25rem * 4); + grid-template-columns: repeat(1, 1fr); + &:not(:last-child) { + border-inline-end: var(--border) dashed currentColor; + @supports (color: color-mix(in lab, red, red)) { + border-inline-end: var(--border) dashed color-mix(in oklab, currentColor 10%, #0000); + } + border-block-end: none; + } + } + } + .card-body { + @layer daisyui.l1.l2.l3 { + display: flex; + flex: auto; + flex-direction: column; + gap: calc(0.25rem * 2); + padding: var(--card-p, 1.5rem); + font-size: var(--card-fs, 0.875rem); + } + @layer daisyui.l1.l2.l3.l4 { + p { + flex-grow: 1; + } + } + } + .alert { + border-width: var(--border); + border-color: var(--alert-border-color, var(--color-base-200)); + @layer daisyui.l1.l2.l3 { + border-style: solid; + --alert-border-color: var(--color-base-200); + display: grid; + align-items: center; + gap: calc(0.25rem * 4); + border-radius: var(--radius-box); + padding-inline: calc(0.25rem * 4); + padding-block: calc(0.25rem * 3); + color: var(--color-base-content); + background-color: var(--alert-color, var(--color-base-200)); + justify-content: start; + justify-items: start; + grid-auto-flow: column; + grid-template-columns: auto; + text-align: start; + font-size: 0.875rem; + line-height: 1.25rem; + background-size: auto, calc(var(--noise) * 33%); + background-image: none, var(--fx-noise); + box-shadow: 0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * 0.08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * 0.08)); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * 0.08)) inset, 0 1px color-mix( in oklab, color-mix(in oklab, #000 20%, var(--alert-color, var(--color-base-200))) calc(var(--depth) * 20%), #0000 ), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * 0.08)); + } + &:has(:nth-child(2)) { + grid-template-columns: auto minmax(auto, 1fr); + } + } + } + .card-actions { + @layer daisyui.l1.l2.l3 { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: calc(0.25rem * 2); + } + } + .avatar-placeholder { + @layer daisyui.l1.l2 { + & > div { + display: flex; + align-items: center; + justify-content: center; + } + } + } + .card-title { + @layer daisyui.l1.l2.l3 { + display: flex; + align-items: center; + gap: calc(0.25rem * 2); + font-size: var(--cardtitle-fs, 1.125rem); + font-weight: 600; + } + } + .prose { + :root & { + --tw-prose-body: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-body: color-mix(in oklab, var(--color-base-content) 80%, #0000); + } + --tw-prose-headings: var(--color-base-content); + --tw-prose-lead: var(--color-base-content); + --tw-prose-links: var(--color-base-content); + --tw-prose-bold: var(--color-base-content); + --tw-prose-counters: var(--color-base-content); + --tw-prose-bullets: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-bullets: color-mix(in oklab, var(--color-base-content) 50%, #0000); + } + --tw-prose-hr: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-hr: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + --tw-prose-quotes: var(--color-base-content); + --tw-prose-quote-borders: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-quote-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + --tw-prose-captions: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-captions: color-mix(in oklab, var(--color-base-content) 50%, #0000); + } + --tw-prose-code: var(--color-base-content); + --tw-prose-pre-code: var(--color-neutral-content); + --tw-prose-pre-bg: var(--color-neutral); + --tw-prose-th-borders: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-th-borders: color-mix(in oklab, var(--color-base-content) 50%, #0000); + } + --tw-prose-td-borders: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-td-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + --tw-prose-kbd: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-kbd: color-mix(in oklab, var(--color-base-content) 80%, #0000); + } + :where(code):not(pre > code) { + background-color: var(--color-base-200); + border-radius: var(--radius-selector); + border: var(--border) solid var(--color-base-300); + padding-inline: 0.5em; + padding-block: 0.2em; + font-weight: inherit; + &:before, &:after { + display: none; + } + } + } + } + .block { + display: block; + } + .flex { + display: flex; + } + .grid { + display: grid; + } + .hidden { + display: none; + } + .inline-block { + display: inline-block; + } + .inline-flex { + display: inline-flex; + } + .table { + display: table; + } + .h-16 { + height: calc(var(--spacing) * 16); + } + .min-h-screen { + min-height: 100vh; + } + .w-16 { + width: calc(var(--spacing) * 16); + } + .w-24 { + width: calc(var(--spacing) * 24); + } + .w-60 { + width: calc(var(--spacing) * 60); + } + .w-fit { + width: fit-content; + } + .w-full { + width: 100%; + } + .max-w-none { + max-width: none; + } + .max-w-xl { + max-width: var(--container-xl); + } + .max-w-xs { + max-width: var(--container-xs); + } + .flex-1 { + flex: 1; + } + .flex-shrink { + flex-shrink: 1; + } + .flex-grow { + flex-grow: 1; + } + .grow { + flex-grow: 1; + } + .border-collapse { + border-collapse: collapse; + } + .transform { + transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); + } + .skeleton { + @layer daisyui.l1.l2.l3 { + border-radius: var(--radius-box); + background-color: var(--color-base-300); + @media (prefers-reduced-motion: reduce) { + transition-duration: 15s; + } + will-change: background-position; + background-image: linear-gradient( 105deg, #0000 0% 40%, var(--color-base-100) 50%, #0000 60% 100% ); + background-size: 200% auto; + background-position-x: -50%; + @media (prefers-reduced-motion: no-preference) { + animation: skeleton 1.8s ease-in-out infinite; + } + } + } + .aura-glow { + @layer daisyui.l1.l2 { + animation: none; + background-image: radial-gradient(closest-corner at center, currentColor 0%, transparent 90%); + &:before { + animation: aura-glow var(--tw-duration, 6s) ease-out infinite; + } + &:after { + animation: aura-glow-after var(--tw-duration, 6s) ease-out infinite; + } + } + } + .link { + @layer daisyui.l1.l2.l3 { + cursor: pointer; + text-decoration-line: underline; + &:focus { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; + } + } + } + .cursor-pointer { + cursor: pointer; + } + .resize { + resize: both; + } + .flex-col { + flex-direction: column; + } + .flex-row { + flex-direction: row; + } + .flex-wrap { + flex-wrap: wrap; + } + .items-center { + align-items: center; + } + .justify-between { + justify-content: space-between; + } + .justify-center { + justify-content: center; + } + .justify-end { + justify-content: flex-end; + } + .justify-start { + justify-content: flex-start; + } + .gap-1 { + gap: var(--spacing); + } + .gap-2 { + gap: calc(var(--spacing) * 2); + } + .gap-3 { + gap: calc(var(--spacing) * 3); + } + .gap-4 { + gap: calc(var(--spacing) * 4); + } + .space-y-2 { + :where(& > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse))); + } + } + .divide-y { + :where(& > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(1px * var(--tw-divide-y-reverse)); + border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + } + } + .divide-base-200 { + :where(& > :not(:last-child)) { + border-color: var(--color-base-200); + } + } + .truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .overflow-x-auto { + overflow-x: auto; + } + .rounded-box { + border-radius: var(--radius-box); + } + .rounded-box { + border-radius: var(--radius-box); + } + .rounded-full { + border-radius: calc(infinity * 1px); + } + .border { + border-style: var(--tw-border-style); + border-width: 1px; + } + .border-2 { + border-style: var(--tw-border-style); + border-width: 2px; + } + .border-b { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 1px; + } + .badge-ghost { + @layer daisyui.l1.l2 { + border-color: var(--color-base-200); + background-color: var(--color-base-200); + color: var(--color-base-content); + background-image: none; + } + } + .border-base-300 { + border-color: var(--color-base-300); + } + .border-base-content { + border-color: var(--color-base-content); + } + .border-base-content\/20 { + border-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + border-color: color-mix(in oklab, var(--color-base-content) 20%, transparent); + } + } + .border-base-content\/70 { + border-color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + border-color: color-mix(in oklab, var(--color-base-content) 70%, transparent); + } + } + .bg-base-100 { + background-color: var(--color-base-100); + } + .bg-base-200 { + background-color: var(--color-base-200); + } + .bg-primary { + background-color: var(--color-primary); + } + .btn-link { + @layer daisyui { + text-decoration-line: underline; + --btn-bg: #0000; + color: var(--btn-color, var(--color-primary)); + --btn-border: #0000; + background-image: none; + --btn-inset: 0 0 0 0 oklch(0% 0 0/0); + --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); + } + } + .alert-soft { + @layer daisyui.l1 { + color: var(--alert-color, var(--color-base-content)); + background: var(--alert-color, var(--color-base-content)); + @supports (color: color-mix(in lab, red, red)) { + background: color-mix( in oklab, var(--alert-color, var(--color-base-content)) 8%, var(--color-base-100) ); + } + --alert-border-color: var(--alert-color, var(--color-base-content)); + @supports (color: color-mix(in lab, red, red)) { + --alert-border-color: color-mix( + in oklab, + var(--alert-color, var(--color-base-content)) 10%, + var(--color-base-100) + ); + } + box-shadow: none; + background-image: none; + } + } + .btn-ghost { + @layer daisyui.l1.l2.l3 { + --btn-bg: #0000; + color: var(--btn-rest-fg, var(--btn-color, var(--color-base-content, currentColor))); + --btn-border: #0000; + background-image: none; + --btn-inset: 0 0 0 0 oklch(0% 0 0/0); + --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); + } + } + .object-contain { + object-fit: contain; + } + .p-2 { + padding: calc(var(--spacing) * 2); + } + .p-4 { + padding: calc(var(--spacing) * 4); + } + .px-4 { + padding-inline: calc(var(--spacing) * 4); + } + .px-6 { + padding-inline: calc(var(--spacing) * 6); + } + .py-2 { + padding-block: calc(var(--spacing) * 2); + } + .pt-2 { + padding-top: calc(var(--spacing) * 2); + } + .pt-3 { + padding-top: calc(var(--spacing) * 3); + } + .text-center { + text-align: center; + } + .text-right { + text-align: right; + } + .font-mono { + font-family: var(--font-mono); + } + .font-roboto { + font-family: var(--font-roboto); + } + .text-2xl { + font-size: var(--text-2xl); + line-height: var(--tw-leading, var(--text-2xl--line-height)); + } + .text-base { + font-size: var(--text-base); + line-height: var(--tw-leading, var(--text-base--line-height)); + } + .text-lg { + font-size: var(--text-lg); + line-height: var(--tw-leading, var(--text-lg--line-height)); + } + .text-sm { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } + .text-xl { + font-size: var(--text-xl); + line-height: var(--tw-leading, var(--text-xl--line-height)); + } + .text-xs { + font-size: var(--text-xs); + line-height: var(--tw-leading, var(--text-xs--line-height)); + } + .badge-sm { + @layer daisyui.l1.l2 { + --size: calc(var(--size-selector, 0.25rem) * 5); + font-size: 0.75rem; + } + } + .font-bold { + --tw-font-weight: var(--font-weight-bold); + font-weight: var(--font-weight-bold); + } + .font-medium { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } + .font-semibold { + --tw-font-weight: var(--font-weight-semibold); + font-weight: var(--font-weight-semibold); + } + .tracking-wider { + --tw-tracking: var(--tracking-wider); + letter-spacing: var(--tracking-wider); + } + .alert-error { + @layer daisyui.l1.l2 { + color: var(--color-error-content); + --alert-border-color: var(--color-error); + --alert-color: var(--color-error); + } + } + .alert-info { + @layer daisyui.l1.l2 { + color: var(--color-info-content); + --alert-border-color: var(--color-info); + --alert-color: var(--color-info); + } + } + .alert-success { + @layer daisyui.l1.l2 { + color: var(--color-success-content); + --alert-border-color: var(--color-success); + --alert-color: var(--color-success); + } + } + .alert-warning { + @layer daisyui.l1.l2 { + color: var(--color-warning-content); + --alert-border-color: var(--color-warning); + --alert-color: var(--color-warning); + } + } + .text-base-content { + color: var(--color-base-content); + } + .text-base-content\/50 { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 50%, transparent); + } + } + .text-base-content\/70 { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 70%, transparent); + } + } + .text-base-content\/80 { + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 80%, transparent); + } + } + .text-error { + color: var(--color-error); + } + .text-primary-content { + color: var(--color-primary-content); + } + .text-sky-500 { + color: var(--color-sky-500); + } + .tabular-nums { + --tw-numeric-spacing: tabular-nums; + font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,); + } + .link-hover { + @layer daisyui.l1.l2 { + text-decoration-line: none; + &:hover { + @media (hover: hover) { + text-decoration-line: underline; + } + } + } + } + .prose { + & :where(a.btn:not(.btn-link)):not(:where([class~="not-prose"], [class~="not-prose"] *)) { + text-decoration-line: none; + } + } + .underline { + text-decoration-line: underline; + } + .opacity-50 { + opacity: 50%; + } + .opacity-60 { + opacity: 60%; + } + .opacity-70 { + opacity: 70%; + } + .shadow { + --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + .shadow-sm { + --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + .shadow-xl { + --tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); + box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + } + .outline { + outline-style: var(--tw-outline-style); + outline-width: 1px; + } + .filter { + filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); + } + .transition { + transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events; + transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); + transition-duration: var(--tw-duration, var(--default-transition-duration)); + } + .ease-in-out { + --tw-ease: var(--ease-in-out); + transition-timing-function: var(--ease-in-out); + } + .ease-out { + --tw-ease: var(--ease-out); + transition-timing-function: var(--ease-out); + } + .btn-error { + @layer daisyui.l1.l2 { + --btn-color: var(--color-error); + --btn-fg: var(--color-error-content); + --btn-soft-bg: initial; + } + } + .btn-primary { + @layer daisyui.l1.l2 { + --btn-color: var(--color-primary); + --btn-fg: var(--color-primary-content); + --btn-soft-bg: initial; + } + } + .btn-sm { + @layer daisyui.l1.l2 { + --fontsize: 0.75rem; + --btn-p: 0.75rem; + --size: calc(var(--size-field, 0.25rem) * 8); + } + } + .btn-success { + @layer daisyui.l1.l2 { + --btn-color: var(--color-success); + --btn-fg: var(--color-success-content); + --btn-soft-bg: initial; + } + } + .btn-warning { + @layer daisyui.l1.l2 { + --btn-color: var(--color-warning); + --btn-fg: var(--color-warning-content); + --btn-soft-bg: initial; + } + } + .btn-xs { + @layer daisyui.l1.l2 { + --fontsize: 0.6875rem; + --btn-p: 0.5rem; + --size: calc(var(--size-field, 0.25rem) * 6); + } + } + .badge-error { + @layer daisyui.l1.l2 { + --badge-color: var(--color-error); + --badge-fg: var(--color-error-content); + } + } + .badge-neutral { + @layer daisyui.l1.l2 { + --badge-color: var(--color-neutral); + --badge-fg: var(--color-neutral-content); + } + } + .badge-success { + @layer daisyui.l1.l2 { + --badge-color: var(--color-success); + --badge-fg: var(--color-success-content); + } + } + .badge-warning { + @layer daisyui.l1.l2 { + --badge-color: var(--color-warning); + --badge-fg: var(--color-warning-content); + } + } + .input-error { + @layer daisyui.l1.l2 { + &, &:focus, &:focus-within { + --input-color: var(--color-error); + } + } + } + .md\:grid-cols-2 { + @media (width >= 48rem) { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + } +} +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("../fonts/ubuntu-latin-400-normal.woff2") format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("../fonts/ubuntu-latin-ext-400-normal.woff2") format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: "Ubuntu"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url("../fonts/ubuntu-latin-400-italic.woff2") format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +@font-face { + font-family: "Ubuntu"; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url("../fonts/ubuntu-latin-ext-400-italic.woff2") format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url("../fonts/ubuntu-latin-500-normal.woff2") format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url("../fonts/ubuntu-latin-ext-500-normal.woff2") format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url("../fonts/ubuntu-latin-700-normal.woff2") format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +@font-face { + font-family: "Ubuntu"; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url("../fonts/ubuntu-latin-ext-700-normal.woff2") format("woff2"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 100 800; + font-display: swap; + src: url("../fonts/jetbrains-mono-latin-wght-normal.woff2") format("woff2-variations"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +@font-face { + font-family: "JetBrains Mono"; + font-style: normal; + font-weight: 100 800; + font-display: swap; + src: url("../fonts/jetbrains-mono-latin-ext-wght-normal.woff2") format("woff2-variations"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: "Roboto"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url("../fonts/roboto-latin-standard-normal.woff2") format("woff2-variations"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +@font-face { + font-family: "Roboto"; + font-style: normal; + font-weight: 100 900; + font-display: swap; + src: url("../fonts/roboto-latin-ext-standard-normal.woff2") format("woff2-variations"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: "Tourney"; + font-style: normal; + font-weight: 100 900; + font-stretch: 75% 125%; + font-display: swap; + src: url("../fonts/tourney-latin-standard-normal.woff2") format("woff2-variations"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +@font-face { + font-family: "Tourney"; + font-style: normal; + font-weight: 100 900; + font-stretch: 75% 125%; + font-display: swap; + src: url("../fonts/tourney-latin-ext-standard-normal.woff2") format("woff2-variations"); + unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +.logo { + background-image: var(--logo-dark); + background-position: center; + background-repeat: no-repeat; + background-size: contain; +} +@media (prefers-color-scheme: dark) { + .logo { + background-image: var(--logo-light); + } +} +[data-theme="light"] .logo { + background-image: var(--logo-dark); +} +[data-theme="dark"] .logo { + background-image: var(--logo-light); +} +@layer base { + :where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] { + color-scheme: light; + --color-base-100: oklch(100% 0 0); + --color-base-200: oklch(98% 0 0); + --color-base-300: oklch(95% 0 0); + --color-base-content: oklch(21% 0.006 285.885); + --color-primary: oklch(45% 0.24 277.023); + --color-primary-content: oklch(93% 0.034 272.788); + --color-secondary: oklch(65% 0.241 354.308); + --color-secondary-content: oklch(94% 0.028 342.258); + --color-accent: oklch(77% 0.152 181.912); + --color-accent-content: oklch(38% 0.063 188.416); + --color-neutral: oklch(14% 0.005 285.823); + --color-neutral-content: oklch(92% 0.004 286.32); + --color-info: oklch(74% 0.16 232.661); + --color-info-content: oklch(29% 0.066 243.157); + --color-success: oklch(76% 0.177 163.223); + --color-success-content: oklch(37% 0.077 168.94); + --color-warning: oklch(82% 0.189 84.429); + --color-warning-content: oklch(41% 0.112 45.904); + --color-error: oklch(71% 0.194 13.428); + --color-error-content: oklch(27% 0.105 12.094); + --radius-selector: 0.5rem; + --radius-field: 0.25rem; + --radius-box: 0.5rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 1; + --noise: 0; + } +} +@layer base { + @media (prefers-color-scheme: dark) { + :root:not([data-theme]) { + color-scheme: dark; + --color-base-100: oklch(25.33% 0.016 252.42); + --color-base-200: oklch(23.26% 0.014 253.1); + --color-base-300: oklch(21.15% 0.012 254.09); + --color-base-content: oklch(97.807% 0.029 256.847); + --color-primary: oklch(58% 0.233 277.117); + --color-primary-content: oklch(96% 0.018 272.314); + --color-secondary: oklch(65% 0.241 354.308); + --color-secondary-content: oklch(94% 0.028 342.258); + --color-accent: oklch(77% 0.152 181.912); + --color-accent-content: oklch(38% 0.063 188.416); + --color-neutral: oklch(14% 0.005 285.823); + --color-neutral-content: oklch(92% 0.004 286.32); + --color-info: oklch(74% 0.16 232.661); + --color-info-content: oklch(29% 0.066 243.157); + --color-success: oklch(76% 0.177 163.223); + --color-success-content: oklch(37% 0.077 168.94); + --color-warning: oklch(82% 0.189 84.429); + --color-warning-content: oklch(41% 0.112 45.904); + --color-error: oklch(71% 0.194 13.428); + --color-error-content: oklch(27% 0.105 12.094); + --radius-selector: 0.5rem; + --radius-field: 0.25rem; + --radius-box: 0.5rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 1; + --noise: 0; + } + } +} +@layer base { + :root:has(input.theme-controller[value=light]:checked),[data-theme=light] { + color-scheme: light; + --color-base-100: oklch(100% 0 0); + --color-base-200: oklch(98% 0 0); + --color-base-300: oklch(95% 0 0); + --color-base-content: oklch(21% 0.006 285.885); + --color-primary: oklch(45% 0.24 277.023); + --color-primary-content: oklch(93% 0.034 272.788); + --color-secondary: oklch(65% 0.241 354.308); + --color-secondary-content: oklch(94% 0.028 342.258); + --color-accent: oklch(77% 0.152 181.912); + --color-accent-content: oklch(38% 0.063 188.416); + --color-neutral: oklch(14% 0.005 285.823); + --color-neutral-content: oklch(92% 0.004 286.32); + --color-info: oklch(74% 0.16 232.661); + --color-info-content: oklch(29% 0.066 243.157); + --color-success: oklch(76% 0.177 163.223); + --color-success-content: oklch(37% 0.077 168.94); + --color-warning: oklch(82% 0.189 84.429); + --color-warning-content: oklch(41% 0.112 45.904); + --color-error: oklch(71% 0.194 13.428); + --color-error-content: oklch(27% 0.105 12.094); + --radius-selector: 0.5rem; + --radius-field: 0.25rem; + --radius-box: 0.5rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 1; + --noise: 0; + } +} +@layer base { + :root:has(input.theme-controller[value=dark]:checked),[data-theme=dark] { + color-scheme: dark; + --color-base-100: oklch(25.33% 0.016 252.42); + --color-base-200: oklch(23.26% 0.014 253.1); + --color-base-300: oklch(21.15% 0.012 254.09); + --color-base-content: oklch(97.807% 0.029 256.847); + --color-primary: oklch(58% 0.233 277.117); + --color-primary-content: oklch(96% 0.018 272.314); + --color-secondary: oklch(65% 0.241 354.308); + --color-secondary-content: oklch(94% 0.028 342.258); + --color-accent: oklch(77% 0.152 181.912); + --color-accent-content: oklch(38% 0.063 188.416); + --color-neutral: oklch(14% 0.005 285.823); + --color-neutral-content: oklch(92% 0.004 286.32); + --color-info: oklch(74% 0.16 232.661); + --color-info-content: oklch(29% 0.066 243.157); + --color-success: oklch(76% 0.177 163.223); + --color-success-content: oklch(37% 0.077 168.94); + --color-warning: oklch(82% 0.189 84.429); + --color-warning-content: oklch(41% 0.112 45.904); + --color-error: oklch(71% 0.194 13.428); + --color-error-content: oklch(27% 0.105 12.094); + --radius-selector: 0.5rem; + --radius-field: 0.25rem; + --radius-box: 0.5rem; + --size-selector: 0.25rem; + --size-field: 0.25rem; + --border: 1px; + --depth: 1; + --noise: 0; + } +} +@layer base { + @property --radialprogress { + syntax: ""; + inherits: true; + initial-value: 0%; + } + @property --aura-angle { + syntax: ""; + inherits: false; + initial-value: 0deg; + } +} +@layer base { + :root { + scrollbar-color: currentColor #0000; + @supports (color: color-mix(in lab, red, red)) { + scrollbar-color: color-mix(in oklch, currentColor 35%, #0000) #0000; + } + } +} +@layer base { + :root { + --fx-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E"); + } +} +@layer base { + :root { + --page-scroll-lock: initial; + --page-overflow: var(--page-scroll-lock) hidden; + } + :root:not(span) { + overflow: var(--page-overflow); + } +} +@layer base { + :root { + --page-has-backdrop: var(--page-scroll-lock) 1; + --page-scroll-bg: var(--page-scroll-lock) + var(--root-bg, #0000); + @supports (color: color-mix(in lab, red, red)) { + --page-scroll-bg: var(--page-scroll-lock) + color-mix(in srgb, var(--root-bg, #0000), oklch(0% 0 0) calc(var(--page-has-backdrop, 0) * 40%)); + } + background-image: var(--page-scroll-lock) linear-gradient(var(--root-bg, #0000), var(--root-bg, #0000)); + transition: var(--page-scroll-lock) background-color 0.3s ease-out; + animation: var(--page-scroll-lock) set-page-has-scroll forwards; + animation-timeline: var(--page-scroll-lock) scroll(); + --page-has-scroll: initial; + scrollbar-gutter: var(--page-has-scroll) var(--page-scroll-lock) stable; + } + @keyframes set-page-has-scroll { + 0%, to { + --page-has-scroll: ; + } + } +} +@layer base { + :root, [data-theme] { + background-color: var(--root-bg); + color: var(--color-base-content); + } + :root { + background-color: var(--page-scroll-bg, var(--root-bg)); + } + :where(:root, [data-theme]) { + --root-bg: var(--color-base-100); + } +} +@keyframes dropdown { + 0% { + opacity: 0; + } +} +@keyframes rotator { + 89.9999%, 100% { + --first-item-position: 0 0%; + } + 90%, 99.9999% { + --first-item-position: 0 calc(var(--items) * 100%); + } + 100% { + translate: 0 -100%; + } +} +@keyframes radio { + 0% { + padding: 5px; + } + 50% { + padding: 3px; + } +} +@keyframes toast { + 0% { + scale: 0.9; + opacity: 0; + } + 100% { + scale: 1; + opacity: 1; + } +} +@keyframes skeleton { + 0% { + background-position: 150%; + } + 100% { + background-position: -50%; + } +} +@keyframes progress { + 50% { + background-position-x: -115%; + } +} +@keyframes rating { + 0%, 40% { + scale: 1.1; + filter: brightness(1.05) contrast(1.05); + } +} +@keyframes aura { + to { + --aura-angle: 360deg; + transform: translateZ(1px); + } +} +@keyframes aura-glow { + 20%, 80% { + opacity: 0.7; + filter: blur(0.25rem); + } + 50% { + opacity: 1; + filter: blur(0.75rem); + } +} +@keyframes aura-glow-after { + 20%, 80% { + opacity: 0.3; + filter: blur(1rem); + } + 50% { + opacity: 0.6; + filter: blur(1.5rem); + } +} +@keyframes menu { + 0% { + opacity: 0; + } +} +@property --tw-rotate-x { + syntax: "*"; + inherits: false; +} +@property --tw-rotate-y { + syntax: "*"; + inherits: false; +} +@property --tw-rotate-z { + syntax: "*"; + inherits: false; +} +@property --tw-skew-x { + syntax: "*"; + inherits: false; +} +@property --tw-skew-y { + syntax: "*"; + inherits: false; +} +@property --tw-space-y-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-divide-y-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-border-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} +@property --tw-font-weight { + syntax: "*"; + inherits: false; +} +@property --tw-tracking { + syntax: "*"; + inherits: false; +} +@property --tw-ordinal { + syntax: "*"; + inherits: false; +} +@property --tw-slashed-zero { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-figure { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-spacing { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-fraction { + syntax: "*"; + inherits: false; +} +@property --tw-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-inset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-inset-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-inset-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-ring-color { + syntax: "*"; + inherits: false; +} +@property --tw-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-inset-ring-color { + syntax: "*"; + inherits: false; +} +@property --tw-inset-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-ring-inset { + syntax: "*"; + inherits: false; +} +@property --tw-ring-offset-width { + syntax: ""; + inherits: false; + initial-value: 0px; +} +@property --tw-ring-offset-color { + syntax: "*"; + inherits: false; + initial-value: #fff; +} +@property --tw-ring-offset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-outline-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} +@property --tw-blur { + syntax: "*"; + inherits: false; +} +@property --tw-brightness { + syntax: "*"; + inherits: false; +} +@property --tw-contrast { + syntax: "*"; + inherits: false; +} +@property --tw-grayscale { + syntax: "*"; + inherits: false; +} +@property --tw-hue-rotate { + syntax: "*"; + inherits: false; +} +@property --tw-invert { + syntax: "*"; + inherits: false; +} +@property --tw-opacity { + syntax: "*"; + inherits: false; +} +@property --tw-saturate { + syntax: "*"; + inherits: false; +} +@property --tw-sepia { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-drop-shadow-size { + syntax: "*"; + inherits: false; +} +@property --tw-ease { + syntax: "*"; + inherits: false; +} +@layer properties { + @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { + *, ::before, ::after, ::backdrop { + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; + --tw-space-y-reverse: 0; + --tw-divide-y-reverse: 0; + --tw-border-style: solid; + --tw-font-weight: initial; + --tw-tracking: initial; + --tw-ordinal: initial; + --tw-slashed-zero: initial; + --tw-numeric-figure: initial; + --tw-numeric-spacing: initial; + --tw-numeric-fraction: initial; + --tw-shadow: 0 0 #0000; + --tw-shadow-color: initial; + --tw-shadow-alpha: 100%; + --tw-inset-shadow: 0 0 #0000; + --tw-inset-shadow-color: initial; + --tw-inset-shadow-alpha: 100%; + --tw-ring-color: initial; + --tw-ring-shadow: 0 0 #0000; + --tw-inset-ring-color: initial; + --tw-inset-ring-shadow: 0 0 #0000; + --tw-ring-inset: initial; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-offset-shadow: 0 0 #0000; + --tw-outline-style: solid; + --tw-blur: initial; + --tw-brightness: initial; + --tw-contrast: initial; + --tw-grayscale: initial; + --tw-hue-rotate: initial; + --tw-invert: initial; + --tw-opacity: initial; + --tw-saturate: initial; + --tw-sepia: initial; + --tw-drop-shadow: initial; + --tw-drop-shadow-color: initial; + --tw-drop-shadow-alpha: 100%; + --tw-drop-shadow-size: initial; + --tw-ease: initial; + } + } +} diff --git a/templates/allauth/elements/fields.html b/templates/allauth/elements/fields.html index 4ea96bd..faecb41 100644 --- a/templates/allauth/elements/fields.html +++ b/templates/allauth/elements/fields.html @@ -1,4 +1,16 @@ -{% load ui %} +{% load lucide ui %} + +{% comment %} + allauth passes `unlabeled=True` on the entrance forms (login, signup, reset) and + already sets a placeholder on each of their fields ("Email address", "Password"), + so dropping the visible label loses nothing on screen. The label is still emitted + sr-only inside the wrapper: a placeholder is not a label, and it disappears the + moment you start typing. + + Fields with an icon use daisyUI's icon-in-field layout, where the `input` class + goes on the *wrapping label* and the input itself carries only `grow`. Putting + `input` on both draws a box inside a box. +{% endcomment %} {% for field in attrs.form.hidden_fields %}{{ field }}{% endfor %} {% for error in attrs.form.non_field_errors %}
@@ -13,12 +25,30 @@ {{ field.label }} {% else %} - - {{ field|daisy }} + {% if not attrs.unlabeled %} + + {% endif %} + {% with icon=field|field_icon %} + {% if icon %} + + {% else %} + {{ field|daisy }} + {% endif %} + {% endwith %} {% endif %} - {% if field.help_text %}{{ field.help_text }}{% endif %} + {% comment %} + The password field's help_text is allauth's "Forgot your password?" link, so it + gets room to breathe. The id is not decorative: Django points the input's + aria-describedby at `_helptext`, and without it that reference dangles + and the link is never announced. + {% endcomment %} + {% if field.help_text %}{{ field.help_text }}{% endif %} {% for error in field.errors %}{{ error }}{% endfor %}
{% endfor %} diff --git a/templates/allauth/layouts/base.html b/templates/allauth/layouts/base.html index 010645f..ac71a41 100644 --- a/templates/allauth/layouts/base.html +++ b/templates/allauth/layouts/base.html @@ -11,7 +11,7 @@ {% block main %}
-
+
{% block content %}{% endblock content %}
From b3f153a2dc181d68bd86cbace656a1e0cd42f8bd Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 22:47:26 +0200 Subject: [PATCH 045/137] Lay out the login card and wire up the passkey button Sign In and "Sign in with a passkey" (btn-accent) now sit side by side with "Remember Me" on the same row, and the email/password block is given room above and below. The passkey button was dead. It submits a *different* form -- the hidden `mfa_login` that allauth renders from its `extra_body` block -- and our layout base never defined that block, so neither the form nor the webauthn script was ever emitted and clicking the button did nothing. _base.html now has the block, and there is a test asserting the form and script are on the page. The `fields` element grows an `exclude`, so a page can lay a field out itself (here: "remember", moved onto the button row). It splits on commas rather than testing for a substring -- "password" is a substring of "password2", and a page excluding one would otherwise silently drop the other. Co-Authored-By: Claude Opus 4.8 --- controlpanel/templatetags/ui.py | 11 +++++ controlpanel/tests.py | 42 ++++++++++++++++- static/css/app.css | 13 ++++++ templates/_base.html | 7 +++ templates/account/login.html | 65 ++++++++++++++++++++++++++ templates/allauth/elements/fields.html | 3 ++ 6 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 templates/account/login.html diff --git a/controlpanel/templatetags/ui.py b/controlpanel/templatetags/ui.py index 49cfafa..ab581cb 100644 --- a/controlpanel/templatetags/ui.py +++ b/controlpanel/templatetags/ui.py @@ -64,6 +64,17 @@ def field_icon(field): return FIELD_ICONS.get(field.name, "") +@register.filter +def excluded(field, names): + """Is this field in a comma-separated exclude list? + + Split rather than a substring test: ``"password" in "password2"`` is true, and the + login page excluding "remember" must not silently drop a field whose name happens + to contain it. + """ + return field.name in (names or "").split(",") + + @register.filter def daisy(field, css=None): """Render a bound form field with the right daisyUI classes. diff --git a/controlpanel/tests.py b/controlpanel/tests.py index 1aaba5e..73faaca 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -19,7 +19,7 @@ from teams.models import Position, Team, TeamMembership from .services.admins import grant_club_admin from .services.platform_admins import PlatformAdminError, is_last_superuser, set_platform_access from .services.statistics import club_statistics, clubs_with_totals, platform_totals -from .templatetags.ui import as_alert, daisy, field_icon +from .templatetags.ui import as_alert, daisy, excluded, field_icon User = get_user_model() Flag = get_waffle_flag_model() @@ -513,3 +513,43 @@ class LoginFormRenderingTests(TestCase): self.assertContains(self.response, 'id="id_password_helptext"') self.assertContains(self.response, "mt-3") self.assertContains(self.response, "Forgot your password?") + + +class LoginLayoutTests(TestCase): + def setUp(self): + self.response = self.client.get(reverse("account_login")) + + def test_remember_me_is_laid_out_by_the_page_not_the_fields_element(self): + # It sits on the button row, so the fields element must not also render it. + self.assertEqual(self.response.content.count(b'name="remember"'), 1) + self.assertContains(self.response, 'Remember Me') + + def test_the_passkey_button_sits_beside_sign_in(self): + self.assertContains(self.response, "btn btn-accent") + self.assertContains(self.response, "Sign in with a passkey") + + def test_the_passkey_button_has_a_form_to_submit(self): + # The button posts to the hidden `mfa_login` form via its `form` attribute. That + # form comes from allauth's extra_body block — without it the button is dead. + self.assertContains(self.response, 'form="mfa_login"') + self.assertContains(self.response, 'id="mfa_login"') + self.assertContains(self.response, "allauth.webauthn.forms.loginForm") + + +class ExcludedFilterTests(TestCase): + def field(self, name): + class Form(forms.Form): + pass + + Form.base_fields[name] = forms.CharField() + return Form()[name] + + def test_a_field_is_excluded_by_exact_name(self): + self.assertTrue(excluded(self.field("remember"), "remember")) + + def test_a_name_that_merely_contains_another_is_not_excluded(self): + # A substring test would drop "password" when excluding "password2". + self.assertFalse(excluded(self.field("password"), "password2,remember")) + + def test_nothing_is_excluded_without_a_list(self): + self.assertFalse(excluded(self.field("remember"), None)) diff --git a/static/css/app.css b/static/css/app.css index 5f2d35b..672b21b 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -2461,12 +2461,18 @@ .mt-4 { margin-top: calc(var(--spacing) * 4); } + .mt-8 { + margin-top: calc(var(--spacing) * 8); + } .mb-4 { margin-bottom: calc(var(--spacing) * 4); } .mb-6 { margin-bottom: calc(var(--spacing) * 6); } + .mb-10 { + margin-bottom: calc(var(--spacing) * 10); + } .ml-2 { margin-left: calc(var(--spacing) * 2); } @@ -3159,6 +3165,13 @@ --tw-ease: var(--ease-out); transition-timing-function: var(--ease-out); } + .btn-accent { + @layer daisyui.l1.l2 { + --btn-color: var(--color-accent); + --btn-fg: var(--color-accent-content); + --btn-soft-bg: initial; + } + } .btn-error { @layer daisyui.l1.l2 { --btn-color: var(--color-error); diff --git a/templates/_base.html b/templates/_base.html index 69895a1..fc10ef6 100644 --- a/templates/_base.html +++ b/templates/_base.html @@ -121,5 +121,12 @@ applyMode(currentMode()); + + {% comment %} + allauth puts page-level scripts and out-of-form markup here — notably the + hidden `mfa_login` form the passkey button submits. Without this block that + form is never rendered and "Sign in with a passkey" is a dead button. + {% endcomment %} + {% block extra_body %}{% endblock extra_body %} diff --git a/templates/account/login.html b/templates/account/login.html new file mode 100644 index 0000000..923bb9f --- /dev/null +++ b/templates/account/login.html @@ -0,0 +1,65 @@ +{% extends "account/base_entrance.html" %} +{% load allauth i18n lucide ui %} + +{% comment %} + Overridden for layout: "Remember Me" sits on the button row rather than floating as + a third field, and the passkey button sits beside Sign In instead of below a rule. + + The passkey button submits a *different* form (the hidden `mfa_login` rendered by the + webauthn snippet in extra_body) via the `form` attribute — forms cannot nest, so it + can live inside the login form and still post elsewhere. +{% endcomment %} + +{% block head_title %} + {% trans "Sign In" %} +{% endblock head_title %} + +{% block content %} + {% element h1 %} + {% trans "Sign In" %} + {% endelement %} + + + {% csrf_token %} + +
+ {% element fields form=form unlabeled=True exclude="remember" %} + {% endelement %} +
+ + {{ redirect_field }} + +
+ {% if form.fields.remember %} + + {% else %} + + {% endif %} + +
+ + {% if PASSKEY_LOGIN_ENABLED %} + + {% endif %} +
+
+ + + {% if LOGIN_BY_CODE_ENABLED %} + {% element hr %} + {% endelement %} + {% element button href=request_login_code_url tags="prominent,login,outline,primary" %} + {% trans "Send me a sign-in code" %} + {% endelement %} + {% endif %} +{% endblock content %} + +{% block extra_body %} + {{ block.super }} + {% if PASSKEY_LOGIN_ENABLED %} + {% include "mfa/webauthn/snippets/login_script.html" with button_id="passkey_login" %} + {% endif %} +{% endblock extra_body %} diff --git a/templates/allauth/elements/fields.html b/templates/allauth/elements/fields.html index faecb41..dfe145b 100644 --- a/templates/allauth/elements/fields.html +++ b/templates/allauth/elements/fields.html @@ -18,6 +18,8 @@
{% endfor %} {% for field in attrs.form.visible_fields %} + {# `exclude` lets a page lay a field out itself — the login page puts "remember" on the button row. #} + {% if not field|excluded:attrs.exclude %}
{% if field.field.widget.input_type == "checkbox" %}
+ {% endif %} {% endfor %} From 2b7b2b64db405fc3dc6082b2c2e95d2a0a19aeb5 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 22:59:00 +0200 Subject: [PATCH 046/137] Fix empty allauth forms, and lay out the 2FA page The 2FA code input was not invisible -- it was absent, along with the fields of every other allauth form except login. Cause: the `fields` element passed `attrs.exclude` straight into a filter. On a page that never sets it, resolving a filter *argument* raises VariableDoesNotExist; Django rescues that for the main variable of an expression but not for a filter argument, and {% if %} then swallows it and reads the condition as false. So every field was skipped. Login was the one page that passes `exclude`, which is exactly why it kept working and hid the damage. `exclude` is now pinned to a real variable first, with tests that render the login, signup and password-reset forms and assert their inputs exist. Two dangling buttons fixed while in here: `elements/form.html` dropped the `id` attribute, so the out-of-band forms allauth generates (webauthn_form, logout-from-stage) had no id for a button's `form` attribute to point at. "Use a security key" submitted nothing. Layout: the code is a daisyUI otp field, Cancel sits beside Sign In as a plain button, both gain icons, and "Use a security key" becomes an accent button. The otp boxes yield once more than six characters are typed. allauth accepts a TOTP code (6) *or* a recovery code (8) in this one field, so hard-boxing it to six would have locked out every recovery code. Co-Authored-By: Claude Opus 4.8 --- authentication/tests.py | 46 ++++++ static/css/app.css | 187 +++++++++++++++++++++++++ templates/allauth/elements/fields.html | 13 +- templates/allauth/elements/form.html | 15 +- templates/allauth/elements/h1.html | 2 +- templates/mfa/authenticate.html | 109 ++++++++++++++ 6 files changed, 366 insertions(+), 6 deletions(-) create mode 100644 templates/mfa/authenticate.html diff --git a/authentication/tests.py b/authentication/tests.py index 871d356..01dfdd5 100644 --- a/authentication/tests.py +++ b/authentication/tests.py @@ -232,3 +232,49 @@ class AdminLoginRoutingTests(TestCase): def test_allauth_login_page_loads(self): self.assertEqual(self.client.get(reverse("account_login")).status_code, 200) + + +class AuthFormRenderingTests(TestCase): + """Every allauth form must actually render its fields. + + Regression: the `fields` element passed `attrs.exclude` straight into a filter. + On a page that never sets it, resolving a filter *argument* raises + VariableDoesNotExist — which Django swallows inside {% if %} and reads as false — + so every field was silently dropped from every form except the login page (the one + page that does pass `exclude`). + """ + + def test_the_login_form_renders_its_fields(self): + self.assertContains(self.client.get(reverse("account_login")), 'name="login"') + + def test_the_password_reset_form_renders_its_fields(self): + self.assertContains(self.client.get(reverse("account_reset_password")), 'name="email"') + + def test_the_signup_form_renders_its_fields(self): + self.assertContains(self.client.get(reverse("account_signup")), 'name="password1"') + + +class TwoFactorPageTests(TestCase): + def setUp(self): + user = User.objects.create_user(email="mfa@example.com", password="pw-secret-123") + enrol_mfa(user) + # Password accepted, second factor still owed: this is the 2FA challenge page. + self.response = self.client.post(reverse("account_login"), {"login": "mfa@example.com", "password": "pw-secret-123"}, follow=True) + + def test_the_code_field_renders_as_an_otp_input(self): + self.assertContains(self.response, 'name="code"') + self.assertContains(self.response, "otp otp-lg") + + def test_cancel_sits_beside_sign_in_and_is_not_primary(self): + self.assertContains(self.response, '
{% endfor %} +{% comment %} + `exclude` lets a page lay a field out itself — the login page puts "remember" on the + button row. It is pinned to a real variable first, and that is not cosmetic: passing + `attrs.exclude` straight into a filter on a page that never set it raises + VariableDoesNotExist, which Django catches for the *main* variable of an expression + but not for a filter *argument*. {% if %} then swallows it and reads the condition as + false — silently dropping every field on every form that doesn't pass `exclude`. +{% endcomment %} +{% with exclude=attrs.exclude|default:"" %} {% for field in attrs.form.visible_fields %} - {# `exclude` lets a page lay a field out itself — the login page puts "remember" on the button row. #} - {% if not field|excluded:attrs.exclude %} + {% if not field|excluded:exclude %}
{% if field.field.widget.input_type == "checkbox" %}
{% endif %} {% endfor %} +{% endwith %} diff --git a/templates/allauth/elements/form.html b/templates/allauth/elements/form.html index 38ea765..64e47d5 100644 --- a/templates/allauth/elements/form.html +++ b/templates/allauth/elements/form.html @@ -1,9 +1,18 @@ {% load allauth %} + +{% comment %} + `id` is not decorative: allauth renders out-of-band forms (webauthn, logout-from-stage) + and points buttons at them with the HTML `form` attribute. Drop the id and those + buttons submit nothing. +{% endcomment %}
{% slot body %}{% endslot %} -
- {% slot actions %}{% endslot %} -
+ {% if not attrs.no_visible_fields %} +
+ {% slot actions %}{% endslot %} +
+ {% endif %}
diff --git a/templates/allauth/elements/h1.html b/templates/allauth/elements/h1.html index a3feddd..92a6edb 100644 --- a/templates/allauth/elements/h1.html +++ b/templates/allauth/elements/h1.html @@ -1 +1 @@ -{% comment %} djlint:off {% endcomment %}{% load allauth %}

{% slot %}{% endslot %}

+{% comment %} djlint:off {% endcomment %}{% load allauth %}

{% slot %}{% endslot %}

diff --git a/templates/mfa/authenticate.html b/templates/mfa/authenticate.html new file mode 100644 index 0000000..2fe2200 --- /dev/null +++ b/templates/mfa/authenticate.html @@ -0,0 +1,109 @@ +{% extends "mfa/base_entrance.html" %} +{% load allauth i18n lucide ui %} + +{% comment %} + Overridden for layout: the code is a daisyUI otp field, Cancel sits beside Sign In, + and "Use a security key" is an accent button under Alternative options. + + Cancel and the security key both submit *other* forms (`logout-from-stage`, + `webauthn_form`) through the HTML `form` attribute — forms cannot nest, so those live + at the end of the page and the buttons point at them by id. +{% endcomment %} + +{% block head_title %} + {% trans "Sign In" %} +{% endblock head_title %} + +{% block content %} + {% element h1 %} + {% trans "Two-Factor Authentication" %} + {% endelement %} + {% element p %} + {% blocktranslate %}Your account is protected by two-factor authentication. Please enter an authenticator code:{% endblocktranslate %} + {% endelement %} + +
+ {% csrf_token %} + +
+ {% comment %} + daisyUI's otp is one input overlaid on the boxes. Six of them, for a TOTP + code — but this same field also accepts an 8-character recovery code, so the + boxes step aside once what's typed no longer fits them (see the script + below). Boxing it to six and calling it done would lock out recovery codes. + {% endcomment %} +
+ {{ form.code|daisy:"" }} + + + + + + +
+ {% for error in form.code.errors %}{{ error }}{% endfor %} + {% for error in form.non_field_errors %}{{ error }}{% endfor %} +
+ +
+ + +
+
+ + {% if "webauthn" in MFA_SUPPORTED_TYPES %} + {% element hr %} + {% endelement %} + {% element h2 %} + {% translate "Alternative options" %} + {% endelement %} + + + +
+ {% csrf_token %} + {{ webauthn_form.credential }} +
+ {{ js_data|json_script:"js_data" }} + {% include "mfa/webauthn/snippets/scripts.html" %} + + {% endif %} + +
+ + {% csrf_token %} +
+ + +{% endblock content %} From addfc61a2c0e529f0882222a3f4cfb3dd659d846 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 23:07:44 +0200 Subject: [PATCH 047/137] Style every MFA screen through the element system The MFA pages (manage, TOTP activate/deactivate, recovery codes, security keys, reauthenticate) are built almost entirely from allauth's `element` primitives, so they are styled by overriding the elements rather than by rewriting eight page templates. New allauth pages then inherit the look for free. - field + img elements were missing entirely, so allauth fell back to bare HTML: the TOTP secret and recovery-code list rendered as unstyled inputs. The QR now sits on a white plate -- it is dark modules on a transparent ground, so on the dark theme it was dark-on-dark and phones could not scan it. - button now honours the tags allauth sets. They were all flattened to btn-primary, which made "Deactivate" look exactly as safe as "View". - the `code` field renders as a daisyUI otp wherever it appears, so the reauthenticate and activate pages get the same input as the login challenge. The boxes step aside past six characters: allauth accepts a TOTP code (6) or a recovery code (8) in that one field. Fixes a crash: the security-key list does {% load humanize %}, which raised TemplateSyntaxError because django.contrib.humanize was not installed. That page 500'd on every request; it is now installed and covered by a test. Co-Authored-By: Claude Opus 4.8 --- authentication/tests.py | 44 +++++++++++++++++++ controlpanel/templatetags/ui.py | 9 ++++ rosterchief/settings.py | 3 ++ static/css/app.css | 26 ++++++++++++ templates/_base.html | 23 ++++++++++ templates/allauth/elements/button.html | 10 ++++- templates/allauth/elements/field.html | 42 +++++++++++++++++++ templates/allauth/elements/fields.html | 20 ++++++++- templates/allauth/elements/img.html | 10 +++++ templates/mfa/authenticate.html | 58 +++++--------------------- 10 files changed, 196 insertions(+), 49 deletions(-) create mode 100644 templates/allauth/elements/field.html create mode 100644 templates/allauth/elements/img.html diff --git a/authentication/tests.py b/authentication/tests.py index 01dfdd5..b0c0270 100644 --- a/authentication/tests.py +++ b/authentication/tests.py @@ -278,3 +278,47 @@ class TwoFactorPageTests(TestCase): # The id lives on the form element — without it the button submits nothing. self.assertContains(self.response, 'id="webauthn_form"') self.assertContains(self.response, "allauth.webauthn.forms.authenticateForm") + + +class MfaPageTests(TestCase): + """Every MFA screen must render. They are built from allauth's `element` primitives, + so styling lives in the element overrides rather than in eight page templates.""" + + def setUp(self): + self.user = User.objects.create_user(email="mfa@example.com", password="pw-secret-123") + # A real password login (not force_login) so allauth counts it as a recent + # authentication and doesn't bounce the sensitive pages to reauthenticate. + self.client.post(reverse("account_login"), {"login": "mfa@example.com", "password": "pw-secret-123"}, follow=True) + + def test_the_manage_page_renders_a_panel_per_authenticator(self): + response = self.client.get(reverse("mfa_index")) + + self.assertContains(response, "Authenticator App") + self.assertContains(response, "card border") + + def test_the_security_key_list_renders(self): + # Regression: allauth's template does {% load humanize %}, which raised + # TemplateSyntaxError until django.contrib.humanize was installed. + self.assertEqual(self.client.get(reverse("mfa_list_webauthn")).status_code, 200) + + def test_the_totp_activate_page_boxes_the_code_and_plates_the_qr(self): + response = self.client.get(reverse("mfa_activate_totp")) + + self.assertContains(response, "otp otp-lg") + # The QR is dark-on-transparent: without a white plate it is unscannable on the + # dark theme. + self.assertContains(response, "bg-white p-3") + self.assertContains(response, "font-mono") # the secret, to be copied by hand + + def test_the_deactivate_button_is_destructive(self): + enrol_mfa(self.user) + + response = self.client.get(reverse("mfa_index")) + + # allauth tags it "danger" — it must not look like the safe action. + self.assertContains(response, "btn-error") + + def test_reauthenticating_with_a_code_boxes_the_input(self): + enrol_mfa(self.user) + + self.assertContains(self.client.get(reverse("mfa_reauthenticate")), "otp otp-lg") diff --git a/controlpanel/templatetags/ui.py b/controlpanel/templatetags/ui.py index ab581cb..199a249 100644 --- a/controlpanel/templatetags/ui.py +++ b/controlpanel/templatetags/ui.py @@ -59,6 +59,15 @@ FIELD_ICONS = { } +#: Fields rendered as a boxed one-time-code input rather than a plain text field. +OTP_FIELDS = {"code"} + + +@register.filter +def is_otp(field): + return field.name in OTP_FIELDS + + @register.filter def field_icon(field): return FIELD_ICONS.get(field.name, "") diff --git a/rosterchief/settings.py b/rosterchief/settings.py index 110057e..c13f0e1 100644 --- a/rosterchief/settings.py +++ b/rosterchief/settings.py @@ -43,6 +43,9 @@ INSTALLED_APPS = [ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", + # Required by allauth's security-key list template ({% load humanize %}); without it + # that page raises TemplateSyntaxError. + "django.contrib.humanize", "phonenumber_field", "lucide", # Auth: allauth deliberately WITHOUT django.contrib.sites — it is optional in diff --git a/static/css/app.css b/static/css/app.css index 74cc999..0d852fc 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -7,6 +7,7 @@ --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace; --color-sky-500: oklch(68.5% 0.169 237.323); --color-black: #000; + --color-white: #fff; --spacing: 0.25rem; --container-xs: 20rem; --container-xl: 36rem; @@ -3089,6 +3090,9 @@ .bg-primary { background-color: var(--color-primary); } + .bg-white { + background-color: var(--color-white); + } .btn-link { @layer daisyui { text-decoration-line: underline; @@ -3119,6 +3123,17 @@ background-image: none; } } + .btn-outline { + @layer daisyui.l1.l2.l3 { + --btn-bg: #0000; + color: var(--btn-rest-fg, var(--btn-color, var(--color-base-content))); + --btn-border: var(--btn-color, var(--color-base-content)); + --btn-border-style: solid; + background-image: none; + --btn-inset: 0 0 0 0 oklch(0% 0 0/0); + --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); + } + } .btn-ghost { @layer daisyui.l1.l2.l3 { --btn-bg: #0000; @@ -3135,6 +3150,9 @@ .p-2 { padding: calc(var(--spacing) * 2); } + .p-3 { + padding: calc(var(--spacing) * 3); + } .p-4 { padding: calc(var(--spacing) * 4); } @@ -3352,6 +3370,14 @@ } } } + .btn-neutral { + @layer daisyui.l1.l2 { + --btn-color: var(--color-neutral); + --btn-fg: var(--color-neutral-content); + --btn-soft-bg: var(--color-neutral-content) 80%; + --btn-rest-fg: initial; + } + } .btn-accent { @layer daisyui.l1.l2 { --btn-color: var(--color-accent); diff --git a/templates/_base.html b/templates/_base.html index fc10ef6..a2e337d 100644 --- a/templates/_base.html +++ b/templates/_base.html @@ -122,6 +122,29 @@ applyMode(currentMode()); + + {% comment %} allauth puts page-level scripts and out-of-form markup here — notably the hidden `mfa_login` form the passkey button submits. Without this block that diff --git a/templates/allauth/elements/button.html b/templates/allauth/elements/button.html index e4a04e9..8e24b3f 100644 --- a/templates/allauth/elements/button.html +++ b/templates/allauth/elements/button.html @@ -1,7 +1,15 @@ {% load allauth %} + +{% comment %} + allauth tags every button it renders, and mapping those tags is what styles the MFA + and account pages without overriding each one. The ranking matters: those pages put + three buttons in a row (Deactivate / View / Download) and rely on the tags to say + which is destructive and which is secondary. Flattening them all to btn-primary makes + "Deactivate" look exactly as safe as "View". +{% endcomment %} {% comment %} djlint:off {% endcomment %} <{% if attrs.href %}a href="{{ attrs.href }}"{% else %}button{% endif %} -class="btn {% if attrs.tags and 'danger' in attrs.tags %}btn-error{% elif attrs.tags and 'secondary' in attrs.tags %}btn-ghost{% elif attrs.tags and 'link' in attrs.tags %}btn-link{% else %}btn-primary{% endif %}" +class="btn {% if attrs.tags and 'danger' in attrs.tags %}btn-error{% elif attrs.tags and 'link' in attrs.tags %}btn-link{% elif attrs.tags and 'secondary' in attrs.tags %}btn-outline btn-neutral{% elif attrs.tags and 'outline' in attrs.tags %}btn-outline btn-primary{% else %}btn-primary{% endif %}" {% if attrs.form %}form="{{ attrs.form }}"{% endif %} {% if attrs.id %}id="{{ attrs.id }}"{% endif %} {% if attrs.name %}name="{{ attrs.name }}"{% endif %} diff --git a/templates/allauth/elements/field.html b/templates/allauth/elements/field.html new file mode 100644 index 0000000..19aa365 --- /dev/null +++ b/templates/allauth/elements/field.html @@ -0,0 +1,42 @@ +{% load allauth %} + +{% comment %} + A standalone field that isn't part of a Django form — allauth uses it for the TOTP + secret and the recovery-code list. Mono, because both are strings people copy + character by character and must not misread. +{% endcomment %} +{{ attrs.errors }} +
+ {% if slots.label %} + + {% endif %} + + {% if attrs.type == "textarea" %} + + {% else %} + + {% endif %} + + {% if slots.help_text %} + {% slot help_text %}{% endslot %} + {% endif %} +
diff --git a/templates/allauth/elements/fields.html b/templates/allauth/elements/fields.html index b76f58d..372d767 100644 --- a/templates/allauth/elements/fields.html +++ b/templates/allauth/elements/fields.html @@ -41,7 +41,25 @@ {% endif %} {% with icon=field|field_icon %} - {% if icon %} + {% if field|is_otp %} + {% comment %} + daisyUI's otp is one input overlaid on the boxes, and it sizes itself from + its `span` children — so the sr-only label goes OUTSIDE the container, or it + would count as a seventh box and throw the width off. + {% endcomment %} + {% if attrs.unlabeled %}{% endif %} +
+
+ {{ field|daisy:"" }} + + + + + + +
+
+ {% elif icon %}
{% endfor %} diff --git a/controlpanel/templates/controlpanel/club_form.html b/controlpanel/templates/controlpanel/club_form.html index e52f492..97a127d 100644 --- a/controlpanel/templates/controlpanel/club_form.html +++ b/controlpanel/templates/controlpanel/club_form.html @@ -1,5 +1,5 @@ {% extends "controlpanel/base.html" %} -{% load ui %} +{% load lucide ui %} {% block heading %}{% if object %}Edit {{ object }}{% else %}New club{% endif %}{% endblock heading %} @@ -24,7 +24,7 @@
{% endfor %} diff --git a/controlpanel/templates/controlpanel/flag_form.html b/controlpanel/templates/controlpanel/flag_form.html index 56c4f23..9e9f830 100644 --- a/controlpanel/templates/controlpanel/flag_form.html +++ b/controlpanel/templates/controlpanel/flag_form.html @@ -1,5 +1,5 @@ {% extends "controlpanel/base.html" %} -{% load ui %} +{% load lucide ui %} {% block heading %}{% if object %}Edit {{ object.name }}{% else %}New feature{% endif %}{% endblock heading %} @@ -24,7 +24,7 @@
{% endfor %} diff --git a/static/css/app.css b/static/css/app.css index 0d852fc..1669192 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -1,4123 +1,2 @@ /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */ -@layer properties; -@layer theme, base, components, utilities; -@layer theme { - :root, :host { - --font-sans: "Ubuntu", ui-sans-serif, system-ui, sans-serif; - --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace; - --color-sky-500: oklch(68.5% 0.169 237.323); - --color-black: #000; - --color-white: #fff; - --spacing: 0.25rem; - --container-xs: 20rem; - --container-xl: 36rem; - --text-xs: 0.75rem; - --text-xs--line-height: calc(1 / 0.75); - --text-sm: 0.875rem; - --text-sm--line-height: calc(1.25 / 0.875); - --text-base: 1rem; - --text-base--line-height: calc(1.5 / 1); - --text-lg: 1.125rem; - --text-lg--line-height: calc(1.75 / 1.125); - --text-xl: 1.25rem; - --text-xl--line-height: calc(1.75 / 1.25); - --text-2xl: 1.5rem; - --text-2xl--line-height: calc(2 / 1.5); - --font-weight-medium: 500; - --font-weight-semibold: 600; - --font-weight-bold: 700; - --tracking-wider: 0.05em; - --ease-out: cubic-bezier(0, 0, 0.2, 1); - --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); - --default-transition-duration: 150ms; - --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - --default-font-family: var(--font-sans); - --default-mono-font-family: var(--font-mono); - --font-roboto: "Roboto", ui-sans-serif, system-ui, sans-serif; - } -} -@layer base { - *, ::after, ::before, ::backdrop, ::file-selector-button { - box-sizing: border-box; - margin: 0; - padding: 0; - border: 0 solid; - } - html, :host { - line-height: 1.5; - -webkit-text-size-adjust: 100%; - tab-size: 4; - font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); - font-feature-settings: var(--default-font-feature-settings, normal); - font-variation-settings: var(--default-font-variation-settings, normal); - -webkit-tap-highlight-color: transparent; - } - hr { - height: 0; - color: inherit; - border-top-width: 1px; - } - abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - } - h1, h2, h3, h4, h5, h6 { - font-size: inherit; - font-weight: inherit; - } - a { - color: inherit; - -webkit-text-decoration: inherit; - text-decoration: inherit; - } - b, strong { - font-weight: bolder; - } - code, kbd, samp, pre { - font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); - font-feature-settings: var(--default-mono-font-feature-settings, normal); - font-variation-settings: var(--default-mono-font-variation-settings, normal); - font-size: 1em; - } - small { - font-size: 80%; - } - sub, sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; - } - sub { - bottom: -0.25em; - } - sup { - top: -0.5em; - } - table { - text-indent: 0; - border-color: inherit; - border-collapse: collapse; - } - :-moz-focusring { - outline: auto; - } - progress { - vertical-align: baseline; - } - summary { - display: list-item; - } - ol, ul, menu { - list-style: none; - } - img, svg, video, canvas, audio, iframe, embed, object { - display: block; - vertical-align: middle; - } - img, video { - max-width: 100%; - height: auto; - } - button, input, select, optgroup, textarea, ::file-selector-button { - font: inherit; - font-feature-settings: inherit; - font-variation-settings: inherit; - letter-spacing: inherit; - color: inherit; - border-radius: 0; - background-color: transparent; - opacity: 1; - } - :where(select:is([multiple], [size])) optgroup { - font-weight: bolder; - } - :where(select:is([multiple], [size])) optgroup option { - padding-inline-start: 20px; - } - ::file-selector-button { - margin-inline-end: 4px; - } - ::placeholder { - opacity: 1; - } - @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) { - ::placeholder { - color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, currentcolor 50%, transparent); - } - } - } - textarea { - resize: vertical; - } - ::-webkit-search-decoration { - -webkit-appearance: none; - } - ::-webkit-date-and-time-value { - min-height: 1lh; - text-align: inherit; - } - ::-webkit-datetime-edit { - display: inline-flex; - } - ::-webkit-datetime-edit-fields-wrapper { - padding: 0; - } - ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { - padding-block: 0; - } - ::-webkit-calendar-picker-indicator { - line-height: 1; - } - :-moz-ui-invalid { - box-shadow: none; - } - button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button { - appearance: button; - } - ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { - height: auto; - } - [hidden]:where(:not([hidden="until-found"])) { - display: none !important; - } -} -@layer utilities { - .tab { - @layer daisyui.l1.l2.l3 { - &:is(.tabs > .tab) { - position: relative; - display: inline-flex; - cursor: pointer; - appearance: none; - flex-wrap: wrap; - align-items: center; - justify-content: center; - text-align: center; - webkit-user-select: none; - user-select: none; - &:hover { - @media (hover: hover) { - color: var(--color-base-content); - } - } - --tab-p: 0.75rem; - --tab-bg: var(--color-base-100); - --tab-border-color: var(--color-base-300); - --tab-radius-ss: 0; - --tab-radius-se: 0; - --tab-radius-es: 0; - --tab-radius-ee: 0; - --tab-order: 0; - --tab-radius-min: calc(0.75rem - var(--border)); - --tab-radius-limit: min(var(--radius-field), var(--tab-radius-min)); - --tab-radius-grad: #0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + 0.25px), - var(--tab-border-color) 69%, var(--tab-bg) calc(69% + 0.25px); - border-color: #0000; - order: var(--tab-order); - height: var(--tab-height); - font-size: 0.875rem; - padding-inline: var(--tab-p); - &:is(input[type="radio"]) { - min-width: fit-content; - &:after { - --tw-content: attr(aria-label); - content: var(--tw-content); - } - } - &:is(label) { - position: relative; - input { - position: absolute; - inset: 0; - cursor: pointer; - appearance: none; - opacity: 0%; - } - } - &:checked, &:is(label:has(:checked)), &:is(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]) { - & + .tab-content { - display: block; - } - } - &:not(:checked, label:has(:checked), :hover, .tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"] ) { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 50%, transparent); - } - } - &:not(input):empty { - flex-grow: 1; - cursor: default; - } - &:focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:focus-visible, &:is(label:has(:checked:focus-visible)) { - outline: 2px solid currentColor; - outline-offset: -5px; - } - &[disabled] { - pointer-events: none; - opacity: 40%; - } - } - } - } - .menu { - @layer daisyui.l1.l2.l3 { - display: flex; - width: fit-content; - flex-direction: column; - flex-wrap: wrap; - padding: calc(0.25rem * 2); - --menu-active-fg: var(--color-neutral-content); - --menu-active-bg: var(--color-neutral); - font-size: 0.875rem; - :where(li ul, li menu) { - position: relative; - margin-inline-start: calc(0.25rem * 4); - padding-inline-start: calc(0.25rem * 2); - white-space: nowrap; - &:before { - position: absolute; - inset-inline-start: calc(0.25rem * 0); - top: calc(0.25rem * 3); - bottom: calc(0.25rem * 3); - background-color: var(--color-base-content); - opacity: 10%; - width: var(--border); - content: ""; - } - } - :where(li > .menu-dropdown:not(.menu-dropdown-show)) { - display: none; - } - :where(li:not(.menu-title) > *:not(ul, menu, details, .menu-title, .btn)), :where(li:not(.menu-title) > details > summary:not(.menu-title)) { - display: grid; - grid-auto-flow: column; - align-content: flex-start; - align-items: center; - gap: calc(0.25rem * 2); - border-radius: var(--radius-field); - padding-inline: calc(0.25rem * 3); - padding-block: calc(0.25rem * 1.5); - text-align: start; - transition-property: color, background-color, box-shadow; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - grid-auto-columns: minmax(auto, max-content) auto max-content; - user-select: none; - } - :where(li > details > summary) { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - &::-webkit-details-marker { - display: none; - } - } - :where(li > details > summary), :where(li > .menu-dropdown-toggle) { - &:after { - justify-self: flex-end; - display: block; - height: 0.375rem; - width: 0.375rem; - rotate: -135deg; - translate: 0 -1px; - transition-property: rotate, translate; - transition-duration: 0.2s; - content: ""; - transform-origin: 50% 50%; - box-shadow: 2px 2px inset; - pointer-events: none; - } - } - details { - overflow: hidden; - interpolate-size: allow-keywords; - } - details::details-content { - block-size: 0; - @media (prefers-reduced-motion: no-preference) { - transition-behavior: allow-discrete; - transition-property: block-size, content-visibility; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - } - } - details[open]::details-content { - block-size: auto; - } - :where(li > details[open] > summary):after, :where(li > .menu-dropdown-toggle.menu-dropdown-show):after { - rotate: 45deg; - translate: 0 1px; - } - :where(li:not(.menu-title, .disabled) > *:not(ul, menu, details, .menu-title), li:not(.menu-title, .disabled) > details > summary:not(.menu-title) ):not(.menu-active, :active, .btn) { - &.menu-focus, &:focus-visible { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - color: var(--color-base-content); - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - } - :where(li:not(.menu-title, .disabled) > *:not(ul, menu, details, .menu-title):not(.menu-active, :active, .btn):hover, li:not(.menu-title, .disabled) > details > summary:not(.menu-title):not(.menu-active, :active, .btn):hover ) { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - box-shadow: 0 1px oklch(0% 0 0 / 0.01) inset, 0 -1px oklch(100% 0 0 / 0.01) inset; - } - :where(li:empty) { - background-color: var(--color-base-content); - opacity: 10%; - margin: 0.5rem 1rem; - height: 1px; - } - :where(li) { - position: relative; - display: flex; - flex-shrink: 0; - flex-direction: column; - flex-wrap: wrap; - align-items: stretch; - .badge { - justify-self: flex-end; - } - & > *:not(ul, menu, .menu-title, details, .btn):active, & > *:not(ul, menu, .menu-title, details, .btn).menu-active, & > details > summary:active { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - color: var(--menu-active-fg); - background-color: var(--menu-active-bg); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - &:not(&:active) { - box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg); - } - } - &.menu-disabled, [disabled] { - pointer-events: none; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - } - } - .dropdown:focus-within { - .menu-dropdown-toggle:after { - rotate: 45deg; - translate: 0 1px; - } - } - .dropdown-content { - margin-top: calc(0.25rem * 2); - padding: calc(0.25rem * 2); - &:before { - display: none; - } - } - } - } - .otp { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - font-family: var(--font-mono); - clip-path: inset(-3.5px 3.5px -3.5px -3.5px); - border-radius: var(--radius-field); - font-size: 1.75rem; - gap: var(--otp-gap); - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --otp-ch: 1ch; - @supports (font: -apple-system-body) { - --otp-ch: 0.618164em; - } - --otp-gap: calc(var(--otp-ch) * 0.5); - --otp-w: calc(var(--otp-ch) * 2); - --otp-size: calc(var(--size-field, 0.25rem) * 10); - --stride: calc(var(--otp-w) + var(--otp-gap)); - > input { - pointer-events: none; - inset-inline-start: calc(0.25rem * 0); - z-index: 1; - margin: 0; - appearance: none; - border-style: var(--tw-border-style); - border-width: 0px; - background-color: transparent; - padding: 0; - outline-style: var(--tw-outline-style); - outline-width: 0px; - field-sizing: content; - padding-inline-start: calc(var(--otp-ch) * 0.5 + var(--border)); - line-height: 1; - letter-spacing: calc(var(--stride) - var(--otp-ch)); - font-variant-numeric: tabular-nums; - &::selection { - background-color: transparent; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - } - &:valid { - caret-color: transparent; - } - } - &:has( > span:nth-child(1)) { - width: calc(var(--stride) * 1); - } - &:has( > span:nth-child(2)) { - width: calc(var(--stride) * 2); - } - &:has( > span:nth-child(3)) { - width: calc(var(--stride) * 3); - } - &:has( > span:nth-child(4)) { - width: calc(var(--stride) * 4); - } - &:has( > span:nth-child(5)) { - width: calc(var(--stride) * 5); - } - &:has( > span:nth-child(6)) { - width: calc(var(--stride) * 6); - } - &:has( > span:nth-child(7)) { - width: calc(var(--stride) * 7); - } - &:has( > span:nth-child(8)) { - width: calc(var(--stride) * 8); - } - > span { - position: absolute; - display: flex; - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - transition: border-color 0.2s; - inline-size: var(--otp-w); - block-size: var(--otp-size); - background-color: var(--color-base-100); - border: var(--border) solid var(--input-color); - border-radius: inherit; - outline: 2px solid #0000; - outline-offset: 1px; - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - &:nth-child(1) { - left: 1px; - @supports (font: -apple-system-body) { - left: 2px; - } - @supports (-moz-appearance: none) { - left: 0px; - } - } - &:nth-child(2) { - left: calc(var(--stride) * 1); - transition-delay: 0.02s; - } - &:nth-child(3) { - left: calc(var(--stride) * 2); - transition-delay: 0.04s; - } - &:nth-child(4) { - left: calc(var(--stride) * 3); - transition-delay: 0.06s; - } - &:nth-child(5) { - left: calc(var(--stride) * 4); - transition-delay: 0.08s; - } - &:nth-child(6) { - left: calc(var(--stride) * 5); - transition-delay: 0.1s; - } - &:nth-child(7) { - left: calc(var(--stride) * 6); - transition-delay: 0.12s; - } - &:nth-child(8) { - left: calc(var(--stride) * 7); - transition-delay: 0.14s; - } - } - &:has(input:valid:focus) { - > span { - outline: 2px solid var(--input-color); - outline-offset: 1px; - } - &:after { - opacity: 0; - } - } - &:after { - flex-shrink: 0; - content: ""; - width: var(--otp-w); - height: var(--otp-size); - border-radius: var(--radius-field); - outline: 2px solid #0000; - outline-offset: 1px; - z-index: 10; - margin-inline-start: calc(-1 * (var(--otp-gap) + var(--otp-ch) * 0.5 + var(--border))); - } - &:focus-within { - isolation: isolate; - --input-color: var(--color-base-content); - > span { - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - } - &:after { - outline-color: var(--input-color); - } - } - } - } - .collapse-arrow { - @layer daisyui.l1.l2 { - > .collapse-title:after { - position: absolute; - display: block; - height: 0.5rem; - width: 0.5rem; - transform: translateY(-100%) rotate(45deg); - @media (prefers-reduced-motion: no-preference) { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 0.2s; - } - top: 50%; - inset-inline-end: 1.4rem; - content: ""; - transform-origin: 75% 75%; - box-shadow: 2px 2px; - pointer-events: none; - } - } - } - .collapse-plus { - @layer daisyui.l1.l2 { - > .collapse-title:after { - position: absolute; - display: block; - height: 0.5rem; - width: 0.5rem; - @media (prefers-reduced-motion: no-preference) { - transition-property: all; - transition-duration: 300ms; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - top: 0.9rem; - inset-inline-end: 1.4rem; - --tw-content: "+"; - content: var(--tw-content); - pointer-events: none; - } - } - } - .dropdown { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - position-area: var(--anchor-v, bottom) var(--anchor-h, span-right); - & > *:not(:has( ~ [class*="dropdown-content"])):focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - .dropdown-content { - position: absolute; - } - &.dropdown-close .dropdown-content, &:not(details, .dropdown-open, .dropdown-hover:hover, :focus-within) .dropdown-content, &.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible) ~ .dropdown-content { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - &[popover], .dropdown-content { - z-index: 999; - @media (prefers-reduced-motion: no-preference) { - animation: dropdown 0.2s; - transition-property: opacity, scale, display, overlay; - transition-behavior: allow-discrete; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - } - @starting-style { - &[popover], .dropdown-content { - scale: 95%; - opacity: 0; - } - } - &:not(.dropdown-close) { - &.dropdown-open, &:not(.dropdown-hover):focus, &:focus-within { - > [tabindex]:first-child { - pointer-events: none; - } - .dropdown-content { - opacity: 100%; - scale: 100%; - } - } - &.dropdown-hover:hover { - .dropdown-content { - opacity: 100%; - scale: 100%; - } - } - } - &:is(details) { - overflow: revert-layer; - summary { - &::-webkit-details-marker { - display: none; - } - } - } - &:where([popover]) { - background: #0000; - } - &[popover] { - position: fixed; - color: inherit; - @supports not (position-area: bottom) { - margin: auto; - &.dropdown-close, &.dropdown-open:not(:popover-open) { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - &::backdrop { - background-color: color-mix(in oklab, #000 30%, #0000); - } - } - &.dropdown-close, &:not(.dropdown-open, :popover-open) { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - } - } - } - .btn { - :where(&) { - @layer daisyui.l1.l2.l3 { - width: unset; - } - } - .prose :where(a&:not(.btn-link)):not(:where([class~="not-prose"], [class~="not-prose"] *)) { - text-decoration-line: none; - } - @layer daisyui.l1.l2.l3 { - --size: calc(var(--size-field, 0.25rem) * 10); - --btn-p: 1rem; - --btn-fg: var(--color-base-content); - display: inline-flex; - flex-shrink: 0; - cursor: pointer; - flex-wrap: nowrap; - align-items: center; - justify-content: center; - gap: calc(0.25rem * 1.5); - text-align: center; - vertical-align: middle; - outline-offset: 2px; - webkit-user-select: none; - user-select: none; - font-weight: 600; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - border-width: var(--border); - touch-action: manipulation; - transition-property: color, background-color, border-color, box-shadow, transform; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-duration: 0.2s; - --btn-bg: var(--btn-color, var(--color-base-200)); - --btn-border: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix( - in oklab, - var(--btn-color, var(--color-base-200)), - #000 calc(var(--depth) * 5%) - ); - } - --btn-soft-bg: initial; - --btn-shadow: 0 3px 2px -2px var(--btn-bg), - 0 4px 3px -2px var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), - 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); - } - --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); - height: var(--size); - padding-inline: var(--btn-p); - font-size: var(--fontsize, 0.875rem); - background-color: var(--btn-bg); - color: var(--btn-fg); - border-color: var(--btn-border); - border-style: var(--btn-border-style, solid); - outline-color: var(--btn-color, var(--color-base-content)); - --tw-prose-links: var(--btn-fg); - background-image: none, var(--fx-noise); - background-size: auto, calc(var(--noise, 0) * 100%); - text-shadow: 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 0.15)); - box-shadow: var(--btn-inset) inset, var(--btn-shadow); - &:is([type="checkbox"], [type="radio"]) { - appearance: none; - &[aria-label]::after { - --tw-content: attr(aria-label); - content: var(--tw-content); - } - } - } - @layer daisyui.l1 { - &:where(:checked:not(.filter [type="radio"].btn)) { - --btn-color: var(--color-primary); - --btn-fg: var(--color-primary-content); - } - @media (hover: hover) { - &:hover { - --btn-bg: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-bg: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 7%); - } - color: var(--btn-fg); - --btn-border: var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%)); - } - --btn-border-style: solid; - --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); - --btn-shadow: 0 3px 2px -2px var(--btn-bg), - 0 4px 3px -2px var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), - 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); - } - } - } - &:active:not(.btn-active) { - translate: 0 0.5px; - --btn-bg: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-bg: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 5%); - } - color: var(--btn-fg, var(--color-base-content)); - --btn-border: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 7%); - } - --btn-border-style: solid; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - } - &:where(:checked:not(.filter [type="radio"].btn), :not([type="radio"], [type="checkbox"]):focus-visible ) { - --btn-bg: var(--btn-color, var(--color-base-200)); - color: var(--btn-fg, var(--color-base-content)); - --btn-border: var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%)); - } - --btn-border-style: solid; - --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); - --btn-shadow: 0 3px 2px -2px var(--btn-bg), - 0 4px 3px -2px var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), - 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); - } - isolation: isolate; - } - &:focus-visible, &:has(:focus-visible) { - outline-width: 2px; - outline-style: solid; - isolation: isolate; - } - } - &:is(:disabled, [disabled], [aria-disabled="true"]) { - @layer daisyui { - pointer-events: none; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklch, var(--color-base-content) 20%, #0000); - } - --btn-bg: #0000; - --btn-border: #0000; - background-image: none; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - &:not(.btn-link, .btn-ghost) { - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - } - } - } - } - .collapse { - &:not(td, tr, colgroup) { - visibility: revert-layer; - } - @layer daisyui.l1.l2.l3 { - display: grid; - position: relative; - border-radius: var(--radius-box, 1rem); - width: 100%; - grid-template-rows: max-content 0fr; - grid-template-columns: minmax(0, 1fr); - isolation: isolate; - @media (prefers-reduced-motion: no-preference) { - transition: grid-template-rows 0.2s; - } - > input:is([type="checkbox"], [type="radio"]) { - grid-column-start: 1; - grid-row-start: 1; - appearance: none; - opacity: 0; - z-index: 1; - width: 100%; - padding: 1rem; - padding-inline-end: 3rem; - min-height: 1lh; - transition: background-color 0.2s ease-out; - } - &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ), &:not(.collapse-close):has( > input:is([type="checkbox"], [type="radio"]):checked) { - grid-template-rows: max-content 1fr; - } - &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ) > .collapse-content, &:not(.collapse-close) > :where(input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-content) { - --overflow-delay: 0.2s; - overflow: revert-layer; - content-visibility: visible; - min-height: fit-content; - @supports not (content-visibility: visible) { - visibility: visible; - } - } - &:focus-visible, &:has( > input:is([type="checkbox"], [type="radio"]):focus-visible), &:has(summary:focus-visible) { - outline-color: var(--color-base-content); - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; - } - &:not(.collapse-close) { - > input[type="checkbox"], > input[type="radio"]:not(:checked), > .collapse-title { - cursor: pointer; - } - } - &[tabindex]:focus:not(.collapse-close, .collapse[open]), &[tabindex]:focus-within:not(.collapse-close, .collapse[open]) { - > .collapse-title { - cursor: unset; - } - } - &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ) > :where(.collapse-content), &:not(.collapse-close) > :where(input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-content) { - padding-bottom: 1rem; - } - &:is(details) { - width: 100%; - &::details-content { - --overflow-delay: 0s; - overflow: clip; - height: 0; - } - &:where([open])::details-content { - overflow: revert-layer; - height: auto; - } - @media (prefers-reduced-motion: no-preference) { - &::details-content { - transition: overflow 0.2s allow-discrete var(--overflow-delay), content-visibility 0.2s allow-discrete, visibility 0.2s allow-discrete, min-height 0.2s ease-out allow-discrete, padding 0.1s ease-out 20ms, background-color 0.2s ease-out, height 0.2s; - interpolate-size: allow-keywords; - } - &:where([open])::details-content { - --overflow-delay: 0.2s; - } - } - > summary { - position: relative; - display: block; - outline: none; - &::-webkit-details-marker { - display: none; - } - } - } - } - @layer daisyui.l1.l2 { - &:is([open]) { - &.collapse-arrow { - > .collapse-title:after { - @media (prefers-reduced-motion: no-preference) { - transform: translateY(-50%) rotate(225deg); - } - } - } - } - &.collapse-open { - &.collapse-arrow { - > .collapse-title:after { - @media (prefers-reduced-motion: no-preference) { - transform: translateY(-50%) rotate(225deg); - } - } - } - &.collapse-plus { - > .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - } - &[tabindex].collapse-arrow:focus:not(.collapse-close), &.collapse-arrow[tabindex]:focus-within:not(.collapse-close) { - > .collapse-title:after { - transform: translateY(-50%) rotate(225deg); - } - } - &.collapse-arrow:not(.collapse-close) { - > input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-title:after { - transform: translateY(-50%) rotate(225deg); - } - } - &[open] { - &.collapse-plus { - > .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - } - &[tabindex].collapse-plus:focus:not(.collapse-close) { - > .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - &.collapse-plus:not(.collapse-close) { - > input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - } - } - .collapse-content { - @layer daisyui.l1.l2.l3 { - --overflow-delay: 0s; - content-visibility: hidden; - overflow: clip; - grid-column-start: 1; - grid-row-start: 2; - min-height: 0; - padding-left: 1rem; - padding-right: 1rem; - cursor: unset; - @supports not (content-visibility: hidden) { - visibility: hidden; - } - @media (prefers-reduced-motion: no-preference) { - transition: overflow 0.2s allow-discrete var(--overflow-delay), content-visibility 0.2s allow-discrete, visibility 0.2s allow-discrete, min-height 0.2s ease-out allow-discrete, padding 0.1s ease-out 20ms, background-color 0.2s ease-out; - } - details > & { - content-visibility: visible; - } - } - } - .filter { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-wrap: wrap; - [type="radio"] { - width: auto; - } - input { - overflow: hidden; - opacity: 100%; - scale: 1; - transition: visibility 0.1s allow-discrete, margin 0.1s, opacity 0.3s, padding 0.3s, border-width 0.1s; - &.filter-reset { - aspect-ratio: 1 / 1; - &::after { - --tw-content: "×"; - content: var(--tw-content); - } - } - } - > input:not(:last-child), > :not(:last-child) input { - margin-inline-end: 0.25rem; - } - } - @layer daisyui.l1 { - &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible) { - visibility: hidden; - } - &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible), &:not(:has(:focus-visible)):has(:checked:not(.filter-reset, [type="checkbox"])) :is(input, button):not(:checked, .filter-reset, [type="reset"]) { - margin-inline: 0; - width: 0; - padding-inline: 0; - opacity: 0%; - scale: 0; - border-width: 0; - } - } - } - .collapse-open { - @layer daisyui.l1.l2 { - grid-template-rows: max-content 1fr; - > .collapse-content { - --overflow-delay: 0.2s; - overflow: revert-layer; - content-visibility: visible; - min-height: fit-content; - padding-bottom: 1rem; - @supports not (content-visibility: visible) { - visibility: visible; - } - } - } - } - .collapse { - visibility: collapse; - } - .visible { - visibility: visible; - } - .list { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-direction: column; - font-size: 0.875rem; - .list-row { - --list-grid-cols: minmax(0, auto) 1fr; - position: relative; - display: grid; - grid-auto-flow: column; - gap: calc(0.25rem * 4); - border-radius: var(--radius-box); - padding: calc(0.25rem * 4); - word-break: break-word; - grid-template-columns: var(--list-grid-cols); - } - & > :not(:last-child) { - &.list-row, .list-row { - &:after { - content: ""; - border-bottom: var(--border) solid; - inset-inline: var(--radius-box); - position: absolute; - bottom: 0; - border-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-base-content) 5%, transparent); - } - } - } - } - } - @layer daisyui.l1.l2 { - .list-row { - &:has(.list-col-grow:nth-child(1)) { - --list-grid-cols: 1fr; - } - &:has(.list-col-grow:nth-child(2)) { - --list-grid-cols: minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(3)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(4)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(5)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(6)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) - minmax(0, auto) 1fr; - } - > * { - grid-row-start: 1; - } - } - } - } - .toast { - @layer daisyui.l1.l2.l3 { - position: fixed; - inset-inline-start: auto; - inset-inline-end: calc(0.25rem * 4); - top: auto; - bottom: calc(0.25rem * 4); - display: flex; - flex-direction: column; - gap: calc(0.25rem * 2); - background-color: transparent; - translate: var(--toast-x, 0) var(--toast-y, 0); - width: max-content; - max-width: calc(100vw - 2rem); - & > * { - @media (prefers-reduced-motion: no-preference) { - animation: toast 0.25s ease-out; - } - } - } - } - .toggle { - @layer daisyui.l1.l2.l3 { - border: var(--border) solid currentColor; - color: var(--input-color); - position: relative; - display: inline-grid; - flex-shrink: 0; - cursor: pointer; - appearance: none; - place-content: center; - vertical-align: middle; - webkit-user-select: none; - user-select: none; - grid-template-columns: 0fr 1fr 1fr; - --radius-selector-max: calc( - var(--radius-selector) + var(--radius-selector) + var(--radius-selector) - ); - border-radius: calc( var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)) ); - padding: var(--toggle-p); - box-shadow: 0 1px currentColor inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset; - } - transition: color 0.3s, grid-template-columns 0.2s; - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --toggle-p: calc(var(--size) * 0.125); - --size: calc(var(--size-selector, 0.25rem) * 6); - width: calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2); - height: var(--size); - > * { - z-index: 1; - grid-column: span 1 / span 1; - grid-column-start: 2; - grid-row-start: 1; - height: 100%; - cursor: pointer; - appearance: none; - background-color: transparent; - padding: calc(0.25rem * 0.5); - transition: opacity 0.2s, rotate 0.4s; - border: none; - &:focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:nth-child(2) { - color: var(--color-base-100); - rotate: 0deg; - } - &:nth-child(3) { - color: var(--color-base-100); - opacity: 0%; - rotate: -15deg; - } - } - &:has(:checked) { - > :nth-child(2) { - opacity: 0%; - rotate: 15deg; - } - > :nth-child(3) { - opacity: 100%; - rotate: 0deg; - } - } - &:before { - position: relative; - inset-inline-start: calc(0.25rem * 0); - grid-column-start: 2; - grid-row-start: 1; - aspect-ratio: 1 / 1; - height: 100%; - width: 100%; - border-radius: var(--radius-selector); - background-color: currentcolor; - translate: 0; - --tw-content: ""; - content: var(--tw-content); - transition: background-color 0.1s, translate 0.2s, inset-inline-start 0.2s; - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px currentColor; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000); - } - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - } - @media (forced-colors: active) { - &:before { - outline-style: var(--tw-outline-style); - outline-width: 1px; - outline-offset: calc(1px * -1); - } - } - @media print { - &:before { - outline: 0.25rem solid; - outline-offset: -1rem; - } - } - &:focus-visible, &:has(:focus-visible) { - outline: 2px solid currentColor; - outline-offset: 2px; - } - &:checked, &[aria-checked="true"], &:has( > input:checked) { - grid-template-columns: 1fr 1fr 0fr; - background-color: var(--color-base-100); - --input-color: var(--color-base-content); - &:before { - background-color: currentcolor; - } - @starting-style { - &:before { - opacity: 0; - } - } - } - &:indeterminate { - grid-template-columns: 0.5fr 1fr 0.5fr; - } - &:disabled { - cursor: not-allowed; - opacity: 30%; - &:before { - background-color: transparent; - border: var(--border) solid currentColor; - } - } - } - } - .input { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - flex-shrink: 1; - appearance: none; - align-items: center; - gap: calc(0.25rem * 2); - background-color: var(--color-base-100); - padding-inline: calc(0.25rem * 3); - vertical-align: middle; - white-space: nowrap; - --size: calc(var(--size-field, 0.25rem) * var(--in-size-mul)); - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - cursor: text; - width: clamp(3rem, 20rem, 100%); - height: var(--size); - font-size: max(var(--font-size, 0rem), var(--font-size-min)); - touch-action: manipulation; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - border: var(--border) solid var(--input-color, #0000); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - input { - height: 100%; - width: 100%; - appearance: none; - background-color: transparent; - border: none; - &::placeholder { - color: var(--color-base-content); - opacity: 50%; - } - &:focus, &:focus-within { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &::-webkit-calendar-picker-indicator { - inset-inline-end: -0.15em; - } - } - &::-webkit-inner-spin-button { - margin-inline-end: -10px; - } - &::-webkit-calendar-picker-indicator { - inset-inline-end: 0.75em; - } - input&, input { - position: relative; - display: inline-flex; - &[type="url"], &[type="email"] { - text-align: left; - } - &::-webkit-datetime-edit, &::-webkit-date-and-time-value { - display: grid; - min-height: 100%; - align-items: center; - text-align: inherit; - } - &::-webkit-inner-spin-button { - margin-block: calc(0.25rem * var(--spin-my)); - } - &::-webkit-calendar-picker-indicator { - position: absolute; - width: 1em; - height: 1em; - cursor: pointer; - } - &::-webkit-color-swatch-wrapper { - padding-block: 0.25rem; - } - } - &:focus, &:focus-within { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - } - @media (pointer: coarse) { - @supports (-webkit-touch-callout: none) { - &:focus, &:focus-within { - --font-size: 1rem; - } - } - } - &:has( > input[disabled]), &:is(:disabled, [disabled]), fieldset:disabled & { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - &:is(input), :is(input) { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 40%, transparent); - } - } - box-shadow: none; - &::placeholder, ::placeholder { - color: var(--color-base-content); - opacity: 20%; - } - } - &:has( > input[disabled]) > input[disabled] { - cursor: not-allowed; - } - } - @layer daisyui.l1.l2 { - --in-size-mul: 10; - --font-size-min: 0.875rem; - --spin-my: -3; - .floating-label:has(&) { - --top-mul: 5; - --font-size: 0.875rem; - } - } - } - .table { - @layer daisyui.l1.l2.l3 { - font-size: 0.875rem; - position: relative; - width: 100%; - border-collapse: separate; - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); - border-radius: var(--radius-box); - text-align: left; - &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) { - text-align: right; - } - tr.row-hover { - &, &:nth-child(even) { - &:hover { - @media (hover: hover) { - background-color: var(--color-base-200); - } - } - } - } - :where(th, td) { - padding-inline: calc(0.25rem * 4); - padding-block: calc(0.25rem * 3); - vertical-align: middle; - } - :where(thead, tfoot) { - white-space: nowrap; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 60%, transparent); - } - font-size: 0.875rem; - font-weight: 600; - } - :where(tfoot tr:first-child :is(td, th)) { - border-top: var(--border) solid var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-top: var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000); - } - } - :where(.table-pin-rows thead tr) { - position: sticky; - top: 0; - z-index: 1; - background-color: var(--color-base-100); - } - :where(.table-pin-rows tfoot tr) { - position: sticky; - bottom: 0; - z-index: 1; - background-color: var(--color-base-100); - } - :where(.table-pin-cols tr th) { - position: sticky; - right: 0; - left: 0; - background-color: var(--color-base-100); - } - :where(thead tr :is(td, th), tbody tr:not(:last-child) :is(td, th)) { - border-bottom: var(--border) solid var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-bottom: var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000); - } - } - } - } - .aura { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - --aura-padding: 0.125rem; - padding: var(--aura-padding); - border-radius: calc(var(--aura-padding) + var(--aura-radius, var(--radius-box))); - animation: aura var(--tw-duration, 6s) linear infinite; - background-image: conic-gradient(from var(--aura-angle), transparent 225deg, currentColor); - &:has( > .card, > .alert) { - --aura-radius: var(--radius-box); - } - &:has( > .btn, > .input, > .select) { - --aura-radius: var(--radius-field); - } - &:has( > .checkbox, > .toggle, > .badge) { - --aura-radius: var(--radius-selector); - } - &:before, &:after { - animation: inherit; - background-color: inherit; - background-image: inherit; - border-radius: inherit; - position: absolute; - top: calc(1 / 2 * 100%); - left: calc(1 / 2 * 100%); - z-index: 0; - display: block; - opacity: 70%; - filter: blur(0.25rem); - translate: -50% -50%; - width: 100%; - height: 100%; - content: ""; - } - &:after { - opacity: 30%; - filter: blur(1rem); - } - & > * { - position: relative; - z-index: 1; - } - } - } - .select { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - flex-shrink: 1; - appearance: none; - align-items: center; - gap: calc(0.25rem * 1.5); - background-color: var(--color-base-100); - padding-inline-start: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 7); - vertical-align: middle; - --size: calc(var(--size-field, 0.25rem) * var(--sl-size-mul)); - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - width: clamp(3rem, 20rem, 100%); - height: var(--size); - font-size: max(var(--font-size, 0rem), var(--font-size-min)); - touch-action: manipulation; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - background-image: linear-gradient(45deg, #0000 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, #0000 50%); - background-position: calc(100% - 20px) calc(1px + 50%), calc(100% - 16.1px) calc(1px + 50%); - background-size: 4px 4px, 4px 4px; - background-repeat: no-repeat; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - border: var(--border) solid var(--input-color, #0000); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - [dir="rtl"] & { - background-position: calc(0% + 12px) calc(1px + 50%), calc(0% + 16px) calc(1px + 50%); - &::picker(select), select::picker(select) { - translate: 0.5rem 0; - } - } - &[multiple] { - height: auto; - overflow: auto; - padding-block: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 3); - background-image: none; - } - select { - margin-inline-start: calc(0.25rem * -3); - margin-inline-end: calc(0.25rem * -7); - width: calc(100% + 2.75rem); - appearance: none; - padding-inline-start: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 7); - height: calc(100% - calc(var(--border) * 2)); - align-items: center; - background: inherit; - border-radius: inherit; - border-style: none; - &::placeholder { - color: var(--color-base-content); - opacity: 50%; - } - &:focus, &:focus-within { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:not(:last-child) { - margin-inline-end: calc(0.25rem * -5.5); - background-image: none; - } - } - &:focus, &:focus-within, &:open { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - background-image: linear-gradient(135deg, #0000 50%, currentColor 50%), linear-gradient(45deg, currentColor 50%, #0000 50%); - } - &:has( > select[disabled]), &:is(:disabled, [disabled]), fieldset:disabled & { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - &:is(select), :is(select) { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 40%, transparent); - } - } - box-shadow: none; - &::placeholder, ::placeholder { - color: var(--color-base-content); - opacity: 20%; - } - } - &:has( > select[disabled]) > select[disabled] { - cursor: not-allowed; - } - &, & select { - @supports (appearance: base-select) { - &, &::picker(select) { - appearance: base-select; - } - } - &::picker(select) { - color: inherit; - max-height: min(24rem, 70dvh); - margin-inline: 0.5rem; - translate: -0.5rem 0; - border: var(--border) solid var(--color-base-200); - margin-block: calc(0.25rem * 2); - border-radius: var(--radius-box); - padding: calc(0.25rem * 2); - background-color: inherit; - box-shadow: 0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/0.2); - box-shadow: 0 20px 25px -5px rgb(0 0 0 / calc(var(--depth) * 0.1)), 0 8px 10px -6px rgb(0 0 0 / calc(var(--depth) * 0.1)); - } - &::picker-icon { - display: none; - } - selectedcontent { - width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - optgroup { - padding-top: 0.5em; - option { - &:nth-child(1) { - margin-top: 0.5em; - } - } - } - option { - border-radius: var(--radius-field); - padding-block: calc(0.25rem * 1.5); - padding-inline: calc(0.25rem * var(--option-px)); - transition-property: color, background-color; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - white-space: normal; - &:not(:disabled) { - &:hover, &:focus-visible { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:active { - background-color: var(--color-neutral); - color: var(--color-neutral-content); - box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--color-neutral); - } - } - } - } - } - @layer daisyui.l1.l2 { - --sl-size-mul: 10; - --font-size-min: 0.875rem; - --option-px: 3; - .floating-label:has(&) { - --top-mul: 5; - --font-size: 0.875rem; - } - } - } - .collapse-title { - @layer daisyui.l1.l2.l3 { - grid-column-start: 1; - grid-row-start: 1; - position: relative; - width: 100%; - padding: 1rem; - padding-inline-end: 3rem; - min-height: 1lh; - transition: background-color 0.2s ease-out; - } - } - .sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip-path: inset(50%); - white-space: nowrap; - border-width: 0; - } - .avatar { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - vertical-align: middle; - & > div { - display: block; - aspect-ratio: 1 / 1; - overflow: hidden; - } - img { - height: 100%; - width: 100%; - object-fit: cover; - } - } - } - .checkbox { - @layer daisyui.l1.l2.l3 { - border: var(--border) solid var(--input-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - border: var(--border) solid var(--input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000)); - } - position: relative; - display: inline-block; - flex-shrink: 0; - cursor: pointer; - appearance: none; - border-radius: var(--radius-selector); - padding: 0.25rem; - vertical-align: middle; - color: var(--color-base-content); - box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 0 #0000 inset, 0 0 #0000; - transition: background-color 0.2s, box-shadow 0.2s; - --size: calc(var(--size-selector, 0.25rem) * 6); - width: var(--size); - height: var(--size); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - &:before { - --tw-content: ""; - content: var(--tw-content); - display: block; - width: 100%; - height: 100%; - rotate: 45deg; - background-color: currentcolor; - opacity: 0%; - transition: clip-path 0.3s, opacity 0.1s, rotate 0.3s, translate 0.3s; - transition-delay: 0.1s; - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 70% 80%, 70% 100%); - box-shadow: 0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - font-size: 1rem; - line-height: 0.75; - } - &:focus-visible { - outline: 2px solid var(--input-color, currentColor); - outline-offset: 2px; - } - &:checked, &[aria-checked="true"] { - background-color: var(--input-color, #0000); - box-shadow: 0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); - &:before { - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 0%, 70% 0%, 70% 100%); - opacity: 100%; - } - @media (forced-colors: active) { - &:before { - rotate: 0deg; - background-color: transparent; - --tw-content: "✔︎"; - clip-path: none; - } - } - @media print { - &:before { - rotate: 0deg; - background-color: transparent; - --tw-content: "✔︎"; - clip-path: none; - } - } - } - &:indeterminate { - background-color: var(--input-color, var(--color-base-content) ); - @supports (color: color-mix(in lab, red, red)) { - background-color: var(--input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000) ); - } - &:before { - rotate: 0deg; - opacity: 100%; - translate: 0 -35%; - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 80% 80%, 80% 100%); - } - } - } - &:disabled { - @layer daisyui.l1.l2 { - cursor: not-allowed; - opacity: 20%; - } - } - } - .radio { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - flex-shrink: 0; - cursor: pointer; - appearance: none; - border-radius: calc(infinity * 1px); - padding: 0.25rem; - vertical-align: middle; - border: var(--border) solid var(--input-color, currentColor); - @supports (color: color-mix(in lab, red, red)) { - border: var(--border) solid var(--input-color, color-mix(in srgb, currentColor 20%, #0000)); - } - box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset; - --size: calc(var(--size-selector, 0.25rem) * 6); - width: var(--size); - height: var(--size); - color: var(--input-color, currentColor); - &:before { - display: block; - width: 100%; - height: 100%; - border-radius: calc(infinity * 1px); - --tw-content: ""; - content: var(--tw-content); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - } - &:focus-visible { - outline: 2px solid currentColor; - } - &:checked, &[aria-checked="true"] { - border-color: currentcolor; - background-color: var(--color-base-100); - @media (prefers-reduced-motion: no-preference) { - animation: radio 0.2s ease-out; - } - &:before { - background-color: currentcolor; - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); - } - @media (forced-colors: active) { - &:before { - outline-style: var(--tw-outline-style); - outline-width: 1px; - outline-offset: calc(1px * -1); - } - } - @media print { - &:before { - outline: 0.25rem solid; - outline-offset: -1rem; - } - } - } - } - &:disabled { - @layer daisyui.l1.l2 { - cursor: not-allowed; - opacity: 20%; - } - } - } - .rating { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - vertical-align: middle; - --size: var(--size-selector, 0.25rem) * 6; - input { - cursor: pointer; - appearance: none; - } - * { - border-radius: 0; - background-color: var(--color-base-content); - opacity: 20%; - width: calc(var(--size) * 1); - height: calc(var(--size)); - @media (prefers-reduced-motion: no-preference) { - animation: rating 0.25s ease-out; - } - } - .rating-hidden { - width: calc(0.25rem * 2); - background-color: transparent; - } - :checked, [aria-checked="true"], [aria-current="true"], :has( ~ :checked, ~ [aria-checked="true"], ~ [aria-current="true"]) { - opacity: 100%; - } - :focus-visible { - scale: 1.1; - @media (prefers-reduced-motion: no-preference) { - transition: scale 0.2s ease-out; - } - } - :active:focus { - animation: none; - scale: 1.1; - } - } - @layer daisyui.l1.l2 { - --size: var(--size-selector, 0.25rem) * 6; - } - } - .navbar { - @layer daisyui.l1.l2.l3 { - display: flex; - width: 100%; - align-items: center; - padding: 0.5rem; - min-height: 4rem; - } - :where(&) { - @layer daisyui.l1.l2 { - position: relative; - } - } - } - .card { - @layer daisyui.l1.l2.l3 { - position: relative; - display: flex; - flex-direction: column; - border-radius: var(--radius-box); - transition: outline 0.2s ease-in-out; - outline: 2px solid #0000; - outline-offset: 2px; - &:focus-visible, &[aria-checked="true"], &:has( > :checked, > :is([type="checkbox"], [type="radio"]):focus-visible) { - outline-color: currentColor; - } - &:has( > :checked:focus-visible), &[aria-checked="true"]:focus-visible, &[aria-checked="true"]:has( > :is([type="checkbox"], [type="radio"]):focus-visible) { - outline-width: 4px; - } - &:has( > :is([type="checkbox"], [type="radio"])) { - cursor: pointer; - user-select: none; - } - > :is([type="checkbox"], [type="radio"]) { - appearance: none; - } - } - @layer daisyui.l1.l2.l3.l4 { - figure:first-child { - overflow: hidden; - border-start-start-radius: inherit; - border-start-end-radius: inherit; - border-end-start-radius: unset; - border-end-end-radius: unset; - } - figure:last-child { - overflow: hidden; - border-start-start-radius: unset; - border-start-end-radius: unset; - border-end-start-radius: inherit; - border-end-end-radius: inherit; - } - figure { - display: flex; - align-items: center; - justify-content: center; - } - } - } - .stats { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-grid; - grid-auto-flow: column; - overflow-x: auto; - border-radius: var(--radius-box); - } - } - .progress { - @layer daisyui.l1.l2.l3 { - position: relative; - height: calc(0.25rem * 2); - width: 100%; - appearance: none; - overflow: hidden; - border-radius: var(--radius-box); - background-color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, currentcolor 20%, transparent); - } - color: var(--color-base-content); - &:indeterminate { - background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); - background-size: 200%; - background-position-x: 15%; - @media (prefers-reduced-motion: no-preference) { - animation: progress 5s ease-in-out infinite; - } - @supports (-moz-appearance: none) { - &::-moz-progress-bar { - background-color: transparent; - @media (prefers-reduced-motion: no-preference) { - animation: progress 5s ease-in-out infinite; - background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); - background-size: 200%; - background-position-x: 15%; - } - } - } - } - @supports (-moz-appearance: none) { - &::-moz-progress-bar { - border-radius: var(--radius-box); - background-color: currentcolor; - @media (prefers-reduced-motion: no-preference) { - transition: inline-size 0.3s ease; - } - } - } - @supports (-webkit-appearance: none) { - &::-webkit-progress-bar { - border-radius: var(--radius-box); - background-color: transparent; - } - &::-webkit-progress-value { - border-radius: var(--radius-box); - background-color: currentColor; - @media (prefers-reduced-motion: no-preference) { - transition: inline-size 0.3s ease; - } - } - } - } - } - .fixed { - position: fixed; - } - .static { - position: static; - } - .dropdown-right { - @layer daisyui.l1.l2 { - --anchor-h: right; - --anchor-v: span-bottom; - .dropdown-content { - inset-inline-start: 100%; - top: 0; - bottom: auto; - transform-origin: 0; - } - } - } - .dropdown-left { - @layer daisyui.l1.l2 { - --anchor-h: left; - --anchor-v: span-bottom; - .dropdown-content { - inset-inline-end: 100%; - top: 0; - bottom: auto; - transform-origin: 100%; - } - } - } - .dropdown-end { - @layer daisyui.l1.l2 { - --anchor-h: span-left; - :where(.dropdown-content) { - inset-inline-end: calc(0.25rem * 0); - translate: 0 0; - [dir="rtl"] & { - translate: 0 0; - } - } - &.dropdown-left { - --anchor-h: left; - --anchor-v: span-top; - .dropdown-content { - top: auto; - bottom: 0; - } - } - &.dropdown-right { - --anchor-h: right; - --anchor-v: span-top; - .dropdown-content { - top: auto; - bottom: 0; - } - } - } - } - .file-input { - @layer daisyui.l1.l2.l3 { - cursor: pointer; - cursor: pointer; - border: var(--border) solid #0000; - display: inline-flex; - appearance: none; - align-items: center; - background-color: var(--color-base-100); - vertical-align: middle; - webkit-user-select: none; - user-select: none; - width: clamp(3rem, 20rem, 100%); - height: var(--size); - padding-inline-end: 0.75rem; - font-size: 0.875rem; - line-height: 2; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - border-color: var(--input-color); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - --size: calc(var(--size-field, 0.25rem) * 10); - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - &::file-selector-button { - margin-inline-end: calc(0.25rem * 4); - cursor: pointer; - padding-inline: calc(0.25rem * 4); - webkit-user-select: none; - user-select: none; - height: calc(100% + var(--border) * 2); - margin-block: calc(var(--border) * -1); - margin-inline-start: calc(var(--border) * -1); - font-size: 0.875rem; - color: var(--btn-fg); - border-width: var(--border); - border-style: solid; - border-color: var(--btn-border); - border-start-start-radius: calc(var(--join-ss, var(--radius-field) - var(--border))); - border-end-start-radius: calc(var(--join-es, var(--radius-field) - var(--border))); - font-weight: 600; - background-color: var(--btn-bg); - background-size: calc(var(--noise) * 100%); - background-image: var(--fx-noise); - text-shadow: 0 0.5px oklch(1 0 0 / calc(var(--depth) * 0.15)); - box-shadow: 0 0.5px 0 0.5px white inset, var(--btn-shadow); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 0.5px 0 0.5px color-mix( in oklab, color-mix(in oklab, white 30%, var(--btn-bg)) calc(var(--depth) * 20%), #0000 ) inset, var(--btn-shadow); - } - --size: calc(var(--size-field, 0.25rem) * 10); - --btn-bg: var(--btn-color, var(--color-base-200)); - --btn-fg: var(--color-base-content); - --btn-border: var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix(in oklab, var(--btn-bg), #000 5%); - } - --btn-shadow: 0 3px 2px -2px var(--btn-bg), - 0 4px 3px -2px var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000), - 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000); - } - } - &:focus { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) 10%, #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - } - &:has( > input[disabled]), &:is(:disabled, [disabled]) { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - &::placeholder { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - } - box-shadow: none; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklch, var(--color-base-content) 20%, #0000); - } - &::file-selector-button { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - --btn-border: #0000; - background-image: none; - --btn-fg: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --btn-fg: color-mix(in oklch, var(--color-base-content) 20%, #0000); - } - } - } - } - } - .textarea { - @layer daisyui.l1.l2.l3 { - min-height: calc(0.25rem * 20); - flex-shrink: 1; - appearance: none; - border-radius: var(--radius-field); - background-color: var(--color-base-100); - padding-inline: calc(0.25rem * 3); - padding-block: calc(0.25rem * 2); - vertical-align: middle; - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - width: clamp(3rem, 20rem, 100%); - font-size: max(var(--font-size, 0rem), var(--font-size-min)); - touch-action: manipulation; - border: var(--border) solid var(--input-color, #0000); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - textarea { - appearance: none; - background-color: transparent; - border: none; - &::placeholder { - color: var(--color-base-content); - opacity: 50%; - } - &:focus, &:focus-within { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - } - &:focus, &:focus-within { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - } - @media (pointer: coarse) { - @supports (-webkit-touch-callout: none) { - &:focus, &:focus-within { - --font-size: 1rem; - } - } - } - &:has( > textarea[disabled]), &:is(:disabled, [disabled]) { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 40%, transparent); - } - box-shadow: none; - &::placeholder, ::placeholder { - color: var(--color-base-content); - opacity: 20%; - } - } - &:has( > textarea[disabled]) > textarea[disabled] { - cursor: not-allowed; - } - } - @layer daisyui.l1.l2 { - --font-size-min: 0.875rem; - .floating-label:has(&) { - --top-mul: 5; - --font-size: 0.875rem; - } - } - } - .stack { - @layer daisyui.l1.l2.l3 { - display: inline-grid; - grid-template-columns: 3px 4px 1fr 4px 3px; - grid-template-rows: 3px 4px 1fr 4px 3px; - & > * { - height: 100%; - width: 100%; - &:nth-child(n + 2) { - width: 100%; - opacity: 70%; - } - &:nth-child(2) { - z-index: 2; - opacity: 90%; - } - &:nth-child(1) { - z-index: 3; - width: 100%; - } - } - } - @layer daisyui.l1.l2 { - &, &.stack-bottom { - > * { - grid-column: 3 / 4; - grid-row: 3 / 6; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 1 / 6; - grid-row: 1 / 4; - } - } - } - &.stack-top { - > * { - grid-column: 3 / 4; - grid-row: 1 / 4; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 1 / 6; - grid-row: 3 / 6; - } - } - } - &.stack-start { - > * { - grid-column: 1 / 4; - grid-row: 3 / 4; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 3 / 6; - grid-row: 1 / 6; - } - } - } - &.stack-end { - > * { - grid-column: 3 / 6; - grid-row: 3 / 4; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 1 / 4; - grid-row: 1 / 6; - } - } - } - } - } - .z-10 { - z-index: 10; - } - .tab-content { - @layer daisyui.l1.l2.l3 { - order: var(--tabcontent-order); - display: none; - border-color: transparent; - --tabcontent-radius-ss: var(--radius-box); - --tabcontent-radius-se: var(--radius-box); - --tabcontent-radius-es: var(--radius-box); - --tabcontent-radius-ee: var(--radius-box); - --tabcontent-order: 1; - width: 100%; - height: calc(100% - var(--tab-height) + var(--border)); - margin: var(--tabcontent-margin); - border-width: var(--border); - border-start-start-radius: var(--tabcontent-radius-ss); - border-start-end-radius: var(--tabcontent-radius-se); - border-end-start-radius: var(--tabcontent-radius-es); - border-end-end-radius: var(--tabcontent-radius-ee); - } - } - .hero { - @layer daisyui.l1.l2.l3 { - display: grid; - width: 100%; - place-items: center; - background-size: cover; - background-position: center; - & > * { - grid-column-start: 1; - grid-row-start: 1; - } - } - } - .stat-value { - @layer daisyui.l1.l2.l3 { - grid-column-start: 1; - white-space: nowrap; - font-size: 2rem; - font-weight: 800; - } - } - .stat-title { - @layer daisyui.l1.l2.l3 { - grid-column-start: 1; - white-space: nowrap; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 60%, transparent); - } - font-size: 0.75rem; - } - } - .container { - width: 100%; - @media (width >= 40rem) { - max-width: 40rem; - } - @media (width >= 48rem) { - max-width: 48rem; - } - @media (width >= 64rem) { - max-width: 64rem; - } - @media (width >= 80rem) { - max-width: 80rem; - } - @media (width >= 96rem) { - max-width: 96rem; - } - } - .divider { - @layer daisyui.l1.l2.l3 { - display: flex; - height: calc(0.25rem * 4); - flex-direction: row; - align-items: center; - align-self: stretch; - white-space: nowrap; - margin: var(--divider-m, 1rem 0); - --divider-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --divider-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - &:before, &:after { - content: ""; - height: calc(0.25rem * 0.5); - width: 100%; - flex-grow: 1; - background-color: var(--divider-color); - } - @media print { - &:before, &:after { - border: 0.5px solid; - } - } - &:not(:empty) { - gap: calc(0.25rem * 4); - } - } - } - .mx-auto { - margin-inline: auto; - } - .my-2 { - margin-block: calc(var(--spacing) * 2); - } - .my-3 { - margin-block: calc(var(--spacing) * 3); - } - .my-4 { - margin-block: calc(var(--spacing) * 4); - } - .label { - @layer daisyui.l1.l2.l3 { - display: inline-flex; - align-items: center; - gap: calc(0.25rem * 1.5); - white-space: nowrap; - color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, currentcolor 60%, transparent); - } - &:has(input) { - cursor: pointer; - } - &:is(.input > *, .select > *) { - display: flex; - height: calc(100% - 0.5rem); - align-items: center; - padding-inline: calc(0.25rem * 3); - white-space: nowrap; - font-size: inherit; - &:first-child { - margin-inline-start: calc(0.25rem * -3); - margin-inline-end: calc(0.25rem * 3); - border-inline-end: var(--border) solid currentColor; - @supports (color: color-mix(in lab, red, red)) { - border-inline-end: var(--border) solid color-mix(in oklab, currentColor 10%, #0000); - } - } - &:last-child { - margin-inline-start: calc(0.25rem * 3); - margin-inline-end: calc(0.25rem * -3); - border-inline-start: var(--border) solid currentColor; - @supports (color: color-mix(in lab, red, red)) { - border-inline-start: var(--border) solid color-mix(in oklab, currentColor 10%, #0000); - } - } - } - } - } - .mt-1 { - margin-top: var(--spacing); - } - .mt-2 { - margin-top: calc(var(--spacing) * 2); - } - .mt-3 { - margin-top: calc(var(--spacing) * 3); - } - .mt-4 { - margin-top: calc(var(--spacing) * 4); - } - .mt-8 { - margin-top: calc(var(--spacing) * 8); - } - .mb-2 { - margin-bottom: calc(var(--spacing) * 2); - } - .mb-4 { - margin-bottom: calc(var(--spacing) * 4); - } - .mb-6 { - margin-bottom: calc(var(--spacing) * 6); - } - .mb-10 { - margin-bottom: calc(var(--spacing) * 10); - } - .ml-2 { - margin-left: calc(var(--spacing) * 2); - } - .status { - @layer daisyui.l1.l2.l3 { - display: inline-block; - aspect-ratio: 1 / 1; - width: calc(0.25rem * 2); - height: calc(0.25rem * 2); - border-radius: var(--radius-selector); - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - background-position: center; - background-repeat: no-repeat; - vertical-align: middle; - color: color-mix(in srgb, #000 30%, transparent); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-black) 30%, transparent); - } - background-image: radial-gradient( circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * 0.5)), #0000 ); - box-shadow: 0 2px 3px -1px currentColor; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000); - } - } - } - .badge { - @layer daisyui.l1.l2.l3 { - display: inline-flex; - align-items: center; - justify-content: center; - gap: calc(0.25rem * 2); - border-radius: var(--radius-selector); - vertical-align: middle; - color: var(--badge-fg); - border: var(--border) solid var(--badge-color, var(--color-base-200)); - font-size: 0.875rem; - width: fit-content; - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - background-color: var(--badge-bg); - --badge-bg: var(--badge-color, var(--color-base-100)); - --badge-fg: var(--color-base-content); - --size: calc(var(--size-selector, 0.25rem) * 6); - height: var(--size); - padding-inline: calc(var(--size) / 2 - var(--border)); - } - } - .tabs { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-wrap: wrap; - --tabs-height: auto; - --tabs-direction: row; - --tab-height: calc(var(--size-field, 0.25rem) * 10); - height: var(--tabs-height); - flex-direction: var(--tabs-direction); - } - } - .stat { - @layer daisyui.l1.l2.l3 { - display: inline-grid; - width: 100%; - column-gap: calc(0.25rem * 4); - padding-inline: calc(0.25rem * 6); - padding-block: calc(0.25rem * 4); - grid-template-columns: repeat(1, 1fr); - &:not(:last-child) { - border-inline-end: var(--border) dashed currentColor; - @supports (color: color-mix(in lab, red, red)) { - border-inline-end: var(--border) dashed color-mix(in oklab, currentColor 10%, #0000); - } - border-block-end: none; - } - } - } - .card-body { - @layer daisyui.l1.l2.l3 { - display: flex; - flex: auto; - flex-direction: column; - gap: calc(0.25rem * 2); - padding: var(--card-p, 1.5rem); - font-size: var(--card-fs, 0.875rem); - } - @layer daisyui.l1.l2.l3.l4 { - p { - flex-grow: 1; - } - } - } - .alert { - border-width: var(--border); - border-color: var(--alert-border-color, var(--color-base-200)); - @layer daisyui.l1.l2.l3 { - border-style: solid; - --alert-border-color: var(--color-base-200); - display: grid; - align-items: center; - gap: calc(0.25rem * 4); - border-radius: var(--radius-box); - padding-inline: calc(0.25rem * 4); - padding-block: calc(0.25rem * 3); - color: var(--color-base-content); - background-color: var(--alert-color, var(--color-base-200)); - justify-content: start; - justify-items: start; - grid-auto-flow: column; - grid-template-columns: auto; - text-align: start; - font-size: 0.875rem; - line-height: 1.25rem; - background-size: auto, calc(var(--noise) * 33%); - background-image: none, var(--fx-noise); - box-shadow: 0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * 0.08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * 0.08)); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * 0.08)) inset, 0 1px color-mix( in oklab, color-mix(in oklab, #000 20%, var(--alert-color, var(--color-base-200))) calc(var(--depth) * 20%), #0000 ), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * 0.08)); - } - &:has(:nth-child(2)) { - grid-template-columns: auto minmax(auto, 1fr); - } - } - } - .card-actions { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-wrap: wrap; - align-items: flex-start; - gap: calc(0.25rem * 2); - } - } - .avatar-placeholder { - @layer daisyui.l1.l2 { - & > div { - display: flex; - align-items: center; - justify-content: center; - } - } - } - .card-title { - @layer daisyui.l1.l2.l3 { - display: flex; - align-items: center; - gap: calc(0.25rem * 2); - font-size: var(--cardtitle-fs, 1.125rem); - font-weight: 600; - } - } - .prose { - :root & { - --tw-prose-body: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-body: color-mix(in oklab, var(--color-base-content) 80%, #0000); - } - --tw-prose-headings: var(--color-base-content); - --tw-prose-lead: var(--color-base-content); - --tw-prose-links: var(--color-base-content); - --tw-prose-bold: var(--color-base-content); - --tw-prose-counters: var(--color-base-content); - --tw-prose-bullets: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-bullets: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-hr: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-hr: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-quotes: var(--color-base-content); - --tw-prose-quote-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-quote-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-captions: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-captions: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-code: var(--color-base-content); - --tw-prose-pre-code: var(--color-neutral-content); - --tw-prose-pre-bg: var(--color-neutral); - --tw-prose-th-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-th-borders: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-td-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-td-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-kbd: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-kbd: color-mix(in oklab, var(--color-base-content) 80%, #0000); - } - :where(code):not(pre > code) { - background-color: var(--color-base-200); - border-radius: var(--radius-selector); - border: var(--border) solid var(--color-base-300); - padding-inline: 0.5em; - padding-block: 0.2em; - font-weight: inherit; - &:before, &:after { - display: none; - } - } - } - } - .block { - display: block; - } - .flex { - display: flex; - } - .grid { - display: grid; - } - .hidden { - display: none; - } - .inline-block { - display: inline-block; - } - .inline-flex { - display: inline-flex; - } - .table { - display: table; - } - .h-16 { - height: calc(var(--spacing) * 16); - } - .min-h-screen { - min-height: 100vh; - } - .w-16 { - width: calc(var(--spacing) * 16); - } - .w-24 { - width: calc(var(--spacing) * 24); - } - .w-60 { - width: calc(var(--spacing) * 60); - } - .w-fit { - width: fit-content; - } - .w-full { - width: 100%; - } - .max-w-none { - max-width: none; - } - .max-w-xl { - max-width: var(--container-xl); - } - .max-w-xs { - max-width: var(--container-xs); - } - .flex-1 { - flex: 1; - } - .flex-shrink { - flex-shrink: 1; - } - .flex-grow { - flex-grow: 1; - } - .grow { - flex-grow: 1; - } - .border-collapse { - border-collapse: collapse; - } - .transform { - transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); - } - .skeleton { - @layer daisyui.l1.l2.l3 { - border-radius: var(--radius-box); - background-color: var(--color-base-300); - @media (prefers-reduced-motion: reduce) { - transition-duration: 15s; - } - will-change: background-position; - background-image: linear-gradient( 105deg, #0000 0% 40%, var(--color-base-100) 50%, #0000 60% 100% ); - background-size: 200% auto; - background-position-x: -50%; - @media (prefers-reduced-motion: no-preference) { - animation: skeleton 1.8s ease-in-out infinite; - } - } - } - .aura-glow { - @layer daisyui.l1.l2 { - animation: none; - background-image: radial-gradient(closest-corner at center, currentColor 0%, transparent 90%); - &:before { - animation: aura-glow var(--tw-duration, 6s) ease-out infinite; - } - &:after { - animation: aura-glow-after var(--tw-duration, 6s) ease-out infinite; - } - } - } - .link { - @layer daisyui.l1.l2.l3 { - cursor: pointer; - text-decoration-line: underline; - &:focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:focus-visible { - outline: 2px solid currentColor; - outline-offset: 2px; - } - } - } - .cursor-pointer { - cursor: pointer; - } - .resize { - resize: both; - } - .flex-col { - flex-direction: column; - } - .flex-row { - flex-direction: row; - } - .flex-wrap { - flex-wrap: wrap; - } - .items-center { - align-items: center; - } - .justify-between { - justify-content: space-between; - } - .justify-center { - justify-content: center; - } - .justify-end { - justify-content: flex-end; - } - .justify-start { - justify-content: flex-start; - } - .gap-1 { - gap: var(--spacing); - } - .gap-2 { - gap: calc(var(--spacing) * 2); - } - .gap-3 { - gap: calc(var(--spacing) * 3); - } - .gap-4 { - gap: calc(var(--spacing) * 4); - } - .space-y-2 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse))); - } - } - .divide-y { - :where(& > :not(:last-child)) { - --tw-divide-y-reverse: 0; - border-bottom-style: var(--tw-border-style); - border-top-style: var(--tw-border-style); - border-top-width: calc(1px * var(--tw-divide-y-reverse)); - border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - } - } - .divide-base-200 { - :where(& > :not(:last-child)) { - border-color: var(--color-base-200); - } - } - .truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .overflow-x-auto { - overflow-x: auto; - } - .rounded-box { - border-radius: var(--radius-box); - } - .rounded-box { - border-radius: var(--radius-box); - } - .rounded-full { - border-radius: calc(infinity * 1px); - } - .border { - border-style: var(--tw-border-style); - border-width: 1px; - } - .border-2 { - border-style: var(--tw-border-style); - border-width: 2px; - } - .border-b { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - } - .badge-ghost { - @layer daisyui.l1.l2 { - border-color: var(--color-base-200); - background-color: var(--color-base-200); - color: var(--color-base-content); - background-image: none; - } - } - .border-base-300 { - border-color: var(--color-base-300); - } - .border-base-content { - border-color: var(--color-base-content); - } - .border-base-content\/20 { - border-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - } - .border-base-content\/70 { - border-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-base-content) 70%, transparent); - } - } - .bg-base-100 { - background-color: var(--color-base-100); - } - .bg-base-200 { - background-color: var(--color-base-200); - } - .bg-primary { - background-color: var(--color-primary); - } - .bg-white { - background-color: var(--color-white); - } - .btn-link { - @layer daisyui { - text-decoration-line: underline; - --btn-bg: #0000; - color: var(--btn-color, var(--color-primary)); - --btn-border: #0000; - background-image: none; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - } - } - .alert-soft { - @layer daisyui.l1 { - color: var(--alert-color, var(--color-base-content)); - background: var(--alert-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - background: color-mix( in oklab, var(--alert-color, var(--color-base-content)) 8%, var(--color-base-100) ); - } - --alert-border-color: var(--alert-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - --alert-border-color: color-mix( - in oklab, - var(--alert-color, var(--color-base-content)) 10%, - var(--color-base-100) - ); - } - box-shadow: none; - background-image: none; - } - } - .btn-outline { - @layer daisyui.l1.l2.l3 { - --btn-bg: #0000; - color: var(--btn-rest-fg, var(--btn-color, var(--color-base-content))); - --btn-border: var(--btn-color, var(--color-base-content)); - --btn-border-style: solid; - background-image: none; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - } - } - .btn-ghost { - @layer daisyui.l1.l2.l3 { - --btn-bg: #0000; - color: var(--btn-rest-fg, var(--btn-color, var(--color-base-content, currentColor))); - --btn-border: #0000; - background-image: none; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - } - } - .object-contain { - object-fit: contain; - } - .p-2 { - padding: calc(var(--spacing) * 2); - } - .p-3 { - padding: calc(var(--spacing) * 3); - } - .p-4 { - padding: calc(var(--spacing) * 4); - } - .px-4 { - padding-inline: calc(var(--spacing) * 4); - } - .px-6 { - padding-inline: calc(var(--spacing) * 6); - } - .py-2 { - padding-block: calc(var(--spacing) * 2); - } - .pt-2 { - padding-top: calc(var(--spacing) * 2); - } - .pt-3 { - padding-top: calc(var(--spacing) * 3); - } - .pb-2 { - padding-bottom: calc(var(--spacing) * 2); - } - .text-center { - text-align: center; - } - .text-right { - text-align: right; - } - .font-mono { - font-family: var(--font-mono); - } - .font-roboto { - font-family: var(--font-roboto); - } - .text-2xl { - font-size: var(--text-2xl); - line-height: var(--tw-leading, var(--text-2xl--line-height)); - } - .text-base { - font-size: var(--text-base); - line-height: var(--tw-leading, var(--text-base--line-height)); - } - .text-lg { - font-size: var(--text-lg); - line-height: var(--tw-leading, var(--text-lg--line-height)); - } - .text-sm { - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - } - .text-xl { - font-size: var(--text-xl); - line-height: var(--tw-leading, var(--text-xl--line-height)); - } - .text-xs { - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - } - .badge-sm { - @layer daisyui.l1.l2 { - --size: calc(var(--size-selector, 0.25rem) * 5); - font-size: 0.75rem; - } - } - .otp-lg { - @layer daisyui.l1.l2 { - font-size: 2rem; - --otp-size: calc(var(--size-field, 0.25rem) * 12); - } - } - .font-bold { - --tw-font-weight: var(--font-weight-bold); - font-weight: var(--font-weight-bold); - } - .font-medium { - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - } - .font-semibold { - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - } - .tracking-wider { - --tw-tracking: var(--tracking-wider); - letter-spacing: var(--tracking-wider); - } - .alert-error { - @layer daisyui.l1.l2 { - color: var(--color-error-content); - --alert-border-color: var(--color-error); - --alert-color: var(--color-error); - } - } - .alert-info { - @layer daisyui.l1.l2 { - color: var(--color-info-content); - --alert-border-color: var(--color-info); - --alert-color: var(--color-info); - } - } - .alert-success { - @layer daisyui.l1.l2 { - color: var(--color-success-content); - --alert-border-color: var(--color-success); - --alert-color: var(--color-success); - } - } - .alert-warning { - @layer daisyui.l1.l2 { - color: var(--color-warning-content); - --alert-border-color: var(--color-warning); - --alert-color: var(--color-warning); - } - } - .text-base-content { - color: var(--color-base-content); - } - .text-base-content\/50 { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 50%, transparent); - } - } - .text-base-content\/70 { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 70%, transparent); - } - } - .text-base-content\/80 { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 80%, transparent); - } - } - .text-error { - color: var(--color-error); - } - .text-primary-content { - color: var(--color-primary-content); - } - .text-sky-500 { - color: var(--color-sky-500); - } - .tabular-nums { - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,); - } - .link-hover { - @layer daisyui.l1.l2 { - text-decoration-line: none; - &:hover { - @media (hover: hover) { - text-decoration-line: underline; - } - } - } - } - .prose { - & :where(a.btn:not(.btn-link)):not(:where([class~="not-prose"], [class~="not-prose"] *)) { - text-decoration-line: none; - } - } - .underline { - text-decoration-line: underline; - } - .opacity-50 { - opacity: 50%; - } - .opacity-60 { - opacity: 60%; - } - .opacity-70 { - opacity: 70%; - } - .shadow { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .shadow-sm { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .shadow-xl { - --tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .outline { - outline-style: var(--tw-outline-style); - outline-width: 1px; - } - .filter { - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); - } - .transition { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .ease-in-out { - --tw-ease: var(--ease-in-out); - transition-timing-function: var(--ease-in-out); - } - .ease-out { - --tw-ease: var(--ease-out); - transition-timing-function: var(--ease-out); - } - .input-lg { - @layer daisyui.l1.l2 { - --in-size-mul: 12; - --font-size-min: 1.125rem; - --spin-my: -3; - .floating-label:has(&) { - --top-mul: 6; - --font-size: 1.125rem; - } - } - } - .btn-neutral { - @layer daisyui.l1.l2 { - --btn-color: var(--color-neutral); - --btn-fg: var(--color-neutral-content); - --btn-soft-bg: var(--color-neutral-content) 80%; - --btn-rest-fg: initial; - } - } - .btn-accent { - @layer daisyui.l1.l2 { - --btn-color: var(--color-accent); - --btn-fg: var(--color-accent-content); - --btn-soft-bg: initial; - } - } - .btn-error { - @layer daisyui.l1.l2 { - --btn-color: var(--color-error); - --btn-fg: var(--color-error-content); - --btn-soft-bg: initial; - } - } - .btn-primary { - @layer daisyui.l1.l2 { - --btn-color: var(--color-primary); - --btn-fg: var(--color-primary-content); - --btn-soft-bg: initial; - } - } - .btn-sm { - @layer daisyui.l1.l2 { - --fontsize: 0.75rem; - --btn-p: 0.75rem; - --size: calc(var(--size-field, 0.25rem) * 8); - } - } - .btn-success { - @layer daisyui.l1.l2 { - --btn-color: var(--color-success); - --btn-fg: var(--color-success-content); - --btn-soft-bg: initial; - } - } - .btn-warning { - @layer daisyui.l1.l2 { - --btn-color: var(--color-warning); - --btn-fg: var(--color-warning-content); - --btn-soft-bg: initial; - } - } - .btn-xs { - @layer daisyui.l1.l2 { - --fontsize: 0.6875rem; - --btn-p: 0.5rem; - --size: calc(var(--size-field, 0.25rem) * 6); - } - } - .badge-error { - @layer daisyui.l1.l2 { - --badge-color: var(--color-error); - --badge-fg: var(--color-error-content); - } - } - .badge-neutral { - @layer daisyui.l1.l2 { - --badge-color: var(--color-neutral); - --badge-fg: var(--color-neutral-content); - } - } - .badge-success { - @layer daisyui.l1.l2 { - --badge-color: var(--color-success); - --badge-fg: var(--color-success-content); - } - } - .badge-warning { - @layer daisyui.l1.l2 { - --badge-color: var(--color-warning); - --badge-fg: var(--color-warning-content); - } - } - .input-error { - @layer daisyui.l1.l2 { - &, &:focus, &:focus-within { - --input-color: var(--color-error); - } - } - } - .md\:grid-cols-2 { - @media (width >= 48rem) { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url("../fonts/ubuntu-latin-400-normal.woff2") format("woff2"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url("../fonts/ubuntu-latin-ext-400-normal.woff2") format("woff2"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "Ubuntu"; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url("../fonts/ubuntu-latin-400-italic.woff2") format("woff2"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Ubuntu"; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url("../fonts/ubuntu-latin-ext-400-italic.woff2") format("woff2"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url("../fonts/ubuntu-latin-500-normal.woff2") format("woff2"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url("../fonts/ubuntu-latin-ext-500-normal.woff2") format("woff2"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url("../fonts/ubuntu-latin-700-normal.woff2") format("woff2"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url("../fonts/ubuntu-latin-ext-700-normal.woff2") format("woff2"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "JetBrains Mono"; - font-style: normal; - font-weight: 100 800; - font-display: swap; - src: url("../fonts/jetbrains-mono-latin-wght-normal.woff2") format("woff2-variations"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "JetBrains Mono"; - font-style: normal; - font-weight: 100 800; - font-display: swap; - src: url("../fonts/jetbrains-mono-latin-ext-wght-normal.woff2") format("woff2-variations"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "Roboto"; - font-style: normal; - font-weight: 100 900; - font-display: swap; - src: url("../fonts/roboto-latin-standard-normal.woff2") format("woff2-variations"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Roboto"; - font-style: normal; - font-weight: 100 900; - font-display: swap; - src: url("../fonts/roboto-latin-ext-standard-normal.woff2") format("woff2-variations"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "Tourney"; - font-style: normal; - font-weight: 100 900; - font-stretch: 75% 125%; - font-display: swap; - src: url("../fonts/tourney-latin-standard-normal.woff2") format("woff2-variations"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Tourney"; - font-style: normal; - font-weight: 100 900; - font-stretch: 75% 125%; - font-display: swap; - src: url("../fonts/tourney-latin-ext-standard-normal.woff2") format("woff2-variations"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -.logo { - background-image: var(--logo-dark); - background-position: center; - background-repeat: no-repeat; - background-size: contain; -} -@media (prefers-color-scheme: dark) { - .logo { - background-image: var(--logo-light); - } -} -[data-theme="light"] .logo { - background-image: var(--logo-dark); -} -[data-theme="dark"] .logo { - background-image: var(--logo-light); -} -@layer base { - :where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] { - color-scheme: light; - --color-base-100: oklch(100% 0 0); - --color-base-200: oklch(98% 0 0); - --color-base-300: oklch(95% 0 0); - --color-base-content: oklch(21% 0.006 285.885); - --color-primary: oklch(45% 0.24 277.023); - --color-primary-content: oklch(93% 0.034 272.788); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } -} -@layer base { - @media (prefers-color-scheme: dark) { - :root:not([data-theme]) { - color-scheme: dark; - --color-base-100: oklch(25.33% 0.016 252.42); - --color-base-200: oklch(23.26% 0.014 253.1); - --color-base-300: oklch(21.15% 0.012 254.09); - --color-base-content: oklch(97.807% 0.029 256.847); - --color-primary: oklch(58% 0.233 277.117); - --color-primary-content: oklch(96% 0.018 272.314); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } - } -} -@layer base { - :root:has(input.theme-controller[value=light]:checked),[data-theme=light] { - color-scheme: light; - --color-base-100: oklch(100% 0 0); - --color-base-200: oklch(98% 0 0); - --color-base-300: oklch(95% 0 0); - --color-base-content: oklch(21% 0.006 285.885); - --color-primary: oklch(45% 0.24 277.023); - --color-primary-content: oklch(93% 0.034 272.788); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } -} -@layer base { - :root:has(input.theme-controller[value=dark]:checked),[data-theme=dark] { - color-scheme: dark; - --color-base-100: oklch(25.33% 0.016 252.42); - --color-base-200: oklch(23.26% 0.014 253.1); - --color-base-300: oklch(21.15% 0.012 254.09); - --color-base-content: oklch(97.807% 0.029 256.847); - --color-primary: oklch(58% 0.233 277.117); - --color-primary-content: oklch(96% 0.018 272.314); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } -} -@layer base { - @property --radialprogress { - syntax: ""; - inherits: true; - initial-value: 0%; - } - @property --aura-angle { - syntax: ""; - inherits: false; - initial-value: 0deg; - } -} -@layer base { - :root { - scrollbar-color: currentColor #0000; - @supports (color: color-mix(in lab, red, red)) { - scrollbar-color: color-mix(in oklch, currentColor 35%, #0000) #0000; - } - } -} -@layer base { - :root { - --fx-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E"); - } -} -@layer base { - :root { - --page-scroll-lock: initial; - --page-overflow: var(--page-scroll-lock) hidden; - } - :root:not(span) { - overflow: var(--page-overflow); - } -} -@layer base { - :root { - --page-has-backdrop: var(--page-scroll-lock) 1; - --page-scroll-bg: var(--page-scroll-lock) - var(--root-bg, #0000); - @supports (color: color-mix(in lab, red, red)) { - --page-scroll-bg: var(--page-scroll-lock) - color-mix(in srgb, var(--root-bg, #0000), oklch(0% 0 0) calc(var(--page-has-backdrop, 0) * 40%)); - } - background-image: var(--page-scroll-lock) linear-gradient(var(--root-bg, #0000), var(--root-bg, #0000)); - transition: var(--page-scroll-lock) background-color 0.3s ease-out; - animation: var(--page-scroll-lock) set-page-has-scroll forwards; - animation-timeline: var(--page-scroll-lock) scroll(); - --page-has-scroll: initial; - scrollbar-gutter: var(--page-has-scroll) var(--page-scroll-lock) stable; - } - @keyframes set-page-has-scroll { - 0%, to { - --page-has-scroll: ; - } - } -} -@layer base { - :root, [data-theme] { - background-color: var(--root-bg); - color: var(--color-base-content); - } - :root { - background-color: var(--page-scroll-bg, var(--root-bg)); - } - :where(:root, [data-theme]) { - --root-bg: var(--color-base-100); - } -} -@keyframes dropdown { - 0% { - opacity: 0; - } -} -@keyframes rotator { - 89.9999%, 100% { - --first-item-position: 0 0%; - } - 90%, 99.9999% { - --first-item-position: 0 calc(var(--items) * 100%); - } - 100% { - translate: 0 -100%; - } -} -@keyframes radio { - 0% { - padding: 5px; - } - 50% { - padding: 3px; - } -} -@keyframes toast { - 0% { - scale: 0.9; - opacity: 0; - } - 100% { - scale: 1; - opacity: 1; - } -} -@keyframes skeleton { - 0% { - background-position: 150%; - } - 100% { - background-position: -50%; - } -} -@keyframes progress { - 50% { - background-position-x: -115%; - } -} -@keyframes rating { - 0%, 40% { - scale: 1.1; - filter: brightness(1.05) contrast(1.05); - } -} -@keyframes aura { - to { - --aura-angle: 360deg; - transform: translateZ(1px); - } -} -@keyframes aura-glow { - 20%, 80% { - opacity: 0.7; - filter: blur(0.25rem); - } - 50% { - opacity: 1; - filter: blur(0.75rem); - } -} -@keyframes aura-glow-after { - 20%, 80% { - opacity: 0.3; - filter: blur(1rem); - } - 50% { - opacity: 0.6; - filter: blur(1.5rem); - } -} -@keyframes menu { - 0% { - opacity: 0; - } -} -@property --tw-rotate-x { - syntax: "*"; - inherits: false; -} -@property --tw-rotate-y { - syntax: "*"; - inherits: false; -} -@property --tw-rotate-z { - syntax: "*"; - inherits: false; -} -@property --tw-skew-x { - syntax: "*"; - inherits: false; -} -@property --tw-skew-y { - syntax: "*"; - inherits: false; -} -@property --tw-space-y-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-divide-y-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-border-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} -@property --tw-font-weight { - syntax: "*"; - inherits: false; -} -@property --tw-tracking { - syntax: "*"; - inherits: false; -} -@property --tw-ordinal { - syntax: "*"; - inherits: false; -} -@property --tw-slashed-zero { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-figure { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-spacing { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-fraction { - syntax: "*"; - inherits: false; -} -@property --tw-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-inset-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-inset-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-inset-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-ring-color { - syntax: "*"; - inherits: false; -} -@property --tw-ring-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-inset-ring-color { - syntax: "*"; - inherits: false; -} -@property --tw-inset-ring-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-ring-inset { - syntax: "*"; - inherits: false; -} -@property --tw-ring-offset-width { - syntax: ""; - inherits: false; - initial-value: 0px; -} -@property --tw-ring-offset-color { - syntax: "*"; - inherits: false; - initial-value: #fff; -} -@property --tw-ring-offset-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-outline-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} -@property --tw-blur { - syntax: "*"; - inherits: false; -} -@property --tw-brightness { - syntax: "*"; - inherits: false; -} -@property --tw-contrast { - syntax: "*"; - inherits: false; -} -@property --tw-grayscale { - syntax: "*"; - inherits: false; -} -@property --tw-hue-rotate { - syntax: "*"; - inherits: false; -} -@property --tw-invert { - syntax: "*"; - inherits: false; -} -@property --tw-opacity { - syntax: "*"; - inherits: false; -} -@property --tw-saturate { - syntax: "*"; - inherits: false; -} -@property --tw-sepia { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-drop-shadow-size { - syntax: "*"; - inherits: false; -} -@property --tw-ease { - syntax: "*"; - inherits: false; -} -@layer properties { - @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { - *, ::before, ::after, ::backdrop { - --tw-rotate-x: initial; - --tw-rotate-y: initial; - --tw-rotate-z: initial; - --tw-skew-x: initial; - --tw-skew-y: initial; - --tw-space-y-reverse: 0; - --tw-divide-y-reverse: 0; - --tw-border-style: solid; - --tw-font-weight: initial; - --tw-tracking: initial; - --tw-ordinal: initial; - --tw-slashed-zero: initial; - --tw-numeric-figure: initial; - --tw-numeric-spacing: initial; - --tw-numeric-fraction: initial; - --tw-shadow: 0 0 #0000; - --tw-shadow-color: initial; - --tw-shadow-alpha: 100%; - --tw-inset-shadow: 0 0 #0000; - --tw-inset-shadow-color: initial; - --tw-inset-shadow-alpha: 100%; - --tw-ring-color: initial; - --tw-ring-shadow: 0 0 #0000; - --tw-inset-ring-color: initial; - --tw-inset-ring-shadow: 0 0 #0000; - --tw-ring-inset: initial; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-offset-shadow: 0 0 #0000; - --tw-outline-style: solid; - --tw-blur: initial; - --tw-brightness: initial; - --tw-contrast: initial; - --tw-grayscale: initial; - --tw-hue-rotate: initial; - --tw-invert: initial; - --tw-opacity: initial; - --tw-saturate: initial; - --tw-sepia: initial; - --tw-drop-shadow: initial; - --tw-drop-shadow-color: initial; - --tw-drop-shadow-alpha: 100%; - --tw-drop-shadow-size: initial; - --tw-ease: initial; - } - } -} +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:"Ubuntu", ui-sans-serif, system-ui, sans-serif;--font-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, monospace;--color-sky-500:oklch(68.5% .169 237.323);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-roboto:"Roboto", ui-sans-serif, system-ui, sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax:"";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tab:is(.tabs>.tab){cursor:pointer;appearance:none;text-align:center;webkit-user-select:none;-webkit-user-select:none;user-select:none;flex-wrap:wrap;justify-content:center;align-items:center;display:inline-flex;position:relative}@media (hover:hover){.tab:is(.tabs>.tab):hover{color:var(--color-base-content)}}.tab:is(.tabs>.tab){--tab-p:.75rem;--tab-bg:var(--color-base-100);--tab-border-color:var(--color-base-300);--tab-radius-ss:0;--tab-radius-se:0;--tab-radius-es:0;--tab-radius-ee:0;--tab-order:0;--tab-radius-min:calc(.75rem - var(--border));--tab-radius-limit:min(var(--radius-field), var(--tab-radius-min));--tab-radius-grad:#0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + .25px), var(--tab-border-color) 69%, var(--tab-bg) calc(69% + .25px);order:var(--tab-order);height:var(--tab-height);padding-inline:var(--tab-p);border-color:#0000;font-size:.875rem}.tab:is(.tabs>.tab):is(input[type=radio]){min-width:fit-content}.tab:is(.tabs>.tab):is(input[type=radio]):after{--tw-content:attr(aria-label);content:var(--tw-content)}.tab:is(.tabs>.tab):is(label){position:relative}.tab:is(.tabs>.tab):is(label) input{cursor:pointer;appearance:none;opacity:0;position:absolute;inset:0}:is(.tab:is(.tabs>.tab):checked,.tab:is(.tabs>.tab):is(label:has(:checked)),.tab:is(.tabs>.tab):is(.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]))+.tab-content{display:block}.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.tab:is(.tabs>.tab):not(:checked,label:has(:checked),:hover,.tab-active,[aria-selected=true],[aria-current=true],[aria-current=page]){color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.tab:is(.tabs>.tab):not(input):empty{cursor:default;flex-grow:1}.tab:is(.tabs>.tab):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.tab:is(.tabs>.tab):focus{outline-offset:2px;outline:2px solid #0000}}.tab:is(.tabs>.tab):focus-visible,.tab:is(.tabs>.tab):is(label:has(:checked:focus-visible)){outline-offset:-5px;outline:2px solid}.tab:is(.tabs>.tab)[disabled]{pointer-events:none;opacity:.4}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.otp{font-family:var(--font-mono);clip-path:inset(-3.5px 3.5px -3.5px -3.5px);border-radius:var(--radius-field);gap:var(--otp-gap);--input-color:var(--color-base-content);font-size:1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.otp{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.otp{--otp-ch:1ch}@supports (font:-apple-system-body){.otp{--otp-ch:.618164em}}.otp{--otp-gap:calc(var(--otp-ch) * .5);--otp-w:calc(var(--otp-ch) * 2);--otp-size:calc(var(--size-field,.25rem) * 10);--stride:calc(var(--otp-w) + var(--otp-gap))}.otp>input{pointer-events:none;z-index:1;appearance:none;border-style:var(--tw-border-style);outline-style:var(--tw-outline-style);field-sizing:content;letter-spacing:calc(var(--stride) - var(--otp-ch));font-variant-numeric:tabular-nums;background-color:#0000;border-width:0;outline-width:0;margin:0;padding:0;padding-inline-start:calc(var(--otp-ch) * .5 + var(--border));line-height:1;inset-inline-start:0}.otp>input::selection{color:var(--color-base-content);background-color:#0000}@supports (color:color-mix(in lab, red, red)){.otp>input::selection{color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.otp>input:valid{caret-color:#0000}.otp:has(>span:first-child){width:calc(var(--stride) * 1)}.otp:has(>span:nth-child(2)){width:calc(var(--stride) * 2)}.otp:has(>span:nth-child(3)){width:calc(var(--stride) * 3)}.otp:has(>span:nth-child(4)){width:calc(var(--stride) * 4)}.otp:has(>span:nth-child(5)){width:calc(var(--stride) * 5)}.otp:has(>span:nth-child(6)){width:calc(var(--stride) * 6)}.otp:has(>span:nth-child(7)){width:calc(var(--stride) * 7)}.otp:has(>span:nth-child(8)){width:calc(var(--stride) * 8)}.otp>span{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));inline-size:var(--otp-w);block-size:var(--otp-size);background-color:var(--color-base-100);border:var(--border) solid var(--input-color);border-radius:inherit;outline-offset:1px;box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;outline:2px solid #0000;transition:border-color .2s;display:flex;position:absolute}@supports (color:color-mix(in lab, red, red)){.otp>span{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.otp>span:first-child{left:1px}@supports (font:-apple-system-body){.otp>span:first-child{left:2px}}@supports ((-moz-appearance:none)){.otp>span:first-child{left:0}}.otp>span:nth-child(2){left:calc(var(--stride) * 1);transition-delay:20ms}.otp>span:nth-child(3){left:calc(var(--stride) * 2);transition-delay:40ms}.otp>span:nth-child(4){left:calc(var(--stride) * 3);transition-delay:60ms}.otp>span:nth-child(5){left:calc(var(--stride) * 4);transition-delay:80ms}.otp>span:nth-child(6){left:calc(var(--stride) * 5);transition-delay:.1s}.otp>span:nth-child(7){left:calc(var(--stride) * 6);transition-delay:.12s}.otp>span:nth-child(8){left:calc(var(--stride) * 7);transition-delay:.14s}.otp:has(input:valid:focus)>span{outline:2px solid var(--input-color);outline-offset:1px}.otp:has(input:valid:focus):after{opacity:0}.otp:after{content:"";width:var(--otp-w);height:var(--otp-size);border-radius:var(--radius-field);outline-offset:1px;z-index:10;outline:2px solid #0000;flex-shrink:0;margin-inline-start:calc(-1 * (var(--otp-gap) + var(--otp-ch) * .5 + var(--border)))}.otp:focus-within{isolation:isolate;--input-color:var(--color-base-content)}.otp:focus-within>span{box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.otp:focus-within>span{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.otp:focus-within:after{outline-color:var(--input-color)}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.avatar{vertical-align:middle;display:inline-flex;position:relative}.avatar>div{aspect-ratio:1;display:block;overflow:hidden}.avatar img{object-fit:cover;width:100%;height:100%}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.file-input{cursor:pointer;cursor:pointer;border:var(--border) solid #0000;appearance:none;background-color:var(--color-base-100);vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;width:clamp(3rem,20rem,100%);height:var(--size);border-color:var(--input-color);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));align-items:center;padding-inline-end:.75rem;font-size:.875rem;line-height:2;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.file-input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.file-input{--size:calc(var(--size-field,.25rem) * 10);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.file-input::file-selector-button{cursor:pointer;webkit-user-select:none;-webkit-user-select:none;user-select:none;height:calc(100% + var(--border) * 2);margin-inline-end:1rem;margin-block:calc(var(--border) * -1);color:var(--btn-fg);border-width:var(--border);border-style:solid;border-color:var(--btn-border);background-color:var(--btn-bg);background-size:calc(var(--noise) * 100%);background-image:var(--fx-noise);text-shadow:0 .5px oklch(1 0 0 / calc(var(--depth) * .15));box-shadow:0 .5px 0 .5px white inset, var(--btn-shadow);border-start-start-radius:calc(var(--join-ss,var(--radius-field) - var(--border)));border-end-start-radius:calc(var(--join-es,var(--radius-field) - var(--border)));margin-inline-start:calc(var(--border) * -1);padding-inline:1rem;font-size:.875rem;font-weight:600}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{box-shadow:0 .5px 0 .5px color-mix(in oklab, color-mix(in oklab, white 30%, var(--btn-bg)) calc(var(--depth) * 20%), #0000) inset, var(--btn-shadow)}}.file-input::file-selector-button{--size:calc(var(--size-field,.25rem) * 10);--btn-bg:var(--btn-color,var(--color-base-200));--btn-fg:var(--color-base-content);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-border:color-mix(in oklab, var(--btn-bg), #000 5%)}}.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000)}}.file-input:focus{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.file-input:focus{box-shadow:0 1px color-mix(in oklab, var(--input-color) 10%, #0000)}}.file-input:focus{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){box-shadow:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);--btn-border:#0000;--btn-fg:var(--color-base-content);background-image:none}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{--btn-fg:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.tabs{--tabs-height:auto;--tabs-direction:row;--tab-height:calc(var(--size-field,.25rem) * 10);height:var(--tabs-height);flex-wrap:wrap;flex-direction:var(--tabs-direction);display:flex}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-outline{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content)));--btn-border:var(--btn-color,var(--color-base-content));--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}}@layer daisyui.l1.l2{.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}.avatar-placeholder>div{justify-content:center;align-items:center;display:flex}.badge-ghost{border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content);background-image:none}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.otp-lg{--otp-size:calc(var(--size-field,.25rem) * 12);font-size:2rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.input-lg{--in-size-mul:12;--font-size-min:1.125rem;--spin-my:-3}.floating-label:has(.input-lg){--top-mul:6;--font-size:1.125rem}.btn-neutral{--btn-color:var(--color-neutral);--btn-fg:var(--color-neutral-content);--btn-soft-bg:var(--color-neutral-content) 80%;--btn-rest-fg:initial}.btn-accent{--btn-color:var(--color-accent);--btn-fg:var(--color-accent-content);--btn-soft-bg:initial}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-error{--badge-color:var(--color-error);--badge-fg:var(--color-error-content)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-success{--badge-color:var(--color-success);--badge-fg:var(--color-success-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}.input-error,.input-error:focus,.input-error:focus-within{--input-color:var(--color-error)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}.alert-soft{color:var(--alert-color,var(--color-base-content));background:var(--alert-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.alert-soft{background:color-mix(in oklab, var(--alert-color,var(--color-base-content)) 8%, var(--color-base-100))}}.alert-soft{--alert-border-color:var(--alert-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.alert-soft{--alert-border-color:color-mix(in oklab, var(--alert-color,var(--color-base-content)) 10%, var(--color-base-100))}}.alert-soft{box-shadow:none;background-image:none}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.fixed{position:fixed}.static{position:static}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-16{height:calc(var(--spacing) * 16)}.min-h-screen{min-height:100vh}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-60{width:calc(var(--spacing) * 60)}.w-fit{width:fit-content}.w-full{width:100%}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.grow{flex-grow:1}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-base-300{border-color:var(--color-base-300)}.border-base-content,.border-base-content\/20{border-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.border-base-content\/20{border-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.bg-primary{background-color:var(--color-primary)}.bg-white{background-color:var(--color-white)}.object-contain{object-fit:contain}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-2{padding-block:calc(var(--spacing) * 2)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-roboto{font-family:var(--font-roboto)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-base-content\/50{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/50{color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.text-primary-content{color:var(--color-primary-content)}.text-sky-500{color:var(--color-sky-500)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-italic.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-italic.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-ext-500-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-ext-700-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}.logo{background-image:var(--logo-dark);background-position:50%;background-repeat:no-repeat;background-size:contain}@media (prefers-color-scheme:dark){.logo{background-image:var(--logo-light)}}[data-theme=light] .logo{background-image:var(--logo-dark)}[data-theme=dark] .logo{background-image:var(--logo-light)}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file diff --git a/templates/account/logout.html b/templates/account/logout.html index a1f3c72..0deb535 100644 --- a/templates/account/logout.html +++ b/templates/account/logout.html @@ -26,7 +26,7 @@ {{ redirect_field }} diff --git a/templates/mfa/authenticate.html b/templates/mfa/authenticate.html index 574768a..d8650fa 100644 --- a/templates/mfa/authenticate.html +++ b/templates/mfa/authenticate.html @@ -33,7 +33,7 @@
- +
From bf86654e724be8697355a090fc961fbbdeb4e0a7 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Mon, 13 Jul 2026 23:53:35 +0200 Subject: [PATCH 053/137] Give every auth button an icon, and lay out the password and MFA screens The button element now takes an `icon`, so a page gets one by passing icon="name" rather than by hand-rolling its own button markup. Every button on the account and MFA screens carries one; a test walks each page and asserts no button is left bare. Change password: labels dropped (allauth already sets a placeholder on each field), the current password set apart from the new pair, help text kept on the new password, and Forgot Password promoted from a bare link to an accent button. MFA management: recovery-code actions are now ranked -- View is primary, Download and Generate are outline. Generate silently invalidates the codes you already hold, so it must not read as the obvious thing to click. Panel actions get breathing room from the body text (card-actions mt-4). Viewing recovery codes: Download and Generate sit side by side instead of stacking. TOTP activate: the code box loses its heading -- an otp field never takes a visible label, the boxes say what they are -- and the authenticator secret gets margin around it, since it is copied out by hand. Co-Authored-By: Claude Opus 4.8 --- authentication/tests.py | 80 + static/css/app.css | 4135 ++++++++++++++++- templates/_base.html | 2 +- templates/account/password_change.html | 40 + templates/allauth/elements/button.html | 5 +- templates/allauth/elements/field.html | 2 +- templates/allauth/elements/fields.html | 6 +- templates/allauth/elements/panel.html | 5 +- templates/mfa/index.html | 128 + templates/mfa/recovery_codes/generate.html | 35 + templates/mfa/recovery_codes/index.html | 65 + templates/mfa/totp/activate_form.html | 45 + templates/mfa/totp/deactivate_form.html | 30 + templates/mfa/webauthn/add_form.html | 39 + .../authenticator_confirm_delete.html | 25 + .../mfa/webauthn/authenticator_list.html | 72 + 16 files changed, 4705 insertions(+), 9 deletions(-) create mode 100644 templates/account/password_change.html create mode 100644 templates/mfa/index.html create mode 100644 templates/mfa/recovery_codes/generate.html create mode 100644 templates/mfa/recovery_codes/index.html create mode 100644 templates/mfa/totp/activate_form.html create mode 100644 templates/mfa/totp/deactivate_form.html create mode 100644 templates/mfa/webauthn/add_form.html create mode 100644 templates/mfa/webauthn/authenticator_confirm_delete.html create mode 100644 templates/mfa/webauthn/authenticator_list.html diff --git a/authentication/tests.py b/authentication/tests.py index 14d54ff..799a18a 100644 --- a/authentication/tests.py +++ b/authentication/tests.py @@ -1,8 +1,10 @@ +import re import uuid from urllib.parse import parse_qs, urlparse from allauth.core import context from allauth.mfa.models import Authenticator +from allauth.mfa.recovery_codes.internal.auth import RecoveryCodes from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser from django.db import IntegrityError @@ -388,3 +390,81 @@ class SignOutPageTests(TestCase): self.client.post(reverse("account_logout")) self.assertIsNone(self.client.session.get("_auth_user_id")) + + +class ChangePasswordPageTests(TestCase): + def setUp(self): + User.objects.create_user(email="mfa@example.com", password="pw-secret-123") + self.client.post(reverse("account_login"), {"login": "mfa@example.com", "password": "pw-secret-123"}, follow=True) + self.response = self.client.get(reverse("account_change_password")) + + def test_the_fields_have_no_visible_labels(self): + # allauth gives each a placeholder, so the label would only repeat it. + self.assertNotContains(self.response, 'Current Password') + self.assertContains(self.response, 'name="oldpassword"') + self.assertContains(self.response, 'name="password1"') + + def test_the_new_password_keeps_its_help_text(self): + self.assertContains(self.response, "id_password1_helptext") + + def test_the_current_password_is_set_apart_from_the_new_one(self): + self.assertContains(self.response, "mt-10") + + def test_forgot_password_is_an_accent_button_and_both_actions_have_icons(self): + html = self.response.content.decode() + forgot = html[html.index('class="btn btn-accent gap-2"') :] + submit = html[html.index('class="btn btn-primary gap-2"') :] + + self.assertIn("")]) + self.assertIn("")]) + + +class MfaButtonIconTests(TestCase): + """Every button on the MFA screens carries an icon, and the recovery-code actions are + ranked: View is primary, Download and Generate are outline. Generate throws away the + codes you already have, so it must not read as the obvious thing to click.""" + + def setUp(self): + self.user = User.objects.create_user(email="mfa@example.com", password="pw-secret-123") + # Sign in *before* enrolling: a user who already holds a second factor is stopped at + # the 2FA challenge and never reaches these pages. + self.client.post(reverse("account_login"), {"login": "mfa@example.com", "password": "pw-secret-123"}, follow=True) + enrol_mfa(self.user) + RecoveryCodes.activate(self.user).instance.save() + + def buttons(self, url): + """Every / +
+ +{% endblock content %} diff --git a/templates/allauth/elements/button.html b/templates/allauth/elements/button.html index 8e24b3f..5c648a6 100644 --- a/templates/allauth/elements/button.html +++ b/templates/allauth/elements/button.html @@ -1,4 +1,4 @@ -{% load allauth %} +{% load allauth lucide %} {% comment %} allauth tags every button it renders, and mapping those tags is what styles the MFA @@ -9,13 +9,14 @@ {% endcomment %} {% comment %} djlint:off {% endcomment %} <{% if attrs.href %}a href="{{ attrs.href }}"{% else %}button{% endif %} -class="btn {% if attrs.tags and 'danger' in attrs.tags %}btn-error{% elif attrs.tags and 'link' in attrs.tags %}btn-link{% elif attrs.tags and 'secondary' in attrs.tags %}btn-outline btn-neutral{% elif attrs.tags and 'outline' in attrs.tags %}btn-outline btn-primary{% else %}btn-primary{% endif %}" +class="btn gap-2 {% if attrs.tags and 'danger' in attrs.tags %}btn-error{% elif attrs.tags and 'link' in attrs.tags %}btn-link{% elif attrs.tags and 'secondary' in attrs.tags %}btn-outline btn-neutral{% elif attrs.tags and 'outline' in attrs.tags %}btn-outline btn-primary{% else %}btn-primary{% endif %}" {% if attrs.form %}form="{{ attrs.form }}"{% endif %} {% if attrs.id %}id="{{ attrs.id }}"{% endif %} {% if attrs.name %}name="{{ attrs.name }}"{% endif %} {% if attrs.value %}value="{{ attrs.value }}"{% endif %} {% if attrs.type %}type="{{ attrs.type }}"{% endif %} > +{% if attrs.icon %}{% lucide attrs.icon size=16 %}{% endif %} {% slot %} {% endslot %} diff --git a/templates/allauth/elements/field.html b/templates/allauth/elements/field.html index 19aa365..5e6bd51 100644 --- a/templates/allauth/elements/field.html +++ b/templates/allauth/elements/field.html @@ -6,7 +6,7 @@ character by character and must not misread. {% endcomment %} {{ attrs.errors }} -
+
{% if slots.label %} {% else %} - {% if not attrs.unlabeled %} + {# An otp box never takes a visible label — the boxes say what they are, and a + heading above them just crowds the field. It keeps an sr-only one below. #} + {% if not attrs.unlabeled and not field|is_otp %} @@ -53,7 +55,7 @@ For the same reason the sr-only label lives outside the container: another child would throw the count off again. {% endcomment %} - {% if attrs.unlabeled %}{% endif %} +
diff --git a/templates/allauth/elements/panel.html b/templates/allauth/elements/panel.html index f295669..44c3931 100644 --- a/templates/allauth/elements/panel.html +++ b/templates/allauth/elements/panel.html @@ -1,8 +1,9 @@ {% load allauth %} -
+
{% if slots.title %}

{% slot title %}{% endslot %}

{% endif %}
{% slot body %}{% endslot %}
- {% if slots.actions %}
{% slot actions %}{% endslot %}
{% endif %} + {% if slots.actions %} +
{% slot actions %}{% endslot %}
{% endif %}
diff --git a/templates/mfa/index.html b/templates/mfa/index.html new file mode 100644 index 0000000..704b91b --- /dev/null +++ b/templates/mfa/index.html @@ -0,0 +1,128 @@ +{% extends "mfa/base_manage.html" %} +{% load allauth i18n %} + +{% comment %} + allauth's own index, with an icon on every button and the recovery-code actions given + the ranking they deserve: View is the primary action, Download and Generate are outline + (Generate silently invalidates the codes you already have, so it must not look like the + obvious thing to click). +{% endcomment %} + +{% block head_title %} + {% trans "Two-Factor Authentication" %} +{% endblock head_title %} + +{% block content %} + {% element h1 tags="mfa,index" %} + {% trans "Two-Factor Authentication" %} + {% endelement %} + + {% if "totp" in MFA_SUPPORTED_TYPES %} + {% element panel %} + {% slot title %} + {% translate "Authenticator App" %} + {% endslot %} + {% slot body %} + {% if authenticators.totp %} + {% element p %} + {% translate "Authentication using an authenticator app is active." %} + {% endelement %} + {% else %} + {% element p %} + {% translate "An authenticator app is not active." %} + {% endelement %} + {% endif %} + {% endslot %} + {% slot actions %} + {% url 'mfa_deactivate_totp' as deactivate_url %} + {% url 'mfa_activate_totp' as activate_url %} + {% if authenticators.totp %} + {% element button href=deactivate_url icon="power" tags="danger,delete,panel" %} + {% translate "Deactivate" %} + {% endelement %} + {% else %} + {% element button href=activate_url icon="smartphone" tags="panel" %} + {% translate "Activate" %} + {% endelement %} + {% endif %} + {% endslot %} + {% endelement %} + {% endif %} + + {% if "webauthn" in MFA_SUPPORTED_TYPES %} + {% element panel %} + {% slot title %} + {% translate "Security Keys" %} + {% endslot %} + {% slot body %} + {% if authenticators.webauthn|length %} + {% element p %} + {% blocktranslate count count=authenticators.webauthn|length %}You have added {{ count }} security key.{% plural %}You have added {{ count }} security keys.{% endblocktranslate %} + {% endelement %} + {% else %} + {% element p %} + {% translate "No security keys have been added." %} + {% endelement %} + {% endif %} + {% endslot %} + {% slot actions %} + {% if authenticators.webauthn|length %} + {% url 'mfa_list_webauthn' as webauthn_list_url %} + {% element button href=webauthn_list_url icon="settings" %} + {% translate "Manage" %} + {% endelement %} + {% else %} + {% url 'mfa_add_webauthn' as webauthn_add_url %} + {% element button href=webauthn_add_url icon="usb" %} + {% translate "Add" %} + {% endelement %} + {% endif %} + {% endslot %} + {% endelement %} + {% endif %} + + {% if "recovery_codes" in MFA_SUPPORTED_TYPES %} + {% with total_count=authenticators.recovery_codes.generate_codes|length unused_count=authenticators.recovery_codes.get_unused_codes|length %} + {% element panel %} + {% slot title %} + {% translate "Recovery Codes" %} + {% endslot %} + {% slot body %} + {% if authenticators.recovery_codes %} + {% element p %} + {% blocktranslate count unused_count=unused_count %}There is {{ unused_count }} out of {{ total_count }} recovery codes available.{% plural %}There are {{ unused_count }} out of {{ total_count }} recovery codes available.{% endblocktranslate %} + {% endelement %} + {% else %} + {% element p %} + {% translate "No recovery codes set up." %} + {% endelement %} + {% endif %} + {% endslot %} + {% if is_mfa_enabled %} + {% if authenticators.recovery_codes %} + {% if unused_count > 0 and not MFA_RECOVERY_CODES_SHOW_ONCE %} + {% slot actions %} + {% url 'mfa_view_recovery_codes' as view_url %} + {% element button href=view_url icon="eye" tags="panel" %} + {% translate "View" %} + {% endelement %} + {% endslot %} + {% slot actions %} + {% url 'mfa_download_recovery_codes' as download_url %} + {% element button href=download_url icon="download" tags="outline,panel" %} + {% translate "Download" %} + {% endelement %} + {% endslot %} + {% endif %} + {% endif %} + {% slot actions %} + {% url 'mfa_generate_recovery_codes' as generate_url %} + {% element button href=generate_url icon="refresh-cw" tags="outline,panel" %} + {% translate "Generate" %} + {% endelement %} + {% endslot %} + {% endif %} + {% endelement %} + {% endwith %} + {% endif %} +{% endblock content %} diff --git a/templates/mfa/recovery_codes/generate.html b/templates/mfa/recovery_codes/generate.html new file mode 100644 index 0000000..25c8dfd --- /dev/null +++ b/templates/mfa/recovery_codes/generate.html @@ -0,0 +1,35 @@ +{% extends "mfa/recovery_codes/base.html" %} +{% load i18n allauth %} + +{% block content %} + {% element h1 %} + {% translate "Recovery Codes" %} + {% endelement %} + {% element p %} + {% blocktranslate %}You are about to generate a new set of recovery codes for your account.{% endblocktranslate %} + {% if unused_code_count %} + {% blocktranslate %}This action will invalidate your existing codes.{% endblocktranslate %} + {% endif %} + {% blocktranslate %}Are you sure?{% endblocktranslate %} + {% endelement %} + + {% url 'mfa_generate_recovery_codes' as action_url %} + {% element form method="post" action=action_url no_visible_fields=True %} + {% slot body %} + {% csrf_token %} + {{ form.as_p }} + {% endslot %} + {% slot actions %} + {# Destructive when codes already exist: generating throws the old ones away. #} + {% setvar tags %} + {% if unused_code_count %} + danger + {% else %} + {% endif %} + {% endsetvar %} + {% element button type="submit" icon="refresh-cw" tags=tags %} + {% trans "Generate" %} + {% endelement %} + {% endslot %} + {% endelement %} +{% endblock content %} diff --git a/templates/mfa/recovery_codes/index.html b/templates/mfa/recovery_codes/index.html new file mode 100644 index 0000000..f17d8c3 --- /dev/null +++ b/templates/mfa/recovery_codes/index.html @@ -0,0 +1,65 @@ +{% extends "mfa/recovery_codes/base.html" %} +{% load i18n allauth static %} + +{% comment %} + Overridden for layout: Download and Generate sit side by side under the codes rather + than stacking. Generate is outline-only — it silently invalidates the codes on screen, + so it must not be the button your eye lands on. +{% endcomment %} + +{% block content %} + {% element h1 %} + {% translate "Recovery Codes" %} + {% endelement %} + {% element p %} + {% blocktranslate count unused_count=unused_codes|length %}There is {{ unused_count }} out of {{ total_count }} recovery codes available.{% plural %}There are {{ unused_count }} out of {{ total_count }} recovery codes available.{% endblocktranslate %} + {% endelement %} + + {% if can_view_codes %} + {% element field id="recovery_codes" type="textarea" rows=unused_codes|length readonly=True %} + {% slot label %} + {% translate "Unused codes" %} + {% endslot %} + {# djlint:off #} +{% slot value %}{% for code in unused_codes %}{% if forloop.counter0 %} +{% endif %}{{ code }}{% endfor %}{% endslot %} + {# djlint:on #} + {% endelement %} + {% endif %} + +
+ {% if can_download_codes %} + {% url 'mfa_download_recovery_codes' as download_url %} + {% element button href=download_url icon="download" %} + {% translate "Download codes" %} + {% endelement %} + {% endif %} + {% if can_generate_codes %} + {% url 'mfa_generate_recovery_codes' as generate_url %} + {% element button href=generate_url icon="refresh-cw" tags="outline" %} + {% translate "Generate new codes" %} + {% endelement %} + {% endif %} +
+ + {% if MFA_RECOVERY_CODES_SHOW_ONCE and can_view_codes %} + {% element field type="checkbox" id="codes_saved" %} + {% slot label %} + {% translate "I have saved my recovery codes" %} + {% endslot %} + {% endelement %} + {% endif %} +{% endblock content %} + +{% block extra_body %} + {{ block.super }} + + + +{% endblock extra_body %} diff --git a/templates/mfa/totp/activate_form.html b/templates/mfa/totp/activate_form.html new file mode 100644 index 0000000..c81c39a --- /dev/null +++ b/templates/mfa/totp/activate_form.html @@ -0,0 +1,45 @@ +{% extends "mfa/totp/base.html" %} +{% load allauth i18n %} + +{% comment %} + The code field is boxed and label-less (the `fields` element does that for any `code` + field), and the secret sits in a mono field with room around it — it is copied by hand, + character by character. +{% endcomment %} + +{% block head_title %} + {% translate "Activate Authenticator App" %} +{% endblock head_title %} + +{% block content %} + {% element h1 %} + {% translate "Activate Authenticator App" %} + {% endelement %} + {% element p %} + {% blocktranslate %}To protect your account with two-factor authentication, scan the QR code below with your authenticator app. Then, input the verification code generated by the app below.{% endblocktranslate %} + {% endelement %} + + {% url 'mfa_activate_totp' as action_url %} + {% element form form=form method="post" action=action_url %} + {% slot body %} + {% element img src=totp_svg_data_uri alt=form.secret tags="mfa,totp,qr" %} + {% endelement %} + {% csrf_token %} + {% element field id="authenticator_secret" type="text" value=form.secret disabled=True %} + {% slot label %} + {% translate "Authenticator secret" %} + {% endslot %} + {% slot help_text %} + {% translate "You can store this secret and use it to reinstall your authenticator app at a later time." %} + {% endslot %} + {% endelement %} + {% element fields form=form %} + {% endelement %} + {% endslot %} + {% slot actions %} + {% element button type="submit" icon="check" %} + {% trans "Activate" %} + {% endelement %} + {% endslot %} + {% endelement %} +{% endblock content %} diff --git a/templates/mfa/totp/deactivate_form.html b/templates/mfa/totp/deactivate_form.html new file mode 100644 index 0000000..36736bb --- /dev/null +++ b/templates/mfa/totp/deactivate_form.html @@ -0,0 +1,30 @@ +{% extends "mfa/totp/base.html" %} +{% load allauth i18n %} + +{% block head_title %} + {% trans "Deactivate Authenticator App" %} +{% endblock head_title %} + +{% block content %} + {% element h1 %} + {% trans "Deactivate Authenticator App" %} + {% endelement %} + {% element p %} + {% blocktranslate %}You are about to deactivate authenticator app based authentication. Are you sure?{% endblocktranslate %} + {% endelement %} + + {% url 'mfa_deactivate_totp' as action_url %} + {% element form form=form method="post" action=action_url no_visible_fields=True %} + {% slot body %} + {% csrf_token %} + {% element fields form=form %} + {{ form.as_p }} + {% endelement %} + {% endslot %} + {% slot actions %} + {% element button type="submit" icon="power" tags="danger,delete" %} + {% trans "Deactivate" %} + {% endelement %} + {% endslot %} + {% endelement %} +{% endblock content %} diff --git a/templates/mfa/webauthn/add_form.html b/templates/mfa/webauthn/add_form.html new file mode 100644 index 0000000..68af5c6 --- /dev/null +++ b/templates/mfa/webauthn/add_form.html @@ -0,0 +1,39 @@ +{% extends "mfa/webauthn/base.html" %} +{% load allauth i18n static %} + +{% block head_title %} + {% trans "Add Security Key" %} +{% endblock head_title %} + +{% block content %} + {% element h1 %} + {% trans "Add Security Key" %} + {% endelement %} + + {% url 'mfa_add_webauthn' as action_url %} + {% element form form=form method="post" action=action_url %} + {% slot body %} + {% csrf_token %} + {% element fields form=form %} + {% endelement %} + {% endslot %} + {% slot actions %} + {# type="button": the webauthn script takes over the click and posts the form itself. #} + {% element button id="mfa_webauthn_add" type="button" icon="usb" %} + {% trans "Add" %} + {% endelement %} + {% endslot %} + {% endelement %} + + {% include "mfa/webauthn/snippets/scripts.html" %} + {{ js_data|json_script:"js_data" }} + +{% endblock content %} diff --git a/templates/mfa/webauthn/authenticator_confirm_delete.html b/templates/mfa/webauthn/authenticator_confirm_delete.html new file mode 100644 index 0000000..82e9945 --- /dev/null +++ b/templates/mfa/webauthn/authenticator_confirm_delete.html @@ -0,0 +1,25 @@ +{% extends "mfa/webauthn/base.html" %} +{% load allauth i18n %} + +{% block head_title %} + {% trans "Remove Security Key" %} +{% endblock head_title %} + +{% block content %} + {% element h1 %} + {% trans "Remove Security Key" %} + {% endelement %} + {% element p %} + {% blocktranslate %}Are you sure you want to remove this security key?{% endblocktranslate %} + {% endelement %} + + {% url 'mfa_remove_webauthn' pk=authenticator.pk as action_url %} + {% element form method="post" action=action_url no_visible_fields=True %} + {% slot actions %} + {% csrf_token %} + {% element button type="submit" icon="trash-2" tags="danger" %} + {% translate "Remove" %} + {% endelement %} + {% endslot %} + {% endelement %} +{% endblock content %} diff --git a/templates/mfa/webauthn/authenticator_list.html b/templates/mfa/webauthn/authenticator_list.html new file mode 100644 index 0000000..872afbf --- /dev/null +++ b/templates/mfa/webauthn/authenticator_list.html @@ -0,0 +1,72 @@ +{% extends "mfa/webauthn/base.html" %} +{% load i18n %} +{% load static %} +{% load allauth %} +{% load humanize %} +{% block content %} + {% element h1 %} + {% trans "Security Keys" %} + {% endelement %} + {% if authenticators|length == 0 %} + {% element p %} + {% blocktranslate %}No security keys have been added.{% endblocktranslate %} + {% endelement %} + {% else %} + {% element table %} + {% element thead %} + {% element th %} + {% translate "Key" %} + {% endelement %} + {% element th %} + {% translate "Usage" %} + {% endelement %} + {% element th %} + {% endelement %} + {% endelement %} + {% element tbody %} + {% for authenticator in authenticators %} + {% element tr %} + {% element td %} + {{ authenticator }} + {% if authenticator.wrap.is_passwordless is True %} + {% element badge tags="mfa,key,primary" %} + {% translate "Passkey" %} + {% endelement %} + {% elif authenticator.wrap.is_passwordless is False %} + {% element badge tags="mfa,key,secondary" %} + {% translate "Security key" %} + {% endelement %} + {% else %} + {% element badge title=_("This key does not indicate whether it is a passkey.") tags="mfa,key,warning" %} + {% translate "Unspecified" %} + {% endelement %} + {% endif %} + {% endelement %} + {% element td %} + {% blocktranslate with created_at=authenticator.created_at|date:"SHORT_DATE_FORMAT" %}Added on {{ created_at }}{% endblocktranslate %}. + {% if authenticator.last_used_at %} + {% blocktranslate with last_used=authenticator.last_used_at|naturaltime %}Last used {{ last_used }}{% endblocktranslate %} + {% else %} + Not used. + {% endif %} + {% endelement %} + {% element td align="right" %} + {% url 'mfa_edit_webauthn' pk=authenticator.pk as edit_url %} + {% element button href=edit_url icon="pencil" tags="mfa,authenticator,edit,tool" %} + {% translate "Edit" %} + {% endelement %} + {% url 'mfa_remove_webauthn' pk=authenticator.pk as remove_url %} + {% element button href=remove_url icon="trash-2" tags="mfa,authenticator,danger,delete,tool" %} + {% translate "Remove" %} + {% endelement %} + {% endelement %} + {% endelement %} + {% endfor %} + {% endelement %} + {% endelement %} + {% endif %} + {% url 'mfa_add_webauthn' as add_url %} + {% element button href=add_url icon="usb" %} + {% translate "Add" %} + {% endelement %} +{% endblock %} From 1dd2e5099f417bb0418093330ca6d82fd2a26bb6 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 00:31:22 +0200 Subject: [PATCH 054/137] Replace "usb" icons with "user-key" for WebAuthn buttons; refine button styles and layouts across templates --- .idea/ClubManager.iml | 1 + controlpanel/templates/controlpanel/base.html | 2 - templates/account/password_change.html | 2 +- templates/allauth/elements/fields.html | 92 +++++++++---------- templates/mfa/authenticate.html | 2 +- templates/mfa/index.html | 7 +- templates/mfa/webauthn/add_form.html | 2 +- .../mfa/webauthn/authenticator_list.html | 2 +- 8 files changed, 54 insertions(+), 56 deletions(-) diff --git a/.idea/ClubManager.iml b/.idea/ClubManager.iml index cf6280b..b034188 100644 --- a/.idea/ClubManager.iml +++ b/.idea/ClubManager.iml @@ -14,6 +14,7 @@ + diff --git a/controlpanel/templates/controlpanel/base.html b/controlpanel/templates/controlpanel/base.html index ece8afd..5595f43 100644 --- a/controlpanel/templates/controlpanel/base.html +++ b/controlpanel/templates/controlpanel/base.html @@ -1,5 +1,3 @@ -{# Hardcoded, not `base_template`: the panel is platform-only, and a branding bug - must never be able to dress it up as a club. #} {% extends "_platform_base.html" %} {% load lucide %} diff --git a/templates/account/password_change.html b/templates/account/password_change.html index 724e94e..226ae62 100644 --- a/templates/account/password_change.html +++ b/templates/account/password_change.html @@ -33,7 +33,7 @@
diff --git a/templates/allauth/elements/fields.html b/templates/allauth/elements/fields.html index 900f973..ab228ee 100644 --- a/templates/allauth/elements/fields.html +++ b/templates/allauth/elements/fields.html @@ -26,25 +26,23 @@ false — silently dropping every field on every form that doesn't pass `exclude`. {% endcomment %} {% with exclude=attrs.exclude|default:"" %} -{% for field in attrs.form.visible_fields %} - {% if not field|excluded:exclude %} -
- {% if field.field.widget.input_type == "checkbox" %} - - {% else %} - {# An otp box never takes a visible label — the boxes say what they are, and a - heading above them just crowds the field. It keeps an sr-only one below. #} - {% if not attrs.unlabeled and not field|is_otp %} - - {% endif %} - {% with icon=field|field_icon %} - {% if field|is_otp %} - {% comment %} + {% for field in attrs.form.visible_fields %} + {% if not field|excluded:exclude %} +
+ {% if field.field.widget.input_type == "checkbox" %} + + {% else %} + {% if not attrs.unlabeled and not field|is_otp %} + + {% endif %} + {% with icon=field|field_icon %} + {% if field|is_otp %} + {% comment %} daisyUI's otp is one input overlaid on the boxes. Child order is load-bearing: it places each box with `nth-child`, which counts *every* child, not just the spans — so the input must come LAST. Put it first and all six boxes shift one @@ -55,38 +53,38 @@ For the same reason the sr-only label lives outside the container: another child would throw the count off again. {% endcomment %} - -
-
- - - - - - - {{ field|as_otp }} -
-
- {% elif icon %} - - {% else %} - {{ field|daisy }} + +
+
+ + + + + + + {{ field|as_otp }} +
+
+ {% elif icon %} + + {% else %} + {{ field|daisy }} + {% endif %} + {% endwith %} {% endif %} - {% endwith %} - {% endif %} - {% comment %} + {% comment %} The password field's help_text is allauth's "Forgot your password?" link, so it gets room to breathe. The id is not decorative: Django points the input's aria-describedby at `_helptext`, and without it that reference dangles and the link is never announced. {% endcomment %} - {% if field.help_text %}{{ field.help_text }}{% endif %} - {% for error in field.errors %}{{ error }}{% endfor %} -
- {% endif %} -{% endfor %} + {% if field.help_text %}{{ field.help_text }}{% endif %} + {% for error in field.errors %}{{ error }}{% endfor %} +
+ {% endif %} + {% endfor %} {% endwith %} diff --git a/templates/mfa/authenticate.html b/templates/mfa/authenticate.html index d8650fa..925302c 100644 --- a/templates/mfa/authenticate.html +++ b/templates/mfa/authenticate.html @@ -46,7 +46,7 @@ {% endelement %}
diff --git a/templates/mfa/index.html b/templates/mfa/index.html index 704b91b..1281248 100644 --- a/templates/mfa/index.html +++ b/templates/mfa/index.html @@ -41,7 +41,7 @@ {% translate "Deactivate" %} {% endelement %} {% else %} - {% element button href=activate_url icon="smartphone" tags="panel" %} + {% element button href=activate_url icon="qr-code" tags="panel" %} {% translate "Activate" %} {% endelement %} {% endif %} @@ -73,7 +73,7 @@ {% endelement %} {% else %} {% url 'mfa_add_webauthn' as webauthn_add_url %} - {% element button href=webauthn_add_url icon="usb" %} + {% element button href=webauthn_add_url icon="user-key" %} {% translate "Add" %} {% endelement %} {% endif %} @@ -90,7 +90,8 @@ {% slot body %} {% if authenticators.recovery_codes %} {% element p %} - {% blocktranslate count unused_count=unused_count %}There is {{ unused_count }} out of {{ total_count }} recovery codes available.{% plural %}There are {{ unused_count }} out of {{ total_count }} recovery codes available.{% endblocktranslate %} + {% blocktranslate count unused_count=unused_count %}There is {{ unused_count }} out of {{ total_count }} recovery codes available.{% plural %}There are {{ unused_count }} out of {{ total_count }} recovery codes available. + {% endblocktranslate %} {% endelement %} {% else %} {% element p %} diff --git a/templates/mfa/webauthn/add_form.html b/templates/mfa/webauthn/add_form.html index 68af5c6..27a17bc 100644 --- a/templates/mfa/webauthn/add_form.html +++ b/templates/mfa/webauthn/add_form.html @@ -19,7 +19,7 @@ {% endslot %} {% slot actions %} {# type="button": the webauthn script takes over the click and posts the form itself. #} - {% element button id="mfa_webauthn_add" type="button" icon="usb" %} + {% element button id="mfa_webauthn_add" type="button" icon="user-key" %} {% trans "Add" %} {% endelement %} {% endslot %} diff --git a/templates/mfa/webauthn/authenticator_list.html b/templates/mfa/webauthn/authenticator_list.html index 872afbf..2ed3050 100644 --- a/templates/mfa/webauthn/authenticator_list.html +++ b/templates/mfa/webauthn/authenticator_list.html @@ -66,7 +66,7 @@ {% endelement %} {% endif %} {% url 'mfa_add_webauthn' as add_url %} - {% element button href=add_url icon="usb" %} + {% element button href=add_url icon="user-key" %} {% translate "Add" %} {% endelement %} {% endblock %} From 9127be0c42fb4db861a806e502d7415d467381f3 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 00:53:12 +0200 Subject: [PATCH 055/137] Give every model created/modified timestamps TimeStampedModel goes on UUIDModel, so all 28 domain models get row birthdays in one place. Without them the dashboard can only ever describe the present: "42 members" is knowable, "members joined this month" is not, and no metric can show direction. Order dropped its own created/modified -- redeclaring a field from an abstract base is an error, and its column survives as a plain AlterField (verbose_name only), so no order data moves. Note for reading early charts: auto_now_add backfills existing rows with a single migration timestamp, so everything that predates this commit shares one birthday and will show up as a spike at that instant rather than as real history. Co-Authored-By: Claude Opus 4.8 --- authentication/tests.py | 2 +- ...odified_clubmembership_created_and_more.py | 58 ++++++++++ controlpanel/admin.py | 1 - controlpanel/models.py | 1 - controlpanel/templates/controlpanel/base.html | 8 +- .../templates/controlpanel/dashboard.html | 5 +- ...endance_modified_event_created_and_more.py | 69 +++++++++++ ..._answer_modified_field_created_and_more.py | 58 ++++++++++ ...family_modified_member_created_and_more.py | 36 ++++++ rosterchief/base.py | 15 ++- ...eated_applieddiscount_modified_and_more.py | 107 ++++++++++++++++++ shop/models.py | 4 +- static/css/app.css | 12 ++ ...tion_created_position_modified_and_more.py | 58 ++++++++++ templates/_base.html | 2 +- 15 files changed, 423 insertions(+), 13 deletions(-) create mode 100644 club/migrations/0013_club_created_club_modified_clubmembership_created_and_more.py delete mode 100644 controlpanel/admin.py delete mode 100644 controlpanel/models.py create mode 100644 events/migrations/0009_attendance_created_attendance_modified_event_created_and_more.py create mode 100644 formbuilder/migrations/0005_answer_created_answer_modified_field_created_and_more.py create mode 100644 members/migrations/0003_family_created_family_modified_member_created_and_more.py create mode 100644 shop/migrations/0010_applieddiscount_created_applieddiscount_modified_and_more.py create mode 100644 teams/migrations/0004_position_created_position_modified_and_more.py diff --git a/authentication/tests.py b/authentication/tests.py index 799a18a..d44dfe5 100644 --- a/authentication/tests.py +++ b/authentication/tests.py @@ -412,7 +412,7 @@ class ChangePasswordPageTests(TestCase): def test_forgot_password_is_an_accent_button_and_both_actions_have_icons(self): html = self.response.content.decode() - forgot = html[html.index('class="btn btn-accent gap-2"') :] + forgot = html[html.index("btn-accent") :] submit = html[html.index('class="btn btn-primary gap-2"') :] self.assertIn("")]) diff --git a/club/migrations/0013_club_created_club_modified_clubmembership_created_and_more.py b/club/migrations/0013_club_created_club_modified_clubmembership_created_and_more.py new file mode 100644 index 0000000..fcc113a --- /dev/null +++ b/club/migrations/0013_club_created_club_modified_clubmembership_created_and_more.py @@ -0,0 +1,58 @@ +# Generated by Django 6.0.6 on 2026-07-13 22:51 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('club', '0012_club_logo_club_primary_color'), + ] + + operations = [ + migrations.AddField( + model_name='club', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 565739, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='club', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='clubmembership', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 565911, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='clubmembership', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='clubrole', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 565939, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='clubrole', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='season', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 565961, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='season', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + ] diff --git a/controlpanel/admin.py b/controlpanel/admin.py deleted file mode 100644 index 846f6b4..0000000 --- a/controlpanel/admin.py +++ /dev/null @@ -1 +0,0 @@ -# Register your models here. diff --git a/controlpanel/models.py b/controlpanel/models.py deleted file mode 100644 index 6b20219..0000000 --- a/controlpanel/models.py +++ /dev/null @@ -1 +0,0 @@ -# Create your models here. diff --git a/controlpanel/templates/controlpanel/base.html b/controlpanel/templates/controlpanel/base.html index 5595f43..a1f346d 100644 --- a/controlpanel/templates/controlpanel/base.html +++ b/controlpanel/templates/controlpanel/base.html @@ -7,16 +7,18 @@ {% block main %}
-
-

+
+

{% block heading %}Control panel{% endblock heading %}

- {% block subheading %}{% endblock subheading %} + {% block subheading %}{% endblock subheading %}
+
{% block actions %}{% endblock actions %}

+
{% lucide "layout-dashboard" size=16 %} Dashboard {% lucide "building-2" size=16 %} Clubs diff --git a/controlpanel/templates/controlpanel/dashboard.html b/controlpanel/templates/controlpanel/dashboard.html index ed95e7c..a5c5832 100644 --- a/controlpanel/templates/controlpanel/dashboard.html +++ b/controlpanel/templates/controlpanel/dashboard.html @@ -1,10 +1,11 @@ {% extends "controlpanel/base.html" %} {% load lucide %} -{% block heading %}Platform overview{% endblock heading %} +{% block heading %}RosterChief Platform Dashboard{% endblock heading %} +{% block subheading %}Welcome back {{ user.member.first_name }}!{% endblock subheading %} {% block actions %} - {% lucide "plus" size=16 %} New club + {% lucide "plus" size=16 %} Create new club {% endblock actions %} {% block panel %} diff --git a/events/migrations/0009_attendance_created_attendance_modified_event_created_and_more.py b/events/migrations/0009_attendance_created_attendance_modified_event_created_and_more.py new file mode 100644 index 0000000..4a3a4d3 --- /dev/null +++ b/events/migrations/0009_attendance_created_attendance_modified_event_created_and_more.py @@ -0,0 +1,69 @@ +# Generated by Django 6.0.6 on 2026-07-13 22:51 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0008_event_created_by'), + ] + + operations = [ + migrations.AddField( + model_name='attendance', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 565980, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='attendance', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='event', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 565998, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='event', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='eventseries', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566017, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='eventseries', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='location', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566033, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='location', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='opponent', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566050, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='opponent', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + ] diff --git a/formbuilder/migrations/0005_answer_created_answer_modified_field_created_and_more.py b/formbuilder/migrations/0005_answer_created_answer_modified_field_created_and_more.py new file mode 100644 index 0000000..c068112 --- /dev/null +++ b/formbuilder/migrations/0005_answer_created_answer_modified_field_created_and_more.py @@ -0,0 +1,58 @@ +# Generated by Django 6.0.6 on 2026-07-13 22:51 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('formbuilder', '0004_alter_field_key_alter_form_slug'), + ] + + operations = [ + migrations.AddField( + model_name='answer', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566066, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='answer', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='field', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566082, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='field', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='form', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566098, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='form', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='submission', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566113, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='submission', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + ] diff --git a/members/migrations/0003_family_created_family_modified_member_created_and_more.py b/members/migrations/0003_family_created_family_modified_member_created_and_more.py new file mode 100644 index 0000000..e878d25 --- /dev/null +++ b/members/migrations/0003_family_created_family_modified_member_created_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 6.0.6 on 2026-07-13 22:51 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('members', '0002_alter_familymembership_unique_together_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='family', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566131, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='family', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='member', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566186, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='member', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + ] diff --git a/rosterchief/base.py b/rosterchief/base.py index 173b25a..827012d 100644 --- a/rosterchief/base.py +++ b/rosterchief/base.py @@ -68,8 +68,19 @@ class TenantQuerySet(models.QuerySet): return self.filter(club=require_current_club()) -class UUIDModel(models.Model): - """Abstract base class giving every model a UUID primary key""" +class TimeStampedModel(models.Model): + """Row birthdays. Without these, every metric can only describe the present — + "42 members" is knowable, "members joined this month" is not.""" + + created = models.DateTimeField(_("created"), auto_now_add=True) + modified = models.DateTimeField(_("modified"), auto_now=True) + + class Meta: + abstract = True + + +class UUIDModel(TimeStampedModel): + """Abstract base class giving every model a UUID primary key and timestamps.""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) diff --git a/shop/migrations/0010_applieddiscount_created_applieddiscount_modified_and_more.py b/shop/migrations/0010_applieddiscount_created_applieddiscount_modified_and_more.py new file mode 100644 index 0000000..52e69ff --- /dev/null +++ b/shop/migrations/0010_applieddiscount_created_applieddiscount_modified_and_more.py @@ -0,0 +1,107 @@ +# Generated by Django 6.0.6 on 2026-07-13 22:51 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('shop', '0009_applieddiscount_unique_discount_per_order'), + ] + + operations = [ + migrations.AddField( + model_name='applieddiscount', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566202, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='applieddiscount', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='cart', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566218, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='cart', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='cartitem', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566233, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='cartitem', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='discount', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566250, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='discount', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='invoice', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566266, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='invoice', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='orderline', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566282, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='orderline', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='payment', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566299, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='payment', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='product', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566314, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='product', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AlterField( + model_name='order', + name='created', + field=models.DateTimeField(auto_now_add=True, verbose_name='created'), + ), + ] diff --git a/shop/models.py b/shop/models.py index 9e4a30f..7331d28 100644 --- a/shop/models.py +++ b/shop/models.py @@ -142,8 +142,8 @@ class Order(ClubScopedModel): status = models.CharField(_("status"), max_length=255, choices=OrderStatus.choices, default=OrderStatus.PENDING) total = models.DecimalField(_("total"), max_digits=10, decimal_places=2) - created = models.DateTimeField(_("created at"), auto_now_add=True) - modified = models.DateTimeField(_("modified"), auto_now=True) + # `created`/`modified` come from TimeStampedModel — declaring them here as well + # would clash with the abstract base. class Meta: verbose_name = _("order") diff --git a/static/css/app.css b/static/css/app.css index 0e46d71..bda83c8 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -23,6 +23,8 @@ --text-xl--line-height: calc(1.75 / 1.25); --text-2xl: 1.5rem; --text-2xl--line-height: calc(2 / 1.5); + --text-3xl: 1.875rem; + --text-3xl--line-height: calc(2.25 / 1.875); --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; @@ -3174,9 +3176,15 @@ .px-6 { padding-inline: calc(var(--spacing) * 6); } + .px-8 { + padding-inline: calc(var(--spacing) * 8); + } .py-2 { padding-block: calc(var(--spacing) * 2); } + .py-4 { + padding-block: calc(var(--spacing) * 4); + } .pt-2 { padding-top: calc(var(--spacing) * 2); } @@ -3202,6 +3210,10 @@ font-size: var(--text-2xl); line-height: var(--tw-leading, var(--text-2xl--line-height)); } + .text-3xl { + font-size: var(--text-3xl); + line-height: var(--tw-leading, var(--text-3xl--line-height)); + } .text-base { font-size: var(--text-base); line-height: var(--tw-leading, var(--text-base--line-height)); diff --git a/teams/migrations/0004_position_created_position_modified_and_more.py b/teams/migrations/0004_position_created_position_modified_and_more.py new file mode 100644 index 0000000..0b4cf51 --- /dev/null +++ b/teams/migrations/0004_position_created_position_modified_and_more.py @@ -0,0 +1,58 @@ +# Generated by Django 6.0.6 on 2026-07-13 22:51 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('teams', '0003_position_management_position_implies_staff_position'), + ] + + operations = [ + migrations.AddField( + model_name='position', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566331, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='position', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='staffassignment', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566347, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='staffassignment', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='team', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566410, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='team', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + migrations.AddField( + model_name='teammembership', + name='created', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2026, 7, 13, 22, 51, 35, 566427, tzinfo=datetime.timezone.utc), verbose_name='created'), + preserve_default=False, + ), + migrations.AddField( + model_name='teammembership', + name='modified', + field=models.DateTimeField(auto_now=True, verbose_name='modified'), + ), + ] diff --git a/templates/_base.html b/templates/_base.html index e55b53f..7bc4ab4 100644 --- a/templates/_base.html +++ b/templates/_base.html @@ -81,7 +81,7 @@
{% endif %} -
+
{% block main %}{% endblock main %}
From 016206a79ec32592e151942140caaaaa075b7725 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 00:59:28 +0200 Subject: [PATCH 056/137] Build the platform metrics dashboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dashboard leads with the numbers that are supposed to be zero, because a dashboard of healthy counts is one nobody opens: - Clubs with no season covering today. Seasons scope memberships, rosters and events, so such a club cannot take a signup or schedule a match -- and it fails silently, nothing errors, it is just inert. - Dormant clubs: nothing on the calendar for 30 days. Churn signal. - Admins pending MFA. RequireMFAMiddleware redirects them to enrolment, so they are locked out of their own club until they act: a support queue, not a stat. - Outstanding money across every club. Then the shape of the business: an onboarding funnel (clubs → with members → with a team → with events, which separates working clubs from shells), feature-flag adoption per club, and two charts -- signups and revenue per month. Charts use chart.js, self-hosted rather than pulled from a CDN, for the same reason as the fonts: no third-party in the render path. Two things the browser taught me: the canvas needs a height-bounded wrapper (with maintainAspectRatio off it sizes to its parent, and a parent with no height grew it to 3489px), and chart.js cannot read daisyUI's CSS variables, so the charts re-render on a data-theme change or keep the light palette in dark mode. The month series is zero-filled: a chart that skips empty months draws a smooth line straight over a month in which nothing happened. Co-Authored-By: Claude Opus 4.8 --- controlpanel/services/statistics.py | 104 +++++ .../templates/controlpanel/dashboard.html | 169 ++++++++- controlpanel/tests.py | 165 +++++++- controlpanel/views.py | 6 +- package-lock.json | 21 ++ package.json | 3 + static/css/app.css | 356 ++++++++++++++++++ static/js/chart.js | 14 + 8 files changed, 835 insertions(+), 3 deletions(-) create mode 100644 static/js/chart.js diff --git a/controlpanel/services/statistics.py b/controlpanel/services/statistics.py index 3600aa5..1b9a553 100644 --- a/controlpanel/services/statistics.py +++ b/controlpanel/services/statistics.py @@ -5,11 +5,17 @@ means adding an entry here and nothing else. ``clubs_with_totals`` annotates in a single query — the club list must not fan out into N+1. """ +from datetime import timedelta from decimal import Decimal +from allauth.mfa.models import Authenticator +from django.contrib.auth import get_user_model from django.db.models import Count, Q, Sum +from django.db.models.functions import TruncMonth from django.utils import timezone +from waffle import get_waffle_flag_model +from authentication.middleware import ELEVATED_ROLES from club.models import Club, ClubMembership, ClubRole, Season from events.models import Event from members.models import Member @@ -21,6 +27,10 @@ ZERO = Decimal("0.00") PAID_STATUSES = (Order.OrderStatus.PAID, Order.OrderStatus.DELIVERED) OWED_STATUSES = (Order.OrderStatus.PENDING, Order.OrderStatus.PARTIALLY_PAID) +#: A club with nothing scheduled inside this window has stopped using the product. +DORMANT_DAYS = 30 +MONTHS_OF_HISTORY = 12 + def clubs_with_totals(queryset=None): """Clubs annotated with headline counts (one query, no N+1).""" @@ -42,6 +52,100 @@ def platform_totals(): } +def clubs_without_a_season(today=None): + """Clubs with no season covering today. + + Not cosmetic: seasons scope memberships, rosters and events, so a club without + one cannot take a signup or schedule a match. It fails silently — nothing errors, + the club is simply inert — which is exactly why it belongs on a dashboard. + """ + today = today or timezone.localdate() + return Club.objects.active().exclude(seasons__start_date__lte=today, seasons__end_date__gte=today) + + +def dormant_clubs(days=DORMANT_DAYS): + """Active clubs with nothing on the calendar in the next ``days``. Churn signal.""" + now = timezone.now() + return Club.objects.active().exclude(events__start__gte=now, events__start__lte=now + timedelta(days=days)) + + +def admins_pending_mfa(): + """Privileged users who have not enrolled a second factor. + + They are locked out until they do (RequireMFAMiddleware redirects them to the + enrolment page), so this is a support queue rather than a statistic. The rule is + the middleware's own: platform staff, plus anyone holding an elevated ClubRole. + """ + User = get_user_model() + elevated = User.objects.filter(Q(is_staff=True) | Q(is_superuser=True) | Q(member__roles__role__in=ELEVATED_ROLES)) + + return elevated.exclude(pk__in=Authenticator.objects.values("user")).distinct() + + +def onboarding_funnel(): + """How far each active club got: created → has members → has a team → has events. + + Separates working clubs from empty shells someone created and walked away from, + and shows which step people stall on. + """ + clubs = clubs_with_totals(Club.objects.active()) + total = len(clubs) + + return [ + {"label": "Clubs", "count": total, "icon": "building-2"}, + {"label": "With members", "count": sum(1 for club in clubs if club.member_count), "icon": "users"}, + {"label": "With a team", "count": sum(1 for club in clubs if club.team_count), "icon": "shield"}, + {"label": "With events", "count": sum(1 for club in clubs if club.event_count), "icon": "calendar-days"}, + ] + + +def flag_adoption(): + """Clubs per feature flag. `everyone` overrides club targeting, so a flag set that + way is on (or off) everywhere and its club count says nothing — hence `overridden`.""" + Flag = get_waffle_flag_model() + + return [{"name": flag.name, "clubs": flag.clubs.count(), "everyone": flag.everyone, "overridden": flag.everyone is not None} for flag in Flag.objects.annotate(club_total=Count("clubs")).order_by("name")] + + +def platform_attention(): + """The numbers that are supposed to be zero. A dashboard of healthy counts is a + dashboard nobody opens.""" + members = Member.objects.count() + + return { + "clubs_without_season": clubs_without_a_season().count(), + "dormant_clubs": dormant_clubs().count(), + "admins_pending_mfa": admins_pending_mfa().count(), + "outstanding": _money(Order.objects.filter(status__in=OWED_STATUSES)), + "members_without_login": Member.objects.filter(user__isnull=True).count(), + "members": members, + } + + +def _monthly(queryset, field, value, months=MONTHS_OF_HISTORY): + """A dense month-by-month series — zero-filled, because a chart that silently skips + empty months draws a smooth line over a month where nothing happened.""" + start = (timezone.now() - timedelta(days=30 * months)).replace(day=1, hour=0, minute=0, second=0, microsecond=0) + + rows = queryset.filter(**{f"{field}__gte": start}).annotate(month=TruncMonth(field)).values("month").annotate(value=value).order_by("month") + found = {row["month"].strftime("%Y-%m"): row["value"] or 0 for row in rows if row["month"]} + + series, cursor = [], start + while cursor <= timezone.now(): + key = cursor.strftime("%Y-%m") + series.append({"month": cursor.strftime("%b %Y"), "value": float(found.get(key, 0))}) + cursor = (cursor + timedelta(days=32)).replace(day=1) + + return series + + +def platform_charts(): + return { + "signups": _monthly(ClubMembership.objects.filter(signed_up_at__isnull=False), "signed_up_at", Count("id")), + "revenue": _monthly(Order.objects.filter(status__in=PAID_STATUSES), "created", Sum("total")), + } + + def _money(queryset): return queryset.aggregate(total=Sum("total"))["total"] or ZERO diff --git a/controlpanel/templates/controlpanel/dashboard.html b/controlpanel/templates/controlpanel/dashboard.html index a5c5832..1d82e07 100644 --- a/controlpanel/templates/controlpanel/dashboard.html +++ b/controlpanel/templates/controlpanel/dashboard.html @@ -1,5 +1,5 @@ {% extends "controlpanel/base.html" %} -{% load lucide %} +{% load static lucide %} {% block heading %}RosterChief Platform Dashboard{% endblock heading %} {% block subheading %}Welcome back {{ user.member.first_name }}!{% endblock subheading %} @@ -9,6 +9,118 @@ {% endblock actions %} {% block panel %} + {% comment %} + Needs attention first: these are the numbers that are supposed to be zero. A club with + no current season cannot take a signup or schedule a match — and it fails silently, + nothing errors — while an admin without a second factor is locked out of their own + club. Both are work queues, not statistics. The vanity totals sit further down. + {% endcomment %} +
+
+
+
{% lucide "calendar-x" size=16 %} No current season
+
{{ attention.clubs_without_season }}
+
Clubs that cannot take signups
+
+
+
+
+
{% lucide "moon-star" size=16 %} Dormant
+
{{ attention.dormant_clubs }}
+
Nothing scheduled in 30 days
+
+
+
+
+
{% lucide "shield-alert" size=16 %} MFA pending
+
{{ attention.admins_pending_mfa }}
+
Admins locked out until they enrol
+
+
+
+
+
{% lucide "banknote" size=16 %} Outstanding
+
€{{ attention.outstanding }}
+
Unpaid across every club
+
+
+
+ +
+
+
+

{% lucide "user-plus" size=18 %} Signups per month

+ {# The wrapper's height is what bounds the canvas: with maintainAspectRatio off, + Chart.js sizes to its parent, and a parent with no height grows without end. #} +
+ +
+
+
+
+
+

{% lucide "euro" size=18 %} Revenue per month

+
+ +
+
+
+
+ +
+
+
+

{% lucide "milestone" size=18 %} Onboarding

+

Where clubs stall. One with no team or no events is a shell.

+
+ {% for step in funnel %} +
+
+ {% lucide step.icon size=14 %} {{ step.label }} + {{ step.count }} +
+ +
+ {% endfor %} +
+
+
+ +
+
+
+

{% lucide "toggle-right" size=18 %} Feature adoption

+ Manage +
+
+ + + {% for flag in flags %} + + + + + {% empty %} + + + + {% endfor %} + +
{{ flag.name }} + {% if flag.overridden %} + {# `everyone` overrides club targeting, so the club count says nothing here. #} + + {% if flag.everyone %}On for all{% else %}Off everywhere{% endif %} + + {% else %} + {{ flag.clubs }} / {{ totals.clubs }} clubs + {% endif %} +
No features yet.
+
+
+
+
+
Active clubs
@@ -21,12 +133,14 @@
Members
{{ totals.members }}
+
{{ attention.members_without_login }} without a login
Club admins
{{ totals.admins }}
+

Clubs

@@ -64,3 +178,56 @@
{% endblock panel %} + +{% block extra_body %} + {{ charts|json_script:"chart-data" }} + + +{% endblock extra_body %} diff --git a/controlpanel/tests.py b/controlpanel/tests.py index 73faaca..e56e340 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -1,3 +1,4 @@ +import datetime from decimal import Decimal from allauth.mfa.models import Authenticator @@ -12,13 +13,14 @@ from django.utils import timezone from waffle import get_waffle_flag_model, get_waffle_switch_model from club.models import Club, ClubMembership, ClubRole, Season +from events.models import Event from members.models import Member from shop.models import Order from teams.models import Position, Team, TeamMembership from .services.admins import grant_club_admin from .services.platform_admins import PlatformAdminError, is_last_superuser, set_platform_access -from .services.statistics import club_statistics, clubs_with_totals, platform_totals +from .services.statistics import admins_pending_mfa, club_statistics, clubs_with_totals, clubs_without_a_season, dormant_clubs, flag_adoption, onboarding_funnel, platform_attention, platform_charts, platform_totals from .templatetags.ui import as_alert, daisy, excluded, field_icon User = get_user_model() @@ -553,3 +555,164 @@ class ExcludedFilterTests(TestCase): def test_nothing_is_excluded_without_a_list(self): self.assertFalse(excluded(self.field("remember"), None)) + + +class PlatformAttentionTests(TestCase): + """The numbers that are supposed to be zero.""" + + def setUp(self): + self.club = Club.objects.create(name="Ajax United") + self.today = timezone.localdate() + + def season(self, club, start, end): + return Season.objects.create(club=club, start_date=start, end_date=end) + + def test_a_club_with_no_season_covering_today_is_flagged(self): + self.assertIn(self.club, clubs_without_a_season()) + + self.season(self.club, self.today - datetime.timedelta(days=10), self.today + datetime.timedelta(days=10)) + + self.assertNotIn(self.club, clubs_without_a_season()) + + def test_a_past_season_does_not_count_as_a_current_one(self): + self.season(self.club, self.today - datetime.timedelta(days=400), self.today - datetime.timedelta(days=40)) + + self.assertIn(self.club, clubs_without_a_season()) + + def test_an_archived_club_is_not_chased(self): + self.club.archive() + + self.assertNotIn(self.club, clubs_without_a_season()) + self.assertNotIn(self.club, dormant_clubs()) + + def test_a_club_with_nothing_scheduled_is_dormant(self): + self.assertIn(self.club, dormant_clubs()) + + Event.objects.create(club=self.club, title="Training", start=timezone.now() + datetime.timedelta(days=3)) + + self.assertNotIn(self.club, dormant_clubs()) + + def test_an_event_beyond_the_horizon_does_not_wake_a_club(self): + Event.objects.create(club=self.club, title="Far off", start=timezone.now() + datetime.timedelta(days=90)) + + self.assertIn(self.club, dormant_clubs()) + + def test_a_past_event_does_not_wake_a_club(self): + Event.objects.create(club=self.club, title="Gone", start=timezone.now() - datetime.timedelta(days=3)) + + self.assertIn(self.club, dormant_clubs()) + + +class MfaPendingTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United") + + def test_staff_without_a_second_factor_are_pending(self): + user = User.objects.create_user(email="staff@example.com", password="pw-secret-123", is_staff=True) + + self.assertIn(user, admins_pending_mfa()) + + enrol_mfa(user) + + self.assertNotIn(user, admins_pending_mfa()) + + def test_a_club_admin_without_a_second_factor_is_pending(self): + # They are locked out until they enrol, so this is a support queue. + user = User.objects.create_user(email="admin@example.com", password="pw-secret-123") + member = Member.objects.create(first_name="Ada", last_name="Lovelace", user=user) + ClubRole.objects.create(club=self.club, member=member, role=ClubRole.Roles.ADMIN) + + self.assertIn(user, admins_pending_mfa()) + + def test_an_ordinary_member_is_not_chased(self): + user = User.objects.create_user(email="member@example.com", password="pw-secret-123") + member = Member.objects.create(first_name="Bob", last_name="Bobson", user=user) + ClubRole.objects.create(club=self.club, member=member, role=ClubRole.Roles.MEMBER) + + self.assertNotIn(user, admins_pending_mfa()) + + def test_each_pending_admin_is_counted_once(self): + # Two elevated roles in two clubs is still one person to chase. + other = Club.objects.create(name="Feyenoord") + user = User.objects.create_user(email="admin@example.com", password="pw-secret-123", is_staff=True) + member = Member.objects.create(first_name="Ada", last_name="Lovelace", user=user) + ClubRole.objects.create(club=self.club, member=member, role=ClubRole.Roles.ADMIN) + ClubRole.objects.create(club=other, member=member, role=ClubRole.Roles.EDITOR) + + self.assertEqual(admins_pending_mfa().count(), 1) + + +class OnboardingFunnelTests(TestCase): + def test_the_funnel_narrows_as_clubs_stall(self): + empty = Club.objects.create(name="Empty FC") # noqa: F841 — a shell, counted only at step one + with_member = Club.objects.create(name="Members FC") + season = Season.objects.create(club=with_member, start_date=timezone.localdate(), end_date=timezone.localdate() + datetime.timedelta(days=30)) + member = Member.objects.create(first_name="Ada", last_name="Lovelace") + ClubMembership.objects.create(club=with_member, season=season, member=member) + + steps = {step["label"]: step["count"] for step in onboarding_funnel()} + + self.assertEqual(steps["Clubs"], 2) + self.assertEqual(steps["With members"], 1) + self.assertEqual(steps["With a team"], 0) + self.assertEqual(steps["With events"], 0) + + +class FlagAdoptionTests(TestCase): + def setUp(self): + cache.clear() + self.addCleanup(cache.clear) + self.club = Club.objects.create(name="Ajax United") + + def test_clubs_are_counted_per_flag(self): + flag = Flag.objects.create(name="shop") + flag.clubs.add(self.club) + + self.assertEqual(flag_adoption(), [{"name": "shop", "clubs": 1, "everyone": None, "overridden": False}]) + + def test_an_everyone_flag_reports_itself_as_overridden(self): + # `everyone` beats club targeting, so the club count would be a lie. + Flag.objects.create(name="shop", everyone=True) + + self.assertTrue(flag_adoption()[0]["overridden"]) + + +class PlatformChartTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United") + self.season = Season.objects.create(club=self.club, start_date=timezone.localdate(), end_date=timezone.localdate() + datetime.timedelta(days=30)) + self.member = Member.objects.create(first_name="Ada", last_name="Lovelace") + + def test_the_series_is_dense(self): + # Zero-filled: a chart that skips empty months draws a smooth line over a month + # in which nothing happened. + series = platform_charts()["signups"] + + self.assertEqual(len(series), 13) + self.assertTrue(all(point["value"] == 0 for point in series)) + + def test_signups_land_in_the_month_they_happened(self): + ClubMembership.objects.create(club=self.club, season=self.season, member=self.member, signed_up_at=timezone.localdate()) + + series = platform_charts()["signups"] + + self.assertEqual(series[-1]["value"], 1) + + def test_only_paid_orders_count_as_revenue(self): + Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("50.00"), status=Order.OrderStatus.PAID) + Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("30.00"), status=Order.OrderStatus.PENDING) + + self.assertEqual(platform_charts()["revenue"][-1]["value"], 50.0) + self.assertEqual(platform_attention()["outstanding"], Decimal("30.00")) + + +class DashboardMetricsTests(ControlPanelTestBase): + def test_the_dashboard_renders_its_metrics_and_charts(self): + response = self.client.get(reverse("controlpanel:dashboard")) + + self.assertContains(response, "No current season") + self.assertContains(response, "MFA pending") + self.assertContains(response, 'id="signups-chart"') + self.assertContains(response, 'id="revenue-chart"') + self.assertContains(response, "js/chart.js") + self.assertIn("signups", response.context["charts"]) diff --git a/controlpanel/views.py b/controlpanel/views.py index 289237c..43cae05 100644 --- a/controlpanel/views.py +++ b/controlpanel/views.py @@ -17,7 +17,7 @@ from .services.platform_admins import ( revoke_platform_access, set_platform_access, ) -from .services.statistics import club_statistics, clubs_with_totals, platform_totals +from .services.statistics import club_statistics, clubs_with_totals, flag_adoption, onboarding_funnel, platform_attention, platform_charts, platform_totals Flag = get_waffle_flag_model() Switch = get_waffle_switch_model() @@ -30,6 +30,10 @@ class DashboardView(PlatformStaffRequiredMixin, TemplateView): return super().get_context_data( nav="dashboard", totals=platform_totals(), + attention=platform_attention(), + funnel=onboarding_funnel(), + flags=flag_adoption(), + charts=platform_charts(), clubs=clubs_with_totals(Club.objects.active()), **kwargs, ) diff --git a/package-lock.json b/package-lock.json index 4af8852..96338b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,9 @@ "packages": { "": { "name": "rosterchief", + "dependencies": { + "chart.js": "^4.5.1" + }, "devDependencies": { "@fontsource-variable/jetbrains-mono": "^5.2.8", "@fontsource-variable/roboto": "^5.2.10", @@ -105,6 +108,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "license": "MIT" + }, "node_modules/@parcel/watcher": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", @@ -733,6 +742,18 @@ "node": ">=8" } }, + "node_modules/chart.js": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, "node_modules/daisyui": { "version": "5.6.18", "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.6.18.tgz", diff --git a/package.json b/package.json index d68246f..0f40fe9 100644 --- a/package.json +++ b/package.json @@ -14,5 +14,8 @@ "@tailwindcss/cli": "^4.1.0", "daisyui": "^5.0.0", "tailwindcss": "^4.1.0" + }, + "dependencies": { + "chart.js": "^4.5.1" } } diff --git a/static/css/app.css b/static/css/app.css index bda83c8..2d7c3fa 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -187,6 +187,75 @@ } } @layer utilities { + .tooltip { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-block; + --tt-bg: var(--color-neutral); + --tt-off: calc(100% + 0.5rem); + --tt-tail: calc(100% + 1px + 0.25rem); + --tt-tail-off: 0.5rem; + & > .tooltip-content, &[data-tip]:before { + position: absolute; + max-width: 20rem; + border-radius: var(--radius-field); + padding-inline: calc(0.25rem * 2); + padding-block: 0.25rem; + text-align: center; + white-space: normal; + color: var(--color-neutral-content); + opacity: 0%; + font-size: 0.875rem; + line-height: 1.25; + background-color: var(--tt-bg); + width: max-content; + pointer-events: none; + z-index: 2; + --tw-content: attr(data-tip); + content: var(--tw-content); + } + &:after { + opacity: 0%; + background-color: var(--tt-bg); + content: ""; + pointer-events: none; + width: 0.625rem; + height: 0.25rem; + display: block; + position: absolute; + mask-repeat: no-repeat; + mask-position: -1px 0; + --mask-tooltip: url("data:image/svg+xml,%3Csvg width='10' height='4' viewBox='0 0 8 4' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.500009 1C3.5 1 3.00001 4 5.00001 4C7 4 6.5 1 9.5 1C10 1 10 0.499897 10 0H0C-1.99338e-08 0.5 0 1 0.500009 1Z' fill='black'/%3E%3C/svg%3E%0A"); + mask-image: var(--mask-tooltip); + } + @media (prefers-reduced-motion: no-preference) { + & > .tooltip-content, &[data-tip]:before, &:after { + transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1) 75ms, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1) 75ms; + } + } + &:is([data-tip]:not([data-tip=""]), :has(.tooltip-content:not(:empty))) { + &.tooltip-open, &:hover, &:has(:focus-visible) { + & > .tooltip-content, &[data-tip]:before, &:after { + opacity: 100%; + --tt-pos: 0rem; + @media (prefers-reduced-motion: no-preference) { + transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1) 0s, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1) 0s; + } + } + } + } + } + @layer daisyui.l1.l2 { + > .tooltip-content, &[data-tip]:before { + transform: translateX(var(--tt-trans, -50%)) translateY(var(--tt-pos, 0.25rem)); + inset: auto auto var(--tt-off) 50%; + } + &:after { + transform: translateX(var(--tt-trans, -50%)) translateY(var(--tt-pos, 0.25rem)); + inset: auto auto var(--tt-tail) 50%; + } + } + } .tab { @layer daisyui.l1.l2.l3 { &:is(.tabs > .tab) { @@ -1590,6 +1659,116 @@ } } } + .steps { + @layer daisyui.l1.l2.l3 { + display: inline-grid; + grid-auto-flow: column; + overflow: hidden; + overflow-x: auto; + counter-reset: step; + grid-auto-columns: 1fr; + .step { + display: grid; + grid-template-columns: repeat(1, minmax(0, 1fr)); + grid-template-columns: auto; + grid-template-rows: repeat(2, minmax(0, 1fr)); + grid-template-rows: 40px 1fr; + place-items: center; + text-align: center; + min-width: 4rem; + --step-bg: var(--color-base-300); + --step-fg: var(--color-base-content); + &:before { + top: 0; + grid-column-start: 1; + grid-row-start: 1; + height: calc(0.25rem * 2); + width: 100%; + border: 1px solid; + color: var(--step-bg); + background-color: var(--step-bg); + content: ""; + margin-inline-start: -100%; + } + > .step-icon, &:not(:has(.step-icon)):after { + --tw-content: counter(step); + content: var(--tw-content); + counter-increment: step; + z-index: 1; + color: var(--step-fg); + background-color: var(--step-bg); + border: 1px solid var(--step-bg); + position: relative; + grid-column-start: 1; + grid-row-start: 1; + display: grid; + height: calc(0.25rem * 8); + width: calc(0.25rem * 8); + place-items: center; + place-self: center; + border-radius: calc(infinity * 1px); + } + &:first-child:before { + --tw-content: none; + content: var(--tw-content); + } + &[data-content]:after { + --tw-content: attr(data-content); + content: var(--tw-content); + } + } + } + @layer daisyui.l1.l2 { + .step-neutral { + + .step-neutral:before, &:after, > .step-icon { + --step-bg: var(--color-neutral); + --step-fg: var(--color-neutral-content); + } + } + .step-primary { + + .step-primary:before, &:after, > .step-icon { + --step-bg: var(--color-primary); + --step-fg: var(--color-primary-content); + } + } + .step-secondary { + + .step-secondary:before, &:after, > .step-icon { + --step-bg: var(--color-secondary); + --step-fg: var(--color-secondary-content); + } + } + .step-accent { + + .step-accent:before, &:after, > .step-icon { + --step-bg: var(--color-accent); + --step-fg: var(--color-accent-content); + } + } + .step-info { + + .step-info:before, &:after, > .step-icon { + --step-bg: var(--color-info); + --step-fg: var(--color-info-content); + } + } + .step-success { + + .step-success:before, &:after, > .step-icon { + --step-bg: var(--color-success); + --step-fg: var(--color-success-content); + } + } + .step-warning { + + .step-warning:before, &:after, > .step-icon { + --step-bg: var(--color-warning); + --step-fg: var(--color-warning-content); + } + } + .step-error { + + .step-error:before, &:after, > .step-icon { + --step-bg: var(--color-error); + --step-fg: var(--color-error-content); + } + } + } + } .select { @layer daisyui.l1.l2.l3 { position: relative; @@ -2462,6 +2641,87 @@ } } } + .stack\! { + @layer daisyui.l1.l2.l3 { + display: inline-grid !important; + grid-template-columns: 3px 4px 1fr 4px 3px !important; + grid-template-rows: 3px 4px 1fr 4px 3px !important; + & > * { + height: 100% !important; + width: 100% !important; + &:nth-child(n + 2) { + width: 100% !important; + opacity: 70% !important; + } + &:nth-child(2) { + z-index: 2 !important; + opacity: 90% !important; + } + &:nth-child(1) { + z-index: 3 !important; + width: 100% !important; + } + } + } + @layer daisyui.l1.l2 { + &, &.stack-bottom { + > * { + grid-column: 3 / 4 !important; + grid-row: 3 / 6 !important; + &:nth-child(2) { + grid-column: 2 / 5 !important; + grid-row: 2 / 5 !important; + } + &:nth-child(1) { + grid-column: 1 / 6 !important; + grid-row: 1 / 4 !important; + } + } + } + &.stack-top { + > * { + grid-column: 3 / 4 !important; + grid-row: 1 / 4 !important; + &:nth-child(2) { + grid-column: 2 / 5 !important; + grid-row: 2 / 5 !important; + } + &:nth-child(1) { + grid-column: 1 / 6 !important; + grid-row: 3 / 6 !important; + } + } + } + &.stack-start { + > * { + grid-column: 1 / 4 !important; + grid-row: 3 / 4 !important; + &:nth-child(2) { + grid-column: 2 / 5 !important; + grid-row: 2 / 5 !important; + } + &:nth-child(1) { + grid-column: 3 / 6 !important; + grid-row: 1 / 6 !important; + } + } + } + &.stack-end { + > * { + grid-column: 3 / 6 !important; + grid-row: 3 / 4 !important; + &:nth-child(2) { + grid-column: 2 / 5 !important; + grid-row: 2 / 5 !important; + } + &:nth-child(1) { + grid-column: 1 / 4 !important; + grid-row: 1 / 6 !important; + } + } + } + } + } .z-10 { z-index: 10; } @@ -2506,6 +2766,17 @@ font-weight: 800; } } + .stat-desc { + @layer daisyui.l1.l2.l3 { + grid-column-start: 1; + white-space: nowrap; + color: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + color: color-mix(in oklab, var(--color-base-content) 60%, transparent); + } + font-size: 0.75rem; + } + } .stat-title { @layer daisyui.l1.l2.l3 { grid-column-start: 1; @@ -2640,6 +2911,9 @@ .mt-10 { margin-top: calc(var(--spacing) * 10); } + .mb-1 { + margin-bottom: var(--spacing); + } .mb-2 { margin-bottom: calc(var(--spacing) * 2); } @@ -2713,6 +2987,31 @@ flex-direction: var(--tabs-direction); } } + .footer { + @layer daisyui.l1.l2.l3 { + display: grid; + width: 100%; + grid-auto-flow: row; + place-items: start; + column-gap: calc(0.25rem * 4); + row-gap: calc(0.25rem * 10); + font-size: 0.875rem; + line-height: 1.25rem; + & > *:not(script, style, template) { + display: grid; + place-items: start; + gap: calc(0.25rem * 2); + } + &.footer-center { + grid-auto-flow: column dense; + place-items: center; + text-align: center; + & > *:not(script, style, template) { + place-items: center; + } + } + } + } .stat { @layer daisyui.l1.l2.l3 { display: inline-grid; @@ -2877,12 +3176,18 @@ .inline-flex { display: inline-flex; } + .inline-grid { + display: inline-grid; + } .table { display: table; } .h-16 { height: calc(var(--spacing) * 16); } + .h-56 { + height: calc(var(--spacing) * 56); + } .min-h-screen { min-height: 100vh; } @@ -3023,6 +3328,13 @@ margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse))); } } + .space-y-3 { + :where(& > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse)); + margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse))); + } + } .divide-y { :where(& > :not(:last-child)) { --tw-divide-y-reverse: 0; @@ -3066,6 +3378,10 @@ border-bottom-style: var(--tw-border-style); border-bottom-width: 1px; } + .border-l-4 { + border-left-style: var(--tw-border-style); + border-left-width: 4px; + } .badge-ghost { @layer daisyui.l1.l2 { border-color: var(--color-base-200); @@ -3092,6 +3408,12 @@ border-color: color-mix(in oklab, var(--color-base-content) 70%, transparent); } } + .border-error { + border-color: var(--color-error); + } + .border-warning { + border-color: var(--color-warning); + } .border-white { border-color: var(--color-white); } @@ -3158,6 +3480,9 @@ --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); } } + .mask-repeat { + mask-repeat: repeat; + } .object-contain { object-fit: contain; } @@ -3170,6 +3495,17 @@ .p-4 { padding: calc(var(--spacing) * 4); } + .table-sm { + @layer daisyui.l1.l2 { + :not(thead, tfoot) tr { + font-size: 0.75rem; + } + :where(th, td) { + padding-inline: calc(0.25rem * 3); + padding-block: calc(0.25rem * 2); + } + } + } .px-4 { padding-inline: calc(var(--spacing) * 4); } @@ -3290,6 +3626,11 @@ --alert-color: var(--color-warning); } } + .progress-primary { + @layer daisyui.l1.l2 { + color: var(--color-primary); + } + } .text-base-content { color: var(--color-base-content); } @@ -3482,11 +3823,26 @@ } } } + .sm\:grid-cols-2 { + @media (width >= 40rem) { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + } .md\:grid-cols-2 { @media (width >= 48rem) { grid-template-columns: repeat(2, minmax(0, 1fr)); } } + .lg\:grid-cols-2 { + @media (width >= 64rem) { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + } + .lg\:grid-cols-4 { + @media (width >= 64rem) { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + } } @font-face { font-family: "Ubuntu"; diff --git a/static/js/chart.js b/static/js/chart.js new file mode 100644 index 0000000..4cfce5c --- /dev/null +++ b/static/js/chart.js @@ -0,0 +1,14 @@ +/*! + * Chart.js v4.5.1 + * https://www.chartjs.org + * (c) 2025 Chart.js Contributors + * Released under the MIT License + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Chart=e()}(this,(function(){"use strict";var t=Object.freeze({__proto__:null,get Colors(){return Jo},get Decimation(){return ta},get Filler(){return ba},get Legend(){return Ma},get SubTitle(){return Pa},get Title(){return ka},get Tooltip(){return Na}});function e(){}const i=(()=>{let t=0;return()=>t++})();function s(t){return null==t}function n(t){if(Array.isArray&&Array.isArray(t))return!0;const e=Object.prototype.toString.call(t);return"[object"===e.slice(0,7)&&"Array]"===e.slice(-6)}function o(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)}function a(t){return("number"==typeof t||t instanceof Number)&&isFinite(+t)}function r(t,e){return a(t)?t:e}function l(t,e){return void 0===t?e:t}const h=(t,e)=>"string"==typeof t&&t.endsWith("%")?parseFloat(t)/100:+t/e,c=(t,e)=>"string"==typeof t&&t.endsWith("%")?parseFloat(t)/100*e:+t;function d(t,e,i){if(t&&"function"==typeof t.call)return t.apply(i,e)}function u(t,e,i,s){let a,r,l;if(n(t))if(r=t.length,s)for(a=r-1;a>=0;a--)e.call(i,t[a],a);else for(a=0;at,x:t=>t.x,y:t=>t.y};function v(t){const e=t.split("."),i=[];let s="";for(const t of e)s+=t,s.endsWith("\\")?s=s.slice(0,-1)+".":(i.push(s),s="");return i}function M(t,e){const i=y[e]||(y[e]=function(t){const e=v(t);return t=>{for(const i of e){if(""===i)break;t=t&&t[i]}return t}}(e));return i(t)}function w(t){return t.charAt(0).toUpperCase()+t.slice(1)}const k=t=>void 0!==t,S=t=>"function"==typeof t,P=(t,e)=>{if(t.size!==e.size)return!1;for(const i of t)if(!e.has(i))return!1;return!0};function D(t){return"mouseup"===t.type||"click"===t.type||"contextmenu"===t.type}const C=Math.PI,O=2*C,A=O+C,T=Number.POSITIVE_INFINITY,L=C/180,E=C/2,R=C/4,I=2*C/3,z=Math.log10,F=Math.sign;function V(t,e,i){return Math.abs(t-e)t-e)).pop(),e}function N(t){return!function(t){return"symbol"==typeof t||"object"==typeof t&&null!==t&&!(Symbol.toPrimitive in t||"toString"in t||"valueOf"in t)}(t)&&!isNaN(parseFloat(t))&&isFinite(t)}function H(t,e){const i=Math.round(t);return i-e<=t&&i+e>=t}function j(t,e,i){let s,n,o;for(s=0,n=t.length;sl&&h=Math.min(e,i)-s&&t<=Math.max(e,i)+s}function et(t,e,i){i=i||(i=>t[i]1;)s=o+n>>1,i(s)?o=s:n=s;return{lo:o,hi:n}}const it=(t,e,i,s)=>et(t,i,s?s=>{const n=t[s][e];return nt[s][e]et(t,i,(s=>t[s][e]>=i));function nt(t,e,i){let s=0,n=t.length;for(;ss&&t[n-1]>i;)n--;return s>0||n{const i="_onData"+w(e),s=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value(...e){const n=s.apply(this,e);return t._chartjs.listeners.forEach((t=>{"function"==typeof t[i]&&t[i](...e)})),n}})})))}function rt(t,e){const i=t._chartjs;if(!i)return;const s=i.listeners,n=s.indexOf(e);-1!==n&&s.splice(n,1),s.length>0||(ot.forEach((e=>{delete t[e]})),delete t._chartjs)}function lt(t){const e=new Set(t);return e.size===t.length?t:Array.from(e)}const ht="undefined"==typeof window?function(t){return t()}:window.requestAnimationFrame;function ct(t,e){let i=[],s=!1;return function(...n){i=n,s||(s=!0,ht.call(window,(()=>{s=!1,t.apply(e,i)})))}}function dt(t,e){let i;return function(...s){return e?(clearTimeout(i),i=setTimeout(t,e,s)):t.apply(this,s),e}}const ut=t=>"start"===t?"left":"end"===t?"right":"center",ft=(t,e,i)=>"start"===t?e:"end"===t?i:(e+i)/2,gt=(t,e,i,s)=>t===(s?"left":"right")?i:"center"===t?(e+i)/2:e;function pt(t,e,i){const n=e.length;let o=0,a=n;if(t._sorted){const{iScale:r,vScale:l,_parsed:h}=t,c=t.dataset&&t.dataset.options?t.dataset.options.spanGaps:null,d=r.axis,{min:u,max:f,minDefined:g,maxDefined:p}=r.getUserBounds();if(g){if(o=Math.min(it(h,d,u).lo,i?n:it(e,d,r.getPixelForValue(u)).lo),c){const t=h.slice(0,o+1).reverse().findIndex((t=>!s(t[l.axis])));o-=Math.max(0,t)}o=Z(o,0,n-1)}if(p){let t=Math.max(it(h,r.axis,f,!0).hi+1,i?0:it(e,d,r.getPixelForValue(f),!0).hi+1);if(c){const e=h.slice(t-1).findIndex((t=>!s(t[l.axis])));t+=Math.max(0,e)}a=Z(t,o,n)-o}else a=n-o}return{start:o,count:a}}function mt(t){const{xScale:e,yScale:i,_scaleRanges:s}=t,n={xmin:e.min,xmax:e.max,ymin:i.min,ymax:i.max};if(!s)return t._scaleRanges=n,!0;const o=s.xmin!==e.min||s.xmax!==e.max||s.ymin!==i.min||s.ymax!==i.max;return Object.assign(s,n),o}class xt{constructor(){this._request=null,this._charts=new Map,this._running=!1,this._lastDate=void 0}_notify(t,e,i,s){const n=e.listeners[s],o=e.duration;n.forEach((s=>s({chart:t,initial:e.initial,numSteps:o,currentStep:Math.min(i-e.start,o)})))}_refresh(){this._request||(this._running=!0,this._request=ht.call(window,(()=>{this._update(),this._request=null,this._running&&this._refresh()})))}_update(t=Date.now()){let e=0;this._charts.forEach(((i,s)=>{if(!i.running||!i.items.length)return;const n=i.items;let o,a=n.length-1,r=!1;for(;a>=0;--a)o=n[a],o._active?(o._total>i.duration&&(i.duration=o._total),o.tick(t),r=!0):(n[a]=n[n.length-1],n.pop());r&&(s.draw(),this._notify(s,i,t,"progress")),n.length||(i.running=!1,this._notify(s,i,t,"complete"),i.initial=!1),e+=n.length})),this._lastDate=t,0===e&&(this._running=!1)}_getAnims(t){const e=this._charts;let i=e.get(t);return i||(i={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},e.set(t,i)),i}listen(t,e,i){this._getAnims(t).listeners[e].push(i)}add(t,e){e&&e.length&&this._getAnims(t).items.push(...e)}has(t){return this._getAnims(t).items.length>0}start(t){const e=this._charts.get(t);e&&(e.running=!0,e.start=Date.now(),e.duration=e.items.reduce(((t,e)=>Math.max(t,e._duration)),0),this._refresh())}running(t){if(!this._running)return!1;const e=this._charts.get(t);return!!(e&&e.running&&e.items.length)}stop(t){const e=this._charts.get(t);if(!e||!e.items.length)return;const i=e.items;let s=i.length-1;for(;s>=0;--s)i[s].cancel();e.items=[],this._notify(t,e,Date.now(),"complete")}remove(t){return this._charts.delete(t)}}var bt=new xt; +/*! + * @kurkle/color v0.3.2 + * https://github.com/kurkle/color#readme + * (c) 2023 Jukka Kurkela + * Released under the MIT License + */function _t(t){return t+.5|0}const yt=(t,e,i)=>Math.max(Math.min(t,i),e);function vt(t){return yt(_t(2.55*t),0,255)}function Mt(t){return yt(_t(255*t),0,255)}function wt(t){return yt(_t(t/2.55)/100,0,1)}function kt(t){return yt(_t(100*t),0,100)}const St={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Pt=[..."0123456789ABCDEF"],Dt=t=>Pt[15&t],Ct=t=>Pt[(240&t)>>4]+Pt[15&t],Ot=t=>(240&t)>>4==(15&t);function At(t){var e=(t=>Ot(t.r)&&Ot(t.g)&&Ot(t.b)&&Ot(t.a))(t)?Dt:Ct;return t?"#"+e(t.r)+e(t.g)+e(t.b)+((t,e)=>t<255?e(t):"")(t.a,e):void 0}const Tt=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function Lt(t,e,i){const s=e*Math.min(i,1-i),n=(e,n=(e+t/30)%12)=>i-s*Math.max(Math.min(n-3,9-n,1),-1);return[n(0),n(8),n(4)]}function Et(t,e,i){const s=(s,n=(s+t/60)%6)=>i-i*e*Math.max(Math.min(n,4-n,1),0);return[s(5),s(3),s(1)]}function Rt(t,e,i){const s=Lt(t,1,.5);let n;for(e+i>1&&(n=1/(e+i),e*=n,i*=n),n=0;n<3;n++)s[n]*=1-e-i,s[n]+=e;return s}function It(t){const e=t.r/255,i=t.g/255,s=t.b/255,n=Math.max(e,i,s),o=Math.min(e,i,s),a=(n+o)/2;let r,l,h;return n!==o&&(h=n-o,l=a>.5?h/(2-n-o):h/(n+o),r=function(t,e,i,s,n){return t===n?(e-i)/s+(e>16&255,o>>8&255,255&o]}return t}(),Ht.transparent=[0,0,0,0]);const e=Ht[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:4===e.length?e[3]:255}}const $t=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;const Yt=t=>t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055,Ut=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4);function Xt(t,e,i){if(t){let s=It(t);s[e]=Math.max(0,Math.min(s[e]+s[e]*i,0===e?360:1)),s=Ft(s),t.r=s[0],t.g=s[1],t.b=s[2]}}function qt(t,e){return t?Object.assign(e||{},t):t}function Kt(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=Mt(t[3]))):(e=qt(t,{r:0,g:0,b:0,a:1})).a=Mt(e.a),e}function Gt(t){return"r"===t.charAt(0)?function(t){const e=$t.exec(t);let i,s,n,o=255;if(e){if(e[7]!==i){const t=+e[7];o=e[8]?vt(t):yt(255*t,0,255)}return i=+e[1],s=+e[3],n=+e[5],i=255&(e[2]?vt(i):yt(i,0,255)),s=255&(e[4]?vt(s):yt(s,0,255)),n=255&(e[6]?vt(n):yt(n,0,255)),{r:i,g:s,b:n,a:o}}}(t):Bt(t)}class Jt{constructor(t){if(t instanceof Jt)return t;const e=typeof t;let i;var s,n,o;"object"===e?i=Kt(t):"string"===e&&(o=(s=t).length,"#"===s[0]&&(4===o||5===o?n={r:255&17*St[s[1]],g:255&17*St[s[2]],b:255&17*St[s[3]],a:5===o?17*St[s[4]]:255}:7!==o&&9!==o||(n={r:St[s[1]]<<4|St[s[2]],g:St[s[3]]<<4|St[s[4]],b:St[s[5]]<<4|St[s[6]],a:9===o?St[s[7]]<<4|St[s[8]]:255})),i=n||jt(t)||Gt(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=qt(this._rgb);return t&&(t.a=wt(t.a)),t}set rgb(t){this._rgb=Kt(t)}rgbString(){return this._valid?(t=this._rgb)&&(t.a<255?`rgba(${t.r}, ${t.g}, ${t.b}, ${wt(t.a)})`:`rgb(${t.r}, ${t.g}, ${t.b})`):void 0;var t}hexString(){return this._valid?At(this._rgb):void 0}hslString(){return this._valid?function(t){if(!t)return;const e=It(t),i=e[0],s=kt(e[1]),n=kt(e[2]);return t.a<255?`hsla(${i}, ${s}%, ${n}%, ${wt(t.a)})`:`hsl(${i}, ${s}%, ${n}%)`}(this._rgb):void 0}mix(t,e){if(t){const i=this.rgb,s=t.rgb;let n;const o=e===n?.5:e,a=2*o-1,r=i.a-s.a,l=((a*r==-1?a:(a+r)/(1+a*r))+1)/2;n=1-l,i.r=255&l*i.r+n*s.r+.5,i.g=255&l*i.g+n*s.g+.5,i.b=255&l*i.b+n*s.b+.5,i.a=o*i.a+(1-o)*s.a,this.rgb=i}return this}interpolate(t,e){return t&&(this._rgb=function(t,e,i){const s=Ut(wt(t.r)),n=Ut(wt(t.g)),o=Ut(wt(t.b));return{r:Mt(Yt(s+i*(Ut(wt(e.r))-s))),g:Mt(Yt(n+i*(Ut(wt(e.g))-n))),b:Mt(Yt(o+i*(Ut(wt(e.b))-o))),a:t.a+i*(e.a-t.a)}}(this._rgb,t._rgb,e)),this}clone(){return new Jt(this.rgb)}alpha(t){return this._rgb.a=Mt(t),this}clearer(t){return this._rgb.a*=1-t,this}greyscale(){const t=this._rgb,e=_t(.3*t.r+.59*t.g+.11*t.b);return t.r=t.g=t.b=e,this}opaquer(t){return this._rgb.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return Xt(this._rgb,2,t),this}darken(t){return Xt(this._rgb,2,-t),this}saturate(t){return Xt(this._rgb,1,t),this}desaturate(t){return Xt(this._rgb,1,-t),this}rotate(t){return function(t,e){var i=It(t);i[0]=Vt(i[0]+e),i=Ft(i),t.r=i[0],t.g=i[1],t.b=i[2]}(this._rgb,t),this}}function Zt(t){if(t&&"object"==typeof t){const e=t.toString();return"[object CanvasPattern]"===e||"[object CanvasGradient]"===e}return!1}function Qt(t){return Zt(t)?t:new Jt(t)}function te(t){return Zt(t)?t:new Jt(t).saturate(.5).darken(.1).hexString()}const ee=["x","y","borderWidth","radius","tension"],ie=["color","borderColor","backgroundColor"];const se=new Map;function ne(t,e,i){return function(t,e){e=e||{};const i=t+JSON.stringify(e);let s=se.get(i);return s||(s=new Intl.NumberFormat(t,e),se.set(i,s)),s}(e,i).format(t)}const oe={values:t=>n(t)?t:""+t,numeric(t,e,i){if(0===t)return"0";const s=this.chart.options.locale;let n,o=t;if(i.length>1){const e=Math.max(Math.abs(i[0].value),Math.abs(i[i.length-1].value));(e<1e-4||e>1e15)&&(n="scientific"),o=function(t,e){let i=e.length>3?e[2].value-e[1].value:e[1].value-e[0].value;Math.abs(i)>=1&&t!==Math.floor(t)&&(i=t-Math.floor(t));return i}(t,i)}const a=z(Math.abs(o)),r=isNaN(a)?1:Math.max(Math.min(-1*Math.floor(a),20),0),l={notation:n,minimumFractionDigits:r,maximumFractionDigits:r};return Object.assign(l,this.options.ticks.format),ne(t,s,l)},logarithmic(t,e,i){if(0===t)return"0";const s=i[e].significand||t/Math.pow(10,Math.floor(z(t)));return[1,2,3,5,10,15].includes(s)||e>.8*i.length?oe.numeric.call(this,t,e,i):""}};var ae={formatters:oe};const re=Object.create(null),le=Object.create(null);function he(t,e){if(!e)return t;const i=e.split(".");for(let e=0,s=i.length;et.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(t,e)=>te(e.backgroundColor),this.hoverBorderColor=(t,e)=>te(e.borderColor),this.hoverColor=(t,e)=>te(e.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(e)}set(t,e){return ce(this,t,e)}get(t){return he(this,t)}describe(t,e){return ce(le,t,e)}override(t,e){return ce(re,t,e)}route(t,e,i,s){const n=he(this,t),a=he(this,i),r="_"+e;Object.defineProperties(n,{[r]:{value:n[e],writable:!0},[e]:{enumerable:!0,get(){const t=this[r],e=a[s];return o(t)?Object.assign({},e,t):l(t,e)},set(t){this[r]=t}}})}apply(t){t.forEach((t=>t(this)))}}var ue=new de({_scriptable:t=>!t.startsWith("on"),_indexable:t=>"events"!==t,hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[function(t){t.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),t.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>"onProgress"!==t&&"onComplete"!==t&&"fn"!==t}),t.set("animations",{colors:{type:"color",properties:ie},numbers:{type:"number",properties:ee}}),t.describe("animations",{_fallback:"animation"}),t.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>0|t}}}})},function(t){t.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})},function(t){t.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:ae.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),t.route("scale.ticks","color","","color"),t.route("scale.grid","color","","borderColor"),t.route("scale.border","color","","borderColor"),t.route("scale.title","color","","color"),t.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&"callback"!==t&&"parser"!==t,_indexable:t=>"borderDash"!==t&&"tickBorderDash"!==t&&"dash"!==t}),t.describe("scales",{_fallback:"scale"}),t.describe("scale.ticks",{_scriptable:t=>"backdropPadding"!==t&&"callback"!==t,_indexable:t=>"backdropPadding"!==t})}]);function fe(){return"undefined"!=typeof window&&"undefined"!=typeof document}function ge(t){let e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e}function pe(t,e,i){let s;return"string"==typeof t?(s=parseInt(t,10),-1!==t.indexOf("%")&&(s=s/100*e.parentNode[i])):s=t,s}const me=t=>t.ownerDocument.defaultView.getComputedStyle(t,null);function xe(t,e){return me(t).getPropertyValue(e)}const be=["top","right","bottom","left"];function _e(t,e,i){const s={};i=i?"-"+i:"";for(let n=0;n<4;n++){const o=be[n];s[o]=parseFloat(t[e+"-"+o+i])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}const ye=(t,e,i)=>(t>0||e>0)&&(!i||!i.shadowRoot);function ve(t,e){if("native"in t)return t;const{canvas:i,currentDevicePixelRatio:s}=e,n=me(i),o="border-box"===n.boxSizing,a=_e(n,"padding"),r=_e(n,"border","width"),{x:l,y:h,box:c}=function(t,e){const i=t.touches,s=i&&i.length?i[0]:t,{offsetX:n,offsetY:o}=s;let a,r,l=!1;if(ye(n,o,t.target))a=n,r=o;else{const t=e.getBoundingClientRect();a=s.clientX-t.left,r=s.clientY-t.top,l=!0}return{x:a,y:r,box:l}}(t,i),d=a.left+(c&&r.left),u=a.top+(c&&r.top);let{width:f,height:g}=e;return o&&(f-=a.width+r.width,g-=a.height+r.height),{x:Math.round((l-d)/f*i.width/s),y:Math.round((h-u)/g*i.height/s)}}const Me=t=>Math.round(10*t)/10;function we(t,e,i,s){const n=me(t),o=_e(n,"margin"),a=pe(n.maxWidth,t,"clientWidth")||T,r=pe(n.maxHeight,t,"clientHeight")||T,l=function(t,e,i){let s,n;if(void 0===e||void 0===i){const o=t&&ge(t);if(o){const t=o.getBoundingClientRect(),a=me(o),r=_e(a,"border","width"),l=_e(a,"padding");e=t.width-l.width-r.width,i=t.height-l.height-r.height,s=pe(a.maxWidth,o,"clientWidth"),n=pe(a.maxHeight,o,"clientHeight")}else e=t.clientWidth,i=t.clientHeight}return{width:e,height:i,maxWidth:s||T,maxHeight:n||T}}(t,e,i);let{width:h,height:c}=l;if("content-box"===n.boxSizing){const t=_e(n,"border","width"),e=_e(n,"padding");h-=e.width+t.width,c-=e.height+t.height}h=Math.max(0,h-o.width),c=Math.max(0,s?h/s:c-o.height),h=Me(Math.min(h,a,l.maxWidth)),c=Me(Math.min(c,r,l.maxHeight)),h&&!c&&(c=Me(h/2));return(void 0!==e||void 0!==i)&&s&&l.height&&c>l.height&&(c=l.height,h=Me(Math.floor(c*s))),{width:h,height:c}}function ke(t,e,i){const s=e||1,n=Me(t.height*s),o=Me(t.width*s);t.height=Me(t.height),t.width=Me(t.width);const a=t.canvas;return a.style&&(i||!a.style.height&&!a.style.width)&&(a.style.height=`${t.height}px`,a.style.width=`${t.width}px`),(t.currentDevicePixelRatio!==s||a.height!==n||a.width!==o)&&(t.currentDevicePixelRatio=s,a.height=n,a.width=o,t.ctx.setTransform(s,0,0,s,0,0),!0)}const Se=function(){let t=!1;try{const e={get passive(){return t=!0,!1}};fe()&&(window.addEventListener("test",null,e),window.removeEventListener("test",null,e))}catch(t){}return t}();function Pe(t,e){const i=xe(t,e),s=i&&i.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function De(t){return!t||s(t.size)||s(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}function Ce(t,e,i,s,n){let o=e[n];return o||(o=e[n]=t.measureText(n).width,i.push(n)),o>s&&(s=o),s}function Oe(t,e,i,s){let o=(s=s||{}).data=s.data||{},a=s.garbageCollect=s.garbageCollect||[];s.font!==e&&(o=s.data={},a=s.garbageCollect=[],s.font=e),t.save(),t.font=e;let r=0;const l=i.length;let h,c,d,u,f;for(h=0;hi.length){for(h=0;h0&&t.stroke()}}function Re(t,e,i){return i=i||.5,!e||t&&t.x>e.left-i&&t.xe.top-i&&t.y0&&""!==r.strokeColor;let c,d;for(t.save(),t.font=a.string,function(t,e){e.translation&&t.translate(e.translation[0],e.translation[1]),s(e.rotation)||t.rotate(e.rotation),e.color&&(t.fillStyle=e.color),e.textAlign&&(t.textAlign=e.textAlign),e.textBaseline&&(t.textBaseline=e.textBaseline)}(t,r),c=0;ct[0])){const o=i||t;void 0===s&&(s=ti("_fallback",t));const a={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:t,_rootScopes:o,_fallback:s,_getTarget:n,override:i=>je([i,...t],e,o,s)};return new Proxy(a,{deleteProperty:(e,i)=>(delete e[i],delete e._keys,delete t[0][i],!0),get:(i,s)=>qe(i,s,(()=>function(t,e,i,s){let n;for(const o of e)if(n=ti(Ue(o,t),i),void 0!==n)return Xe(t,n)?Ze(i,s,t,n):n}(s,e,t,i))),getOwnPropertyDescriptor:(t,e)=>Reflect.getOwnPropertyDescriptor(t._scopes[0],e),getPrototypeOf:()=>Reflect.getPrototypeOf(t[0]),has:(t,e)=>ei(t).includes(e),ownKeys:t=>ei(t),set(t,e,i){const s=t._storage||(t._storage=n());return t[e]=s[e]=i,delete t._keys,!0}})}function $e(t,e,i,s){const a={_cacheable:!1,_proxy:t,_context:e,_subProxy:i,_stack:new Set,_descriptors:Ye(t,s),setContext:e=>$e(t,e,i,s),override:n=>$e(t.override(n),e,i,s)};return new Proxy(a,{deleteProperty:(e,i)=>(delete e[i],delete t[i],!0),get:(t,e,i)=>qe(t,e,(()=>function(t,e,i){const{_proxy:s,_context:a,_subProxy:r,_descriptors:l}=t;let h=s[e];S(h)&&l.isScriptable(e)&&(h=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_stack:r}=i;if(r.has(t))throw new Error("Recursion detected: "+Array.from(r).join("->")+"->"+t);r.add(t);let l=e(o,a||s);r.delete(t),Xe(t,l)&&(l=Ze(n._scopes,n,t,l));return l}(e,h,t,i));n(h)&&h.length&&(h=function(t,e,i,s){const{_proxy:n,_context:a,_subProxy:r,_descriptors:l}=i;if(void 0!==a.index&&s(t))return e[a.index%e.length];if(o(e[0])){const i=e,s=n._scopes.filter((t=>t!==i));e=[];for(const o of i){const i=Ze(s,n,t,o);e.push($e(i,a,r&&r[t],l))}}return e}(e,h,t,l.isIndexable));Xe(e,h)&&(h=$e(h,a,r&&r[e],l));return h}(t,e,i))),getOwnPropertyDescriptor:(e,i)=>e._descriptors.allKeys?Reflect.has(t,i)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,i),getPrototypeOf:()=>Reflect.getPrototypeOf(t),has:(e,i)=>Reflect.has(t,i),ownKeys:()=>Reflect.ownKeys(t),set:(e,i,s)=>(t[i]=s,delete e[i],!0)})}function Ye(t,e={scriptable:!0,indexable:!0}){const{_scriptable:i=e.scriptable,_indexable:s=e.indexable,_allKeys:n=e.allKeys}=t;return{allKeys:n,scriptable:i,indexable:s,isScriptable:S(i)?i:()=>i,isIndexable:S(s)?s:()=>s}}const Ue=(t,e)=>t?t+w(e):e,Xe=(t,e)=>o(e)&&"adapters"!==t&&(null===Object.getPrototypeOf(e)||e.constructor===Object);function qe(t,e,i){if(Object.prototype.hasOwnProperty.call(t,e)||"constructor"===e)return t[e];const s=i();return t[e]=s,s}function Ke(t,e,i){return S(t)?t(e,i):t}const Ge=(t,e)=>!0===t?e:"string"==typeof t?M(e,t):void 0;function Je(t,e,i,s,n){for(const o of e){const e=Ge(i,o);if(e){t.add(e);const o=Ke(e._fallback,i,n);if(void 0!==o&&o!==i&&o!==s)return o}else if(!1===e&&void 0!==s&&i!==s)return null}return!1}function Ze(t,e,i,s){const a=e._rootScopes,r=Ke(e._fallback,i,s),l=[...t,...a],h=new Set;h.add(s);let c=Qe(h,l,i,r||i,s);return null!==c&&((void 0===r||r===i||(c=Qe(h,l,r,c,s),null!==c))&&je(Array.from(h),[""],a,r,(()=>function(t,e,i){const s=t._getTarget();e in s||(s[e]={});const a=s[e];if(n(a)&&o(i))return i;return a||{}}(e,i,s))))}function Qe(t,e,i,s,n){for(;i;)i=Je(t,e,i,s,n);return i}function ti(t,e){for(const i of e){if(!i)continue;const e=i[t];if(void 0!==e)return e}}function ei(t){let e=t._keys;return e||(e=t._keys=function(t){const e=new Set;for(const i of t)for(const t of Object.keys(i).filter((t=>!t.startsWith("_"))))e.add(t);return Array.from(e)}(t._scopes)),e}function ii(t,e,i,s){const{iScale:n}=t,{key:o="r"}=this._parsing,a=new Array(s);let r,l,h,c;for(r=0,l=s;re"x"===t?"y":"x";function ai(t,e,i,s){const n=t.skip?e:t,o=e,a=i.skip?e:i,r=q(o,n),l=q(a,o);let h=r/(r+l),c=l/(r+l);h=isNaN(h)?0:h,c=isNaN(c)?0:c;const d=s*h,u=s*c;return{previous:{x:o.x-d*(a.x-n.x),y:o.y-d*(a.y-n.y)},next:{x:o.x+u*(a.x-n.x),y:o.y+u*(a.y-n.y)}}}function ri(t,e="x"){const i=oi(e),s=t.length,n=Array(s).fill(0),o=Array(s);let a,r,l,h=ni(t,0);for(a=0;a!t.skip))),"monotone"===e.cubicInterpolationMode)ri(t,n);else{let i=s?t[t.length-1]:t[0];for(o=0,a=t.length;o0===t||1===t,di=(t,e,i)=>-Math.pow(2,10*(t-=1))*Math.sin((t-e)*O/i),ui=(t,e,i)=>Math.pow(2,-10*t)*Math.sin((t-e)*O/i)+1,fi={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*E),easeOutSine:t=>Math.sin(t*E),easeInOutSine:t=>-.5*(Math.cos(C*t)-1),easeInExpo:t=>0===t?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>1===t?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>ci(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>ci(t)?t:di(t,.075,.3),easeOutElastic:t=>ci(t)?t:ui(t,.075,.3),easeInOutElastic(t){const e=.1125;return ci(t)?t:t<.5?.5*di(2*t,e,.45):.5+.5*ui(2*t-1,e,.45)},easeInBack(t){const e=1.70158;return t*t*((e+1)*t-e)},easeOutBack(t){const e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:t=>1-fi.easeOutBounce(1-t),easeOutBounce(t){const e=7.5625,i=2.75;return t<1/i?e*t*t:t<2/i?e*(t-=1.5/i)*t+.75:t<2.5/i?e*(t-=2.25/i)*t+.9375:e*(t-=2.625/i)*t+.984375},easeInOutBounce:t=>t<.5?.5*fi.easeInBounce(2*t):.5*fi.easeOutBounce(2*t-1)+.5};function gi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:t.y+i*(e.y-t.y)}}function pi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:"middle"===s?i<.5?t.y:e.y:"after"===s?i<1?t.y:e.y:i>0?e.y:t.y}}function mi(t,e,i,s){const n={x:t.cp2x,y:t.cp2y},o={x:e.cp1x,y:e.cp1y},a=gi(t,n,i),r=gi(n,o,i),l=gi(o,e,i),h=gi(a,r,i),c=gi(r,l,i);return gi(h,c,i)}const xi=/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/,bi=/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;function _i(t,e){const i=(""+t).match(xi);if(!i||"normal"===i[1])return 1.2*e;switch(t=+i[2],i[3]){case"px":return t;case"%":t/=100}return e*t}const yi=t=>+t||0;function vi(t,e){const i={},s=o(e),n=s?Object.keys(e):e,a=o(t)?s?i=>l(t[i],t[e[i]]):e=>t[e]:()=>t;for(const t of n)i[t]=yi(a(t));return i}function Mi(t){return vi(t,{top:"y",right:"x",bottom:"y",left:"x"})}function wi(t){return vi(t,["topLeft","topRight","bottomLeft","bottomRight"])}function ki(t){const e=Mi(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function Si(t,e){t=t||{},e=e||ue.font;let i=l(t.size,e.size);"string"==typeof i&&(i=parseInt(i,10));let s=l(t.style,e.style);s&&!(""+s).match(bi)&&(console.warn('Invalid font style specified: "'+s+'"'),s=void 0);const n={family:l(t.family,e.family),lineHeight:_i(l(t.lineHeight,e.lineHeight),i),size:i,style:s,weight:l(t.weight,e.weight),string:""};return n.string=De(n),n}function Pi(t,e,i,s){let o,a,r,l=!0;for(o=0,a=t.length;oi&&0===t?0:t+e;return{min:a(s,-Math.abs(o)),max:a(n,o)}}function Ci(t,e){return Object.assign(Object.create(t),e)}function Oi(t,e,i){return t?function(t,e){return{x:i=>t+t+e-i,setWidth(t){e=t},textAlign:t=>"center"===t?t:"right"===t?"left":"right",xPlus:(t,e)=>t-e,leftForLtr:(t,e)=>t-e}}(e,i):{x:t=>t,setWidth(t){},textAlign:t=>t,xPlus:(t,e)=>t+e,leftForLtr:(t,e)=>t}}function Ai(t,e){let i,s;"ltr"!==e&&"rtl"!==e||(i=t.canvas.style,s=[i.getPropertyValue("direction"),i.getPropertyPriority("direction")],i.setProperty("direction",e,"important"),t.prevTextDirection=s)}function Ti(t,e){void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}function Li(t){return"angle"===t?{between:J,compare:K,normalize:G}:{between:tt,compare:(t,e)=>t-e,normalize:t=>t}}function Ei({start:t,end:e,count:i,loop:s,style:n}){return{start:t%i,end:e%i,loop:s&&(e-t+1)%i==0,style:n}}function Ri(t,e,i){if(!i)return[t];const{property:s,start:n,end:o}=i,a=e.length,{compare:r,between:l,normalize:h}=Li(s),{start:c,end:d,loop:u,style:f}=function(t,e,i){const{property:s,start:n,end:o}=i,{between:a,normalize:r}=Li(s),l=e.length;let h,c,{start:d,end:u,loop:f}=t;if(f){for(d+=l,u+=l,h=0,c=l;hb||l(n,x,p)&&0!==r(n,x),v=()=>!b||0===r(o,p)||l(o,x,p);for(let t=c,i=c;t<=d;++t)m=e[t%a],m.skip||(p=h(m[s]),p!==x&&(b=l(p,n,o),null===_&&y()&&(_=0===r(p,n)?t:i),null!==_&&v()&&(g.push(Ei({start:_,end:t,loop:u,count:a,style:f})),_=null),i=t,x=p));return null!==_&&g.push(Ei({start:_,end:d,loop:u,count:a,style:f})),g}function Ii(t,e){const i=[],s=t.segments;for(let n=0;nn&&t[o%e].skip;)o--;return o%=e,{start:n,end:o}}(i,n,o,s);if(!0===s)return Fi(t,[{start:a,end:r,loop:o}],i,e);return Fi(t,function(t,e,i,s){const n=t.length,o=[];let a,r=e,l=t[e];for(a=e+1;a<=i;++a){const i=t[a%n];i.skip||i.stop?l.skip||(s=!1,o.push({start:e%n,end:(a-1)%n,loop:s}),e=r=i.stop?a:null):(r=a,l.skip&&(e=a)),l=i}return null!==r&&o.push({start:e%n,end:r%n,loop:s}),o}(i,a,r!s(t[e.axis])));n.lo-=Math.max(0,a);const r=i.slice(n.hi).findIndex((t=>!s(t[e.axis])));n.hi+=Math.max(0,r)}return n}if(o._sharedOptions){const t=a[0],s="function"==typeof t.getRange&&t.getRange(e);if(s){const t=r(a,e,i-s),n=r(a,e,i+s);return{lo:t.lo,hi:n.hi}}}}return{lo:0,hi:a.length-1}}function $i(t,e,i,s,n){const o=t.getSortedVisibleDatasetMetas(),a=i[e];for(let t=0,i=o.length;t{t[a]&&t[a](e[i],n)&&(o.push({element:t,datasetIndex:s,index:l}),r=r||t.inRange(e.x,e.y,n))})),s&&!r?[]:o}var Ki={evaluateInteractionItems:$i,modes:{index(t,e,i,s){const n=ve(e,t),o=i.axis||"x",a=i.includeInvisible||!1,r=i.intersect?Yi(t,n,o,s,a):Xi(t,n,o,!1,s,a),l=[];return r.length?(t.getSortedVisibleDatasetMetas().forEach((t=>{const e=r[0].index,i=t.data[e];i&&!i.skip&&l.push({element:i,datasetIndex:t.index,index:e})})),l):[]},dataset(t,e,i,s){const n=ve(e,t),o=i.axis||"xy",a=i.includeInvisible||!1;let r=i.intersect?Yi(t,n,o,s,a):Xi(t,n,o,!1,s,a);if(r.length>0){const e=r[0].datasetIndex,i=t.getDatasetMeta(e).data;r=[];for(let t=0;tYi(t,ve(e,t),i.axis||"xy",s,i.includeInvisible||!1),nearest(t,e,i,s){const n=ve(e,t),o=i.axis||"xy",a=i.includeInvisible||!1;return Xi(t,n,o,i.intersect,s,a)},x:(t,e,i,s)=>qi(t,ve(e,t),"x",i.intersect,s),y:(t,e,i,s)=>qi(t,ve(e,t),"y",i.intersect,s)}};const Gi=["left","top","right","bottom"];function Ji(t,e){return t.filter((t=>t.pos===e))}function Zi(t,e){return t.filter((t=>-1===Gi.indexOf(t.pos)&&t.box.axis===e))}function Qi(t,e){return t.sort(((t,i)=>{const s=e?i:t,n=e?t:i;return s.weight===n.weight?s.index-n.index:s.weight-n.weight}))}function ts(t,e){const i=function(t){const e={};for(const i of t){const{stack:t,pos:s,stackWeight:n}=i;if(!t||!Gi.includes(s))continue;const o=e[t]||(e[t]={count:0,placed:0,weight:0,size:0});o.count++,o.weight+=n}return e}(t),{vBoxMaxWidth:s,hBoxMaxHeight:n}=e;let o,a,r;for(o=0,a=t.length;o{s[t]=Math.max(e[t],i[t])})),s}return s(t?["left","right"]:["top","bottom"])}function os(t,e,i,s){const n=[];let o,a,r,l,h,c;for(o=0,a=t.length,h=0;ot.box.fullSize)),!0),s=Qi(Ji(e,"left"),!0),n=Qi(Ji(e,"right")),o=Qi(Ji(e,"top"),!0),a=Qi(Ji(e,"bottom")),r=Zi(e,"x"),l=Zi(e,"y");return{fullSize:i,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(a).concat(r),chartArea:Ji(e,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(a).concat(r)}}(t.boxes),l=r.vertical,h=r.horizontal;u(t.boxes,(t=>{"function"==typeof t.beforeLayout&&t.beforeLayout()}));const c=l.reduce(((t,e)=>e.box.options&&!1===e.box.options.display?t:t+1),0)||1,d=Object.freeze({outerWidth:e,outerHeight:i,padding:n,availableWidth:o,availableHeight:a,vBoxMaxWidth:o/2/c,hBoxMaxHeight:a/2}),f=Object.assign({},n);is(f,ki(s));const g=Object.assign({maxPadding:f,w:o,h:a,x:n.left,y:n.top},n),p=ts(l.concat(h),d);os(r.fullSize,g,d,p),os(l,g,d,p),os(h,g,d,p)&&os(l,g,d,p),function(t){const e=t.maxPadding;function i(i){const s=Math.max(e[i]-t[i],0);return t[i]+=s,s}t.y+=i("top"),t.x+=i("left"),i("right"),i("bottom")}(g),rs(r.leftAndTop,g,d,p),g.x+=g.w,g.y+=g.h,rs(r.rightAndBottom,g,d,p),t.chartArea={left:g.left,top:g.top,right:g.left+g.w,bottom:g.top+g.h,height:g.h,width:g.w},u(r.chartArea,(e=>{const i=e.box;Object.assign(i,t.chartArea),i.update(g.w,g.h,{left:0,top:0,right:0,bottom:0})}))}};class hs{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,i){}removeEventListener(t,e,i){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,i,s){return e=Math.max(0,e||t.width),i=i||t.height,{width:e,height:Math.max(0,s?Math.floor(e/s):i)}}isAttached(t){return!0}updateConfig(t){}}class cs extends hs{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const ds="$chartjs",us={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},fs=t=>null===t||""===t;const gs=!!Se&&{passive:!0};function ps(t,e,i){t&&t.canvas&&t.canvas.removeEventListener(e,i,gs)}function ms(t,e){for(const i of t)if(i===e||i.contains(e))return!0}function xs(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||ms(i.addedNodes,s),e=e&&!ms(i.removedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}function bs(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||ms(i.removedNodes,s),e=e&&!ms(i.addedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}const _s=new Map;let ys=0;function vs(){const t=window.devicePixelRatio;t!==ys&&(ys=t,_s.forEach(((e,i)=>{i.currentDevicePixelRatio!==t&&e()})))}function Ms(t,e,i){const s=t.canvas,n=s&&ge(s);if(!n)return;const o=ct(((t,e)=>{const s=n.clientWidth;i(t,e),s{const e=t[0],i=e.contentRect.width,s=e.contentRect.height;0===i&&0===s||o(i,s)}));return a.observe(n),function(t,e){_s.size||window.addEventListener("resize",vs),_s.set(t,e)}(t,o),a}function ws(t,e,i){i&&i.disconnect(),"resize"===e&&function(t){_s.delete(t),_s.size||window.removeEventListener("resize",vs)}(t)}function ks(t,e,i){const s=t.canvas,n=ct((e=>{null!==t.ctx&&i(function(t,e){const i=us[t.type]||t.type,{x:s,y:n}=ve(t,e);return{type:i,chart:e,native:t,x:void 0!==s?s:null,y:void 0!==n?n:null}}(e,t))}),t);return function(t,e,i){t&&t.addEventListener(e,i,gs)}(s,e,n),n}class Ss extends hs{acquireContext(t,e){const i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(function(t,e){const i=t.style,s=t.getAttribute("height"),n=t.getAttribute("width");if(t[ds]={initial:{height:s,width:n,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||"block",i.boxSizing=i.boxSizing||"border-box",fs(n)){const e=Pe(t,"width");void 0!==e&&(t.width=e)}if(fs(s))if(""===t.style.height)t.height=t.width/(e||2);else{const e=Pe(t,"height");void 0!==e&&(t.height=e)}}(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e[ds])return!1;const i=e[ds].initial;["height","width"].forEach((t=>{const n=i[t];s(n)?e.removeAttribute(t):e.setAttribute(t,n)}));const n=i.style||{};return Object.keys(n).forEach((t=>{e.style[t]=n[t]})),e.width=e.width,delete e[ds],!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),n={attach:xs,detach:bs,resize:Ms}[e]||ks;s[e]=n(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:ws,detach:ws,resize:ws}[e]||ps)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return we(t,e,i,s)}isAttached(t){const e=t&&ge(t);return!(!e||!e.isConnected)}}function Ps(t){return!fe()||"undefined"!=typeof OffscreenCanvas&&t instanceof OffscreenCanvas?cs:Ss}var Ds=Object.freeze({__proto__:null,BasePlatform:hs,BasicPlatform:cs,DomPlatform:Ss,_detectPlatform:Ps});const Cs="transparent",Os={boolean:(t,e,i)=>i>.5?e:t,color(t,e,i){const s=Qt(t||Cs),n=s.valid&&Qt(e||Cs);return n&&n.valid?n.mix(s,i).hexString():e},number:(t,e,i)=>t+(e-t)*i};class As{constructor(t,e,i,s){const n=e[i];s=Pi([t.to,s,n,t.from]);const o=Pi([t.from,n,s]);this._active=!0,this._fn=t.fn||Os[t.type||typeof o],this._easing=fi[t.easing]||fi.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=i,this._from=o,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],n=i-this._start,o=this._duration-n;this._start=i,this._duration=Math.floor(Math.max(o,t.duration)),this._total+=n,this._loop=!!t.loop,this._to=Pi([t.to,e,s,t.from]),this._from=Pi([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,n=this._from,o=this._loop,a=this._to;let r;if(this._active=n!==a&&(o||e1?2-r:r,r=this._easing(Math.min(1,Math.max(0,r))),this._target[s]=this._fn(n,a,r))}wait(){const t=this._promises||(this._promises=[]);return new Promise(((e,i)=>{t.push({res:e,rej:i})}))}_notify(t){const e=t?"res":"rej",i=this._promises||[];for(let t=0;t{const a=t[s];if(!o(a))return;const r={};for(const t of e)r[t]=a[t];(n(a.properties)&&a.properties||[s]).forEach((t=>{t!==s&&i.has(t)||i.set(t,r)}))}))}_animateOptions(t,e){const i=e.options,s=function(t,e){if(!e)return;let i=t.options;if(!i)return void(t.options=e);i.$shared&&(t.options=i=Object.assign({},i,{$shared:!1,$animations:{}}));return i}(t,i);if(!s)return[];const n=this._createAnimations(s,i);return i.$shared&&function(t,e){const i=[],s=Object.keys(e);for(let e=0;e{t.options=i}),(()=>{})),n}_createAnimations(t,e){const i=this._properties,s=[],n=t.$animations||(t.$animations={}),o=Object.keys(e),a=Date.now();let r;for(r=o.length-1;r>=0;--r){const l=o[r];if("$"===l.charAt(0))continue;if("options"===l){s.push(...this._animateOptions(t,e));continue}const h=e[l];let c=n[l];const d=i.get(l);if(c){if(d&&c.active()){c.update(d,h,a);continue}c.cancel()}d&&d.duration?(n[l]=c=new As(d,t,l,h),s.push(c)):t[l]=h}return s}update(t,e){if(0===this._properties.size)return void Object.assign(t,e);const i=this._createAnimations(t,e);return i.length?(bt.add(this._chart,i),!0):void 0}}function Ls(t,e){const i=t&&t.options||{},s=i.reverse,n=void 0===i.min?e:0,o=void 0===i.max?e:0;return{start:s?o:n,end:s?n:o}}function Es(t,e){const i=[],s=t._getSortedDatasetMetas(e);let n,o;for(n=0,o=s.length;n0||!i&&e<0)return n.index}return null}function Vs(t,e){const{chart:i,_cachedMeta:s}=t,n=i._stacks||(i._stacks={}),{iScale:o,vScale:a,index:r}=s,l=o.axis,h=a.axis,c=function(t,e,i){return`${t.id}.${e.id}.${i.stack||i.type}`}(o,a,s),d=e.length;let u;for(let t=0;ti[t].axis===e)).shift()}function Ws(t,e){const i=t.controller.index,s=t.vScale&&t.vScale.axis;if(s){e=e||t._parsed;for(const t of e){const e=t._stacks;if(!e||void 0===e[s]||void 0===e[s][i])return;delete e[s][i],void 0!==e[s]._visualValues&&void 0!==e[s]._visualValues[i]&&delete e[s]._visualValues[i]}}}const Ns=t=>"reset"===t||"none"===t,Hs=(t,e)=>e?t:Object.assign({},t);class js{static defaults={};static datasetElementType=null;static dataElementType=null;constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=Is(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Ws(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(t,e,i,s)=>"x"===t?e:"r"===t?s:i,n=e.xAxisID=l(i.xAxisID,Bs(t,"x")),o=e.yAxisID=l(i.yAxisID,Bs(t,"y")),a=e.rAxisID=l(i.rAxisID,Bs(t,"r")),r=e.indexAxis,h=e.iAxisID=s(r,n,o,a),c=e.vAxisID=s(r,o,n,a);e.xScale=this.getScaleForId(n),e.yScale=this.getScaleForId(o),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(h),e.vScale=this.getScaleForId(c)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&rt(this._data,this),t._stacked&&Ws(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(o(e)){const t=this._cachedMeta;this._data=function(t,e){const{iScale:i,vScale:s}=e,n="x"===i.axis?"x":"y",o="x"===s.axis?"x":"y",a=Object.keys(t),r=new Array(a.length);let l,h,c;for(l=0,h=a.length;l0&&i._parsed[t-1];if(!1===this._parsing)i._parsed=s,i._sorted=!0,d=s;else{d=n(s[t])?this.parseArrayData(i,s,t,e):o(s[t])?this.parseObjectData(i,s,t,e):this.parsePrimitiveData(i,s,t,e);const a=()=>null===c[l]||f&&c[l]t&&!e.hidden&&e._stacked&&{keys:Es(i,!0),values:null})(e,i,this.chart),h={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:c,max:d}=function(t){const{min:e,max:i,minDefined:s,maxDefined:n}=t.getUserBounds();return{min:s?e:Number.NEGATIVE_INFINITY,max:n?i:Number.POSITIVE_INFINITY}}(r);let u,f;function g(){f=s[u];const e=f[r.axis];return!a(f[t.axis])||c>e||d=0;--u)if(!g()){this.updateRangeFromParsed(h,t,f,l);break}return h}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,n,o;for(s=0,n=e.length;s=0&&tthis.getContext(i,s,e)),c);return f.$shared&&(f.$shared=r,n[o]=Object.freeze(Hs(f,r))),f}_resolveAnimations(t,e,i){const s=this.chart,n=this._cachedDataOpts,o=`animation-${e}`,a=n[o];if(a)return a;let r;if(!1!==s.options.animation){const s=this.chart.config,n=s.datasetAnimationScopeKeys(this._type,e),o=s.getOptionScopes(this.getDataset(),n);r=s.createResolver(o,this.getContext(t,i,e))}const l=new Ts(s,r&&r.animations);return r&&r._cacheable&&(n[o]=Object.freeze(l)),l}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||Ns(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const i=this.resolveDataElementOptions(t,e),s=this._sharedOptions,n=this.getSharedOptions(i),o=this.includeOptions(e,n)||n!==s;return this.updateSharedOptions(n,e,i),{sharedOptions:n,includeOptions:o}}updateElement(t,e,i,s){Ns(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!Ns(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const n=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(n)||n})}removeHoverStyle(t,e,i){this._setStyle(t,i,"active",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,i=this._cachedMeta.data;for(const[t,e,i]of this._syncList)this[t](e,i);this._syncList=[];const s=i.length,n=e.length,o=Math.min(n,s);o&&this.parse(0,o),n>s?this._insertElements(s,n-s,t):n{for(t.length+=e,a=t.length-1;a>=o;a--)t[a]=t[a-e]};for(r(n),a=t;a{s[t]=i[t]&&i[t].active()?i[t]._to:this[t]})),s}}function Ys(t,e){const i=t.options.ticks,n=function(t){const e=t.options.offset,i=t._tickSize(),s=t._length/i+(e?0:1),n=t._maxLength/i;return Math.floor(Math.min(s,n))}(t),o=Math.min(i.maxTicksLimit||n,n),a=i.major.enabled?function(t){const e=[];let i,s;for(i=0,s=t.length;io)return function(t,e,i,s){let n,o=0,a=i[0];for(s=Math.ceil(s),n=0;nn)return e}return Math.max(n,1)}(a,e,o);if(r>0){let t,i;const n=r>1?Math.round((h-l)/(r-1)):null;for(Us(e,c,d,s(n)?0:l-n,l),t=0,i=r-1;t"top"===e||"left"===e?t[e]+i:t[e]-i,qs=(t,e)=>Math.min(e||t,t);function Ks(t,e){const i=[],s=t.length/e,n=t.length;let o=0;for(;oa+r)))return h}function Js(t){return t.drawTicks?t.tickLength:0}function Zs(t,e){if(!t.display)return 0;const i=Si(t.font,e),s=ki(t.padding);return(n(t.text)?t.text.length:1)*i.lineHeight+s.height}function Qs(t,e,i){let s=ut(t);return(i&&"right"!==e||!i&&"right"===e)&&(s=(t=>"left"===t?"right":"right"===t?"left":t)(s)),s}class tn extends $s{constructor(t){super(),this.id=t.id,this.type=t.type,this.options=void 0,this.ctx=t.ctx,this.chart=t.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(t){this.options=t.setContext(this.getContext()),this.axis=t.axis,this._userMin=this.parse(t.min),this._userMax=this.parse(t.max),this._suggestedMin=this.parse(t.suggestedMin),this._suggestedMax=this.parse(t.suggestedMax)}parse(t,e){return t}getUserBounds(){let{_userMin:t,_userMax:e,_suggestedMin:i,_suggestedMax:s}=this;return t=r(t,Number.POSITIVE_INFINITY),e=r(e,Number.NEGATIVE_INFINITY),i=r(i,Number.POSITIVE_INFINITY),s=r(s,Number.NEGATIVE_INFINITY),{min:r(t,i),max:r(e,s),minDefined:a(t),maxDefined:a(e)}}getMinMax(t){let e,{min:i,max:s,minDefined:n,maxDefined:o}=this.getUserBounds();if(n&&o)return{min:i,max:s};const a=this.getMatchingVisibleMetas();for(let r=0,l=a.length;rs?s:i,s=n&&i>s?i:s,{min:r(i,r(s,i)),max:r(s,r(i,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){d(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:n,ticks:o}=this.options,a=o.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=Di(this,n,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const r=a=n||i<=1||!this.isHorizontal())return void(this.labelRotation=s);const h=this._getLabelSizes(),c=h.widest.width,d=h.highest.height,u=Z(this.chart.width-c,0,this.maxWidth);o=t.offset?this.maxWidth/i:u/(i-1),c+6>o&&(o=u/(i-(t.offset?.5:1)),a=this.maxHeight-Js(t.grid)-e.padding-Zs(t.title,this.chart.options.font),r=Math.sqrt(c*c+d*d),l=Y(Math.min(Math.asin(Z((h.highest.height+6)/o,-1,1)),Math.asin(Z(a/r,-1,1))-Math.asin(Z(d/r,-1,1)))),l=Math.max(s,Math.min(n,l))),this.labelRotation=l}afterCalculateLabelRotation(){d(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){d(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:n}}=this,o=this._isVisible(),a=this.isHorizontal();if(o){const o=Zs(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Js(n)+o):(t.height=this.maxHeight,t.width=Js(n)+o),i.display&&this.ticks.length){const{first:e,last:s,widest:n,highest:o}=this._getLabelSizes(),r=2*i.padding,l=$(this.labelRotation),h=Math.cos(l),c=Math.sin(l);if(a){const e=i.mirror?0:c*n.width+h*o.height;t.height=Math.min(this.maxHeight,t.height+e+r)}else{const e=i.mirror?0:h*n.width+c*o.height;t.width=Math.min(this.maxWidth,t.width+e+r)}this._calculatePadding(e,s,c,h)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,i,s){const{ticks:{align:n,padding:o},position:a}=this.options,r=0!==this.labelRotation,l="top"!==a&&"x"===this.axis;if(this.isHorizontal()){const a=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let c=0,d=0;r?l?(c=s*t.width,d=i*e.height):(c=i*t.height,d=s*e.width):"start"===n?d=e.width:"end"===n?c=t.width:"inner"!==n&&(c=t.width/2,d=e.width/2),this.paddingLeft=Math.max((c-a+o)*this.width/(this.width-a),0),this.paddingRight=Math.max((d-h+o)*this.width/(this.width-h),0)}else{let i=e.height/2,s=t.height/2;"start"===n?(i=0,s=t.height):"end"===n&&(i=e.height,s=0),this.paddingTop=i+o,this.paddingBottom=s+o}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){d(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return"top"===e||"bottom"===e||"x"===t}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){let e,i;for(this.beforeTickToLabelConversion(),this.generateTickLabels(t),e=0,i=t.length;e{const i=t.gc,s=i.length/2;let n;if(s>e){for(n=0;n({width:r[t]||0,height:l[t]||0});return{first:P(0),last:P(e-1),widest:P(k),highest:P(S),widths:r,heights:l}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return Q(this._alignToPixels?Ae(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&ta*s?a/i:r/s:r*s0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:n,position:a,border:r}=s,h=n.offset,c=this.isHorizontal(),d=this.ticks.length+(h?1:0),u=Js(n),f=[],g=r.setContext(this.getContext()),p=g.display?g.width:0,m=p/2,x=function(t){return Ae(i,t,p)};let b,_,y,v,M,w,k,S,P,D,C,O;if("top"===a)b=x(this.bottom),w=this.bottom-u,S=b-m,D=x(t.top)+m,O=t.bottom;else if("bottom"===a)b=x(this.top),D=t.top,O=x(t.bottom)-m,w=b+m,S=this.top+u;else if("left"===a)b=x(this.right),M=this.right-u,k=b-m,P=x(t.left)+m,C=t.right;else if("right"===a)b=x(this.left),P=t.left,C=x(t.right)-m,M=b+m,k=this.left+u;else if("x"===e){if("center"===a)b=x((t.top+t.bottom)/2+.5);else if(o(a)){const t=Object.keys(a)[0],e=a[t];b=x(this.chart.scales[t].getPixelForValue(e))}D=t.top,O=t.bottom,w=b+m,S=w+u}else if("y"===e){if("center"===a)b=x((t.left+t.right)/2);else if(o(a)){const t=Object.keys(a)[0],e=a[t];b=x(this.chart.scales[t].getPixelForValue(e))}M=b-m,k=M-u,P=t.left,C=t.right}const A=l(s.ticks.maxTicksLimit,d),T=Math.max(1,Math.ceil(d/A));for(_=0;_0&&(o-=s/2)}d={left:o,top:n,width:s+e.width,height:i+e.height,color:t.backdropColor}}x.push({label:v,font:P,textOffset:O,options:{rotation:m,color:i,strokeColor:o,strokeWidth:h,textAlign:f,textBaseline:A,translation:[M,w],backdrop:d}})}return x}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-$(this.labelRotation))return"top"===t?"left":"right";let i="center";return"start"===e.align?i="left":"end"===e.align?i="right":"inner"===e.align&&(i="inner"),i}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:i,mirror:s,padding:n}}=this.options,o=t+n,a=this._getLabelSizes().widest.width;let r,l;return"left"===e?s?(l=this.right+n,"near"===i?r="left":"center"===i?(r="center",l+=a/2):(r="right",l+=a)):(l=this.right-o,"near"===i?r="right":"center"===i?(r="center",l-=a/2):(r="left",l=this.left)):"right"===e?s?(l=this.left+n,"near"===i?r="right":"center"===i?(r="center",l-=a/2):(r="left",l-=a)):(l=this.left+o,"near"===i?r="left":"center"===i?(r="center",l+=a/2):(r="right",l=this.right)):r="right",{textAlign:r,x:l}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;return"left"===e||"right"===e?{top:0,left:this.left,bottom:t.height,right:this.right}:"top"===e||"bottom"===e?{top:this.top,left:0,bottom:this.bottom,right:t.width}:void 0}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:i,top:s,width:n,height:o}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(i,s,n,o),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const i=this.ticks.findIndex((e=>e.value===t));if(i>=0){return e.setContext(this.getContext(i)).lineWidth}return 0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let n,o;const a=(t,e,s)=>{s.width&&s.color&&(i.save(),i.lineWidth=s.width,i.strokeStyle=s.color,i.setLineDash(s.borderDash||[]),i.lineDashOffset=s.borderDashOffset,i.beginPath(),i.moveTo(t.x,t.y),i.lineTo(e.x,e.y),i.stroke(),i.restore())};if(e.display)for(n=0,o=s.length;n{this.drawBackground(),this.drawGrid(t),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:e,draw:t=>{this.drawLabels(t)}}]:[{z:e,draw:t=>{this.draw(t)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+"AxisID",s=[];let n,o;for(n=0,o=e.length;n{const s=i.split("."),n=s.pop(),o=[t].concat(s).join("."),a=e[i].split("."),r=a.pop(),l=a.join(".");ue.route(o,n,l,r)}))}(e,t.defaultRoutes);t.descriptors&&ue.describe(e,t.descriptors)}(t,o,i),this.override&&ue.override(t.id,t.overrides)),o}get(t){return this.items[t]}unregister(t){const e=this.items,i=t.id,s=this.scope;i in e&&delete e[i],s&&i in ue[s]&&(delete ue[s][i],this.override&&delete re[i])}}class sn{constructor(){this.controllers=new en(js,"datasets",!0),this.elements=new en($s,"elements"),this.plugins=new en(Object,"plugins"),this.scales=new en(tn,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,i){[...e].forEach((e=>{const s=i||this._getRegistryForType(e);i||s.isForType(e)||s===this.plugins&&e.id?this._exec(t,s,e):u(e,(e=>{const s=i||this._getRegistryForType(e);this._exec(t,s,e)}))}))}_exec(t,e,i){const s=w(t);d(i["before"+s],[],i),e[t](i),d(i["after"+s],[],i)}_getRegistryForType(t){for(let e=0;et.filter((t=>!e.some((e=>t.plugin.id===e.plugin.id))));this._notify(s(e,i),t,"stop"),this._notify(s(i,e),t,"start")}}function an(t,e){return e||!1!==t?!0===t?{}:t:null}function rn(t,{plugin:e,local:i},s,n){const o=t.pluginScopeKeys(e),a=t.getOptionScopes(s,o);return i&&e.defaults&&a.push(e.defaults),t.createResolver(a,n,[""],{scriptable:!1,indexable:!1,allKeys:!0})}function ln(t,e){const i=ue.datasets[t]||{};return((e.datasets||{})[t]||{}).indexAxis||e.indexAxis||i.indexAxis||"x"}function hn(t){if("x"===t||"y"===t||"r"===t)return t}function cn(t,...e){if(hn(t))return t;for(const s of e){const e=s.axis||("top"===(i=s.position)||"bottom"===i?"x":"left"===i||"right"===i?"y":void 0)||t.length>1&&hn(t[0].toLowerCase());if(e)return e}var i;throw new Error(`Cannot determine type of '${t}' axis. Please provide 'axis' or 'position' option.`)}function dn(t,e,i){if(i[e+"AxisID"]===t)return{axis:e}}function un(t,e){const i=re[t.type]||{scales:{}},s=e.scales||{},n=ln(t.type,e),a=Object.create(null);return Object.keys(s).forEach((e=>{const r=s[e];if(!o(r))return console.error(`Invalid scale configuration for scale: ${e}`);if(r._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${e}`);const l=cn(e,r,function(t,e){if(e.data&&e.data.datasets){const i=e.data.datasets.filter((e=>e.xAxisID===t||e.yAxisID===t));if(i.length)return dn(t,"x",i[0])||dn(t,"y",i[0])}return{}}(e,t),ue.scales[r.type]),h=function(t,e){return t===e?"_index_":"_value_"}(l,n),c=i.scales||{};a[e]=b(Object.create(null),[{axis:l},r,c[l],c[h]])})),t.data.datasets.forEach((i=>{const n=i.type||t.type,o=i.indexAxis||ln(n,e),r=(re[n]||{}).scales||{};Object.keys(r).forEach((t=>{const e=function(t,e){let i=t;return"_index_"===t?i=e:"_value_"===t&&(i="x"===e?"y":"x"),i}(t,o),n=i[e+"AxisID"]||e;a[n]=a[n]||Object.create(null),b(a[n],[{axis:e},s[n],r[t]])}))})),Object.keys(a).forEach((t=>{const e=a[t];b(e,[ue.scales[e.type],ue.scale])})),a}function fn(t){const e=t.options||(t.options={});e.plugins=l(e.plugins,{}),e.scales=un(t,e)}function gn(t){return(t=t||{}).datasets=t.datasets||[],t.labels=t.labels||[],t}const pn=new Map,mn=new Set;function xn(t,e){let i=pn.get(t);return i||(i=e(),pn.set(t,i),mn.add(i)),i}const bn=(t,e,i)=>{const s=M(e,i);void 0!==s&&t.add(s)};class _n{constructor(t){this._config=function(t){return(t=t||{}).data=gn(t.data),fn(t),t}(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=gn(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),fn(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return xn(t,(()=>[[`datasets.${t}`,""]]))}datasetAnimationScopeKeys(t,e){return xn(`${t}.transition.${e}`,(()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]]))}datasetElementScopeKeys(t,e){return xn(`${t}-${e}`,(()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]]))}pluginScopeKeys(t){const e=t.id;return xn(`${this.type}-plugin-${e}`,(()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]]))}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return s&&!e||(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:n}=this,o=this._cachedScopes(t,i),a=o.get(e);if(a)return a;const r=new Set;e.forEach((e=>{t&&(r.add(t),e.forEach((e=>bn(r,t,e)))),e.forEach((t=>bn(r,s,t))),e.forEach((t=>bn(r,re[n]||{},t))),e.forEach((t=>bn(r,ue,t))),e.forEach((t=>bn(r,le,t)))}));const l=Array.from(r);return 0===l.length&&l.push(Object.create(null)),mn.has(e)&&o.set(e,l),l}chartOptionScopes(){const{options:t,type:e}=this;return[t,re[e]||{},ue.datasets[e]||{},{type:e},ue,le]}resolveNamedOptions(t,e,i,s=[""]){const o={$shared:!0},{resolver:a,subPrefixes:r}=yn(this._resolverCache,t,s);let l=a;if(function(t,e){const{isScriptable:i,isIndexable:s}=Ye(t);for(const o of e){const e=i(o),a=s(o),r=(a||e)&&t[o];if(e&&(S(r)||vn(r))||a&&n(r))return!0}return!1}(a,e)){o.$shared=!1;l=$e(a,i=S(i)?i():i,this.createResolver(t,i,r))}for(const t of e)o[t]=l[t];return o}createResolver(t,e,i=[""],s){const{resolver:n}=yn(this._resolverCache,t,i);return o(e)?$e(n,e,void 0,s):n}}function yn(t,e,i){let s=t.get(e);s||(s=new Map,t.set(e,s));const n=i.join();let o=s.get(n);if(!o){o={resolver:je(e,i),subPrefixes:i.filter((t=>!t.toLowerCase().includes("hover")))},s.set(n,o)}return o}const vn=t=>o(t)&&Object.getOwnPropertyNames(t).some((e=>S(t[e])));const Mn=["top","bottom","left","right","chartArea"];function wn(t,e){return"top"===t||"bottom"===t||-1===Mn.indexOf(t)&&"x"===e}function kn(t,e){return function(i,s){return i[t]===s[t]?i[e]-s[e]:i[t]-s[t]}}function Sn(t){const e=t.chart,i=e.options.animation;e.notifyPlugins("afterRender"),d(i&&i.onComplete,[t],e)}function Pn(t){const e=t.chart,i=e.options.animation;d(i&&i.onProgress,[t],e)}function Dn(t){return fe()&&"string"==typeof t?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const Cn={},On=t=>{const e=Dn(t);return Object.values(Cn).filter((t=>t.canvas===e)).pop()};function An(t,e,i){const s=Object.keys(t);for(const n of s){const s=+n;if(s>=e){const o=t[n];delete t[n],(i>0||s>e)&&(t[s+i]=o)}}}class Tn{static defaults=ue;static instances=Cn;static overrides=re;static registry=nn;static version="4.5.1";static getChart=On;static register(...t){nn.add(...t),Ln()}static unregister(...t){nn.remove(...t),Ln()}constructor(t,e){const s=this.config=new _n(e),n=Dn(t),o=On(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const a=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||Ps(n)),this.platform.updateConfig(s);const r=this.platform.acquireContext(n,a.aspectRatio),l=r&&r.canvas,h=l&&l.height,c=l&&l.width;this.id=i(),this.ctx=r,this.canvas=l,this.width=c,this.height=h,this._options=a,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new on,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=dt((t=>this.update(t)),a.resizeDelay||0),this._dataChanges=[],Cn[this.id]=this,r&&l?(bt.listen(this,"complete",Sn),bt.listen(this,"progress",Pn),this._initialize(),this.attached&&this.update()):console.error("Failed to create chart: can't acquire context from the given item")}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:n,_aspectRatio:o}=this;return s(t)?e&&o?o:n?i/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return nn}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():ke(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Te(this.canvas,this.ctx),this}stop(){return bt.stop(this),this}resize(t,e){bt.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,n=i.maintainAspectRatio&&this.aspectRatio,o=this.platform.getMaximumSize(s,t,e,n),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),r=this.width?"resize":"attach";this.width=o.width,this.height=o.height,this._aspectRatio=this.aspectRatio,ke(this,a,!0)&&(this.notifyPlugins("resize",{size:o}),d(i.onResize,[this,o],this),this.attached&&this._doResize(r)&&this.render())}ensureScalesHaveIDs(){u(this.options.scales||{},((t,e)=>{t.id=e}))}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce(((t,e)=>(t[e]=!1,t)),{});let n=[];e&&(n=n.concat(Object.keys(e).map((t=>{const i=e[t],s=cn(t,i),n="r"===s,o="x"===s;return{options:i,dposition:n?"chartArea":o?"bottom":"left",dtype:n?"radialLinear":o?"category":"linear"}})))),u(n,(e=>{const n=e.options,o=n.id,a=cn(o,n),r=l(n.type,e.dtype);void 0!==n.position&&wn(n.position,a)===wn(e.dposition)||(n.position=e.dposition),s[o]=!0;let h=null;if(o in i&&i[o].type===r)h=i[o];else{h=new(nn.getScale(r))({id:o,type:r,ctx:this.ctx,chart:this}),i[h.id]=h}h.init(n,t)})),u(s,((t,e)=>{t||delete i[e]})),u(i,(t=>{ls.configure(this,t,t.options),ls.addBox(this,t)}))}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort(((t,e)=>t.index-e.index)),i>e){for(let t=e;te.length&&delete this._stacks,t.forEach(((t,i)=>{0===e.filter((e=>e===t._dataset)).length&&this._destroyDatasetMeta(i)}))}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i{this.getDatasetMeta(e).controller.reset()}),this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),!1===this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0}))return;const n=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let o=0;for(let t=0,e=this.data.datasets.length;t{t.reset()})),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(kn("z","_idx"));const{_active:a,_lastEvent:r}=this;r?this._eventHandler(r,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){u(this.scales,(t=>{ls.removeBox(this,t)})),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);P(e,i)&&!!this._responsiveListeners===t.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:n}of e){An(t,s,"_removeElements"===i?-n:n)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=e=>new Set(t.filter((t=>t[0]===e)).map(((t,e)=>e+","+t.splice(1).join(",")))),s=i(0);for(let t=1;tt.split(","))).map((t=>({method:t[1],start:+t[2],count:+t[3]})))}_updateLayout(t){if(!1===this.notifyPlugins("beforeLayout",{cancelable:!0}))return;ls.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],u(this.boxes,(t=>{i&&"chartArea"===t.position||(t.configure&&t.configure(),this._layers.push(...t._layers()))}),this),this._layers.forEach(((t,e)=>{t._idx=e})),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(!1!==this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})){for(let t=0,e=this.data.datasets.length;t=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,i={meta:t,index:t.index,cancelable:!0},s=Ni(this,t);!1!==this.notifyPlugins("beforeDatasetDraw",i)&&(s&&Ie(e,s),t.controller.draw(),s&&ze(e),i.cancelable=!1,this.notifyPlugins("afterDatasetDraw",i))}isPointInArea(t){return Re(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,i,s){const n=Ki.modes[e];return"function"==typeof n?n(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter((t=>t&&t._dataset===e)).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},i.push(s)),s}getContext(){return this.$context||(this.$context=Ci(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return"boolean"==typeof i.hidden?!i.hidden:!e.hidden}setDatasetVisibility(t,e){this.getDatasetMeta(t).hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?"show":"hide",n=this.getDatasetMeta(t),o=n.controller._resolveAnimations(void 0,s);k(e)?(n.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),o.update(n,{visible:i}),this.update((e=>e.datasetIndex===t?s:void 0)))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),bt.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,i,s),t[i]=s},s=(t,e,i)=>{t.offsetX=e,t.offsetY=i,this._eventHandler(t)};u(this.options.events,(t=>i(t,s)))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(i,s)=>{t[i]&&(e.removeEventListener(this,i,s),delete t[i])},n=(t,e)=>{this.canvas&&this.resize(t,e)};let o;const a=()=>{s("attach",a),this.attached=!0,this.resize(),i("resize",n),i("detach",o)};o=()=>{this.attached=!1,s("resize",n),this._stop(),this._resize(0,0),i("attach",a)},e.isAttached(this.canvas)?a():o()}unbindEvents(){u(this._listeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._listeners={},u(this._responsiveListeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?"set":"remove";let n,o,a,r;for("dataset"===e&&(n=this.getDatasetMeta(t[0].datasetIndex),n.controller["_"+s+"DatasetHoverStyle"]()),a=0,r=t.length;a{const i=this.getDatasetMeta(t);if(!i)throw new Error("No dataset found at index "+t);return{datasetIndex:t,element:i.data[e],index:e}}));!f(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}isPluginEnabled(t){return 1===this._plugins._cache.filter((e=>e.plugin.id===t)).length}_updateHoverStyles(t,e,i){const s=this.options.hover,n=(t,e)=>t.filter((t=>!e.some((e=>t.datasetIndex===e.datasetIndex&&t.index===e.index)))),o=n(e,t),a=i?t:n(t,e);o.length&&this.updateHoverStyle(o,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},s=e=>(e.options.events||this.options.events).includes(t.native.type);if(!1===this.notifyPlugins("beforeEvent",i,s))return;const n=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins("afterEvent",i,s),(n||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:n}=this,o=e,a=this._getActiveElements(t,s,i,o),r=D(t),l=function(t,e,i,s){return i&&"mouseout"!==t.type?s?e:t:null}(t,this._lastEvent,i,r);i&&(this._lastEvent=null,d(n.onHover,[t,a,this],this),r&&d(n.onClick,[t,a,this],this));const h=!f(a,s);return(h||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=l,h}_getActiveElements(t,e,i,s){if("mouseout"===t.type)return[];if(!i)return e;const n=this.options.hover;return this.getElementsAtEventForMode(t,n.mode,n,s)}}function Ln(){return u(Tn.instances,(t=>t._plugins.invalidate()))}function En(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class Rn{static override(t){Object.assign(Rn.prototype,t)}options;constructor(t){this.options=t||{}}init(){}formats(){return En()}parse(){return En()}format(){return En()}add(){return En()}diff(){return En()}startOf(){return En()}endOf(){return En()}}var In={_date:Rn};function zn(t){const e=t.iScale,i=function(t,e){if(!t._cache.$bar){const i=t.getMatchingVisibleMetas(e);let s=[];for(let e=0,n=i.length;et-e)))}return t._cache.$bar}(e,t.type);let s,n,o,a,r=e._length;const l=()=>{32767!==o&&-32768!==o&&(k(a)&&(r=Math.min(r,Math.abs(o-a)||r)),a=o)};for(s=0,n=i.length;sMath.abs(r)&&(l=r,h=a),e[i.axis]=h,e._custom={barStart:l,barEnd:h,start:n,end:o,min:a,max:r}}(t,e,i,s):e[i.axis]=i.parse(t,s),e}function Vn(t,e,i,s){const n=t.iScale,o=t.vScale,a=n.getLabels(),r=n===o,l=[];let h,c,d,u;for(h=i,c=i+s;ht.x,i="left",s="right"):(e=t.base"spacing"!==t,_indexable:t=>"spacing"!==t&&!t.startsWith("borderDash")&&!t.startsWith("hoverBorderDash")};static overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data,{labels:{pointStyle:i,textAlign:s,color:n,useBorderRadius:o,borderRadius:a}}=t.legend.options;return e.labels.length&&e.datasets.length?e.labels.map(((e,r)=>{const l=t.getDatasetMeta(0).controller.getStyle(r);return{text:e,fillStyle:l.backgroundColor,fontColor:n,hidden:!t.getDataVisibility(r),lineDash:l.borderDash,lineDashOffset:l.borderDashOffset,lineJoin:l.borderJoinStyle,lineWidth:l.borderWidth,strokeStyle:l.borderColor,textAlign:s,pointStyle:i,borderRadius:o&&(a||l.borderRadius),index:r}})):[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}}};constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(!1===this._parsing)s._parsed=i;else{let n,a,r=t=>+i[t];if(o(i[t])){const{key:t="value"}=this._parsing;r=e=>+M(i[e],t)}for(n=t,a=t+e;nJ(t,r,l,!0)?1:Math.max(e,e*i,s,s*i),g=(t,e,s)=>J(t,r,l,!0)?-1:Math.min(e,e*i,s,s*i),p=f(0,h,d),m=f(E,c,u),x=g(C,h,d),b=g(C+E,c,u);s=(p-x)/2,n=(m-b)/2,o=-(p+x)/2,a=-(m+b)/2}return{ratioX:s,ratioY:n,offsetX:o,offsetY:a}}(u,d,r),x=(i.width-o)/f,b=(i.height-o)/g,_=Math.max(Math.min(x,b)/2,0),y=c(this.options.radius,_),v=(y-Math.max(y*r,0))/this._getVisibleDatasetWeightTotal();this.offsetX=p*y,this.offsetY=m*y,s.total=this.calculateTotal(),this.outerRadius=y-v*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-v*l,0),this.updateElements(n,0,n.length,t)}_circumference(t,e){const i=this.options,s=this._cachedMeta,n=this._getCircumference();return e&&i.animation.animateRotate||!this.chart.getDataVisibility(t)||null===s._parsed[t]||s.data[t].hidden?0:this.calculateCircumference(s._parsed[t]*n/O)}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=o.chartArea,r=o.options.animation,l=(a.left+a.right)/2,h=(a.top+a.bottom)/2,c=n&&r.animateScale,d=c?0:this.innerRadius,u=c?0:this.outerRadius,{sharedOptions:f,includeOptions:g}=this._getSharedOptions(e,s);let p,m=this._getRotation();for(p=0;p0&&!isNaN(t)?O*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t],i.options.locale);return{label:s[t]||"",value:n}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,n,o,a,r;if(!t)for(s=0,n=i.data.datasets.length;s{const o=t.getDatasetMeta(0).controller.getStyle(n);return{text:e,fillStyle:o.backgroundColor,strokeStyle:o.borderColor,fontColor:s,lineWidth:o.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(n),index:n}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}};constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t].r,i.options.locale);return{label:s[t]||"",value:n}}parseObjectData(t,e,i,s){return ii.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}getMinMax(){const t=this._cachedMeta,e={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return t.data.forEach(((t,i)=>{const s=this.getParsed(i).r;!isNaN(s)&&this.chart.getDataVisibility(i)&&(se.max&&(e.max=s))})),e}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),n=Math.max(s/2,0),o=(n-Math.max(i.cutoutPercentage?n/100*i.cutoutPercentage:1,0))/t.getVisibleDatasetCount();this.outerRadius=n-o*this.index,this.innerRadius=this.outerRadius-o}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=o.options.animation,r=this._cachedMeta.rScale,l=r.xCenter,h=r.yCenter,c=r.getIndexAngle(0)-.5*C;let d,u=c;const f=360/this.countVisibleElements();for(d=0;d{!isNaN(this.getParsed(i).r)&&this.chart.getDataVisibility(i)&&e++})),e}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?$(this.resolveDataElementOptions(t,e).angle||i):0}}var Un=Object.freeze({__proto__:null,BarController:class extends js{static id="bar";static defaults={datasetElementType:!1,dataElementType:"bar",categoryPercentage:.8,barPercentage:.9,grouped:!0,animations:{numbers:{type:"number",properties:["x","y","base","width","height"]}}};static overrides={scales:{_index_:{type:"category",offset:!0,grid:{offset:!0}},_value_:{type:"linear",beginAtZero:!0}}};parsePrimitiveData(t,e,i,s){return Vn(t,e,i,s)}parseArrayData(t,e,i,s){return Vn(t,e,i,s)}parseObjectData(t,e,i,s){const{iScale:n,vScale:o}=t,{xAxisKey:a="x",yAxisKey:r="y"}=this._parsing,l="x"===n.axis?a:r,h="x"===o.axis?a:r,c=[];let d,u,f,g;for(d=i,u=i+s;dt.controller.options.grouped)),o=i.options.stacked,a=[],r=this._cachedMeta.controller.getParsed(e),l=r&&r[i.axis],h=t=>{const e=t._parsed.find((t=>t[i.axis]===l)),n=e&&e[t.vScale.axis];if(s(n)||isNaN(n))return!0};for(const i of n)if((void 0===e||!h(i))&&((!1===o||-1===a.indexOf(i.stack)||void 0===o&&void 0===i.stack)&&a.push(i.stack),i.index===t))break;return a.length||a.push(void 0),a}_getStackCount(t){return this._getStacks(void 0,t).length}_getAxisCount(){return this._getAxis().length}getFirstScaleIdForIndexAxis(){const t=this.chart.scales,e=this.chart.options.indexAxis;return Object.keys(t).filter((i=>t[i].axis===e)).shift()}_getAxis(){const t={},e=this.getFirstScaleIdForIndexAxis();for(const i of this.chart.data.datasets)t[l("x"===this.chart.options.indexAxis?i.xAxisID:i.yAxisID,e)]=!0;return Object.keys(t)}_getStackIndex(t,e,i){const s=this._getStacks(t,i),n=void 0!==e?s.indexOf(e):-1;return-1===n?s.length-1:n}_getRuler(){const t=this.options,e=this._cachedMeta,i=e.iScale,s=[];let n,o;for(n=0,o=e.data.length;n=i?1:-1)}(u,e,r)*a,f===r&&(x-=u/2);const t=e.getPixelForDecimal(0),s=e.getPixelForDecimal(1),o=Math.min(t,s),h=Math.max(t,s);x=Math.max(Math.min(x,h),o),d=x+u,i&&!c&&(l._stacks[e.axis]._visualValues[n]=e.getValueForPixel(d)-e.getValueForPixel(x))}if(x===e.getPixelForValue(r)){const t=F(u)*e.getLineWidthForValue(r)/2;x+=t,u-=t}return{size:u,base:x,head:d,center:d+u/2}}_calculateBarIndexPixels(t,e){const i=e.scale,n=this.options,o=n.skipNull,a=l(n.maxBarThickness,1/0);let r,h;const c=this._getAxisCount();if(e.grouped){const i=o?this._getStackCount(t):e.stackCount,d="flex"===n.barThickness?function(t,e,i,s){const n=e.pixels,o=n[t];let a=t>0?n[t-1]:null,r=t=0;--i)e=Math.max(e,t[i].size(this.resolveDataElementOptions(i))/2);return e>0&&e}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart.data.labels||[],{xScale:s,yScale:n}=e,o=this.getParsed(t),a=s.getLabelForValue(o.x),r=n.getLabelForValue(o.y),l=o._custom;return{label:i[t]||"",value:"("+a+", "+r+(l?", "+l:"")+")"}}update(t){const e=this._cachedMeta.data;this.updateElements(e,0,e.length,t)}updateElements(t,e,i,s){const n="reset"===s,{iScale:o,vScale:a}=this._cachedMeta,{sharedOptions:r,includeOptions:l}=this._getSharedOptions(e,s),h=o.axis,c=a.axis;for(let d=e;d0&&this.getParsed(e-1);for(let i=0;i<_;++i){const g=t[i],_=x?g:{};if(i=b){_.skip=!0;continue}const v=this.getParsed(i),M=s(v[f]),w=_[u]=a.getPixelForValue(v[u],i),k=_[f]=o||M?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,v,l):v[f],i);_.skip=isNaN(w)||isNaN(k)||M,_.stop=i>0&&Math.abs(v[u]-y[u])>m,p&&(_.parsed=v,_.raw=h.data[i]),d&&(_.options=c||this.resolveDataElementOptions(i,g.active?"active":n)),x||this.updateElement(g,i,_,n),y=v}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const n=s[0].size(this.resolveDataElementOptions(0)),o=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,n,o)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}},PieController:class extends $n{static id="pie";static defaults={cutout:0,rotation:0,circumference:360,radius:"100%"}},PolarAreaController:Yn,RadarController:class extends js{static id="radar";static defaults={datasetElementType:"line",dataElementType:"point",indexAxis:"r",showLine:!0,elements:{line:{fill:"start"}}};static overrides={aspectRatio:1,scales:{r:{type:"radialLinear"}}};getLabelAndValue(t){const e=this._cachedMeta.vScale,i=this.getParsed(t);return{label:e.getLabels()[t],value:""+e.getLabelForValue(i[e.axis])}}parseObjectData(t,e,i,s){return ii.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta,i=e.dataset,s=e.data||[],n=e.iScale.getLabels();if(i.points=s,"resize"!==t){const e=this.resolveDatasetElementOptions(t);this.options.showLine||(e.borderWidth=0);const o={_loop:!0,_fullLoop:n.length===s.length,options:e};this.updateElement(i,void 0,o,t)}this.updateElements(s,0,s.length,t)}updateElements(t,e,i,s){const n=this._cachedMeta.rScale,o="reset"===s;for(let a=e;a0&&this.getParsed(e-1);for(let c=e;c0&&Math.abs(i[f]-_[f])>x,m&&(p.parsed=i,p.raw=h.data[c]),u&&(p.options=d||this.resolveDataElementOptions(c,e.active?"active":n)),b||this.updateElement(e,c,p,n),_=i}this.updateSharedOptions(d,n,c)}getMaxOverflow(){const t=this._cachedMeta,e=t.data||[];if(!this.options.showLine){let t=0;for(let i=e.length-1;i>=0;--i)t=Math.max(t,e[i].size(this.resolveDataElementOptions(i))/2);return t>0&&t}const i=t.dataset,s=i.options&&i.options.borderWidth||0;if(!e.length)return s;const n=e[0].size(this.resolveDataElementOptions(0)),o=e[e.length-1].size(this.resolveDataElementOptions(e.length-1));return Math.max(s,n,o)/2}}});function Xn(t,e,i,s){const n=vi(t.options.borderRadius,["outerStart","outerEnd","innerStart","innerEnd"]);const o=(i-e)/2,a=Math.min(o,s*e/2),r=t=>{const e=(i-Math.min(o,t))*s/2;return Z(t,0,Math.min(o,e))};return{outerStart:r(n.outerStart),outerEnd:r(n.outerEnd),innerStart:Z(n.innerStart,0,a),innerEnd:Z(n.innerEnd,0,a)}}function qn(t,e,i,s){return{x:i+t*Math.cos(e),y:s+t*Math.sin(e)}}function Kn(t,e,i,s,n,o){const{x:a,y:r,startAngle:l,pixelMargin:h,innerRadius:c}=e,d=Math.max(e.outerRadius+s+i-h,0),u=c>0?c+s+i+h:0;let f=0;const g=n-l;if(s){const t=((c>0?c-s:0)+(d>0?d-s:0))/2;f=(g-(0!==t?g*t/(t+s):g))/2}const p=(g-Math.max(.001,g*d-i/C)/d)/2,m=l+p+f,x=n-p-f,{outerStart:b,outerEnd:_,innerStart:y,innerEnd:v}=Xn(e,u,d,x-m),M=d-b,w=d-_,k=m+b/M,S=x-_/w,P=u+y,D=u+v,O=m+y/P,A=x-v/D;if(t.beginPath(),o){const e=(k+S)/2;if(t.arc(a,r,d,k,e),t.arc(a,r,d,e,S),_>0){const e=qn(w,S,a,r);t.arc(e.x,e.y,_,S,x+E)}const i=qn(D,x,a,r);if(t.lineTo(i.x,i.y),v>0){const e=qn(D,A,a,r);t.arc(e.x,e.y,v,x+E,A+Math.PI)}const s=(x-v/u+(m+y/u))/2;if(t.arc(a,r,u,x-v/u,s,!0),t.arc(a,r,u,s,m+y/u,!0),y>0){const e=qn(P,O,a,r);t.arc(e.x,e.y,y,O+Math.PI,m-E)}const n=qn(M,m,a,r);if(t.lineTo(n.x,n.y),b>0){const e=qn(M,k,a,r);t.arc(e.x,e.y,b,m-E,k)}}else{t.moveTo(a,r);const e=Math.cos(k)*d+a,i=Math.sin(k)*d+r;t.lineTo(e,i);const s=Math.cos(S)*d+a,n=Math.sin(S)*d+r;t.lineTo(s,n)}t.closePath()}function Gn(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r,options:l}=e,{borderWidth:h,borderJoinStyle:c,borderDash:d,borderDashOffset:u,borderRadius:f}=l,g="inner"===l.borderAlign;if(!h)return;t.setLineDash(d||[]),t.lineDashOffset=u,g?(t.lineWidth=2*h,t.lineJoin=c||"round"):(t.lineWidth=h,t.lineJoin=c||"bevel");let p=e.endAngle;if(o){Kn(t,e,i,s,p,n);for(let e=0;en?(h=n/l,t.arc(o,a,l,i+h,s-h,!0)):t.arc(o,a,n,i+E,s-E),t.closePath(),t.clip()}(t,e,p),l.selfJoin&&p-a>=C&&0===f&&"miter"!==c&&function(t,e,i){const{startAngle:s,x:n,y:o,outerRadius:a,innerRadius:r,options:l}=e,{borderWidth:h,borderJoinStyle:c}=l,d=Math.min(h/a,G(s-i));if(t.beginPath(),t.arc(n,o,a-h/2,s+d/2,i-d/2),r>0){const e=Math.min(h/r,G(s-i));t.arc(n,o,r+h/2,i-e/2,s+e/2,!0)}else{const e=Math.min(h/2,a*G(s-i));if("round"===c)t.arc(n,o,e,i-C/2,s+C/2,!0);else if("bevel"===c){const a=2*e*e,r=-a*Math.cos(i+C/2)+n,l=-a*Math.sin(i+C/2)+o,h=a*Math.cos(s+C/2)+n,c=a*Math.sin(s+C/2)+o;t.lineTo(r,l),t.lineTo(h,c)}}t.closePath(),t.moveTo(0,0),t.rect(0,0,t.canvas.width,t.canvas.height),t.clip("evenodd")}(t,e,p),o||(Kn(t,e,i,s,p,n),t.stroke())}function Jn(t,e,i=e){t.lineCap=l(i.borderCapStyle,e.borderCapStyle),t.setLineDash(l(i.borderDash,e.borderDash)),t.lineDashOffset=l(i.borderDashOffset,e.borderDashOffset),t.lineJoin=l(i.borderJoinStyle,e.borderJoinStyle),t.lineWidth=l(i.borderWidth,e.borderWidth),t.strokeStyle=l(i.borderColor,e.borderColor)}function Zn(t,e,i){t.lineTo(i.x,i.y)}function Qn(t,e,i={}){const s=t.length,{start:n=0,end:o=s-1}=i,{start:a,end:r}=e,l=Math.max(n,a),h=Math.min(o,r),c=nr&&o>r;return{count:s,start:l,loop:e.loop,ilen:h(a+(h?r-t:t))%o,_=()=>{f!==g&&(t.lineTo(m,g),t.lineTo(m,f),t.lineTo(m,p))};for(l&&(d=n[b(0)],t.moveTo(d.x,d.y)),c=0;c<=r;++c){if(d=n[b(c)],d.skip)continue;const e=d.x,i=d.y,s=0|e;s===u?(ig&&(g=i),m=(x*m+e)/++x):(_(),t.lineTo(e,i),u=s,x=0,f=g=i),p=i}_()}function io(t){const e=t.options,i=e.borderDash&&e.borderDash.length;return!(t._decimated||t._loop||e.tension||"monotone"===e.cubicInterpolationMode||e.stepped||i)?eo:to}const so="function"==typeof Path2D;function no(t,e,i,s){so&&!e.options.segment?function(t,e,i,s){let n=e._path;n||(n=e._path=new Path2D,e.path(n,i,s)&&n.closePath()),Jn(t,e.options),t.stroke(n)}(t,e,i,s):function(t,e,i,s){const{segments:n,options:o}=e,a=io(e);for(const r of n)Jn(t,o,r.style),t.beginPath(),a(t,e,r,{start:i,end:i+s-1})&&t.closePath(),t.stroke()}(t,e,i,s)}class oo extends $s{static id="line";static defaults={borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderWidth:3,capBezierPoints:!0,cubicInterpolationMode:"default",fill:!1,spanGaps:!1,stepped:!1,tension:0};static defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};static descriptors={_scriptable:!0,_indexable:t=>"borderDash"!==t&&"fill"!==t};constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const i=this.options;if((i.tension||"monotone"===i.cubicInterpolationMode)&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;hi(this._points,i,t,s,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=zi(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],n=this.points,o=Ii(this,{property:e,start:s,end:s});if(!o.length)return;const a=[],r=function(t){return t.stepped?pi:t.tension||"monotone"===t.cubicInterpolationMode?mi:gi}(i);let l,h;for(l=0,h=o.length;l"borderDash"!==t};circumference;endAngle;fullCircles;innerRadius;outerRadius;pixelMargin;startAngle;constructor(t){super(),this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.getProps(["x","y"],i),{angle:n,distance:o}=X(s,{x:t,y:e}),{startAngle:a,endAngle:r,innerRadius:h,outerRadius:c,circumference:d}=this.getProps(["startAngle","endAngle","innerRadius","outerRadius","circumference"],i),u=(this.options.spacing+this.options.borderWidth)/2,f=l(d,r-a),g=J(n,a,r)&&a!==r,p=f>=O||g,m=tt(o,h+u,c+u);return p&&m}getCenterPoint(t){const{x:e,y:i,startAngle:s,endAngle:n,innerRadius:o,outerRadius:a}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius"],t),{offset:r,spacing:l}=this.options,h=(s+n)/2,c=(o+a+l+r)/2;return{x:e+Math.cos(h)*c,y:i+Math.sin(h)*c}}tooltipPosition(t){return this.getCenterPoint(t)}draw(t){const{options:e,circumference:i}=this,s=(e.offset||0)/4,n=(e.spacing||0)/2,o=e.circular;if(this.pixelMargin="inner"===e.borderAlign?.33:0,this.fullCircles=i>O?Math.floor(i/O):0,0===i||this.innerRadius<0||this.outerRadius<0)return;t.save();const a=(this.startAngle+this.endAngle)/2;t.translate(Math.cos(a)*s,Math.sin(a)*s);const r=s*(1-Math.sin(Math.min(C,i||0)));t.fillStyle=e.backgroundColor,t.strokeStyle=e.borderColor,function(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r}=e;let l=e.endAngle;if(o){Kn(t,e,i,s,l,n);for(let e=0;e("string"==typeof e?(i=t.push(e)-1,s.unshift({index:i,label:e})):isNaN(e)&&(i=null),i))(t,e,i,s);return n!==t.lastIndexOf(e)?i:n}function mo(t){const e=this.getLabels();return t>=0&&ts=e?s:t,a=t=>n=i?n:t;if(t){const t=F(s),e=F(n);t<0&&e<0?a(0):t>0&&e>0&&o(0)}if(s===n){let e=0===n?1:Math.abs(.05*n);a(n+e),t||o(s-e)}this.min=s,this.max=n}getTickLimit(){const t=this.options.ticks;let e,{maxTicksLimit:i,stepSize:s}=t;return s?(e=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,e>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${e} ticks. Limiting to 1000.`),e=1e3)):(e=this.computeTickLimit(),i=i||11),i&&(e=Math.min(i,e)),e}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const n=function(t,e){const i=[],{bounds:n,step:o,min:a,max:r,precision:l,count:h,maxTicks:c,maxDigits:d,includeBounds:u}=t,f=o||1,g=c-1,{min:p,max:m}=e,x=!s(a),b=!s(r),_=!s(h),y=(m-p)/(d+1);let v,M,w,k,S=B((m-p)/g/f)*f;if(S<1e-14&&!x&&!b)return[{value:p},{value:m}];k=Math.ceil(m/S)-Math.floor(p/S),k>g&&(S=B(k*S/g/f)*f),s(l)||(v=Math.pow(10,l),S=Math.ceil(S*v)/v),"ticks"===n?(M=Math.floor(p/S)*S,w=Math.ceil(m/S)*S):(M=p,w=m),x&&b&&o&&H((r-a)/o,S/1e3)?(k=Math.round(Math.min((r-a)/S,c)),S=(r-a)/k,M=a,w=r):_?(M=x?a:M,w=b?r:w,k=h-1,S=(w-M)/k):(k=(w-M)/S,k=V(k,Math.round(k),S/1e3)?Math.round(k):Math.ceil(k));const P=Math.max(U(S),U(M));v=Math.pow(10,s(l)?P:l),M=Math.round(M*v)/v,w=Math.round(w*v)/v;let D=0;for(x&&(u&&M!==a?(i.push({value:a}),Mr)break;i.push({value:t})}return b&&u&&w!==r?i.length&&V(i[i.length-1].value,r,xo(r,y,t))?i[i.length-1].value=r:i.push({value:r}):b&&w!==r||i.push({value:w}),i}({maxTicks:i,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:!1!==e.includeBounds},this._range||this);return"ticks"===t.bounds&&j(n,this,"value"),t.reverse?(n.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),n}configure(){const t=this.ticks;let e=this.min,i=this.max;if(super.configure(),this.options.offset&&t.length){const s=(i-e)/Math.max(t.length-1,1)/2;e-=s,i+=s}this._startValue=e,this._endValue=i,this._valueRange=i-e}getLabelForValue(t){return ne(t,this.chart.options.locale,this.options.ticks.format)}}class _o extends bo{static id="linear";static defaults={ticks:{callback:ae.formatters.numeric}};determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?t:0,this.max=a(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,i=$(this.options.ticks.minRotation),s=(t?Math.sin(i):Math.cos(i))||.001,n=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,n.lineHeight/s))}getPixelForValue(t){return null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}const yo=t=>Math.floor(z(t)),vo=(t,e)=>Math.pow(10,yo(t)+e);function Mo(t){return 1===t/Math.pow(10,yo(t))}function wo(t,e,i){const s=Math.pow(10,i),n=Math.floor(t/s);return Math.ceil(e/s)-n}function ko(t,{min:e,max:i}){e=r(t.min,e);const s=[],n=yo(e);let o=function(t,e){let i=yo(e-t);for(;wo(t,e,i)>10;)i++;for(;wo(t,e,i)<10;)i--;return Math.min(i,yo(t))}(e,i),a=o<0?Math.pow(10,Math.abs(o)):1;const l=Math.pow(10,o),h=n>o?Math.pow(10,n):0,c=Math.round((e-h)*a)/a,d=Math.floor((e-h)/l/10)*l*10;let u=Math.floor((c-d)/Math.pow(10,o)),f=r(t.min,Math.round((h+d+u*Math.pow(10,o))*a)/a);for(;f=10?u=u<15?15:20:u++,u>=20&&(o++,u=2,a=o>=0?1:a),f=Math.round((h+d+u*Math.pow(10,o))*a)/a;const g=r(t.max,f);return s.push({value:g,major:Mo(g),significand:u}),s}class So extends tn{static id="logarithmic";static defaults={ticks:{callback:ae.formatters.logarithmic,major:{enabled:!0}}};constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(t,e){const i=bo.prototype.parse.apply(this,[t,e]);if(0!==i)return a(i)&&i>0?i:null;this._zero=!0}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?Math.max(0,t):null,this.max=a(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!a(this._userMin)&&(this.min=t===vo(this.min,0)?vo(this.min,-1):vo(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const n=e=>i=t?i:e,o=t=>s=e?s:t;i===s&&(i<=0?(n(1),o(10)):(n(vo(i,-1)),o(vo(s,1)))),i<=0&&n(vo(s,-1)),s<=0&&o(vo(i,1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e=ko({min:this._userMin,max:this._userMax},this);return"ticks"===t.bounds&&j(e,this,"value"),t.reverse?(e.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),e}getLabelForValue(t){return void 0===t?"0":ne(t,this.chart.options.locale,this.options.ticks.format)}configure(){const t=this.min;super.configure(),this._startValue=z(t),this._valueRange=z(this.max)-z(t)}getPixelForValue(t){return void 0!==t&&0!==t||(t=this.min),null===t||isNaN(t)?NaN:this.getPixelForDecimal(t===this.min?0:(z(t)-this._startValue)/this._valueRange)}getValueForPixel(t){const e=this.getDecimalForPixel(t);return Math.pow(10,this._startValue+e*this._valueRange)}}function Po(t){const e=t.ticks;if(e.display&&t.display){const t=ki(e.backdropPadding);return l(e.font&&e.font.size,ue.font.size)+t.height}return 0}function Do(t,e,i,s,n){return t===s||t===n?{start:e-i/2,end:e+i/2}:tn?{start:e-i,end:e}:{start:e,end:e+i}}function Co(t){const e={l:t.left+t._padding.left,r:t.right-t._padding.right,t:t.top+t._padding.top,b:t.bottom-t._padding.bottom},i=Object.assign({},e),s=[],o=[],a=t._pointLabels.length,r=t.options.pointLabels,l=r.centerPointLabels?C/a:0;for(let u=0;ue.r&&(r=(s.end-e.r)/o,t.r=Math.max(t.r,e.r+r)),n.starte.b&&(l=(n.end-e.b)/a,t.b=Math.max(t.b,e.b+l))}function Ao(t,e,i){const s=t.drawingArea,{extra:n,additionalAngle:o,padding:a,size:r}=i,l=t.getPointPosition(e,s+n+a,o),h=Math.round(Y(G(l.angle+E))),c=function(t,e,i){90===i||270===i?t-=e/2:(i>270||i<90)&&(t-=e);return t}(l.y,r.h,h),d=function(t){if(0===t||180===t)return"center";if(t<180)return"left";return"right"}(h),u=function(t,e,i){"right"===i?t-=e:"center"===i&&(t-=e/2);return t}(l.x,r.w,d);return{visible:!0,x:l.x,y:c,textAlign:d,left:u,top:c,right:u+r.w,bottom:c+r.h}}function To(t,e){if(!e)return!0;const{left:i,top:s,right:n,bottom:o}=t;return!(Re({x:i,y:s},e)||Re({x:i,y:o},e)||Re({x:n,y:s},e)||Re({x:n,y:o},e))}function Lo(t,e,i){const{left:n,top:o,right:a,bottom:r}=i,{backdropColor:l}=e;if(!s(l)){const i=wi(e.borderRadius),s=ki(e.backdropPadding);t.fillStyle=l;const h=n-s.left,c=o-s.top,d=a-n+s.width,u=r-o+s.height;Object.values(i).some((t=>0!==t))?(t.beginPath(),He(t,{x:h,y:c,w:d,h:u,radius:i}),t.fill()):t.fillRect(h,c,d,u)}}function Eo(t,e,i,s){const{ctx:n}=t;if(i)n.arc(t.xCenter,t.yCenter,e,0,O);else{let i=t.getPointPosition(0,e);n.moveTo(i.x,i.y);for(let o=1;ot,padding:5,centerPointLabels:!1}};static defaultRoutes={"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"};static descriptors={angleLines:{_fallback:"grid"}};constructor(t){super(t),this.xCenter=void 0,this.yCenter=void 0,this.drawingArea=void 0,this._pointLabels=[],this._pointLabelItems=[]}setDimensions(){const t=this._padding=ki(Po(this.options)/2),e=this.width=this.maxWidth-t.width,i=this.height=this.maxHeight-t.height;this.xCenter=Math.floor(this.left+e/2+t.left),this.yCenter=Math.floor(this.top+i/2+t.top),this.drawingArea=Math.floor(Math.min(e,i)/2)}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!1);this.min=a(t)&&!isNaN(t)?t:0,this.max=a(e)&&!isNaN(e)?e:0,this.handleTickRangeOptions()}computeTickLimit(){return Math.ceil(this.drawingArea/Po(this.options))}generateTickLabels(t){bo.prototype.generateTickLabels.call(this,t),this._pointLabels=this.getLabels().map(((t,e)=>{const i=d(this.options.pointLabels.callback,[t,e],this);return i||0===i?i:""})).filter(((t,e)=>this.chart.getDataVisibility(e)))}fit(){const t=this.options;t.display&&t.pointLabels.display?Co(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){return G(t*(O/(this._pointLabels.length||1))+$(this.options.startAngle||0))}getDistanceFromCenterForValue(t){if(s(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if(s(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t=0;n--){const e=t._pointLabelItems[n];if(!e.visible)continue;const o=s.setContext(t.getPointLabelContext(n));Lo(i,o,e);const a=Si(o.font),{x:r,y:l,textAlign:h}=e;Ne(i,t._pointLabels[n],r,l+a.lineHeight/2,a,{color:o.color,textAlign:h,textBaseline:"middle"})}}(this,o),s.display&&this.ticks.forEach(((t,e)=>{if(0!==e||0===e&&this.min<0){r=this.getDistanceFromCenterForValue(t.value);const i=this.getContext(e),a=s.setContext(i),l=n.setContext(i);!function(t,e,i,s,n){const o=t.ctx,a=e.circular,{color:r,lineWidth:l}=e;!a&&!s||!r||!l||i<0||(o.save(),o.strokeStyle=r,o.lineWidth=l,o.setLineDash(n.dash||[]),o.lineDashOffset=n.dashOffset,o.beginPath(),Eo(t,i,a,s),o.closePath(),o.stroke(),o.restore())}(this,a,r,o,l)}})),i.display){for(t.save(),a=o-1;a>=0;a--){const s=i.setContext(this.getPointLabelContext(a)),{color:n,lineWidth:o}=s;o&&n&&(t.lineWidth=o,t.strokeStyle=n,t.setLineDash(s.borderDash),t.lineDashOffset=s.borderDashOffset,r=this.getDistanceFromCenterForValue(e.reverse?this.min:this.max),l=this.getPointPosition(a,r),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(l.x,l.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let n,o;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach(((s,a)=>{if(0===a&&this.min>=0&&!e.reverse)return;const r=i.setContext(this.getContext(a)),l=Si(r.font);if(n=this.getDistanceFromCenterForValue(this.ticks[a].value),r.showLabelBackdrop){t.font=l.string,o=t.measureText(s.label).width,t.fillStyle=r.backdropColor;const e=ki(r.backdropPadding);t.fillRect(-o/2-e.left,-n-l.size/2-e.top,o+e.width,l.size+e.height)}Ne(t,s.label,0,-n,l,{color:r.color,strokeColor:r.textStrokeColor,strokeWidth:r.textStrokeWidth})})),t.restore()}drawTitle(){}}const Io={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},zo=Object.keys(Io);function Fo(t,e){return t-e}function Vo(t,e){if(s(e))return null;const i=t._adapter,{parser:n,round:o,isoWeekday:r}=t._parseOpts;let l=e;return"function"==typeof n&&(l=n(l)),a(l)||(l="string"==typeof n?i.parse(l,n):i.parse(l)),null===l?null:(o&&(l="week"!==o||!N(r)&&!0!==r?i.startOf(l,o):i.startOf(l,"isoWeek",r)),+l)}function Bo(t,e,i,s){const n=zo.length;for(let o=zo.indexOf(t);o=e?i[s]:i[n]]=!0}}else t[e]=!0}function No(t,e,i){const s=[],n={},o=e.length;let a,r;for(a=0;a=0&&(e[l].major=!0);return e}(t,s,n,i):s}class Ho extends tn{static id="time";static defaults={bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{source:"auto",callback:!1,major:{enabled:!1}}};constructor(t){super(t),this._cache={data:[],labels:[],all:[]},this._unit="day",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(t,e={}){const i=t.time||(t.time={}),s=this._adapter=new In._date(t.adapters.date);s.init(e),b(i.displayFormats,s.formats()),this._parseOpts={parser:i.parser,round:i.round,isoWeekday:i.isoWeekday},super.init(t),this._normalized=e.normalized}parse(t,e){return void 0===t?null:Vo(this,t)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const t=this.options,e=this._adapter,i=t.time.unit||"day";let{min:s,max:n,minDefined:o,maxDefined:r}=this.getUserBounds();function l(t){o||isNaN(t.min)||(s=Math.min(s,t.min)),r||isNaN(t.max)||(n=Math.max(n,t.max))}o&&r||(l(this._getLabelBounds()),"ticks"===t.bounds&&"labels"===t.ticks.source||l(this.getMinMax(!1))),s=a(s)&&!isNaN(s)?s:+e.startOf(Date.now(),i),n=a(n)&&!isNaN(n)?n:+e.endOf(Date.now(),i)+1,this.min=Math.min(s,n-1),this.max=Math.max(s+1,n)}_getLabelBounds(){const t=this.getLabelTimestamps();let e=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;return t.length&&(e=t[0],i=t[t.length-1]),{min:e,max:i}}buildTicks(){const t=this.options,e=t.time,i=t.ticks,s="labels"===i.source?this.getLabelTimestamps():this._generate();"ticks"===t.bounds&&s.length&&(this.min=this._userMin||s[0],this.max=this._userMax||s[s.length-1]);const n=this.min,o=nt(s,n,this.max);return this._unit=e.unit||(i.autoSkip?Bo(e.minUnit,this.min,this.max,this._getLabelCapacity(n)):function(t,e,i,s,n){for(let o=zo.length-1;o>=zo.indexOf(i);o--){const i=zo[o];if(Io[i].common&&t._adapter.diff(n,s,i)>=e-1)return i}return zo[i?zo.indexOf(i):0]}(this,o.length,e.minUnit,this.min,this.max)),this._majorUnit=i.major.enabled&&"year"!==this._unit?function(t){for(let e=zo.indexOf(t)+1,i=zo.length;e+t.value)))}initOffsets(t=[]){let e,i,s=0,n=0;this.options.offset&&t.length&&(e=this.getDecimalForValue(t[0]),s=1===t.length?1-e:(this.getDecimalForValue(t[1])-e)/2,i=this.getDecimalForValue(t[t.length-1]),n=1===t.length?i:(i-this.getDecimalForValue(t[t.length-2]))/2);const o=t.length<3?.5:.25;s=Z(s,0,o),n=Z(n,0,o),this._offsets={start:s,end:n,factor:1/(s+1+n)}}_generate(){const t=this._adapter,e=this.min,i=this.max,s=this.options,n=s.time,o=n.unit||Bo(n.minUnit,e,i,this._getLabelCapacity(e)),a=l(s.ticks.stepSize,1),r="week"===o&&n.isoWeekday,h=N(r)||!0===r,c={};let d,u,f=e;if(h&&(f=+t.startOf(f,"isoWeek",r)),f=+t.startOf(f,h?"day":o),t.diff(i,e,o)>1e5*a)throw new Error(e+" and "+i+" are too far apart with stepSize of "+a+" "+o);const g="data"===s.ticks.source&&this.getDataTimestamps();for(d=f,u=0;d+t))}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}format(t,e){const i=this.options.time.displayFormats,s=this._unit,n=e||i[s];return this._adapter.format(t,n)}_tickFormatFunction(t,e,i,s){const n=this.options,o=n.ticks.callback;if(o)return d(o,[t,e,i],this);const a=n.time.displayFormats,r=this._unit,l=this._majorUnit,h=r&&a[r],c=l&&a[l],u=i[e],f=l&&c&&u&&u.major;return this._adapter.format(t,s||(f?c:h))}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e0?a:1}getDataTimestamps(){let t,e,i=this._cache.data||[];if(i.length)return i;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(t=0,e=s.length;t=t[r].pos&&e<=t[l].pos&&({lo:r,hi:l}=it(t,"pos",e)),({pos:s,time:o}=t[r]),({pos:n,time:a}=t[l])):(e>=t[r].time&&e<=t[l].time&&({lo:r,hi:l}=it(t,"time",e)),({time:s,pos:o}=t[r]),({time:n,pos:a}=t[l]));const h=n-s;return h?o+(a-o)*(e-s)/h:o}var $o=Object.freeze({__proto__:null,CategoryScale:class extends tn{static id="category";static defaults={ticks:{callback:mo}};constructor(t){super(t),this._startValue=void 0,this._valueRange=0,this._addedLabels=[]}init(t){const e=this._addedLabels;if(e.length){const t=this.getLabels();for(const{index:i,label:s}of e)t[i]===s&&t.splice(i,1);this._addedLabels=[]}super.init(t)}parse(t,e){if(s(t))return null;const i=this.getLabels();return((t,e)=>null===t?null:Z(Math.round(t),0,e))(e=isFinite(e)&&i[e]===t?e:po(i,t,l(e,t),this._addedLabels),i.length-1)}determineDataLimits(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let{min:i,max:s}=this.getMinMax(!0);"ticks"===this.options.bounds&&(t||(i=0),e||(s=this.getLabels().length-1)),this.min=i,this.max=s}buildTicks(){const t=this.min,e=this.max,i=this.options.offset,s=[];let n=this.getLabels();n=0===t&&e===n.length-1?n:n.slice(t,e+1),this._valueRange=Math.max(n.length-(i?0:1),1),this._startValue=this.min-(i?.5:0);for(let i=t;i<=e;i++)s.push({value:i});return s}getLabelForValue(t){return mo.call(this,t)}configure(){super.configure(),this.isHorizontal()||(this._reversePixels=!this._reversePixels)}getPixelForValue(t){return"number"!=typeof t&&(t=this.parse(t)),null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}},LinearScale:_o,LogarithmicScale:So,RadialLinearScale:Ro,TimeScale:Ho,TimeSeriesScale:class extends Ho{static id="timeseries";static defaults=Ho.defaults;constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=jo(e,this.min),this._tableRange=jo(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],n=[];let o,a,r,l,h;for(o=0,a=t.length;o=e&&l<=i&&s.push(l);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(o=0,a=s.length;ot-e))}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),i=this.getLabelTimestamps();return t=e.length&&i.length?this.normalize(e.concat(i)):e.length?e:i,t=this._cache.all=t,t}getDecimalForValue(t){return(jo(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return jo(this._table,i*this._tableRange+this._minPos,!0)}}});const Yo=["rgb(54, 162, 235)","rgb(255, 99, 132)","rgb(255, 159, 64)","rgb(255, 205, 86)","rgb(75, 192, 192)","rgb(153, 102, 255)","rgb(201, 203, 207)"],Uo=Yo.map((t=>t.replace("rgb(","rgba(").replace(")",", 0.5)")));function Xo(t){return Yo[t%Yo.length]}function qo(t){return Uo[t%Uo.length]}function Ko(t){let e=0;return(i,s)=>{const n=t.getDatasetMeta(s).controller;n instanceof $n?e=function(t,e){return t.backgroundColor=t.data.map((()=>Xo(e++))),e}(i,e):n instanceof Yn?e=function(t,e){return t.backgroundColor=t.data.map((()=>qo(e++))),e}(i,e):n&&(e=function(t,e){return t.borderColor=Xo(e),t.backgroundColor=qo(e),++e}(i,e))}}function Go(t){let e;for(e in t)if(t[e].borderColor||t[e].backgroundColor)return!0;return!1}var Jo={id:"colors",defaults:{enabled:!0,forceOverride:!1},beforeLayout(t,e,i){if(!i.enabled)return;const{data:{datasets:s},options:n}=t.config,{elements:o}=n,a=Go(s)||(r=n)&&(r.borderColor||r.backgroundColor)||o&&Go(o)||"rgba(0,0,0,0.1)"!==ue.borderColor||"rgba(0,0,0,0.1)"!==ue.backgroundColor;var r;if(!i.forceOverride&&a)return;const l=Ko(t);s.forEach(l)}};function Zo(t){if(t._decimated){const e=t._data;delete t._decimated,delete t._data,Object.defineProperty(t,"data",{configurable:!0,enumerable:!0,writable:!0,value:e})}}function Qo(t){t.data.datasets.forEach((t=>{Zo(t)}))}var ta={id:"decimation",defaults:{algorithm:"min-max",enabled:!1},beforeElementsUpdate:(t,e,i)=>{if(!i.enabled)return void Qo(t);const n=t.width;t.data.datasets.forEach(((e,o)=>{const{_data:a,indexAxis:r}=e,l=t.getDatasetMeta(o),h=a||e.data;if("y"===Pi([r,t.options.indexAxis]))return;if(!l.controller.supportsDecimation)return;const c=t.scales[l.xAxisID];if("linear"!==c.type&&"time"!==c.type)return;if(t.options.parsing)return;let{start:d,count:u}=function(t,e){const i=e.length;let s,n=0;const{iScale:o}=t,{min:a,max:r,minDefined:l,maxDefined:h}=o.getUserBounds();return l&&(n=Z(it(e,o.axis,a).lo,0,i-1)),s=h?Z(it(e,o.axis,r).hi+1,n,i)-n:i-n,{start:n,count:s}}(l,h);if(u<=(i.threshold||4*n))return void Zo(e);let f;switch(s(a)&&(e._data=h,delete e.data,Object.defineProperty(e,"data",{configurable:!0,enumerable:!0,get:function(){return this._decimated},set:function(t){this._data=t}})),i.algorithm){case"lttb":f=function(t,e,i,s,n){const o=n.samples||s;if(o>=i)return t.slice(e,e+i);const a=[],r=(i-2)/(o-2);let l=0;const h=e+i-1;let c,d,u,f,g,p=e;for(a[l++]=t[p],c=0;cu&&(u=f,d=t[s],g=s);a[l++]=d,p=g}return a[l++]=t[h],a}(h,d,u,n,i);break;case"min-max":f=function(t,e,i,n){let o,a,r,l,h,c,d,u,f,g,p=0,m=0;const x=[],b=e+i-1,_=t[e].x,y=t[b].x-_;for(o=e;og&&(g=l,d=o),p=(m*p+a.x)/++m;else{const i=o-1;if(!s(c)&&!s(d)){const e=Math.min(c,d),s=Math.max(c,d);e!==u&&e!==i&&x.push({...t[e],x:p}),s!==u&&s!==i&&x.push({...t[s],x:p})}o>0&&i!==u&&x.push(t[i]),x.push(a),h=e,m=0,f=g=l,c=d=u=o}}return x}(h,d,u,n);break;default:throw new Error(`Unsupported decimation algorithm '${i.algorithm}'`)}e._decimated=f}))},destroy(t){Qo(t)}};function ea(t,e,i,s){if(s)return;let n=e[t],o=i[t];return"angle"===t&&(n=G(n),o=G(o)),{property:t,start:n,end:o}}function ia(t,e,i){for(;e>t;e--){const t=i[e];if(!isNaN(t.x)&&!isNaN(t.y))break}return e}function sa(t,e,i,s){return t&&e?s(t[i],e[i]):t?t[i]:e?e[i]:0}function na(t,e){let i=[],s=!1;return n(t)?(s=!0,i=t):i=function(t,e){const{x:i=null,y:s=null}=t||{},n=e.points,o=[];return e.segments.forEach((({start:t,end:e})=>{e=ia(t,e,n);const a=n[t],r=n[e];null!==s?(o.push({x:a.x,y:s}),o.push({x:r.x,y:s})):null!==i&&(o.push({x:i,y:a.y}),o.push({x:i,y:r.y}))})),o}(t,e),i.length?new oo({points:i,options:{tension:0},_loop:s,_fullLoop:s}):null}function oa(t){return t&&!1!==t.fill}function aa(t,e,i){let s=t[e].fill;const n=[e];let o;if(!i)return s;for(;!1!==s&&-1===n.indexOf(s);){if(!a(s))return s;if(o=t[s],!o)return!1;if(o.visible)return s;n.push(s),s=o.fill}return!1}function ra(t,e,i){const s=function(t){const e=t.options,i=e.fill;let s=l(i&&i.target,i);void 0===s&&(s=!!e.backgroundColor);if(!1===s||null===s)return!1;if(!0===s)return"origin";return s}(t);if(o(s))return!isNaN(s.value)&&s;let n=parseFloat(s);return a(n)&&Math.floor(n)===n?function(t,e,i,s){"-"!==t&&"+"!==t||(i=e+i);if(i===e||i<0||i>=s)return!1;return i}(s[0],e,n,i):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}function la(t,e,i){const s=[];for(let n=0;n=0;--e){const i=n[e].$filler;i&&(i.line.updateControlPoints(o,i.axis),s&&i.fill&&ua(t.ctx,i,o))}},beforeDatasetsDraw(t,e,i){if("beforeDatasetsDraw"!==i.drawTime)return;const s=t.getSortedVisibleDatasetMetas();for(let e=s.length-1;e>=0;--e){const i=s[e].$filler;oa(i)&&ua(t.ctx,i,t.chartArea)}},beforeDatasetDraw(t,e,i){const s=e.meta.$filler;oa(s)&&"beforeDatasetDraw"===i.drawTime&&ua(t.ctx,s,t.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const _a=(t,e)=>{let{boxHeight:i=e,boxWidth:s=e}=t;return t.usePointStyle&&(i=Math.min(i,e),s=t.pointStyleWidth||Math.min(s,e)),{boxWidth:s,boxHeight:i,itemHeight:Math.max(e,i)}};class ya extends $s{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=d(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter((e=>t.filter(e,this.chart.data)))),t.sort&&(e=e.sort(((e,i)=>t.sort(e,i,this.chart.data)))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display)return void(this.width=this.height=0);const i=t.labels,s=Si(i.font),n=s.size,o=this._computeTitleHeight(),{boxWidth:a,itemHeight:r}=_a(i,n);let l,h;e.font=s.string,this.isHorizontal()?(l=this.maxWidth,h=this._fitRows(o,n,a,r)+10):(h=this.maxHeight,l=this._fitCols(o,s,a,r)+10),this.width=Math.min(l,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:n,maxWidth:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.lineWidths=[0],h=s+a;let c=t;n.textAlign="left",n.textBaseline="middle";let d=-1,u=-h;return this.legendItems.forEach(((t,f)=>{const g=i+e/2+n.measureText(t.text).width;(0===f||l[l.length-1]+g+2*a>o)&&(c+=h,l[l.length-(f>0?0:1)]=0,u+=h,d++),r[f]={left:0,top:u,row:d,width:g,height:s},l[l.length-1]+=g+a})),c}_fitCols(t,e,i,s){const{ctx:n,maxHeight:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.columnSizes=[],h=o-t;let c=a,d=0,u=0,f=0,g=0;return this.legendItems.forEach(((t,o)=>{const{itemWidth:p,itemHeight:m}=function(t,e,i,s,n){const o=function(t,e,i,s){let n=t.text;n&&"string"!=typeof n&&(n=n.reduce(((t,e)=>t.length>e.length?t:e)));return e+i.size/2+s.measureText(n).width}(s,t,e,i),a=function(t,e,i){let s=t;"string"!=typeof e.text&&(s=va(e,i));return s}(n,s,e.lineHeight);return{itemWidth:o,itemHeight:a}}(i,e,n,t,s);o>0&&u+m+2*a>h&&(c+=d+a,l.push({width:d,height:u}),f+=d+a,g++,d=u=0),r[o]={left:f,top:u,col:g,width:p,height:m},d=Math.max(d,p),u+=m+a})),c+=d,l.push({width:d,height:u}),c}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:n}}=this,o=Oi(n,this.left,this.width);if(this.isHorizontal()){let n=0,a=ft(i,this.left+s,this.right-this.lineWidths[n]);for(const r of e)n!==r.row&&(n=r.row,a=ft(i,this.left+s,this.right-this.lineWidths[n])),r.top+=this.top+t+s,r.left=o.leftForLtr(o.x(a),r.width),a+=r.width+s}else{let n=0,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height);for(const r of e)r.col!==n&&(n=r.col,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height)),r.top=a,r.left+=this.left+s,r.left=o.leftForLtr(o.x(r.left),r.width),a+=r.height+s}}isHorizontal(){return"top"===this.options.position||"bottom"===this.options.position}draw(){if(this.options.display){const t=this.ctx;Ie(t,this),this._draw(),ze(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:n,labels:o}=t,a=ue.color,r=Oi(t.rtl,this.left,this.width),h=Si(o.font),{padding:c}=o,d=h.size,u=d/2;let f;this.drawTitle(),s.textAlign=r.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=h.string;const{boxWidth:g,boxHeight:p,itemHeight:m}=_a(o,d),x=this.isHorizontal(),b=this._computeTitleHeight();f=x?{x:ft(n,this.left+c,this.right-i[0]),y:this.top+c+b,line:0}:{x:this.left+c,y:ft(n,this.top+b+c,this.bottom-e[0].height),line:0},Ai(this.ctx,t.textDirection);const _=m+c;this.legendItems.forEach(((y,v)=>{s.strokeStyle=y.fontColor,s.fillStyle=y.fontColor;const M=s.measureText(y.text).width,w=r.textAlign(y.textAlign||(y.textAlign=o.textAlign)),k=g+u+M;let S=f.x,P=f.y;r.setWidth(this.width),x?v>0&&S+k+c>this.right&&(P=f.y+=_,f.line++,S=f.x=ft(n,this.left+c,this.right-i[f.line])):v>0&&P+_>this.bottom&&(S=f.x=S+e[f.line].width+c,f.line++,P=f.y=ft(n,this.top+b+c,this.bottom-e[f.line].height));if(function(t,e,i){if(isNaN(g)||g<=0||isNaN(p)||p<0)return;s.save();const n=l(i.lineWidth,1);if(s.fillStyle=l(i.fillStyle,a),s.lineCap=l(i.lineCap,"butt"),s.lineDashOffset=l(i.lineDashOffset,0),s.lineJoin=l(i.lineJoin,"miter"),s.lineWidth=n,s.strokeStyle=l(i.strokeStyle,a),s.setLineDash(l(i.lineDash,[])),o.usePointStyle){const a={radius:p*Math.SQRT2/2,pointStyle:i.pointStyle,rotation:i.rotation,borderWidth:n},l=r.xPlus(t,g/2);Ee(s,a,l,e+u,o.pointStyleWidth&&g)}else{const o=e+Math.max((d-p)/2,0),a=r.leftForLtr(t,g),l=wi(i.borderRadius);s.beginPath(),Object.values(l).some((t=>0!==t))?He(s,{x:a,y:o,w:g,h:p,radius:l}):s.rect(a,o,g,p),s.fill(),0!==n&&s.stroke()}s.restore()}(r.x(S),P,y),S=gt(w,S+g+u,x?S+k:this.right,t.rtl),function(t,e,i){Ne(s,i.text,t,e+m/2,h,{strikethrough:i.hidden,textAlign:r.textAlign(i.textAlign)})}(r.x(S),P,y),x)f.x+=k+c;else if("string"!=typeof y.text){const t=h.lineHeight;f.y+=va(y,t)+c}else f.y+=_})),Ti(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=Si(e.font),s=ki(e.padding);if(!e.display)return;const n=Oi(t.rtl,this.left,this.width),o=this.ctx,a=e.position,r=i.size/2,l=s.top+r;let h,c=this.left,d=this.width;if(this.isHorizontal())d=Math.max(...this.lineWidths),h=this.top+l,c=ft(t.align,c,this.right-d);else{const e=this.columnSizes.reduce(((t,e)=>Math.max(t,e.height)),0);h=l+ft(t.align,this.top,this.bottom-e-t.labels.padding-this._computeTitleHeight())}const u=ft(a,c,c+d);o.textAlign=n.textAlign(ut(a)),o.textBaseline="middle",o.strokeStyle=e.color,o.fillStyle=e.color,o.font=i.string,Ne(o,e.text,u,h,i)}_computeTitleHeight(){const t=this.options.title,e=Si(t.font),i=ki(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,n;if(tt(t,this.left,this.right)&&tt(e,this.top,this.bottom))for(n=this.legendHitBoxes,i=0;it.chart.options.color,boxWidth:40,padding:10,generateLabels(t){const e=t.data.datasets,{labels:{usePointStyle:i,pointStyle:s,textAlign:n,color:o,useBorderRadius:a,borderRadius:r}}=t.legend.options;return t._getSortedDatasetMetas().map((t=>{const l=t.controller.getStyle(i?0:void 0),h=ki(l.borderWidth);return{text:e[t.index].label,fillStyle:l.backgroundColor,fontColor:o,hidden:!t.visible,lineCap:l.borderCapStyle,lineDash:l.borderDash,lineDashOffset:l.borderDashOffset,lineJoin:l.borderJoinStyle,lineWidth:(h.width+h.height)/4,strokeStyle:l.borderColor,pointStyle:s||l.pointStyle,rotation:l.rotation,textAlign:n||l.textAlign,borderRadius:a&&(r||l.borderRadius),datasetIndex:t.index}}),this)}},title:{color:t=>t.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:t=>!t.startsWith("on"),labels:{_scriptable:t=>!["generateLabels","filter","sort"].includes(t)}}};class wa extends $s{constructor(t){super(),this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e){const i=this.options;if(this.left=0,this.top=0,!i.display)return void(this.width=this.height=this.right=this.bottom=0);this.width=this.right=t,this.height=this.bottom=e;const s=n(i.text)?i.text.length:1;this._padding=ki(i.padding);const o=s*Si(i.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=o:this.width=o}isHorizontal(){const t=this.options.position;return"top"===t||"bottom"===t}_drawArgs(t){const{top:e,left:i,bottom:s,right:n,options:o}=this,a=o.align;let r,l,h,c=0;return this.isHorizontal()?(l=ft(a,i,n),h=e+t,r=n-i):("left"===o.position?(l=i+t,h=ft(a,s,e),c=-.5*C):(l=n-t,h=ft(a,e,s),c=.5*C),r=s-e),{titleX:l,titleY:h,maxWidth:r,rotation:c}}draw(){const t=this.ctx,e=this.options;if(!e.display)return;const i=Si(e.font),s=i.lineHeight/2+this._padding.top,{titleX:n,titleY:o,maxWidth:a,rotation:r}=this._drawArgs(s);Ne(t,e.text,0,0,i,{color:e.color,maxWidth:a,rotation:r,textAlign:ut(e.align),textBaseline:"middle",translation:[n,o]})}}var ka={id:"title",_element:wa,start(t,e,i){!function(t,e){const i=new wa({ctx:t.ctx,options:e,chart:t});ls.configure(t,i,e),ls.addBox(t,i),t.titleBlock=i}(t,i)},stop(t){const e=t.titleBlock;ls.removeBox(t,e),delete t.titleBlock},beforeUpdate(t,e,i){const s=t.titleBlock;ls.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"bold"},fullSize:!0,padding:10,position:"top",text:"",weight:2e3},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Sa=new WeakMap;var Pa={id:"subtitle",start(t,e,i){const s=new wa({ctx:t.ctx,options:i,chart:t});ls.configure(t,s,i),ls.addBox(t,s),Sa.set(t,s)},stop(t){ls.removeBox(t,Sa.get(t)),Sa.delete(t)},beforeUpdate(t,e,i){const s=Sa.get(t);ls.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"normal"},fullSize:!0,padding:0,position:"top",text:"",weight:1500},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Da={average(t){if(!t.length)return!1;let e,i,s=new Set,n=0,o=0;for(e=0,i=t.length;et+e))/s.size,y:n/o}},nearest(t,e){if(!t.length)return!1;let i,s,n,o=e.x,a=e.y,r=Number.POSITIVE_INFINITY;for(i=0,s=t.length;i-1?t.split("\n"):t}function Aa(t,e){const{element:i,datasetIndex:s,index:n}=e,o=t.getDatasetMeta(s).controller,{label:a,value:r}=o.getLabelAndValue(n);return{chart:t,label:a,parsed:o.getParsed(n),raw:t.data.datasets[s].data[n],formattedValue:r,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:i}}function Ta(t,e){const i=t.chart.ctx,{body:s,footer:n,title:o}=t,{boxWidth:a,boxHeight:r}=e,l=Si(e.bodyFont),h=Si(e.titleFont),c=Si(e.footerFont),d=o.length,f=n.length,g=s.length,p=ki(e.padding);let m=p.height,x=0,b=s.reduce(((t,e)=>t+e.before.length+e.lines.length+e.after.length),0);if(b+=t.beforeBody.length+t.afterBody.length,d&&(m+=d*h.lineHeight+(d-1)*e.titleSpacing+e.titleMarginBottom),b){m+=g*(e.displayColors?Math.max(r,l.lineHeight):l.lineHeight)+(b-g)*l.lineHeight+(b-1)*e.bodySpacing}f&&(m+=e.footerMarginTop+f*c.lineHeight+(f-1)*e.footerSpacing);let _=0;const y=function(t){x=Math.max(x,i.measureText(t).width+_)};return i.save(),i.font=h.string,u(t.title,y),i.font=l.string,u(t.beforeBody.concat(t.afterBody),y),_=e.displayColors?a+2+e.boxPadding:0,u(s,(t=>{u(t.before,y),u(t.lines,y),u(t.after,y)})),_=0,i.font=c.string,u(t.footer,y),i.restore(),x+=p.width,{width:x,height:m}}function La(t,e,i,s){const{x:n,width:o}=i,{width:a,chartArea:{left:r,right:l}}=t;let h="center";return"center"===s?h=n<=(r+l)/2?"left":"right":n<=o/2?h="left":n>=a-o/2&&(h="right"),function(t,e,i,s){const{x:n,width:o}=s,a=i.caretSize+i.caretPadding;return"left"===t&&n+o+a>e.width||"right"===t&&n-o-a<0||void 0}(h,t,e,i)&&(h="center"),h}function Ea(t,e,i){const s=i.yAlign||e.yAlign||function(t,e){const{y:i,height:s}=e;return it.height-s/2?"bottom":"center"}(t,i);return{xAlign:i.xAlign||e.xAlign||La(t,e,i,s),yAlign:s}}function Ra(t,e,i,s){const{caretSize:n,caretPadding:o,cornerRadius:a}=t,{xAlign:r,yAlign:l}=i,h=n+o,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=wi(a);let g=function(t,e){let{x:i,width:s}=t;return"right"===e?i-=s:"center"===e&&(i-=s/2),i}(e,r);const p=function(t,e,i){let{y:s,height:n}=t;return"top"===e?s+=i:s-="bottom"===e?n+i:n/2,s}(e,l,h);return"center"===l?"left"===r?g+=h:"right"===r&&(g-=h):"left"===r?g-=Math.max(c,u)+n:"right"===r&&(g+=Math.max(d,f)+n),{x:Z(g,0,s.width-e.width),y:Z(p,0,s.height-e.height)}}function Ia(t,e,i){const s=ki(i.padding);return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-s.right:t.x+s.left}function za(t){return Ca([],Oa(t))}function Fa(t,e){const i=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return i?t.override(i):t}const Va={beforeTitle:e,title(t){if(t.length>0){const e=t[0],i=e.chart.data.labels,s=i?i.length:0;if(this&&this.options&&"dataset"===this.options.mode)return e.dataset.label||"";if(e.label)return e.label;if(s>0&&e.dataIndex{const e={before:[],lines:[],after:[]},n=Fa(i,t);Ca(e.before,Oa(Ba(n,"beforeLabel",this,t))),Ca(e.lines,Ba(n,"label",this,t)),Ca(e.after,Oa(Ba(n,"afterLabel",this,t))),s.push(e)})),s}getAfterBody(t,e){return za(Ba(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:i}=e,s=Ba(i,"beforeFooter",this,t),n=Ba(i,"footer",this,t),o=Ba(i,"afterFooter",this,t);let a=[];return a=Ca(a,Oa(s)),a=Ca(a,Oa(n)),a=Ca(a,Oa(o)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],n=[],o=[];let a,r,l=[];for(a=0,r=e.length;at.filter(e,s,n,i)))),t.itemSort&&(l=l.sort(((e,s)=>t.itemSort(e,s,i)))),u(l,(e=>{const i=Fa(t.callbacks,e);s.push(Ba(i,"labelColor",this,e)),n.push(Ba(i,"labelPointStyle",this,e)),o.push(Ba(i,"labelTextColor",this,e))})),this.labelColors=s,this.labelPointStyles=n,this.labelTextColors=o,this.dataPoints=l,l}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let n,o=[];if(s.length){const t=Da[i.position].call(this,s,this._eventPosition);o=this._createItems(i),this.title=this.getTitle(o,i),this.beforeBody=this.getBeforeBody(o,i),this.body=this.getBody(o,i),this.afterBody=this.getAfterBody(o,i),this.footer=this.getFooter(o,i);const e=this._size=Ta(this,i),a=Object.assign({},t,e),r=Ea(this.chart,i,a),l=Ra(i,a,r,this.chart);this.xAlign=r.xAlign,this.yAlign=r.yAlign,n={opacity:1,x:l.x,y:l.y,width:e.width,height:e.height,caretX:t.x,caretY:t.y}}else 0!==this.opacity&&(n={opacity:0});this._tooltipItems=o,this.$context=void 0,n&&this._resolveAnimations().update(this,n),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const n=this.getCaretPosition(t,i,s);e.lineTo(n.x1,n.y1),e.lineTo(n.x2,n.y2),e.lineTo(n.x3,n.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:n}=this,{caretSize:o,cornerRadius:a}=i,{topLeft:r,topRight:l,bottomLeft:h,bottomRight:c}=wi(a),{x:d,y:u}=t,{width:f,height:g}=e;let p,m,x,b,_,y;return"center"===n?(_=u+g/2,"left"===s?(p=d,m=p-o,b=_+o,y=_-o):(p=d+f,m=p+o,b=_-o,y=_+o),x=p):(m="left"===s?d+Math.max(r,h)+o:"right"===s?d+f-Math.max(l,c)-o:this.caretX,"top"===n?(b=u,_=b-o,p=m-o,x=m+o):(b=u+g,_=b+o,p=m+o,x=m-o),y=b),{x1:p,x2:m,x3:x,y1:b,y2:_,y3:y}}drawTitle(t,e,i){const s=this.title,n=s.length;let o,a,r;if(n){const l=Oi(i.rtl,this.x,this.width);for(t.x=Ia(this,i.titleAlign,i),e.textAlign=l.textAlign(i.titleAlign),e.textBaseline="middle",o=Si(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=o.string,r=0;r0!==t))?(t.beginPath(),t.fillStyle=n.multiKeyBackground,He(t,{x:e,y:g,w:h,h:l,radius:r}),t.fill(),t.stroke(),t.fillStyle=a.backgroundColor,t.beginPath(),He(t,{x:i,y:g+1,w:h-2,h:l-2,radius:r}),t.fill()):(t.fillStyle=n.multiKeyBackground,t.fillRect(e,g,h,l),t.strokeRect(e,g,h,l),t.fillStyle=a.backgroundColor,t.fillRect(i,g+1,h-2,l-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:n,bodyAlign:o,displayColors:a,boxHeight:r,boxWidth:l,boxPadding:h}=i,c=Si(i.bodyFont);let d=c.lineHeight,f=0;const g=Oi(i.rtl,this.x,this.width),p=function(i){e.fillText(i,g.x(t.x+f),t.y+d/2),t.y+=d+n},m=g.textAlign(o);let x,b,_,y,v,M,w;for(e.textAlign=o,e.textBaseline="middle",e.font=c.string,t.x=Ia(this,m,i),e.fillStyle=i.bodyColor,u(this.beforeBody,p),f=a&&"right"!==m?"center"===o?l/2+h:l+2+h:0,y=0,M=s.length;y0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,n=i&&i.y;if(s||n){const i=Da[t.position].call(this,this._active,this._eventPosition);if(!i)return;const o=this._size=Ta(this,t),a=Object.assign({},i,this._size),r=Ea(e,t,a),l=Ra(t,a,r,e);s._to===l.x&&n._to===l.y||(this.xAlign=r.xAlign,this.yAlign=r.yAlign,this.width=o.width,this.height=o.height,this.caretX=i.x,this.caretY=i.y,this._resolveAnimations().update(this,l))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},n={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const o=ki(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=i,this.drawBackground(n,t,s,e),Ai(t,e.textDirection),n.y+=o.top,this.drawTitle(n,t,e),this.drawBody(n,t,e),this.drawFooter(n,t,e),Ti(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map((({datasetIndex:t,index:e})=>{const i=this.chart.getDatasetMeta(t);if(!i)throw new Error("Cannot find a dataset at index "+t);return{datasetIndex:t,element:i.data[e],index:e}})),n=!f(i,s),o=this._positionChanged(s,e);(n||o)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,n=this._active||[],o=this._getActiveElements(t,n,e,i),a=this._positionChanged(o,t),r=e||!f(o,n)||a;return r&&(this._active=o,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),r}_getActiveElements(t,e,i,s){const n=this.options;if("mouseout"===t.type)return[];if(!s)return e.filter((t=>this.chart.data.datasets[t.datasetIndex]&&void 0!==this.chart.getDatasetMeta(t.datasetIndex).controller.getParsed(t.index)));const o=this.chart.getElementsAtEventForMode(t,n.mode,n,i);return n.reverse&&o.reverse(),o}_positionChanged(t,e){const{caretX:i,caretY:s,options:n}=this,o=Da[n.position].call(this,t,e);return!1!==o&&(i!==o.x||s!==o.y)}}var Na={id:"tooltip",_element:Wa,positioners:Da,afterInit(t,e,i){i&&(t.tooltip=new Wa({chart:t,options:i}))},beforeUpdate(t,e,i){t.tooltip&&t.tooltip.initialize(i)},reset(t,e,i){t.tooltip&&t.tooltip.initialize(i)},afterDraw(t){const e=t.tooltip;if(e&&e._willRender()){const i={tooltip:e};if(!1===t.notifyPlugins("beforeTooltipDraw",{...i,cancelable:!0}))return;e.draw(t.ctx),t.notifyPlugins("afterTooltipDraw",i)}},afterEvent(t,e){if(t.tooltip){const i=e.replay;t.tooltip.handleEvent(e.event,i,e.inChartArea)&&(e.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(t,e)=>e.bodyFont.size,boxWidth:(t,e)=>e.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:Va},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:t=>"filter"!==t&&"itemSort"!==t&&"external"!==t,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};return Tn.register(Un,$o,go,t),Tn.helpers={...Hi},Tn._adapters=In,Tn.Animation=As,Tn.Animations=Ts,Tn.animator=bt,Tn.controllers=nn.controllers.items,Tn.DatasetController=js,Tn.Element=$s,Tn.elements=go,Tn.Interaction=Ki,Tn.layouts=ls,Tn.platforms=Ds,Tn.Scale=tn,Tn.Ticks=ae,Object.assign(Tn,Un,$o,go,t,Ds),Tn.Chart=Tn,"undefined"!=typeof window&&(window.Chart=Tn),Tn})); +//# sourceMappingURL=chart.umd.js.map From 21d3947d08e6e1c27a31c424fec1c8288deb986c Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 01:02:14 +0200 Subject: [PATCH 057/137] Label the revenue axis in euros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The y-axis ticks and the hover both carry the unit now — an axis in euros with a bare number in the tooltip reads as two different quantities. Two formatters, not one: the axis rounds to whole euros to keep the labels short, but the tooltip keeps the cents. Rounding an amount someone is reading off a chart to answer "how much did we take in March" is a lie, and €1.234,50 becoming "€ 1.235" is exactly that. Locale is nl-BE, so it renders "€ 1.234,50" rather than "€1,234.5". Co-Authored-By: Claude Opus 4.8 --- .../templates/controlpanel/dashboard.html | 25 +++++++++++++++---- static/css/app.css | 3 +++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/controlpanel/templates/controlpanel/dashboard.html b/controlpanel/templates/controlpanel/dashboard.html index 1d82e07..8fff2a9 100644 --- a/controlpanel/templates/controlpanel/dashboard.html +++ b/controlpanel/templates/controlpanel/dashboard.html @@ -196,7 +196,13 @@ const ink = css("--color-base-content", "#333"); const grid = "color-mix(in oklab, " + ink + " 15%, transparent)"; - const build = (id, label, series, colour, type) => + // Locale-aware, so 1234.5 reads as "€ 1.234,50" rather than "€1,234.5". Two of + // them: the axis is rounded to keep the labels short, but the tooltip keeps the + // cents — rounding a euro amount someone is reading off a chart is a lie. + const axisEuros = new Intl.NumberFormat("nl-BE", { style: "currency", currency: "EUR", maximumFractionDigits: 0 }); + const exactEuros = new Intl.NumberFormat("nl-BE", { style: "currency", currency: "EUR", minimumFractionDigits: 2 }); + + const build = (id, label, series, colour, type, money) => new Chart(document.getElementById(id), { type, data: { @@ -206,17 +212,26 @@ options: { responsive: true, maintainAspectRatio: false, - plugins: { legend: { display: false } }, + plugins: { + legend: { display: false }, + // The tooltip carries the unit too: an axis in euros and a bare + // number on hover reads as two different quantities. + tooltip: money ? { callbacks: { label: (item) => exactEuros.format(item.parsed.y) } } : {}, + }, scales: { x: { ticks: { color: ink }, grid: { color: grid } }, - y: { beginAtZero: true, ticks: { color: ink, precision: 0 }, grid: { color: grid } }, + y: { + beginAtZero: true, + grid: { color: grid }, + ticks: { color: ink, precision: 0, callback: money ? (value) => axisEuros.format(value) : undefined }, + }, }, }, }); return [ - build("signups-chart", "Signups", data.signups, css("--color-primary", "#4f46e5"), "bar"), - build("revenue-chart", "Revenue", data.revenue, css("--color-accent", "#0ea5e9"), "line"), + build("signups-chart", "Signups", data.signups, css("--color-primary", "#4f46e5"), "bar", false), + build("revenue-chart", "Revenue", data.revenue, css("--color-accent", "#0ea5e9"), "line", true), ]; }; diff --git a/static/css/app.css b/static/css/app.css index 2d7c3fa..ee01d86 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -3357,6 +3357,9 @@ .overflow-x-auto { overflow-x: auto; } + .rounded { + border-radius: 0.25rem; + } .rounded-box { border-radius: var(--radius-box); } From f2b78bb1ddc6fdda12de84d797fe9faed7c9352e Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 01:08:37 +0200 Subject: [PATCH 058/137] Build the club metrics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The club page now leads with its own numbers that should be zero, then the health signals underneath. - Teams with no coach. Not a statistic but a defect in the club's setup: with nobody in a management position the access service grants no authority over that team, so nobody can pick the squad. A physio does not count -- the query keys on Position.management_position, and on this season only. - Unrostered members: active, paid, and on no team. - Unpaid money bucketed by age. "€250 overdue past 60 days" drives a phone call; "€250 outstanding" does not. - Renewal rate -- last season's actives who signed up again. Exactly computable because memberships are season-scoped. - Turnout, plus the share who never responded. Silence is not an absence, so it is excluded from turnout and reported separately: no-response is the leading indicator, since it measures whether members use the app at all. Two of these return None rather than a number, deliberately: a club in its first season has not failed to renew anyone, and a season with no past events has no turnout. Rendering either as 0% would libel the club, so the page says why instead. Money is pinned to two decimals -- SQLite's Sum() drops trailing zeros, so an aggregate rendered "€250" next to a "€0.00" constant on the same card. Co-Authored-By: Claude Opus 4.8 --- controlpanel/services/statistics.py | 134 ++++++++++++- .../templates/controlpanel/club_detail.html | 179 +++++++++++++++++- .../templates/controlpanel/dashboard.html | 22 +-- controlpanel/tests.py | 156 ++++++++++++++- controlpanel/views.py | 4 +- static/css/app.css | 31 +++ 6 files changed, 508 insertions(+), 18 deletions(-) diff --git a/controlpanel/services/statistics.py b/controlpanel/services/statistics.py index 1b9a553..a0e4570 100644 --- a/controlpanel/services/statistics.py +++ b/controlpanel/services/statistics.py @@ -17,7 +17,7 @@ from waffle import get_waffle_flag_model from authentication.middleware import ELEVATED_ROLES from club.models import Club, ClubMembership, ClubRole, Season -from events.models import Event +from events.models import Attendance, Event from members.models import Member from shop.models import Cart, Order from teams.models import StaffAssignment, Team, TeamMembership @@ -150,6 +150,138 @@ def _money(queryset): return queryset.aggregate(total=Sum("total"))["total"] or ZERO +def previous_season(club, season): + """The season immediately before ``season``. Seasons are ordered by name (which is + derived from the years), so go by the date instead — a club may skip a year.""" + if season is None: + return None + + return Season.objects.filter(club=club, end_date__lt=season.start_date).order_by("-end_date").first() + + +def renewal_rate(club, season): + """Share of last season's active members who signed up again. + + The single best health signal a club has, and it is exactly computable here because + memberships are season-scoped. Returns None when there is no season to compare + against — a first-season club has not failed to renew anyone, and rendering that as + 0% would libel it. + """ + previous = previous_season(club, season) + if previous is None: + return None + + was_active = ClubMembership.objects.filter(club=club, season=previous, status=ClubMembership.StatusChoices.ACTIVE) + total = was_active.count() + if not total: + return None + + returned = ClubMembership.objects.filter(club=club, season=season, member__in=was_active.values("member")).count() + + return round(100 * returned / total) + + +def teams_without_a_manager(club, season): + """Teams with nobody in a management position this season. + + A defect in the club's own setup, not a statistic: without a coach or manager the + access service grants nobody authority over that team, so nobody can pick the squad. + """ + if season is None: + return Team.objects.none() + + return Team.objects.filter(club=club).exclude(staff_assignments__season=season, staff_assignments__position__management_position=True) + + +def unrostered_members(club, season): + """Active members who are on no team this season — people who paid and play nowhere.""" + if season is None: + return Member.objects.none() + + rostered = TeamMembership.objects.filter(team__club=club, season=season).values("member") + + return Member.objects.filter(member_of__club=club, member_of__season=season, member_of__status=ClubMembership.StatusChoices.ACTIVE).exclude(pk__in=rostered).distinct() + + +def fee_aging(club): + """Unpaid orders bucketed by age. "€2,400 overdue past 60 days" drives a phone call; + "€2,400 outstanding" does not.""" + now = timezone.now() + owed = Order.objects.filter(club=club, status__in=OWED_STATUSES) + + buckets = [] + for label, older_than, newer_than in (("0-30 days", 0, 30), ("30-60 days", 30, 60), ("60+ days", 60, None)): + rows = owed.filter(created__lte=now - timedelta(days=older_than)) + if newer_than is not None: + rows = rows.filter(created__gt=now - timedelta(days=newer_than)) + buckets.append({"label": label, "total": _money(rows), "count": rows.count(), "overdue": newer_than is None}) + + return buckets + + +def attendance_rates(club, season): + """Turnout, and how many never answered. + + The no-response share is the leading indicator: it measures whether members are using + the app at all, which every other number here depends on. + """ + if season is None: + return {"turnout": None, "no_response": None, "responses": 0} + + counts = Attendance.objects.filter(event__club=club, event__season=season, event__start__lt=timezone.now()).aggregate( + present=Count("id", filter=Q(status=Attendance.AttendanceStatus.PRESENT)), + absent=Count("id", filter=Q(status=Attendance.AttendanceStatus.ABSENT)), + silent=Count("id", filter=Q(status=Attendance.AttendanceStatus.NO_RESPONSE)), + total=Count("id"), + ) + + answered = counts["present"] + counts["absent"] + + return { + "turnout": round(100 * counts["present"] / answered) if answered else None, + "no_response": round(100 * counts["silent"] / counts["total"]) if counts["total"] else None, + "responses": counts["total"], + } + + +def club_attention(club): + """A club's own numbers that are supposed to be zero.""" + season = Season.covering(club, timezone.localdate()) + memberships = ClubMembership.objects.filter(club=club) + + return { + "season": season, + "no_season": season is None, + "outstanding": _money(Order.objects.filter(club=club, status__in=OWED_STATUSES)), + "aging": fee_aging(club), + "unpaid_members": memberships.filter(season=season, fee_status=ClubMembership.FeeStatus.UNPAID).count() if season else 0, + "pending_approvals": memberships.filter(status=ClubMembership.StatusChoices.PENDING).count(), + "teams_without_manager": teams_without_a_manager(club, season).count(), + "unrostered": unrostered_members(club, season).count(), + "renewal_rate": renewal_rate(club, season), + "attendance": attendance_rates(club, season), + } + + +def club_charts(club): + season = Season.covering(club, timezone.localdate()) + memberships = ClubMembership.objects.filter(club=club, season=season) if season else ClubMembership.objects.none() + + return { + "signups": _monthly(ClubMembership.objects.filter(club=club, signed_up_at__isnull=False), "signed_up_at", Count("id")), + # Fee status this season, in the order a treasurer cares about. + "fees": [ + {"label": label, "value": memberships.filter(fee_status=status).count()} + for status, label in ( + (ClubMembership.FeeStatus.PAID, "Paid"), + (ClubMembership.FeeStatus.PARTIALLY_PAID, "Partial"), + (ClubMembership.FeeStatus.UNPAID, "Unpaid"), + (ClubMembership.FeeStatus.WAIVED, "Waived"), + ) + ], + } + + def club_statistics(club): """Stat groups for one club. Add new groups here as the domain grows.""" season = Season.covering(club, timezone.localdate()) diff --git a/controlpanel/templates/controlpanel/club_detail.html b/controlpanel/templates/controlpanel/club_detail.html index cca65d6..49d5504 100644 --- a/controlpanel/templates/controlpanel/club_detail.html +++ b/controlpanel/templates/controlpanel/club_detail.html @@ -1,5 +1,5 @@ {% extends "controlpanel/base.html" %} -{% load lucide %} +{% load static lucide %} {% block heading %}{{ club.name }}{% endblock heading %} @@ -33,6 +33,120 @@ This club is archived: its subdomain no longer resolves. Nothing has been deleted — restore it to bring it back.
{% endif %} + {% if attention.no_season %} +
+ {% lucide "calendar-x" size=20 %} + + No season covers today, so this club cannot take a signup or schedule a match. Nothing errors — it is simply inert. + +
+ {% endif %} + + {% comment %} + The club's own numbers that should be zero. Teams without a manager is a defect in + the club's setup, not a statistic: with nobody in a management position the access + service grants no authority over that team, so nobody can pick the squad. + {% endcomment %} +
+
+
+
{% lucide "banknote" size=16 %} Outstanding
+
€{{ attention.outstanding|floatformat:2 }}
+
{{ attention.unpaid_members }} member{{ attention.unpaid_members|pluralize }} unpaid this season
+
+
+
+
+
{% lucide "user-x" size=16 %} No coach
+
{{ attention.teams_without_manager }}
+
Teams nobody can pick a squad for
+
+
+
+
+
{% lucide "user-minus" size=16 %} Unrostered
+
{{ attention.unrostered }}
+
Active members on no team
+
+
+
+
+
{% lucide "clock" size=16 %} Pending
+
{{ attention.pending_approvals }}
+
Memberships awaiting approval
+
+
+
+ +
+
+
+

{% lucide "repeat" size=18 %} Renewal

+ {% if attention.renewal_rate is None %} + {# No prior season to compare against: a first-season club has not failed to renew anyone. #} +

No previous season to compare against yet.

+ {% else %} +
{{ attention.renewal_rate }}%
+

of last season's active members signed up again

+ + {% endif %} +
+
+ +
+
+

{% lucide "user-check" size=18 %} Attendance

+ {% if attention.attendance.turnout is None %} +

No past events with responses this season.

+ {% else %} +
{{ attention.attendance.turnout }}%
+

turnout of those who answered

+

+ {# The leading indicator: it measures whether members use the app at all. #} + {{ attention.attendance.no_response }}% + never responded +

+ {% endif %} +
+
+ +
+
+

{% lucide "hourglass" size=18 %} Unpaid, by age

+ + + {% for bucket in attention.aging %} + + + + + + {% endfor %} + +
{{ bucket.label }}€{{ bucket.total|floatformat:2 }}{{ bucket.count }} order{{ bucket.count|pluralize }}
+
+
+
+ +
+
+
+

{% lucide "user-plus" size=18 %} Signups per month

+
+ +
+
+
+
+
+

{% lucide "wallet" size=18 %} Fee status this season

+
+ +
+
+
+
+
{% for group in groups %}
@@ -127,3 +241,66 @@
{% endblock panel %} + +{% block extra_body %} + {{ charts|json_script:"chart-data" }} + + +{% endblock extra_body %} diff --git a/controlpanel/templates/controlpanel/dashboard.html b/controlpanel/templates/controlpanel/dashboard.html index 8fff2a9..1485b2b 100644 --- a/controlpanel/templates/controlpanel/dashboard.html +++ b/controlpanel/templates/controlpanel/dashboard.html @@ -40,7 +40,7 @@
{% lucide "banknote" size=16 %} Outstanding
-
€{{ attention.outstanding }}
+
€{{ attention.outstanding|floatformat:2 }}
Unpaid across every club
@@ -50,8 +50,6 @@

{% lucide "user-plus" size=18 %} Signups per month

- {# The wrapper's height is what bounds the canvas: with maintainAspectRatio off, - Chart.js sizes to its parent, and a parent with no height grows without end. #}
@@ -199,31 +197,31 @@ // Locale-aware, so 1234.5 reads as "€ 1.234,50" rather than "€1,234.5". Two of // them: the axis is rounded to keep the labels short, but the tooltip keeps the // cents — rounding a euro amount someone is reading off a chart is a lie. - const axisEuros = new Intl.NumberFormat("nl-BE", { style: "currency", currency: "EUR", maximumFractionDigits: 0 }); - const exactEuros = new Intl.NumberFormat("nl-BE", { style: "currency", currency: "EUR", minimumFractionDigits: 2 }); + const axisEuros = new Intl.NumberFormat("nl-BE", {style: "currency", currency: "EUR", maximumFractionDigits: 0}); + const exactEuros = new Intl.NumberFormat("nl-BE", {style: "currency", currency: "EUR", minimumFractionDigits: 2}); const build = (id, label, series, colour, type, money) => new Chart(document.getElementById(id), { type, data: { labels: series.map((point) => point.month), - datasets: [{ label, data: series.map((point) => point.value), borderColor: colour, backgroundColor: colour, tension: 0.3 }], + datasets: [{label, data: series.map((point) => point.value), borderColor: colour, backgroundColor: colour, tension: 0.3}], }, options: { responsive: true, maintainAspectRatio: false, plugins: { - legend: { display: false }, + legend: {display: false}, // The tooltip carries the unit too: an axis in euros and a bare // number on hover reads as two different quantities. - tooltip: money ? { callbacks: { label: (item) => exactEuros.format(item.parsed.y) } } : {}, + tooltip: money ? {callbacks: {label: (item) => exactEuros.format(item.parsed.y)}} : {}, }, scales: { - x: { ticks: { color: ink }, grid: { color: grid } }, + x: {ticks: {color: ink}, grid: {color: grid}}, y: { beginAtZero: true, - grid: { color: grid }, - ticks: { color: ink, precision: 0, callback: money ? (value) => axisEuros.format(value) : undefined }, + grid: {color: grid}, + ticks: {color: ink, precision: 0, callback: money ? (value) => axisEuros.format(value) : undefined}, }, }, }, @@ -242,7 +240,7 @@ new MutationObserver(() => { charts.forEach((chart) => chart.destroy()); charts = render(); - }).observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] }); + }).observe(document.documentElement, {attributes: true, attributeFilter: ["data-theme"]}); })(); {% endblock extra_body %} diff --git a/controlpanel/tests.py b/controlpanel/tests.py index e56e340..ad415bf 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -13,14 +13,31 @@ from django.utils import timezone from waffle import get_waffle_flag_model, get_waffle_switch_model from club.models import Club, ClubMembership, ClubRole, Season -from events.models import Event +from events.models import Attendance, Event from members.models import Member from shop.models import Order -from teams.models import Position, Team, TeamMembership +from teams.models import Position, StaffAssignment, Team, TeamMembership from .services.admins import grant_club_admin from .services.platform_admins import PlatformAdminError, is_last_superuser, set_platform_access -from .services.statistics import admins_pending_mfa, club_statistics, clubs_with_totals, clubs_without_a_season, dormant_clubs, flag_adoption, onboarding_funnel, platform_attention, platform_charts, platform_totals +from .services.statistics import ( + admins_pending_mfa, + attendance_rates, + club_attention, + club_statistics, + clubs_with_totals, + clubs_without_a_season, + dormant_clubs, + fee_aging, + flag_adoption, + onboarding_funnel, + platform_attention, + platform_charts, + platform_totals, + renewal_rate, + teams_without_a_manager, + unrostered_members, +) from .templatetags.ui import as_alert, daisy, excluded, field_icon User = get_user_model() @@ -716,3 +733,136 @@ class DashboardMetricsTests(ControlPanelTestBase): self.assertContains(response, 'id="revenue-chart"') self.assertContains(response, "js/chart.js") self.assertIn("signups", response.context["charts"]) + + +class ClubAttentionTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United") + self.today = timezone.localdate() + self.season = Season.objects.create(club=self.club, start_date=self.today - datetime.timedelta(days=30), end_date=self.today + datetime.timedelta(days=300)) + self.member = Member.objects.create(first_name="Ada", last_name="Lovelace") + + def membership(self, member=None, season=None, **kwargs): + return ClubMembership.objects.create(club=self.club, season=season or self.season, member=member or self.member, **kwargs) + + def test_a_team_with_no_manager_is_flagged(self): + team = Team.objects.create(club=self.club, name="U15") + + self.assertIn(team, teams_without_a_manager(self.club, self.season)) + + coach = Position.objects.create(club=self.club, name="Coach", staff_position=True, management_position=True) + StaffAssignment.objects.create(team=team, member=self.member, season=self.season, position=coach) + + self.assertNotIn(team, teams_without_a_manager(self.club, self.season)) + + def test_a_non_management_staffer_does_not_count_as_a_coach(self): + # Somebody has to be able to pick the squad; a physio cannot. + team = Team.objects.create(club=self.club, name="U15") + physio = Position.objects.create(club=self.club, name="Physio", staff_position=True, management_position=False) + StaffAssignment.objects.create(team=team, member=self.member, season=self.season, position=physio) + + self.assertIn(team, teams_without_a_manager(self.club, self.season)) + + def test_a_coach_from_a_previous_season_does_not_count(self): + old = Season.objects.create(club=self.club, start_date=self.today - datetime.timedelta(days=400), end_date=self.today - datetime.timedelta(days=40)) + team = Team.objects.create(club=self.club, name="U15") + coach = Position.objects.create(club=self.club, name="Coach", staff_position=True, management_position=True) + StaffAssignment.objects.create(team=team, member=self.member, season=old, position=coach) + + self.assertIn(team, teams_without_a_manager(self.club, self.season)) + + def test_an_active_member_on_no_team_is_unrostered(self): + self.membership(status=ClubMembership.StatusChoices.ACTIVE) + + self.assertIn(self.member, unrostered_members(self.club, self.season)) + + team = Team.objects.create(club=self.club, name="U15") + position = Position.objects.create(club=self.club, name="Forward") + TeamMembership.objects.create(team=team, member=self.member, season=self.season, position=position, jersey_number=9) + + self.assertNotIn(self.member, unrostered_members(self.club, self.season)) + + def test_a_pending_member_is_not_counted_as_unrostered(self): + # They have not been let in yet, so having no team is expected. + self.membership(status=ClubMembership.StatusChoices.PENDING) + + self.assertNotIn(self.member, unrostered_members(self.club, self.season)) + + def test_renewal_compares_against_the_previous_season(self): + previous = Season.objects.create(club=self.club, start_date=self.today - datetime.timedelta(days=400), end_date=self.today - datetime.timedelta(days=40)) + stayed = self.member + left = Member.objects.create(first_name="Bob", last_name="Bobson") + self.membership(member=stayed, season=previous, status=ClubMembership.StatusChoices.ACTIVE) + self.membership(member=left, season=previous, status=ClubMembership.StatusChoices.ACTIVE) + self.membership(member=stayed, season=self.season) + + self.assertEqual(renewal_rate(self.club, self.season), 50) + + def test_a_previous_season_with_nobody_active_yields_no_rate(self): + # There is a season to compare against but nobody to renew — dividing by that + # would blow up, and calling it 0% would be a lie. + previous = Season.objects.create(club=self.club, start_date=self.today - datetime.timedelta(days=400), end_date=self.today - datetime.timedelta(days=40)) + self.membership(season=previous, status=ClubMembership.StatusChoices.LAPSED) + + self.assertIsNone(renewal_rate(self.club, self.season)) + + def test_a_first_season_club_has_no_renewal_rate(self): + # It has not failed to renew anyone; rendering that as 0% would libel it. + self.membership(status=ClubMembership.StatusChoices.ACTIVE) + + self.assertIsNone(renewal_rate(self.club, self.season)) + + def test_unpaid_orders_are_bucketed_by_age(self): + old = Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("100.00"), status=Order.OrderStatus.PENDING) + Order.objects.filter(pk=old.pk).update(created=timezone.now() - datetime.timedelta(days=90)) + Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("40.00"), status=Order.OrderStatus.PENDING) + Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("999.00"), status=Order.OrderStatus.PAID) + + buckets = {bucket["label"]: bucket["total"] for bucket in fee_aging(self.club)} + + self.assertEqual(buckets["0-30 days"], Decimal("40.00")) + self.assertEqual(buckets["60+ days"], Decimal("100.00")) # paid orders are not owed + + def test_attendance_is_turnout_of_those_who_answered(self): + event = Event.objects.create(club=self.club, season=self.season, title="Match", start=timezone.now() - datetime.timedelta(days=1)) + bob = Member.objects.create(first_name="Bob", last_name="Bobson") + carol = Member.objects.create(first_name="Carol", last_name="Carolson") + Attendance.objects.create(event=event, member=self.member, status=Attendance.AttendanceStatus.PRESENT) + Attendance.objects.create(event=event, member=bob, status=Attendance.AttendanceStatus.ABSENT) + Attendance.objects.create(event=event, member=carol, status=Attendance.AttendanceStatus.NO_RESPONSE) + + rates = attendance_rates(self.club, self.season) + + self.assertEqual(rates["turnout"], 50) # 1 present of 2 who answered — silence is not an absence + self.assertEqual(rates["no_response"], 33) # 1 of 3 never answered + + def test_a_future_event_does_not_drag_turnout_down(self): + event = Event.objects.create(club=self.club, season=self.season, title="Next week", start=timezone.now() + datetime.timedelta(days=7)) + Attendance.objects.create(event=event, member=self.member, status=Attendance.AttendanceStatus.NO_RESPONSE) + + self.assertIsNone(attendance_rates(self.club, self.season)["turnout"]) + + def test_a_club_with_no_season_reports_no_rates(self): + Season.objects.all().delete() + + attention = club_attention(self.club) + + self.assertTrue(attention["no_season"]) + self.assertEqual(attention["teams_without_manager"], 0) + self.assertIsNone(attention["renewal_rate"]) + + +class ClubDetailMetricsTests(ControlPanelTestBase): + def test_the_club_page_renders_its_metrics_and_charts(self): + response = self.client.get(reverse("controlpanel:club_detail", args=[self.club.pk])) + + self.assertContains(response, "No coach") + self.assertContains(response, "Unrostered") + self.assertContains(response, "Unpaid, by age") + self.assertContains(response, 'id="fees-chart"') + self.assertIn("fees", response.context["charts"]) + + def test_a_club_without_a_season_is_told_it_is_inert(self): + response = self.client.get(reverse("controlpanel:club_detail", args=[self.club.pk])) + + self.assertContains(response, "cannot take a signup") diff --git a/controlpanel/views.py b/controlpanel/views.py index 43cae05..93d87b1 100644 --- a/controlpanel/views.py +++ b/controlpanel/views.py @@ -17,7 +17,7 @@ from .services.platform_admins import ( revoke_platform_access, set_platform_access, ) -from .services.statistics import club_statistics, clubs_with_totals, flag_adoption, onboarding_funnel, platform_attention, platform_charts, platform_totals +from .services.statistics import club_attention, club_charts, club_statistics, clubs_with_totals, flag_adoption, onboarding_funnel, platform_attention, platform_charts, platform_totals Flag = get_waffle_flag_model() Switch = get_waffle_switch_model() @@ -95,6 +95,8 @@ class ClubDetailView(PlatformStaffRequiredMixin, DetailView): return super().get_context_data( nav="clubs", groups=club_statistics(self.object), + attention=club_attention(self.object), + charts=club_charts(self.object), admins=ClubRole.objects.filter(club=self.object, role=ClubRole.Roles.ADMIN).select_related("member", "member__user"), flags=flags_for_club(self.object), **kwargs, diff --git a/static/css/app.css b/static/css/app.css index ee01d86..080d6b1 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -25,6 +25,8 @@ --text-2xl--line-height: calc(2 / 1.5); --text-3xl: 1.875rem; --text-3xl--line-height: calc(2.25 / 1.875); + --text-4xl: 2.25rem; + --text-4xl--line-height: calc(2.5 / 2.25); --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; @@ -1545,6 +1547,23 @@ } } } + .indicator { + @layer daisyui.l1.l2.l3 { + position: relative; + display: inline-flex; + width: max-content; + :where(.indicator-item) { + z-index: 1; + position: absolute; + white-space: nowrap; + top: var(--indicator-t, 0); + bottom: var(--indicator-b, auto); + left: var(--indicator-s, auto); + right: var(--indicator-e, 0); + translate: var(--indicator-x, 50%) var(--indicator-y, -50%); + } + } + } .table { @layer daisyui.l1.l2.l3 { font-size: 0.875rem; @@ -3553,6 +3572,10 @@ font-size: var(--text-3xl); line-height: var(--tw-leading, var(--text-3xl--line-height)); } + .text-4xl { + font-size: var(--text-4xl); + line-height: var(--tw-leading, var(--text-4xl--line-height)); + } .text-base { font-size: var(--text-base); line-height: var(--tw-leading, var(--text-base--line-height)); @@ -3664,6 +3687,9 @@ .text-sky-500 { color: var(--color-sky-500); } + .text-warning { + color: var(--color-warning); + } .tabular-nums { --tw-numeric-spacing: tabular-nums; font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,); @@ -3841,6 +3867,11 @@ grid-template-columns: repeat(2, minmax(0, 1fr)); } } + .lg\:grid-cols-3 { + @media (width >= 64rem) { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + } .lg\:grid-cols-4 { @media (width >= 64rem) { grid-template-columns: repeat(4, minmax(0, 1fr)); From 639807b2d2c9c700e8e0dee099bc6fa2bb81efce Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 01:12:01 +0200 Subject: [PATCH 059/137] Split club signups into new members and renewals A "New members" card sits beside Renewal, and the signups chart becomes a stacked bar: bar height stays "signups this month" while the split shows where they came from. New means "first-ever season at this club", not "signed up recently". A member who lapsed for a year and came back is a renewal, and counting them as new would flatter every recovery into growth. It is also per club, not per platform: someone who plays for another club is still new here. The split resolves each member's earliest season once up front rather than asking per row, so the chart costs two queries instead of one per membership. Co-Authored-By: Claude Opus 4.8 --- controlpanel/services/statistics.py | 47 +++++++++++++- .../templates/controlpanel/club_detail.html | 25 ++++++-- controlpanel/tests.py | 64 +++++++++++++++++++ 3 files changed, 130 insertions(+), 6 deletions(-) diff --git a/controlpanel/services/statistics.py b/controlpanel/services/statistics.py index a0e4570..1b23dc1 100644 --- a/controlpanel/services/statistics.py +++ b/controlpanel/services/statistics.py @@ -5,6 +5,7 @@ means adding an entry here and nothing else. ``clubs_with_totals`` annotates in a single query — the club list must not fan out into N+1. """ +from collections import defaultdict from datetime import timedelta from decimal import Decimal @@ -181,6 +182,49 @@ def renewal_rate(club, season): return round(100 * returned / total) +def new_members(club, season): + """Members whose first-ever season at this club is ``season``. + + Keyed on "has no membership in an earlier season", not on "signed up recently" — a + member who lapsed for a year and came back is a renewal, not a new member, and + counting them as new would flatter every recovery into growth. + """ + if season is None: + return Member.objects.none() + + seen_before = ClubMembership.objects.filter(club=club, season__start_date__lt=season.start_date).values("member") + + return Member.objects.filter(member_of__club=club, member_of__season=season).exclude(pk__in=seen_before).distinct() + + +def signup_split(club, months=MONTHS_OF_HISTORY): + """Signups per month, split into first-timers and returners. + + Which season a signup belongs to decides the split, so the member's earliest season at + this club is looked up once for everyone rather than per row — the same question asked + inside a loop is a query per membership. + """ + start = (timezone.now() - timedelta(days=30 * months)).replace(day=1, hour=0, minute=0, second=0, microsecond=0) + + first_season = {} + for member_id, season_start in ClubMembership.objects.filter(club=club).values_list("member_id", "season__start_date"): + if member_id not in first_season or season_start < first_season[member_id]: + first_season[member_id] = season_start + + counts = defaultdict(lambda: {"new": 0, "returning": 0}) + for member_id, season_start, signed_up_at in ClubMembership.objects.filter(club=club, signed_up_at__isnull=False, signed_up_at__gte=start).values_list("member_id", "season__start_date", "signed_up_at"): + kind = "new" if season_start == first_season[member_id] else "returning" + counts[signed_up_at.strftime("%Y-%m")][kind] += 1 + + series, cursor = [], start + while cursor <= timezone.now(): + month = counts[cursor.strftime("%Y-%m")] + series.append({"month": cursor.strftime("%b %Y"), "new": month["new"], "returning": month["returning"]}) + cursor = (cursor + timedelta(days=32)).replace(day=1) + + return series + + def teams_without_a_manager(club, season): """Teams with nobody in a management position this season. @@ -258,6 +302,7 @@ def club_attention(club): "pending_approvals": memberships.filter(status=ClubMembership.StatusChoices.PENDING).count(), "teams_without_manager": teams_without_a_manager(club, season).count(), "unrostered": unrostered_members(club, season).count(), + "new_members": new_members(club, season).count(), "renewal_rate": renewal_rate(club, season), "attendance": attendance_rates(club, season), } @@ -268,7 +313,7 @@ def club_charts(club): memberships = ClubMembership.objects.filter(club=club, season=season) if season else ClubMembership.objects.none() return { - "signups": _monthly(ClubMembership.objects.filter(club=club, signed_up_at__isnull=False), "signed_up_at", Count("id")), + "signups": signup_split(club), # Fee status this season, in the order a treasurer cares about. "fees": [ {"label": label, "value": memberships.filter(fee_status=status).count()} diff --git a/controlpanel/templates/controlpanel/club_detail.html b/controlpanel/templates/controlpanel/club_detail.html index 49d5504..58b619d 100644 --- a/controlpanel/templates/controlpanel/club_detail.html +++ b/controlpanel/templates/controlpanel/club_detail.html @@ -78,7 +78,16 @@
-
+
+
+
+

{% lucide "sparkles" size=18 %} New members

+
{{ attention.new_members }}
+ {# First season at this club — someone returning after a year away is a renewal. #} +

first season at this club

+
+
+

{% lucide "repeat" size=18 %} Renewal

@@ -132,6 +141,7 @@

{% lucide "user-plus" size=18 %} Signups per month

+

New members against returning ones.

@@ -254,19 +264,24 @@ const ink = css("--color-base-content", "#333"); const grid = "color-mix(in oklab, " + ink + " 15%, transparent)"; + // Stacked: the bar height stays "signups this month" while the split shows where + // they came from. Side-by-side bars would answer a different question. const signups = new Chart(document.getElementById("signups-chart"), { type: "bar", data: { labels: data.signups.map((point) => point.month), - datasets: [{ label: "Signups", data: data.signups.map((point) => point.value), backgroundColor: css("--color-primary", "#4f46e5") }], + datasets: [ + { label: "New", data: data.signups.map((point) => point.new), backgroundColor: css("--color-primary", "#4f46e5") }, + { label: "Returning", data: data.signups.map((point) => point.returning), backgroundColor: css("--color-accent", "#0ea5e9") }, + ], }, options: { responsive: true, maintainAspectRatio: false, - plugins: { legend: { display: false } }, + plugins: { legend: { position: "bottom", labels: { color: ink } } }, scales: { - x: { ticks: { color: ink }, grid: { color: grid } }, - y: { beginAtZero: true, ticks: { color: ink, precision: 0 }, grid: { color: grid } }, + x: { stacked: true, ticks: { color: ink }, grid: { color: grid } }, + y: { stacked: true, beginAtZero: true, ticks: { color: ink, precision: 0 }, grid: { color: grid } }, }, }, }); diff --git a/controlpanel/tests.py b/controlpanel/tests.py index ad415bf..acda275 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -30,11 +30,13 @@ from .services.statistics import ( dormant_clubs, fee_aging, flag_adoption, + new_members, onboarding_funnel, platform_attention, platform_charts, platform_totals, renewal_rate, + signup_split, teams_without_a_manager, unrostered_members, ) @@ -866,3 +868,65 @@ class ClubDetailMetricsTests(ControlPanelTestBase): response = self.client.get(reverse("controlpanel:club_detail", args=[self.club.pk])) self.assertContains(response, "cannot take a signup") + + +class NewMemberTests(TestCase): + def setUp(self): + self.club = Club.objects.create(name="Ajax United") + self.today = timezone.localdate() + self.previous = Season.objects.create(club=self.club, start_date=self.today - datetime.timedelta(days=400), end_date=self.today - datetime.timedelta(days=40)) + self.season = Season.objects.create(club=self.club, start_date=self.today - datetime.timedelta(days=30), end_date=self.today + datetime.timedelta(days=300)) + self.veteran = Member.objects.create(first_name="Ada", last_name="Lovelace") + self.rookie = Member.objects.create(first_name="Bob", last_name="Bobson") + + def membership(self, member, season, signed_up_at=None): + return ClubMembership.objects.create(club=self.club, season=season, member=member, status=ClubMembership.StatusChoices.ACTIVE, signed_up_at=signed_up_at) + + def test_only_first_timers_count_as_new(self): + self.membership(self.veteran, self.previous) + self.membership(self.veteran, self.season) + self.membership(self.rookie, self.season) + + new = new_members(self.club, self.season) + + self.assertIn(self.rookie, new) + self.assertNotIn(self.veteran, new) + + def test_a_member_returning_after_a_gap_is_not_new(self): + # They skipped a season and came back. Counting that as growth would flatter every + # recovery; they are a renewal. + self.membership(self.veteran, self.previous) + gap = Season.objects.create(club=self.club, start_date=self.today - datetime.timedelta(days=39), end_date=self.today - datetime.timedelta(days=31)) # noqa: F841 + self.membership(self.veteran, self.season) + + self.assertNotIn(self.veteran, new_members(self.club, self.season)) + + def test_a_member_of_another_club_is_new_here(self): + # "New" is per club, not per platform. + other = Club.objects.create(name="Feyenoord") + other_season = Season.objects.create(club=other, start_date=self.today - datetime.timedelta(days=400), end_date=self.today - datetime.timedelta(days=40)) + ClubMembership.objects.create(club=other, season=other_season, member=self.rookie) + self.membership(self.rookie, self.season) + + self.assertIn(self.rookie, new_members(self.club, self.season)) + + def test_a_club_with_no_season_has_no_new_members(self): + self.assertEqual(new_members(self.club, None).count(), 0) + + def test_signups_are_split_by_month_into_new_and_returning(self): + self.membership(self.veteran, self.previous, signed_up_at=self.today - datetime.timedelta(days=200)) + self.membership(self.veteran, self.season, signed_up_at=self.today) + self.membership(self.rookie, self.season, signed_up_at=self.today) + + this_month = signup_split(self.club)[-1] + + self.assertEqual(this_month["new"], 1) # the rookie + self.assertEqual(this_month["returning"], 1) # the veteran renewing + + def test_a_first_ever_signup_counts_as_new_in_its_own_month(self): + self.membership(self.veteran, self.previous, signed_up_at=self.today - datetime.timedelta(days=200)) + + series = signup_split(self.club) + + self.assertEqual(sum(month["new"] for month in series), 1) + self.assertEqual(sum(month["returning"] for month in series), 0) From 192fe5ad0ed9e840ed21bcc25c108759ba4e428e Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 01:17:26 +0200 Subject: [PATCH 060/137] Split platform signups, and rework the club table into health MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The platform signups chart gets the same stacked new/returning split as the club one. "First season" is keyed on (club, member), never the member alone: the same person can be new at one club while renewing at another, and collapsing that would file their second club's very first signup as a renewal. The dashboard's club table stops reporting vanity counts. A member total says nothing you can act on; "no coach", "nothing scheduled", "€ owed" and "no admins" each name something somebody has to go and fix. Columns are now active members, unpaid members, money owed, teams (flagging those nobody can pick a squad for), upcoming events, and admins -- with No season / Dormant badges on the club itself. Every column is annotated in ONE query, each aggregate in its own subquery. That is not stylistic: aggregates spanning different joins multiply each other's rows, so a Sum of orders sitting next to a Count of memberships returns the club's debt multiplied by its membership count. A test pins €100 against three memberships and would catch it coming back as €300. Co-Authored-By: Claude Opus 4.8 --- controlpanel/services/statistics.py | 72 +++++++++++++--- .../templates/controlpanel/dashboard.html | 67 +++++++++++---- controlpanel/tests.py | 82 ++++++++++++++++++- controlpanel/views.py | 4 +- static/css/app.css | 9 ++ 5 files changed, 203 insertions(+), 31 deletions(-) diff --git a/controlpanel/services/statistics.py b/controlpanel/services/statistics.py index 1b23dc1..406fb91 100644 --- a/controlpanel/services/statistics.py +++ b/controlpanel/services/statistics.py @@ -11,8 +11,8 @@ from decimal import Decimal from allauth.mfa.models import Authenticator from django.contrib.auth import get_user_model -from django.db.models import Count, Q, Sum -from django.db.models.functions import TruncMonth +from django.db.models import Count, DecimalField, Exists, F, IntegerField, OuterRef, Q, Subquery, Sum, Value +from django.db.models.functions import Coalesce, TruncMonth from django.utils import timezone from waffle import get_waffle_flag_model @@ -44,6 +44,46 @@ def clubs_with_totals(queryset=None): ) +def _subquery(queryset, expression, output_field): + """One aggregate, in its own subquery. + + Deliberately not a pile of annotate(Count(...), Sum(...)) on one queryset: aggregates + that span *different* joins multiply each other's rows, so a club's outstanding total + would come back doubled for every membership it happens to have. Subqueries each stand + alone, so nothing can inflate anything else. + """ + return Coalesce(Subquery(queryset.filter(club=OuterRef("pk")).values("club").annotate(value=expression).values("value"), output_field=output_field), Value(0), output_field=output_field) + + +def clubs_with_health(queryset=None, today=None, now=None): + """Clubs annotated with the health of each — for the dashboard table, in one query.""" + today = today or timezone.localdate() + now = now or timezone.now() + clubs = Club.objects.active() if queryset is None else queryset + + in_season = Q(season__start_date__lte=today, season__end_date__gte=today) + managed_this_season = Q( + staff_assignments__season__start_date__lte=today, + staff_assignments__season__end_date__gte=today, + staff_assignments__position__management_position=True, + ) + + return ( + clubs.annotate( + has_season=Exists(Season.objects.filter(club=OuterRef("pk"), start_date__lte=today, end_date__gte=today)), + active_members=_subquery(ClubMembership.objects.filter(in_season, status=ClubMembership.StatusChoices.ACTIVE), Count("pk"), IntegerField()), + unpaid_members=_subquery(ClubMembership.objects.filter(in_season, fee_status=ClubMembership.FeeStatus.UNPAID), Count("pk"), IntegerField()), + outstanding=_subquery(Order.objects.filter(status__in=OWED_STATUSES), Sum("total"), DecimalField(max_digits=10, decimal_places=2)), + upcoming_events=_subquery(Event.objects.filter(start__gte=now, start__lte=now + timedelta(days=DORMANT_DAYS)), Count("pk"), IntegerField()), + team_count=_subquery(Team.objects.all(), Count("pk"), IntegerField()), + teams_managed=_subquery(Team.objects.filter(managed_this_season), Count("pk", distinct=True), IntegerField()), + admin_count=_subquery(ClubRole.objects.filter(role=ClubRole.Roles.ADMIN), Count("pk"), IntegerField()), + ) + .annotate(teams_without_coach=F("team_count") - F("teams_managed")) + .order_by("name") + ) + + def platform_totals(): return { "clubs": Club.objects.active().count(), @@ -142,7 +182,7 @@ def _monthly(queryset, field, value, months=MONTHS_OF_HISTORY): def platform_charts(): return { - "signups": _monthly(ClubMembership.objects.filter(signed_up_at__isnull=False), "signed_up_at", Count("id")), + "signups": signup_split(), "revenue": _monthly(Order.objects.filter(status__in=PAID_STATUSES), "created", Sum("total")), } @@ -197,23 +237,29 @@ def new_members(club, season): return Member.objects.filter(member_of__club=club, member_of__season=season).exclude(pk__in=seen_before).distinct() -def signup_split(club, months=MONTHS_OF_HISTORY): - """Signups per month, split into first-timers and returners. +def signup_split(club=None, months=MONTHS_OF_HISTORY): + """Signups per month, split into first-timers and returners. ``club=None`` is platform-wide. - Which season a signup belongs to decides the split, so the member's earliest season at - this club is looked up once for everyone rather than per row — the same question asked - inside a loop is a query per membership. + "First" is keyed on (club, member), never on the member alone — the same person can be + new at one club while renewing at another, and collapsing that would mark their second + club's very first signup as a renewal. + + Each member's earliest season is resolved once up front rather than per row: the same + question asked inside a loop is one query per membership. """ start = (timezone.now() - timedelta(days=30 * months)).replace(day=1, hour=0, minute=0, second=0, microsecond=0) + memberships = ClubMembership.objects.all() if club is None else ClubMembership.objects.filter(club=club) + first_season = {} - for member_id, season_start in ClubMembership.objects.filter(club=club).values_list("member_id", "season__start_date"): - if member_id not in first_season or season_start < first_season[member_id]: - first_season[member_id] = season_start + for club_id, member_id, season_start in memberships.values_list("club_id", "member_id", "season__start_date"): + key = (club_id, member_id) + if key not in first_season or season_start < first_season[key]: + first_season[key] = season_start counts = defaultdict(lambda: {"new": 0, "returning": 0}) - for member_id, season_start, signed_up_at in ClubMembership.objects.filter(club=club, signed_up_at__isnull=False, signed_up_at__gte=start).values_list("member_id", "season__start_date", "signed_up_at"): - kind = "new" if season_start == first_season[member_id] else "returning" + for club_id, member_id, season_start, signed_up_at in memberships.filter(signed_up_at__isnull=False, signed_up_at__gte=start).values_list("club_id", "member_id", "season__start_date", "signed_up_at"): + kind = "new" if season_start == first_season[(club_id, member_id)] else "returning" counts[signed_up_at.strftime("%Y-%m")][kind] += 1 series, cursor = [], start diff --git a/controlpanel/templates/controlpanel/dashboard.html b/controlpanel/templates/controlpanel/dashboard.html index 1485b2b..ff02672 100644 --- a/controlpanel/templates/controlpanel/dashboard.html +++ b/controlpanel/templates/controlpanel/dashboard.html @@ -50,6 +50,7 @@

{% lucide "user-plus" size=18 %} Signups per month

+

New members against returning ones, across every club.

@@ -142,15 +143,22 @@

Clubs

+ {% comment %} + Health, not vanity: a club's member count says nothing you can act on, while + "no coach", "nothing scheduled" and "€ owed" each name a thing somebody has to + go and fix. Every column here is annotated in the same single query. + {% endcomment %}
- - - - + + + + + + @@ -158,16 +166,27 @@ - - - - + + + + + + {% empty %} - + {% endfor %} @@ -227,10 +246,30 @@ }, }); - return [ - build("signups-chart", "Signups", data.signups, css("--color-primary", "#4f46e5"), "bar", false), - build("revenue-chart", "Revenue", data.revenue, css("--color-accent", "#0ea5e9"), "line", true), - ]; + // Stacked, so the bar height stays "signups this month" while the split shows + // where they came from. New is per club: joining a second club is a new + // membership there, even for someone who has been on the platform for years. + const signups = new Chart(document.getElementById("signups-chart"), { + type: "bar", + data: { + labels: data.signups.map((point) => point.month), + datasets: [ + { label: "New", data: data.signups.map((point) => point.new), backgroundColor: css("--color-primary", "#4f46e5") }, + { label: "Returning", data: data.signups.map((point) => point.returning), backgroundColor: css("--color-accent", "#0ea5e9") }, + ], + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { legend: { position: "bottom", labels: { color: ink } } }, + scales: { + x: { stacked: true, ticks: { color: ink }, grid: { color: grid } }, + y: { stacked: true, beginAtZero: true, ticks: { color: ink, precision: 0 }, grid: { color: grid } }, + }, + }, + }); + + return [signups, build("revenue-chart", "Revenue", data.revenue, css("--color-accent", "#0ea5e9"), "line", true)]; }; let charts = render(); diff --git a/controlpanel/tests.py b/controlpanel/tests.py index acda275..b52ba57 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -25,6 +25,7 @@ from .services.statistics import ( attendance_rates, club_attention, club_statistics, + clubs_with_health, clubs_with_totals, clubs_without_a_season, dormant_clubs, @@ -708,14 +709,24 @@ class PlatformChartTests(TestCase): series = platform_charts()["signups"] self.assertEqual(len(series), 13) - self.assertTrue(all(point["value"] == 0 for point in series)) + self.assertTrue(all(point["new"] == 0 and point["returning"] == 0 for point in series)) def test_signups_land_in_the_month_they_happened(self): ClubMembership.objects.create(club=self.club, season=self.season, member=self.member, signed_up_at=timezone.localdate()) series = platform_charts()["signups"] - self.assertEqual(series[-1]["value"], 1) + self.assertEqual(series[-1]["new"], 1) + + def test_new_is_per_club_not_per_platform(self): + # A veteran of one club joining a second is new *there*. Keying "first season" on the + # member alone would file their very first signup at the new club as a renewal. + other = Club.objects.create(name="Feyenoord") + old_season = Season.objects.create(club=other, start_date=timezone.localdate() - datetime.timedelta(days=400), end_date=timezone.localdate() - datetime.timedelta(days=40)) + ClubMembership.objects.create(club=other, season=old_season, member=self.member, signed_up_at=timezone.localdate() - datetime.timedelta(days=300)) + ClubMembership.objects.create(club=self.club, season=self.season, member=self.member, signed_up_at=timezone.localdate()) + + self.assertEqual(platform_charts()["signups"][-1]["new"], 1) def test_only_paid_orders_count_as_revenue(self): Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("50.00"), status=Order.OrderStatus.PAID) @@ -930,3 +941,70 @@ class NewMemberTests(TestCase): self.assertEqual(sum(month["new"] for month in series), 1) self.assertEqual(sum(month["returning"] for month in series), 0) + + +class ClubHealthTableTests(TestCase): + def setUp(self): + self.today = timezone.localdate() + self.club = Club.objects.create(name="Ajax United") + self.season = Season.objects.create(club=self.club, start_date=self.today - datetime.timedelta(days=30), end_date=self.today + datetime.timedelta(days=300)) + self.member = Member.objects.create(first_name="Ada", last_name="Lovelace") + + def health(self): + return clubs_with_health().get(pk=self.club.pk) + + def test_the_whole_table_costs_one_query(self): + Club.objects.create(name="Feyenoord") + + with self.assertNumQueries(1): + [(club.active_members, club.outstanding, club.teams_without_coach) for club in clubs_with_health()] + + def test_money_is_not_inflated_by_other_joins(self): + # The reason each aggregate is its own subquery: a Sum and a Count spanning different + # joins multiply each other's rows, and the club's debt comes back doubled for every + # membership it happens to have. + for name in ("Bob", "Carol", "Dave"): + member = Member.objects.create(first_name=name, last_name="Bobson") + ClubMembership.objects.create(club=self.club, season=self.season, member=member, status=ClubMembership.StatusChoices.ACTIVE) + Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("100.00"), status=Order.OrderStatus.PENDING) + + health = self.health() + + self.assertEqual(health.outstanding, Decimal("100.00")) # not 300.00 + self.assertEqual(health.active_members, 3) + + def test_a_club_reports_its_missing_coaches(self): + Team.objects.create(club=self.club, name="U15") + managed = Team.objects.create(club=self.club, name="U17") + coach = Position.objects.create(club=self.club, name="Coach", staff_position=True, management_position=True) + StaffAssignment.objects.create(team=managed, member=self.member, season=self.season, position=coach) + + health = self.health() + + self.assertEqual(health.team_count, 2) + self.assertEqual(health.teams_without_coach, 1) + + def test_a_club_with_no_season_and_no_events_is_marked(self): + Season.objects.all().delete() + + health = self.health() + + self.assertFalse(health.has_season) + self.assertEqual(health.upcoming_events, 0) + + def test_upcoming_events_only_count_the_next_thirty_days(self): + Event.objects.create(club=self.club, title="Soon", start=timezone.now() + datetime.timedelta(days=3)) + Event.objects.create(club=self.club, title="Far", start=timezone.now() + datetime.timedelta(days=90)) + + self.assertEqual(self.health().upcoming_events, 1) + + def test_the_dashboard_table_shows_health_not_vanity(self): + user = User.objects.create_user(email="root@example.com", password="pw-secret-123", is_staff=True) + enrol_mfa(user) + self.client.force_login(user) + + response = self.client.get(reverse("controlpanel:dashboard")) + + self.assertContains(response, "Owed") + self.assertContains(response, "Upcoming") + self.assertContains(response, "Unpaid") diff --git a/controlpanel/views.py b/controlpanel/views.py index 93d87b1..4547c3b 100644 --- a/controlpanel/views.py +++ b/controlpanel/views.py @@ -17,7 +17,7 @@ from .services.platform_admins import ( revoke_platform_access, set_platform_access, ) -from .services.statistics import club_attention, club_charts, club_statistics, clubs_with_totals, flag_adoption, onboarding_funnel, platform_attention, platform_charts, platform_totals +from .services.statistics import club_attention, club_charts, club_statistics, clubs_with_health, clubs_with_totals, flag_adoption, onboarding_funnel, platform_attention, platform_charts, platform_totals Flag = get_waffle_flag_model() Switch = get_waffle_switch_model() @@ -34,7 +34,7 @@ class DashboardView(PlatformStaffRequiredMixin, TemplateView): funnel=onboarding_funnel(), flags=flag_adoption(), charts=platform_charts(), - clubs=clubs_with_totals(Club.objects.active()), + clubs=clubs_with_health(), **kwargs, ) diff --git a/static/css/app.css b/static/css/app.css index 080d6b1..6c628d3 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -2945,6 +2945,9 @@ .mb-10 { margin-bottom: calc(var(--spacing) * 10); } + .ml-1 { + margin-left: var(--spacing); + } .ml-2 { margin-left: calc(var(--spacing) * 2); } @@ -3602,6 +3605,12 @@ font-size: 0.75rem; } } + .badge-xs { + @layer daisyui.l1.l2 { + --size: calc(var(--size-selector, 0.25rem) * 4); + font-size: 0.625rem; + } + } .otp-lg { @layer daisyui.l1.l2 { font-size: 2rem; From 3a6dc00e05b70c65ab42098a1047f5b60bd636cb Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 01:24:19 +0200 Subject: [PATCH 061/137] Guard against multi-line {# #} template comments Django's {# #} is single-line only -- its lexer regex is not DOTALL -- so a multi-line one is not a comment at all and renders to the page as text. It shipped into the clubs list, where the archived row read: Probe Retired probe-retired {# An archived club's subdomain does not... #} A test now walks every template and fails on a {# without a closing #} on the same line, since this is an easy habit to fall back into and the failure is invisible until someone looks at the rendered page. Co-Authored-By: Claude Opus 4.8 --- .../controlpanel/_club_health_table.html | 62 +++++++++++++++++++ controlpanel/tests.py | 47 ++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 controlpanel/templates/controlpanel/_club_health_table.html diff --git a/controlpanel/templates/controlpanel/_club_health_table.html b/controlpanel/templates/controlpanel/_club_health_table.html new file mode 100644 index 0000000..b8795bd --- /dev/null +++ b/controlpanel/templates/controlpanel/_club_health_table.html @@ -0,0 +1,62 @@ +{% load lucide %} + +{% comment %} + The club table, shared by the dashboard and the clubs list so the two cannot drift apart. + Health, not vanity: a member total says nothing you can act on, while "no coach", + "nothing scheduled", "€ owed" and "no admins" each name something somebody has to go and + fix. Every column is annotated by clubs_with_health() in a single query. + + Expects: clubs (from clubs_with_health), and optionally empty_message. +{% endcomment %} +
+
ClubMembersTeamsEventsAdminsMembersUnpaidOwedTeamsUpcomingAdmins
{{ club.name }} -
{{ club.slug }}
+
+ {{ club.slug }} + {% if not club.has_season %}{% lucide "calendar-x" size=10 %} No season{% endif %} + {% if not club.upcoming_events %}{% lucide "moon-star" size=10 %} Dormant{% endif %} +
{{ club.member_count }}{{ club.team_count }}{{ club.event_count }}{{ club.admin_count }}{{ club.active_members }}{{ club.unpaid_members }}€{{ club.outstanding|floatformat:2 }} + {{ club.team_count }} + {% if club.teams_without_coach %} + {{ club.teams_without_coach }} no coach + {% endif %} + {{ club.upcoming_events }}{{ club.admin_count }}
No clubs yet.No clubs yet.
+ + + + + + + + + + + + + {% for club in clubs %} + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
ClubMembersUnpaidOwedTeamsUpcomingAdmins
+ {{ club.name }} +
+ {{ club.slug }} + {% if club.is_archived %} + {% comment %} + An archived club's subdomain does not resolve, so "dormant" and + "no season" would be noise: of course nothing is scheduled. + {% endcomment %} + {% lucide "archive" size=10 %} Archived + {% else %} + {% if not club.has_season %}{% lucide "calendar-x" size=10 %} No season{% endif %} + {% if not club.upcoming_events %}{% lucide "moon-star" size=10 %} Dormant{% endif %} + {% endif %} +
+
{{ club.active_members }}{{ club.unpaid_members }}€{{ club.outstanding|floatformat:2 }} + {{ club.team_count }} + {% if club.teams_without_coach %} + {{ club.teams_without_coach }} no coach + {% endif %} + {{ club.upcoming_events }}{{ club.admin_count }}
{{ empty_message|default:"No clubs yet." }}
+
diff --git a/controlpanel/tests.py b/controlpanel/tests.py index b52ba57..c40a4df 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -1,8 +1,10 @@ import datetime +import pathlib from decimal import Decimal from allauth.mfa.models import Authenticator from django import forms +from django.conf import settings from django.contrib import messages from django.contrib.auth import get_user_model from django.contrib.messages.storage.base import Message @@ -1008,3 +1010,48 @@ class ClubHealthTableTests(TestCase): self.assertContains(response, "Owed") self.assertContains(response, "Upcoming") self.assertContains(response, "Unpaid") + + +class ClubListHealthTests(ControlPanelTestBase): + def test_the_list_shows_the_same_health_columns_as_the_dashboard(self): + response = self.client.get(reverse("controlpanel:club_list")) + + self.assertContains(response, "Owed") + self.assertContains(response, "Upcoming") + self.assertContains(response, "Unpaid") + self.assertTemplateUsed(response, "controlpanel/_club_health_table.html") + + def test_an_archived_club_is_badged_archived_rather_than_dormant(self): + # Its subdomain does not resolve, so "nothing scheduled" is not news. + self.club.archive() + + response = self.client.get(reverse("controlpanel:club_list"), {"archived": "1"}) + + self.assertContains(response, "Archived") + self.assertNotContains(response, "Dormant") + + def test_searching_keeps_the_health_annotations(self): + response = self.client.get(reverse("controlpanel:club_list"), {"q": "Ajax"}) + + club = response.context["clubs"][0] + + self.assertEqual(club.active_members, 0) + self.assertEqual(club.teams_without_coach, 0) + + def test_the_list_does_not_fan_out_per_club(self): + for name in ("Feyenoord", "PSV", "Twente"): + Club.objects.create(name=name) + + with self.assertNumQueries(1): + [(club.outstanding, club.upcoming_events) for club in clubs_with_health()] + + +class TemplateCommentTests(TestCase): + def test_no_template_uses_a_multiline_hash_comment(self): + """Django's {# #} is single-line only — its lexer regex is not DOTALL, so a + multi-line one is not a comment at all: it renders to the page as text.""" + templates = [path for path in pathlib.Path(settings.BASE_DIR).glob("**/templates/**/*.html") if ".venv" not in path.parts and "node_modules" not in path.parts] + offenders = [f"{path.relative_to(settings.BASE_DIR)}:{number}" for path in templates for number, line in enumerate(path.read_text().splitlines(), start=1) if "{#" in line and "#}" not in line] + + self.assertTrue(templates) # the glob must actually be finding our templates + self.assertEqual(offenders, [], "use {% comment %} for multi-line comments") From 6899e203f664a872586db2ff49fe517816c5e887 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 01:24:31 +0200 Subject: [PATCH 062/137] Show club health on the clubs list too The list page carried the same vanity counts the dashboard just lost. It now shows active members, unpaid members, money owed, teams (flagging those nobody can pick a squad for), upcoming events and admins, with the same No season / Dormant badges -- and the annotations survive search and the archived filter. The table lives in one partial, included by both pages, so they cannot drift apart. Archived clubs are badged "Archived" rather than "Dormant": their subdomain does not resolve, so of course nothing is scheduled, and flagging that as a problem would be noise on the one page where every row has it. assertNumQueries(1) covers the list as well now -- health is annotated per club, so the page must not fan out as clubs are added. Co-Authored-By: Claude Opus 4.8 --- .../templates/controlpanel/club_list.html | 38 ++------------ .../templates/controlpanel/dashboard.html | 50 +------------------ controlpanel/views.py | 4 +- 3 files changed, 8 insertions(+), 84 deletions(-) diff --git a/controlpanel/templates/controlpanel/club_list.html b/controlpanel/templates/controlpanel/club_list.html index 5d4f740..6427ce6 100644 --- a/controlpanel/templates/controlpanel/club_list.html +++ b/controlpanel/templates/controlpanel/club_list.html @@ -24,39 +24,11 @@
-
- - - - - - - - - - - - {% for club in clubs %} - - - - - - - - {% empty %} - - - - {% endfor %} - -
ClubMembersTeamsEventsAdmins
- {{ club.name }} -
{{ club.slug }}
-
{{ club.member_count }}{{ club.team_count }}{{ club.event_count }}{{ club.admin_count }}
- {% if show_archived %}No archived clubs.{% else %}No clubs yet.{% endif %} -
-
+ {% if show_archived %} + {% include "controlpanel/_club_health_table.html" with empty_message="No archived clubs." %} + {% else %} + {% include "controlpanel/_club_health_table.html" %} + {% endif %}
{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/dashboard.html b/controlpanel/templates/controlpanel/dashboard.html index ff02672..b553d4f 100644 --- a/controlpanel/templates/controlpanel/dashboard.html +++ b/controlpanel/templates/controlpanel/dashboard.html @@ -143,55 +143,7 @@

Clubs

- {% comment %} - Health, not vanity: a club's member count says nothing you can act on, while - "no coach", "nothing scheduled" and "€ owed" each name a thing somebody has to - go and fix. Every column here is annotated in the same single query. - {% endcomment %} -
- - - - - - - - - - - - - - {% for club in clubs %} - - - - - - - - - - {% empty %} - - - - {% endfor %} - -
ClubMembersUnpaidOwedTeamsUpcomingAdmins
- {{ club.name }} -
- {{ club.slug }} - {% if not club.has_season %}{% lucide "calendar-x" size=10 %} No season{% endif %} - {% if not club.upcoming_events %}{% lucide "moon-star" size=10 %} Dormant{% endif %} -
-
{{ club.active_members }}{{ club.unpaid_members }}€{{ club.outstanding|floatformat:2 }} - {{ club.team_count }} - {% if club.teams_without_coach %} - {{ club.teams_without_coach }} no coach - {% endif %} - {{ club.upcoming_events }}{{ club.admin_count }}
No clubs yet.
-
+ {% include "controlpanel/_club_health_table.html" %}
{% endblock panel %} diff --git a/controlpanel/views.py b/controlpanel/views.py index 4547c3b..eebf84c 100644 --- a/controlpanel/views.py +++ b/controlpanel/views.py @@ -17,7 +17,7 @@ from .services.platform_admins import ( revoke_platform_access, set_platform_access, ) -from .services.statistics import club_attention, club_charts, club_statistics, clubs_with_health, clubs_with_totals, flag_adoption, onboarding_funnel, platform_attention, platform_charts, platform_totals +from .services.statistics import club_attention, club_charts, club_statistics, clubs_with_health, flag_adoption, onboarding_funnel, platform_attention, platform_charts, platform_totals Flag = get_waffle_flag_model() Switch = get_waffle_switch_model() @@ -52,7 +52,7 @@ class ClubListView(PlatformStaffRequiredMixin, ListView): search = self.request.GET.get("q", "").strip() if search: clubs = clubs.filter(name__icontains=search) - return clubs_with_totals(clubs) + return clubs_with_health(clubs) def get_context_data(self, **kwargs): return super().get_context_data(nav="clubs", show_archived=self.show_archived, search=self.request.GET.get("q", ""), **kwargs) From 60bfac98813f9fb5ba8fd754c9b8a535bf99608a Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 01:45:15 +0200 Subject: [PATCH 063/137] Add platform billing: tiers, dues, payments and invoices RosterChief charging the clubs, which is a different domain from `shop` (a club charging its members). Nothing here is club-scoped: these rows reference a Club, they are not owned by one, and no club user ever sees them. - Tier + TierPrice. Prices are dated, not keyed by year: a rate change is one row with a future active_from, and price_on(day) answers "what was in force then". A tier with no price yet returns None, which callers must treat as "cannot bill" -- never as free. - Due: one rolling-year period per club, with a 45-day grace tail. The tier and the amount are SNAPSHOTS taken when the period opens. Raise the price and last year's period must still say what was actually charged; reading it back through the tier would silently rewrite financial history. - DuePayment: partial payments accumulate. amount_paid is re-summed from the payments on every change, never incremented -- an increment drifts the moment a payment is deleted, and the drift still looks like money. - Invoice: PDF via WeasyPrint, rendered on demand from the frozen snapshot. Only the number is stored, in one platform-wide series (unlike the shop's per-club order numbers), and re-issuing returns the existing one rather than burning a number -- a gap in an invoice series is a question you don't want to answer. WeasyPrint is imported lazily: it binds to native pango/cairo, and the app, the tests and every other page must still run on a machine without them. - archive_overdue_clubs reports by default and archives only with --commit. That asymmetry is deliberate: this switches off paying customers, so a bad clock or a cron misconfiguration should cost an email, not a morning of angry clubs. A club with auto_archive off is spared entirely. Renewal continues from the last period end, not from the payment date: a club that pays two months late has still used those two months. Co-Authored-By: Claude Opus 4.8 --- billing/__init__.py | 0 billing/admin.py | 53 +++ billing/apps.py | 7 + billing/management/__init__.py | 0 billing/management/commands/__init__.py | 0 .../commands/archive_overdue_clubs.py | 38 ++ billing/migrations/0001_initial.py | 138 +++++++ billing/migrations/__init__.py | 0 billing/models.py | 247 +++++++++++++ billing/services/__init__.py | 6 + billing/services/dues.py | 153 ++++++++ billing/services/invoices.py | 40 +++ billing/templates/billing/invoice.html | 106 ++++++ billing/tests.py | 337 ++++++++++++++++++ pyproject.toml | 4 +- rosterchief/settings.py | 2 + static/css/app.css | 3 + uv.lock | 172 +++++++++ 18 files changed, 1305 insertions(+), 1 deletion(-) create mode 100644 billing/__init__.py create mode 100644 billing/admin.py create mode 100644 billing/apps.py create mode 100644 billing/management/__init__.py create mode 100644 billing/management/commands/__init__.py create mode 100644 billing/management/commands/archive_overdue_clubs.py create mode 100644 billing/migrations/0001_initial.py create mode 100644 billing/migrations/__init__.py create mode 100644 billing/models.py create mode 100644 billing/services/__init__.py create mode 100644 billing/services/dues.py create mode 100644 billing/services/invoices.py create mode 100644 billing/templates/billing/invoice.html create mode 100644 billing/tests.py diff --git a/billing/__init__.py b/billing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/billing/admin.py b/billing/admin.py new file mode 100644 index 0000000..a72326f --- /dev/null +++ b/billing/admin.py @@ -0,0 +1,53 @@ +from django.contrib import admin + +from .models import Due, DuePayment, Subscription, Tier, TierPrice + + +class TierPriceInline(admin.TabularInline): + model = TierPrice + extra = 0 + + +@admin.register(Tier) +class TierAdmin(admin.ModelAdmin): + list_display = ["name", "is_active"] + list_filter = ["is_active"] + search_fields = ["name"] + prepopulated_fields = {"slug": ["name"]} + inlines = [TierPriceInline] + + +@admin.register(TierPrice) +class TierPriceAdmin(admin.ModelAdmin): + list_display = ["tier", "amount", "active_from"] + list_filter = ["tier"] + + +@admin.register(Subscription) +class SubscriptionAdmin(admin.ModelAdmin): + list_display = ["club", "tier", "auto_archive"] + list_filter = ["tier", "auto_archive"] + search_fields = ["club__name"] + + +class DuePaymentInline(admin.TabularInline): + model = DuePayment + extra = 0 + readonly_fields = ["recorded_by"] + + +@admin.register(Due) +class DueAdmin(admin.ModelAdmin): + list_display = ["club", "tier", "period_start", "period_end", "amount", "amount_paid", "status"] + list_filter = ["status", "tier"] + search_fields = ["club__name"] + # Money is settled by the billing service, which re-derives these from the payments. + readonly_fields = ["amount_paid", "status", "paid_at"] + inlines = [DuePaymentInline] + + +@admin.register(DuePayment) +class DuePaymentAdmin(admin.ModelAdmin): + list_display = ["due", "amount", "method", "paid_at", "recorded_by"] + list_filter = ["method"] + search_fields = ["due__club__name", "reference"] diff --git a/billing/apps.py b/billing/apps.py new file mode 100644 index 0000000..33b9fa5 --- /dev/null +++ b/billing/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class BillingConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "billing" + verbose_name = "Billing" diff --git a/billing/management/__init__.py b/billing/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/billing/management/commands/__init__.py b/billing/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/billing/management/commands/archive_overdue_clubs.py b/billing/management/commands/archive_overdue_clubs.py new file mode 100644 index 0000000..3c6368b --- /dev/null +++ b/billing/management/commands/archive_overdue_clubs.py @@ -0,0 +1,38 @@ +"""Archive clubs whose billing period has gone unpaid past its grace period. + +Reports by default and only acts with --commit. That asymmetry is the point: this command +switches off paying customers, and a cron misconfiguration, a clock skew or a bad import +should cost you a confusing email, not a morning of angry clubs. +""" + +from django.core.management.base import BaseCommand +from django.utils import timezone + +from billing.services.dues import archivable_clubs + + +class Command(BaseCommand): + help = "Archive clubs that are unpaid past their grace period (dry run unless --commit)." + + def add_arguments(self, parser): + parser.add_argument("--commit", action="store_true", help="Actually archive them. Without this the command only reports.") + + def handle(self, *args, **options): + today = timezone.localdate() + overdue = list(archivable_clubs(today)) + + if not overdue: + self.stdout.write(self.style.SUCCESS("Nothing overdue past grace.")) + return + + for due in overdue: + days = (today - due.grace_until).days + self.stdout.write(f"{due.club} — {due.tier}, {due.balance} owed, grace ended {due.grace_until} ({days} day{'s'[: days != 1]} ago)") + + if not options["commit"]: + self.stdout.write(self.style.WARNING(f"\nDry run: {len(overdue)} club(s) would be archived. Re-run with --commit to do it.")) + return + + for due in overdue: + due.club.archive() + self.stdout.write(self.style.SUCCESS(f"\nArchived {len(overdue)} club(s). Their data is kept; restoring re-opens billing.")) diff --git a/billing/migrations/0001_initial.py b/billing/migrations/0001_initial.py new file mode 100644 index 0000000..71943f4 --- /dev/null +++ b/billing/migrations/0001_initial.py @@ -0,0 +1,138 @@ +# Generated by Django 6.0.6 on 2026-07-13 23:40 + +import django.core.validators +import django.db.models.deletion +import django.utils.timezone +import uuid +from decimal import Decimal +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('club', '0013_club_created_club_modified_clubmembership_created_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Tier', + fields=[ + ('created', models.DateTimeField(auto_now_add=True, verbose_name='created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='modified')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('slug', models.SlugField(blank=True, max_length=255, unique=True, verbose_name='slug')), + ('description', models.TextField(blank=True, verbose_name='description')), + ('is_active', models.BooleanField(default=True, help_text='Inactive tiers keep billing existing subscriptions but cannot be chosen for new ones.', verbose_name='active')), + ], + options={ + 'verbose_name': 'tier', + 'verbose_name_plural': 'tiers', + 'ordering': ['name'], + }, + ), + migrations.CreateModel( + name='Due', + fields=[ + ('created', models.DateTimeField(auto_now_add=True, verbose_name='created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='modified')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('amount', models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='amount')), + ('amount_paid', models.DecimalField(decimal_places=2, default=Decimal('0.00'), help_text='Kept in step with the payments by the billing service.', max_digits=10, verbose_name='amount paid')), + ('period_start', models.DateField(verbose_name='period start')), + ('period_end', models.DateField(blank=True, verbose_name='period end')), + ('grace_until', models.DateField(blank=True, help_text='Past this date an unpaid club is archived.', verbose_name='grace until')), + ('status', models.CharField(choices=[('unpaid', 'unpaid'), ('partial', 'partially paid'), ('paid', 'paid'), ('waived', 'waived'), ('cancelled', 'cancelled')], default='unpaid', max_length=20, verbose_name='status')), + ('paid_at', models.DateTimeField(blank=True, null=True, verbose_name='paid at')), + ('club', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dues', to='club.club', verbose_name='club')), + ('tier', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='dues', to='billing.tier', verbose_name='tier')), + ], + options={ + 'verbose_name': 'due', + 'verbose_name_plural': 'dues', + 'ordering': ['-period_start', 'club__name'], + }, + ), + migrations.CreateModel( + name='DuePayment', + fields=[ + ('created', models.DateTimeField(auto_now_add=True, verbose_name='created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='modified')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('amount', models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))], verbose_name='amount')), + ('method', models.CharField(choices=[('bank_transfer', 'bank transfer'), ('card', 'card'), ('cash', 'cash'), ('other', 'other')], default='bank_transfer', max_length=20, verbose_name='method')), + ('reference', models.CharField(blank=True, help_text='Bank reference, transaction id — whatever lets you find this again.', max_length=255, verbose_name='reference')), + ('paid_at', models.DateTimeField(default=django.utils.timezone.now, verbose_name='paid at')), + ('note', models.TextField(blank=True, verbose_name='note')), + ('due', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='billing.due', verbose_name='due')), + ('recorded_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='recorded_due_payments', to=settings.AUTH_USER_MODEL, verbose_name='recorded by')), + ], + options={ + 'verbose_name': 'due payment', + 'verbose_name_plural': 'due payments', + 'ordering': ['-paid_at'], + }, + ), + migrations.CreateModel( + name='Invoice', + fields=[ + ('created', models.DateTimeField(auto_now_add=True, verbose_name='created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='modified')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('number', models.CharField(blank=True, max_length=32, unique=True, verbose_name='number')), + ('issued_at', models.DateTimeField(default=django.utils.timezone.now, verbose_name='issued at')), + ('due', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='invoice', to='billing.due', verbose_name='due')), + ], + options={ + 'verbose_name': 'invoice', + 'verbose_name_plural': 'invoices', + 'ordering': ['-issued_at'], + }, + ), + migrations.CreateModel( + name='Subscription', + fields=[ + ('created', models.DateTimeField(auto_now_add=True, verbose_name='created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='modified')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('auto_archive', models.BooleanField(default=True, help_text='Archive this club when a period goes unpaid past its grace period.', verbose_name='auto archive')), + ('notes', models.TextField(blank=True, verbose_name='notes')), + ('club', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='subscription', to='club.club', verbose_name='club')), + ('tier', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='subscriptions', to='billing.tier', verbose_name='tier')), + ], + options={ + 'verbose_name': 'subscription', + 'verbose_name_plural': 'subscriptions', + 'ordering': ['club__name'], + }, + ), + migrations.CreateModel( + name='TierPrice', + fields=[ + ('created', models.DateTimeField(auto_now_add=True, verbose_name='created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='modified')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('active_from', models.DateField(help_text='Periods opening on or after this date are billed at this amount.', verbose_name='active from')), + ('amount', models.DecimalField(decimal_places=2, max_digits=10, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='amount')), + ('tier', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prices', to='billing.tier', verbose_name='tier')), + ], + options={ + 'verbose_name': 'tier price', + 'verbose_name_plural': 'tier prices', + 'ordering': ['tier__name', '-active_from'], + }, + ), + migrations.AddConstraint( + model_name='due', + constraint=models.UniqueConstraint(fields=('club', 'period_start'), name='unique_due_per_club_per_period'), + ), + migrations.AddConstraint( + model_name='tierprice', + constraint=models.UniqueConstraint(fields=('tier', 'active_from'), name='unique_tier_price_per_start_date'), + ), + ] diff --git a/billing/migrations/__init__.py b/billing/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/billing/models.py b/billing/models.py new file mode 100644 index 0000000..eacb9b7 --- /dev/null +++ b/billing/models.py @@ -0,0 +1,247 @@ +"""What the platform charges a club. + +Deliberately NOT club-scoped. `shop` is a club charging its members — tenant data, owned by +the club. This is RosterChief charging the club: platform-owned, and no club user ever sees +it. Nothing here inherits ClubScopedModel: these rows reference a Club, they are not owned +by one, and a tenant-scoped manager would be exactly the wrong default. +""" + +from datetime import date, timedelta +from decimal import Decimal + +from django.conf import settings +from django.core.validators import MinValueValidator +from django.db import models +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ + +from rosterchief.base import UUIDModel, unique_slugify + +ZERO = Decimal("0.00") + +#: A club stays live for six weeks past the end of an unpaid period before it is archived. +GRACE_DAYS = 45 + + +def add_one_year(day: date) -> date: + """The day one year on. 29 February has no counterpart in a common year, so it falls + back to the 28th rather than raising.""" + try: + return day.replace(year=day.year + 1) + except ValueError: + return day.replace(year=day.year + 1, day=28) + + +class Tier(UUIDModel): + """A price band. The price itself lives in TierPrice, which is dated.""" + + name = models.CharField(_("name"), max_length=255) + slug = models.SlugField(_("slug"), max_length=255, unique=True, blank=True) + description = models.TextField(_("description"), blank=True) + is_active = models.BooleanField(_("active"), default=True, help_text=_("Inactive tiers keep billing existing subscriptions but cannot be chosen for new ones.")) + + class Meta: + verbose_name = _("tier") + verbose_name_plural = _("tiers") + ordering = ["name"] + + def __str__(self): + return self.name + + def save(self, *args, **kwargs): + if not self.slug: + self.slug = unique_slugify(self, self.name) + super().save(*args, **kwargs) + + def price_on(self, day: date | None = None) -> Decimal | None: + """The price in force on ``day`` — the latest one that had started by then. + + None means the tier had no price yet on that date. Callers must treat that as + "cannot bill", never as free. + """ + day = day or timezone.localdate() + price = self.prices.filter(active_from__lte=day).order_by("-active_from").first() + + return price.amount if price else None + + +class TierPrice(UUIDModel): + """A dated price for a tier. + + Dated rather than keyed by year: a rate change is one new row with a future + ``active_from``, and every period already opened keeps the amount it was billed at. + """ + + tier = models.ForeignKey(Tier, on_delete=models.CASCADE, related_name="prices", verbose_name=_("tier")) + active_from = models.DateField(_("active from"), help_text=_("Periods opening on or after this date are billed at this amount.")) + amount = models.DecimalField(_("amount"), max_digits=10, decimal_places=2, validators=[MinValueValidator(ZERO)]) + + class Meta: + verbose_name = _("tier price") + verbose_name_plural = _("tier prices") + ordering = ["tier__name", "-active_from"] + constraints = [ + models.UniqueConstraint(fields=["tier", "active_from"], name="unique_tier_price_per_start_date"), + ] + + def __str__(self): + return f"{self.tier} — {self.amount} from {self.active_from}" + + +class Subscription(UUIDModel): + """A club's current plan. The periods it is billed for are Dues.""" + + club = models.OneToOneField("club.Club", on_delete=models.CASCADE, related_name="subscription", verbose_name=_("club")) + tier = models.ForeignKey(Tier, on_delete=models.PROTECT, related_name="subscriptions", verbose_name=_("tier")) + auto_archive = models.BooleanField(_("auto archive"), default=True, help_text=_("Archive this club when a period goes unpaid past its grace period.")) + notes = models.TextField(_("notes"), blank=True) + + class Meta: + verbose_name = _("subscription") + verbose_name_plural = _("subscriptions") + ordering = ["club__name"] + + def __str__(self): + return f"{self.club} — {self.tier}" + + +class Due(UUIDModel): + """One billing period for one club. + + ``tier`` and ``amount`` are snapshots taken when the period opens, never read back + through the tier at display time: raise the price and last year's period must still say + what was actually charged. A live lookup would rewrite financial history. + """ + + class Status(models.TextChoices): + UNPAID = "unpaid", _("unpaid") + PARTIAL = "partial", _("partially paid") + PAID = "paid", _("paid") + WAIVED = "waived", _("waived") + CANCELLED = "cancelled", _("cancelled") + + #: Statuses that still owe money. + OWING = (Status.UNPAID, Status.PARTIAL) + + club = models.ForeignKey("club.Club", on_delete=models.CASCADE, related_name="dues", verbose_name=_("club")) + tier = models.ForeignKey(Tier, on_delete=models.PROTECT, related_name="dues", verbose_name=_("tier")) + + amount = models.DecimalField(_("amount"), max_digits=10, decimal_places=2, validators=[MinValueValidator(ZERO)]) + amount_paid = models.DecimalField(_("amount paid"), max_digits=10, decimal_places=2, default=ZERO, help_text=_("Kept in step with the payments by the billing service.")) + + period_start = models.DateField(_("period start")) + period_end = models.DateField(_("period end"), blank=True) + grace_until = models.DateField(_("grace until"), blank=True, help_text=_("Past this date an unpaid club is archived.")) + + status = models.CharField(_("status"), max_length=20, choices=Status.choices, default=Status.UNPAID) + paid_at = models.DateTimeField(_("paid at"), null=True, blank=True) + + class Meta: + verbose_name = _("due") + verbose_name_plural = _("dues") + ordering = ["-period_start", "club__name"] + constraints = [ + models.UniqueConstraint(fields=["club", "period_start"], name="unique_due_per_club_per_period"), + ] + + def __str__(self): + return f"{self.club} — {self.period_start} to {self.period_end}" + + def save(self, *args, **kwargs): + # A period runs a rolling year from its start and the grace hangs off its end. + # Derived here so no caller can open a period without them. + if not self.period_end: + self.period_end = add_one_year(self.period_start) - timedelta(days=1) + if not self.grace_until: + self.grace_until = self.period_end + timedelta(days=GRACE_DAYS) + super().save(*args, **kwargs) + + @property + def balance(self) -> Decimal: + return self.amount - self.amount_paid + + @property + def is_owing(self) -> bool: + return self.status in self.OWING + + def is_in_grace(self, today: date | None = None) -> bool: + """The period has ended unpaid, but the club is not archivable yet.""" + today = today or timezone.localdate() + + return self.is_owing and self.period_end < today <= self.grace_until + + def is_overdue(self, today: date | None = None) -> bool: + """Unpaid past grace — this is what makes a club archivable.""" + today = today or timezone.localdate() + + return self.is_owing and self.grace_until < today + + +class DuePayment(UUIDModel): + """Money received against a due. + + Several may land on one due: a club that pays in two transfers must not read as unpaid, + and the half that did arrive has to be recorded somewhere. + """ + + class Method(models.TextChoices): + BANK_TRANSFER = "bank_transfer", _("bank transfer") + CARD = "card", _("card") + CASH = "cash", _("cash") + OTHER = "other", _("other") + + due = models.ForeignKey(Due, on_delete=models.CASCADE, related_name="payments", verbose_name=_("due")) + amount = models.DecimalField(_("amount"), max_digits=10, decimal_places=2, validators=[MinValueValidator(Decimal("0.01"))]) + method = models.CharField(_("method"), max_length=20, choices=Method.choices, default=Method.BANK_TRANSFER) + reference = models.CharField(_("reference"), max_length=255, blank=True, help_text=_("Bank reference, transaction id — whatever lets you find this again.")) + paid_at = models.DateTimeField(_("paid at"), default=timezone.now) + note = models.TextField(_("note"), blank=True) + recorded_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="recorded_due_payments", verbose_name=_("recorded by")) + + class Meta: + verbose_name = _("due payment") + verbose_name_plural = _("due payments") + ordering = ["-paid_at"] + + def __str__(self): + return f"{self.amount} — {self.due}" + + +class Invoice(UUIDModel): + """The bill for one period. + + Only the number and the issue date are stored: the money, the tier and the dates are + already frozen on the Due, so the PDF is rendered from those snapshots on demand. The + number, though, must be stable and gapless — it is the thing an accountant reconciles + against, so it is allocated once and never recomputed. + """ + + due = models.OneToOneField(Due, on_delete=models.CASCADE, related_name="invoice", verbose_name=_("due")) + number = models.CharField(_("number"), max_length=32, unique=True, blank=True) + issued_at = models.DateTimeField(_("issued at"), default=timezone.now) + + class Meta: + verbose_name = _("invoice") + verbose_name_plural = _("invoices") + ordering = ["-issued_at"] + + def __str__(self): + return self.number + + def save(self, *args, **kwargs): + if not self.number: + self.number = self.next_number(self.issued_at.year) + super().save(*args, **kwargs) + + @classmethod + def next_number(cls, year: int) -> str: + """INV-2026-00001, restarting each year. + + Platform-wide, unlike the shop's order numbers, which are per club: these are OUR + invoices, and one sequence has to cover every club we bill. + """ + prefix = f"INV-{year}-" + last = cls.objects.filter(number__startswith=prefix).order_by("-number").first() + sequence = int(last.number.removeprefix(prefix)) + 1 if last else 1 + + return f"{prefix}{sequence:05d}" diff --git a/billing/services/__init__.py b/billing/services/__init__.py new file mode 100644 index 0000000..b96f3b2 --- /dev/null +++ b/billing/services/__init__.py @@ -0,0 +1,6 @@ +class BillingError(Exception): + """A billing action that must not silently half-happen. + + Lives here rather than in dues.py so invoices.py can raise it without the two modules + importing each other in a circle. + """ diff --git a/billing/services/dues.py b/billing/services/dues.py new file mode 100644 index 0000000..13cb9e7 --- /dev/null +++ b/billing/services/dues.py @@ -0,0 +1,153 @@ +"""The billing lifecycle. Views and the archive command go through here, never through the +models directly — a Due whose amount_paid disagrees with its payments is a wrong invoice. +""" + +from datetime import date, timedelta +from decimal import Decimal + +from django.db import transaction +from django.db.models import Sum +from django.utils import timezone + +from billing.models import ZERO, Due, DuePayment, Subscription, Tier +from billing.services import BillingError +from billing.services.invoices import issue_invoice + + +def subscribe(club, tier: Tier, *, start: date | None = None, auto_archive: bool = True) -> Subscription: + """Put a club on a tier and open its first period.""" + subscription, _created = Subscription.objects.update_or_create(club=club, defaults={"tier": tier, "auto_archive": auto_archive}) + open_period(club, start=start) + + return subscription + + +def next_period_start(club, today: date | None = None) -> date: + """Where the club's next period begins. + + The day after the last one ended — not today. A club that pays two months late has still + used those two months, and restarting the clock at the payment date would quietly gift + them away. Callers can override; that is what the start field on the renew form is for. + """ + today = today or timezone.localdate() + last = club.dues.exclude(status=Due.Status.CANCELLED).order_by("-period_end").first() + + return last.period_end + timedelta(days=1) if last else today + + +@transaction.atomic +def open_period(club, *, start: date | None = None, tier: Tier | None = None) -> Due: + """Issue the next due for a club, snapshotting the tier and the price of the day.""" + subscription = getattr(club, "subscription", None) + tier = tier or (subscription.tier if subscription else None) + if tier is None: + raise BillingError(f"{club} has no tier: put it on a subscription before billing it.") + + start = start or next_period_start(club) + + amount = tier.price_on(start) + if amount is None: + raise BillingError(f"{tier} has no price in force on {start:%d %b %Y}. Add one before opening the period.") + + if club.dues.filter(period_start=start).exists(): + raise BillingError(f"{club} is already billed for a period starting {start:%d %b %Y}.") + + due = Due.objects.create(club=club, tier=tier, amount=amount, period_start=start) + issue_invoice(due) # every period is billable the moment it opens + + return due + + +@transaction.atomic +def record_payment(due: Due, amount: Decimal, *, method=DuePayment.Method.BANK_TRANSFER, reference: str = "", paid_at=None, note: str = "", user=None) -> DuePayment: + """Log money against a due and re-derive its status from the payments.""" + if due.status in (Due.Status.WAIVED, Due.Status.CANCELLED): + raise BillingError(f"This period is {due.get_status_display()}; it cannot take a payment.") + if amount <= ZERO: + raise BillingError("A payment must be for a positive amount.") + + payment = DuePayment.objects.create(due=due, amount=amount, method=method, reference=reference, paid_at=paid_at or timezone.now(), note=note, recorded_by=user) + _resettle(due) + + return payment + + +@transaction.atomic +def remove_payment(payment: DuePayment) -> None: + """Undo a mis-keyed payment, then re-derive the due from what is left.""" + due = payment.due + payment.delete() + _resettle(due) + + +def _resettle(due: Due) -> None: + """Recompute amount_paid and status from the payments on record. + + Summed from the payments rather than incremented: an increment drifts the moment a + payment is edited or deleted, and the drift is invisible — the number still looks like + money. + """ + paid = due.payments.aggregate(total=Sum("amount"))["total"] or ZERO + + due.amount_paid = paid + if paid >= due.amount: + due.status = Due.Status.PAID + due.paid_at = due.payments.order_by("-paid_at").first().paid_at + elif paid > ZERO: + due.status = Due.Status.PARTIAL + due.paid_at = None + else: + due.status = Due.Status.UNPAID + due.paid_at = None + due.save(update_fields=["amount_paid", "status", "paid_at", "modified"]) + + +@transaction.atomic +def waive(due: Due, *, note: str = "") -> Due: + """Write a period off. It stops owing, and stops counting towards archiving.""" + if due.payments.exists(): + raise BillingError("This period has payments against it; remove them before waiving it.") + + due.status = Due.Status.WAIVED + due.save(update_fields=["status", "modified"]) + + return due + + +def owing_dues(today: date | None = None): + return Due.objects.filter(status__in=Due.OWING) + + +def dues_in_grace(today: date | None = None): + """Period over, unpaid, not yet archivable.""" + today = today or timezone.localdate() + + return owing_dues().filter(period_end__lt=today, grace_until__gte=today) + + +def dues_overdue(today: date | None = None): + """Past grace: these are the clubs the archive command would take down.""" + today = today or timezone.localdate() + + return owing_dues().filter(grace_until__lt=today) + + +def archivable_clubs(today: date | None = None): + """Clubs the archive command would act on: overdue, still live, and opted in. + + A club with auto_archive off is deliberately spared — that flag is how you keep a club + you are negotiating with from being switched off overnight. + """ + return dues_overdue(today).filter(club__archived_at__isnull=True, club__subscription__auto_archive=True).select_related("club", "tier").order_by("club__name") + + +@transaction.atomic +def reactivate(club, *, start: date | None = None) -> Due: + """Bring an archived club back and bill it again. + + The new period defaults to continuing from the last one, so a lapsed year is still owed. + Pass ``start`` to forgive the gap and begin today instead. + """ + club.restore() + + return open_period(club, start=start) diff --git a/billing/services/invoices.py b/billing/services/invoices.py new file mode 100644 index 0000000..18cde7a --- /dev/null +++ b/billing/services/invoices.py @@ -0,0 +1,40 @@ +"""Invoice PDFs. + +The PDF is rendered on demand from the Due's frozen snapshot (tier, amount, dates), so it +carries no state of its own beyond the number. Only the number is stored — an accountant +reconciles against it, so it is allocated once, never recomputed. +""" + +from django.template.loader import render_to_string + +from billing.models import Due, Invoice +from billing.services import BillingError + + +def issue_invoice(due: Due) -> Invoice: + """One invoice per due, allocated once. Re-issuing returns the existing one rather than + burning a number — a gap in an invoice series is a question you do not want to answer.""" + invoice, _created = Invoice.objects.get_or_create(due=due) + + return invoice + + +def render_pdf(html: str) -> bytes: + """HTML to PDF. + + WeasyPrint is imported here, not at module scope: it binds to native pango/cairo + libraries, and a machine without them must still be able to run the app, the tests and + every other page — it should only fail when someone actually asks for a PDF, and say why. + """ + try: + from weasyprint import HTML + except (ImportError, OSError) as error: + raise BillingError("PDF rendering needs the native pango/cairo libraries (on macOS: brew install pango).") from error + + return HTML(string=html).write_pdf() + + +def invoice_pdf(invoice: Invoice, base_url: str | None = None) -> bytes: + html = render_to_string("billing/invoice.html", {"invoice": invoice, "due": invoice.due, "club": invoice.due.club, "payments": invoice.due.payments.all()}) + + return render_pdf(html) diff --git a/billing/templates/billing/invoice.html b/billing/templates/billing/invoice.html new file mode 100644 index 0000000..e4f98fe --- /dev/null +++ b/billing/templates/billing/invoice.html @@ -0,0 +1,106 @@ +{% load static %} + +{% comment %} + Rendered by WeasyPrint, not by a browser: this is a standalone document with its own + print stylesheet. It deliberately does NOT pull in app.css — daisyUI is built for a + screen, and half of it (dark theme, flex layouts) means nothing on paper. +{% endcomment %} + + + + + {{ invoice.number }} + + + +
+
+

RosterChief

+
Club & team management
+
+
+

Invoice

+
{{ invoice.number }}
+
Issued {{ invoice.issued_at|date:"j F Y" }}
+
+
+ +
+
+

Billed to

+
{{ club.name }}
+
{{ club.slug }}.rosterchief.app
+
+
+

Period

+
{{ due.period_start|date:"j F Y" }} — {{ due.period_end|date:"j F Y" }}
+
Payable by {{ due.grace_until|date:"j F Y" }}
+
+
+ + + + + + + + + + + + + + {% for payment in payments %} + + + + + {% endfor %} + + + + + +
DescriptionAmount
+ {{ due.tier.name }} — platform subscription +
{{ due.period_start|date:"j M Y" }} to {{ due.period_end|date:"j M Y" }}
+
€{{ due.amount|floatformat:2 }}
+ Payment received {{ payment.paid_at|date:"j M Y" }} ({{ payment.get_method_display }}{% if payment.reference %}, {{ payment.reference }}{% endif %}) + −€{{ payment.amount|floatformat:2 }}
Balance due€{{ due.balance|floatformat:2 }}
+ + {% if due.status == "paid" %} + + {% elif due.status == "waived" %} +

Waived. Nothing is owed for this period.

+ {% else %} +

+ Payable by {{ due.grace_until|date:"j F Y" }}. Unpaid past that date the club is archived: its + subdomain stops resolving, though nothing is deleted. +

+ {% endif %} + + diff --git a/billing/tests.py b/billing/tests.py new file mode 100644 index 0000000..5808b26 --- /dev/null +++ b/billing/tests.py @@ -0,0 +1,337 @@ +import datetime +import sys +from decimal import Decimal +from io import StringIO +from unittest import mock + +from django.core.management import call_command +from django.test import TestCase +from django.utils import timezone + +from club.models import Club + +from .models import GRACE_DAYS, Due, Invoice, Subscription, Tier, TierPrice, add_one_year +from .services import BillingError +from .services.dues import archivable_clubs, dues_in_grace, dues_overdue, next_period_start, open_period, reactivate, record_payment, remove_payment, subscribe, waive +from .services.invoices import invoice_pdf, issue_invoice, render_pdf + + +class BillingTestBase(TestCase): + def setUp(self): + self.today = timezone.localdate() + self.club = Club.objects.create(name="Ajax United") + self.tier = Tier.objects.create(name="Standard") + # Priced well back, so a backdated (lapsed) period still has a price in force — + # opening one before any price existed is refused, and rightly so. + TierPrice.objects.create(tier=self.tier, active_from=self.today - datetime.timedelta(days=1200), amount=Decimal("500.00")) + + def bill(self, start=None, club=None): + return open_period(club or self.club, start=start, tier=self.tier) + + +class TierPriceTests(BillingTestBase): + def test_the_price_in_force_is_the_latest_one_that_has_started(self): + TierPrice.objects.create(tier=self.tier, active_from=self.today, amount=Decimal("600.00")) + + self.assertEqual(self.tier.price_on(self.today - datetime.timedelta(days=1)), Decimal("500.00")) + self.assertEqual(self.tier.price_on(self.today), Decimal("600.00")) + + def test_a_future_price_does_not_apply_yet(self): + TierPrice.objects.create(tier=self.tier, active_from=self.today + datetime.timedelta(days=30), amount=Decimal("600.00")) + + self.assertEqual(self.tier.price_on(self.today), Decimal("500.00")) + + def test_a_tier_with_no_price_yet_cannot_be_billed(self): + # None must never be read as free. + empty = Tier.objects.create(name="Enterprise") + + self.assertIsNone(empty.price_on(self.today)) + + with self.assertRaises(BillingError): + open_period(self.club, tier=empty) + + +class PeriodTests(BillingTestBase): + def test_a_period_runs_a_rolling_year_with_a_grace_tail(self): + due = self.bill(start=datetime.date(2026, 3, 1)) + + self.assertEqual(due.period_end, datetime.date(2027, 2, 28)) + self.assertEqual(due.grace_until, due.period_end + datetime.timedelta(days=GRACE_DAYS)) + + def test_a_leap_day_period_does_not_explode(self): + # 29 February has no counterpart in a common year. + self.assertEqual(add_one_year(datetime.date(2028, 2, 29)), datetime.date(2029, 2, 28)) + + def test_the_next_period_continues_from_the_last_one(self): + # Not from today: a club that pays two months late has still used those two months, + # and restarting the clock at the payment date would quietly gift them away. + first = self.bill(start=self.today - datetime.timedelta(days=400)) + + self.assertEqual(next_period_start(self.club), first.period_end + datetime.timedelta(days=1)) + + def test_a_first_period_starts_today(self): + self.assertEqual(next_period_start(self.club), self.today) + + def test_the_amount_is_snapshotted_at_the_price_of_the_day(self): + due = self.bill() + TierPrice.objects.create(tier=self.tier, active_from=self.today + datetime.timedelta(days=1), amount=Decimal("900.00")) + due.refresh_from_db() + + # Raising the rate must not rewrite what was already billed. + self.assertEqual(due.amount, Decimal("500.00")) + + def test_a_club_cannot_be_billed_twice_for_one_period(self): + self.bill(start=self.today) + + with self.assertRaises(BillingError): + self.bill(start=self.today) + + def test_a_club_with_no_tier_cannot_be_billed(self): + with self.assertRaises(BillingError): + open_period(Club.objects.create(name="Feyenoord")) + + def test_subscribing_puts_a_club_on_a_tier_and_opens_a_period(self): + club = Club.objects.create(name="Feyenoord") + + subscribe(club, self.tier) + + self.assertEqual(Subscription.objects.get(club=club).tier, self.tier) + self.assertEqual(club.dues.count(), 1) + + +class PaymentTests(BillingTestBase): + def setUp(self): + super().setUp() + self.due = self.bill() + + def test_a_part_payment_leaves_the_due_partially_paid(self): + record_payment(self.due, Decimal("200.00")) + self.due.refresh_from_db() + + self.assertEqual(self.due.status, Due.Status.PARTIAL) + self.assertEqual(self.due.balance, Decimal("300.00")) + self.assertIsNone(self.due.paid_at) + + def test_payments_accumulate_until_the_due_is_settled(self): + record_payment(self.due, Decimal("200.00")) + record_payment(self.due, Decimal("300.00")) + self.due.refresh_from_db() + + self.assertEqual(self.due.status, Due.Status.PAID) + self.assertEqual(self.due.balance, Decimal("0.00")) + self.assertIsNotNone(self.due.paid_at) + + def test_an_overpayment_still_settles_the_due(self): + record_payment(self.due, Decimal("600.00")) + self.due.refresh_from_db() + + self.assertEqual(self.due.status, Due.Status.PAID) + + def test_removing_a_payment_re_derives_the_due(self): + # amount_paid is summed from the payments, never incremented: an increment drifts the + # moment one is deleted, and the drift still looks like money. + first = record_payment(self.due, Decimal("200.00")) + record_payment(self.due, Decimal("300.00")) + + remove_payment(first) + self.due.refresh_from_db() + + self.assertEqual(self.due.amount_paid, Decimal("300.00")) + self.assertEqual(self.due.status, Due.Status.PARTIAL) + + def test_removing_the_only_payment_puts_the_due_back_to_unpaid(self): + payment = record_payment(self.due, Decimal("500.00")) + + remove_payment(payment) + self.due.refresh_from_db() + + self.assertEqual(self.due.status, Due.Status.UNPAID) + self.assertEqual(self.due.amount_paid, Decimal("0.00")) + self.assertIsNone(self.due.paid_at) + + def test_a_zero_payment_is_refused(self): + with self.assertRaises(BillingError): + record_payment(self.due, Decimal("0.00")) + + def test_a_waived_period_cannot_take_a_payment(self): + waive(self.due) + + with self.assertRaises(BillingError): + record_payment(self.due, Decimal("100.00")) + + def test_a_period_with_payments_cannot_be_waived(self): + record_payment(self.due, Decimal("100.00")) + + with self.assertRaises(BillingError): + waive(self.due) + + def test_a_waived_period_owes_nothing_and_never_archives_a_club(self): + waive(self.due) + self.due.refresh_from_db() + + self.assertFalse(self.due.is_owing) + self.assertFalse(self.due.is_overdue(self.due.grace_until + datetime.timedelta(days=1))) + + +class GraceAndArchiveTests(BillingTestBase): + LAPSED = 365 + GRACE_DAYS + 10 + + def test_a_period_past_its_end_but_inside_grace_is_in_grace(self): + due = self.bill(start=self.today - datetime.timedelta(days=370)) + + self.assertTrue(due.is_in_grace(self.today)) + self.assertFalse(due.is_overdue(self.today)) + self.assertIn(due, dues_in_grace(self.today)) + + def test_a_period_past_grace_is_overdue(self): + due = self.bill(start=self.today - datetime.timedelta(days=self.LAPSED)) + + self.assertTrue(due.is_overdue(self.today)) + self.assertFalse(due.is_in_grace(self.today)) + self.assertIn(due, dues_overdue(self.today)) + + def test_a_paid_period_is_never_overdue(self): + due = self.bill(start=self.today - datetime.timedelta(days=self.LAPSED)) + record_payment(due, Decimal("500.00")) + due.refresh_from_db() + + self.assertFalse(due.is_overdue(self.today)) + self.assertNotIn(due, dues_overdue(self.today)) + + def test_an_overdue_club_is_archivable(self): + subscribe(self.club, self.tier, start=self.today - datetime.timedelta(days=self.LAPSED)) + + self.assertEqual(archivable_clubs(self.today).count(), 1) + + def test_a_club_that_opted_out_is_never_archived(self): + # auto_archive off is how you stop a club you are negotiating with from being + # switched off overnight. + subscribe(self.club, self.tier, start=self.today - datetime.timedelta(days=self.LAPSED), auto_archive=False) + + self.assertEqual(archivable_clubs(self.today).count(), 0) + + def test_an_already_archived_club_is_not_archived_again(self): + subscribe(self.club, self.tier, start=self.today - datetime.timedelta(days=self.LAPSED)) + self.club.archive() + + self.assertEqual(archivable_clubs(self.today).count(), 0) + + +class ArchiveCommandTests(BillingTestBase): + def setUp(self): + super().setUp() + subscribe(self.club, self.tier, start=self.today - datetime.timedelta(days=365 + GRACE_DAYS + 10)) + + def run_command(self, *args): + out = StringIO() + call_command("archive_overdue_clubs", *args, stdout=out) + return out.getvalue() + + def test_it_reports_without_archiving_by_default(self): + # The asymmetry is the point: this switches off paying customers, so a cron + # misconfiguration or a clock skew must cost an email, not a morning of angry clubs. + output = self.run_command() + + self.club.refresh_from_db() + self.assertFalse(self.club.is_archived) + self.assertIn("Dry run", output) + self.assertIn("Ajax United", output) + + def test_it_archives_with_commit(self): + self.run_command("--commit") + + self.club.refresh_from_db() + self.assertTrue(self.club.is_archived) + + def test_it_says_so_when_nothing_is_overdue(self): + record_payment(self.club.dues.first(), Decimal("500.00")) + + self.assertIn("Nothing overdue", self.run_command()) + + +class ReactivationTests(BillingTestBase): + def setUp(self): + super().setUp() + # Through subscribe(), not open_period(): reactivating reads the club's tier off its + # subscription, and a club billed without one cannot be re-billed later. + subscribe(self.club, self.tier, start=self.today - datetime.timedelta(days=400)) + self.first = self.club.dues.first() + self.club.archive() + + def test_reactivating_continues_from_the_lapsed_period_by_default(self): + due = reactivate(self.club) + + self.club.refresh_from_db() + self.assertFalse(self.club.is_archived) + self.assertEqual(due.period_start, self.first.period_end + datetime.timedelta(days=1)) + + def test_a_chosen_start_forgives_the_gap(self): + due = reactivate(self.club, start=self.today) + + self.assertEqual(due.period_start, self.today) + + +class InvoiceTests(BillingTestBase): + def test_every_period_is_invoiced_when_it_opens(self): + due = self.bill() + + self.assertTrue(Invoice.objects.filter(due=due).exists()) + + def test_numbers_run_in_one_platform_wide_series(self): + # Unlike the shop's per-club order numbers: these are OUR invoices, and one sequence + # covers every club we bill. + first = self.bill(start=self.today).invoice + second = open_period(Club.objects.create(name="Feyenoord"), tier=self.tier).invoice + + year = timezone.now().year + self.assertEqual(first.number, f"INV-{year}-00001") + self.assertEqual(second.number, f"INV-{year}-00002") + + def test_re_issuing_does_not_burn_a_number(self): + # A gap in an invoice series is a question you do not want to have to answer. + due = self.bill() + + self.assertEqual(issue_invoice(due), due.invoice) + self.assertEqual(Invoice.objects.count(), 1) + + def test_the_invoice_renders_the_frozen_snapshot(self): + due = self.bill() + record_payment(due, Decimal("200.00"), reference="TRX-9") + due.refresh_from_db() + + with mock.patch("billing.services.invoices.render_pdf", return_value=b"%PDF-fake") as renderer: + invoice_pdf(due.invoice) + + html = renderer.call_args.args[0] + self.assertIn("INV-", html) + self.assertIn("Ajax United", html) + self.assertIn("500.00", html) # billed + self.assertIn("200.00", html) # paid + self.assertIn("300.00", html) # balance + + def test_the_pdf_library_is_only_needed_when_a_pdf_is_asked_for(self): + # WeasyPrint binds to native pango/cairo. The app, the tests and every other page must + # run without them; only this call may fail. + with mock.patch.dict(sys.modules, {"weasyprint": mock.MagicMock()}): + sys.modules["weasyprint"].HTML.return_value.write_pdf.return_value = b"%PDF-1.7" + + self.assertEqual(render_pdf("

hi

"), b"%PDF-1.7") + + def test_a_missing_pdf_library_says_what_is_missing(self): + with mock.patch.dict(sys.modules, {"weasyprint": None}), self.assertRaises(BillingError) as caught: + render_pdf("

hi

") + + self.assertIn("pango", str(caught.exception)) + + +class ModelStringTests(BillingTestBase): + def test_models_describe_themselves(self): + due = self.bill() + payment = record_payment(due, Decimal("10.00")) + + self.assertEqual(str(self.tier), "Standard") + self.assertIn("500.00", str(self.tier.prices.first())) + self.assertIn("Ajax United", str(due)) + self.assertIn("10.00", str(payment)) + self.assertIn("INV-", str(due.invoice)) + self.assertIn("Standard", str(subscribe(Club.objects.create(name="PSV"), self.tier))) diff --git a/pyproject.toml b/pyproject.toml index 7fa16ec..c541f1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ dependencies = [ "pillow>=12.3.0", "python-dateutil>=2.9.0.post0", "python-decouple>=3.8", + "weasyprint>=69.0", ] [dependency-groups] @@ -49,7 +50,8 @@ ignore = [ "rosterchief/settings/*" = ["F403", "F405", "E501"] [tool.ruff.lint.isort] -known-first-party = ["authentication", "club", "members", "teams", "events", "formbuilder", "shop", "controlpanel", "news", "pages", "home", "search", "rosterchief"] +known-first-party = [ + "billing", "authentication", "club", "members", "teams", "events", "formbuilder", "shop", "controlpanel", "news", "pages", "home", "search", "rosterchief"] [tool.uv.sources] django-lucide = { git = "https://github.com/bsiebens/lucide" } diff --git a/rosterchief/settings.py b/rosterchief/settings.py index c13f0e1..4deccc0 100644 --- a/rosterchief/settings.py +++ b/rosterchief/settings.py @@ -61,6 +61,8 @@ INSTALLED_APPS = [ "events.apps.EventsConfig", "formbuilder.apps.FormbuilderConfig", "shop.apps.ShopConfig", + # Platform billing: RosterChief charging the clubs. Not tenant data — see billing/models.py. + "billing.apps.BillingConfig", "waffle", "features.apps.FeaturesConfig", "controlpanel.apps.ControlpanelConfig", diff --git a/static/css/app.css b/static/css/app.css index 6c628d3..13066f5 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -1197,6 +1197,9 @@ .collapse { visibility: collapse; } + .invisible { + visibility: hidden; + } .visible { visibility: visible; } diff --git a/uv.lock b/uv.lock index cecb875..74e172a 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" }, ] +[[package]] +name = "brotli" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/16/c92ca344d646e71a43b8bb353f0a6490d7f6e06210f8554c8f874e454285/brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a", size = 7388632, upload-time = "2025-11-05T18:39:42.86Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/e1/298c2ddf786bb7347a1cd71d63a347a79e5712a7c0cba9e3c3458ebd976f/brotli-1.2.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:6c12dad5cd04530323e723787ff762bac749a7b256a5bece32b2243dd5c27b21", size = 863080, upload-time = "2025-11-05T18:38:45.503Z" }, + { url = "https://files.pythonhosted.org/packages/84/0c/aac98e286ba66868b2b3b50338ffbd85a35c7122e9531a73a37a29763d38/brotli-1.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:3219bd9e69868e57183316ee19c84e03e8f8b5a1d1f2667e1aa8c2f91cb061ac", size = 445453, upload-time = "2025-11-05T18:38:46.433Z" }, + { url = "https://files.pythonhosted.org/packages/ec/f1/0ca1f3f99ae300372635ab3fe2f7a79fa335fee3d874fa7f9e68575e0e62/brotli-1.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:963a08f3bebd8b75ac57661045402da15991468a621f014be54e50f53a58d19e", size = 1528168, upload-time = "2025-11-05T18:38:47.371Z" }, + { url = "https://files.pythonhosted.org/packages/d6/a6/2ebfc8f766d46df8d3e65b880a2e220732395e6d7dc312c1e1244b0f074a/brotli-1.2.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9322b9f8656782414b37e6af884146869d46ab85158201d82bab9abbcb971dc7", size = 1627098, upload-time = "2025-11-05T18:38:48.385Z" }, + { url = "https://files.pythonhosted.org/packages/f3/2f/0976d5b097ff8a22163b10617f76b2557f15f0f39d6a0fe1f02b1a53e92b/brotli-1.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf9cba6f5b78a2071ec6fb1e7bd39acf35071d90a81231d67e92d637776a6a63", size = 1419861, upload-time = "2025-11-05T18:38:49.372Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/d76df7176a2ce7616ff94c1fb72d307c9a30d2189fe877f3dd99af00ea5a/brotli-1.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7547369c4392b47d30a3467fe8c3330b4f2e0f7730e45e3103d7d636678a808b", size = 1484594, upload-time = "2025-11-05T18:38:50.655Z" }, + { url = "https://files.pythonhosted.org/packages/d3/93/14cf0b1216f43df5609f5b272050b0abd219e0b54ea80b47cef9867b45e7/brotli-1.2.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1530af5c3c275b8524f2e24841cbe2599d74462455e9bae5109e9ff42e9361", size = 1593455, upload-time = "2025-11-05T18:38:51.624Z" }, + { url = "https://files.pythonhosted.org/packages/b3/73/3183c9e41ca755713bdf2cc1d0810df742c09484e2e1ddd693bee53877c1/brotli-1.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d2d085ded05278d1c7f65560aae97b3160aeb2ea2c0b3e26204856beccb60888", size = 1488164, upload-time = "2025-11-05T18:38:53.079Z" }, + { url = "https://files.pythonhosted.org/packages/64/6a/0c78d8f3a582859236482fd9fa86a65a60328a00983006bcf6d83b7b2253/brotli-1.2.0-cp314-cp314-win32.whl", hash = "sha256:832c115a020e463c2f67664560449a7bea26b0c1fdd690352addad6d0a08714d", size = 339280, upload-time = "2025-11-05T18:38:54.02Z" }, + { url = "https://files.pythonhosted.org/packages/f5/10/56978295c14794b2c12007b07f3e41ba26acda9257457d7085b0bb3bb90c/brotli-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:e7c0af964e0b4e3412a0ebf341ea26ec767fa0b4cf81abb5e897c9338b5ad6a3", size = 375639, upload-time = "2025-11-05T18:38:55.67Z" }, +] + +[[package]] +name = "brotlicffi" +version = "1.2.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/b6/017dc5f852ed9b8735af77774509271acbf1de02d238377667145fcee01d/brotlicffi-1.2.0.1.tar.gz", hash = "sha256:c20d5c596278307ad06414a6d95a892377ea274a5c6b790c2548c009385d621c", size = 478156, upload-time = "2026-03-05T19:54:11.547Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/f9/dfa56316837fa798eac19358351e974de8e1e2ca9475af4cb90293cd6576/brotlicffi-1.2.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c85e65913cf2b79c57a3fdd05b98d9731d9255dc0cb696b09376cc091b9cddd", size = 433046, upload-time = "2026-03-05T19:53:46.209Z" }, + { url = "https://files.pythonhosted.org/packages/4a/f5/f8f492158c76b0d940388801f04f747028971ad5774287bded5f1e53f08d/brotlicffi-1.2.0.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:535f2d05d0273408abc13fc0eebb467afac17b0ad85090c8913690d40207dac5", size = 1541126, upload-time = "2026-03-05T19:53:48.248Z" }, + { url = "https://files.pythonhosted.org/packages/3b/e1/ff87af10ac419600c63e9287a0649c673673ae6b4f2bcf48e96cb2f89f60/brotlicffi-1.2.0.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce17eb798ca59ecec67a9bb3fd7a4304e120d1cd02953ce522d959b9a84d58ac", size = 1541983, upload-time = "2026-03-05T19:53:50.317Z" }, + { url = "https://files.pythonhosted.org/packages/47/c0/80ecd9bd45776109fab14040e478bf63e456967c9ddee2353d8330ed8de1/brotlicffi-1.2.0.1-cp314-cp314t-win32.whl", hash = "sha256:3c9544f83cb715d95d7eab3af4adbbef8b2093ad6382288a83b3a25feb1a57ec", size = 349047, upload-time = "2026-03-05T19:53:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/ab/98/13e5b250236a281b6cd9e92a01ee1ae231029fa78faee932ef3766e1cb24/brotlicffi-1.2.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:625f8115d32ae9c0740d01ea51518437c3fbaa3e78d41cb18459f6f7ac326000", size = 385652, upload-time = "2026-03-05T19:53:53.892Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9f/b98dcd4af47994cee97aebac866996a006a2e5fc1fd1e2b82a8ad95cf09c/brotlicffi-1.2.0.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:91ba5f0ccc040f6ff8f7efaf839f797723d03ed46acb8ae9408f99ffd2572cf4", size = 432608, upload-time = "2026-03-05T19:53:56.736Z" }, + { url = "https://files.pythonhosted.org/packages/b1/7a/ac4ee56595a061e3718a6d1ea7e921f4df156894acffb28ed88a1fd52022/brotlicffi-1.2.0.1-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9a670c6811af30a4bd42d7116dc5895d3b41beaa8ed8a89050447a0181f5ce", size = 1534257, upload-time = "2026-03-05T19:53:58.667Z" }, + { url = "https://files.pythonhosted.org/packages/99/39/e7410db7f6f56de57744ea52a115084ceb2735f4d44973f349bb92136586/brotlicffi-1.2.0.1-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3314a3476f59e5443f9f72a6dff16edc0c3463c9b318feaef04ae3e4683f5a", size = 1536838, upload-time = "2026-03-05T19:54:00.705Z" }, + { url = "https://files.pythonhosted.org/packages/a6/75/6e7977d1935fc3fbb201cbd619be8f2c7aea25d40a096967132854b34708/brotlicffi-1.2.0.1-cp38-abi3-win32.whl", hash = "sha256:82ea52e2b5d3145b6c406ebd3efb0d55db718b7ad996bd70c62cec0439de1187", size = 343337, upload-time = "2026-03-05T19:54:02.446Z" }, + { url = "https://files.pythonhosted.org/packages/d8/ef/e7e485ce5e4ba3843a0a92feb767c7b6098fd6e65ce752918074d175ae71/brotlicffi-1.2.0.1-cp38-abi3-win_amd64.whl", hash = "sha256:da2e82a08e7778b8bc539d27ca03cdd684113e81394bfaaad8d0dfc6a17ddede", size = 379026, upload-time = "2026-03-05T19:54:04.322Z" }, +] + [[package]] name = "cffi" version = "2.1.0" @@ -168,6 +207,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001", size = 3785547, upload-time = "2026-06-12T20:02:26.847Z" }, ] +[[package]] +name = "cssselect2" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tinycss2" }, + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/20/92eaa6b0aec7189fa4b75c890640e076e9e793095721db69c5c81142c2e1/cssselect2-0.9.0.tar.gz", hash = "sha256:759aa22c216326356f65e62e791d66160a0f9c91d1424e8d8adc5e74dddfc6fb", size = 35595, upload-time = "2026-02-12T17:16:39.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/0e/8459ca4413e1a21a06c97d134bfaf18adfd27cea068813dc0faae06cbf00/cssselect2-0.9.0-py3-none-any.whl", hash = "sha256:6a99e5f91f9a016a304dd929b0966ca464bcfda15177b6fb4a118fc0fb5d9563", size = 15453, upload-time = "2026-02-12T17:16:38.317Z" }, +] + [[package]] name = "dj-database-url" version = "3.1.2" @@ -272,6 +324,38 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/71/84/198d99c3312557ef6121cf78c38281efe9b3bc88cba0e2c05446f38a024d/fido2-2.2.1-py3-none-any.whl", hash = "sha256:ed397da981b9ab133da6ead7309e41f924b566b749956129efe286fae097749f", size = 238354, upload-time = "2026-06-29T17:41:09.921Z" }, ] +[[package]] +name = "fonttools" +version = "4.63.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/d2/23d25e3f247b328be58d04a4c9f894178a0d1eda7d42867cfb388adaf416/fonttools-4.63.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fd1e3094f42d806d3d7c79162fc59e5910fcbe3a7360c385b8da969bc4493745", size = 2875338, upload-time = "2026-05-14T12:03:50.052Z" }, + { url = "https://files.pythonhosted.org/packages/cd/58/7dfa0c761cb3b2964e2a84c4dc986c926a87de0cb9fb60d5b28ded3f2914/fonttools-4.63.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6e528da43bc3791085f8cb6141b1d13e459226790240340fcbb4625649238b03", size = 2422661, upload-time = "2026-05-14T12:03:52.154Z" }, + { url = "https://files.pythonhosted.org/packages/dd/87/64cfa18a7a1621d17b7f4502b2b0ed8a135a90c3db51ea590ee99043e76b/fonttools-4.63.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b2248c5decb223562f7902ff6325077a073f608ee8e33e88ad88db734eb9f49", size = 5010526, upload-time = "2026-05-14T12:03:54.647Z" }, + { url = "https://files.pythonhosted.org/packages/36/e1/a8933a72c45a87177fbde2696e0d0755c8c9062f8c077a961c6215fa27b1/fonttools-4.63.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:308f957cdeaf8abe4e5f2f124902ef405448af92c90f80e302a3b771c2e6116b", size = 4923946, upload-time = "2026-05-14T12:03:56.984Z" }, + { url = "https://files.pythonhosted.org/packages/27/60/872e6e233b8c5e8b41413796ff18b7fe479661bd40147e071b450dfad7a1/fonttools-4.63.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:bf00f21eb5fb721dbaf73d1e9da6d02a1af7768f2ebcf9798be98beab8ba90f6", size = 4962489, upload-time = "2026-05-14T12:03:59.443Z" }, + { url = "https://files.pythonhosted.org/packages/30/c4/83c24f2ec38b90cfda84bf4b1a1f49df80e84a1db4e7ac6e0d41bf23bc39/fonttools-4.63.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c1aaa4b9c75798400ac043ce04d74e7830376c85095a5a6ed7cba2f17a266bf4", size = 5071870, upload-time = "2026-05-14T12:04:02.122Z" }, + { url = "https://files.pythonhosted.org/packages/de/40/3ae22b60ff1d41ce0bd044b31238cdc72cef99f28b976f1e128ebd618c9b/fonttools-4.63.0-cp314-cp314-win32.whl", hash = "sha256:22693918177bd9ceabec4736d338045f357769416fc6b0b2508eefef75b08616", size = 2295026, upload-time = "2026-05-14T12:04:04.47Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d4/98078064ccc76b45cb0f6c002452011e93c4bd26f6850344f0951cc1fe89/fonttools-4.63.0-cp314-cp314-win_amd64.whl", hash = "sha256:7d782fac32985914c351556f68ac0855391572bcd87de50e05970d3cd4c96fc5", size = 2347454, upload-time = "2026-05-14T12:04:06.752Z" }, + { url = "https://files.pythonhosted.org/packages/49/4e/652d1580c5f4e39f7d103b0c793e4773129ad633dce4addd0cf4dfebde02/fonttools-4.63.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:6db5140a60a5d731d21ec076745b40a310607731b0a565b50776393188649001", size = 2958152, upload-time = "2026-05-14T12:04:08.706Z" }, + { url = "https://files.pythonhosted.org/packages/0e/55/ad864c9a9b219f552eb46b32cd7906c466e5a578ba0c3abfcc0fe7413eb6/fonttools-4.63.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d76edbff9014094dbf03bd2d074709dfa6ec7aba13d838c937a2b33d2d6a86e", size = 2460809, upload-time = "2026-05-14T12:04:10.783Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/0aa8db70f18cf52e49b4ed5ecec68547f981160bf5ded3b5aed6faa0a6f9/fonttools-4.63.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0eac00b9118c3c2f87d272e45341871c5b3066baa3c86897fa634a7c3fb59096", size = 5148649, upload-time = "2026-05-14T12:04:12.747Z" }, + { url = "https://files.pythonhosted.org/packages/7f/63/18e4369c25043096f1048e0c9915951adc4f842bd81c6b18155824d6fa99/fonttools-4.63.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51394295f1a51de8b5f30bdb1e1b9a4231536c7064ef5c6e211eec19fa36036f", size = 4932147, upload-time = "2026-05-14T12:04:14.806Z" }, + { url = "https://files.pythonhosted.org/packages/a1/3f/67f3eac2ffd8a98446c5022f8ed3864eac878a5ff7af8df4c8286dba16cc/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9e12f105d2b6342c559c298afb674006bb2893afc7102dcf8a1b55b0486b4e40", size = 5027237, upload-time = "2026-05-14T12:04:17.675Z" }, + { url = "https://files.pythonhosted.org/packages/1a/ba/4e6214cb38a7b04779e97bb7636de9a5c7f20af7018d03dee0b64c08510a/fonttools-4.63.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:796f27556dbe094c4824f75ca85267e4df776c79036c8441469a4df37038c196", size = 5053933, upload-time = "2026-05-14T12:04:20.818Z" }, + { url = "https://files.pythonhosted.org/packages/34/3b/214dcc19ee31d3d38fb5ad2755c11ef0514e5dc300bbaf41c0b69f393799/fonttools-4.63.0-cp314-cp314t-win32.whl", hash = "sha256:948428a275741f0b64b113c955425a953314f4b9ab9997f73a72c83e68e569c8", size = 2359326, upload-time = "2026-05-14T12:04:24.22Z" }, + { url = "https://files.pythonhosted.org/packages/dd/1e/3ff1a9b523058c2eeb6a9d50f5574e2a738200d0d94107d5bc4105e8da3f/fonttools-4.63.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6d4741eb179121cab9eea4cb2393d24492373a260d7945006358c08cfbf45419", size = 2425829, upload-time = "2026-05-14T12:04:26.829Z" }, + { url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" }, +] + +[package.optional-dependencies] +woff = [ + { name = "brotli", marker = "platform_python_implementation == 'CPython'" }, + { name = "brotlicffi", marker = "platform_python_implementation != 'CPython'" }, + { name = "zopfli" }, +] + [[package]] name = "phonenumbers" version = "9.0.33" @@ -340,6 +424,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, ] +[[package]] +name = "pydyf" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/ee/fb410c5c854b6a081a49077912a9765aeffd8e07cbb0663cfda310b01fb4/pydyf-0.12.1.tar.gz", hash = "sha256:fbd7e759541ac725c29c506612003de393249b94310ea78ae44cb1d04b220095", size = 17716, upload-time = "2025-12-02T14:52:14.244Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/11/47efe2f66ba848a107adfd490b508f5c0cedc82127950553dca44d29e6c4/pydyf-0.12.1-py3-none-any.whl", hash = "sha256:ea25b4e1fe7911195cb57067560daaa266639184e8335365cc3ee5214e7eaadc", size = 8028, upload-time = "2025-12-02T14:52:12.938Z" }, +] + +[[package]] +name = "pyphen" +version = "0.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/56/e4d7e1bd70d997713649c5ce530b2d15a5fc2245a74ca820fc2d51d89d4d/pyphen-0.17.2.tar.gz", hash = "sha256:f60647a9c9b30ec6c59910097af82bc5dd2d36576b918e44148d8b07ef3b4aa3", size = 2079470, upload-time = "2025-01-20T13:18:36.296Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/1f/c2142d2edf833a90728e5cdeb10bdbdc094dde8dbac078cee0cf33f5e11b/pyphen-0.17.2-py3-none-any.whl", hash = "sha256:3a07fb017cb2341e1d9ff31b8634efb1ae4dc4b130468c7c39dd3d32e7c3affd", size = 2079358, upload-time = "2025-01-20T13:18:29.629Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -387,6 +489,7 @@ dependencies = [ { name = "pillow" }, { name = "python-dateutil" }, { name = "python-decouple" }, + { name = "weasyprint" }, ] [package.dev-dependencies] @@ -407,6 +510,7 @@ requires-dist = [ { name = "pillow", specifier = ">=12.3.0" }, { name = "python-dateutil", specifier = ">=2.9.0.post0" }, { name = "python-decouple", specifier = ">=3.8" }, + { name = "weasyprint", specifier = ">=69.0" }, ] [package.metadata.requires-dev] @@ -459,6 +563,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, ] +[[package]] +name = "tinycss2" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/ae/2ca4913e5c0f09781d75482874c3a95db9105462a92ddd303c7d285d3df2/tinycss2-1.5.1.tar.gz", hash = "sha256:d339d2b616ba90ccce58da8495a78f46e55d4d25f9fd71dfd526f07e7d53f957", size = 88195, upload-time = "2025-11-23T10:29:10.082Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/45/c7b5c3168458db837e8ceab06dc77824e18202679d0463f0e8f002143a97/tinycss2-1.5.1-py3-none-any.whl", hash = "sha256:3415ba0f5839c062696996998176c4a3751d18b7edaaeeb658c9ce21ec150661", size = 28404, upload-time = "2025-11-23T10:29:08.676Z" }, +] + +[[package]] +name = "tinyhtml5" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "webencodings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/1f/cfe2f6b30557c92b3f31d41707e09cef5c1efbd87392bc6c0430c46b0e4d/tinyhtml5-2.1.0.tar.gz", hash = "sha256:60a50ec3d938a37e491efa01af895853060943dcebb5627de5b10d188b338a67", size = 179242, upload-time = "2026-03-05T17:06:30.704Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/48/01695a036b695f83fea7aef6955d735db0f517b1c8e25ddb399ac0bdbcbf/tinyhtml5-2.1.0-py3-none-any.whl", hash = "sha256:6e11cfff38515834268daf89d5f85bbde0b6dd02e8d9e212d1385c2289b89f0a", size = 39686, upload-time = "2026-03-05T17:06:28.498Z" }, +] + [[package]] name = "tzdata" version = "2026.2" @@ -467,3 +595,47 @@ sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35c wheels = [ { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, ] + +[[package]] +name = "weasyprint" +version = "69.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, + { name = "cssselect2" }, + { name = "fonttools", extra = ["woff"] }, + { name = "pillow" }, + { name = "pydyf" }, + { name = "pyphen" }, + { name = "tinycss2" }, + { name = "tinyhtml5" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/53/dcc3885c2f7a47faa45f6b8b801412f5f9e055173a52801ef01c09943c5a/weasyprint-69.0.tar.gz", hash = "sha256:a7a32f39ca16bd82ef11de99c92ea4b5f14951c9033af035e451ce4f4ee0a88c", size = 1549834, upload-time = "2026-06-02T14:42:17.765Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/cb/208525c6bd5033d7b2589b55e07bec23d9c61bb00703cbaf20ef52c3811f/weasyprint-69.0-py3-none-any.whl", hash = "sha256:475951cfd917014de6d4d005caff48c6aa867e7e42b80cd5b16a0484a1609ee6", size = 322872, upload-time = "2026-06-02T14:42:15.871Z" }, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, +] + +[[package]] +name = "zopfli" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/21/3b6af43a663b22b00e738bb0642931a2579e15da6852613d56c6aa535d28/zopfli-0.4.3.tar.gz", hash = "sha256:d3a50f91a13cea9bafe025de8fd87a005eb26de02a4f0c193127ddbf23ac8ebe", size = 179156, upload-time = "2026-06-10T09:10:19.96Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/5f/b7d81b670daf990e15a0f7551da96c3c0700f69ae6d96b0245d6a19f51f3/zopfli-0.4.3-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:88f4fbe429aad72bc206275d81fab11a097e0f951a5848d1f51083c37ea73073", size = 291492, upload-time = "2026-06-10T09:10:06.621Z" }, + { url = "https://files.pythonhosted.org/packages/55/c8/d8d8d731e0b192024567b7198fb77b748821d355f3c8bf0109de27191f43/zopfli-0.4.3-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:769875152d0625c46707bcca57d4b2233fe653482067acd55fbf6ec525cb9bdc", size = 829354, upload-time = "2026-06-10T09:10:07.909Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2b/fbe8ba2ec40f5986b8983a4752f7a32672a80a10ea6e68213324a7055469/zopfli-0.4.3-cp310-abi3-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0c9c1d40a8cb1d58762d7e57290ccb753e0828c4d01be8acb59aae5d0ca206", size = 818436, upload-time = "2026-06-10T09:10:09.063Z" }, + { url = "https://files.pythonhosted.org/packages/de/d9/63568c54c8b68b9135f3456c5add83797a5528d596657f0e4f4910173b08/zopfli-0.4.3-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7fa3c35193475290e3f007bbcdebdbae64ba2f012d75c632da0d727e1da50d5e", size = 1778931, upload-time = "2026-06-10T09:10:10.282Z" }, + { url = "https://files.pythonhosted.org/packages/7a/05/8f3aac10a858e89c2146d3a1f6ce33634c3db757365b4148fef1b85784d2/zopfli-0.4.3-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:47604eee5c6704bdf0e94d8391fe3b74ddb2abd84128fbcfdc3ee0fc265feaef", size = 1864132, upload-time = "2026-06-10T09:10:11.595Z" }, + { url = "https://files.pythonhosted.org/packages/8d/20/9ca59d14b91f9fbc631793b4b085b309777edadaca496aa518a180817827/zopfli-0.4.3-cp310-abi3-win32.whl", hash = "sha256:628c3e941752880b3491db8d44163d0aedb221944e22a17187ff7fc549b050f6", size = 271715, upload-time = "2026-06-10T09:10:12.7Z" }, + { url = "https://files.pythonhosted.org/packages/9d/3a/4ff4fdead77ef30f5832b38a47eb7a1283e98b3c678576b83f8fdfff53eb/zopfli-0.4.3-cp310-abi3-win_amd64.whl", hash = "sha256:921c2c9907f4364963848da5ad194b46d68865e07fdb975d04fd09bc42d47357", size = 288550, upload-time = "2026-06-10T09:10:13.639Z" }, + { url = "https://files.pythonhosted.org/packages/e6/44/6264f929057236fde72dd6d271f54612b4811ce37288e002f5d5339d696a/zopfli-0.4.3-cp310-abi3-win_arm64.whl", hash = "sha256:7e9703ca6e7ef66c8d05e0826b6f558b680c9db8206f84f05a3ee93430a12e42", size = 451343, upload-time = "2026-06-10T09:10:14.72Z" }, +] From 29d61eeea8230102d4dda9e20eebe346058b1545 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 01:52:43 +0200 Subject: [PATCH 064/137] Manage billing from the control panel A Billing tab (tiers, their dated prices, and everything we are owed), a billing panel on each club (plan, periods, payment history, invoice), and the dues on the dashboard and the club tables. Every state change goes through the billing service, and a BillingError surfaces as a message rather than a 500 -- so "that period is waived", "no price in force", "already billed for that period" and a missing PDF library all explain themselves instead of crashing. The dashboard now separates the two pots of money that were previously one word. "Revenue per month" was CLUB SHOP revenue -- members paying their clubs, which is never ours -- sitting on our dashboard under a label that implied it was income. It is now "Platform dues per month" (what clubs paid us) with the club-shop series renamed club_revenue, and the club tables carry a Plan column and what each club owes us, annotated in the same single query. Rate changes are add-only in the UI as well as the model: the price form creates a dated row and never edits the last one, and a test asserts that raising the rate leaves an already-open period at the amount it was billed at. Co-Authored-By: Claude Opus 4.8 --- controlpanel/forms.py | 47 ++++ controlpanel/services/statistics.py | 25 +- .../templates/controlpanel/_billing_form.html | 40 +++ .../controlpanel/_club_health_table.html | 23 +- controlpanel/templates/controlpanel/base.html | 1 + .../templates/controlpanel/billing.html | 117 ++++++++ .../templates/controlpanel/club_detail.html | 96 +++++++ .../templates/controlpanel/dashboard.html | 25 +- .../templates/controlpanel/payment_form.html | 14 + .../templates/controlpanel/period_form.html | 12 + .../controlpanel/subscription_form.html | 8 + .../templates/controlpanel/tier_form.html | 8 + .../controlpanel/tier_price_form.html | 8 + controlpanel/tests.py | 264 +++++++++++++++++- controlpanel/urls.py | 10 + controlpanel/views.py | 210 +++++++++++++- static/css/app.css | 12 + 17 files changed, 908 insertions(+), 12 deletions(-) create mode 100644 controlpanel/templates/controlpanel/_billing_form.html create mode 100644 controlpanel/templates/controlpanel/billing.html create mode 100644 controlpanel/templates/controlpanel/payment_form.html create mode 100644 controlpanel/templates/controlpanel/period_form.html create mode 100644 controlpanel/templates/controlpanel/subscription_form.html create mode 100644 controlpanel/templates/controlpanel/tier_form.html create mode 100644 controlpanel/templates/controlpanel/tier_price_form.html diff --git a/controlpanel/forms.py b/controlpanel/forms.py index 0bddd5a..92a077d 100644 --- a/controlpanel/forms.py +++ b/controlpanel/forms.py @@ -1,7 +1,10 @@ +from decimal import Decimal + from django import forms from django.utils.translation import gettext_lazy as _ from waffle import get_waffle_flag_model +from billing.models import DuePayment, Subscription, Tier, TierPrice from club.models import Club from .services.admins import find_member_by_email @@ -56,3 +59,47 @@ class FlagForm(forms.ModelForm): help_texts = { "everyone": _("Yes = on for all clubs, No = off everywhere (overrides club targeting). Leave unknown to target clubs."), } + + +class TierForm(forms.ModelForm): + class Meta: + model = Tier + fields = ["name", "description", "is_active"] + + +class TierPriceForm(forms.ModelForm): + class Meta: + model = TierPrice + fields = ["active_from", "amount"] + widgets = {"active_from": forms.DateInput(attrs={"type": "date"})} + help_texts = {"active_from": _("Periods opening on or after this date are billed at this amount. Existing periods keep the amount they were billed at.")} + + +class SubscriptionForm(forms.ModelForm): + """Put a club on a tier. The first period opens when the subscription is created.""" + + start = forms.DateField(required=False, widget=forms.DateInput(attrs={"type": "date"}), label=_("First period starts"), help_text=_("Left blank, the period starts today.")) + + class Meta: + model = Subscription + fields = ["tier", "auto_archive", "notes"] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # An inactive tier still bills its existing subscriptions, but must not be picked up + # by a new one — which is the whole point of retiring a tier. + self.fields["tier"].queryset = Tier.objects.filter(is_active=True) + + +class DuePaymentForm(forms.Form): + amount = forms.DecimalField(max_digits=10, decimal_places=2, min_value=Decimal("0.01"), label=_("Amount")) + method = forms.ChoiceField(choices=DuePayment.Method.choices, initial=DuePayment.Method.BANK_TRANSFER, label=_("Method")) + reference = forms.CharField(required=False, label=_("Reference"), help_text=_("Bank reference, transaction id — whatever lets you find this again.")) + paid_at = forms.DateTimeField(required=False, widget=forms.DateTimeInput(attrs={"type": "datetime-local"}), label=_("Received"), help_text=_("Left blank, now.")) + note = forms.CharField(required=False, widget=forms.Textarea(attrs={"rows": 2}), label=_("Note")) + + +class OpenPeriodForm(forms.Form): + """Renew, or reactivate an archived club.""" + + start = forms.DateField(required=False, widget=forms.DateInput(attrs={"type": "date"}), label=_("Period starts"), help_text=_("Left blank, it continues from the end of the last period — so a lapsed year is still owed.")) diff --git a/controlpanel/services/statistics.py b/controlpanel/services/statistics.py index 406fb91..ea0fe45 100644 --- a/controlpanel/services/statistics.py +++ b/controlpanel/services/statistics.py @@ -17,6 +17,8 @@ from django.utils import timezone from waffle import get_waffle_flag_model from authentication.middleware import ELEVATED_ROLES +from billing.models import Due, DuePayment, Subscription +from billing.services.dues import dues_in_grace, dues_overdue from club.models import Club, ClubMembership, ClubRole, Season from events.models import Attendance, Event from members.models import Member @@ -78,6 +80,10 @@ def clubs_with_health(queryset=None, today=None, now=None): team_count=_subquery(Team.objects.all(), Count("pk"), IntegerField()), teams_managed=_subquery(Team.objects.filter(managed_this_season), Count("pk", distinct=True), IntegerField()), admin_count=_subquery(ClubRole.objects.filter(role=ClubRole.Roles.ADMIN), Count("pk"), IntegerField()), + tier_name=Subquery(Subscription.objects.filter(club=OuterRef("pk")).values("tier__name")[:1]), + dues_owed=_subquery(Due.objects.filter(status__in=Due.OWING), Sum(F("amount") - F("amount_paid")), DecimalField(max_digits=10, decimal_places=2)), + dues_grace_until=Subquery(Due.objects.filter(club=OuterRef("pk"), status__in=Due.OWING).order_by("grace_until").values("grace_until")[:1]), + dues_period_end=Subquery(Due.objects.filter(club=OuterRef("pk"), status__in=Due.OWING).order_by("period_end").values("period_end")[:1]), ) .annotate(teams_without_coach=F("team_count") - F("teams_managed")) .order_by("name") @@ -160,9 +166,22 @@ def platform_attention(): "outstanding": _money(Order.objects.filter(status__in=OWED_STATUSES)), "members_without_login": Member.objects.filter(user__isnull=True).count(), "members": members, + # Platform billing: what the clubs owe US. Distinct from `outstanding`, which is + # what members owe their clubs — that money is never ours. + "dues_owed": _dues_owed(), + "dues_in_grace": dues_in_grace().count(), + "dues_overdue": dues_overdue().count(), + "clubs_unbilled": Club.objects.active().filter(subscription__isnull=True).count(), } +def _dues_owed(): + """What clubs owe the platform right now.""" + owed = Due.objects.filter(status__in=Due.OWING).aggregate(total=Sum(F("amount") - F("amount_paid")))["total"] + + return owed or ZERO + + def _monthly(queryset, field, value, months=MONTHS_OF_HISTORY): """A dense month-by-month series — zero-filled, because a chart that silently skips empty months draws a smooth line over a month where nothing happened.""" @@ -183,7 +202,11 @@ def _monthly(queryset, field, value, months=MONTHS_OF_HISTORY): def platform_charts(): return { "signups": signup_split(), - "revenue": _monthly(Order.objects.filter(status__in=PAID_STATUSES), "created", Sum("total")), + # Two different pots of money: `dues` is platform income (clubs paying us), while + # `club_revenue` is members paying their clubs — never ours, and labelling it + # "revenue" on our dashboard would be a lie. + "dues": _monthly(DuePayment.objects.all(), "paid_at", Sum("amount")), + "club_revenue": _monthly(Order.objects.filter(status__in=PAID_STATUSES), "created", Sum("total")), } diff --git a/controlpanel/templates/controlpanel/_billing_form.html b/controlpanel/templates/controlpanel/_billing_form.html new file mode 100644 index 0000000..1ec68c7 --- /dev/null +++ b/controlpanel/templates/controlpanel/_billing_form.html @@ -0,0 +1,40 @@ +{% load lucide ui %} + +{% comment %} + The shell every billing form uses: card, fields, cancel + submit. Included with + `heading`, `blurb`, `submit_label`, `submit_icon` and `cancel_url`. +{% endcomment %} +
+
+ {% if blurb %}

{{ blurb }}

{% endif %} +
+ {% csrf_token %} + {% for error in form.non_field_errors %} +
+ {{ error }} +
+ {% endfor %} + {% for field in form %} +
+ {% if field.field.widget.input_type == "checkbox" %} + + {% else %} + + {{ field|daisy }} + {% endif %} + {% if field.help_text %}{{ field.help_text }}{% endif %} + {% for error in field.errors %}{{ error }}{% endfor %} +
+ {% endfor %} +
+ {% lucide "arrow-left" size=16 %} Cancel + +
+
+
+
diff --git a/controlpanel/templates/controlpanel/_club_health_table.html b/controlpanel/templates/controlpanel/_club_health_table.html index b8795bd..68ed2da 100644 --- a/controlpanel/templates/controlpanel/_club_health_table.html +++ b/controlpanel/templates/controlpanel/_club_health_table.html @@ -16,6 +16,8 @@ Members Unpaid Owed + Plan + Dues Teams Upcoming Admins @@ -43,6 +45,25 @@ {{ club.active_members }} {{ club.unpaid_members }} €{{ club.outstanding|floatformat:2 }} + + {% if club.tier_name %} + {{ club.tier_name }} + {% else %} + Not billed + {% endif %} + + + {% if not club.dues_owed %} + {% if club.tier_name %}Paid{% endif %} + {% else %} + €{{ club.dues_owed|floatformat:2 }} + {% if club.dues_grace_until < today %} + Overdue + {% elif club.dues_period_end < today %} + Grace + {% endif %} + {% endif %} + {{ club.team_count }} {% if club.teams_without_coach %} @@ -54,7 +75,7 @@ {% empty %} - {{ empty_message|default:"No clubs yet." }} + {{ empty_message|default:"No clubs yet." }} {% endfor %} diff --git a/controlpanel/templates/controlpanel/base.html b/controlpanel/templates/controlpanel/base.html index a1f346d..cd469c9 100644 --- a/controlpanel/templates/controlpanel/base.html +++ b/controlpanel/templates/controlpanel/base.html @@ -22,6 +22,7 @@
{% lucide "layout-dashboard" size=16 %} Dashboard {% lucide "building-2" size=16 %} Clubs + {% lucide "receipt-euro" size=16 %} Billing {% lucide "toggle-right" size=16 %} Features {% if user.is_superuser %} {% lucide "user-cog" size=16 %} Admins diff --git a/controlpanel/templates/controlpanel/billing.html b/controlpanel/templates/controlpanel/billing.html new file mode 100644 index 0000000..e8f57a3 --- /dev/null +++ b/controlpanel/templates/controlpanel/billing.html @@ -0,0 +1,117 @@ +{% extends "controlpanel/base.html" %} +{% load lucide %} + +{% block heading %}Billing{% endblock heading %} +{% block subheading %}

What the platform charges its clubs.

{% endblock subheading %} + +{% block actions %} + {% lucide "plus" size=16 %} New tier +{% endblock actions %} + +{% block panel %} +
+
+

{% lucide "layers" size=18 %} Tiers

+ {% comment %} + Prices are dated, not edited. A rate change is a new row with a future + active_from; every period already opened keeps the amount it was billed at, + so raising the price cannot rewrite an invoice you have already sent. + {% endcomment %} +

A rate change is a new dated price. Periods already billed keep the amount they were issued at.

+
+ + + + + + + + + + + {% for tier in tiers %} + + + + + + + {% empty %} + + + + {% endfor %} + +
TierClubsPrices
+
{{ tier.name }}
+ {% if not tier.is_active %}Retired{% endif %} + {% if tier.description %}
{{ tier.description }}
{% endif %} +
{{ tier.club_count }} + {% for price in tier.prices.all %} +
+ €{{ price.amount|floatformat:2 }} + from {{ price.active_from|date:"j M Y" }} + {% if price.active_from > today %}Scheduled{% endif %} +
+ {% empty %} + No price — cannot be billed + {% endfor %} +
+ {% lucide "euro" size=14 %} New price + {% lucide "pencil" size=14 %} Edit +
No tiers yet.
+
+
+
+ +
+
+

{% lucide "receipt-euro" size=18 %} Owed

+
+ + + + + + + + + + + + {% for due in owing %} + + + + + + + + {% empty %} + + + + {% endfor %} + +
ClubPeriodOwedStatus
+ {{ due.club.name }} +
{{ due.tier.name }}
+
+ {{ due.period_start|date:"j M Y" }} — {{ due.period_end|date:"j M Y" }} +
Grace to {{ due.grace_until|date:"j M Y" }}
+
€{{ due.balance|floatformat:2 }} + {% if due.grace_until < today %} + {% lucide "triangle-alert" size=12 %} Overdue + {% elif due.period_end < today %} + {% lucide "hourglass" size=12 %} In grace + {% else %} + {{ due.get_status_display }} + {% endif %} + + {% lucide "banknote" size=14 %} Record payment + {% lucide "file-text" size=14 %} Invoice +
Nothing outstanding.
+
+
+
+{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/club_detail.html b/controlpanel/templates/controlpanel/club_detail.html index 58b619d..9d2c674 100644 --- a/controlpanel/templates/controlpanel/club_detail.html +++ b/controlpanel/templates/controlpanel/club_detail.html @@ -213,6 +213,102 @@
+
+
+ + + {% if not subscription %} +

This club is not billed for anything. Put it on a tier to start.

+ {% else %} +

+ On {{ subscription.tier.name }}. + {% if subscription.auto_archive %} + Archived automatically when a period goes unpaid past its grace period. + {% else %} + Auto-archive off — it will never be archived for non-payment. + {% endif %} +

+ +
+ + + + + + + + + + + + {% for due in dues %} + + + + + + + + {% for payment in due.payments.all %} + + + + + + {% endfor %} + {% empty %} + + + + {% endfor %} + +
PeriodBilledPaidStatus
+ {{ due.period_start|date:"j M Y" }} — {{ due.period_end|date:"j M Y" }} +
{{ due.tier.name }} · {{ due.invoice.number }} · grace to {{ due.grace_until|date:"j M Y" }}
+
€{{ due.amount|floatformat:2 }}€{{ due.amount_paid|floatformat:2 }} + {% if due.status == "paid" %} + {% lucide "check" size=12 %} Paid + {% elif due.status == "waived" %} + Waived + {% elif due.grace_until < today %} + {% lucide "triangle-alert" size=12 %} Overdue + {% elif due.period_end < today %} + {% lucide "hourglass" size=12 %} In grace + {% else %} + {{ due.get_status_display }} + {% endif %} + + {% if due.is_owing %} + {% lucide "banknote" size=14 %} Pay + {% if not due.payments.all %} +
+ {% csrf_token %} + +
+ {% endif %} + {% endif %} + {% lucide "file-text" size=14 %} Invoice +
+ {% lucide "corner-down-right" size=12 %} + {{ payment.paid_at|date:"j M Y" }} · {{ payment.get_method_display }}{% if payment.reference %} · {{ payment.reference }}{% endif %} + €{{ payment.amount|floatformat:2 }}
No periods billed yet.
+
+ {% endif %} +
+
+
diff --git a/controlpanel/templates/controlpanel/dashboard.html b/controlpanel/templates/controlpanel/dashboard.html index b553d4f..e24c190 100644 --- a/controlpanel/templates/controlpanel/dashboard.html +++ b/controlpanel/templates/controlpanel/dashboard.html @@ -37,11 +37,18 @@
Admins locked out until they enrol
-
+
-
{% lucide "banknote" size=16 %} Outstanding
-
€{{ attention.outstanding|floatformat:2 }}
-
Unpaid across every club
+ {% comment %} + What the CLUBS owe US. Not to be confused with the club-shop money below, + which members owe their clubs and is never ours. + {% endcomment %} +
{% lucide "receipt-euro" size=16 %} Dues owed
+
€{{ attention.dues_owed|floatformat:2 }}
+
+ {{ attention.dues_in_grace }} in grace · + {{ attention.dues_overdue }} overdue +
@@ -58,7 +65,8 @@
-

{% lucide "euro" size=18 %} Revenue per month

+

{% lucide "receipt-euro" size=18 %} Platform dues per month

+

What clubs paid us. Club-shop money is theirs, not ours.

@@ -138,6 +146,11 @@
Club admins
{{ totals.admins }}
+
+
Not billed
+
{{ attention.clubs_unbilled }}
+
Clubs on no tier
+
@@ -221,7 +234,7 @@ }, }); - return [signups, build("revenue-chart", "Revenue", data.revenue, css("--color-accent", "#0ea5e9"), "line", true)]; + return [signups, build("revenue-chart", "Dues", data.dues, css("--color-accent", "#0ea5e9"), "bar", true)]; }; let charts = render(); diff --git a/controlpanel/templates/controlpanel/payment_form.html b/controlpanel/templates/controlpanel/payment_form.html new file mode 100644 index 0000000..b547539 --- /dev/null +++ b/controlpanel/templates/controlpanel/payment_form.html @@ -0,0 +1,14 @@ +{% extends "controlpanel/base.html" %} + +{% block heading %}Record payment — {{ due.club }}{% endblock heading %} + +{% block subheading %} +

+ {{ due.period_start|date:"j M Y" }} to {{ due.period_end|date:"j M Y" }} · €{{ due.amount|floatformat:2 }} billed · €{{ due.balance|floatformat:2 }} outstanding +

+{% endblock subheading %} + +{% block panel %} + {% url 'controlpanel:club_detail' due.club.pk as club_url %} + {% include "controlpanel/_billing_form.html" with cancel_url=club_url submit_label="Record payment" submit_icon="banknote" blurb="Part payments are fine: they accumulate against the period until it is settled." %} +{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/period_form.html b/controlpanel/templates/controlpanel/period_form.html new file mode 100644 index 0000000..0d5bbe6 --- /dev/null +++ b/controlpanel/templates/controlpanel/period_form.html @@ -0,0 +1,12 @@ +{% extends "controlpanel/base.html" %} + +{% block heading %}{% if club.is_archived %}Reactivate{% else %}Open period{% endif %} — {{ club }}{% endblock heading %} + +{% block subheading %} +

Next period starts {{ next_start|date:"j M Y" }} unless you say otherwise.

+{% endblock subheading %} + +{% block panel %} + {% url 'controlpanel:club_detail' club.pk as club_url %} + {% include "controlpanel/_billing_form.html" with cancel_url=club_url submit_label="Open period" submit_icon="calendar-plus" blurb="By default the period continues from the end of the last one, so a lapsed year is still owed. Pick a start date to forgive the gap." %} +{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/subscription_form.html b/controlpanel/templates/controlpanel/subscription_form.html new file mode 100644 index 0000000..0557502 --- /dev/null +++ b/controlpanel/templates/controlpanel/subscription_form.html @@ -0,0 +1,8 @@ +{% extends "controlpanel/base.html" %} + +{% block heading %}{% if subscription %}Change plan{% else %}Start billing{% endif %} — {{ club }}{% endblock heading %} + +{% block panel %} + {% url 'controlpanel:club_detail' club.pk as club_url %} + {% include "controlpanel/_billing_form.html" with cancel_url=club_url submit_label="Save plan" submit_icon="layers" blurb="Changing tier does not re-bill: the current period keeps the amount it was issued at, and the new rate applies from the next one." %} +{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/tier_form.html b/controlpanel/templates/controlpanel/tier_form.html new file mode 100644 index 0000000..d38cb77 --- /dev/null +++ b/controlpanel/templates/controlpanel/tier_form.html @@ -0,0 +1,8 @@ +{% extends "controlpanel/base.html" %} + +{% block heading %}{% if object %}Edit {{ object }}{% else %}New tier{% endif %}{% endblock heading %} + +{% block panel %} + {% url 'controlpanel:billing' as billing_url %} + {% include "controlpanel/_billing_form.html" with cancel_url=billing_url submit_label="Save tier" submit_icon="layers" blurb="A tier has no price until you add one. A tier with no price cannot be billed." %} +{% endblock panel %} diff --git a/controlpanel/templates/controlpanel/tier_price_form.html b/controlpanel/templates/controlpanel/tier_price_form.html new file mode 100644 index 0000000..5802ebf --- /dev/null +++ b/controlpanel/templates/controlpanel/tier_price_form.html @@ -0,0 +1,8 @@ +{% extends "controlpanel/base.html" %} + +{% block heading %}New price for {{ tier }}{% endblock heading %} + +{% block panel %} + {% url 'controlpanel:billing' as billing_url %} + {% include "controlpanel/_billing_form.html" with cancel_url=billing_url submit_label="Add price" submit_icon="euro" blurb="Prices are dated, never edited: periods already opened keep the amount they were billed at, so this cannot rewrite an invoice you have already sent." %} +{% endblock panel %} diff --git a/controlpanel/tests.py b/controlpanel/tests.py index c40a4df..444151e 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -1,6 +1,7 @@ import datetime import pathlib from decimal import Decimal +from unittest import mock from allauth.mfa.models import Authenticator from django import forms @@ -14,6 +15,9 @@ from django.urls import reverse from django.utils import timezone from waffle import get_waffle_flag_model, get_waffle_switch_model +from billing.models import GRACE_DAYS, Due, Tier, TierPrice +from billing.services import BillingError +from billing.services.dues import record_payment, subscribe from club.models import Club, ClubMembership, ClubRole, Season from events.models import Attendance, Event from members.models import Member @@ -730,11 +734,13 @@ class PlatformChartTests(TestCase): self.assertEqual(platform_charts()["signups"][-1]["new"], 1) - def test_only_paid_orders_count_as_revenue(self): + def test_only_paid_orders_count_as_club_revenue(self): + # `club_revenue` is members paying their clubs. It is NOT platform income, which is + # why it no longer shares a chart (or a name) with our dues. Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("50.00"), status=Order.OrderStatus.PAID) Order.objects.create(club=self.club, purchaser=self.member, total=Decimal("30.00"), status=Order.OrderStatus.PENDING) - self.assertEqual(platform_charts()["revenue"][-1]["value"], 50.0) + self.assertEqual(platform_charts()["club_revenue"][-1]["value"], 50.0) self.assertEqual(platform_attention()["outstanding"], Decimal("30.00")) @@ -1055,3 +1061,257 @@ class TemplateCommentTests(TestCase): self.assertTrue(templates) # the glob must actually be finding our templates self.assertEqual(offenders, [], "use {% comment %} for multi-line comments") + + +class PlatformDuesMetricTests(TestCase): + """What the clubs owe US — kept strictly apart from what members owe their clubs.""" + + def setUp(self): + self.today = timezone.localdate() + self.club = Club.objects.create(name="Ajax United") + self.tier = Tier.objects.create(name="Standard") + TierPrice.objects.create(tier=self.tier, active_from=self.today - datetime.timedelta(days=1200), amount=Decimal("500.00")) + + def test_dues_owed_is_the_unpaid_balance_across_every_club(self): + subscribe(self.club, self.tier) + record_payment(self.club.dues.first(), Decimal("200.00")) + + self.assertEqual(platform_attention()["dues_owed"], Decimal("300.00")) + + def test_grace_and_overdue_are_counted_separately(self): + in_grace = Club.objects.create(name="Grace FC") + overdue = Club.objects.create(name="Overdue FC") + subscribe(in_grace, self.tier, start=self.today - datetime.timedelta(days=370)) + subscribe(overdue, self.tier, start=self.today - datetime.timedelta(days=365 + GRACE_DAYS + 10)) + + attention = platform_attention() + + self.assertEqual(attention["dues_in_grace"], 1) + self.assertEqual(attention["dues_overdue"], 1) + + def test_clubs_on_no_tier_are_flagged(self): + self.assertEqual(platform_attention()["clubs_unbilled"], 1) + + subscribe(self.club, self.tier) + + self.assertEqual(platform_attention()["clubs_unbilled"], 0) + + def test_platform_dues_and_club_shop_money_are_different_charts(self): + subscribe(self.club, self.tier) + record_payment(self.club.dues.first(), Decimal("500.00")) + + charts = platform_charts() + + self.assertEqual(charts["dues"][-1]["value"], 500.0) + self.assertEqual(charts["club_revenue"][-1]["value"], 0.0) # never ours + + def test_the_health_table_carries_the_plan_and_what_is_owed(self): + subscribe(self.club, self.tier) + + club = clubs_with_health().get(pk=self.club.pk) + + self.assertEqual(club.tier_name, "Standard") + self.assertEqual(club.dues_owed, Decimal("500.00")) + + def test_the_health_table_still_costs_one_query_with_billing_on_it(self): + subscribe(self.club, self.tier) + subscribe(Club.objects.create(name="Feyenoord"), self.tier) + + with self.assertNumQueries(1): + [(club.tier_name, club.dues_owed, club.outstanding) for club in clubs_with_health()] + + +class BillingPanelTests(ControlPanelTestBase): + def setUp(self): + super().setUp() + self.today = timezone.localdate() + self.tier = Tier.objects.create(name="Standard") + TierPrice.objects.create(tier=self.tier, active_from=self.today - datetime.timedelta(days=1200), amount=Decimal("500.00")) + + def test_the_billing_page_lists_tiers_and_what_is_owed(self): + subscribe(self.club, self.tier) + + response = self.client.get(reverse("controlpanel:billing")) + + self.assertContains(response, "Standard") + self.assertContains(response, "500.00") + + def test_a_tier_can_be_created_and_priced(self): + self.client.post(reverse("controlpanel:tier_create"), {"name": "Large", "description": "", "is_active": "on"}) + tier = Tier.objects.get(name="Large") + + self.client.post(reverse("controlpanel:tier_price_create", args=[tier.pk]), {"active_from": self.today.isoformat(), "amount": "900.00"}) + + self.assertEqual(tier.price_on(self.today), Decimal("900.00")) + + def test_a_rate_change_does_not_rewrite_an_open_period(self): + subscribe(self.club, self.tier) + + self.client.post(reverse("controlpanel:tier_price_create", args=[self.tier.pk]), {"active_from": self.today.isoformat(), "amount": "900.00"}) + + self.assertEqual(self.club.dues.first().amount, Decimal("500.00")) + + def test_subscribing_a_club_opens_its_first_period(self): + self.client.post(reverse("controlpanel:club_subscribe", args=[self.club.pk]), {"tier": self.tier.pk, "auto_archive": "on", "notes": ""}) + + self.assertEqual(self.club.dues.count(), 1) + self.assertEqual(self.club.subscription.tier, self.tier) + + def test_a_payment_can_be_recorded_and_settles_the_due(self): + subscribe(self.club, self.tier) + due = self.club.dues.first() + + self.client.post(reverse("controlpanel:due_pay", args=[due.pk]), {"amount": "500.00", "method": "bank_transfer", "reference": "TRX-1", "paid_at": "", "note": ""}) + + due.refresh_from_db() + self.assertEqual(due.status, Due.Status.PAID) + self.assertEqual(due.payments.first().recorded_by, self.staff) + + def test_a_part_payment_leaves_a_balance(self): + subscribe(self.club, self.tier) + due = self.club.dues.first() + + self.client.post(reverse("controlpanel:due_pay", args=[due.pk]), {"amount": "200.00", "method": "bank_transfer", "reference": "", "paid_at": "", "note": ""}) + + due.refresh_from_db() + self.assertEqual(due.balance, Decimal("300.00")) + + def test_a_billing_error_is_shown_rather_than_raised(self): + # A waived period cannot take a payment; the panel must say so, not 500. + subscribe(self.club, self.tier) + due = self.club.dues.first() + self.client.post(reverse("controlpanel:due_waive", args=[due.pk])) + + response = self.client.post(reverse("controlpanel:due_pay", args=[due.pk]), {"amount": "50.00", "method": "cash", "reference": "", "paid_at": "", "note": ""}, follow=True) + + self.assertContains(response, "cannot take a payment") + + def test_a_period_can_be_waived(self): + subscribe(self.club, self.tier) + due = self.club.dues.first() + + self.client.post(reverse("controlpanel:due_waive", args=[due.pk])) + + due.refresh_from_db() + self.assertEqual(due.status, Due.Status.WAIVED) + + def test_opening_a_period_continues_from_the_last_one(self): + subscribe(self.club, self.tier, start=self.today - datetime.timedelta(days=400)) + first = self.club.dues.first() + + self.client.post(reverse("controlpanel:club_open_period", args=[self.club.pk]), {"start": ""}) + + latest = self.club.dues.order_by("-period_start").first() + self.assertEqual(latest.period_start, first.period_end + datetime.timedelta(days=1)) + + def test_reactivating_an_archived_club_restores_it(self): + subscribe(self.club, self.tier, start=self.today - datetime.timedelta(days=400)) + self.club.archive() + + self.client.post(reverse("controlpanel:club_open_period", args=[self.club.pk]), {"start": self.today.isoformat()}) + + self.club.refresh_from_db() + self.assertFalse(self.club.is_archived) + + def test_the_club_page_shows_the_plan_and_its_periods(self): + subscribe(self.club, self.tier) + + response = self.client.get(reverse("controlpanel:club_detail", args=[self.club.pk])) + + self.assertContains(response, "Standard") + self.assertContains(response, "INV-") + + def test_an_invoice_downloads_as_a_pdf(self): + subscribe(self.club, self.tier) + due = self.club.dues.first() + + with mock.patch("controlpanel.views.invoice_pdf", return_value=b"%PDF-1.7 fake"): + response = self.client.get(reverse("controlpanel:due_invoice", args=[due.pk])) + + self.assertEqual(response["Content-Type"], "application/pdf") + self.assertIn(due.invoice.number, response["Content-Disposition"]) + + def test_a_missing_pdf_library_is_reported_rather_than_a_500(self): + # WeasyPrint needs native libs. Without them the button must explain itself. + subscribe(self.club, self.tier) + due = self.club.dues.first() + + with mock.patch("controlpanel.views.invoice_pdf", side_effect=BillingError("PDF rendering needs the native pango/cairo libraries.")): + response = self.client.get(reverse("controlpanel:due_invoice", args=[due.pk]), follow=True) + + self.assertContains(response, "pango") + + +class BillingFormRenderTests(ControlPanelTestBase): + def setUp(self): + super().setUp() + self.today = timezone.localdate() + self.tier = Tier.objects.create(name="Standard") + TierPrice.objects.create(tier=self.tier, active_from=self.today - datetime.timedelta(days=1200), amount=Decimal("500.00")) + + def test_the_billing_forms_render(self): + subscribe(self.club, self.tier) + due = self.club.dues.first() + + for url in ( + reverse("controlpanel:tier_create"), + reverse("controlpanel:tier_update", args=[self.tier.pk]), + reverse("controlpanel:tier_price_create", args=[self.tier.pk]), + reverse("controlpanel:club_subscribe", args=[self.club.pk]), + reverse("controlpanel:club_open_period", args=[self.club.pk]), + reverse("controlpanel:due_pay", args=[due.pk]), + ): + self.assertEqual(self.client.get(url).status_code, 200, url) + + def test_the_payment_form_defaults_to_the_outstanding_balance(self): + subscribe(self.club, self.tier) + due = self.club.dues.first() + record_payment(due, Decimal("200.00")) + due.refresh_from_db() + + response = self.client.get(reverse("controlpanel:due_pay", args=[due.pk])) + + self.assertEqual(response.context["form"].initial["amount"], Decimal("300.00")) + + def test_a_tier_can_be_renamed(self): + self.client.post(reverse("controlpanel:tier_update", args=[self.tier.pk]), {"name": "Standard plus", "description": "", "is_active": "on"}) + + self.tier.refresh_from_db() + self.assertEqual(self.tier.name, "Standard plus") + + def test_changing_tier_leaves_the_open_period_alone(self): + # The current period keeps the amount it was issued at; the new rate bites next time. + subscribe(self.club, self.tier) + premium = Tier.objects.create(name="Premium") + TierPrice.objects.create(tier=premium, active_from=self.today, amount=Decimal("900.00")) + + response = self.client.post(reverse("controlpanel:club_subscribe", args=[self.club.pk]), {"tier": premium.pk, "auto_archive": "on", "notes": ""}, follow=True) + + self.club.refresh_from_db() + self.assertEqual(self.club.subscription.tier, premium) + self.assertEqual(self.club.dues.first().amount, Decimal("500.00")) + self.assertContains(response, "keeps the amount it was billed at") + + def test_subscribing_to_an_unpriced_tier_reports_itself(self): + unpriced = Tier.objects.create(name="Enterprise") + + response = self.client.post(reverse("controlpanel:club_subscribe", args=[self.club.pk]), {"tier": unpriced.pk, "auto_archive": "on", "notes": ""}, follow=True) + + self.assertContains(response, "no price in force") + + def test_billing_a_period_twice_reports_itself(self): + subscribe(self.club, self.tier) + start = self.club.dues.first().period_start + + response = self.client.post(reverse("controlpanel:club_open_period", args=[self.club.pk]), {"start": start.isoformat()}, follow=True) + + self.assertContains(response, "already billed") + + def test_waiving_a_paid_period_reports_itself(self): + subscribe(self.club, self.tier) + due = self.club.dues.first() + record_payment(due, Decimal("500.00")) + + response = self.client.post(reverse("controlpanel:due_waive", args=[due.pk]), follow=True) + + self.assertContains(response, "remove them before waiving") diff --git a/controlpanel/urls.py b/controlpanel/urls.py index dc1e25d..db81799 100644 --- a/controlpanel/urls.py +++ b/controlpanel/urls.py @@ -21,6 +21,16 @@ urlpatterns = [ path("features/flags/new/", views.FlagCreateView.as_view(), name="flag_create"), path("features/flags//edit/", views.FlagUpdateView.as_view(), name="flag_update"), path("features/switches//toggle/", views.SwitchToggleView.as_view(), name="switch_toggle"), + # Billing (platform charging the clubs) + path("billing/", views.BillingView.as_view(), name="billing"), + path("billing/tiers/new/", views.TierCreateView.as_view(), name="tier_create"), + path("billing/tiers//edit/", views.TierUpdateView.as_view(), name="tier_update"), + path("billing/tiers//prices/new/", views.TierPriceCreateView.as_view(), name="tier_price_create"), + path("billing/dues//pay/", views.RecordPaymentView.as_view(), name="due_pay"), + path("billing/dues//waive/", views.WaiveDueView.as_view(), name="due_waive"), + path("billing/dues//invoice.pdf", views.InvoicePdfView.as_view(), name="due_invoice"), + path("clubs//subscription/", views.SubscribeClubView.as_view(), name="club_subscribe"), + path("clubs//period/new/", views.OpenPeriodView.as_view(), name="club_open_period"), # Platform admins (superusers only) path("admins/", views.PlatformAdminListView.as_view(), name="admins"), path("admins/add/", views.PlatformAdminAddView.as_view(), name="admin_add"), diff --git a/controlpanel/views.py b/controlpanel/views.py index eebf84c..a26ec66 100644 --- a/controlpanel/views.py +++ b/controlpanel/views.py @@ -1,13 +1,20 @@ from django.contrib import messages from django.contrib.auth import get_user_model +from django.db.models import Count +from django.http import HttpResponse from django.shortcuts import get_object_or_404, redirect from django.urls import reverse +from django.utils import timezone from django.views.generic import CreateView, DetailView, FormView, ListView, TemplateView, UpdateView, View from waffle import get_waffle_flag_model, get_waffle_switch_model +from billing.models import Due, Tier, TierPrice +from billing.services import BillingError +from billing.services.dues import next_period_start, open_period, reactivate, record_payment, subscribe, waive +from billing.services.invoices import invoice_pdf, issue_invoice from club.models import Club, ClubRole -from .forms import ClubAdminForm, ClubForm, FlagForm, PlatformAdminForm +from .forms import ClubAdminForm, ClubForm, DuePaymentForm, FlagForm, OpenPeriodForm, PlatformAdminForm, SubscriptionForm, TierForm, TierPriceForm from .mixins import PlatformStaffRequiredMixin, PlatformSuperuserRequiredMixin from .services.admins import grant_club_admin, revoke_club_admin from .services.platform_admins import ( @@ -35,6 +42,7 @@ class DashboardView(PlatformStaffRequiredMixin, TemplateView): flags=flag_adoption(), charts=platform_charts(), clubs=clubs_with_health(), + today=timezone.localdate(), **kwargs, ) @@ -55,7 +63,7 @@ class ClubListView(PlatformStaffRequiredMixin, ListView): return clubs_with_health(clubs) def get_context_data(self, **kwargs): - return super().get_context_data(nav="clubs", show_archived=self.show_archived, search=self.request.GET.get("q", ""), **kwargs) + return super().get_context_data(nav="clubs", show_archived=self.show_archived, search=self.request.GET.get("q", ""), today=timezone.localdate(), **kwargs) class ClubCreateView(PlatformStaffRequiredMixin, CreateView): @@ -97,6 +105,9 @@ class ClubDetailView(PlatformStaffRequiredMixin, DetailView): groups=club_statistics(self.object), attention=club_attention(self.object), charts=club_charts(self.object), + subscription=getattr(self.object, "subscription", None), + dues=self.object.dues.select_related("tier", "invoice").prefetch_related("payments"), + today=timezone.localdate(), admins=ClubRole.objects.filter(club=self.object, role=ClubRole.Roles.ADMIN).select_related("member", "member__user"), flags=flags_for_club(self.object), **kwargs, @@ -283,3 +294,198 @@ class PlatformAdminRevokeView(PlatformSuperuserRequiredMixin, View): else: messages.warning(request, f"{user.email} no longer has platform access.") return redirect("controlpanel:admins") + + +class BillingView(PlatformStaffRequiredMixin, TemplateView): + """Tiers and their prices, plus every period we are owed money for.""" + + template_name = "controlpanel/billing.html" + + def get_context_data(self, **kwargs): + today = timezone.localdate() + return super().get_context_data( + nav="billing", + tiers=Tier.objects.prefetch_related("prices").annotate(club_count=Count("subscriptions")), + owing=Due.objects.filter(status__in=Due.OWING).select_related("club", "tier").order_by("grace_until"), + today=today, + **kwargs, + ) + + +class TierCreateView(PlatformStaffRequiredMixin, CreateView): + model = Tier + form_class = TierForm + template_name = "controlpanel/tier_form.html" + + def get_context_data(self, **kwargs): + return super().get_context_data(nav="billing", **kwargs) + + def get_success_url(self): + messages.success(self.request, f"Tier “{self.object}” created. Give it a price before billing anyone.") + return reverse("controlpanel:billing") + + +class TierUpdateView(PlatformStaffRequiredMixin, UpdateView): + model = Tier + form_class = TierForm + template_name = "controlpanel/tier_form.html" + + def get_context_data(self, **kwargs): + return super().get_context_data(nav="billing", **kwargs) + + def get_success_url(self): + messages.success(self.request, f"Tier “{self.object}” updated.") + return reverse("controlpanel:billing") + + +class TierPriceCreateView(PlatformStaffRequiredMixin, CreateView): + """A rate change is a new dated price, never an edit of the old one — periods already + billed keep the amount they were billed at.""" + + model = TierPrice + form_class = TierPriceForm + template_name = "controlpanel/tier_price_form.html" + + @property + def tier(self): + return get_object_or_404(Tier, pk=self.kwargs["pk"]) + + def get_context_data(self, **kwargs): + return super().get_context_data(nav="billing", tier=self.tier, **kwargs) + + def form_valid(self, form): + form.instance.tier = self.tier + response = super().form_valid(form) + messages.success(self.request, f"{self.tier} is €{self.object.amount} for periods opening from {self.object.active_from}.") + return response + + def get_success_url(self): + return reverse("controlpanel:billing") + + +class SubscribeClubView(PlatformStaffRequiredMixin, FormView): + """Put a club on a tier, which opens its first period.""" + + form_class = SubscriptionForm + template_name = "controlpanel/subscription_form.html" + + @property + def club(self): + return get_object_or_404(Club, pk=self.kwargs["pk"]) + + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + subscription = getattr(self.club, "subscription", None) + if subscription: + kwargs["instance"] = subscription + return kwargs + + def get_context_data(self, **kwargs): + return super().get_context_data(nav="clubs", club=self.club, subscription=getattr(self.club, "subscription", None), **kwargs) + + def form_valid(self, form): + club = self.club + existing = getattr(club, "subscription", None) + try: + if existing: + # Changing tier does not re-bill: the current period keeps the amount it was + # issued at, and the new rate applies from the next one. + subscription = form.save(commit=False) + subscription.club = club + subscription.save() + messages.success(self.request, f"{club} is now on {subscription.tier}. The current period keeps the amount it was billed at.") + else: + subscribe(club, form.cleaned_data["tier"], start=form.cleaned_data.get("start"), auto_archive=form.cleaned_data["auto_archive"]) + messages.success(self.request, f"{club} is on {form.cleaned_data['tier']}. Its first period is open.") + except BillingError as error: + messages.error(self.request, str(error)) + + return redirect("controlpanel:club_detail", pk=club.pk) + + +class RecordPaymentView(PlatformStaffRequiredMixin, FormView): + form_class = DuePaymentForm + template_name = "controlpanel/payment_form.html" + + @property + def due(self): + return get_object_or_404(Due.objects.select_related("club", "tier"), pk=self.kwargs["pk"]) + + def get_initial(self): + return {"amount": self.due.balance} + + def get_context_data(self, **kwargs): + return super().get_context_data(nav="clubs", due=self.due, **kwargs) + + def form_valid(self, form): + due = self.due + try: + record_payment( + due, + form.cleaned_data["amount"], + method=form.cleaned_data["method"], + reference=form.cleaned_data["reference"], + paid_at=form.cleaned_data["paid_at"], + note=form.cleaned_data["note"], + user=self.request.user, + ) + due.refresh_from_db() + messages.success(self.request, f"€{form.cleaned_data['amount']} recorded. {due.get_status_display().capitalize()} — €{due.balance} outstanding.") + except BillingError as error: + messages.error(self.request, str(error)) + + return redirect("controlpanel:club_detail", pk=due.club_id) + + +class WaiveDueView(PlatformStaffRequiredMixin, View): + def post(self, request, pk): + due = get_object_or_404(Due, pk=pk) + try: + waive(due) + messages.warning(request, f"Period {due.period_start} to {due.period_end} waived. Nothing is owed and the club will not be archived for it.") + except BillingError as error: + messages.error(request, str(error)) + + return redirect("controlpanel:club_detail", pk=due.club_id) + + +class OpenPeriodView(PlatformStaffRequiredMixin, FormView): + """Renew a club, or reactivate an archived one.""" + + form_class = OpenPeriodForm + template_name = "controlpanel/period_form.html" + + @property + def club(self): + return get_object_or_404(Club, pk=self.kwargs["pk"]) + + def get_context_data(self, **kwargs): + club = self.club + return super().get_context_data(nav="clubs", club=club, next_start=next_period_start(club), **kwargs) + + def form_valid(self, form): + club = self.club + start = form.cleaned_data.get("start") + try: + due = reactivate(club, start=start) if club.is_archived else open_period(club, start=start) + messages.success(self.request, f"Period {due.period_start} to {due.period_end} opened for €{due.amount}. Invoice {due.invoice.number}.") + except BillingError as error: + messages.error(self.request, str(error)) + + return redirect("controlpanel:club_detail", pk=club.pk) + + +class InvoicePdfView(PlatformStaffRequiredMixin, View): + def get(self, request, pk): + due = get_object_or_404(Due.objects.select_related("club", "tier", "invoice"), pk=pk) + invoice = issue_invoice(due) + try: + pdf = invoice_pdf(invoice) + except BillingError as error: + # The native PDF libraries are missing: say so rather than 500. + messages.error(request, str(error)) + return redirect("controlpanel:club_detail", pk=due.club_id) + + response = HttpResponse(pdf, content_type="application/pdf") + response["Content-Disposition"] = f'attachment; filename="{invoice.number}.pdf"' + return response diff --git a/static/css/app.css b/static/css/app.css index 13066f5..dc3dfde 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -3195,6 +3195,9 @@ .hidden { display: none; } + .inline { + display: inline; + } .inline-block { display: inline-block; } @@ -3558,6 +3561,9 @@ .pb-2 { padding-bottom: calc(var(--spacing) * 2); } + .pl-8 { + padding-left: calc(var(--spacing) * 8); + } .text-center { text-align: center; } @@ -3839,6 +3845,12 @@ --badge-fg: var(--color-error-content); } } + .badge-info { + @layer daisyui.l1.l2 { + --badge-color: var(--color-info); + --badge-fg: var(--color-info-content); + } + } .badge-neutral { @layer daisyui.l1.l2 { --badge-color: var(--color-neutral); From e5a93194bf543cb8077dfdfd986e7f3b626c9ff3 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 09:37:36 +0200 Subject: [PATCH 065/137] Make the app deployable: Postgres, shared cache, S3, HTTPS Three things would have broken a deploy, all invisible until it happened: - psycopg was missing. dj-database-url parses postgres:// happily, so the app would have started and died on its first query. - No CACHES, so Django used LocMemCache -- private to one process. waffle caches each flag's targeting there, so under several gunicorn workers a toggle flipped in the control panel flushes ONE worker and the others keep serving the stale flag. That is a feature that "sometimes doesn't turn on", and it makes Redis a requirement of the first multi-worker deploy, not of the second server. - Uploads (club logos) sat on local disk. Fine on one box; on two, a logo uploaded to node A 404s on node B. Storage now switches to S3 the moment a bucket is configured, so adding a server stays a config change. HTTPS behind a proxy: SECURE_PROXY_SSL_HEADER is not optional once Caddy terminates TLS -- without it Django thinks every request is plain HTTP, request.is_secure() is false, WebAuthn disagrees with the browser about the origin, and SECURE_SSL_REDIRECT turns into a loop. HSTS covers subdomains, because every club is one. The SSL/cookie flags default to off and are switched on by the production environment on purpose: defaulting them to `not DEBUG` would redirect every test request to https and break the suite wherever DEBUG is unset. `check --deploy` is what catches a deploy that forgot them. Static files are served by WhiteNoise from the app itself, so a second app server needs no shared volume or CDN. Manifest storage is production-only: it demands a collectstatic manifest that no test run has. Co-Authored-By: Claude Opus 4.8 --- pyproject.toml | 5 ++ rosterchief/settings.py | 57 ++++++++++++- uv.lock | 172 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c541f1c..f45a80e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,11 +8,16 @@ dependencies = [ "django-allauth[mfa]>=65.18.0", "django-lucide", "django-phonenumber-field[phonenumbers]>=8.4.0", + "django-redis>=7.0.0", + "django-storages[s3]>=1.14.6", "django-waffle>=5.0.0", + "gunicorn>=26.0.0", "pillow>=12.3.0", + "psycopg[binary]>=3.3.4", "python-dateutil>=2.9.0.post0", "python-decouple>=3.8", "weasyprint>=69.0", + "whitenoise>=6.12.0", ] [dependency-groups] diff --git a/rosterchief/settings.py b/rosterchief/settings.py index 4deccc0..972833c 100644 --- a/rosterchief/settings.py +++ b/rosterchief/settings.py @@ -75,6 +75,10 @@ WAFFLE_FLAG_MODEL = "features.Flag" MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", + # Directly after SecurityMiddleware, per WhiteNoise's contract. It serves the collected + # static files from the app itself, so no shared volume or CDN is needed to add a second + # app server. + "whitenoise.middleware.WhiteNoiseMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", @@ -209,7 +213,20 @@ USE_I18N = True USE_TZ = True -# Static files (CSS, JavaScript, Images) +# Cache +# +# Redis in production, and not merely for speed: waffle caches each flag's targeting in the +# Django cache, and LocMemCache is private to one process. Under several gunicorn workers a +# toggle flipped in the control panel flushes ONE worker's cache while the others keep +# serving the stale flag — a feature that "sometimes doesn't turn on". A shared cache is the +# fix, so Redis is required from the first multi-worker deploy, not from the second server. + +REDIS_URL = config("DJANGO_REDIS_URL", default="") + +CACHES = {"default": {"BACKEND": "django_redis.cache.RedisCache", "LOCATION": REDIS_URL, "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"}} if REDIS_URL else {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}} + + +# Static files and uploads # https://docs.djangoproject.com/en/6.0/howto/static-files/ STATIC_URL = "static/" @@ -219,6 +236,44 @@ STATICFILES_DIRS = [BASE_DIR / "static"] MEDIA_URL = "media/" MEDIA_ROOT = BASE_DIR / "media" +# Uploads (club logos) go to S3-compatible storage as soon as a bucket is configured. On one +# server the local disk works; on two, a logo uploaded to node A 404s on node B — so this is +# the switch that decides whether "add a server" is an afternoon or a migration. +AWS_STORAGE_BUCKET_NAME = config("AWS_STORAGE_BUCKET_NAME", default="") +AWS_S3_ENDPOINT_URL = config("AWS_S3_ENDPOINT_URL", default="") # set for Hetzner/Scaleway/Backblaze +AWS_S3_REGION_NAME = config("AWS_S3_REGION_NAME", default="") +AWS_ACCESS_KEY_ID = config("AWS_ACCESS_KEY_ID", default="") +AWS_SECRET_ACCESS_KEY = config("AWS_SECRET_ACCESS_KEY", default="") +AWS_S3_FILE_OVERWRITE = False +AWS_QUERYSTRING_AUTH = False # logos are public; signed URLs would break browser caching + +STORAGES = { + "default": {"BACKEND": "storages.backends.s3.S3Storage"} if AWS_STORAGE_BUCKET_NAME else {"BACKEND": "django.core.files.storage.FileSystemStorage"}, + # Manifest storage only in production: it demands a collectstatic manifest, and every + # {% static %} in a test would blow up without one. + "staticfiles": {"BACKEND": config("DJANGO_STATICFILES_BACKEND", default="django.contrib.staticfiles.storage.StaticFilesStorage")}, +} + + +# HTTPS, behind a reverse proxy +# +# SECURE_PROXY_SSL_HEADER is not optional here: Caddy terminates TLS, so without it Django +# believes every request is plain HTTP. request.is_secure() goes false, allauth and WebAuthn +# disagree with the browser about the origin, and SECURE_SSL_REDIRECT becomes a loop. +SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") + +# Off by default and switched on by the production env, deliberately: defaulting these to +# `not DEBUG` would redirect every test request to https and break the suite anywhere DEBUG +# is unset. `manage.py check --deploy` is what catches a deploy that forgot them. +SECURE_SSL_REDIRECT = config("DJANGO_SECURE_SSL_REDIRECT", default=False, cast=bool) +SESSION_COOKIE_SECURE = config("DJANGO_SESSION_COOKIE_SECURE", default=False, cast=bool) +CSRF_COOKIE_SECURE = config("DJANGO_CSRF_COOKIE_SECURE", default=False, cast=bool) + +SECURE_HSTS_SECONDS = config("DJANGO_SECURE_HSTS_SECONDS", default=0, cast=int) +# Every club is a subdomain, so HSTS must cover them all or it protects only the bare domain. +SECURE_HSTS_INCLUDE_SUBDOMAINS = config("DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS", default=True, cast=bool) +SECURE_HSTS_PRELOAD = config("DJANGO_SECURE_HSTS_PRELOAD", default=False, cast=bool) + # Phone numbers (django-phonenumber-field) diff --git a/uv.lock b/uv.lock index 74e172a..bbb70be 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" }, ] +[[package]] +name = "boto3" +version = "1.43.47" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, + { name = "jmespath" }, + { name = "s3transfer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/76/8c2b062b9a9f27db8d78c3183213efd2cb31493b9a81853025da5254652e/boto3-1.43.47.tar.gz", hash = "sha256:09a8ce4abab4391bf08315e2dcc449b4b33e97ce7654ee9398d9fe4ef73a33da", size = 112681, upload-time = "2026-07-13T19:32:13.511Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/86/4aaa23e1433a6f4021258b1d8e4ec129bf691e419339454806807595a93d/boto3-1.43.47-py3-none-any.whl", hash = "sha256:aef0cccd5bc4a443fb3e64fada023f7b5b9da36864cb4255b68cf93067476676", size = 140031, upload-time = "2026-07-13T19:32:11.072Z" }, +] + +[[package]] +name = "botocore" +version = "1.43.47" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/2c/279bf51f68e85a12323996aa4a7f2a163da84dad949ee751caa318928ce1/botocore-1.43.47.tar.gz", hash = "sha256:9e04d8da7f9cff8a911b14284829f78b74e1ce785444833199837decb5ecc17a", size = 15696311, upload-time = "2026-07-13T19:32:01.095Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/28/c0/6a8f824d613c5e5b8755ce99e3cf83fd1db135f5df02da7ed5bf106abdfd/botocore-1.43.47-py3-none-any.whl", hash = "sha256:4d16559a3a1866fa7b9e651dd1422c1a033497e069f8a73cae5eebafe70ff39c", size = 15382537, upload-time = "2026-07-13T19:31:56.213Z" }, +] + [[package]] name = "brotli" version = "1.2.0" @@ -300,6 +328,36 @@ phonenumbers = [ { name = "phonenumbers" }, ] +[[package]] +name = "django-redis" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "redis" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/78/203a0cdc0f1c083a5407d01f77b58099a120bb8a5a04562f56a9bb341314/django_redis-7.0.0.tar.gz", hash = "sha256:e48491c862f4350b0747ceb1016700686fb93c4f4e0fb9c490fe6c6658ffd933", size = 64601, upload-time = "2026-06-02T14:17:48.819Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/9f/09cdb9a1eebe8533b02a7694ca787acfc1e4d93b5b6175ff99366d4e6d64/django_redis-7.0.0-py3-none-any.whl", hash = "sha256:4b23aa6e0cd0937bb1242e9a463809e6004de3ca2150f34e986306bb6220d688", size = 38932, upload-time = "2026-06-02T14:17:47.281Z" }, +] + +[[package]] +name = "django-storages" +version = "1.14.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/d6/2e50e378fff0408d558f36c4acffc090f9a641fd6e084af9e54d45307efa/django_storages-1.14.6.tar.gz", hash = "sha256:7a25ce8f4214f69ac9c7ce87e2603887f7ae99326c316bc8d2d75375e09341c9", size = 87587, upload-time = "2025-04-02T02:34:55.103Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/21/3cedee63417bc5553eed0c204be478071c9ab208e5e259e97287590194f1/django_storages-1.14.6-py3-none-any.whl", hash = "sha256:11b7b6200e1cb5ffcd9962bd3673a39c7d6a6109e8096f0e03d46fab3d3aabd9", size = 33095, upload-time = "2025-04-02T02:34:53.291Z" }, +] + +[package.optional-dependencies] +s3 = [ + { name = "boto3" }, +] + [[package]] name = "django-waffle" version = "5.0.0" @@ -356,6 +414,36 @@ woff = [ { name = "zopfli" }, ] +[[package]] +name = "gunicorn" +version = "26.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/b7/a4a3f632f823e432ce6bc65f62961b7980c898c77f075a2f7118cb3846fe/gunicorn-26.0.0.tar.gz", hash = "sha256:ca9346f85e3a4aeeb64d491045c16b9a35647abd37ea15efe53080eb8b090baf", size = 727286, upload-time = "2026-05-05T06:38:25.529Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/40/9c2384fc2be4ad25dd4a49decd5ad9ea5a3639814c11bd40ab77cb9f0a14/gunicorn-26.0.0-py3-none-any.whl", hash = "sha256:40233d26a5f0d1872916188c276e21641155111c2853f0c2cd55260aec0d24fc", size = 212009, upload-time = "2026-05-05T06:38:23.007Z" }, +] + +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + [[package]] name = "phonenumbers" version = "9.0.33" @@ -415,6 +503,41 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, ] +[[package]] +name = "psycopg" +version = "3.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/2f/cb91e5502ec9de1de6f1b76cfbf69531932725361168bb06963620c77e2e/psycopg-3.3.4.tar.gz", hash = "sha256:e21207764952cff81b6b8bdacad9a3939f2793367fdac2987b3aac36a651b5bc", size = 165799, upload-time = "2026-05-01T23:31:55.179Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/e0/7b3dee031daae7743609ce3c746565d4a3ed7c2c186479eb48e34e838c64/psycopg-3.3.4-py3-none-any.whl", hash = "sha256:b6bbc25ccf05c8fad3b061d9db2ef0909a555171b84b07f29458a447253d679a", size = 213001, upload-time = "2026-05-01T23:20:50.816Z" }, +] + +[package.optional-dependencies] +binary = [ + { name = "psycopg-binary", marker = "implementation_name != 'pypy'" }, +] + +[[package]] +name = "psycopg-binary" +version = "3.3.4" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/a6/828c9185701dab71b234c2a76c38a08b098ebfec5020716b4e93807492b5/psycopg_binary-3.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:28b7398fdd19db3232c884fb24550bdfe951221f510e195e233299e4c9b78f97", size = 4607292, upload-time = "2026-05-01T23:30:38.962Z" }, + { url = "https://files.pythonhosted.org/packages/92/58/5b40dbc9d839045c9dae956960e4fb6d20bcabe6c59a2aa34fc3a371913f/psycopg_binary-3.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1fbaa292a3c8bb61b45df1ad3da1908ccee7cb889db9425e3557d9e34e2a4829", size = 4687023, upload-time = "2026-05-01T23:30:47.227Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/793f0ac107a9003b48441d0d1f9f616d96e0f37458dd8dc12528ceff55fb/psycopg_binary-3.3.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94596f9e7633ee3f6440711d43bb70aa31cc0a46a900ab8b4201a366ace5c9e7", size = 5486985, upload-time = "2026-05-01T23:30:55.517Z" }, + { url = "https://files.pythonhosted.org/packages/8f/26/42e8533497e2592334f68ec529cf5f840f7fa4e99575a4bb61aa184dbfbf/psycopg_binary-3.3.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8c0056529e68dbe9184cd4019a1f3d8f3a4ead2f6fc7a5afcf27d3314edd1277", size = 5168745, upload-time = "2026-05-01T23:31:01.904Z" }, + { url = "https://files.pythonhosted.org/packages/15/af/b7151776cc08d5935d45c833ec818a9beb417cf7c08239af1aafbdae78ee/psycopg_binary-3.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c09aad7051326e7603c14e50636db9c01f78272dc54b3accff03d46370461e6", size = 6761486, upload-time = "2026-05-01T23:31:14.511Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ed/c92533b9124712d592cbf1cd6c76da933a2e0acea81dfe1fbe7e735f0cff/psycopg_binary-3.3.4-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:514404ed543efd620c85602b747df2a23cf1241b4067199e1a66f2d2757aaa41", size = 4997427, upload-time = "2026-05-01T23:31:20.901Z" }, + { url = "https://files.pythonhosted.org/packages/a2/23/ccadfd0de416aa188356daa199453af24087b042e296088706d190ae0295/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:46893c26858be12cc49ca4226ed6a60b4bfccadd946b3bebb783a60b38788228", size = 4533549, upload-time = "2026-05-01T23:31:26.204Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a0/c8f43cee36386f7bc891ab41a9d31ea07cf9826038e732da79f26b1e5f34/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:df1d567fc430f6df15c9fcf67d87685fc49bdb325adc0db5af1adfb2f44eb5c9", size = 4210256, upload-time = "2026-05-01T23:31:33.884Z" }, + { url = "https://files.pythonhosted.org/packages/4e/2c/c1547871be3790676e8868b38655496422f94f0978dfb66b74bdba2f1676/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:6b9016b1714da4dd5ecaaa75b82098aa5a0b87854ce9b092e21c27c4ae23e014", size = 3946204, upload-time = "2026-05-01T23:31:39.626Z" }, + { url = "https://files.pythonhosted.org/packages/c4/b1/f6670f00fa7ea601584623f6c11602ab92117d83eaff885e0210f6de7418/psycopg_binary-3.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:47c656a8a7ba6eb0cff1801a4caaa9c8bdc12d03080e273aff1c8ac39971a77e", size = 4255811, upload-time = "2026-05-01T23:31:44.986Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e6/5fff07a70d1f945ed90ae131c3bd76cab32beff7c58c6db15ad5820b6d1f/psycopg_binary-3.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:c37e024c07308cd06cf3ec51bfd0e7f6157585a4d84d1bce4a7f5f7913719bf8", size = 3666849, upload-time = "2026-05-01T23:31:51.165Z" }, +] + [[package]] name = "pycparser" version = "3.0" @@ -475,6 +598,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dd/b8/d2d6d731733f51684bbf76bf34dab3b70a9148e8f2cef2bb544fccec681a/qrcode-8.2-py3-none-any.whl", hash = "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f", size = 45986, upload-time = "2025-05-01T15:44:22.781Z" }, ] +[[package]] +name = "redis" +version = "8.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/c3/928b290c2c0ca99ab96eea5b4ff8f30be8112b075301a7d3ba214a3c8c12/redis-8.0.1.tar.gz", hash = "sha256:afc5a7a2f5a084f5b1880dec548dd45be17db7e43c82a30d84f952aefb05cfb0", size = 5114170, upload-time = "2026-06-23T14:52:37.728Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/0a/c2345ebf1ebe70840ce3f6c6ee612f8fa749cfbd1b03069c53bf0c62aaad/redis-8.0.1-py3-none-any.whl", hash = "sha256:47daa35a058c23468d6437f17a8c76882cb316b838ef763036af99b96cedd743", size = 502406, upload-time = "2026-06-23T14:52:36.137Z" }, +] + [[package]] name = "rosterchief" version = "0.1.0" @@ -485,11 +617,16 @@ dependencies = [ { name = "django-allauth", extra = ["mfa"] }, { name = "django-lucide" }, { name = "django-phonenumber-field", extra = ["phonenumbers"] }, + { name = "django-redis" }, + { name = "django-storages", extra = ["s3"] }, { name = "django-waffle" }, + { name = "gunicorn" }, { name = "pillow" }, + { name = "psycopg", extra = ["binary"] }, { name = "python-dateutil" }, { name = "python-decouple" }, { name = "weasyprint" }, + { name = "whitenoise" }, ] [package.dev-dependencies] @@ -506,11 +643,16 @@ requires-dist = [ { name = "django-allauth", extras = ["mfa"], specifier = ">=65.18.0" }, { name = "django-lucide", git = "https://github.com/bsiebens/lucide" }, { name = "django-phonenumber-field", extras = ["phonenumbers"], specifier = ">=8.4.0" }, + { name = "django-redis", specifier = ">=7.0.0" }, + { name = "django-storages", extras = ["s3"], specifier = ">=1.14.6" }, { name = "django-waffle", specifier = ">=5.0.0" }, + { name = "gunicorn", specifier = ">=26.0.0" }, { name = "pillow", specifier = ">=12.3.0" }, + { name = "psycopg", extras = ["binary"], specifier = ">=3.3.4" }, { name = "python-dateutil", specifier = ">=2.9.0.post0" }, { name = "python-decouple", specifier = ">=3.8" }, { name = "weasyprint", specifier = ">=69.0" }, + { name = "whitenoise", specifier = ">=6.12.0" }, ] [package.metadata.requires-dev] @@ -545,6 +687,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2d/c7/c53e8dbff9c9dc4b7928773421ae294a5d28fcb8dcda1a089579d3a7e510/ruff-0.15.17-py3-none-win_arm64.whl", hash = "sha256:f3be1fbb34bcdfd146240d8fb92a709d4c2c8191348580a3c044ec60fa0b4456", size = 11355275, upload-time = "2026-06-11T17:54:43.635Z" }, ] +[[package]] +name = "s3transfer" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/65/da/4bef7ce7bb989b222aa4785a413896dbec53306dfc59c6ce7d16a7ffbd6a/s3transfer-0.19.1.tar.gz", hash = "sha256:d3d6371dc3f1e5c5427b2b457bcf13bcf87bec334c95aed18642eae61f6926f3", size = 165354, upload-time = "2026-07-10T19:32:04.849Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/23/e84c64ad0e8bc59cd1b2ef98def848deff0ef3456c542afe74d51e9e8c85/s3transfer-0.19.1-py3-none-any.whl", hash = "sha256:d5fd7005ee39307455ad5f310b5ea67f4b1960d7fed5b3671ee50c249de675de", size = 90072, upload-time = "2026-07-10T19:32:03.673Z" }, +] + [[package]] name = "six" version = "1.17.0" @@ -596,6 +750,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, ] +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] + [[package]] name = "weasyprint" version = "69.0" @@ -624,6 +787,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, ] +[[package]] +name = "whitenoise" +version = "6.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/2a/55b3f3a4ec326cd077c1c3defeee656b9298372a69229134d930151acd01/whitenoise-6.12.0.tar.gz", hash = "sha256:f723ebb76a112e98816ff80fcea0a6c9b8ecde835f8ddda25df7a30a3c2db6ad", size = 26841, upload-time = "2026-02-27T00:05:42.028Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/eb/d5583a11486211f3ebd4b385545ae787f32363d453c19fffd81106c9c138/whitenoise-6.12.0-py3-none-any.whl", hash = "sha256:fc5e8c572e33ebf24795b47b6a7da8da3c00cff2349f5b04c02f28d0cc5a3cc2", size = 20302, upload-time = "2026-02-27T00:05:40.086Z" }, +] + [[package]] name = "zopfli" version = "0.4.3" From 35d1ec45a7fa1b4a255de5b02b98a9d00198f2d0 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 09:41:51 +0200 Subject: [PATCH 066/137] Containerise: Dockerfile, Compose stack and wildcard TLS One server now, the same image and env vars for many later: point DJANGO_DATABASE_URL / DJANGO_REDIS_URL at central services, set a bucket, drop the db and redis services, run several web containers behind a load balancer. No code changes. The wildcard certificate is what shapes this. Subdomain tenancy needs *.rosterchief.app, and Let's Encrypt will not issue a wildcard over HTTP-01 -- only DNS-01 -- so Caddy is built with a DNS provider plugin and needs an API token. That single constraint is why the proxy is Caddy rather than the usual nginx+certbot. The image apt-installs libpango and friends, which is what WeasyPrint binds to. The PDF invoices that cannot render on a Mac without Homebrew work in the container by construction. Migrations are NOT run by the entrypoint: with more than one web container they would race, and a starting gunicorn worker is a bad place to discover a failed migration. Deploy runs them once, explicitly. Two things the local build check caught, either of which would have failed the image build at collectstatic (manifest storage treats a missing referenced file as fatal): - chart.js ended with a sourceMappingURL pointing at a .map we never vendored. Stripped, with an npm script so re-vendoring cannot bring it back. - The Tailwind INPUT file lived at static/src/app.css, inside the served static tree, so collectstatic collected it and then choked on its @import "tailwindcss". It belongs outside: it is a build input, not an asset. Now assets/app.css. Verified locally under gunicorn + WhiteNoise + manifest storage: pages serve and the CSS comes back hashed. The image itself is unverified -- there is no container runtime on this machine. Co-Authored-By: Claude Opus 4.8 --- .dockerignore | 13 + .env.compose.example | 10 + .env.production.example | 38 +++ .gitignore | 1 + Dockerfile | 73 ++++++ {static/src => assets}/app.css | 5 +- compose.yaml | 72 ++++++ deploy/caddy/Caddyfile | 24 ++ deploy/caddy/Dockerfile | 9 + package.json | 5 +- static/css/app.css | 459 +++++++++++++++++++++++++++++++++ static/js/chart.js | 1 - 12 files changed, 705 insertions(+), 5 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.compose.example create mode 100644 .env.production.example create mode 100644 Dockerfile rename {static/src => assets}/app.css (99%) create mode 100644 compose.yaml create mode 100644 deploy/caddy/Caddyfile create mode 100644 deploy/caddy/Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..54cdb12 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +.git +.venv +node_modules +staticfiles +media +db.sqlite3 +.env +*.pyc +__pycache__ +.coverage +.idea +.ruff_cache +ARCHITECTURE.pdf diff --git a/.env.compose.example b/.env.compose.example new file mode 100644 index 0000000..247d84c --- /dev/null +++ b/.env.compose.example @@ -0,0 +1,10 @@ +# Copy to .env — read by docker compose itself (not by Django). +ROSTERCHIEF_BASE_DOMAIN=rosterchief.app +ACME_EMAIL=you@example.com + +# DNS-01 is the only way to get the *.rosterchief.app wildcard. Token needs DNS:Edit on the zone. +CLOUDFLARE_API_TOKEN= + +POSTGRES_DB=rosterchief +POSTGRES_USER=rosterchief +POSTGRES_PASSWORD= diff --git a/.env.production.example b/.env.production.example new file mode 100644 index 0000000..5a86d9b --- /dev/null +++ b/.env.production.example @@ -0,0 +1,38 @@ +# Copy to .env.production and fill in. Everything here is read by python-decouple. + +# --- Django --- +DJANGO_SECRET_KEY= # python -c "import secrets; print(secrets.token_urlsafe(64))" +DJANGO_DEBUG=False +# The leading dot matches every club subdomain. +DJANGO_ALLOWED_HOSTS=.rosterchief.app +DJANGO_CSRF_TRUSTED_ORIGINS=https://rosterchief.app,https://*.rosterchief.app +DJANGO_TIME_ZONE=Europe/Brussels + +# --- Tenancy --- +# Drives subdomain resolution, the shared session cookie, and the WebAuthn RP ID (one passkey +# across every club). +ROSTERCHIEF_BASE_DOMAIN=rosterchief.app +ROSTERCHIEF_RP_NAME=RosterChief + +# --- Services --- +DJANGO_DATABASE_URL=postgres://rosterchief:CHANGEME@db:5432/rosterchief +DJANGO_REDIS_URL=redis://redis:6379/0 + +# --- HTTPS (off by default in code; the deploy is what turns them on) --- +DJANGO_SECURE_SSL_REDIRECT=True +DJANGO_SESSION_COOKIE_SECURE=True +DJANGO_CSRF_COOKIE_SECURE=True +DJANGO_SECURE_HSTS_SECONDS=31536000 +DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS=True +# Preload is a one-way door — turn it on only once the wildcard cert has proven itself. +DJANGO_SECURE_HSTS_PRELOAD=False + +# --- Static --- +DJANGO_STATICFILES_BACKEND=whitenoise.storage.CompressedManifestStaticFilesStorage + +# --- Uploads: set these and club logos move off local disk (required for >1 app server) --- +# AWS_STORAGE_BUCKET_NAME=rosterchief-media +# AWS_S3_ENDPOINT_URL=https://fsn1.your-objectstorage.com +# AWS_S3_REGION_NAME=fsn1 +# AWS_ACCESS_KEY_ID= +# AWS_SECRET_ACCESS_KEY= diff --git a/.gitignore b/.gitignore index ae60515..b987436 100644 --- a/.gitignore +++ b/.gitignore @@ -379,3 +379,4 @@ pyrightconfig.json # End of https://www.toptal.com/developers/gitignore/api/python,pycharm,django% # Node node_modules/ +staticfiles/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7a9f6e6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,73 @@ +# syntax=docker/dockerfile:1 + +# --- 1. the stylesheet ------------------------------------------------------- +# Tailwind is a build-time concern: the CSS it emits is committed, but building it here means +# the image never depends on someone having remembered to run `npm run build`. +FROM node:22-slim AS css + +WORKDIR /build +COPY package.json package-lock.json ./ +RUN npm ci +COPY assets ./assets +COPY templates ./templates +COPY controlpanel ./controlpanel +COPY billing ./billing +RUN npm run build + + +# --- 2. the runtime ---------------------------------------------------------- +FROM python:3.14-slim AS app + +# WeasyPrint binds to these at import: no pango, no invoices. This is also why building the +# PDF path in a container is easier than on a Mac — apt has what Homebrew would have to. +RUN apt-get update && apt-get install --no-install-recommends -y \ + libpango-1.0-0 \ + libpangoft2-1.0-0 \ + libharfbuzz0b \ + libffi8 \ + libjpeg62-turbo \ + libopenjp2-7 \ + shared-mime-info \ + curl \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv + +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + UV_COMPILE_BYTECODE=1 \ + UV_LINK_MODE=copy \ + PATH="/app/.venv/bin:$PATH" + +WORKDIR /app + +# Dependencies first: they change far less often than the code, so this layer caches. +COPY pyproject.toml uv.lock ./ +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-dev --no-install-project + +COPY . . +COPY --from=css /build/static/css/app.css ./static/css/app.css + +RUN --mount=type=cache,target=/root/.cache/uv uv sync --frozen --no-dev + +# collectstatic needs a settings module that imports: a throwaway key, never used at runtime. +RUN DJANGO_SECRET_KEY=build-only-not-a-secret \ + DJANGO_STATICFILES_BACKEND=whitenoise.storage.CompressedManifestStaticFilesStorage \ + python manage.py collectstatic --noinput + +RUN useradd --system --uid 1000 rosterchief && chown -R rosterchief /app +USER rosterchief + +EXPOSE 8000 + +# Migrations are NOT run here. With more than one app container they would race, and a failed +# migration inside a starting web process is a bad place to find out — deploy runs them once, +# explicitly (see DEPLOYMENT.md). +CMD ["gunicorn", "rosterchief.wsgi:application", \ + "--bind", "0.0.0.0:8000", \ + "--workers", "3", \ + "--threads", "4", \ + "--timeout", "60", \ + "--access-logfile", "-", \ + "--error-logfile", "-"] diff --git a/static/src/app.css b/assets/app.css similarity index 99% rename from static/src/app.css rename to assets/app.css index bb45435..a69239b 100644 --- a/static/src/app.css +++ b/assets/app.css @@ -2,8 +2,9 @@ /* Scan Django templates for utility classes (Tailwind's auto-detection doesn't know about our template dirs). */ -@source "../../templates"; -@source "../../controlpanel"; +@source "../templates"; +@source "../controlpanel"; +@source "../billing"; /* daisyUI: light is the default, dark applies automatically when the OS asks for it. An explicit data-theme on (set by the toggle) overrides both. */ diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..53210c9 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,72 @@ +# One server. The same image and the same environment variables run a multi-server +# deployment: point DJANGO_DATABASE_URL / DJANGO_REDIS_URL at your central services, set a +# bucket, drop the `db` and `redis` services, and run several `web` containers behind a load +# balancer. Nothing in the code changes. + +name: rosterchief + +services: + caddy: + build: + context: ./deploy/caddy + restart: unless-stopped + ports: + - "80:80" + - "443:443" + - "443:443/udp" + environment: + # A wildcard certificate for *.rosterchief.app cannot be issued over HTTP-01 — Let's + # Encrypt only does wildcards via DNS-01. That is why Caddy needs a DNS API token, and + # why this image is built with the provider's DNS plugin rather than pulled as-is. + ROSTERCHIEF_BASE_DOMAIN: ${ROSTERCHIEF_BASE_DOMAIN:?set the base domain, e.g. rosterchief.app} + ACME_EMAIL: ${ACME_EMAIL:?set an email for Let's Encrypt} + CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN:?DNS-01 needs an API token with DNS:Edit on the zone} + volumes: + - ./deploy/caddy/Caddyfile:/etc/caddy/Caddyfile:ro + - caddy_data:/data + - caddy_config:/config + depends_on: + - web + + web: + build: . + restart: unless-stopped + env_file: .env.production + depends_on: + db: + condition: service_healthy + redis: + condition: service_started + healthcheck: + test: ["CMD", "curl", "-fsS", "http://localhost:8000/healthz"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 20s + + db: + image: postgres:17-alpine + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB:-rosterchief} + POSTGRES_USER: ${POSTGRES_USER:-rosterchief} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set a database password} + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-rosterchief}"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + restart: unless-stopped + command: ["redis-server", "--save", "", "--appendonly", "no"] + # Cache only, so nothing here needs to survive a restart. It is not optional though: it + # is what keeps every gunicorn worker agreeing about which feature flags are on. + +volumes: + pgdata: + caddy_data: + caddy_config: diff --git a/deploy/caddy/Caddyfile b/deploy/caddy/Caddyfile new file mode 100644 index 0000000..dd1c219 --- /dev/null +++ b/deploy/caddy/Caddyfile @@ -0,0 +1,24 @@ +{ + email {$ACME_EMAIL} +} + +# The bare domain (control panel, admin, auth) and every club subdomain, on one certificate. +{$ROSTERCHIEF_BASE_DOMAIN}, *.{$ROSTERCHIEF_BASE_DOMAIN} { + tls { + dns cloudflare {$CLOUDFLARE_API_TOKEN} + } + + encode zstd gzip + + # X-Forwarded-Proto is what SECURE_PROXY_SSL_HEADER reads. Without it Django believes every + # request is plain HTTP: request.is_secure() goes false, WebAuthn disagrees with the browser + # about the origin, and the SSL redirect becomes a loop. + reverse_proxy web:8000 { + header_up X-Forwarded-Proto {scheme} + header_up X-Real-IP {remote_host} + } + + log { + output stdout + } +} diff --git a/deploy/caddy/Dockerfile b/deploy/caddy/Dockerfile new file mode 100644 index 0000000..5d1b48d --- /dev/null +++ b/deploy/caddy/Dockerfile @@ -0,0 +1,9 @@ +# Caddy with a DNS plugin. The stock image cannot solve a DNS-01 challenge, and DNS-01 is the +# only way Let's Encrypt issues the *.rosterchief.app wildcard that subdomain tenancy needs. +# +# Swap the module if your DNS lives elsewhere: caddy-dns/route53, caddy-dns/digitalocean, ... +FROM caddy:2-builder AS builder +RUN xcaddy build --with github.com/caddy-dns/cloudflare + +FROM caddy:2 +COPY --from=builder /usr/bin/caddy /usr/bin/caddy diff --git a/package.json b/package.json index 0f40fe9..1b7712c 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,9 @@ "private": true, "type": "module", "scripts": { - "build": "tailwindcss -i ./static/src/app.css -o ./static/css/app.css --minify", - "watch": "tailwindcss -i ./static/src/app.css -o ./static/css/app.css --watch" + "build": "tailwindcss -i ./assets/app.css -o ./static/css/app.css --minify", + "watch": "tailwindcss -i ./assets/app.css -o ./static/css/app.css --watch", + "vendor:chart": "node -e \"const fs=require('fs');const s=fs.readFileSync('node_modules/chart.js/dist/chart.umd.js','utf8').split('\\n').filter(l=>!l.startsWith('//# sourceMappingURL=')).join('\\n');fs.writeFileSync('static/js/chart.js',s)\"" }, "devDependencies": { "@fontsource-variable/jetbrains-mono": "^5.2.8", diff --git a/static/css/app.css b/static/css/app.css index dc3dfde..c3cf6d0 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -37,7 +37,9 @@ --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); --default-font-family: var(--font-sans); --default-mono-font-family: var(--font-mono); + --font-ubuntu: "Ubuntu", ui-sans-serif, system-ui, sans-serif; --font-roboto: "Roboto", ui-sans-serif, system-ui, sans-serif; + --font-tourney: "Tourney", ui-sans-serif, system-ui, sans-serif; } } @layer base { @@ -189,6 +191,67 @@ } } @layer utilities { + .diff { + @layer daisyui.l1.l2 { + position: relative; + display: grid; + width: 100%; + overflow: hidden; + webkit-user-select: none; + user-select: none; + align-items: normal; + grid-template-rows: 1fr 1.8rem 1fr; + direction: ltr; + container-type: inline-size; + grid-template-columns: auto 1fr; + &:focus-visible, &:has(.diff-item-1:focus-visible) { + outline-style: var(--tw-outline-style); + outline-width: 2px; + outline-offset: 1px; + outline-color: var(--color-base-content); + } + &:focus-visible { + outline-style: var(--tw-outline-style); + outline-width: 2px; + outline-offset: 1px; + outline-color: var(--color-base-content); + .diff-resizer { + min-width: 95cqi; + max-width: 95cqi; + } + } + &:has(.diff-item-1:focus-visible) { + outline-style: var(--tw-outline-style); + outline-width: 2px; + outline-offset: 1px; + .diff-resizer { + min-width: 5cqi; + max-width: 5cqi; + } + } + &:hover { + .diff-item-2 { + &::after { + height: 2.4rem; + } + } + } + @supports (-webkit-overflow-scrolling: touch) and (overflow: -webkit-paged-x) { + &:focus { + .diff-resizer { + min-width: 5cqi; + max-width: 5cqi; + } + } + &:has(.diff-item-1:focus) { + .diff-resizer { + min-width: 95cqi; + max-width: 95cqi; + } + } + } + } + } .tooltip { @layer daisyui.l1.l2.l3 { position: relative; @@ -726,6 +789,57 @@ } } } + .diff-item-2 { + @layer daisyui.l1.l2.l3 { + position: relative; + grid-column-start: 1; + grid-row: span 3 / span 3; + grid-row-start: 1; + &:after { + pointer-events: none; + position: absolute; + top: calc(1 / 2 * 100%); + right: 1px; + bottom: 0; + z-index: 2; + border-radius: calc(infinity * 1px); + background-color: var(--color-base-100); + @supports (color: color-mix(in lab, red, red)) { + background-color: color-mix(in oklab, var(--color-base-100) 98%, transparent); + } + width: 1.2rem; + height: 1.8rem; + border: 2px solid var(--color-base-100); + content: ""; + box-shadow: 0 0 0 2px #0000002a; + outline: 2px solid var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + outline: 2px solid color-mix(in oklab, var(--color-base-content) 10%, #0000); + } + outline-offset: -3px; + translate: 50% -50%; + transition: height 0.3s linear(0, 0.931 13.8%, 1.196 21.4%, 1.343 29.8%, 1.378 36%, 1.365 43.2%, 1.059 78%, 1); + } + > * { + pointer-events: none; + position: absolute; + top: 0; + bottom: 0; + left: 0; + height: 100%; + width: 100cqi; + max-width: none; + object-fit: cover; + object-position: center; + } + @supports (-webkit-overflow-scrolling: touch) and (overflow: -webkit-paged-x) { + &:after { + --tw-content: none; + content: var(--tw-content); + } + } + } + } .dropdown { @layer daisyui.l1.l2.l3 { position: relative; @@ -974,6 +1088,23 @@ } } } + .loading { + @layer daisyui.l1.l2.l3 { + pointer-events: none; + display: inline-block; + aspect-ratio: 1 / 1; + background-color: currentcolor; + vertical-align: middle; + width: calc(var(--size-selector, 0.25rem) * 6); + mask-size: 100%; + mask-repeat: no-repeat; + mask-position: center; + mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='8s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='6s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='6s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); + @media (prefers-reduced-motion: no-preference) { + mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); + } + } + } .collapse { &:not(td, tr, colgroup) { visibility: revert-layer; @@ -1141,6 +1272,44 @@ } } } + .\!filter { + @layer daisyui.l1.l2.l3 { + display: flex !important; + flex-wrap: wrap !important; + [type="radio"] { + width: auto !important; + } + input { + overflow: hidden !important; + opacity: 100% !important; + scale: 1 !important; + transition: visibility 0.1s allow-discrete, margin 0.1s, opacity 0.3s, padding 0.3s, border-width 0.1s !important; + &.filter-reset { + aspect-ratio: 1 / 1 !important; + &::after { + --tw-content: "×" !important; + content: var(--tw-content) !important; + } + } + } + > input:not(:last-child), > :not(:last-child) input { + margin-inline-end: 0.25rem !important; + } + } + @layer daisyui.l1 { + &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible) { + visibility: hidden !important; + } + &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible), &:not(:has(:focus-visible)):has(:checked:not(.filter-reset, [type="checkbox"])) :is(input, button):not(:checked, .filter-reset, [type="reset"]) { + margin-inline: 0 !important; + width: 0 !important; + padding-inline: 0 !important; + opacity: 0% !important; + scale: 0 !important; + border-width: 0 !important; + } + } + } .filter { @layer daisyui.l1.l2.l3 { display: flex; @@ -1179,6 +1348,36 @@ } } } + .validator-hint { + @layer daisyui.l1.l2.l3 { + visibility: hidden; + margin-top: calc(0.25rem * 2); + font-size: 0.75rem; + } + } + .validator { + @layer daisyui.l1.l2.l3 { + &:user-valid, &:has(:user-valid) { + &, &:focus, &:checked, &[aria-checked="true"], &:focus-within { + --input-color: var(--color-success); + } + } + &:user-invalid, &:has(:user-invalid), &[aria-invalid]:not([aria-invalid="false"]), &:has([aria-invalid]:not([aria-invalid="false"])) { + &, &:focus, &:checked, &[aria-checked="true"], &:focus-within { + --input-color: var(--color-error); + } + & ~ .validator-hint { + visibility: visible; + color: var(--color-error); + } + } + } + &:user-invalid, &:has(:user-invalid), &[aria-invalid]:not([aria-invalid="false"]), &:has([aria-invalid]:not([aria-invalid="false"])) { + & ~ .validator-hint { + display: revert-layer; + } + } + } .collapse-open { @layer daisyui.l1.l2 { grid-template-rows: max-content 1fr; @@ -1550,6 +1749,105 @@ } } } + .range { + @layer daisyui.l1.l2.l3 { + appearance: none; + webkit-appearance: none; + --range-thumb: var(--color-base-100); + --range-thumb-size: calc(var(--size-selector, 0.25rem) * 6); + --range-progress: currentColor; + --range-fill: 1; + --range-p: 0.25rem; + --range-bg: currentColor; + @supports (color: color-mix(in lab, red, red)) { + --range-bg: color-mix(in oklab, currentColor 10%, #0000); + } + --range-fill-x: calc( + (var(--range-dir, 1) * -100cqw) - (var(--range-dir, 1) * var(--range-thumb-size) / 2) + ); + --range-fill-y: 0; + --range-fill-spread: calc(100cqw * var(--range-fill)); + cursor: pointer; + overflow: hidden; + background-color: transparent; + vertical-align: middle; + width: clamp(3rem, 20rem, 100%); + --radius-selector-max: calc( + var(--radius-selector) + var(--radius-selector) + var(--radius-selector) + ); + border-radius: calc(var(--radius-selector) + min(var(--range-p), var(--radius-selector-max))); + border: none; + height: var(--range-thumb-size); + [dir="rtl"] & { + --range-dir: -1; + } + &:focus { + outline: none; + } + &:focus-visible { + outline: 2px solid; + outline-offset: 2px; + } + &::-webkit-slider-runnable-track { + width: 100%; + background-color: var(--range-bg); + border-radius: var(--radius-selector); + height: calc(var(--range-thumb-size) * 0.5); + } + @media (forced-colors: active) { + &::-webkit-slider-runnable-track { + border: 1px solid; + } + } + @media (forced-colors: active) { + &::-moz-range-track { + border: 1px solid; + } + } + &::-webkit-slider-thumb { + position: relative; + box-sizing: border-box; + border-radius: calc(var(--radius-selector) + min(var(--range-p), var(--radius-selector-max))); + background-color: var(--range-thumb); + height: var(--range-thumb-size); + width: var(--range-thumb-size); + border: var(--range-p) solid; + appearance: none; + webkit-appearance: none; + inset-block-start: 50%; + color: var(--range-progress); + transform: translateY(-50%); + box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px currentColor, 0 0 0 2rem var(--range-thumb) inset, var(--range-fill-x) var(--range-fill-y) 0 var(--range-fill-spread); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000), 0 0 0 2rem var(--range-thumb) inset, var(--range-fill-x) var(--range-fill-y) 0 var(--range-fill-spread); + } + } + &::-moz-range-track { + width: 100%; + background-color: var(--range-bg); + border-radius: var(--radius-selector); + height: calc(var(--range-thumb-size) * 0.5); + } + &::-moz-range-thumb { + position: relative; + box-sizing: border-box; + border-radius: calc(var(--radius-selector) + min(var(--range-p), var(--radius-selector-max))); + background-color: currentColor; + height: var(--range-thumb-size); + width: var(--range-thumb-size); + border: var(--range-p) solid; + color: var(--range-progress); + box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px currentColor, 0 0 0 2rem var(--range-thumb) inset, var(--range-fill-x) var(--range-fill-y) 0 var(--range-fill-spread); + @supports (color: color-mix(in lab, red, red)) { + box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000), 0 0 0 2rem var(--range-thumb) inset, var(--range-fill-x) var(--range-fill-y) 0 var(--range-fill-spread); + } + } + &:disabled { + cursor: not-allowed; + opacity: 30%; + } + } + } .indicator { @layer daisyui.l1.l2.l3 { position: relative; @@ -1791,6 +2089,27 @@ } } } + .diff-resizer { + @layer daisyui.l1.l2.l3 { + position: relative; + isolation: isolate; + z-index: 2; + grid-column-start: 1; + grid-row-start: 2; + height: calc(0.25rem * 3); + width: 50cqi; + max-width: calc(100cqi - 1rem); + min-width: 1rem; + resize: horizontal; + overflow: hidden; + opacity: 0%; + transform: scaleY(5) translate(0.32rem, 50%); + cursor: ew-resize; + transform-origin: 100% 100%; + clip-path: inset(calc(100% - 0.75rem) 0 0 calc(100% - 0.75rem)); + transition: min-width 0.3s ease-out, max-width 0.3s ease-out; + } + } .select { @layer daisyui.l1.l2.l3 { position: relative; @@ -1977,6 +2296,42 @@ } } } + .swap { + @layer daisyui.l1.l2 { + position: relative; + display: inline-grid; + cursor: pointer; + place-content: center; + vertical-align: middle; + webkit-user-select: none; + user-select: none; + input { + appearance: none; + border: none; + } + > * { + grid-column-start: 1; + grid-row-start: 1; + @media (prefers-reduced-motion: no-preference) { + transition-property: transform, rotate, opacity; + transition-duration: 0.2s; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + } + } + .swap-on, .swap-indeterminate, input:indeterminate ~ .swap-on { + opacity: 0%; + } + input:is(:checked, :indeterminate) { + & ~ .swap-off { + opacity: 0%; + } + } + input:checked ~ .swap-on, input:indeterminate ~ .swap-indeterminate { + opacity: 100%; + backface-visibility: visible; + } + } + } .collapse-title { @layer daisyui.l1.l2.l3 { grid-column-start: 1; @@ -2333,12 +2688,21 @@ } } } + .absolute { + position: absolute; + } .fixed { position: fixed; } + .relative { + position: relative; + } .static { position: static; } + .sticky { + position: sticky; + } .dropdown-right { @layer daisyui.l1.l2 { --anchor-h: right; @@ -2873,6 +3237,62 @@ .my-6 { margin-block: calc(var(--spacing) * 6); } + .breadcrumbs { + @layer daisyui.l1.l2.l3 { + max-width: 100%; + overflow-x: auto; + padding-block: calc(0.25rem * 2); + > menu, > ul, > ol { + display: flex; + min-height: min-content; + align-items: center; + white-space: nowrap; + > li { + display: flex; + align-items: center; + > * { + display: flex; + cursor: pointer; + align-items: center; + gap: calc(0.25rem * 2); + &:hover { + @media (hover: hover) { + text-decoration-line: underline; + } + } + &:focus { + --tw-outline-style: none; + outline-style: none; + @media (forced-colors: active) { + outline: 2px solid transparent; + outline-offset: 2px; + } + } + &:focus-visible { + outline: 2px solid currentColor; + outline-offset: 2px; + } + } + & + *:before { + content: ""; + margin-inline-start: calc(0.25rem * 2); + margin-inline-end: calc(0.25rem * 3); + display: block; + height: calc(0.25rem * 1.5); + width: calc(0.25rem * 1.5); + opacity: 40%; + rotate: 45deg; + border-top: 1px solid; + border-right: 1px solid; + background-color: #0000; + } + [dir="rtl"] & + *:before { + rotate: -135deg; + } + } + } + } + } .label { @layer daisyui.l1.l2.l3 { display: inline-flex; @@ -3101,6 +3521,16 @@ } } } + .fieldset { + @layer daisyui.l1.l2.l3 { + display: grid; + gap: calc(0.25rem * 1.5); + padding-block: 0.25rem; + font-size: 0.75rem; + grid-template-columns: 1fr; + grid-auto-rows: max-content; + } + } .card-actions { @layer daisyui.l1.l2.l3 { display: flex; @@ -3183,9 +3613,15 @@ } } } + .\!hidden { + display: none !important; + } .block { display: block; } + .contents { + display: contents; + } .flex { display: flex; } @@ -3210,6 +3646,12 @@ .table { display: table; } + .table-caption { + display: table-caption; + } + .table-cell { + display: table-cell; + } .h-16 { height: calc(var(--spacing) * 16); } @@ -3708,6 +4150,12 @@ .text-warning { color: var(--color-warning); } + .lowercase { + text-transform: lowercase; + } + .uppercase { + text-transform: uppercase; + } .tabular-nums { --tw-numeric-spacing: tabular-nums; font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,); @@ -3755,6 +4203,17 @@ outline-style: var(--tw-outline-style); outline-width: 1px; } + .blur { + --tw-blur: blur(8px); + filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); + } + .invert { + --tw-invert: invert(100%); + filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); + } + .\!filter { + filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,) !important; + } .filter { filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); } diff --git a/static/js/chart.js b/static/js/chart.js index 4cfce5c..4bd8b46 100644 --- a/static/js/chart.js +++ b/static/js/chart.js @@ -11,4 +11,3 @@ * (c) 2023 Jukka Kurkela * Released under the MIT License */function _t(t){return t+.5|0}const yt=(t,e,i)=>Math.max(Math.min(t,i),e);function vt(t){return yt(_t(2.55*t),0,255)}function Mt(t){return yt(_t(255*t),0,255)}function wt(t){return yt(_t(t/2.55)/100,0,1)}function kt(t){return yt(_t(100*t),0,100)}const St={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Pt=[..."0123456789ABCDEF"],Dt=t=>Pt[15&t],Ct=t=>Pt[(240&t)>>4]+Pt[15&t],Ot=t=>(240&t)>>4==(15&t);function At(t){var e=(t=>Ot(t.r)&&Ot(t.g)&&Ot(t.b)&&Ot(t.a))(t)?Dt:Ct;return t?"#"+e(t.r)+e(t.g)+e(t.b)+((t,e)=>t<255?e(t):"")(t.a,e):void 0}const Tt=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function Lt(t,e,i){const s=e*Math.min(i,1-i),n=(e,n=(e+t/30)%12)=>i-s*Math.max(Math.min(n-3,9-n,1),-1);return[n(0),n(8),n(4)]}function Et(t,e,i){const s=(s,n=(s+t/60)%6)=>i-i*e*Math.max(Math.min(n,4-n,1),0);return[s(5),s(3),s(1)]}function Rt(t,e,i){const s=Lt(t,1,.5);let n;for(e+i>1&&(n=1/(e+i),e*=n,i*=n),n=0;n<3;n++)s[n]*=1-e-i,s[n]+=e;return s}function It(t){const e=t.r/255,i=t.g/255,s=t.b/255,n=Math.max(e,i,s),o=Math.min(e,i,s),a=(n+o)/2;let r,l,h;return n!==o&&(h=n-o,l=a>.5?h/(2-n-o):h/(n+o),r=function(t,e,i,s,n){return t===n?(e-i)/s+(e>16&255,o>>8&255,255&o]}return t}(),Ht.transparent=[0,0,0,0]);const e=Ht[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:4===e.length?e[3]:255}}const $t=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;const Yt=t=>t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055,Ut=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4);function Xt(t,e,i){if(t){let s=It(t);s[e]=Math.max(0,Math.min(s[e]+s[e]*i,0===e?360:1)),s=Ft(s),t.r=s[0],t.g=s[1],t.b=s[2]}}function qt(t,e){return t?Object.assign(e||{},t):t}function Kt(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=Mt(t[3]))):(e=qt(t,{r:0,g:0,b:0,a:1})).a=Mt(e.a),e}function Gt(t){return"r"===t.charAt(0)?function(t){const e=$t.exec(t);let i,s,n,o=255;if(e){if(e[7]!==i){const t=+e[7];o=e[8]?vt(t):yt(255*t,0,255)}return i=+e[1],s=+e[3],n=+e[5],i=255&(e[2]?vt(i):yt(i,0,255)),s=255&(e[4]?vt(s):yt(s,0,255)),n=255&(e[6]?vt(n):yt(n,0,255)),{r:i,g:s,b:n,a:o}}}(t):Bt(t)}class Jt{constructor(t){if(t instanceof Jt)return t;const e=typeof t;let i;var s,n,o;"object"===e?i=Kt(t):"string"===e&&(o=(s=t).length,"#"===s[0]&&(4===o||5===o?n={r:255&17*St[s[1]],g:255&17*St[s[2]],b:255&17*St[s[3]],a:5===o?17*St[s[4]]:255}:7!==o&&9!==o||(n={r:St[s[1]]<<4|St[s[2]],g:St[s[3]]<<4|St[s[4]],b:St[s[5]]<<4|St[s[6]],a:9===o?St[s[7]]<<4|St[s[8]]:255})),i=n||jt(t)||Gt(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=qt(this._rgb);return t&&(t.a=wt(t.a)),t}set rgb(t){this._rgb=Kt(t)}rgbString(){return this._valid?(t=this._rgb)&&(t.a<255?`rgba(${t.r}, ${t.g}, ${t.b}, ${wt(t.a)})`:`rgb(${t.r}, ${t.g}, ${t.b})`):void 0;var t}hexString(){return this._valid?At(this._rgb):void 0}hslString(){return this._valid?function(t){if(!t)return;const e=It(t),i=e[0],s=kt(e[1]),n=kt(e[2]);return t.a<255?`hsla(${i}, ${s}%, ${n}%, ${wt(t.a)})`:`hsl(${i}, ${s}%, ${n}%)`}(this._rgb):void 0}mix(t,e){if(t){const i=this.rgb,s=t.rgb;let n;const o=e===n?.5:e,a=2*o-1,r=i.a-s.a,l=((a*r==-1?a:(a+r)/(1+a*r))+1)/2;n=1-l,i.r=255&l*i.r+n*s.r+.5,i.g=255&l*i.g+n*s.g+.5,i.b=255&l*i.b+n*s.b+.5,i.a=o*i.a+(1-o)*s.a,this.rgb=i}return this}interpolate(t,e){return t&&(this._rgb=function(t,e,i){const s=Ut(wt(t.r)),n=Ut(wt(t.g)),o=Ut(wt(t.b));return{r:Mt(Yt(s+i*(Ut(wt(e.r))-s))),g:Mt(Yt(n+i*(Ut(wt(e.g))-n))),b:Mt(Yt(o+i*(Ut(wt(e.b))-o))),a:t.a+i*(e.a-t.a)}}(this._rgb,t._rgb,e)),this}clone(){return new Jt(this.rgb)}alpha(t){return this._rgb.a=Mt(t),this}clearer(t){return this._rgb.a*=1-t,this}greyscale(){const t=this._rgb,e=_t(.3*t.r+.59*t.g+.11*t.b);return t.r=t.g=t.b=e,this}opaquer(t){return this._rgb.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return Xt(this._rgb,2,t),this}darken(t){return Xt(this._rgb,2,-t),this}saturate(t){return Xt(this._rgb,1,t),this}desaturate(t){return Xt(this._rgb,1,-t),this}rotate(t){return function(t,e){var i=It(t);i[0]=Vt(i[0]+e),i=Ft(i),t.r=i[0],t.g=i[1],t.b=i[2]}(this._rgb,t),this}}function Zt(t){if(t&&"object"==typeof t){const e=t.toString();return"[object CanvasPattern]"===e||"[object CanvasGradient]"===e}return!1}function Qt(t){return Zt(t)?t:new Jt(t)}function te(t){return Zt(t)?t:new Jt(t).saturate(.5).darken(.1).hexString()}const ee=["x","y","borderWidth","radius","tension"],ie=["color","borderColor","backgroundColor"];const se=new Map;function ne(t,e,i){return function(t,e){e=e||{};const i=t+JSON.stringify(e);let s=se.get(i);return s||(s=new Intl.NumberFormat(t,e),se.set(i,s)),s}(e,i).format(t)}const oe={values:t=>n(t)?t:""+t,numeric(t,e,i){if(0===t)return"0";const s=this.chart.options.locale;let n,o=t;if(i.length>1){const e=Math.max(Math.abs(i[0].value),Math.abs(i[i.length-1].value));(e<1e-4||e>1e15)&&(n="scientific"),o=function(t,e){let i=e.length>3?e[2].value-e[1].value:e[1].value-e[0].value;Math.abs(i)>=1&&t!==Math.floor(t)&&(i=t-Math.floor(t));return i}(t,i)}const a=z(Math.abs(o)),r=isNaN(a)?1:Math.max(Math.min(-1*Math.floor(a),20),0),l={notation:n,minimumFractionDigits:r,maximumFractionDigits:r};return Object.assign(l,this.options.ticks.format),ne(t,s,l)},logarithmic(t,e,i){if(0===t)return"0";const s=i[e].significand||t/Math.pow(10,Math.floor(z(t)));return[1,2,3,5,10,15].includes(s)||e>.8*i.length?oe.numeric.call(this,t,e,i):""}};var ae={formatters:oe};const re=Object.create(null),le=Object.create(null);function he(t,e){if(!e)return t;const i=e.split(".");for(let e=0,s=i.length;et.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(t,e)=>te(e.backgroundColor),this.hoverBorderColor=(t,e)=>te(e.borderColor),this.hoverColor=(t,e)=>te(e.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(e)}set(t,e){return ce(this,t,e)}get(t){return he(this,t)}describe(t,e){return ce(le,t,e)}override(t,e){return ce(re,t,e)}route(t,e,i,s){const n=he(this,t),a=he(this,i),r="_"+e;Object.defineProperties(n,{[r]:{value:n[e],writable:!0},[e]:{enumerable:!0,get(){const t=this[r],e=a[s];return o(t)?Object.assign({},e,t):l(t,e)},set(t){this[r]=t}}})}apply(t){t.forEach((t=>t(this)))}}var ue=new de({_scriptable:t=>!t.startsWith("on"),_indexable:t=>"events"!==t,hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[function(t){t.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),t.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>"onProgress"!==t&&"onComplete"!==t&&"fn"!==t}),t.set("animations",{colors:{type:"color",properties:ie},numbers:{type:"number",properties:ee}}),t.describe("animations",{_fallback:"animation"}),t.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>0|t}}}})},function(t){t.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})},function(t){t.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:ae.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),t.route("scale.ticks","color","","color"),t.route("scale.grid","color","","borderColor"),t.route("scale.border","color","","borderColor"),t.route("scale.title","color","","color"),t.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&"callback"!==t&&"parser"!==t,_indexable:t=>"borderDash"!==t&&"tickBorderDash"!==t&&"dash"!==t}),t.describe("scales",{_fallback:"scale"}),t.describe("scale.ticks",{_scriptable:t=>"backdropPadding"!==t&&"callback"!==t,_indexable:t=>"backdropPadding"!==t})}]);function fe(){return"undefined"!=typeof window&&"undefined"!=typeof document}function ge(t){let e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e}function pe(t,e,i){let s;return"string"==typeof t?(s=parseInt(t,10),-1!==t.indexOf("%")&&(s=s/100*e.parentNode[i])):s=t,s}const me=t=>t.ownerDocument.defaultView.getComputedStyle(t,null);function xe(t,e){return me(t).getPropertyValue(e)}const be=["top","right","bottom","left"];function _e(t,e,i){const s={};i=i?"-"+i:"";for(let n=0;n<4;n++){const o=be[n];s[o]=parseFloat(t[e+"-"+o+i])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}const ye=(t,e,i)=>(t>0||e>0)&&(!i||!i.shadowRoot);function ve(t,e){if("native"in t)return t;const{canvas:i,currentDevicePixelRatio:s}=e,n=me(i),o="border-box"===n.boxSizing,a=_e(n,"padding"),r=_e(n,"border","width"),{x:l,y:h,box:c}=function(t,e){const i=t.touches,s=i&&i.length?i[0]:t,{offsetX:n,offsetY:o}=s;let a,r,l=!1;if(ye(n,o,t.target))a=n,r=o;else{const t=e.getBoundingClientRect();a=s.clientX-t.left,r=s.clientY-t.top,l=!0}return{x:a,y:r,box:l}}(t,i),d=a.left+(c&&r.left),u=a.top+(c&&r.top);let{width:f,height:g}=e;return o&&(f-=a.width+r.width,g-=a.height+r.height),{x:Math.round((l-d)/f*i.width/s),y:Math.round((h-u)/g*i.height/s)}}const Me=t=>Math.round(10*t)/10;function we(t,e,i,s){const n=me(t),o=_e(n,"margin"),a=pe(n.maxWidth,t,"clientWidth")||T,r=pe(n.maxHeight,t,"clientHeight")||T,l=function(t,e,i){let s,n;if(void 0===e||void 0===i){const o=t&&ge(t);if(o){const t=o.getBoundingClientRect(),a=me(o),r=_e(a,"border","width"),l=_e(a,"padding");e=t.width-l.width-r.width,i=t.height-l.height-r.height,s=pe(a.maxWidth,o,"clientWidth"),n=pe(a.maxHeight,o,"clientHeight")}else e=t.clientWidth,i=t.clientHeight}return{width:e,height:i,maxWidth:s||T,maxHeight:n||T}}(t,e,i);let{width:h,height:c}=l;if("content-box"===n.boxSizing){const t=_e(n,"border","width"),e=_e(n,"padding");h-=e.width+t.width,c-=e.height+t.height}h=Math.max(0,h-o.width),c=Math.max(0,s?h/s:c-o.height),h=Me(Math.min(h,a,l.maxWidth)),c=Me(Math.min(c,r,l.maxHeight)),h&&!c&&(c=Me(h/2));return(void 0!==e||void 0!==i)&&s&&l.height&&c>l.height&&(c=l.height,h=Me(Math.floor(c*s))),{width:h,height:c}}function ke(t,e,i){const s=e||1,n=Me(t.height*s),o=Me(t.width*s);t.height=Me(t.height),t.width=Me(t.width);const a=t.canvas;return a.style&&(i||!a.style.height&&!a.style.width)&&(a.style.height=`${t.height}px`,a.style.width=`${t.width}px`),(t.currentDevicePixelRatio!==s||a.height!==n||a.width!==o)&&(t.currentDevicePixelRatio=s,a.height=n,a.width=o,t.ctx.setTransform(s,0,0,s,0,0),!0)}const Se=function(){let t=!1;try{const e={get passive(){return t=!0,!1}};fe()&&(window.addEventListener("test",null,e),window.removeEventListener("test",null,e))}catch(t){}return t}();function Pe(t,e){const i=xe(t,e),s=i&&i.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function De(t){return!t||s(t.size)||s(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}function Ce(t,e,i,s,n){let o=e[n];return o||(o=e[n]=t.measureText(n).width,i.push(n)),o>s&&(s=o),s}function Oe(t,e,i,s){let o=(s=s||{}).data=s.data||{},a=s.garbageCollect=s.garbageCollect||[];s.font!==e&&(o=s.data={},a=s.garbageCollect=[],s.font=e),t.save(),t.font=e;let r=0;const l=i.length;let h,c,d,u,f;for(h=0;hi.length){for(h=0;h0&&t.stroke()}}function Re(t,e,i){return i=i||.5,!e||t&&t.x>e.left-i&&t.xe.top-i&&t.y0&&""!==r.strokeColor;let c,d;for(t.save(),t.font=a.string,function(t,e){e.translation&&t.translate(e.translation[0],e.translation[1]),s(e.rotation)||t.rotate(e.rotation),e.color&&(t.fillStyle=e.color),e.textAlign&&(t.textAlign=e.textAlign),e.textBaseline&&(t.textBaseline=e.textBaseline)}(t,r),c=0;ct[0])){const o=i||t;void 0===s&&(s=ti("_fallback",t));const a={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:t,_rootScopes:o,_fallback:s,_getTarget:n,override:i=>je([i,...t],e,o,s)};return new Proxy(a,{deleteProperty:(e,i)=>(delete e[i],delete e._keys,delete t[0][i],!0),get:(i,s)=>qe(i,s,(()=>function(t,e,i,s){let n;for(const o of e)if(n=ti(Ue(o,t),i),void 0!==n)return Xe(t,n)?Ze(i,s,t,n):n}(s,e,t,i))),getOwnPropertyDescriptor:(t,e)=>Reflect.getOwnPropertyDescriptor(t._scopes[0],e),getPrototypeOf:()=>Reflect.getPrototypeOf(t[0]),has:(t,e)=>ei(t).includes(e),ownKeys:t=>ei(t),set(t,e,i){const s=t._storage||(t._storage=n());return t[e]=s[e]=i,delete t._keys,!0}})}function $e(t,e,i,s){const a={_cacheable:!1,_proxy:t,_context:e,_subProxy:i,_stack:new Set,_descriptors:Ye(t,s),setContext:e=>$e(t,e,i,s),override:n=>$e(t.override(n),e,i,s)};return new Proxy(a,{deleteProperty:(e,i)=>(delete e[i],delete t[i],!0),get:(t,e,i)=>qe(t,e,(()=>function(t,e,i){const{_proxy:s,_context:a,_subProxy:r,_descriptors:l}=t;let h=s[e];S(h)&&l.isScriptable(e)&&(h=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_stack:r}=i;if(r.has(t))throw new Error("Recursion detected: "+Array.from(r).join("->")+"->"+t);r.add(t);let l=e(o,a||s);r.delete(t),Xe(t,l)&&(l=Ze(n._scopes,n,t,l));return l}(e,h,t,i));n(h)&&h.length&&(h=function(t,e,i,s){const{_proxy:n,_context:a,_subProxy:r,_descriptors:l}=i;if(void 0!==a.index&&s(t))return e[a.index%e.length];if(o(e[0])){const i=e,s=n._scopes.filter((t=>t!==i));e=[];for(const o of i){const i=Ze(s,n,t,o);e.push($e(i,a,r&&r[t],l))}}return e}(e,h,t,l.isIndexable));Xe(e,h)&&(h=$e(h,a,r&&r[e],l));return h}(t,e,i))),getOwnPropertyDescriptor:(e,i)=>e._descriptors.allKeys?Reflect.has(t,i)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,i),getPrototypeOf:()=>Reflect.getPrototypeOf(t),has:(e,i)=>Reflect.has(t,i),ownKeys:()=>Reflect.ownKeys(t),set:(e,i,s)=>(t[i]=s,delete e[i],!0)})}function Ye(t,e={scriptable:!0,indexable:!0}){const{_scriptable:i=e.scriptable,_indexable:s=e.indexable,_allKeys:n=e.allKeys}=t;return{allKeys:n,scriptable:i,indexable:s,isScriptable:S(i)?i:()=>i,isIndexable:S(s)?s:()=>s}}const Ue=(t,e)=>t?t+w(e):e,Xe=(t,e)=>o(e)&&"adapters"!==t&&(null===Object.getPrototypeOf(e)||e.constructor===Object);function qe(t,e,i){if(Object.prototype.hasOwnProperty.call(t,e)||"constructor"===e)return t[e];const s=i();return t[e]=s,s}function Ke(t,e,i){return S(t)?t(e,i):t}const Ge=(t,e)=>!0===t?e:"string"==typeof t?M(e,t):void 0;function Je(t,e,i,s,n){for(const o of e){const e=Ge(i,o);if(e){t.add(e);const o=Ke(e._fallback,i,n);if(void 0!==o&&o!==i&&o!==s)return o}else if(!1===e&&void 0!==s&&i!==s)return null}return!1}function Ze(t,e,i,s){const a=e._rootScopes,r=Ke(e._fallback,i,s),l=[...t,...a],h=new Set;h.add(s);let c=Qe(h,l,i,r||i,s);return null!==c&&((void 0===r||r===i||(c=Qe(h,l,r,c,s),null!==c))&&je(Array.from(h),[""],a,r,(()=>function(t,e,i){const s=t._getTarget();e in s||(s[e]={});const a=s[e];if(n(a)&&o(i))return i;return a||{}}(e,i,s))))}function Qe(t,e,i,s,n){for(;i;)i=Je(t,e,i,s,n);return i}function ti(t,e){for(const i of e){if(!i)continue;const e=i[t];if(void 0!==e)return e}}function ei(t){let e=t._keys;return e||(e=t._keys=function(t){const e=new Set;for(const i of t)for(const t of Object.keys(i).filter((t=>!t.startsWith("_"))))e.add(t);return Array.from(e)}(t._scopes)),e}function ii(t,e,i,s){const{iScale:n}=t,{key:o="r"}=this._parsing,a=new Array(s);let r,l,h,c;for(r=0,l=s;re"x"===t?"y":"x";function ai(t,e,i,s){const n=t.skip?e:t,o=e,a=i.skip?e:i,r=q(o,n),l=q(a,o);let h=r/(r+l),c=l/(r+l);h=isNaN(h)?0:h,c=isNaN(c)?0:c;const d=s*h,u=s*c;return{previous:{x:o.x-d*(a.x-n.x),y:o.y-d*(a.y-n.y)},next:{x:o.x+u*(a.x-n.x),y:o.y+u*(a.y-n.y)}}}function ri(t,e="x"){const i=oi(e),s=t.length,n=Array(s).fill(0),o=Array(s);let a,r,l,h=ni(t,0);for(a=0;a!t.skip))),"monotone"===e.cubicInterpolationMode)ri(t,n);else{let i=s?t[t.length-1]:t[0];for(o=0,a=t.length;o0===t||1===t,di=(t,e,i)=>-Math.pow(2,10*(t-=1))*Math.sin((t-e)*O/i),ui=(t,e,i)=>Math.pow(2,-10*t)*Math.sin((t-e)*O/i)+1,fi={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*E),easeOutSine:t=>Math.sin(t*E),easeInOutSine:t=>-.5*(Math.cos(C*t)-1),easeInExpo:t=>0===t?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>1===t?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>ci(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>ci(t)?t:di(t,.075,.3),easeOutElastic:t=>ci(t)?t:ui(t,.075,.3),easeInOutElastic(t){const e=.1125;return ci(t)?t:t<.5?.5*di(2*t,e,.45):.5+.5*ui(2*t-1,e,.45)},easeInBack(t){const e=1.70158;return t*t*((e+1)*t-e)},easeOutBack(t){const e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:t=>1-fi.easeOutBounce(1-t),easeOutBounce(t){const e=7.5625,i=2.75;return t<1/i?e*t*t:t<2/i?e*(t-=1.5/i)*t+.75:t<2.5/i?e*(t-=2.25/i)*t+.9375:e*(t-=2.625/i)*t+.984375},easeInOutBounce:t=>t<.5?.5*fi.easeInBounce(2*t):.5*fi.easeOutBounce(2*t-1)+.5};function gi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:t.y+i*(e.y-t.y)}}function pi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:"middle"===s?i<.5?t.y:e.y:"after"===s?i<1?t.y:e.y:i>0?e.y:t.y}}function mi(t,e,i,s){const n={x:t.cp2x,y:t.cp2y},o={x:e.cp1x,y:e.cp1y},a=gi(t,n,i),r=gi(n,o,i),l=gi(o,e,i),h=gi(a,r,i),c=gi(r,l,i);return gi(h,c,i)}const xi=/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/,bi=/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;function _i(t,e){const i=(""+t).match(xi);if(!i||"normal"===i[1])return 1.2*e;switch(t=+i[2],i[3]){case"px":return t;case"%":t/=100}return e*t}const yi=t=>+t||0;function vi(t,e){const i={},s=o(e),n=s?Object.keys(e):e,a=o(t)?s?i=>l(t[i],t[e[i]]):e=>t[e]:()=>t;for(const t of n)i[t]=yi(a(t));return i}function Mi(t){return vi(t,{top:"y",right:"x",bottom:"y",left:"x"})}function wi(t){return vi(t,["topLeft","topRight","bottomLeft","bottomRight"])}function ki(t){const e=Mi(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function Si(t,e){t=t||{},e=e||ue.font;let i=l(t.size,e.size);"string"==typeof i&&(i=parseInt(i,10));let s=l(t.style,e.style);s&&!(""+s).match(bi)&&(console.warn('Invalid font style specified: "'+s+'"'),s=void 0);const n={family:l(t.family,e.family),lineHeight:_i(l(t.lineHeight,e.lineHeight),i),size:i,style:s,weight:l(t.weight,e.weight),string:""};return n.string=De(n),n}function Pi(t,e,i,s){let o,a,r,l=!0;for(o=0,a=t.length;oi&&0===t?0:t+e;return{min:a(s,-Math.abs(o)),max:a(n,o)}}function Ci(t,e){return Object.assign(Object.create(t),e)}function Oi(t,e,i){return t?function(t,e){return{x:i=>t+t+e-i,setWidth(t){e=t},textAlign:t=>"center"===t?t:"right"===t?"left":"right",xPlus:(t,e)=>t-e,leftForLtr:(t,e)=>t-e}}(e,i):{x:t=>t,setWidth(t){},textAlign:t=>t,xPlus:(t,e)=>t+e,leftForLtr:(t,e)=>t}}function Ai(t,e){let i,s;"ltr"!==e&&"rtl"!==e||(i=t.canvas.style,s=[i.getPropertyValue("direction"),i.getPropertyPriority("direction")],i.setProperty("direction",e,"important"),t.prevTextDirection=s)}function Ti(t,e){void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}function Li(t){return"angle"===t?{between:J,compare:K,normalize:G}:{between:tt,compare:(t,e)=>t-e,normalize:t=>t}}function Ei({start:t,end:e,count:i,loop:s,style:n}){return{start:t%i,end:e%i,loop:s&&(e-t+1)%i==0,style:n}}function Ri(t,e,i){if(!i)return[t];const{property:s,start:n,end:o}=i,a=e.length,{compare:r,between:l,normalize:h}=Li(s),{start:c,end:d,loop:u,style:f}=function(t,e,i){const{property:s,start:n,end:o}=i,{between:a,normalize:r}=Li(s),l=e.length;let h,c,{start:d,end:u,loop:f}=t;if(f){for(d+=l,u+=l,h=0,c=l;hb||l(n,x,p)&&0!==r(n,x),v=()=>!b||0===r(o,p)||l(o,x,p);for(let t=c,i=c;t<=d;++t)m=e[t%a],m.skip||(p=h(m[s]),p!==x&&(b=l(p,n,o),null===_&&y()&&(_=0===r(p,n)?t:i),null!==_&&v()&&(g.push(Ei({start:_,end:t,loop:u,count:a,style:f})),_=null),i=t,x=p));return null!==_&&g.push(Ei({start:_,end:d,loop:u,count:a,style:f})),g}function Ii(t,e){const i=[],s=t.segments;for(let n=0;nn&&t[o%e].skip;)o--;return o%=e,{start:n,end:o}}(i,n,o,s);if(!0===s)return Fi(t,[{start:a,end:r,loop:o}],i,e);return Fi(t,function(t,e,i,s){const n=t.length,o=[];let a,r=e,l=t[e];for(a=e+1;a<=i;++a){const i=t[a%n];i.skip||i.stop?l.skip||(s=!1,o.push({start:e%n,end:(a-1)%n,loop:s}),e=r=i.stop?a:null):(r=a,l.skip&&(e=a)),l=i}return null!==r&&o.push({start:e%n,end:r%n,loop:s}),o}(i,a,r!s(t[e.axis])));n.lo-=Math.max(0,a);const r=i.slice(n.hi).findIndex((t=>!s(t[e.axis])));n.hi+=Math.max(0,r)}return n}if(o._sharedOptions){const t=a[0],s="function"==typeof t.getRange&&t.getRange(e);if(s){const t=r(a,e,i-s),n=r(a,e,i+s);return{lo:t.lo,hi:n.hi}}}}return{lo:0,hi:a.length-1}}function $i(t,e,i,s,n){const o=t.getSortedVisibleDatasetMetas(),a=i[e];for(let t=0,i=o.length;t{t[a]&&t[a](e[i],n)&&(o.push({element:t,datasetIndex:s,index:l}),r=r||t.inRange(e.x,e.y,n))})),s&&!r?[]:o}var Ki={evaluateInteractionItems:$i,modes:{index(t,e,i,s){const n=ve(e,t),o=i.axis||"x",a=i.includeInvisible||!1,r=i.intersect?Yi(t,n,o,s,a):Xi(t,n,o,!1,s,a),l=[];return r.length?(t.getSortedVisibleDatasetMetas().forEach((t=>{const e=r[0].index,i=t.data[e];i&&!i.skip&&l.push({element:i,datasetIndex:t.index,index:e})})),l):[]},dataset(t,e,i,s){const n=ve(e,t),o=i.axis||"xy",a=i.includeInvisible||!1;let r=i.intersect?Yi(t,n,o,s,a):Xi(t,n,o,!1,s,a);if(r.length>0){const e=r[0].datasetIndex,i=t.getDatasetMeta(e).data;r=[];for(let t=0;tYi(t,ve(e,t),i.axis||"xy",s,i.includeInvisible||!1),nearest(t,e,i,s){const n=ve(e,t),o=i.axis||"xy",a=i.includeInvisible||!1;return Xi(t,n,o,i.intersect,s,a)},x:(t,e,i,s)=>qi(t,ve(e,t),"x",i.intersect,s),y:(t,e,i,s)=>qi(t,ve(e,t),"y",i.intersect,s)}};const Gi=["left","top","right","bottom"];function Ji(t,e){return t.filter((t=>t.pos===e))}function Zi(t,e){return t.filter((t=>-1===Gi.indexOf(t.pos)&&t.box.axis===e))}function Qi(t,e){return t.sort(((t,i)=>{const s=e?i:t,n=e?t:i;return s.weight===n.weight?s.index-n.index:s.weight-n.weight}))}function ts(t,e){const i=function(t){const e={};for(const i of t){const{stack:t,pos:s,stackWeight:n}=i;if(!t||!Gi.includes(s))continue;const o=e[t]||(e[t]={count:0,placed:0,weight:0,size:0});o.count++,o.weight+=n}return e}(t),{vBoxMaxWidth:s,hBoxMaxHeight:n}=e;let o,a,r;for(o=0,a=t.length;o{s[t]=Math.max(e[t],i[t])})),s}return s(t?["left","right"]:["top","bottom"])}function os(t,e,i,s){const n=[];let o,a,r,l,h,c;for(o=0,a=t.length,h=0;ot.box.fullSize)),!0),s=Qi(Ji(e,"left"),!0),n=Qi(Ji(e,"right")),o=Qi(Ji(e,"top"),!0),a=Qi(Ji(e,"bottom")),r=Zi(e,"x"),l=Zi(e,"y");return{fullSize:i,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(a).concat(r),chartArea:Ji(e,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(a).concat(r)}}(t.boxes),l=r.vertical,h=r.horizontal;u(t.boxes,(t=>{"function"==typeof t.beforeLayout&&t.beforeLayout()}));const c=l.reduce(((t,e)=>e.box.options&&!1===e.box.options.display?t:t+1),0)||1,d=Object.freeze({outerWidth:e,outerHeight:i,padding:n,availableWidth:o,availableHeight:a,vBoxMaxWidth:o/2/c,hBoxMaxHeight:a/2}),f=Object.assign({},n);is(f,ki(s));const g=Object.assign({maxPadding:f,w:o,h:a,x:n.left,y:n.top},n),p=ts(l.concat(h),d);os(r.fullSize,g,d,p),os(l,g,d,p),os(h,g,d,p)&&os(l,g,d,p),function(t){const e=t.maxPadding;function i(i){const s=Math.max(e[i]-t[i],0);return t[i]+=s,s}t.y+=i("top"),t.x+=i("left"),i("right"),i("bottom")}(g),rs(r.leftAndTop,g,d,p),g.x+=g.w,g.y+=g.h,rs(r.rightAndBottom,g,d,p),t.chartArea={left:g.left,top:g.top,right:g.left+g.w,bottom:g.top+g.h,height:g.h,width:g.w},u(r.chartArea,(e=>{const i=e.box;Object.assign(i,t.chartArea),i.update(g.w,g.h,{left:0,top:0,right:0,bottom:0})}))}};class hs{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,i){}removeEventListener(t,e,i){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,i,s){return e=Math.max(0,e||t.width),i=i||t.height,{width:e,height:Math.max(0,s?Math.floor(e/s):i)}}isAttached(t){return!0}updateConfig(t){}}class cs extends hs{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const ds="$chartjs",us={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},fs=t=>null===t||""===t;const gs=!!Se&&{passive:!0};function ps(t,e,i){t&&t.canvas&&t.canvas.removeEventListener(e,i,gs)}function ms(t,e){for(const i of t)if(i===e||i.contains(e))return!0}function xs(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||ms(i.addedNodes,s),e=e&&!ms(i.removedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}function bs(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||ms(i.removedNodes,s),e=e&&!ms(i.addedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}const _s=new Map;let ys=0;function vs(){const t=window.devicePixelRatio;t!==ys&&(ys=t,_s.forEach(((e,i)=>{i.currentDevicePixelRatio!==t&&e()})))}function Ms(t,e,i){const s=t.canvas,n=s&&ge(s);if(!n)return;const o=ct(((t,e)=>{const s=n.clientWidth;i(t,e),s{const e=t[0],i=e.contentRect.width,s=e.contentRect.height;0===i&&0===s||o(i,s)}));return a.observe(n),function(t,e){_s.size||window.addEventListener("resize",vs),_s.set(t,e)}(t,o),a}function ws(t,e,i){i&&i.disconnect(),"resize"===e&&function(t){_s.delete(t),_s.size||window.removeEventListener("resize",vs)}(t)}function ks(t,e,i){const s=t.canvas,n=ct((e=>{null!==t.ctx&&i(function(t,e){const i=us[t.type]||t.type,{x:s,y:n}=ve(t,e);return{type:i,chart:e,native:t,x:void 0!==s?s:null,y:void 0!==n?n:null}}(e,t))}),t);return function(t,e,i){t&&t.addEventListener(e,i,gs)}(s,e,n),n}class Ss extends hs{acquireContext(t,e){const i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(function(t,e){const i=t.style,s=t.getAttribute("height"),n=t.getAttribute("width");if(t[ds]={initial:{height:s,width:n,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||"block",i.boxSizing=i.boxSizing||"border-box",fs(n)){const e=Pe(t,"width");void 0!==e&&(t.width=e)}if(fs(s))if(""===t.style.height)t.height=t.width/(e||2);else{const e=Pe(t,"height");void 0!==e&&(t.height=e)}}(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e[ds])return!1;const i=e[ds].initial;["height","width"].forEach((t=>{const n=i[t];s(n)?e.removeAttribute(t):e.setAttribute(t,n)}));const n=i.style||{};return Object.keys(n).forEach((t=>{e.style[t]=n[t]})),e.width=e.width,delete e[ds],!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),n={attach:xs,detach:bs,resize:Ms}[e]||ks;s[e]=n(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:ws,detach:ws,resize:ws}[e]||ps)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return we(t,e,i,s)}isAttached(t){const e=t&&ge(t);return!(!e||!e.isConnected)}}function Ps(t){return!fe()||"undefined"!=typeof OffscreenCanvas&&t instanceof OffscreenCanvas?cs:Ss}var Ds=Object.freeze({__proto__:null,BasePlatform:hs,BasicPlatform:cs,DomPlatform:Ss,_detectPlatform:Ps});const Cs="transparent",Os={boolean:(t,e,i)=>i>.5?e:t,color(t,e,i){const s=Qt(t||Cs),n=s.valid&&Qt(e||Cs);return n&&n.valid?n.mix(s,i).hexString():e},number:(t,e,i)=>t+(e-t)*i};class As{constructor(t,e,i,s){const n=e[i];s=Pi([t.to,s,n,t.from]);const o=Pi([t.from,n,s]);this._active=!0,this._fn=t.fn||Os[t.type||typeof o],this._easing=fi[t.easing]||fi.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=i,this._from=o,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],n=i-this._start,o=this._duration-n;this._start=i,this._duration=Math.floor(Math.max(o,t.duration)),this._total+=n,this._loop=!!t.loop,this._to=Pi([t.to,e,s,t.from]),this._from=Pi([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,n=this._from,o=this._loop,a=this._to;let r;if(this._active=n!==a&&(o||e1?2-r:r,r=this._easing(Math.min(1,Math.max(0,r))),this._target[s]=this._fn(n,a,r))}wait(){const t=this._promises||(this._promises=[]);return new Promise(((e,i)=>{t.push({res:e,rej:i})}))}_notify(t){const e=t?"res":"rej",i=this._promises||[];for(let t=0;t{const a=t[s];if(!o(a))return;const r={};for(const t of e)r[t]=a[t];(n(a.properties)&&a.properties||[s]).forEach((t=>{t!==s&&i.has(t)||i.set(t,r)}))}))}_animateOptions(t,e){const i=e.options,s=function(t,e){if(!e)return;let i=t.options;if(!i)return void(t.options=e);i.$shared&&(t.options=i=Object.assign({},i,{$shared:!1,$animations:{}}));return i}(t,i);if(!s)return[];const n=this._createAnimations(s,i);return i.$shared&&function(t,e){const i=[],s=Object.keys(e);for(let e=0;e{t.options=i}),(()=>{})),n}_createAnimations(t,e){const i=this._properties,s=[],n=t.$animations||(t.$animations={}),o=Object.keys(e),a=Date.now();let r;for(r=o.length-1;r>=0;--r){const l=o[r];if("$"===l.charAt(0))continue;if("options"===l){s.push(...this._animateOptions(t,e));continue}const h=e[l];let c=n[l];const d=i.get(l);if(c){if(d&&c.active()){c.update(d,h,a);continue}c.cancel()}d&&d.duration?(n[l]=c=new As(d,t,l,h),s.push(c)):t[l]=h}return s}update(t,e){if(0===this._properties.size)return void Object.assign(t,e);const i=this._createAnimations(t,e);return i.length?(bt.add(this._chart,i),!0):void 0}}function Ls(t,e){const i=t&&t.options||{},s=i.reverse,n=void 0===i.min?e:0,o=void 0===i.max?e:0;return{start:s?o:n,end:s?n:o}}function Es(t,e){const i=[],s=t._getSortedDatasetMetas(e);let n,o;for(n=0,o=s.length;n0||!i&&e<0)return n.index}return null}function Vs(t,e){const{chart:i,_cachedMeta:s}=t,n=i._stacks||(i._stacks={}),{iScale:o,vScale:a,index:r}=s,l=o.axis,h=a.axis,c=function(t,e,i){return`${t.id}.${e.id}.${i.stack||i.type}`}(o,a,s),d=e.length;let u;for(let t=0;ti[t].axis===e)).shift()}function Ws(t,e){const i=t.controller.index,s=t.vScale&&t.vScale.axis;if(s){e=e||t._parsed;for(const t of e){const e=t._stacks;if(!e||void 0===e[s]||void 0===e[s][i])return;delete e[s][i],void 0!==e[s]._visualValues&&void 0!==e[s]._visualValues[i]&&delete e[s]._visualValues[i]}}}const Ns=t=>"reset"===t||"none"===t,Hs=(t,e)=>e?t:Object.assign({},t);class js{static defaults={};static datasetElementType=null;static dataElementType=null;constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=Is(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Ws(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(t,e,i,s)=>"x"===t?e:"r"===t?s:i,n=e.xAxisID=l(i.xAxisID,Bs(t,"x")),o=e.yAxisID=l(i.yAxisID,Bs(t,"y")),a=e.rAxisID=l(i.rAxisID,Bs(t,"r")),r=e.indexAxis,h=e.iAxisID=s(r,n,o,a),c=e.vAxisID=s(r,o,n,a);e.xScale=this.getScaleForId(n),e.yScale=this.getScaleForId(o),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(h),e.vScale=this.getScaleForId(c)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&rt(this._data,this),t._stacked&&Ws(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(o(e)){const t=this._cachedMeta;this._data=function(t,e){const{iScale:i,vScale:s}=e,n="x"===i.axis?"x":"y",o="x"===s.axis?"x":"y",a=Object.keys(t),r=new Array(a.length);let l,h,c;for(l=0,h=a.length;l0&&i._parsed[t-1];if(!1===this._parsing)i._parsed=s,i._sorted=!0,d=s;else{d=n(s[t])?this.parseArrayData(i,s,t,e):o(s[t])?this.parseObjectData(i,s,t,e):this.parsePrimitiveData(i,s,t,e);const a=()=>null===c[l]||f&&c[l]t&&!e.hidden&&e._stacked&&{keys:Es(i,!0),values:null})(e,i,this.chart),h={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:c,max:d}=function(t){const{min:e,max:i,minDefined:s,maxDefined:n}=t.getUserBounds();return{min:s?e:Number.NEGATIVE_INFINITY,max:n?i:Number.POSITIVE_INFINITY}}(r);let u,f;function g(){f=s[u];const e=f[r.axis];return!a(f[t.axis])||c>e||d=0;--u)if(!g()){this.updateRangeFromParsed(h,t,f,l);break}return h}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,n,o;for(s=0,n=e.length;s=0&&tthis.getContext(i,s,e)),c);return f.$shared&&(f.$shared=r,n[o]=Object.freeze(Hs(f,r))),f}_resolveAnimations(t,e,i){const s=this.chart,n=this._cachedDataOpts,o=`animation-${e}`,a=n[o];if(a)return a;let r;if(!1!==s.options.animation){const s=this.chart.config,n=s.datasetAnimationScopeKeys(this._type,e),o=s.getOptionScopes(this.getDataset(),n);r=s.createResolver(o,this.getContext(t,i,e))}const l=new Ts(s,r&&r.animations);return r&&r._cacheable&&(n[o]=Object.freeze(l)),l}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||Ns(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const i=this.resolveDataElementOptions(t,e),s=this._sharedOptions,n=this.getSharedOptions(i),o=this.includeOptions(e,n)||n!==s;return this.updateSharedOptions(n,e,i),{sharedOptions:n,includeOptions:o}}updateElement(t,e,i,s){Ns(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!Ns(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const n=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(n)||n})}removeHoverStyle(t,e,i){this._setStyle(t,i,"active",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,i=this._cachedMeta.data;for(const[t,e,i]of this._syncList)this[t](e,i);this._syncList=[];const s=i.length,n=e.length,o=Math.min(n,s);o&&this.parse(0,o),n>s?this._insertElements(s,n-s,t):n{for(t.length+=e,a=t.length-1;a>=o;a--)t[a]=t[a-e]};for(r(n),a=t;a{s[t]=i[t]&&i[t].active()?i[t]._to:this[t]})),s}}function Ys(t,e){const i=t.options.ticks,n=function(t){const e=t.options.offset,i=t._tickSize(),s=t._length/i+(e?0:1),n=t._maxLength/i;return Math.floor(Math.min(s,n))}(t),o=Math.min(i.maxTicksLimit||n,n),a=i.major.enabled?function(t){const e=[];let i,s;for(i=0,s=t.length;io)return function(t,e,i,s){let n,o=0,a=i[0];for(s=Math.ceil(s),n=0;nn)return e}return Math.max(n,1)}(a,e,o);if(r>0){let t,i;const n=r>1?Math.round((h-l)/(r-1)):null;for(Us(e,c,d,s(n)?0:l-n,l),t=0,i=r-1;t"top"===e||"left"===e?t[e]+i:t[e]-i,qs=(t,e)=>Math.min(e||t,t);function Ks(t,e){const i=[],s=t.length/e,n=t.length;let o=0;for(;oa+r)))return h}function Js(t){return t.drawTicks?t.tickLength:0}function Zs(t,e){if(!t.display)return 0;const i=Si(t.font,e),s=ki(t.padding);return(n(t.text)?t.text.length:1)*i.lineHeight+s.height}function Qs(t,e,i){let s=ut(t);return(i&&"right"!==e||!i&&"right"===e)&&(s=(t=>"left"===t?"right":"right"===t?"left":t)(s)),s}class tn extends $s{constructor(t){super(),this.id=t.id,this.type=t.type,this.options=void 0,this.ctx=t.ctx,this.chart=t.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(t){this.options=t.setContext(this.getContext()),this.axis=t.axis,this._userMin=this.parse(t.min),this._userMax=this.parse(t.max),this._suggestedMin=this.parse(t.suggestedMin),this._suggestedMax=this.parse(t.suggestedMax)}parse(t,e){return t}getUserBounds(){let{_userMin:t,_userMax:e,_suggestedMin:i,_suggestedMax:s}=this;return t=r(t,Number.POSITIVE_INFINITY),e=r(e,Number.NEGATIVE_INFINITY),i=r(i,Number.POSITIVE_INFINITY),s=r(s,Number.NEGATIVE_INFINITY),{min:r(t,i),max:r(e,s),minDefined:a(t),maxDefined:a(e)}}getMinMax(t){let e,{min:i,max:s,minDefined:n,maxDefined:o}=this.getUserBounds();if(n&&o)return{min:i,max:s};const a=this.getMatchingVisibleMetas();for(let r=0,l=a.length;rs?s:i,s=n&&i>s?i:s,{min:r(i,r(s,i)),max:r(s,r(i,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){d(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:n,ticks:o}=this.options,a=o.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=Di(this,n,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const r=a=n||i<=1||!this.isHorizontal())return void(this.labelRotation=s);const h=this._getLabelSizes(),c=h.widest.width,d=h.highest.height,u=Z(this.chart.width-c,0,this.maxWidth);o=t.offset?this.maxWidth/i:u/(i-1),c+6>o&&(o=u/(i-(t.offset?.5:1)),a=this.maxHeight-Js(t.grid)-e.padding-Zs(t.title,this.chart.options.font),r=Math.sqrt(c*c+d*d),l=Y(Math.min(Math.asin(Z((h.highest.height+6)/o,-1,1)),Math.asin(Z(a/r,-1,1))-Math.asin(Z(d/r,-1,1)))),l=Math.max(s,Math.min(n,l))),this.labelRotation=l}afterCalculateLabelRotation(){d(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){d(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:n}}=this,o=this._isVisible(),a=this.isHorizontal();if(o){const o=Zs(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Js(n)+o):(t.height=this.maxHeight,t.width=Js(n)+o),i.display&&this.ticks.length){const{first:e,last:s,widest:n,highest:o}=this._getLabelSizes(),r=2*i.padding,l=$(this.labelRotation),h=Math.cos(l),c=Math.sin(l);if(a){const e=i.mirror?0:c*n.width+h*o.height;t.height=Math.min(this.maxHeight,t.height+e+r)}else{const e=i.mirror?0:h*n.width+c*o.height;t.width=Math.min(this.maxWidth,t.width+e+r)}this._calculatePadding(e,s,c,h)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,i,s){const{ticks:{align:n,padding:o},position:a}=this.options,r=0!==this.labelRotation,l="top"!==a&&"x"===this.axis;if(this.isHorizontal()){const a=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let c=0,d=0;r?l?(c=s*t.width,d=i*e.height):(c=i*t.height,d=s*e.width):"start"===n?d=e.width:"end"===n?c=t.width:"inner"!==n&&(c=t.width/2,d=e.width/2),this.paddingLeft=Math.max((c-a+o)*this.width/(this.width-a),0),this.paddingRight=Math.max((d-h+o)*this.width/(this.width-h),0)}else{let i=e.height/2,s=t.height/2;"start"===n?(i=0,s=t.height):"end"===n&&(i=e.height,s=0),this.paddingTop=i+o,this.paddingBottom=s+o}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){d(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return"top"===e||"bottom"===e||"x"===t}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){let e,i;for(this.beforeTickToLabelConversion(),this.generateTickLabels(t),e=0,i=t.length;e{const i=t.gc,s=i.length/2;let n;if(s>e){for(n=0;n({width:r[t]||0,height:l[t]||0});return{first:P(0),last:P(e-1),widest:P(k),highest:P(S),widths:r,heights:l}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return Q(this._alignToPixels?Ae(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&ta*s?a/i:r/s:r*s0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:n,position:a,border:r}=s,h=n.offset,c=this.isHorizontal(),d=this.ticks.length+(h?1:0),u=Js(n),f=[],g=r.setContext(this.getContext()),p=g.display?g.width:0,m=p/2,x=function(t){return Ae(i,t,p)};let b,_,y,v,M,w,k,S,P,D,C,O;if("top"===a)b=x(this.bottom),w=this.bottom-u,S=b-m,D=x(t.top)+m,O=t.bottom;else if("bottom"===a)b=x(this.top),D=t.top,O=x(t.bottom)-m,w=b+m,S=this.top+u;else if("left"===a)b=x(this.right),M=this.right-u,k=b-m,P=x(t.left)+m,C=t.right;else if("right"===a)b=x(this.left),P=t.left,C=x(t.right)-m,M=b+m,k=this.left+u;else if("x"===e){if("center"===a)b=x((t.top+t.bottom)/2+.5);else if(o(a)){const t=Object.keys(a)[0],e=a[t];b=x(this.chart.scales[t].getPixelForValue(e))}D=t.top,O=t.bottom,w=b+m,S=w+u}else if("y"===e){if("center"===a)b=x((t.left+t.right)/2);else if(o(a)){const t=Object.keys(a)[0],e=a[t];b=x(this.chart.scales[t].getPixelForValue(e))}M=b-m,k=M-u,P=t.left,C=t.right}const A=l(s.ticks.maxTicksLimit,d),T=Math.max(1,Math.ceil(d/A));for(_=0;_0&&(o-=s/2)}d={left:o,top:n,width:s+e.width,height:i+e.height,color:t.backdropColor}}x.push({label:v,font:P,textOffset:O,options:{rotation:m,color:i,strokeColor:o,strokeWidth:h,textAlign:f,textBaseline:A,translation:[M,w],backdrop:d}})}return x}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-$(this.labelRotation))return"top"===t?"left":"right";let i="center";return"start"===e.align?i="left":"end"===e.align?i="right":"inner"===e.align&&(i="inner"),i}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:i,mirror:s,padding:n}}=this.options,o=t+n,a=this._getLabelSizes().widest.width;let r,l;return"left"===e?s?(l=this.right+n,"near"===i?r="left":"center"===i?(r="center",l+=a/2):(r="right",l+=a)):(l=this.right-o,"near"===i?r="right":"center"===i?(r="center",l-=a/2):(r="left",l=this.left)):"right"===e?s?(l=this.left+n,"near"===i?r="right":"center"===i?(r="center",l-=a/2):(r="left",l-=a)):(l=this.left+o,"near"===i?r="left":"center"===i?(r="center",l+=a/2):(r="right",l=this.right)):r="right",{textAlign:r,x:l}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;return"left"===e||"right"===e?{top:0,left:this.left,bottom:t.height,right:this.right}:"top"===e||"bottom"===e?{top:this.top,left:0,bottom:this.bottom,right:t.width}:void 0}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:i,top:s,width:n,height:o}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(i,s,n,o),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const i=this.ticks.findIndex((e=>e.value===t));if(i>=0){return e.setContext(this.getContext(i)).lineWidth}return 0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let n,o;const a=(t,e,s)=>{s.width&&s.color&&(i.save(),i.lineWidth=s.width,i.strokeStyle=s.color,i.setLineDash(s.borderDash||[]),i.lineDashOffset=s.borderDashOffset,i.beginPath(),i.moveTo(t.x,t.y),i.lineTo(e.x,e.y),i.stroke(),i.restore())};if(e.display)for(n=0,o=s.length;n{this.drawBackground(),this.drawGrid(t),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:e,draw:t=>{this.drawLabels(t)}}]:[{z:e,draw:t=>{this.draw(t)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+"AxisID",s=[];let n,o;for(n=0,o=e.length;n{const s=i.split("."),n=s.pop(),o=[t].concat(s).join("."),a=e[i].split("."),r=a.pop(),l=a.join(".");ue.route(o,n,l,r)}))}(e,t.defaultRoutes);t.descriptors&&ue.describe(e,t.descriptors)}(t,o,i),this.override&&ue.override(t.id,t.overrides)),o}get(t){return this.items[t]}unregister(t){const e=this.items,i=t.id,s=this.scope;i in e&&delete e[i],s&&i in ue[s]&&(delete ue[s][i],this.override&&delete re[i])}}class sn{constructor(){this.controllers=new en(js,"datasets",!0),this.elements=new en($s,"elements"),this.plugins=new en(Object,"plugins"),this.scales=new en(tn,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,i){[...e].forEach((e=>{const s=i||this._getRegistryForType(e);i||s.isForType(e)||s===this.plugins&&e.id?this._exec(t,s,e):u(e,(e=>{const s=i||this._getRegistryForType(e);this._exec(t,s,e)}))}))}_exec(t,e,i){const s=w(t);d(i["before"+s],[],i),e[t](i),d(i["after"+s],[],i)}_getRegistryForType(t){for(let e=0;et.filter((t=>!e.some((e=>t.plugin.id===e.plugin.id))));this._notify(s(e,i),t,"stop"),this._notify(s(i,e),t,"start")}}function an(t,e){return e||!1!==t?!0===t?{}:t:null}function rn(t,{plugin:e,local:i},s,n){const o=t.pluginScopeKeys(e),a=t.getOptionScopes(s,o);return i&&e.defaults&&a.push(e.defaults),t.createResolver(a,n,[""],{scriptable:!1,indexable:!1,allKeys:!0})}function ln(t,e){const i=ue.datasets[t]||{};return((e.datasets||{})[t]||{}).indexAxis||e.indexAxis||i.indexAxis||"x"}function hn(t){if("x"===t||"y"===t||"r"===t)return t}function cn(t,...e){if(hn(t))return t;for(const s of e){const e=s.axis||("top"===(i=s.position)||"bottom"===i?"x":"left"===i||"right"===i?"y":void 0)||t.length>1&&hn(t[0].toLowerCase());if(e)return e}var i;throw new Error(`Cannot determine type of '${t}' axis. Please provide 'axis' or 'position' option.`)}function dn(t,e,i){if(i[e+"AxisID"]===t)return{axis:e}}function un(t,e){const i=re[t.type]||{scales:{}},s=e.scales||{},n=ln(t.type,e),a=Object.create(null);return Object.keys(s).forEach((e=>{const r=s[e];if(!o(r))return console.error(`Invalid scale configuration for scale: ${e}`);if(r._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${e}`);const l=cn(e,r,function(t,e){if(e.data&&e.data.datasets){const i=e.data.datasets.filter((e=>e.xAxisID===t||e.yAxisID===t));if(i.length)return dn(t,"x",i[0])||dn(t,"y",i[0])}return{}}(e,t),ue.scales[r.type]),h=function(t,e){return t===e?"_index_":"_value_"}(l,n),c=i.scales||{};a[e]=b(Object.create(null),[{axis:l},r,c[l],c[h]])})),t.data.datasets.forEach((i=>{const n=i.type||t.type,o=i.indexAxis||ln(n,e),r=(re[n]||{}).scales||{};Object.keys(r).forEach((t=>{const e=function(t,e){let i=t;return"_index_"===t?i=e:"_value_"===t&&(i="x"===e?"y":"x"),i}(t,o),n=i[e+"AxisID"]||e;a[n]=a[n]||Object.create(null),b(a[n],[{axis:e},s[n],r[t]])}))})),Object.keys(a).forEach((t=>{const e=a[t];b(e,[ue.scales[e.type],ue.scale])})),a}function fn(t){const e=t.options||(t.options={});e.plugins=l(e.plugins,{}),e.scales=un(t,e)}function gn(t){return(t=t||{}).datasets=t.datasets||[],t.labels=t.labels||[],t}const pn=new Map,mn=new Set;function xn(t,e){let i=pn.get(t);return i||(i=e(),pn.set(t,i),mn.add(i)),i}const bn=(t,e,i)=>{const s=M(e,i);void 0!==s&&t.add(s)};class _n{constructor(t){this._config=function(t){return(t=t||{}).data=gn(t.data),fn(t),t}(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=gn(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),fn(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return xn(t,(()=>[[`datasets.${t}`,""]]))}datasetAnimationScopeKeys(t,e){return xn(`${t}.transition.${e}`,(()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]]))}datasetElementScopeKeys(t,e){return xn(`${t}-${e}`,(()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]]))}pluginScopeKeys(t){const e=t.id;return xn(`${this.type}-plugin-${e}`,(()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]]))}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return s&&!e||(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:n}=this,o=this._cachedScopes(t,i),a=o.get(e);if(a)return a;const r=new Set;e.forEach((e=>{t&&(r.add(t),e.forEach((e=>bn(r,t,e)))),e.forEach((t=>bn(r,s,t))),e.forEach((t=>bn(r,re[n]||{},t))),e.forEach((t=>bn(r,ue,t))),e.forEach((t=>bn(r,le,t)))}));const l=Array.from(r);return 0===l.length&&l.push(Object.create(null)),mn.has(e)&&o.set(e,l),l}chartOptionScopes(){const{options:t,type:e}=this;return[t,re[e]||{},ue.datasets[e]||{},{type:e},ue,le]}resolveNamedOptions(t,e,i,s=[""]){const o={$shared:!0},{resolver:a,subPrefixes:r}=yn(this._resolverCache,t,s);let l=a;if(function(t,e){const{isScriptable:i,isIndexable:s}=Ye(t);for(const o of e){const e=i(o),a=s(o),r=(a||e)&&t[o];if(e&&(S(r)||vn(r))||a&&n(r))return!0}return!1}(a,e)){o.$shared=!1;l=$e(a,i=S(i)?i():i,this.createResolver(t,i,r))}for(const t of e)o[t]=l[t];return o}createResolver(t,e,i=[""],s){const{resolver:n}=yn(this._resolverCache,t,i);return o(e)?$e(n,e,void 0,s):n}}function yn(t,e,i){let s=t.get(e);s||(s=new Map,t.set(e,s));const n=i.join();let o=s.get(n);if(!o){o={resolver:je(e,i),subPrefixes:i.filter((t=>!t.toLowerCase().includes("hover")))},s.set(n,o)}return o}const vn=t=>o(t)&&Object.getOwnPropertyNames(t).some((e=>S(t[e])));const Mn=["top","bottom","left","right","chartArea"];function wn(t,e){return"top"===t||"bottom"===t||-1===Mn.indexOf(t)&&"x"===e}function kn(t,e){return function(i,s){return i[t]===s[t]?i[e]-s[e]:i[t]-s[t]}}function Sn(t){const e=t.chart,i=e.options.animation;e.notifyPlugins("afterRender"),d(i&&i.onComplete,[t],e)}function Pn(t){const e=t.chart,i=e.options.animation;d(i&&i.onProgress,[t],e)}function Dn(t){return fe()&&"string"==typeof t?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const Cn={},On=t=>{const e=Dn(t);return Object.values(Cn).filter((t=>t.canvas===e)).pop()};function An(t,e,i){const s=Object.keys(t);for(const n of s){const s=+n;if(s>=e){const o=t[n];delete t[n],(i>0||s>e)&&(t[s+i]=o)}}}class Tn{static defaults=ue;static instances=Cn;static overrides=re;static registry=nn;static version="4.5.1";static getChart=On;static register(...t){nn.add(...t),Ln()}static unregister(...t){nn.remove(...t),Ln()}constructor(t,e){const s=this.config=new _n(e),n=Dn(t),o=On(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const a=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||Ps(n)),this.platform.updateConfig(s);const r=this.platform.acquireContext(n,a.aspectRatio),l=r&&r.canvas,h=l&&l.height,c=l&&l.width;this.id=i(),this.ctx=r,this.canvas=l,this.width=c,this.height=h,this._options=a,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new on,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=dt((t=>this.update(t)),a.resizeDelay||0),this._dataChanges=[],Cn[this.id]=this,r&&l?(bt.listen(this,"complete",Sn),bt.listen(this,"progress",Pn),this._initialize(),this.attached&&this.update()):console.error("Failed to create chart: can't acquire context from the given item")}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:n,_aspectRatio:o}=this;return s(t)?e&&o?o:n?i/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return nn}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():ke(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Te(this.canvas,this.ctx),this}stop(){return bt.stop(this),this}resize(t,e){bt.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,n=i.maintainAspectRatio&&this.aspectRatio,o=this.platform.getMaximumSize(s,t,e,n),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),r=this.width?"resize":"attach";this.width=o.width,this.height=o.height,this._aspectRatio=this.aspectRatio,ke(this,a,!0)&&(this.notifyPlugins("resize",{size:o}),d(i.onResize,[this,o],this),this.attached&&this._doResize(r)&&this.render())}ensureScalesHaveIDs(){u(this.options.scales||{},((t,e)=>{t.id=e}))}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce(((t,e)=>(t[e]=!1,t)),{});let n=[];e&&(n=n.concat(Object.keys(e).map((t=>{const i=e[t],s=cn(t,i),n="r"===s,o="x"===s;return{options:i,dposition:n?"chartArea":o?"bottom":"left",dtype:n?"radialLinear":o?"category":"linear"}})))),u(n,(e=>{const n=e.options,o=n.id,a=cn(o,n),r=l(n.type,e.dtype);void 0!==n.position&&wn(n.position,a)===wn(e.dposition)||(n.position=e.dposition),s[o]=!0;let h=null;if(o in i&&i[o].type===r)h=i[o];else{h=new(nn.getScale(r))({id:o,type:r,ctx:this.ctx,chart:this}),i[h.id]=h}h.init(n,t)})),u(s,((t,e)=>{t||delete i[e]})),u(i,(t=>{ls.configure(this,t,t.options),ls.addBox(this,t)}))}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort(((t,e)=>t.index-e.index)),i>e){for(let t=e;te.length&&delete this._stacks,t.forEach(((t,i)=>{0===e.filter((e=>e===t._dataset)).length&&this._destroyDatasetMeta(i)}))}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i{this.getDatasetMeta(e).controller.reset()}),this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),!1===this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0}))return;const n=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let o=0;for(let t=0,e=this.data.datasets.length;t{t.reset()})),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(kn("z","_idx"));const{_active:a,_lastEvent:r}=this;r?this._eventHandler(r,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){u(this.scales,(t=>{ls.removeBox(this,t)})),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);P(e,i)&&!!this._responsiveListeners===t.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:n}of e){An(t,s,"_removeElements"===i?-n:n)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=e=>new Set(t.filter((t=>t[0]===e)).map(((t,e)=>e+","+t.splice(1).join(",")))),s=i(0);for(let t=1;tt.split(","))).map((t=>({method:t[1],start:+t[2],count:+t[3]})))}_updateLayout(t){if(!1===this.notifyPlugins("beforeLayout",{cancelable:!0}))return;ls.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],u(this.boxes,(t=>{i&&"chartArea"===t.position||(t.configure&&t.configure(),this._layers.push(...t._layers()))}),this),this._layers.forEach(((t,e)=>{t._idx=e})),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(!1!==this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})){for(let t=0,e=this.data.datasets.length;t=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,i={meta:t,index:t.index,cancelable:!0},s=Ni(this,t);!1!==this.notifyPlugins("beforeDatasetDraw",i)&&(s&&Ie(e,s),t.controller.draw(),s&&ze(e),i.cancelable=!1,this.notifyPlugins("afterDatasetDraw",i))}isPointInArea(t){return Re(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,i,s){const n=Ki.modes[e];return"function"==typeof n?n(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter((t=>t&&t._dataset===e)).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},i.push(s)),s}getContext(){return this.$context||(this.$context=Ci(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return"boolean"==typeof i.hidden?!i.hidden:!e.hidden}setDatasetVisibility(t,e){this.getDatasetMeta(t).hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?"show":"hide",n=this.getDatasetMeta(t),o=n.controller._resolveAnimations(void 0,s);k(e)?(n.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),o.update(n,{visible:i}),this.update((e=>e.datasetIndex===t?s:void 0)))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),bt.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,i,s),t[i]=s},s=(t,e,i)=>{t.offsetX=e,t.offsetY=i,this._eventHandler(t)};u(this.options.events,(t=>i(t,s)))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(i,s)=>{t[i]&&(e.removeEventListener(this,i,s),delete t[i])},n=(t,e)=>{this.canvas&&this.resize(t,e)};let o;const a=()=>{s("attach",a),this.attached=!0,this.resize(),i("resize",n),i("detach",o)};o=()=>{this.attached=!1,s("resize",n),this._stop(),this._resize(0,0),i("attach",a)},e.isAttached(this.canvas)?a():o()}unbindEvents(){u(this._listeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._listeners={},u(this._responsiveListeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?"set":"remove";let n,o,a,r;for("dataset"===e&&(n=this.getDatasetMeta(t[0].datasetIndex),n.controller["_"+s+"DatasetHoverStyle"]()),a=0,r=t.length;a{const i=this.getDatasetMeta(t);if(!i)throw new Error("No dataset found at index "+t);return{datasetIndex:t,element:i.data[e],index:e}}));!f(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}isPluginEnabled(t){return 1===this._plugins._cache.filter((e=>e.plugin.id===t)).length}_updateHoverStyles(t,e,i){const s=this.options.hover,n=(t,e)=>t.filter((t=>!e.some((e=>t.datasetIndex===e.datasetIndex&&t.index===e.index)))),o=n(e,t),a=i?t:n(t,e);o.length&&this.updateHoverStyle(o,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},s=e=>(e.options.events||this.options.events).includes(t.native.type);if(!1===this.notifyPlugins("beforeEvent",i,s))return;const n=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins("afterEvent",i,s),(n||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:n}=this,o=e,a=this._getActiveElements(t,s,i,o),r=D(t),l=function(t,e,i,s){return i&&"mouseout"!==t.type?s?e:t:null}(t,this._lastEvent,i,r);i&&(this._lastEvent=null,d(n.onHover,[t,a,this],this),r&&d(n.onClick,[t,a,this],this));const h=!f(a,s);return(h||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=l,h}_getActiveElements(t,e,i,s){if("mouseout"===t.type)return[];if(!i)return e;const n=this.options.hover;return this.getElementsAtEventForMode(t,n.mode,n,s)}}function Ln(){return u(Tn.instances,(t=>t._plugins.invalidate()))}function En(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class Rn{static override(t){Object.assign(Rn.prototype,t)}options;constructor(t){this.options=t||{}}init(){}formats(){return En()}parse(){return En()}format(){return En()}add(){return En()}diff(){return En()}startOf(){return En()}endOf(){return En()}}var In={_date:Rn};function zn(t){const e=t.iScale,i=function(t,e){if(!t._cache.$bar){const i=t.getMatchingVisibleMetas(e);let s=[];for(let e=0,n=i.length;et-e)))}return t._cache.$bar}(e,t.type);let s,n,o,a,r=e._length;const l=()=>{32767!==o&&-32768!==o&&(k(a)&&(r=Math.min(r,Math.abs(o-a)||r)),a=o)};for(s=0,n=i.length;sMath.abs(r)&&(l=r,h=a),e[i.axis]=h,e._custom={barStart:l,barEnd:h,start:n,end:o,min:a,max:r}}(t,e,i,s):e[i.axis]=i.parse(t,s),e}function Vn(t,e,i,s){const n=t.iScale,o=t.vScale,a=n.getLabels(),r=n===o,l=[];let h,c,d,u;for(h=i,c=i+s;ht.x,i="left",s="right"):(e=t.base"spacing"!==t,_indexable:t=>"spacing"!==t&&!t.startsWith("borderDash")&&!t.startsWith("hoverBorderDash")};static overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data,{labels:{pointStyle:i,textAlign:s,color:n,useBorderRadius:o,borderRadius:a}}=t.legend.options;return e.labels.length&&e.datasets.length?e.labels.map(((e,r)=>{const l=t.getDatasetMeta(0).controller.getStyle(r);return{text:e,fillStyle:l.backgroundColor,fontColor:n,hidden:!t.getDataVisibility(r),lineDash:l.borderDash,lineDashOffset:l.borderDashOffset,lineJoin:l.borderJoinStyle,lineWidth:l.borderWidth,strokeStyle:l.borderColor,textAlign:s,pointStyle:i,borderRadius:o&&(a||l.borderRadius),index:r}})):[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}}};constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(!1===this._parsing)s._parsed=i;else{let n,a,r=t=>+i[t];if(o(i[t])){const{key:t="value"}=this._parsing;r=e=>+M(i[e],t)}for(n=t,a=t+e;nJ(t,r,l,!0)?1:Math.max(e,e*i,s,s*i),g=(t,e,s)=>J(t,r,l,!0)?-1:Math.min(e,e*i,s,s*i),p=f(0,h,d),m=f(E,c,u),x=g(C,h,d),b=g(C+E,c,u);s=(p-x)/2,n=(m-b)/2,o=-(p+x)/2,a=-(m+b)/2}return{ratioX:s,ratioY:n,offsetX:o,offsetY:a}}(u,d,r),x=(i.width-o)/f,b=(i.height-o)/g,_=Math.max(Math.min(x,b)/2,0),y=c(this.options.radius,_),v=(y-Math.max(y*r,0))/this._getVisibleDatasetWeightTotal();this.offsetX=p*y,this.offsetY=m*y,s.total=this.calculateTotal(),this.outerRadius=y-v*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-v*l,0),this.updateElements(n,0,n.length,t)}_circumference(t,e){const i=this.options,s=this._cachedMeta,n=this._getCircumference();return e&&i.animation.animateRotate||!this.chart.getDataVisibility(t)||null===s._parsed[t]||s.data[t].hidden?0:this.calculateCircumference(s._parsed[t]*n/O)}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=o.chartArea,r=o.options.animation,l=(a.left+a.right)/2,h=(a.top+a.bottom)/2,c=n&&r.animateScale,d=c?0:this.innerRadius,u=c?0:this.outerRadius,{sharedOptions:f,includeOptions:g}=this._getSharedOptions(e,s);let p,m=this._getRotation();for(p=0;p0&&!isNaN(t)?O*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t],i.options.locale);return{label:s[t]||"",value:n}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,n,o,a,r;if(!t)for(s=0,n=i.data.datasets.length;s{const o=t.getDatasetMeta(0).controller.getStyle(n);return{text:e,fillStyle:o.backgroundColor,strokeStyle:o.borderColor,fontColor:s,lineWidth:o.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(n),index:n}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}};constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t].r,i.options.locale);return{label:s[t]||"",value:n}}parseObjectData(t,e,i,s){return ii.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}getMinMax(){const t=this._cachedMeta,e={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return t.data.forEach(((t,i)=>{const s=this.getParsed(i).r;!isNaN(s)&&this.chart.getDataVisibility(i)&&(se.max&&(e.max=s))})),e}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),n=Math.max(s/2,0),o=(n-Math.max(i.cutoutPercentage?n/100*i.cutoutPercentage:1,0))/t.getVisibleDatasetCount();this.outerRadius=n-o*this.index,this.innerRadius=this.outerRadius-o}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=o.options.animation,r=this._cachedMeta.rScale,l=r.xCenter,h=r.yCenter,c=r.getIndexAngle(0)-.5*C;let d,u=c;const f=360/this.countVisibleElements();for(d=0;d{!isNaN(this.getParsed(i).r)&&this.chart.getDataVisibility(i)&&e++})),e}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?$(this.resolveDataElementOptions(t,e).angle||i):0}}var Un=Object.freeze({__proto__:null,BarController:class extends js{static id="bar";static defaults={datasetElementType:!1,dataElementType:"bar",categoryPercentage:.8,barPercentage:.9,grouped:!0,animations:{numbers:{type:"number",properties:["x","y","base","width","height"]}}};static overrides={scales:{_index_:{type:"category",offset:!0,grid:{offset:!0}},_value_:{type:"linear",beginAtZero:!0}}};parsePrimitiveData(t,e,i,s){return Vn(t,e,i,s)}parseArrayData(t,e,i,s){return Vn(t,e,i,s)}parseObjectData(t,e,i,s){const{iScale:n,vScale:o}=t,{xAxisKey:a="x",yAxisKey:r="y"}=this._parsing,l="x"===n.axis?a:r,h="x"===o.axis?a:r,c=[];let d,u,f,g;for(d=i,u=i+s;dt.controller.options.grouped)),o=i.options.stacked,a=[],r=this._cachedMeta.controller.getParsed(e),l=r&&r[i.axis],h=t=>{const e=t._parsed.find((t=>t[i.axis]===l)),n=e&&e[t.vScale.axis];if(s(n)||isNaN(n))return!0};for(const i of n)if((void 0===e||!h(i))&&((!1===o||-1===a.indexOf(i.stack)||void 0===o&&void 0===i.stack)&&a.push(i.stack),i.index===t))break;return a.length||a.push(void 0),a}_getStackCount(t){return this._getStacks(void 0,t).length}_getAxisCount(){return this._getAxis().length}getFirstScaleIdForIndexAxis(){const t=this.chart.scales,e=this.chart.options.indexAxis;return Object.keys(t).filter((i=>t[i].axis===e)).shift()}_getAxis(){const t={},e=this.getFirstScaleIdForIndexAxis();for(const i of this.chart.data.datasets)t[l("x"===this.chart.options.indexAxis?i.xAxisID:i.yAxisID,e)]=!0;return Object.keys(t)}_getStackIndex(t,e,i){const s=this._getStacks(t,i),n=void 0!==e?s.indexOf(e):-1;return-1===n?s.length-1:n}_getRuler(){const t=this.options,e=this._cachedMeta,i=e.iScale,s=[];let n,o;for(n=0,o=e.data.length;n=i?1:-1)}(u,e,r)*a,f===r&&(x-=u/2);const t=e.getPixelForDecimal(0),s=e.getPixelForDecimal(1),o=Math.min(t,s),h=Math.max(t,s);x=Math.max(Math.min(x,h),o),d=x+u,i&&!c&&(l._stacks[e.axis]._visualValues[n]=e.getValueForPixel(d)-e.getValueForPixel(x))}if(x===e.getPixelForValue(r)){const t=F(u)*e.getLineWidthForValue(r)/2;x+=t,u-=t}return{size:u,base:x,head:d,center:d+u/2}}_calculateBarIndexPixels(t,e){const i=e.scale,n=this.options,o=n.skipNull,a=l(n.maxBarThickness,1/0);let r,h;const c=this._getAxisCount();if(e.grouped){const i=o?this._getStackCount(t):e.stackCount,d="flex"===n.barThickness?function(t,e,i,s){const n=e.pixels,o=n[t];let a=t>0?n[t-1]:null,r=t=0;--i)e=Math.max(e,t[i].size(this.resolveDataElementOptions(i))/2);return e>0&&e}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart.data.labels||[],{xScale:s,yScale:n}=e,o=this.getParsed(t),a=s.getLabelForValue(o.x),r=n.getLabelForValue(o.y),l=o._custom;return{label:i[t]||"",value:"("+a+", "+r+(l?", "+l:"")+")"}}update(t){const e=this._cachedMeta.data;this.updateElements(e,0,e.length,t)}updateElements(t,e,i,s){const n="reset"===s,{iScale:o,vScale:a}=this._cachedMeta,{sharedOptions:r,includeOptions:l}=this._getSharedOptions(e,s),h=o.axis,c=a.axis;for(let d=e;d0&&this.getParsed(e-1);for(let i=0;i<_;++i){const g=t[i],_=x?g:{};if(i=b){_.skip=!0;continue}const v=this.getParsed(i),M=s(v[f]),w=_[u]=a.getPixelForValue(v[u],i),k=_[f]=o||M?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,v,l):v[f],i);_.skip=isNaN(w)||isNaN(k)||M,_.stop=i>0&&Math.abs(v[u]-y[u])>m,p&&(_.parsed=v,_.raw=h.data[i]),d&&(_.options=c||this.resolveDataElementOptions(i,g.active?"active":n)),x||this.updateElement(g,i,_,n),y=v}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const n=s[0].size(this.resolveDataElementOptions(0)),o=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,n,o)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}},PieController:class extends $n{static id="pie";static defaults={cutout:0,rotation:0,circumference:360,radius:"100%"}},PolarAreaController:Yn,RadarController:class extends js{static id="radar";static defaults={datasetElementType:"line",dataElementType:"point",indexAxis:"r",showLine:!0,elements:{line:{fill:"start"}}};static overrides={aspectRatio:1,scales:{r:{type:"radialLinear"}}};getLabelAndValue(t){const e=this._cachedMeta.vScale,i=this.getParsed(t);return{label:e.getLabels()[t],value:""+e.getLabelForValue(i[e.axis])}}parseObjectData(t,e,i,s){return ii.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta,i=e.dataset,s=e.data||[],n=e.iScale.getLabels();if(i.points=s,"resize"!==t){const e=this.resolveDatasetElementOptions(t);this.options.showLine||(e.borderWidth=0);const o={_loop:!0,_fullLoop:n.length===s.length,options:e};this.updateElement(i,void 0,o,t)}this.updateElements(s,0,s.length,t)}updateElements(t,e,i,s){const n=this._cachedMeta.rScale,o="reset"===s;for(let a=e;a0&&this.getParsed(e-1);for(let c=e;c0&&Math.abs(i[f]-_[f])>x,m&&(p.parsed=i,p.raw=h.data[c]),u&&(p.options=d||this.resolveDataElementOptions(c,e.active?"active":n)),b||this.updateElement(e,c,p,n),_=i}this.updateSharedOptions(d,n,c)}getMaxOverflow(){const t=this._cachedMeta,e=t.data||[];if(!this.options.showLine){let t=0;for(let i=e.length-1;i>=0;--i)t=Math.max(t,e[i].size(this.resolveDataElementOptions(i))/2);return t>0&&t}const i=t.dataset,s=i.options&&i.options.borderWidth||0;if(!e.length)return s;const n=e[0].size(this.resolveDataElementOptions(0)),o=e[e.length-1].size(this.resolveDataElementOptions(e.length-1));return Math.max(s,n,o)/2}}});function Xn(t,e,i,s){const n=vi(t.options.borderRadius,["outerStart","outerEnd","innerStart","innerEnd"]);const o=(i-e)/2,a=Math.min(o,s*e/2),r=t=>{const e=(i-Math.min(o,t))*s/2;return Z(t,0,Math.min(o,e))};return{outerStart:r(n.outerStart),outerEnd:r(n.outerEnd),innerStart:Z(n.innerStart,0,a),innerEnd:Z(n.innerEnd,0,a)}}function qn(t,e,i,s){return{x:i+t*Math.cos(e),y:s+t*Math.sin(e)}}function Kn(t,e,i,s,n,o){const{x:a,y:r,startAngle:l,pixelMargin:h,innerRadius:c}=e,d=Math.max(e.outerRadius+s+i-h,0),u=c>0?c+s+i+h:0;let f=0;const g=n-l;if(s){const t=((c>0?c-s:0)+(d>0?d-s:0))/2;f=(g-(0!==t?g*t/(t+s):g))/2}const p=(g-Math.max(.001,g*d-i/C)/d)/2,m=l+p+f,x=n-p-f,{outerStart:b,outerEnd:_,innerStart:y,innerEnd:v}=Xn(e,u,d,x-m),M=d-b,w=d-_,k=m+b/M,S=x-_/w,P=u+y,D=u+v,O=m+y/P,A=x-v/D;if(t.beginPath(),o){const e=(k+S)/2;if(t.arc(a,r,d,k,e),t.arc(a,r,d,e,S),_>0){const e=qn(w,S,a,r);t.arc(e.x,e.y,_,S,x+E)}const i=qn(D,x,a,r);if(t.lineTo(i.x,i.y),v>0){const e=qn(D,A,a,r);t.arc(e.x,e.y,v,x+E,A+Math.PI)}const s=(x-v/u+(m+y/u))/2;if(t.arc(a,r,u,x-v/u,s,!0),t.arc(a,r,u,s,m+y/u,!0),y>0){const e=qn(P,O,a,r);t.arc(e.x,e.y,y,O+Math.PI,m-E)}const n=qn(M,m,a,r);if(t.lineTo(n.x,n.y),b>0){const e=qn(M,k,a,r);t.arc(e.x,e.y,b,m-E,k)}}else{t.moveTo(a,r);const e=Math.cos(k)*d+a,i=Math.sin(k)*d+r;t.lineTo(e,i);const s=Math.cos(S)*d+a,n=Math.sin(S)*d+r;t.lineTo(s,n)}t.closePath()}function Gn(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r,options:l}=e,{borderWidth:h,borderJoinStyle:c,borderDash:d,borderDashOffset:u,borderRadius:f}=l,g="inner"===l.borderAlign;if(!h)return;t.setLineDash(d||[]),t.lineDashOffset=u,g?(t.lineWidth=2*h,t.lineJoin=c||"round"):(t.lineWidth=h,t.lineJoin=c||"bevel");let p=e.endAngle;if(o){Kn(t,e,i,s,p,n);for(let e=0;en?(h=n/l,t.arc(o,a,l,i+h,s-h,!0)):t.arc(o,a,n,i+E,s-E),t.closePath(),t.clip()}(t,e,p),l.selfJoin&&p-a>=C&&0===f&&"miter"!==c&&function(t,e,i){const{startAngle:s,x:n,y:o,outerRadius:a,innerRadius:r,options:l}=e,{borderWidth:h,borderJoinStyle:c}=l,d=Math.min(h/a,G(s-i));if(t.beginPath(),t.arc(n,o,a-h/2,s+d/2,i-d/2),r>0){const e=Math.min(h/r,G(s-i));t.arc(n,o,r+h/2,i-e/2,s+e/2,!0)}else{const e=Math.min(h/2,a*G(s-i));if("round"===c)t.arc(n,o,e,i-C/2,s+C/2,!0);else if("bevel"===c){const a=2*e*e,r=-a*Math.cos(i+C/2)+n,l=-a*Math.sin(i+C/2)+o,h=a*Math.cos(s+C/2)+n,c=a*Math.sin(s+C/2)+o;t.lineTo(r,l),t.lineTo(h,c)}}t.closePath(),t.moveTo(0,0),t.rect(0,0,t.canvas.width,t.canvas.height),t.clip("evenodd")}(t,e,p),o||(Kn(t,e,i,s,p,n),t.stroke())}function Jn(t,e,i=e){t.lineCap=l(i.borderCapStyle,e.borderCapStyle),t.setLineDash(l(i.borderDash,e.borderDash)),t.lineDashOffset=l(i.borderDashOffset,e.borderDashOffset),t.lineJoin=l(i.borderJoinStyle,e.borderJoinStyle),t.lineWidth=l(i.borderWidth,e.borderWidth),t.strokeStyle=l(i.borderColor,e.borderColor)}function Zn(t,e,i){t.lineTo(i.x,i.y)}function Qn(t,e,i={}){const s=t.length,{start:n=0,end:o=s-1}=i,{start:a,end:r}=e,l=Math.max(n,a),h=Math.min(o,r),c=nr&&o>r;return{count:s,start:l,loop:e.loop,ilen:h(a+(h?r-t:t))%o,_=()=>{f!==g&&(t.lineTo(m,g),t.lineTo(m,f),t.lineTo(m,p))};for(l&&(d=n[b(0)],t.moveTo(d.x,d.y)),c=0;c<=r;++c){if(d=n[b(c)],d.skip)continue;const e=d.x,i=d.y,s=0|e;s===u?(ig&&(g=i),m=(x*m+e)/++x):(_(),t.lineTo(e,i),u=s,x=0,f=g=i),p=i}_()}function io(t){const e=t.options,i=e.borderDash&&e.borderDash.length;return!(t._decimated||t._loop||e.tension||"monotone"===e.cubicInterpolationMode||e.stepped||i)?eo:to}const so="function"==typeof Path2D;function no(t,e,i,s){so&&!e.options.segment?function(t,e,i,s){let n=e._path;n||(n=e._path=new Path2D,e.path(n,i,s)&&n.closePath()),Jn(t,e.options),t.stroke(n)}(t,e,i,s):function(t,e,i,s){const{segments:n,options:o}=e,a=io(e);for(const r of n)Jn(t,o,r.style),t.beginPath(),a(t,e,r,{start:i,end:i+s-1})&&t.closePath(),t.stroke()}(t,e,i,s)}class oo extends $s{static id="line";static defaults={borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderWidth:3,capBezierPoints:!0,cubicInterpolationMode:"default",fill:!1,spanGaps:!1,stepped:!1,tension:0};static defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};static descriptors={_scriptable:!0,_indexable:t=>"borderDash"!==t&&"fill"!==t};constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const i=this.options;if((i.tension||"monotone"===i.cubicInterpolationMode)&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;hi(this._points,i,t,s,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=zi(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],n=this.points,o=Ii(this,{property:e,start:s,end:s});if(!o.length)return;const a=[],r=function(t){return t.stepped?pi:t.tension||"monotone"===t.cubicInterpolationMode?mi:gi}(i);let l,h;for(l=0,h=o.length;l"borderDash"!==t};circumference;endAngle;fullCircles;innerRadius;outerRadius;pixelMargin;startAngle;constructor(t){super(),this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.getProps(["x","y"],i),{angle:n,distance:o}=X(s,{x:t,y:e}),{startAngle:a,endAngle:r,innerRadius:h,outerRadius:c,circumference:d}=this.getProps(["startAngle","endAngle","innerRadius","outerRadius","circumference"],i),u=(this.options.spacing+this.options.borderWidth)/2,f=l(d,r-a),g=J(n,a,r)&&a!==r,p=f>=O||g,m=tt(o,h+u,c+u);return p&&m}getCenterPoint(t){const{x:e,y:i,startAngle:s,endAngle:n,innerRadius:o,outerRadius:a}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius"],t),{offset:r,spacing:l}=this.options,h=(s+n)/2,c=(o+a+l+r)/2;return{x:e+Math.cos(h)*c,y:i+Math.sin(h)*c}}tooltipPosition(t){return this.getCenterPoint(t)}draw(t){const{options:e,circumference:i}=this,s=(e.offset||0)/4,n=(e.spacing||0)/2,o=e.circular;if(this.pixelMargin="inner"===e.borderAlign?.33:0,this.fullCircles=i>O?Math.floor(i/O):0,0===i||this.innerRadius<0||this.outerRadius<0)return;t.save();const a=(this.startAngle+this.endAngle)/2;t.translate(Math.cos(a)*s,Math.sin(a)*s);const r=s*(1-Math.sin(Math.min(C,i||0)));t.fillStyle=e.backgroundColor,t.strokeStyle=e.borderColor,function(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r}=e;let l=e.endAngle;if(o){Kn(t,e,i,s,l,n);for(let e=0;e("string"==typeof e?(i=t.push(e)-1,s.unshift({index:i,label:e})):isNaN(e)&&(i=null),i))(t,e,i,s);return n!==t.lastIndexOf(e)?i:n}function mo(t){const e=this.getLabels();return t>=0&&ts=e?s:t,a=t=>n=i?n:t;if(t){const t=F(s),e=F(n);t<0&&e<0?a(0):t>0&&e>0&&o(0)}if(s===n){let e=0===n?1:Math.abs(.05*n);a(n+e),t||o(s-e)}this.min=s,this.max=n}getTickLimit(){const t=this.options.ticks;let e,{maxTicksLimit:i,stepSize:s}=t;return s?(e=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,e>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${e} ticks. Limiting to 1000.`),e=1e3)):(e=this.computeTickLimit(),i=i||11),i&&(e=Math.min(i,e)),e}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const n=function(t,e){const i=[],{bounds:n,step:o,min:a,max:r,precision:l,count:h,maxTicks:c,maxDigits:d,includeBounds:u}=t,f=o||1,g=c-1,{min:p,max:m}=e,x=!s(a),b=!s(r),_=!s(h),y=(m-p)/(d+1);let v,M,w,k,S=B((m-p)/g/f)*f;if(S<1e-14&&!x&&!b)return[{value:p},{value:m}];k=Math.ceil(m/S)-Math.floor(p/S),k>g&&(S=B(k*S/g/f)*f),s(l)||(v=Math.pow(10,l),S=Math.ceil(S*v)/v),"ticks"===n?(M=Math.floor(p/S)*S,w=Math.ceil(m/S)*S):(M=p,w=m),x&&b&&o&&H((r-a)/o,S/1e3)?(k=Math.round(Math.min((r-a)/S,c)),S=(r-a)/k,M=a,w=r):_?(M=x?a:M,w=b?r:w,k=h-1,S=(w-M)/k):(k=(w-M)/S,k=V(k,Math.round(k),S/1e3)?Math.round(k):Math.ceil(k));const P=Math.max(U(S),U(M));v=Math.pow(10,s(l)?P:l),M=Math.round(M*v)/v,w=Math.round(w*v)/v;let D=0;for(x&&(u&&M!==a?(i.push({value:a}),Mr)break;i.push({value:t})}return b&&u&&w!==r?i.length&&V(i[i.length-1].value,r,xo(r,y,t))?i[i.length-1].value=r:i.push({value:r}):b&&w!==r||i.push({value:w}),i}({maxTicks:i,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:!1!==e.includeBounds},this._range||this);return"ticks"===t.bounds&&j(n,this,"value"),t.reverse?(n.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),n}configure(){const t=this.ticks;let e=this.min,i=this.max;if(super.configure(),this.options.offset&&t.length){const s=(i-e)/Math.max(t.length-1,1)/2;e-=s,i+=s}this._startValue=e,this._endValue=i,this._valueRange=i-e}getLabelForValue(t){return ne(t,this.chart.options.locale,this.options.ticks.format)}}class _o extends bo{static id="linear";static defaults={ticks:{callback:ae.formatters.numeric}};determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?t:0,this.max=a(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,i=$(this.options.ticks.minRotation),s=(t?Math.sin(i):Math.cos(i))||.001,n=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,n.lineHeight/s))}getPixelForValue(t){return null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}const yo=t=>Math.floor(z(t)),vo=(t,e)=>Math.pow(10,yo(t)+e);function Mo(t){return 1===t/Math.pow(10,yo(t))}function wo(t,e,i){const s=Math.pow(10,i),n=Math.floor(t/s);return Math.ceil(e/s)-n}function ko(t,{min:e,max:i}){e=r(t.min,e);const s=[],n=yo(e);let o=function(t,e){let i=yo(e-t);for(;wo(t,e,i)>10;)i++;for(;wo(t,e,i)<10;)i--;return Math.min(i,yo(t))}(e,i),a=o<0?Math.pow(10,Math.abs(o)):1;const l=Math.pow(10,o),h=n>o?Math.pow(10,n):0,c=Math.round((e-h)*a)/a,d=Math.floor((e-h)/l/10)*l*10;let u=Math.floor((c-d)/Math.pow(10,o)),f=r(t.min,Math.round((h+d+u*Math.pow(10,o))*a)/a);for(;f=10?u=u<15?15:20:u++,u>=20&&(o++,u=2,a=o>=0?1:a),f=Math.round((h+d+u*Math.pow(10,o))*a)/a;const g=r(t.max,f);return s.push({value:g,major:Mo(g),significand:u}),s}class So extends tn{static id="logarithmic";static defaults={ticks:{callback:ae.formatters.logarithmic,major:{enabled:!0}}};constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(t,e){const i=bo.prototype.parse.apply(this,[t,e]);if(0!==i)return a(i)&&i>0?i:null;this._zero=!0}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?Math.max(0,t):null,this.max=a(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!a(this._userMin)&&(this.min=t===vo(this.min,0)?vo(this.min,-1):vo(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const n=e=>i=t?i:e,o=t=>s=e?s:t;i===s&&(i<=0?(n(1),o(10)):(n(vo(i,-1)),o(vo(s,1)))),i<=0&&n(vo(s,-1)),s<=0&&o(vo(i,1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e=ko({min:this._userMin,max:this._userMax},this);return"ticks"===t.bounds&&j(e,this,"value"),t.reverse?(e.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),e}getLabelForValue(t){return void 0===t?"0":ne(t,this.chart.options.locale,this.options.ticks.format)}configure(){const t=this.min;super.configure(),this._startValue=z(t),this._valueRange=z(this.max)-z(t)}getPixelForValue(t){return void 0!==t&&0!==t||(t=this.min),null===t||isNaN(t)?NaN:this.getPixelForDecimal(t===this.min?0:(z(t)-this._startValue)/this._valueRange)}getValueForPixel(t){const e=this.getDecimalForPixel(t);return Math.pow(10,this._startValue+e*this._valueRange)}}function Po(t){const e=t.ticks;if(e.display&&t.display){const t=ki(e.backdropPadding);return l(e.font&&e.font.size,ue.font.size)+t.height}return 0}function Do(t,e,i,s,n){return t===s||t===n?{start:e-i/2,end:e+i/2}:tn?{start:e-i,end:e}:{start:e,end:e+i}}function Co(t){const e={l:t.left+t._padding.left,r:t.right-t._padding.right,t:t.top+t._padding.top,b:t.bottom-t._padding.bottom},i=Object.assign({},e),s=[],o=[],a=t._pointLabels.length,r=t.options.pointLabels,l=r.centerPointLabels?C/a:0;for(let u=0;ue.r&&(r=(s.end-e.r)/o,t.r=Math.max(t.r,e.r+r)),n.starte.b&&(l=(n.end-e.b)/a,t.b=Math.max(t.b,e.b+l))}function Ao(t,e,i){const s=t.drawingArea,{extra:n,additionalAngle:o,padding:a,size:r}=i,l=t.getPointPosition(e,s+n+a,o),h=Math.round(Y(G(l.angle+E))),c=function(t,e,i){90===i||270===i?t-=e/2:(i>270||i<90)&&(t-=e);return t}(l.y,r.h,h),d=function(t){if(0===t||180===t)return"center";if(t<180)return"left";return"right"}(h),u=function(t,e,i){"right"===i?t-=e:"center"===i&&(t-=e/2);return t}(l.x,r.w,d);return{visible:!0,x:l.x,y:c,textAlign:d,left:u,top:c,right:u+r.w,bottom:c+r.h}}function To(t,e){if(!e)return!0;const{left:i,top:s,right:n,bottom:o}=t;return!(Re({x:i,y:s},e)||Re({x:i,y:o},e)||Re({x:n,y:s},e)||Re({x:n,y:o},e))}function Lo(t,e,i){const{left:n,top:o,right:a,bottom:r}=i,{backdropColor:l}=e;if(!s(l)){const i=wi(e.borderRadius),s=ki(e.backdropPadding);t.fillStyle=l;const h=n-s.left,c=o-s.top,d=a-n+s.width,u=r-o+s.height;Object.values(i).some((t=>0!==t))?(t.beginPath(),He(t,{x:h,y:c,w:d,h:u,radius:i}),t.fill()):t.fillRect(h,c,d,u)}}function Eo(t,e,i,s){const{ctx:n}=t;if(i)n.arc(t.xCenter,t.yCenter,e,0,O);else{let i=t.getPointPosition(0,e);n.moveTo(i.x,i.y);for(let o=1;ot,padding:5,centerPointLabels:!1}};static defaultRoutes={"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"};static descriptors={angleLines:{_fallback:"grid"}};constructor(t){super(t),this.xCenter=void 0,this.yCenter=void 0,this.drawingArea=void 0,this._pointLabels=[],this._pointLabelItems=[]}setDimensions(){const t=this._padding=ki(Po(this.options)/2),e=this.width=this.maxWidth-t.width,i=this.height=this.maxHeight-t.height;this.xCenter=Math.floor(this.left+e/2+t.left),this.yCenter=Math.floor(this.top+i/2+t.top),this.drawingArea=Math.floor(Math.min(e,i)/2)}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!1);this.min=a(t)&&!isNaN(t)?t:0,this.max=a(e)&&!isNaN(e)?e:0,this.handleTickRangeOptions()}computeTickLimit(){return Math.ceil(this.drawingArea/Po(this.options))}generateTickLabels(t){bo.prototype.generateTickLabels.call(this,t),this._pointLabels=this.getLabels().map(((t,e)=>{const i=d(this.options.pointLabels.callback,[t,e],this);return i||0===i?i:""})).filter(((t,e)=>this.chart.getDataVisibility(e)))}fit(){const t=this.options;t.display&&t.pointLabels.display?Co(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){return G(t*(O/(this._pointLabels.length||1))+$(this.options.startAngle||0))}getDistanceFromCenterForValue(t){if(s(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if(s(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t=0;n--){const e=t._pointLabelItems[n];if(!e.visible)continue;const o=s.setContext(t.getPointLabelContext(n));Lo(i,o,e);const a=Si(o.font),{x:r,y:l,textAlign:h}=e;Ne(i,t._pointLabels[n],r,l+a.lineHeight/2,a,{color:o.color,textAlign:h,textBaseline:"middle"})}}(this,o),s.display&&this.ticks.forEach(((t,e)=>{if(0!==e||0===e&&this.min<0){r=this.getDistanceFromCenterForValue(t.value);const i=this.getContext(e),a=s.setContext(i),l=n.setContext(i);!function(t,e,i,s,n){const o=t.ctx,a=e.circular,{color:r,lineWidth:l}=e;!a&&!s||!r||!l||i<0||(o.save(),o.strokeStyle=r,o.lineWidth=l,o.setLineDash(n.dash||[]),o.lineDashOffset=n.dashOffset,o.beginPath(),Eo(t,i,a,s),o.closePath(),o.stroke(),o.restore())}(this,a,r,o,l)}})),i.display){for(t.save(),a=o-1;a>=0;a--){const s=i.setContext(this.getPointLabelContext(a)),{color:n,lineWidth:o}=s;o&&n&&(t.lineWidth=o,t.strokeStyle=n,t.setLineDash(s.borderDash),t.lineDashOffset=s.borderDashOffset,r=this.getDistanceFromCenterForValue(e.reverse?this.min:this.max),l=this.getPointPosition(a,r),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(l.x,l.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let n,o;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach(((s,a)=>{if(0===a&&this.min>=0&&!e.reverse)return;const r=i.setContext(this.getContext(a)),l=Si(r.font);if(n=this.getDistanceFromCenterForValue(this.ticks[a].value),r.showLabelBackdrop){t.font=l.string,o=t.measureText(s.label).width,t.fillStyle=r.backdropColor;const e=ki(r.backdropPadding);t.fillRect(-o/2-e.left,-n-l.size/2-e.top,o+e.width,l.size+e.height)}Ne(t,s.label,0,-n,l,{color:r.color,strokeColor:r.textStrokeColor,strokeWidth:r.textStrokeWidth})})),t.restore()}drawTitle(){}}const Io={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},zo=Object.keys(Io);function Fo(t,e){return t-e}function Vo(t,e){if(s(e))return null;const i=t._adapter,{parser:n,round:o,isoWeekday:r}=t._parseOpts;let l=e;return"function"==typeof n&&(l=n(l)),a(l)||(l="string"==typeof n?i.parse(l,n):i.parse(l)),null===l?null:(o&&(l="week"!==o||!N(r)&&!0!==r?i.startOf(l,o):i.startOf(l,"isoWeek",r)),+l)}function Bo(t,e,i,s){const n=zo.length;for(let o=zo.indexOf(t);o=e?i[s]:i[n]]=!0}}else t[e]=!0}function No(t,e,i){const s=[],n={},o=e.length;let a,r;for(a=0;a=0&&(e[l].major=!0);return e}(t,s,n,i):s}class Ho extends tn{static id="time";static defaults={bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{source:"auto",callback:!1,major:{enabled:!1}}};constructor(t){super(t),this._cache={data:[],labels:[],all:[]},this._unit="day",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(t,e={}){const i=t.time||(t.time={}),s=this._adapter=new In._date(t.adapters.date);s.init(e),b(i.displayFormats,s.formats()),this._parseOpts={parser:i.parser,round:i.round,isoWeekday:i.isoWeekday},super.init(t),this._normalized=e.normalized}parse(t,e){return void 0===t?null:Vo(this,t)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const t=this.options,e=this._adapter,i=t.time.unit||"day";let{min:s,max:n,minDefined:o,maxDefined:r}=this.getUserBounds();function l(t){o||isNaN(t.min)||(s=Math.min(s,t.min)),r||isNaN(t.max)||(n=Math.max(n,t.max))}o&&r||(l(this._getLabelBounds()),"ticks"===t.bounds&&"labels"===t.ticks.source||l(this.getMinMax(!1))),s=a(s)&&!isNaN(s)?s:+e.startOf(Date.now(),i),n=a(n)&&!isNaN(n)?n:+e.endOf(Date.now(),i)+1,this.min=Math.min(s,n-1),this.max=Math.max(s+1,n)}_getLabelBounds(){const t=this.getLabelTimestamps();let e=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;return t.length&&(e=t[0],i=t[t.length-1]),{min:e,max:i}}buildTicks(){const t=this.options,e=t.time,i=t.ticks,s="labels"===i.source?this.getLabelTimestamps():this._generate();"ticks"===t.bounds&&s.length&&(this.min=this._userMin||s[0],this.max=this._userMax||s[s.length-1]);const n=this.min,o=nt(s,n,this.max);return this._unit=e.unit||(i.autoSkip?Bo(e.minUnit,this.min,this.max,this._getLabelCapacity(n)):function(t,e,i,s,n){for(let o=zo.length-1;o>=zo.indexOf(i);o--){const i=zo[o];if(Io[i].common&&t._adapter.diff(n,s,i)>=e-1)return i}return zo[i?zo.indexOf(i):0]}(this,o.length,e.minUnit,this.min,this.max)),this._majorUnit=i.major.enabled&&"year"!==this._unit?function(t){for(let e=zo.indexOf(t)+1,i=zo.length;e+t.value)))}initOffsets(t=[]){let e,i,s=0,n=0;this.options.offset&&t.length&&(e=this.getDecimalForValue(t[0]),s=1===t.length?1-e:(this.getDecimalForValue(t[1])-e)/2,i=this.getDecimalForValue(t[t.length-1]),n=1===t.length?i:(i-this.getDecimalForValue(t[t.length-2]))/2);const o=t.length<3?.5:.25;s=Z(s,0,o),n=Z(n,0,o),this._offsets={start:s,end:n,factor:1/(s+1+n)}}_generate(){const t=this._adapter,e=this.min,i=this.max,s=this.options,n=s.time,o=n.unit||Bo(n.minUnit,e,i,this._getLabelCapacity(e)),a=l(s.ticks.stepSize,1),r="week"===o&&n.isoWeekday,h=N(r)||!0===r,c={};let d,u,f=e;if(h&&(f=+t.startOf(f,"isoWeek",r)),f=+t.startOf(f,h?"day":o),t.diff(i,e,o)>1e5*a)throw new Error(e+" and "+i+" are too far apart with stepSize of "+a+" "+o);const g="data"===s.ticks.source&&this.getDataTimestamps();for(d=f,u=0;d+t))}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}format(t,e){const i=this.options.time.displayFormats,s=this._unit,n=e||i[s];return this._adapter.format(t,n)}_tickFormatFunction(t,e,i,s){const n=this.options,o=n.ticks.callback;if(o)return d(o,[t,e,i],this);const a=n.time.displayFormats,r=this._unit,l=this._majorUnit,h=r&&a[r],c=l&&a[l],u=i[e],f=l&&c&&u&&u.major;return this._adapter.format(t,s||(f?c:h))}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e0?a:1}getDataTimestamps(){let t,e,i=this._cache.data||[];if(i.length)return i;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(t=0,e=s.length;t=t[r].pos&&e<=t[l].pos&&({lo:r,hi:l}=it(t,"pos",e)),({pos:s,time:o}=t[r]),({pos:n,time:a}=t[l])):(e>=t[r].time&&e<=t[l].time&&({lo:r,hi:l}=it(t,"time",e)),({time:s,pos:o}=t[r]),({time:n,pos:a}=t[l]));const h=n-s;return h?o+(a-o)*(e-s)/h:o}var $o=Object.freeze({__proto__:null,CategoryScale:class extends tn{static id="category";static defaults={ticks:{callback:mo}};constructor(t){super(t),this._startValue=void 0,this._valueRange=0,this._addedLabels=[]}init(t){const e=this._addedLabels;if(e.length){const t=this.getLabels();for(const{index:i,label:s}of e)t[i]===s&&t.splice(i,1);this._addedLabels=[]}super.init(t)}parse(t,e){if(s(t))return null;const i=this.getLabels();return((t,e)=>null===t?null:Z(Math.round(t),0,e))(e=isFinite(e)&&i[e]===t?e:po(i,t,l(e,t),this._addedLabels),i.length-1)}determineDataLimits(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let{min:i,max:s}=this.getMinMax(!0);"ticks"===this.options.bounds&&(t||(i=0),e||(s=this.getLabels().length-1)),this.min=i,this.max=s}buildTicks(){const t=this.min,e=this.max,i=this.options.offset,s=[];let n=this.getLabels();n=0===t&&e===n.length-1?n:n.slice(t,e+1),this._valueRange=Math.max(n.length-(i?0:1),1),this._startValue=this.min-(i?.5:0);for(let i=t;i<=e;i++)s.push({value:i});return s}getLabelForValue(t){return mo.call(this,t)}configure(){super.configure(),this.isHorizontal()||(this._reversePixels=!this._reversePixels)}getPixelForValue(t){return"number"!=typeof t&&(t=this.parse(t)),null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}},LinearScale:_o,LogarithmicScale:So,RadialLinearScale:Ro,TimeScale:Ho,TimeSeriesScale:class extends Ho{static id="timeseries";static defaults=Ho.defaults;constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=jo(e,this.min),this._tableRange=jo(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],n=[];let o,a,r,l,h;for(o=0,a=t.length;o=e&&l<=i&&s.push(l);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(o=0,a=s.length;ot-e))}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),i=this.getLabelTimestamps();return t=e.length&&i.length?this.normalize(e.concat(i)):e.length?e:i,t=this._cache.all=t,t}getDecimalForValue(t){return(jo(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return jo(this._table,i*this._tableRange+this._minPos,!0)}}});const Yo=["rgb(54, 162, 235)","rgb(255, 99, 132)","rgb(255, 159, 64)","rgb(255, 205, 86)","rgb(75, 192, 192)","rgb(153, 102, 255)","rgb(201, 203, 207)"],Uo=Yo.map((t=>t.replace("rgb(","rgba(").replace(")",", 0.5)")));function Xo(t){return Yo[t%Yo.length]}function qo(t){return Uo[t%Uo.length]}function Ko(t){let e=0;return(i,s)=>{const n=t.getDatasetMeta(s).controller;n instanceof $n?e=function(t,e){return t.backgroundColor=t.data.map((()=>Xo(e++))),e}(i,e):n instanceof Yn?e=function(t,e){return t.backgroundColor=t.data.map((()=>qo(e++))),e}(i,e):n&&(e=function(t,e){return t.borderColor=Xo(e),t.backgroundColor=qo(e),++e}(i,e))}}function Go(t){let e;for(e in t)if(t[e].borderColor||t[e].backgroundColor)return!0;return!1}var Jo={id:"colors",defaults:{enabled:!0,forceOverride:!1},beforeLayout(t,e,i){if(!i.enabled)return;const{data:{datasets:s},options:n}=t.config,{elements:o}=n,a=Go(s)||(r=n)&&(r.borderColor||r.backgroundColor)||o&&Go(o)||"rgba(0,0,0,0.1)"!==ue.borderColor||"rgba(0,0,0,0.1)"!==ue.backgroundColor;var r;if(!i.forceOverride&&a)return;const l=Ko(t);s.forEach(l)}};function Zo(t){if(t._decimated){const e=t._data;delete t._decimated,delete t._data,Object.defineProperty(t,"data",{configurable:!0,enumerable:!0,writable:!0,value:e})}}function Qo(t){t.data.datasets.forEach((t=>{Zo(t)}))}var ta={id:"decimation",defaults:{algorithm:"min-max",enabled:!1},beforeElementsUpdate:(t,e,i)=>{if(!i.enabled)return void Qo(t);const n=t.width;t.data.datasets.forEach(((e,o)=>{const{_data:a,indexAxis:r}=e,l=t.getDatasetMeta(o),h=a||e.data;if("y"===Pi([r,t.options.indexAxis]))return;if(!l.controller.supportsDecimation)return;const c=t.scales[l.xAxisID];if("linear"!==c.type&&"time"!==c.type)return;if(t.options.parsing)return;let{start:d,count:u}=function(t,e){const i=e.length;let s,n=0;const{iScale:o}=t,{min:a,max:r,minDefined:l,maxDefined:h}=o.getUserBounds();return l&&(n=Z(it(e,o.axis,a).lo,0,i-1)),s=h?Z(it(e,o.axis,r).hi+1,n,i)-n:i-n,{start:n,count:s}}(l,h);if(u<=(i.threshold||4*n))return void Zo(e);let f;switch(s(a)&&(e._data=h,delete e.data,Object.defineProperty(e,"data",{configurable:!0,enumerable:!0,get:function(){return this._decimated},set:function(t){this._data=t}})),i.algorithm){case"lttb":f=function(t,e,i,s,n){const o=n.samples||s;if(o>=i)return t.slice(e,e+i);const a=[],r=(i-2)/(o-2);let l=0;const h=e+i-1;let c,d,u,f,g,p=e;for(a[l++]=t[p],c=0;cu&&(u=f,d=t[s],g=s);a[l++]=d,p=g}return a[l++]=t[h],a}(h,d,u,n,i);break;case"min-max":f=function(t,e,i,n){let o,a,r,l,h,c,d,u,f,g,p=0,m=0;const x=[],b=e+i-1,_=t[e].x,y=t[b].x-_;for(o=e;og&&(g=l,d=o),p=(m*p+a.x)/++m;else{const i=o-1;if(!s(c)&&!s(d)){const e=Math.min(c,d),s=Math.max(c,d);e!==u&&e!==i&&x.push({...t[e],x:p}),s!==u&&s!==i&&x.push({...t[s],x:p})}o>0&&i!==u&&x.push(t[i]),x.push(a),h=e,m=0,f=g=l,c=d=u=o}}return x}(h,d,u,n);break;default:throw new Error(`Unsupported decimation algorithm '${i.algorithm}'`)}e._decimated=f}))},destroy(t){Qo(t)}};function ea(t,e,i,s){if(s)return;let n=e[t],o=i[t];return"angle"===t&&(n=G(n),o=G(o)),{property:t,start:n,end:o}}function ia(t,e,i){for(;e>t;e--){const t=i[e];if(!isNaN(t.x)&&!isNaN(t.y))break}return e}function sa(t,e,i,s){return t&&e?s(t[i],e[i]):t?t[i]:e?e[i]:0}function na(t,e){let i=[],s=!1;return n(t)?(s=!0,i=t):i=function(t,e){const{x:i=null,y:s=null}=t||{},n=e.points,o=[];return e.segments.forEach((({start:t,end:e})=>{e=ia(t,e,n);const a=n[t],r=n[e];null!==s?(o.push({x:a.x,y:s}),o.push({x:r.x,y:s})):null!==i&&(o.push({x:i,y:a.y}),o.push({x:i,y:r.y}))})),o}(t,e),i.length?new oo({points:i,options:{tension:0},_loop:s,_fullLoop:s}):null}function oa(t){return t&&!1!==t.fill}function aa(t,e,i){let s=t[e].fill;const n=[e];let o;if(!i)return s;for(;!1!==s&&-1===n.indexOf(s);){if(!a(s))return s;if(o=t[s],!o)return!1;if(o.visible)return s;n.push(s),s=o.fill}return!1}function ra(t,e,i){const s=function(t){const e=t.options,i=e.fill;let s=l(i&&i.target,i);void 0===s&&(s=!!e.backgroundColor);if(!1===s||null===s)return!1;if(!0===s)return"origin";return s}(t);if(o(s))return!isNaN(s.value)&&s;let n=parseFloat(s);return a(n)&&Math.floor(n)===n?function(t,e,i,s){"-"!==t&&"+"!==t||(i=e+i);if(i===e||i<0||i>=s)return!1;return i}(s[0],e,n,i):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}function la(t,e,i){const s=[];for(let n=0;n=0;--e){const i=n[e].$filler;i&&(i.line.updateControlPoints(o,i.axis),s&&i.fill&&ua(t.ctx,i,o))}},beforeDatasetsDraw(t,e,i){if("beforeDatasetsDraw"!==i.drawTime)return;const s=t.getSortedVisibleDatasetMetas();for(let e=s.length-1;e>=0;--e){const i=s[e].$filler;oa(i)&&ua(t.ctx,i,t.chartArea)}},beforeDatasetDraw(t,e,i){const s=e.meta.$filler;oa(s)&&"beforeDatasetDraw"===i.drawTime&&ua(t.ctx,s,t.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const _a=(t,e)=>{let{boxHeight:i=e,boxWidth:s=e}=t;return t.usePointStyle&&(i=Math.min(i,e),s=t.pointStyleWidth||Math.min(s,e)),{boxWidth:s,boxHeight:i,itemHeight:Math.max(e,i)}};class ya extends $s{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=d(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter((e=>t.filter(e,this.chart.data)))),t.sort&&(e=e.sort(((e,i)=>t.sort(e,i,this.chart.data)))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display)return void(this.width=this.height=0);const i=t.labels,s=Si(i.font),n=s.size,o=this._computeTitleHeight(),{boxWidth:a,itemHeight:r}=_a(i,n);let l,h;e.font=s.string,this.isHorizontal()?(l=this.maxWidth,h=this._fitRows(o,n,a,r)+10):(h=this.maxHeight,l=this._fitCols(o,s,a,r)+10),this.width=Math.min(l,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:n,maxWidth:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.lineWidths=[0],h=s+a;let c=t;n.textAlign="left",n.textBaseline="middle";let d=-1,u=-h;return this.legendItems.forEach(((t,f)=>{const g=i+e/2+n.measureText(t.text).width;(0===f||l[l.length-1]+g+2*a>o)&&(c+=h,l[l.length-(f>0?0:1)]=0,u+=h,d++),r[f]={left:0,top:u,row:d,width:g,height:s},l[l.length-1]+=g+a})),c}_fitCols(t,e,i,s){const{ctx:n,maxHeight:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.columnSizes=[],h=o-t;let c=a,d=0,u=0,f=0,g=0;return this.legendItems.forEach(((t,o)=>{const{itemWidth:p,itemHeight:m}=function(t,e,i,s,n){const o=function(t,e,i,s){let n=t.text;n&&"string"!=typeof n&&(n=n.reduce(((t,e)=>t.length>e.length?t:e)));return e+i.size/2+s.measureText(n).width}(s,t,e,i),a=function(t,e,i){let s=t;"string"!=typeof e.text&&(s=va(e,i));return s}(n,s,e.lineHeight);return{itemWidth:o,itemHeight:a}}(i,e,n,t,s);o>0&&u+m+2*a>h&&(c+=d+a,l.push({width:d,height:u}),f+=d+a,g++,d=u=0),r[o]={left:f,top:u,col:g,width:p,height:m},d=Math.max(d,p),u+=m+a})),c+=d,l.push({width:d,height:u}),c}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:n}}=this,o=Oi(n,this.left,this.width);if(this.isHorizontal()){let n=0,a=ft(i,this.left+s,this.right-this.lineWidths[n]);for(const r of e)n!==r.row&&(n=r.row,a=ft(i,this.left+s,this.right-this.lineWidths[n])),r.top+=this.top+t+s,r.left=o.leftForLtr(o.x(a),r.width),a+=r.width+s}else{let n=0,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height);for(const r of e)r.col!==n&&(n=r.col,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height)),r.top=a,r.left+=this.left+s,r.left=o.leftForLtr(o.x(r.left),r.width),a+=r.height+s}}isHorizontal(){return"top"===this.options.position||"bottom"===this.options.position}draw(){if(this.options.display){const t=this.ctx;Ie(t,this),this._draw(),ze(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:n,labels:o}=t,a=ue.color,r=Oi(t.rtl,this.left,this.width),h=Si(o.font),{padding:c}=o,d=h.size,u=d/2;let f;this.drawTitle(),s.textAlign=r.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=h.string;const{boxWidth:g,boxHeight:p,itemHeight:m}=_a(o,d),x=this.isHorizontal(),b=this._computeTitleHeight();f=x?{x:ft(n,this.left+c,this.right-i[0]),y:this.top+c+b,line:0}:{x:this.left+c,y:ft(n,this.top+b+c,this.bottom-e[0].height),line:0},Ai(this.ctx,t.textDirection);const _=m+c;this.legendItems.forEach(((y,v)=>{s.strokeStyle=y.fontColor,s.fillStyle=y.fontColor;const M=s.measureText(y.text).width,w=r.textAlign(y.textAlign||(y.textAlign=o.textAlign)),k=g+u+M;let S=f.x,P=f.y;r.setWidth(this.width),x?v>0&&S+k+c>this.right&&(P=f.y+=_,f.line++,S=f.x=ft(n,this.left+c,this.right-i[f.line])):v>0&&P+_>this.bottom&&(S=f.x=S+e[f.line].width+c,f.line++,P=f.y=ft(n,this.top+b+c,this.bottom-e[f.line].height));if(function(t,e,i){if(isNaN(g)||g<=0||isNaN(p)||p<0)return;s.save();const n=l(i.lineWidth,1);if(s.fillStyle=l(i.fillStyle,a),s.lineCap=l(i.lineCap,"butt"),s.lineDashOffset=l(i.lineDashOffset,0),s.lineJoin=l(i.lineJoin,"miter"),s.lineWidth=n,s.strokeStyle=l(i.strokeStyle,a),s.setLineDash(l(i.lineDash,[])),o.usePointStyle){const a={radius:p*Math.SQRT2/2,pointStyle:i.pointStyle,rotation:i.rotation,borderWidth:n},l=r.xPlus(t,g/2);Ee(s,a,l,e+u,o.pointStyleWidth&&g)}else{const o=e+Math.max((d-p)/2,0),a=r.leftForLtr(t,g),l=wi(i.borderRadius);s.beginPath(),Object.values(l).some((t=>0!==t))?He(s,{x:a,y:o,w:g,h:p,radius:l}):s.rect(a,o,g,p),s.fill(),0!==n&&s.stroke()}s.restore()}(r.x(S),P,y),S=gt(w,S+g+u,x?S+k:this.right,t.rtl),function(t,e,i){Ne(s,i.text,t,e+m/2,h,{strikethrough:i.hidden,textAlign:r.textAlign(i.textAlign)})}(r.x(S),P,y),x)f.x+=k+c;else if("string"!=typeof y.text){const t=h.lineHeight;f.y+=va(y,t)+c}else f.y+=_})),Ti(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=Si(e.font),s=ki(e.padding);if(!e.display)return;const n=Oi(t.rtl,this.left,this.width),o=this.ctx,a=e.position,r=i.size/2,l=s.top+r;let h,c=this.left,d=this.width;if(this.isHorizontal())d=Math.max(...this.lineWidths),h=this.top+l,c=ft(t.align,c,this.right-d);else{const e=this.columnSizes.reduce(((t,e)=>Math.max(t,e.height)),0);h=l+ft(t.align,this.top,this.bottom-e-t.labels.padding-this._computeTitleHeight())}const u=ft(a,c,c+d);o.textAlign=n.textAlign(ut(a)),o.textBaseline="middle",o.strokeStyle=e.color,o.fillStyle=e.color,o.font=i.string,Ne(o,e.text,u,h,i)}_computeTitleHeight(){const t=this.options.title,e=Si(t.font),i=ki(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,n;if(tt(t,this.left,this.right)&&tt(e,this.top,this.bottom))for(n=this.legendHitBoxes,i=0;it.chart.options.color,boxWidth:40,padding:10,generateLabels(t){const e=t.data.datasets,{labels:{usePointStyle:i,pointStyle:s,textAlign:n,color:o,useBorderRadius:a,borderRadius:r}}=t.legend.options;return t._getSortedDatasetMetas().map((t=>{const l=t.controller.getStyle(i?0:void 0),h=ki(l.borderWidth);return{text:e[t.index].label,fillStyle:l.backgroundColor,fontColor:o,hidden:!t.visible,lineCap:l.borderCapStyle,lineDash:l.borderDash,lineDashOffset:l.borderDashOffset,lineJoin:l.borderJoinStyle,lineWidth:(h.width+h.height)/4,strokeStyle:l.borderColor,pointStyle:s||l.pointStyle,rotation:l.rotation,textAlign:n||l.textAlign,borderRadius:a&&(r||l.borderRadius),datasetIndex:t.index}}),this)}},title:{color:t=>t.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:t=>!t.startsWith("on"),labels:{_scriptable:t=>!["generateLabels","filter","sort"].includes(t)}}};class wa extends $s{constructor(t){super(),this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e){const i=this.options;if(this.left=0,this.top=0,!i.display)return void(this.width=this.height=this.right=this.bottom=0);this.width=this.right=t,this.height=this.bottom=e;const s=n(i.text)?i.text.length:1;this._padding=ki(i.padding);const o=s*Si(i.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=o:this.width=o}isHorizontal(){const t=this.options.position;return"top"===t||"bottom"===t}_drawArgs(t){const{top:e,left:i,bottom:s,right:n,options:o}=this,a=o.align;let r,l,h,c=0;return this.isHorizontal()?(l=ft(a,i,n),h=e+t,r=n-i):("left"===o.position?(l=i+t,h=ft(a,s,e),c=-.5*C):(l=n-t,h=ft(a,e,s),c=.5*C),r=s-e),{titleX:l,titleY:h,maxWidth:r,rotation:c}}draw(){const t=this.ctx,e=this.options;if(!e.display)return;const i=Si(e.font),s=i.lineHeight/2+this._padding.top,{titleX:n,titleY:o,maxWidth:a,rotation:r}=this._drawArgs(s);Ne(t,e.text,0,0,i,{color:e.color,maxWidth:a,rotation:r,textAlign:ut(e.align),textBaseline:"middle",translation:[n,o]})}}var ka={id:"title",_element:wa,start(t,e,i){!function(t,e){const i=new wa({ctx:t.ctx,options:e,chart:t});ls.configure(t,i,e),ls.addBox(t,i),t.titleBlock=i}(t,i)},stop(t){const e=t.titleBlock;ls.removeBox(t,e),delete t.titleBlock},beforeUpdate(t,e,i){const s=t.titleBlock;ls.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"bold"},fullSize:!0,padding:10,position:"top",text:"",weight:2e3},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Sa=new WeakMap;var Pa={id:"subtitle",start(t,e,i){const s=new wa({ctx:t.ctx,options:i,chart:t});ls.configure(t,s,i),ls.addBox(t,s),Sa.set(t,s)},stop(t){ls.removeBox(t,Sa.get(t)),Sa.delete(t)},beforeUpdate(t,e,i){const s=Sa.get(t);ls.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"normal"},fullSize:!0,padding:0,position:"top",text:"",weight:1500},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Da={average(t){if(!t.length)return!1;let e,i,s=new Set,n=0,o=0;for(e=0,i=t.length;et+e))/s.size,y:n/o}},nearest(t,e){if(!t.length)return!1;let i,s,n,o=e.x,a=e.y,r=Number.POSITIVE_INFINITY;for(i=0,s=t.length;i-1?t.split("\n"):t}function Aa(t,e){const{element:i,datasetIndex:s,index:n}=e,o=t.getDatasetMeta(s).controller,{label:a,value:r}=o.getLabelAndValue(n);return{chart:t,label:a,parsed:o.getParsed(n),raw:t.data.datasets[s].data[n],formattedValue:r,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:i}}function Ta(t,e){const i=t.chart.ctx,{body:s,footer:n,title:o}=t,{boxWidth:a,boxHeight:r}=e,l=Si(e.bodyFont),h=Si(e.titleFont),c=Si(e.footerFont),d=o.length,f=n.length,g=s.length,p=ki(e.padding);let m=p.height,x=0,b=s.reduce(((t,e)=>t+e.before.length+e.lines.length+e.after.length),0);if(b+=t.beforeBody.length+t.afterBody.length,d&&(m+=d*h.lineHeight+(d-1)*e.titleSpacing+e.titleMarginBottom),b){m+=g*(e.displayColors?Math.max(r,l.lineHeight):l.lineHeight)+(b-g)*l.lineHeight+(b-1)*e.bodySpacing}f&&(m+=e.footerMarginTop+f*c.lineHeight+(f-1)*e.footerSpacing);let _=0;const y=function(t){x=Math.max(x,i.measureText(t).width+_)};return i.save(),i.font=h.string,u(t.title,y),i.font=l.string,u(t.beforeBody.concat(t.afterBody),y),_=e.displayColors?a+2+e.boxPadding:0,u(s,(t=>{u(t.before,y),u(t.lines,y),u(t.after,y)})),_=0,i.font=c.string,u(t.footer,y),i.restore(),x+=p.width,{width:x,height:m}}function La(t,e,i,s){const{x:n,width:o}=i,{width:a,chartArea:{left:r,right:l}}=t;let h="center";return"center"===s?h=n<=(r+l)/2?"left":"right":n<=o/2?h="left":n>=a-o/2&&(h="right"),function(t,e,i,s){const{x:n,width:o}=s,a=i.caretSize+i.caretPadding;return"left"===t&&n+o+a>e.width||"right"===t&&n-o-a<0||void 0}(h,t,e,i)&&(h="center"),h}function Ea(t,e,i){const s=i.yAlign||e.yAlign||function(t,e){const{y:i,height:s}=e;return it.height-s/2?"bottom":"center"}(t,i);return{xAlign:i.xAlign||e.xAlign||La(t,e,i,s),yAlign:s}}function Ra(t,e,i,s){const{caretSize:n,caretPadding:o,cornerRadius:a}=t,{xAlign:r,yAlign:l}=i,h=n+o,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=wi(a);let g=function(t,e){let{x:i,width:s}=t;return"right"===e?i-=s:"center"===e&&(i-=s/2),i}(e,r);const p=function(t,e,i){let{y:s,height:n}=t;return"top"===e?s+=i:s-="bottom"===e?n+i:n/2,s}(e,l,h);return"center"===l?"left"===r?g+=h:"right"===r&&(g-=h):"left"===r?g-=Math.max(c,u)+n:"right"===r&&(g+=Math.max(d,f)+n),{x:Z(g,0,s.width-e.width),y:Z(p,0,s.height-e.height)}}function Ia(t,e,i){const s=ki(i.padding);return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-s.right:t.x+s.left}function za(t){return Ca([],Oa(t))}function Fa(t,e){const i=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return i?t.override(i):t}const Va={beforeTitle:e,title(t){if(t.length>0){const e=t[0],i=e.chart.data.labels,s=i?i.length:0;if(this&&this.options&&"dataset"===this.options.mode)return e.dataset.label||"";if(e.label)return e.label;if(s>0&&e.dataIndex{const e={before:[],lines:[],after:[]},n=Fa(i,t);Ca(e.before,Oa(Ba(n,"beforeLabel",this,t))),Ca(e.lines,Ba(n,"label",this,t)),Ca(e.after,Oa(Ba(n,"afterLabel",this,t))),s.push(e)})),s}getAfterBody(t,e){return za(Ba(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:i}=e,s=Ba(i,"beforeFooter",this,t),n=Ba(i,"footer",this,t),o=Ba(i,"afterFooter",this,t);let a=[];return a=Ca(a,Oa(s)),a=Ca(a,Oa(n)),a=Ca(a,Oa(o)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],n=[],o=[];let a,r,l=[];for(a=0,r=e.length;at.filter(e,s,n,i)))),t.itemSort&&(l=l.sort(((e,s)=>t.itemSort(e,s,i)))),u(l,(e=>{const i=Fa(t.callbacks,e);s.push(Ba(i,"labelColor",this,e)),n.push(Ba(i,"labelPointStyle",this,e)),o.push(Ba(i,"labelTextColor",this,e))})),this.labelColors=s,this.labelPointStyles=n,this.labelTextColors=o,this.dataPoints=l,l}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let n,o=[];if(s.length){const t=Da[i.position].call(this,s,this._eventPosition);o=this._createItems(i),this.title=this.getTitle(o,i),this.beforeBody=this.getBeforeBody(o,i),this.body=this.getBody(o,i),this.afterBody=this.getAfterBody(o,i),this.footer=this.getFooter(o,i);const e=this._size=Ta(this,i),a=Object.assign({},t,e),r=Ea(this.chart,i,a),l=Ra(i,a,r,this.chart);this.xAlign=r.xAlign,this.yAlign=r.yAlign,n={opacity:1,x:l.x,y:l.y,width:e.width,height:e.height,caretX:t.x,caretY:t.y}}else 0!==this.opacity&&(n={opacity:0});this._tooltipItems=o,this.$context=void 0,n&&this._resolveAnimations().update(this,n),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const n=this.getCaretPosition(t,i,s);e.lineTo(n.x1,n.y1),e.lineTo(n.x2,n.y2),e.lineTo(n.x3,n.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:n}=this,{caretSize:o,cornerRadius:a}=i,{topLeft:r,topRight:l,bottomLeft:h,bottomRight:c}=wi(a),{x:d,y:u}=t,{width:f,height:g}=e;let p,m,x,b,_,y;return"center"===n?(_=u+g/2,"left"===s?(p=d,m=p-o,b=_+o,y=_-o):(p=d+f,m=p+o,b=_-o,y=_+o),x=p):(m="left"===s?d+Math.max(r,h)+o:"right"===s?d+f-Math.max(l,c)-o:this.caretX,"top"===n?(b=u,_=b-o,p=m-o,x=m+o):(b=u+g,_=b+o,p=m+o,x=m-o),y=b),{x1:p,x2:m,x3:x,y1:b,y2:_,y3:y}}drawTitle(t,e,i){const s=this.title,n=s.length;let o,a,r;if(n){const l=Oi(i.rtl,this.x,this.width);for(t.x=Ia(this,i.titleAlign,i),e.textAlign=l.textAlign(i.titleAlign),e.textBaseline="middle",o=Si(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=o.string,r=0;r0!==t))?(t.beginPath(),t.fillStyle=n.multiKeyBackground,He(t,{x:e,y:g,w:h,h:l,radius:r}),t.fill(),t.stroke(),t.fillStyle=a.backgroundColor,t.beginPath(),He(t,{x:i,y:g+1,w:h-2,h:l-2,radius:r}),t.fill()):(t.fillStyle=n.multiKeyBackground,t.fillRect(e,g,h,l),t.strokeRect(e,g,h,l),t.fillStyle=a.backgroundColor,t.fillRect(i,g+1,h-2,l-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:n,bodyAlign:o,displayColors:a,boxHeight:r,boxWidth:l,boxPadding:h}=i,c=Si(i.bodyFont);let d=c.lineHeight,f=0;const g=Oi(i.rtl,this.x,this.width),p=function(i){e.fillText(i,g.x(t.x+f),t.y+d/2),t.y+=d+n},m=g.textAlign(o);let x,b,_,y,v,M,w;for(e.textAlign=o,e.textBaseline="middle",e.font=c.string,t.x=Ia(this,m,i),e.fillStyle=i.bodyColor,u(this.beforeBody,p),f=a&&"right"!==m?"center"===o?l/2+h:l+2+h:0,y=0,M=s.length;y0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,n=i&&i.y;if(s||n){const i=Da[t.position].call(this,this._active,this._eventPosition);if(!i)return;const o=this._size=Ta(this,t),a=Object.assign({},i,this._size),r=Ea(e,t,a),l=Ra(t,a,r,e);s._to===l.x&&n._to===l.y||(this.xAlign=r.xAlign,this.yAlign=r.yAlign,this.width=o.width,this.height=o.height,this.caretX=i.x,this.caretY=i.y,this._resolveAnimations().update(this,l))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},n={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const o=ki(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=i,this.drawBackground(n,t,s,e),Ai(t,e.textDirection),n.y+=o.top,this.drawTitle(n,t,e),this.drawBody(n,t,e),this.drawFooter(n,t,e),Ti(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map((({datasetIndex:t,index:e})=>{const i=this.chart.getDatasetMeta(t);if(!i)throw new Error("Cannot find a dataset at index "+t);return{datasetIndex:t,element:i.data[e],index:e}})),n=!f(i,s),o=this._positionChanged(s,e);(n||o)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,n=this._active||[],o=this._getActiveElements(t,n,e,i),a=this._positionChanged(o,t),r=e||!f(o,n)||a;return r&&(this._active=o,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),r}_getActiveElements(t,e,i,s){const n=this.options;if("mouseout"===t.type)return[];if(!s)return e.filter((t=>this.chart.data.datasets[t.datasetIndex]&&void 0!==this.chart.getDatasetMeta(t.datasetIndex).controller.getParsed(t.index)));const o=this.chart.getElementsAtEventForMode(t,n.mode,n,i);return n.reverse&&o.reverse(),o}_positionChanged(t,e){const{caretX:i,caretY:s,options:n}=this,o=Da[n.position].call(this,t,e);return!1!==o&&(i!==o.x||s!==o.y)}}var Na={id:"tooltip",_element:Wa,positioners:Da,afterInit(t,e,i){i&&(t.tooltip=new Wa({chart:t,options:i}))},beforeUpdate(t,e,i){t.tooltip&&t.tooltip.initialize(i)},reset(t,e,i){t.tooltip&&t.tooltip.initialize(i)},afterDraw(t){const e=t.tooltip;if(e&&e._willRender()){const i={tooltip:e};if(!1===t.notifyPlugins("beforeTooltipDraw",{...i,cancelable:!0}))return;e.draw(t.ctx),t.notifyPlugins("afterTooltipDraw",i)}},afterEvent(t,e){if(t.tooltip){const i=e.replay;t.tooltip.handleEvent(e.event,i,e.inChartArea)&&(e.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(t,e)=>e.bodyFont.size,boxWidth:(t,e)=>e.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:Va},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:t=>"filter"!==t&&"itemSort"!==t&&"external"!==t,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};return Tn.register(Un,$o,go,t),Tn.helpers={...Hi},Tn._adapters=In,Tn.Animation=As,Tn.Animations=Ts,Tn.animator=bt,Tn.controllers=nn.controllers.items,Tn.DatasetController=js,Tn.Element=$s,Tn.elements=go,Tn.Interaction=Ki,Tn.layouts=ls,Tn.platforms=Ds,Tn.Scale=tn,Tn.Ticks=ae,Object.assign(Tn,Un,$o,go,t,Ds),Tn.Chart=Tn,"undefined"!=typeof window&&(window.Chart=Tn),Tn})); -//# sourceMappingURL=chart.umd.js.map From c0a44093d96986fff0d8753cd573ab7880e62ae2 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 09:45:21 +0200 Subject: [PATCH 067/137] Add a health check and the deployment runbook /healthz checks the database and does a cache ROUND TRIP, not a ping. Both matter: a node that cannot reach Postgres serves nothing, and a cache that accepts writes and returns nothing would have waffle read every feature flag as unset -- so "healthy" has to mean more than "the process is listening", or the load balancer will keep feeding traffic to a node that only looks alive. No auth and no tenant on it: the proxy, and later a load balancer, must reach it on any host. DEPLOYMENT.md is the runbook, and leads with the five things that make this app not a generic Django deploy: the wildcard cert forces DNS-01 (Let's Encrypt will not issue a wildcard over HTTP-01); Redis is required on one server, not two, because of the per-process flag cache; SECURE_PROXY_SSL_HEADER plus Caddy's X-Forwarded-Proto or WebAuthn and the SSL redirect both break; uploads must reach object storage BEFORE the second app server, not during; and invoices need native pango. Also documents why the archive job ships with --commit off, why migrations are run explicitly rather than from the entrypoint, and how to test the restore before the day you need it. Co-Authored-By: Claude Opus 4.8 --- DEPLOYMENT.md | 148 ++++++++++++++++++++++++++++++++++++++++++ rosterchief/health.py | 44 +++++++++++++ rosterchief/tests.py | 52 ++++++++++++++- rosterchief/urls.py | 4 ++ 4 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 DEPLOYMENT.md create mode 100644 rosterchief/health.py diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..5ae6bd3 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,148 @@ +# Deploying RosterChief + +One server today, several later, with no code changes in between — only environment +variables. This document is the runbook and, more usefully, the list of things that are +specific to *this* app and will bite you if you treat it as a generic Django deploy. + +## The five things that make this deployment unusual + +**1. You need a wildcard TLS certificate, and that forces DNS-01.** +Tenancy is subdomain-based (`ajax.rosterchief.app`), so the certificate must cover +`*.rosterchief.app`. Let's Encrypt **will not issue a wildcard over HTTP-01** — only over +DNS-01, which means the TLS terminator needs API access to your DNS zone. That is why +`deploy/caddy/Dockerfile` builds Caddy *with* a DNS provider plugin, and why +`CLOUDFLARE_API_TOKEN` is a required variable rather than a nicety. Swap the plugin +(`caddy-dns/route53`, `caddy-dns/digitalocean`, …) if your DNS lives elsewhere. + +DNS needs two records, both pointing at the server: + +``` +A rosterchief.app -> +A *.rosterchief.app -> +``` + +**2. Redis is not optional, even on one server.** +`waffle` caches each feature flag's targeting in the Django cache, and `LocMemCache` is +private to a single process. Under several gunicorn workers, toggling a feature in the +control panel flushes **one** worker's cache while the others keep serving the stale flag — +a feature that "sometimes doesn't turn on". A shared cache is the fix. + +**3. `SECURE_PROXY_SSL_HEADER` must be set, and Caddy must send the header.** +Caddy terminates TLS, so without it Django believes every request is plain HTTP: +`request.is_secure()` goes false, WebAuthn disagrees with the browser about the origin, and +`SECURE_SSL_REDIRECT` becomes a redirect loop. Both halves are already wired (settings + +`header_up X-Forwarded-Proto`); don't remove either. + +**4. Uploads must move to object storage before the second app server.** +Club logos go to `MEDIA_ROOT` on local disk. On one box that is fine. On two, a logo +uploaded to node A is a 404 on node B. Setting `AWS_STORAGE_BUCKET_NAME` switches the +default storage to S3 — do it *before* you scale, not during. + +**5. PDF invoices need native libraries.** +WeasyPrint binds to pango/cairo. The image installs them; a bare-metal deploy would need +them too, and a Mac needs Homebrew. This is the main reason to run the container even in +development if you touch invoicing. + +## First deploy + +```bash +# 1. Configure +cp .env.compose.example .env # read by docker compose +cp .env.production.example .env.production # read by Django +python -c "import secrets; print(secrets.token_urlsafe(64))" # -> DJANGO_SECRET_KEY + +# 2. Build and start +docker compose build +docker compose up -d db redis +docker compose run --rm web python manage.py migrate +docker compose run --rm web python manage.py createsuperuser +docker compose up -d + +# 3. Verify +curl -fsS https://rosterchief.app/healthz # {"status": "ok", ...} +docker compose run --rm web python manage.py check --deploy +``` + +`check --deploy` is what catches an env file that forgot the HTTPS flags: they default to +**off** in code, because defaulting them to `not DEBUG` would redirect every test request to +https and break the suite anywhere `DEBUG` is unset. + +The first `docker compose up` will take a minute or two: Caddy is provisioning the wildcard +certificate over DNS-01, and DNS propagation is not instant. Watch it with +`docker compose logs -f caddy`. + +## Migrations + +Deliberately **not** run by the container's entrypoint. With more than one web container they +would race, and a starting gunicorn worker is a bad place to discover a failed migration. +Run them once, explicitly, as part of the deploy: + +```bash +docker compose build +docker compose run --rm web python manage.py migrate +docker compose up -d --no-deps web +``` + +## Scheduled jobs + +Two commands need to run on a schedule. Put them on the **host**, not in a container, and on +**exactly one node** when you have several — three nodes archiving the same club is three +emails to the same club. + +```cron +# Bill: archive clubs unpaid past their grace period. +# Run it WITHOUT --commit for the first week and read the output. The flag exists because +# this switches off paying customers: a bad clock or a bad cron should cost you an email, +# not a morning of angry clubs. +0 6 * * * cd /srv/rosterchief && docker compose run --rm web python manage.py archive_overdue_clubs --commit + +# Events: extend recurring series so the calendar never runs dry. +0 3 * * * cd /srv/rosterchief && docker compose run --rm web python manage.py extend_event_series +``` + +## Backups + +Two things carry state: Postgres and the uploads. + +```bash +# Database +docker compose exec -T db pg_dump -U rosterchief rosterchief | gzip > rosterchief-$(date +%F).sql.gz + +# Uploads — until they are on S3, in which case the bucket's own versioning is the backup. +docker compose cp web:/app/media ./media-backup +``` + +Restore is `gunzip -c dump.sql.gz | docker compose exec -T db psql -U rosterchief rosterchief`. +Test it once, now, rather than the first time you need it. + +## Going multi-server + +Nothing in the code changes. What changes is where the services live: + +| | one server | several | +|---|---|---| +| Postgres | `db` container | `DJANGO_DATABASE_URL` → your central Postgres | +| Cache / flags | `redis` container | managed Redis (or your existing one) | +| Uploads | local disk | **S3 bucket** (`AWS_STORAGE_BUCKET_NAME`) | +| Static files | WhiteNoise, in the image | unchanged — that is why WhiteNoise is there | +| Cron | host crontab | one node only | +| TLS | Caddy on the box | load balancer, or Caddy on each node | + +Drop `db` and `redis` from `compose.yaml`, point the URLs at the central services, and run +`web` on as many nodes as you like behind a load balancer pointed at `/healthz`. + +The health check tests the database *and* a cache round trip, not just that the process is +listening — a node that cannot reach Postgres, or whose cache silently swallows writes, is +not healthy, and a load balancer must not keep feeding it traffic. + +## Rollback + +Images are the unit of rollback. Tag on build, keep the last few, and: + +```bash +docker compose up -d --no-deps web # with the previous image tag +``` + +Migrations are the exception: they don't roll back with the image. Prefer additive migrations +(add a column, deploy, backfill, then stop writing the old one) so that yesterday's image +still runs against today's schema. diff --git a/rosterchief/health.py b/rosterchief/health.py new file mode 100644 index 0000000..aa12f1e --- /dev/null +++ b/rosterchief/health.py @@ -0,0 +1,44 @@ +"""Liveness for the proxy today, for a load balancer later. + +Checks the two dependencies whose absence makes the app lie rather than fail: without the +database it cannot serve anything, and without a shared cache the feature flags drift apart +between workers. A health check that only proves the process is listening would call that +healthy. +""" + +from django.core.cache import cache +from django.db import connection +from django.http import JsonResponse +from django.views.decorators.cache import never_cache + +PROBE_KEY = "healthz" + + +@never_cache +def healthz(request): + checks = {"database": _database(), "cache": _cache()} + healthy = all(checks.values()) + + return JsonResponse({"status": "ok" if healthy else "degraded", "checks": checks}, status=200 if healthy else 503) + + +def _database() -> bool: + try: + with connection.cursor() as cursor: + cursor.execute("SELECT 1") + cursor.fetchone() + except Exception: + return False + + return True + + +def _cache() -> bool: + """A round trip, not a ping: a cache that accepts writes and returns nothing is worse + than one that is plainly down, because waffle would read every flag as unset.""" + try: + cache.set(PROBE_KEY, "ok", 10) + + return cache.get(PROBE_KEY) == "ok" + except Exception: + return False diff --git a/rosterchief/tests.py b/rosterchief/tests.py index 2dcf681..3a46149 100644 --- a/rosterchief/tests.py +++ b/rosterchief/tests.py @@ -1,7 +1,9 @@ import importlib +from unittest import mock +from django.db.utils import OperationalError from django.test import SimpleTestCase, override_settings -from django.urls import Resolver404, clear_url_caches, resolve +from django.urls import Resolver404, clear_url_caches, resolve, reverse from . import urls @@ -28,3 +30,51 @@ class BrowserReloadUrlTests(SimpleTestCase): with self.assertRaises(Resolver404): resolve("/__reload__/events/") + + +class HealthCheckTests(SimpleTestCase): + databases = {"default"} + + def test_it_reports_ok_when_the_database_and_cache_answer(self): + response = self.client.get(reverse("healthz")) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), {"status": "ok", "checks": {"database": True, "cache": True}}) + + def test_a_dead_database_is_a_503(self): + # 200 while the database is unreachable is worse than no health check at all: the load + # balancer would keep sending traffic to a node that cannot serve a single page. + with mock.patch("rosterchief.health.connection") as db: + db.cursor.side_effect = OperationalError("connection refused") + + response = self.client.get(reverse("healthz")) + + self.assertEqual(response.status_code, 503) + self.assertFalse(response.json()["checks"]["database"]) + + def test_a_cache_that_swallows_writes_is_unhealthy(self): + # Not a ping: a cache that accepts writes and returns nothing would have waffle read + # every feature flag as unset. + with mock.patch("rosterchief.health.cache") as broken: + broken.get.return_value = None + + response = self.client.get(reverse("healthz")) + + self.assertEqual(response.status_code, 503) + self.assertFalse(response.json()["checks"]["cache"]) + + def test_an_unreachable_cache_is_a_503(self): + # Distinct from the cache that answers wrongly above: here Redis is simply down, and + # the check must fail rather than raise its way to a 500. + with mock.patch("rosterchief.health.cache") as down: + down.set.side_effect = ConnectionError("redis is down") + + response = self.client.get(reverse("healthz")) + + self.assertEqual(response.status_code, 503) + self.assertFalse(response.json()["checks"]["cache"]) + + def test_it_is_never_cached(self): + response = self.client.get(reverse("healthz")) + + self.assertIn("no-cache", response["Cache-Control"]) diff --git a/rosterchief/urls.py b/rosterchief/urls.py index 581bff8..cd1c84d 100644 --- a/rosterchief/urls.py +++ b/rosterchief/urls.py @@ -15,7 +15,11 @@ from django.views.generic import RedirectView from club.views import root +from .health import healthz + urlpatterns = [ + # No auth and no tenant: the proxy and the load balancer must reach it on any host. + path("healthz", healthz, name="healthz"), path("admin/login/", RedirectView.as_view(pattern_name="account_login", query_string=True), name="admin_login_redirect"), path("admin/", admin.site.urls), path("accounts/", include("allauth.urls")), From d30b1631225ebaeb8feed96743ece65e7b146b1c Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 10:11:15 +0200 Subject: [PATCH 068/137] Add maintenance mode: lock the platform down from the control panel Closes every club subdomain with a 503 in that club's own colours, stands the scheduled jobs down, and keeps open exactly what is needed to end it again. The exemptions ARE the feature: - /accounts/ stays open on the base domain. Close it too and you cannot sign in to turn maintenance off -- a lock-down with no key, fixable only from a shell. - /healthz answers on every host. Close it and the load balancer decides the node is dead, stops routing to it, and takes the control panel down with everything else. - migrate and collectstatic are NOT blocked. Maintenance is usually declared in order to run them; a blanket guard on BaseCommand would mean turning the mode off to do the work you turned it on for. Only the domain jobs (archive_overdue_clubs, extend_event_series, import_members_csv) refuse, and they exit non-zero so cron mails you -- a scheduled job that silently skips itself is how a month of billing goes missing. The state is cached with a 10-second TTL, not for ever. Write-through makes the flip instant for the shared Redis of a real deployment, and the TTL is the belt to that braces: on a per-process cache -- a dev box with no Redis, or a misconfigured deploy -- a lock-down that reached only one gunicorn worker would be worse than useless. Live-verified: a club subdomain, its login page and the base domain all 503 while the control panel and the sign-in screens stay up. Also adds the two deployment pieces asked for: compose.behind-proxy.yaml for a dev/test box that already runs Caddy on :80 (app on the loopback, host Caddy proxies to it -- and the host's Caddy still needs the DNS plugin, because the wildcard is still a wildcard), and deploy/backup.sh + restore-check.sh with a cron schedule. The backup writes to a .part file and only lands it once gzip -t says it is readable: a truncated dump that looks like a backup is the failure you find on the day you need it. The weekly restore rehearsal is the only line in that cron that proves the rest work. Co-Authored-By: Claude Opus 4.8 --- DEPLOYMENT.md | 139 ++++++++++++++++- .../commands/archive_overdue_clubs.py | 4 +- compose.behind-proxy.yaml | 55 +++++++ controlpanel/forms.py | 12 ++ controlpanel/templates/controlpanel/base.html | 10 ++ .../templates/controlpanel/features.html | 44 +++++- controlpanel/tests.py | 36 +++++ controlpanel/urls.py | 1 + controlpanel/views.py | 21 ++- deploy/backup.sh | 51 +++++++ deploy/restore-check.sh | 30 ++++ .../commands/extend_event_series.py | 5 +- features/commands.py | 31 ++++ features/context_processors.py | 7 + features/middleware.py | 74 +++++++++ features/migrations/0002_maintenance.py | 33 ++++ features/models.py | 75 +++++++++ features/tests.py | 142 +++++++++++++++++- .../management/commands/import_members_csv.py | 5 +- rosterchief/settings.py | 3 + static/css/app.css | 6 + templates/maintenance.html | 24 +++ 22 files changed, 796 insertions(+), 12 deletions(-) create mode 100644 compose.behind-proxy.yaml create mode 100755 deploy/backup.sh create mode 100755 deploy/restore-check.sh create mode 100644 features/commands.py create mode 100644 features/context_processors.py create mode 100644 features/middleware.py create mode 100644 features/migrations/0002_maintenance.py create mode 100644 templates/maintenance.html diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 5ae6bd3..d81784a 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -100,7 +100,144 @@ emails to the same club. 0 3 * * * cd /srv/rosterchief && docker compose run --rm web python manage.py extend_event_series ``` -## Backups +## Maintenance mode + +Control panel → **Features → Maintenance mode**. While it is on: + +- every **club subdomain** serves a 503 maintenance page, in that club's own colours; +- the **control panel and the sign-in screens stay open**, because closing them would leave + you with no way to turn it back off; +- `/healthz` keeps answering on every host, or the load balancer would take the node out of + rotation and the control panel with it; +- the **scheduled jobs stand down** — `archive_overdue_clubs`, `extend_event_series` and + `import_members_csv` refuse to run. + +`migrate` and `collectstatic` are deliberately **not** blocked. Maintenance is usually +declared *in order* to run them, and a guard that stopped them would mean turning the mode +off to do the work you turned it on for. + +The scheduled jobs exit **non-zero** while the platform is closed, so cron will mail you. +That is intended: a job that silently skips itself is how a month of billing goes missing. If +you genuinely mean to run one during a window, pass `--ignore-maintenance`. + +So a migration-heavy deploy looks like: + +```bash +# 1. Close the platform in the control panel (or from a shell): +docker compose run --rm web python manage.py shell -c \ + "from features.models import Maintenance; Maintenance.start(message='Upgrading. Back by 21:00.')" + +# 2. Do the work — migrate is not blocked. +docker compose build +docker compose run --rm web python manage.py migrate +docker compose up -d --no-deps web + +# 3. Reopen from the control panel. +``` + +The state lives in Redis as well as the database, so it takes effect on **every worker and +every server at once** — a per-process cache would leave some workers still serving clubs. + +## Behind an existing Caddy (dev / test server) + +If the box already runs Caddy on :80 and :443 — a test server sharing a host with other +sites — do **not** run ours: two Caddies cannot both hold port 80. Run the app only, publish +it on the loopback, and add a site block to the Caddy that is already there. + +```bash +docker compose -f compose.behind-proxy.yaml up -d # web + db + redis, no caddy +``` + +`web` publishes on `127.0.0.1:8001` (override with `WEB_PORT`). **Loopback, not 0.0.0.0** — +bound to all interfaces, a test instance is reachable at `http://:8001` with no +TLS, bypassing the proxy and every security header with it. + +Then, in the host's Caddyfile: + +```caddy +test.rosterchief.app, *.test.rosterchief.app { + tls { + dns cloudflare {env.CLOUDFLARE_API_TOKEN} + } + + reverse_proxy 127.0.0.1:8001 { + header_up X-Forwarded-Proto {scheme} + header_up X-Real-IP {remote_host} + } +} +``` + +Three things this needs, and each one is a way to lose an afternoon: + +1. **The host's Caddy must have the DNS plugin too.** The wildcard is still a wildcard: the + stock `caddy` package cannot answer a DNS-01 challenge. `caddy add-package + github.com/caddy-dns/cloudflare` on a package install, or run a Caddy built like + `deploy/caddy/Dockerfile`. +2. **Give the test instance its own subdomain tree** (`*.test.rosterchief.app`) and set + `ROSTERCHIEF_BASE_DOMAIN=test.rosterchief.app`. It drives tenant resolution, the shared + session cookie *and* the WebAuthn RP ID — point it at the production domain and test + passkeys start colliding with real ones. +3. **`header_up X-Forwarded-Proto` is not optional**, exactly as in the bundled Caddyfile. + Without it Django believes the request is plain HTTP behind the proxy. + +DNS still needs both records, pointing at the test box: + +``` +A test.rosterchief.app -> +A *.test.rosterchief.app -> +``` + +The compose project is named `rosterchief-test`, so its containers and volumes never collide +with a production stack on the same host. + +## Automated backups + +`deploy/backup.sh` dumps the database, tars the uploads while they are still on local disk, +prunes anything older than `KEEP_DAYS`, and — if you set `BACKUP_REMOTE` — copies the lot off +the box with rclone. + +```bash +deploy/backup.sh /var/backups/rosterchief +``` + +It writes to a `.part` file and only moves it into place once `gzip -t` says the archive is +readable and non-empty. A truncated dump that *looks* like a backup is the failure mode worth +engineering against, because you only discover it on the day you need it. + +Schedule it as root on the host (single server; on several, run it on the database node): + +```cron +# Nightly at 02:30, before the billing and event jobs. +30 2 * * * cd /srv/rosterchief && BACKUP_REMOTE=b2:rosterchief-backups KEEP_DAYS=14 deploy/backup.sh /var/backups/rosterchief + +# Weekly restore rehearsal into a throwaway database. This is the only line here that proves +# the others work. +0 4 * * 0 cd /srv/rosterchief && deploy/restore-check.sh +``` + +Cron mails you on non-zero exit, and the script uses `set -Eeuo pipefail` so it *does* exit +non-zero. A backup script that fails quietly is worse than none, because you will believe you +have backups. + +**Offsite matters more than frequency.** A dump sitting on the same disk as the database +survives a bad migration but not the server. `BACKUP_REMOTE` takes any rclone remote (S3, +Backblaze, a second box). + +**Once uploads move to S3** (`AWS_STORAGE_BUCKET_NAME`), the script skips the media tarball: +the bucket's own versioning is the backup. Turn versioning on when you create it. + +### Restoring + +```bash +gunzip -c /var/backups/rosterchief/db-2026-07-14-0230.sql.gz \ + | docker compose exec -T db psql -U rosterchief rosterchief +``` + +The dump is taken with `--clean --if-exists`, so it drops and recreates rather than colliding +with what is there. Rehearse it once, now, against a scratch database — not the first time you +need it. + +## Backups (manual) Two things carry state: Postgres and the uploads. diff --git a/billing/management/commands/archive_overdue_clubs.py b/billing/management/commands/archive_overdue_clubs.py index 3c6368b..113c57a 100644 --- a/billing/management/commands/archive_overdue_clubs.py +++ b/billing/management/commands/archive_overdue_clubs.py @@ -5,13 +5,13 @@ switches off paying customers, and a cron misconfiguration, a clock skew or a ba should cost you a confusing email, not a morning of angry clubs. """ -from django.core.management.base import BaseCommand from django.utils import timezone from billing.services.dues import archivable_clubs +from features.commands import MaintenanceAwareCommand -class Command(BaseCommand): +class Command(MaintenanceAwareCommand): help = "Archive clubs that are unpaid past their grace period (dry run unless --commit)." def add_arguments(self, parser): diff --git a/compose.behind-proxy.yaml b/compose.behind-proxy.yaml new file mode 100644 index 0000000..47d4303 --- /dev/null +++ b/compose.behind-proxy.yaml @@ -0,0 +1,55 @@ +# A dev/test deployment on a server that ALREADY runs Caddy on :80/:443. +# +# docker compose -f compose.behind-proxy.yaml up -d +# +# The difference from compose.yaml is only what listens on the network: no caddy service, and +# web publishes on the loopback instead of the public interface. The host's Caddy reverse +# proxies to it (see DEPLOYMENT.md, "Behind an existing Caddy"). +# +# Publishing on 127.0.0.1 and not 0.0.0.0 is the point: bound to all interfaces, a test +# instance is reachable on http://:8001 with no TLS, bypassing the proxy and every +# security header with it. + +name: rosterchief-test + +services: + web: + build: . + restart: unless-stopped + env_file: .env.production + ports: + - "127.0.0.1:${WEB_PORT:-8001}:8000" + depends_on: + db: + condition: service_healthy + redis: + condition: service_started + healthcheck: + test: ["CMD", "curl", "-fsS", "http://localhost:8000/healthz"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 20s + + db: + image: postgres:17-alpine + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB:-rosterchief} + POSTGRES_USER: ${POSTGRES_USER:-rosterchief} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set a database password} + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-rosterchief}"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + restart: unless-stopped + command: ["redis-server", "--save", "", "--appendonly", "no"] + +volumes: + pgdata: diff --git a/controlpanel/forms.py b/controlpanel/forms.py index 92a077d..e8054d2 100644 --- a/controlpanel/forms.py +++ b/controlpanel/forms.py @@ -103,3 +103,15 @@ class OpenPeriodForm(forms.Form): """Renew, or reactivate an archived club.""" start = forms.DateField(required=False, widget=forms.DateInput(attrs={"type": "date"}), label=_("Period starts"), help_text=_("Left blank, it continues from the end of the last period — so a lapsed year is still owed.")) + + +class MaintenanceForm(forms.Form): + """Closing the platform is a deliberate act, so it takes a sentence explaining itself — + that message is the only thing a club will see.""" + + message = forms.CharField( + required=False, + widget=forms.Textarea(attrs={"rows": 2}), + label=_("Message"), + help_text=_("Shown to every club while the platform is closed. Left blank, they get a generic notice."), + ) diff --git a/controlpanel/templates/controlpanel/base.html b/controlpanel/templates/controlpanel/base.html index cd469c9..a08eb18 100644 --- a/controlpanel/templates/controlpanel/base.html +++ b/controlpanel/templates/controlpanel/base.html @@ -6,6 +6,16 @@ {% endblock title %} {% block main %} + {% if maintenance_on %} +
+ {% lucide "wrench" size=20 %} + + The platform is closed for maintenance. + Clubs see a maintenance page and the scheduled jobs are standing down. + + Reopen +
+ {% endif %}

diff --git a/controlpanel/templates/controlpanel/features.html b/controlpanel/templates/controlpanel/features.html index 7c931df..6527131 100644 --- a/controlpanel/templates/controlpanel/features.html +++ b/controlpanel/templates/controlpanel/features.html @@ -1,5 +1,5 @@ {% extends "controlpanel/base.html" %} -{% load lucide %} +{% load lucide ui %} {% block heading %}Features{% endblock heading %} @@ -8,6 +8,48 @@ {% endblock actions %} {% block panel %} + {% comment %} + The lock-down. Clubs get a maintenance page, the scheduled jobs stand down, and the + control panel and the auth screens stay open — otherwise you could not sign in to + turn it back off. + {% endcomment %} +
+
+
+
+

{% lucide "wrench" size=18 %} Maintenance mode

+ {% if maintenance.is_active %} +

+ {% lucide "lock" size=12 %} Platform closed + since {{ maintenance.started_at|date:"j M Y, H:i" }}{% if maintenance.started_by %} by {{ maintenance.started_by.email }}{% endif %}. +

+ {% if maintenance.message %}

“{{ maintenance.message }}”

{% endif %} + {% else %} +

+ Closes every club subdomain and stands the scheduled jobs down. The control panel and the sign-in screens stay open. +

+ {% endif %} +
+
+ +
+ {% csrf_token %} + {% if not maintenance.is_active %} +
+ + {{ maintenance_form.message|daisy }} + {{ maintenance_form.message.help_text }} +
+ + {% else %} + + {% endif %} +
+
+
+

{% lucide "flag" size=18 %} Flags

diff --git a/controlpanel/tests.py b/controlpanel/tests.py index 444151e..f5cdc92 100644 --- a/controlpanel/tests.py +++ b/controlpanel/tests.py @@ -20,6 +20,7 @@ from billing.services import BillingError from billing.services.dues import record_payment, subscribe from club.models import Club, ClubMembership, ClubRole, Season from events.models import Attendance, Event +from features.models import Maintenance from members.models import Member from shop.models import Order from teams.models import Position, StaffAssignment, Team, TeamMembership @@ -1315,3 +1316,38 @@ class BillingFormRenderTests(ControlPanelTestBase): response = self.client.post(reverse("controlpanel:due_waive", args=[due.pk]), follow=True) self.assertContains(response, "remove them before waiving") + + +class MaintenancePanelTests(ControlPanelTestBase): + def setUp(self): + super().setUp() + cache.clear() + self.addCleanup(cache.clear) + + def test_closing_the_platform_records_who_did_it(self): + self.client.post(reverse("controlpanel:maintenance"), {"message": "Database upgrade."}) + + maintenance = Maintenance.current() + self.assertTrue(maintenance.is_active) + self.assertEqual(maintenance.message, "Database upgrade.") + self.assertEqual(maintenance.started_by, self.staff) + + def test_posting_again_reopens_the_platform(self): + Maintenance.start(message="x", user=self.staff) + + self.client.post(reverse("controlpanel:maintenance"), {}) + + self.assertFalse(Maintenance.is_on()) + + def test_every_panel_page_warns_while_the_platform_is_closed(self): + # Not a state to leave on by accident. + Maintenance.start(user=self.staff) + + for url in (reverse("controlpanel:dashboard"), reverse("controlpanel:club_list"), reverse("controlpanel:features")): + self.assertContains(self.client.get(url), "closed for maintenance", msg_prefix=url) + + def test_the_features_page_offers_the_switch(self): + response = self.client.get(reverse("controlpanel:features")) + + self.assertContains(response, "Maintenance mode") + self.assertContains(response, "Close the platform") diff --git a/controlpanel/urls.py b/controlpanel/urls.py index db81799..e3c6fda 100644 --- a/controlpanel/urls.py +++ b/controlpanel/urls.py @@ -18,6 +18,7 @@ urlpatterns = [ path("clubs//features//toggle/", views.ClubFeatureToggleView.as_view(), name="club_feature_toggle"), # Features path("features/", views.FeatureListView.as_view(), name="features"), + path("features/maintenance/", views.MaintenanceView.as_view(), name="maintenance"), path("features/flags/new/", views.FlagCreateView.as_view(), name="flag_create"), path("features/flags//edit/", views.FlagUpdateView.as_view(), name="flag_update"), path("features/switches//toggle/", views.SwitchToggleView.as_view(), name="switch_toggle"), diff --git a/controlpanel/views.py b/controlpanel/views.py index a26ec66..add35dd 100644 --- a/controlpanel/views.py +++ b/controlpanel/views.py @@ -13,8 +13,9 @@ from billing.services import BillingError from billing.services.dues import next_period_start, open_period, reactivate, record_payment, subscribe, waive from billing.services.invoices import invoice_pdf, issue_invoice from club.models import Club, ClubRole +from features.models import Maintenance -from .forms import ClubAdminForm, ClubForm, DuePaymentForm, FlagForm, OpenPeriodForm, PlatformAdminForm, SubscriptionForm, TierForm, TierPriceForm +from .forms import ClubAdminForm, ClubForm, DuePaymentForm, FlagForm, MaintenanceForm, OpenPeriodForm, PlatformAdminForm, SubscriptionForm, TierForm, TierPriceForm from .mixins import PlatformStaffRequiredMixin, PlatformSuperuserRequiredMixin from .services.admins import grant_club_admin, revoke_club_admin from .services.platform_admins import ( @@ -197,10 +198,28 @@ class FeatureListView(PlatformStaffRequiredMixin, TemplateView): nav="features", flags=Flag.objects.prefetch_related("clubs").order_by("name"), switches=Switch.objects.order_by("name"), + maintenance=Maintenance.current(), + maintenance_form=MaintenanceForm(), **kwargs, ) +class MaintenanceView(PlatformStaffRequiredMixin, View): + """Close the platform, or open it again.""" + + def post(self, request): + if Maintenance.is_on(): + Maintenance.stop() + messages.success(request, "Maintenance ended. The clubs are back.") + else: + form = MaintenanceForm(request.POST) + message = form.cleaned_data["message"] if form.is_valid() else "" + Maintenance.start(message=message, user=request.user) + messages.warning(request, "Platform closed. Every club subdomain now serves a maintenance page, and the scheduled jobs stand down.") + + return redirect("controlpanel:features") + + class FlagCreateView(PlatformStaffRequiredMixin, CreateView): model = Flag form_class = FlagForm diff --git a/deploy/backup.sh b/deploy/backup.sh new file mode 100755 index 0000000..410973e --- /dev/null +++ b/deploy/backup.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# Back up what carries state: the database, and the uploads if they are still on local disk. +# +# deploy/backup.sh /var/backups/rosterchief +# +# Runs from cron (see DEPLOYMENT.md). Exits non-zero on any failure, so cron mails you — +# a backup script that fails quietly is worse than no backup script, because you will +# believe you have backups. +set -Eeuo pipefail + +DEST="${1:-/var/backups/rosterchief}" +COMPOSE="${COMPOSE:-docker compose}" +KEEP_DAYS="${KEEP_DAYS:-14}" +STAMP="$(date +%F-%H%M)" + +mkdir -p "$DEST" + +# --- database --------------------------------------------------------------- +# Written to a temporary name and moved into place only on success: a truncated dump that +# looks like a backup is the trap this avoids. +DB_TMP="$DEST/.db-$STAMP.sql.gz.part" +DB_OUT="$DEST/db-$STAMP.sql.gz" + +$COMPOSE exec -T db pg_dump --clean --if-exists -U "${POSTGRES_USER:-rosterchief}" "${POSTGRES_DB:-rosterchief}" | gzip > "$DB_TMP" +gzip -t "$DB_TMP" # the archive is readable +[ -s "$DB_TMP" ] # ...and not empty +mv "$DB_TMP" "$DB_OUT" + +# --- uploads ---------------------------------------------------------------- +# Only while media is local. Once AWS_STORAGE_BUCKET_NAME is set the bucket's own versioning +# is the backup, and this step is skipped. +if [ -z "${AWS_STORAGE_BUCKET_NAME:-}" ]; then + MEDIA_OUT="$DEST/media-$STAMP.tar.gz" + $COMPOSE exec -T web tar -cz -C /app media | cat > "$MEDIA_OUT.part" + mv "$MEDIA_OUT.part" "$MEDIA_OUT" +fi + +# --- retention -------------------------------------------------------------- +find "$DEST" -name 'db-*.sql.gz' -mtime "+$KEEP_DAYS" -delete +find "$DEST" -name 'media-*.tar.gz' -mtime "+$KEEP_DAYS" -delete +find "$DEST" -name '*.part' -mtime +1 -delete + +echo "$(date -Iseconds) backup ok: $(basename "$DB_OUT") ($(du -h "$DB_OUT" | cut -f1))" + +# --- offsite ---------------------------------------------------------------- +# A backup on the same disk as the database is not a backup: it survives a bad migration, but +# not the server. Set BACKUP_REMOTE to an rclone remote to copy it off the box. +if [ -n "${BACKUP_REMOTE:-}" ]; then + rclone copy "$DEST" "$BACKUP_REMOTE" --max-age "${KEEP_DAYS}d" + echo "$(date -Iseconds) copied to $BACKUP_REMOTE" +fi diff --git a/deploy/restore-check.sh b/deploy/restore-check.sh new file mode 100755 index 0000000..dbe73cb --- /dev/null +++ b/deploy/restore-check.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Restore the latest dump into a throwaway database and count the rows. +# +# deploy/restore-check.sh [/var/backups/rosterchief] +# +# The only line in the backup cron that proves the others work. A dump you have never +# restored is a hypothesis, not a backup. +set -Eeuo pipefail + +DEST="${1:-/var/backups/rosterchief}" +COMPOSE="${COMPOSE:-docker compose}" +USER_NAME="${POSTGRES_USER:-rosterchief}" +SCRATCH="restore_check_$(date +%s)" + +LATEST="$(ls -1t "$DEST"/db-*.sql.gz 2>/dev/null | head -1)" +[ -n "$LATEST" ] || { echo "no dump found in $DEST"; exit 1; } + +cleanup() { $COMPOSE exec -T db dropdb -U "$USER_NAME" --if-exists "$SCRATCH" >/dev/null 2>&1 || true; } +trap cleanup EXIT + +$COMPOSE exec -T db createdb -U "$USER_NAME" "$SCRATCH" +gunzip -c "$LATEST" | $COMPOSE exec -T db psql -q -U "$USER_NAME" "$SCRATCH" >/dev/null + +# A restore that produces an empty schema exits 0 and tells you nothing. Ask it something. +CLUBS="$($COMPOSE exec -T db psql -tAq -U "$USER_NAME" "$SCRATCH" -c 'SELECT count(*) FROM club_club')" +USERS="$($COMPOSE exec -T db psql -tAq -U "$USER_NAME" "$SCRATCH" -c 'SELECT count(*) FROM authentication_user')" + +[ "$USERS" -gt 0 ] || { echo "restore check FAILED: $(basename "$LATEST") restored no users"; exit 1; } + +echo "$(date -Iseconds) restore ok: $(basename "$LATEST") -> $CLUBS clubs, $USERS users" diff --git a/events/management/commands/extend_event_series.py b/events/management/commands/extend_event_series.py index bb33cbc..f7fdeb7 100644 --- a/events/management/commands/extend_event_series.py +++ b/events/management/commands/extend_event_series.py @@ -1,10 +1,9 @@ -from django.core.management.base import BaseCommand - from events.models import EventSeries from events.services import generate_occurrences, horizon +from features.commands import MaintenanceAwareCommand -class Command(BaseCommand): +class Command(MaintenanceAwareCommand): help = "Materialise recurring event occurrences up to the rolling horizon." def handle(self, *args, **options): diff --git a/features/commands.py b/features/commands.py new file mode 100644 index 0000000..5f06cb4 --- /dev/null +++ b/features/commands.py @@ -0,0 +1,31 @@ +"""Scheduled work refuses to run while the platform is locked down. + +Deliberately opt-in, per command, rather than a blanket guard on BaseCommand: maintenance is +usually declared IN ORDER to run `migrate` or `collectstatic`, and a guard that blocked those +would make the mode useless — you would have to turn it off to do the work you turned it on +for. Only the domain jobs (which write club data, archive clubs, or import members) stand +down. +""" + +from django.core.management.base import BaseCommand, CommandError + +from features.models import Maintenance + + +class MaintenanceAwareCommand(BaseCommand): + """A command that must not run while the platform is closed.""" + + def execute(self, *args, **options): + if Maintenance.is_on() and not options.get("ignore_maintenance"): + raise CommandError("The platform is in maintenance mode; this command stands down. Pass --ignore-maintenance to override.") + + return super().execute(*args, **options) + + def create_parser(self, prog_name, subcommand, **kwargs): + parser = super().create_parser(prog_name, subcommand, **kwargs) + parser.add_argument( + "--ignore-maintenance", + action="store_true", + help="Run even though the platform is in maintenance mode.", + ) + return parser diff --git a/features/context_processors.py b/features/context_processors.py new file mode 100644 index 0000000..2875c19 --- /dev/null +++ b/features/context_processors.py @@ -0,0 +1,7 @@ +from features.models import Maintenance + + +def maintenance(request): + """So no control-panel page can forget the platform is closed. Cached, so it costs no + query.""" + return {"maintenance_on": Maintenance.is_on()} diff --git a/features/middleware.py b/features/middleware.py new file mode 100644 index 0000000..2cde80f --- /dev/null +++ b/features/middleware.py @@ -0,0 +1,74 @@ +"""Platform lock-down. + +While maintenance is on, every club subdomain is closed and the base domain keeps only what +is needed to *end* the maintenance: the control panel, the auth screens that get you into it, +the static files those pages need, and the health check. + +The exemptions are the whole design. Close /accounts/ as well and you cannot sign in to turn +maintenance off — a lock-down with no key, fixable only from a shell. Close /healthz and the +load balancer concludes the node is dead and stops routing to it, which takes the control +panel down with everything else. +""" + +from django.http import JsonResponse +from django.shortcuts import render +from django.utils.translation import gettext_lazy as _ + +from features.models import Maintenance + +#: Reachable on the base domain while the platform is locked down. +OPEN_PREFIXES = ( + "/controlpanel/", # the point of the exercise + "/accounts/", # ...which you cannot reach without signing in + "/admin/", + "/static/", + "/media/", + "/__reload__/", # dev only; absent outside DEBUG +) + +#: Reachable on every host, always. The health check must answer or the load balancer will +#: take the node out of rotation and the control panel with it. +ALWAYS_OPEN = ("/healthz",) + +RETRY_AFTER_SECONDS = 3600 + + +class MaintenanceMiddleware: + """Runs after ClubTenantMiddleware: whether a request is a club's or the platform's is + decided by ``request.club``, which the tenant middleware has just resolved.""" + + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + if not self.is_closed(request): + return self.get_response(request) + + maintenance = Maintenance.current() + response = self.render(request, maintenance) + response["Retry-After"] = RETRY_AFTER_SECONDS + + return response + + def is_closed(self, request) -> bool: + if request.path.startswith(ALWAYS_OPEN): + return False + + if not Maintenance.is_on(): + return False + + # A club subdomain is closed outright — no login, no shop, nothing. + if getattr(request, "club", None) is not None: + return True + + # The base domain keeps the way back in. + return not request.path.startswith(OPEN_PREFIXES) + + def render(self, request, maintenance): + message = maintenance.message or _("RosterChief is down for maintenance. It will be back shortly.") + + # An API-ish caller gets JSON rather than a page of HTML it cannot read. + if request.headers.get("accept", "").startswith("application/json"): + return JsonResponse({"status": "maintenance", "detail": str(message)}, status=503) + + return render(request, "maintenance.html", {"message": message, "maintenance": maintenance}, status=503) diff --git a/features/migrations/0002_maintenance.py b/features/migrations/0002_maintenance.py new file mode 100644 index 0000000..3048687 --- /dev/null +++ b/features/migrations/0002_maintenance.py @@ -0,0 +1,33 @@ +# Generated by Django 6.0.6 on 2026-07-14 08:00 + +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('features', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Maintenance', + fields=[ + ('created', models.DateTimeField(auto_now_add=True, verbose_name='created')), + ('modified', models.DateTimeField(auto_now=True, verbose_name='modified')), + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('is_active', models.BooleanField(default=False, verbose_name='active')), + ('message', models.TextField(blank=True, help_text='Shown to clubs while the platform is locked down.', verbose_name='message')), + ('started_at', models.DateTimeField(blank=True, null=True, verbose_name='started at')), + ('started_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='maintenance_windows', to=settings.AUTH_USER_MODEL, verbose_name='started by')), + ], + options={ + 'verbose_name': 'maintenance', + 'verbose_name_plural': 'maintenance', + }, + ), + ] diff --git a/features/models.py b/features/models.py index c3385a4..49fc6c3 100644 --- a/features/models.py +++ b/features/models.py @@ -10,11 +10,15 @@ Everything waffle already offers (``everyone`` / ``percent`` / ``staff`` / ``superusers`` / per-user / per-group) keeps working untouched. """ +from django.conf import settings from django.db import models +from django.utils import timezone from django.utils.translation import gettext_lazy as _ from waffle.models import CACHE_EMPTY, AbstractUserFlag from waffle.utils import get_cache, keyfmt +from rosterchief.base import UUIDModel + #: Cache key template for a flag's club ids, mirroring waffle's own #: FLAG_USERS_CACHE_KEY / FLAG_GROUPS_CACHE_KEY. FLAG_CLUBS_CACHE_KEY = "flag:%s:clubs" @@ -69,3 +73,74 @@ class Flag(AbstractUserFlag): if self.everyone is not None: return self.everyone return club.pk in self._get_club_ids() + + +class Maintenance(UUIDModel): + """Platform lock-down. One row, read on every request. + + Cached rather than queried per request, and the cache is the same shared Redis the flags + use — so turning maintenance on in the control panel takes effect on every worker and + every server at once. A per-process cache would leave some workers still serving clubs. + """ + + CACHE_KEY = "maintenance:current" + + #: Cached, but not for ever. Write-through makes the flip instant for the process that + #: made it and — on the shared Redis of a real deployment — for every other one too. The + #: TTL is the belt to that braces: on a per-process cache (a dev box with no Redis, or a + #: misconfigured deploy) a lock-down that only reached one gunicorn worker would be worse + #: than useless, so the others notice within ten seconds regardless. + CACHE_SECONDS = 10 + + is_active = models.BooleanField(_("active"), default=False) + message = models.TextField(_("message"), blank=True, help_text=_("Shown to clubs while the platform is locked down.")) + started_at = models.DateTimeField(_("started at"), null=True, blank=True) + started_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="maintenance_windows", verbose_name=_("started by")) + + class Meta: + verbose_name = _("maintenance") + verbose_name_plural = _("maintenance") + + def __str__(self): + return "Maintenance on" if self.is_active else "Maintenance off" + + def save(self, *args, **kwargs): + super().save(*args, **kwargs) + get_cache().set(self.CACHE_KEY, self, self.CACHE_SECONDS) + + @classmethod + def current(cls) -> Maintenance: + """The one row, created on first read. Cached until it changes.""" + cached = get_cache().get(cls.CACHE_KEY) + if cached is not None: + return cached + + maintenance = cls.objects.first() or cls.objects.create() + get_cache().set(cls.CACHE_KEY, maintenance, cls.CACHE_SECONDS) + + return maintenance + + @classmethod + def is_on(cls) -> bool: + return cls.current().is_active + + @classmethod + def start(cls, *, message: str = "", user=None) -> Maintenance: + maintenance = cls.current() + maintenance.is_active = True + maintenance.message = message + maintenance.started_at = timezone.now() + maintenance.started_by = user + maintenance.save() + + return maintenance + + @classmethod + def stop(cls) -> Maintenance: + maintenance = cls.current() + maintenance.is_active = False + maintenance.started_at = None + maintenance.started_by = None + maintenance.save() + + return maintenance diff --git a/features/tests.py b/features/tests.py index 6e447df..8cc6f81 100644 --- a/features/tests.py +++ b/features/tests.py @@ -1,10 +1,19 @@ +from io import StringIO + +from allauth.mfa.models import Authenticator +from django.contrib.auth import get_user_model from django.core.cache import cache -from django.test import RequestFactory, TestCase +from django.core.management import call_command +from django.core.management.base import CommandError +from django.test import RequestFactory, TestCase, override_settings from waffle import flag_is_active, get_waffle_flag_model from club.models import Club +from .models import Maintenance + Flag = get_waffle_flag_model() +User = get_user_model() class ClubScopedFlagTests(TestCase): @@ -94,3 +103,134 @@ class ClubScopedFlagTests(TestCase): self.club.flags.add(self.flag) self.assertTrue(self.active_for(self.club)) + + +@override_settings( + ROSTERCHIEF_BASE_DOMAIN="rosterchief.app", + ALLOWED_HOSTS=["rosterchief.app", "ajax-united.rosterchief.app", "testserver"], +) +class MaintenanceModeTests(TestCase): + def setUp(self): + cache.clear() + self.addCleanup(cache.clear) + self.club = Club.objects.create(name="Ajax United", slug="ajax-united") + self.user = User.objects.create_user(email="root@example.com", password="pw-secret-123", is_staff=True) + Authenticator.objects.create(user=self.user, type=Authenticator.Type.TOTP, data={"secret": "JBSWY3DPEHPK3PXP"}) + + def club_get(self, path="/"): + return self.client.get(path, HTTP_HOST="ajax-united.rosterchief.app") + + def platform_get(self, path): + return self.client.get(path, HTTP_HOST="rosterchief.app") + + def test_a_club_subdomain_is_closed(self): + Maintenance.start(message="Upgrading the database.") + + response = self.club_get("/accounts/login/") + + self.assertEqual(response.status_code, 503) + self.assertContains(response, "Upgrading the database", status_code=503) + self.assertEqual(response["Retry-After"], "3600") + + def test_a_club_is_open_again_when_maintenance_ends(self): + Maintenance.start() + Maintenance.stop() + + self.assertEqual(self.club_get("/accounts/login/").status_code, 200) + + def test_the_control_panel_stays_reachable(self): + Maintenance.start() + self.client.force_login(self.user) + + self.assertEqual(self.platform_get("/controlpanel/").status_code, 200) + + def test_signing_in_stays_possible(self): + # Close /accounts/ as well and you cannot sign in to turn maintenance off: a + # lock-down with no key, fixable only from a shell. + Maintenance.start() + + self.assertEqual(self.platform_get("/accounts/login/").status_code, 200) + + def test_the_health_check_still_answers(self): + # Close it and the load balancer decides the node is dead and stops routing to it — + # taking the control panel down with everything else. + Maintenance.start() + + self.assertEqual(self.platform_get("/healthz").status_code, 200) + self.assertEqual(self.client.get("/healthz", HTTP_HOST="ajax-united.rosterchief.app").status_code, 200) + + def test_the_rest_of_the_base_domain_is_closed(self): + Maintenance.start() + + self.assertEqual(self.platform_get("/").status_code, 503) + + def test_a_json_caller_gets_json(self): + Maintenance.start(message="Back soon.") + + response = self.client.get("/", HTTP_HOST="ajax-united.rosterchief.app", headers={"accept": "application/json"}) + + self.assertEqual(response.status_code, 503) + self.assertEqual(response.json()["status"], "maintenance") + + def test_the_message_is_optional(self): + Maintenance.start() + + self.assertContains(self.club_get("/"), "down for maintenance", status_code=503) + + def test_it_says_which_state_it_is_in(self): + self.assertEqual(str(Maintenance.start()), "Maintenance on") + self.assertEqual(str(Maintenance.stop()), "Maintenance off") + + def test_it_records_who_closed_the_platform_and_when(self): + maintenance = Maintenance.start(message="db upgrade", user=self.user) + + self.assertTrue(maintenance.is_active) + self.assertEqual(maintenance.started_by, self.user) + self.assertIsNotNone(maintenance.started_at) + + def test_the_state_is_shared_rather_than_per_process(self): + # The cache is the shared one the flags use, so closing the platform reaches every + # worker and every server. A per-process cache would leave some workers serving clubs. + Maintenance.start() + + self.assertTrue(cache.get(Maintenance.CACHE_KEY).is_active) + self.assertTrue(Maintenance.is_on()) + + Maintenance.stop() + + self.assertFalse(cache.get(Maintenance.CACHE_KEY).is_active) + + +class MaintenanceCommandTests(TestCase): + def setUp(self): + cache.clear() + self.addCleanup(cache.clear) + + def run_command(self, name, *args): + out = StringIO() + call_command(name, *args, stdout=out, stderr=out) + return out.getvalue() + + def test_a_scheduled_job_stands_down(self): + Maintenance.start() + + with self.assertRaises(CommandError): + self.run_command("archive_overdue_clubs") + + def test_it_runs_again_once_the_platform_reopens(self): + Maintenance.start() + Maintenance.stop() + + self.assertIn("Nothing overdue", self.run_command("archive_overdue_clubs")) + + def test_an_override_exists_for_when_you_mean_it(self): + Maintenance.start() + + self.assertIn("Nothing overdue", self.run_command("archive_overdue_clubs", "--ignore-maintenance")) + + def test_migrate_is_not_blocked(self): + # Maintenance is usually declared IN ORDER to migrate. A guard on every command would + # mean turning the mode off to do the work you turned it on for. + Maintenance.start() + + self.run_command("migrate", "--check") # raises SystemExit only if migrations are pending diff --git a/members/management/commands/import_members_csv.py b/members/management/commands/import_members_csv.py index 2a0e285..2c092f1 100644 --- a/members/management/commands/import_members_csv.py +++ b/members/management/commands/import_members_csv.py @@ -1,9 +1,8 @@ -from django.core.management.base import BaseCommand, CommandError - +from features.commands import CommandError, MaintenanceAwareCommand from members.services import MemberCsvImporter -class Command(BaseCommand): +class Command(MaintenanceAwareCommand): help = "Import club members from a CSV file." def add_arguments(self, parser) -> None: diff --git a/rosterchief/settings.py b/rosterchief/settings.py index 972833c..46f88d9 100644 --- a/rosterchief/settings.py +++ b/rosterchief/settings.py @@ -86,6 +86,8 @@ MIDDLEWARE = [ "allauth.account.middleware.AccountMiddleware", "authentication.middleware.RequireMFAMiddleware", "club.tenancy.ClubTenantMiddleware", + # After tenancy: it decides club-vs-platform from request.club, which was just resolved. + "features.middleware.MaintenanceMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] @@ -166,6 +168,7 @@ TEMPLATES = [ "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", "club.context_processors.branding", + "features.context_processors.maintenance", ], }, }, diff --git a/static/css/app.css b/static/css/app.css index c3cf6d0..e1ea44b 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -3767,6 +3767,9 @@ .items-center { align-items: center; } + .items-start { + align-items: flex-start; + } .justify-between { justify-content: space-between; } @@ -4187,6 +4190,9 @@ .opacity-70 { opacity: 70%; } + .opacity-80 { + opacity: 80%; + } .shadow { --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); diff --git a/templates/maintenance.html b/templates/maintenance.html new file mode 100644 index 0000000..5b6df86 --- /dev/null +++ b/templates/maintenance.html @@ -0,0 +1,24 @@ +{% extends base_template %} +{% load lucide %} + +{% comment %} + Rendered through the tenant's own skin, so a club sees its own logo and colours rather + than a bare error page — this is the platform being unavailable, not the club being gone. +{% endcomment %} + +{% block head_title %}Maintenance{% endblock head_title %} + +{% block main %} +
+
+
+
{% lucide "wrench" size=48 %}
+

Down for maintenance

+

{{ message }}

+ {% if maintenance.started_at %} +

Since {{ maintenance.started_at|date:"j M Y, H:i" }}

+ {% endif %} +
+
+
+{% endblock main %} From 98e5f2287395e1e7766c260aa81df16a270ce565 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 10:22:37 +0200 Subject: [PATCH 069/137] Document running RosterChief beside another domain on one Caddy The "behind an existing Caddy" section assumed the DNS plugin was being set up from scratch. The realistic case is a box whose Caddy already does Cloudflare DNS-01 for another domain, so it now covers that: set acme_dns once globally and every site inherits it, or scope a token per zone with a snippet. Leads with the failure that will actually happen -- a Cloudflare token is scoped to named zones, so the existing one grants DNS:Edit on the domain it was made for and nothing else, and the new site fails its challenge on a permissions error whose text does not say so. Also spells out that *.test.rosterchief.app does NOT match test.rosterchief.app: a wildcard covers exactly one label, so leaving the bare host off the site line gives the club subdomains a certificate and the control panel none. Co-Authored-By: Claude Opus 4.8 --- DEPLOYMENT.md | 114 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 98 insertions(+), 16 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index d81784a..5178836 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -152,13 +152,33 @@ docker compose -f compose.behind-proxy.yaml up -d # web + db + redis, n bound to all interfaces, a test instance is reachable at `http://:8001` with no TLS, bypassing the proxy and every security header with it. -Then, in the host's Caddyfile: +Then add a site block to the host's Caddyfile. Caddy serves any number of domains on the same +ports — TLS is chosen per connection by SNI — so a second (or tenth) site is just another +block. + +### If that Caddy already does Cloudflare DNS-01 + +Which is the usual case: the box has a domain on Cloudflare and Caddy already has the DNS +plugin. Then set the challenge **once, globally**, and every site inherits it — no `tls` +block per site, and wildcards simply work: ```caddy +{ + email you@example.com + + # Applies DNS-01 to every site below. + acme_dns cloudflare {env.CLOUDFLARE_API_TOKEN} +} + +# --- whatever the box already serves -------------------------------------- +existing-thing.example.com { + reverse_proxy 127.0.0.1:3000 +} + +# --- RosterChief test instance -------------------------------------------- +# The bare host AND the wildcard, on one certificate. test.rosterchief.app, *.test.rosterchief.app { - tls { - dns cloudflare {env.CLOUDFLARE_API_TOKEN} - } + encode zstd gzip reverse_proxy 127.0.0.1:8001 { header_up X-Forwarded-Proto {scheme} @@ -167,20 +187,82 @@ test.rosterchief.app, *.test.rosterchief.app { } ``` -Three things this needs, and each one is a way to lose an afternoon: +### If the two domains need different tokens -1. **The host's Caddy must have the DNS plugin too.** The wildcard is still a wildcard: the - stock `caddy` package cannot answer a DNS-01 challenge. `caddy add-package - github.com/caddy-dns/cloudflare` on a package install, or run a Caddy built like - `deploy/caddy/Dockerfile`. -2. **Give the test instance its own subdomain tree** (`*.test.rosterchief.app`) and set - `ROSTERCHIEF_BASE_DOMAIN=test.rosterchief.app`. It drives tenant resolution, the shared - session cookie *and* the WebAuthn RP ID — point it at the production domain and test - passkeys start colliding with real ones. -3. **`header_up X-Forwarded-Proto` is not optional**, exactly as in the bundled Caddyfile. - Without it Django believes the request is plain HTTP behind the proxy. +Different Cloudflare accounts, or tokens scoped per zone. Drop `acme_dns` and give each site +its own `tls`; a snippet keeps it short: -DNS still needs both records, pointing at the test box: +```caddy +{ + email you@example.com +} + +(cf) { + tls { + dns cloudflare {args[0]} + } +} + +existing-thing.example.com { + import cf {env.CF_TOKEN_EXAMPLE} + reverse_proxy 127.0.0.1:3000 +} + +test.rosterchief.app, *.test.rosterchief.app { + import cf {env.CF_TOKEN_ROSTERCHIEF} + reverse_proxy 127.0.0.1:8001 { + header_up X-Forwarded-Proto {scheme} + } +} +``` + +### What actually goes wrong + +1. **The token must cover the *new* zone.** A Cloudflare token is scoped to named zones, and + an existing one almost certainly grants `Zone:DNS:Edit` on the domain it was made for and + nothing else. The new site then fails its DNS-01 challenge on a permissions error whose + text does not say so. Widen the token, or mint a second one and use the snippet form. +2. **Both hostnames must be listed.** `*.test.rosterchief.app` does **not** match + `test.rosterchief.app` — a wildcard covers exactly one label. Leave the bare host out and + the club subdomains have a certificate while the control panel does not. Hence the comma. + (Wildcards are also only one level deep: `ajax.test.…` yes, `a.b.test.…` no.) +3. **Caddy must have the DNS plugin.** Stock `caddy` cannot answer a DNS-01 challenge at all. + `caddy add-package github.com/caddy-dns/cloudflare`, or run a Caddy built like + `deploy/caddy/Dockerfile`. (If DNS-01 already works on the box, you have it.) +4. **The token must be in *Caddy's* environment**, not your shell's — `{env.…}` reads the + process it runs in: + + ```ini + # /etc/systemd/system/caddy.service.d/override.conf + [Service] + EnvironmentFile=/etc/caddy/caddy.env # CLOUDFLARE_API_TOKEN=... + ``` + + Then `systemctl daemon-reload && systemctl restart caddy`. + +5. **`header_up X-Forwarded-Proto` is not optional**, exactly as in the bundled Caddyfile: + without it Django believes the request behind the proxy is plain HTTP. + +6. **Give the test instance its own subdomain tree** and set + `ROSTERCHIEF_BASE_DOMAIN=test.rosterchief.app`. That variable drives tenant resolution, + the shared session cookie *and* the WebAuthn RP ID — point it at the production domain and + test passkeys start colliding with real ones. + +### Applying and checking it + +```bash +caddy validate --config /etc/caddy/Caddyfile # syntax and modules +systemctl reload caddy # zero downtime; existing certs untouched +journalctl -u caddy -f # watch the DNS-01 challenge + +curl -I https://test.rosterchief.app/healthz +curl -I https://any-club-slug.test.rosterchief.app/ # proves the WILDCARD, not just the host +``` + +Reloading provisions only what is new, so the existing site's certificate is not reissued. +Allow 30–60s for the DNS record to propagate before the challenge completes. + +DNS needs both records, pointing at the test box: ``` A test.rosterchief.app -> From 534d01d6fe7b7e4c52475148c506c3beffc1f84f Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 10:25:40 +0200 Subject: [PATCH 070/137] Size the server, and cost an AWS three-node layout For 1-5 clubs / 1000 members / 10 events per club per week: 2 vCPU, 4 GB, 40 GB. The data does not size this box. Computed from the real schema, attendance dominates (every event invites a squad, so one event is ~20 rows) and the whole thing comes to ~40 MB/year -- 0.2 GB after five years. Invoices are rendered on demand and never stored. What sizes it is the processes, measured rather than guessed: gunicorn master plus three workers is ~270 MB (~54 MB each), and the whole stack idles around 1.0-1.2 GB. 2 GB would run it; 4 GB is the recommendation because `docker compose build` is the memory spike, not serving -- npm, uv and collectstatic together will OOM a 2 GB box that is also running Postgres. Rendering an invoice adds ~50-100 MB to one worker the first time, since WeasyPrint is imported lazily. Also adds the AWS three-node layout for fun, with a cost table. Two things worth knowing there: ACM issues the wildcard certificate free with Route 53 validation, so the entire DNS-01 dance disappears; and a NAT Gateway would cost more than the compute (~$32/month per AZ) if the tasks sit in private subnets. The honest line at the end: ~$110-130/month on AWS against ~EUR 5 on a VPS, for a database that is 200 MB after five years. The money buys resilience, not capacity. Co-Authored-By: Claude Opus 4.8 --- DEPLOYMENT.md | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 5178836..e3db10b 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -334,6 +334,178 @@ docker compose cp web:/app/media ./media-backup Restore is `gunzip -c dump.sql.gz | docker compose exec -T db psql -U rosterchief rosterchief`. Test it once, now, rather than the first time you need it. +## Sizing the server + +For **1–5 clubs, ~1000 members, ~10 events per club per week**. + +The short answer: **2 vCPU, 4 GB RAM, 40 GB SSD** — a €4–6/month VPS (Hetzner CX22 or +equivalent). The interesting part is *why*, because the data is not what sizes this box. + +### The data is negligible + +Row counts for that workload, from the actual schema (attendance dominates: every event +invites a squad, so one event is ~20 rows): + +| table | rows/year | MB/year | +|---|---:|---:| +| `events.Attendance` | 52,000 | 16 | +| `events.Event` | 2,600 | 2 | +| `formbuilder` answers | 10,000 | 3 | +| `shop` orders + lines | 3,000 | 1 | +| members, memberships, rosters | ~3,000 | 1 | +| **total, with WAL and bloat** | | **~40 MB/year** | + +That is **0.2 GB after five years**. Uploads are club logos — a handful of files. Invoices are +rendered on demand and never stored. Nothing here grows into a problem. + +So do not size for the data. Size for the **processes**. + +### What actually consumes the box + +Measured, running this app under gunicorn with `DEBUG=False`: + +| | memory | +|---|---| +| gunicorn master + 3 workers | **~270 MB** (~54 MB per worker) | +| PostgreSQL (default `shared_buffers`) | ~200–400 MB | +| Redis (cache only) | < 50 MB | +| Caddy | ~30 MB | +| OS + Docker daemon | ~400 MB | +| **steady state** | **~1.0–1.2 GB** | + +2 GB would run it. 4 GB is the recommendation for three reasons, all of which are the kind of +thing that bites at the worst moment: + +1. **`docker compose build` is the memory spike, not serving.** npm, uv and `collectstatic` + together will OOM a 2 GB box that is also running Postgres. Either take the 4 GB, or build + the image elsewhere and pull it. +2. **Rendering an invoice loads WeasyPrint.** It is imported lazily (which is why the workers + measure 54 MB and not 150), so pango and its fonts land in whichever worker renders a PDF — + expect that worker to grow by ~50–100 MB the first time someone downloads an invoice. +3. **Headroom is Postgres's page cache.** With 200 MB of data and 4 GB of RAM, the entire + database lives in cache and the disk is never touched for reads. + +### Disk + +| | | +|---|---| +| Docker images (app ~1 GB with pango, postgres, redis, caddy) | ~1.5 GB | +| Build cache | 2–4 GB | +| Database, 5 years | < 0.5 GB | +| Backups: 14 daily compressed dumps | < 0.5 GB | +| Logs | ~1 GB | +| **40 GB is roomy; 20 GB works** | | + +### CPU and concurrency + +2 vCPU. Three workers × four threads is twelve concurrent requests, against a peak of "the +whole club checks the Saturday line-up at 09:00" — perhaps a few hundred requests over a few +minutes. This workload is not CPU-bound; the one CPU-heavy operation is PDF rendering, which +happens a handful of times a month. + +### When to grow + +Not at "more members" — at these: + +- **Uploads become real content** (photo galleries, documents). Media, not rows, is what makes + storage grow, and it is also the trigger for moving to S3. +- **Attendance passes a few million rows** (~20 clubs at this rate, i.e. several years out). + Add an index before adding a server. +- **You want zero-downtime deploys.** That is a second app node, not a bigger one. + +## For fun: three nodes on AWS + +Wildly over-engineered for 1000 members, but here is what it looks like — and what it costs. + +### The layout + +``` +Route 53 (rosterchief.app + *.rosterchief.app) + | + ACM certificate (wildcard, free) + | +Application Load Balancer (TLS terminates here) + | + +----+----+----+ + | | | + ECS task task task 3 × Fargate, one per AZ, same image + | | | + +----+----+----+ + | + +----+---------------+----------------+ + | | | + RDS PostgreSQL ElastiCache Redis S3 (media) + (Multi-AZ) (cache.t4g.micro) + CloudFront (optional) +``` + +**The one genuinely nice thing AWS gives you here: ACM issues the wildcard certificate for +free, with DNS validation in Route 53.** The whole DNS-01 dance disappears — no Caddy plugin, +no API token, no renewal. The ALB terminates TLS and forwards to the tasks. That is the single +biggest simplification versus the VPS. + +### What changes in the app + +Nothing in the code. Only environment: + +| | | +|---|---| +| `DJANGO_DATABASE_URL` | the RDS endpoint | +| `DJANGO_REDIS_URL` | the ElastiCache endpoint | +| `AWS_STORAGE_BUCKET_NAME` | the media bucket — **required** now, three nodes cannot share a disk | +| `SECURE_PROXY_SSL_HEADER` | already set; the ALB sends `X-Forwarded-Proto` | +| health check | point the target group at **`/healthz`** — that is what it is for | + +Sessions are database-backed, so **no sticky sessions**: any task can serve any request. + +**Scheduled jobs get better here.** EventBridge Scheduler firing a one-off ECS task solves the +"run it on exactly one node" problem properly — no cron on three boxes racing each other: + +``` +EventBridge (cron: 0 6 * * ? *) -> ECS RunTask -> archive_overdue_clubs --commit +``` + +Backups become RDS automated snapshots + PITR, and `deploy/backup.sh` retires — though the +*restore rehearsal* does not. Snapshots you have never restored are still a hypothesis. + +### Monthly cost (eu-central-1, on-demand, indicative) + +| | | $/month | +|---|---|---:| +| ALB | fixed + a little LCU | ~22 | +| ECS Fargate | 3 × (0.5 vCPU, 1 GB) | ~54 | +| RDS PostgreSQL | `db.t4g.micro`, 20 GB gp3, single-AZ | ~17 | +| ElastiCache | `cache.t4g.micro` | ~12 | +| S3 + CloudFront | a few GB, low traffic | ~2 | +| Route 53 | hosted zone + queries | ~1 | +| ECR, CloudWatch logs | small | ~3 | +| | **single-AZ total** | **~110** | +| RDS Multi-AZ | doubles the database | +17 | +| | **highly-available total** | **~130** | + +**Watch the NAT Gateway.** If the tasks sit in private subnets and reach the internet through +a NAT Gateway, add **~$32/month per AZ plus data charges** — for three AZs that is more than +the compute. Either put the tasks in public subnets with tight security groups, or use VPC +endpoints for ECR/S3/CloudWatch. It is the single most common surprise on an AWS bill of this +shape. + +Prices are indicative and move; check the calculator before committing. + +### The honest comparison + +| | | | +|---|---|---| +| **Hetzner CX22** | 2 vCPU, 4 GB, 40 GB | **~€5/month** | +| **AWS, three nodes** | as above | **~$110–130/month** | + +Roughly **25×**, for a workload whose database is 200 MB after five years. What the money buys +is real — managed Postgres with PITR, three AZs, no box to patch, free wildcard certificates — +but it is bought for *resilience*, not for capacity. At 1000 members you are paying for the +insurance, not the compute. + +A reasonable middle: one VPS now, and move Postgres to a managed service (RDS, or a €15/month +managed Postgres) the day the data starts to matter more than the uptime. That is the change +that is painful to do late, and everything else in this document is already designed for it. + ## Going multi-server Nothing in the code changes. What changes is where the services live: From 5d42691a10ab18171e627f7b2d2ca07d8f704c1e Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 18:07:54 +0200 Subject: [PATCH 071/137] Fix the image build: fetch the git dependency in its own stage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The build died at `uv sync`: django-lucide is our fork, declared as a git source and pinned by the lock to a commit, so uv shells out to `git` to fetch it — and python:3.14-slim has no git. Installing git in the runtime image would have fixed it and left a build tool, plus its dependency tree, in production for the sake of one package that is already vendored into the venv by then. So the virtualenv is now built in a stage that has git, and the finished .venv is copied into a runtime stage that does not. Same base image, so the compiled wheels inside it stay ABI compatible. Also drops the second `uv sync`, which installed the project itself: there is no [build-system] and rosterchief is not a package — gunicorn imports it from the working directory, exactly as it does locally. Unverified end to end: still no container runtime on this machine. Co-Authored-By: Claude Opus 4.8 --- DEPLOYMENT.md | 16 ++++++++++++++++ Dockerfile | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index e3db10b..6111b3a 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -67,6 +67,22 @@ docker compose run --rm web python manage.py check --deploy **off** in code, because defaulting them to `not DEBUG` would redirect every test request to https and break the suite anywhere `DEBUG` is unset. +### One dependency comes from git + +`django-lucide` is our fork (`[tool.uv.sources]` in `pyproject.toml`, pinned by `uv.lock` to a +commit), so **uv shells out to `git`** to fetch it. `python:*-slim` has no git, which is why +the image builds the virtualenv in a **separate stage** that installs git, and copies the +finished `.venv` into a runtime stage that does not have it — a build tool has no business in +a production image. + +Two consequences worth knowing: + +- The build needs **network access to GitHub**, and the fork must stay reachable. If that ever + becomes awkward (a private runner, an air-gapped build), publish the fork to a private index + or vendor the wheel, and the git stage disappears. +- `uv.lock` pins the exact commit, so the build is reproducible even though the source is a + branch. Don't build with `--no-frozen`. + The first `docker compose up` will take a minute or two: Caddy is provisioning the wildcard certificate over DNS-01, and DNS propagation is not instant. Watch it with `docker compose logs -f caddy`. diff --git a/Dockerfile b/Dockerfile index 7a9f6e6..92283b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,31 @@ COPY billing ./billing RUN npm run build -# --- 2. the runtime ---------------------------------------------------------- +# --- 2. the virtualenv ------------------------------------------------------- +# Separate from the runtime for one reason: django-lucide is a *git* dependency (our lucide +# fork), so uv shells out to git to fetch it. python:*-slim has no git, and installing it in +# the runtime image would leave a build-time tool — plus its dependency tree — in production +# for the sake of one package that is already vendored into the venv by then. +FROM python:3.14-slim AS venv + +RUN apt-get update && apt-get install --no-install-recommends -y git ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv + +ENV UV_COMPILE_BYTECODE=1 \ + UV_LINK_MODE=copy \ + UV_PYTHON_DOWNLOADS=never + +WORKDIR /app + +# Dependencies first: they change far less often than the code, so this layer caches. +COPY pyproject.toml uv.lock ./ +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-dev --no-install-project + + +# --- 3. the runtime ---------------------------------------------------------- FROM python:3.14-slim AS app # WeasyPrint binds to these at import: no pango, no invoices. This is also why building the @@ -31,26 +55,19 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ curl \ && rm -rf /var/lib/apt/lists/* -COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv - ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ - UV_COMPILE_BYTECODE=1 \ - UV_LINK_MODE=copy \ PATH="/app/.venv/bin:$PATH" WORKDIR /app -# Dependencies first: they change far less often than the code, so this layer caches. -COPY pyproject.toml uv.lock ./ -RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --frozen --no-dev --no-install-project +# The venv arrives fully built. Same base image, so the compiled wheels inside it are ABI +# compatible; nothing is re-resolved here, and no git is needed to run what git fetched. +COPY --from=venv /app/.venv ./.venv COPY . . COPY --from=css /build/static/css/app.css ./static/css/app.css -RUN --mount=type=cache,target=/root/.cache/uv uv sync --frozen --no-dev - # collectstatic needs a settings module that imports: a throwaway key, never used at runtime. RUN DJANGO_SECRET_KEY=build-only-not-a-secret \ DJANGO_STATICFILES_BACKEND=whitenoise.storage.CompressedManifestStaticFilesStorage \ From c42963c447341186dd35118c0da06474bafea968 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 18:14:53 +0200 Subject: [PATCH 072/137] Do not let DEBUG=True take down a container that has no dev deps The image installs with --no-dev, so django_browser_reload is absent. Settings and urls both assumed DEBUG implied it was installed, so DJANGO_DEBUG=True in a deployed container did not merely turn on debugging: the app refused to start, with a ModuleNotFoundError that says nothing about the actual mistake. Both now guard on the module being importable. Reproduced the failure locally by hiding the package with DEBUG on, and confirmed the urlconf loads afterwards. DEPLOYMENT.md says the obvious thing out loud: a test server is still a deployment -- real TLS, real domain, real passkeys -- so DEBUG stays off there. The crash is fixed; the reason to keep it off was never the crash. Co-Authored-By: Claude Opus 4.8 --- .idea/pySourceRootDetection.xml | 10 ++++++++++ DEPLOYMENT.md | 10 ++++++++++ rosterchief/settings.py | 16 ++++++++++++---- rosterchief/urls.py | 7 ++++++- 4 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 .idea/pySourceRootDetection.xml diff --git a/.idea/pySourceRootDetection.xml b/.idea/pySourceRootDetection.xml new file mode 100644 index 0000000..089a2ac --- /dev/null +++ b/.idea/pySourceRootDetection.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 6111b3a..d34c184 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -67,6 +67,16 @@ docker compose run --rm web python manage.py check --deploy **off** in code, because defaulting them to `not DEBUG` would redirect every test request to https and break the suite anywhere `DEBUG` is unset. +### Keep DJANGO_DEBUG=False, even on the test server + +A test box is still a deployment: it is behind TLS, on a real domain, with real passkeys. +`DEBUG=True` there leaks tracebacks and settings to anyone who can reach a 500, and turns off +several of the protections in this document. Use it locally, not on a server. + +The app no longer *crashes* if you set it — `django_browser_reload` is a dev dependency that +the image installs with `--no-dev`, so settings guard on the module being importable rather +than assuming DEBUG implies it is there — but the reason to keep it off is not the crash. + ### One dependency comes from git `django-lucide` is our fork (`[tool.uv.sources]` in `pyproject.toml`, pinned by `uv.lock` to a diff --git a/rosterchief/settings.py b/rosterchief/settings.py index 46f88d9..941dffd 100644 --- a/rosterchief/settings.py +++ b/rosterchief/settings.py @@ -10,6 +10,7 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/6.0/ref/settings/ """ +from importlib.util import find_spec from pathlib import Path from decouple import Csv, config @@ -92,10 +93,17 @@ MIDDLEWARE = [ "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -# Reload the browser when templates, static files or Python change. Dev only: it -# injects a script tag into every HTML response and serves an open event stream, -# neither of which belongs in production. -if DEBUG: +# Reload the browser when templates, static files or Python change. Dev only: it injects a +# script tag into every HTML response and serves an open event stream, neither of which +# belongs in production. +# +# Guarded on the module being *importable*, not just on DEBUG: it is a dev dependency, and the +# production image installs with --no-dev. Without the guard, DEBUG=True in a deployed +# container does not merely turn on debugging — it stops the app from starting at all, with a +# ModuleNotFoundError that says nothing about the actual mistake. +BROWSER_RELOAD_AVAILABLE = find_spec("django_browser_reload") is not None + +if DEBUG and BROWSER_RELOAD_AVAILABLE: INSTALLED_APPS += ["django_browser_reload"] MIDDLEWARE += ["django_browser_reload.middleware.BrowserReloadMiddleware"] diff --git a/rosterchief/urls.py b/rosterchief/urls.py index cd1c84d..aa8fa5c 100644 --- a/rosterchief/urls.py +++ b/rosterchief/urls.py @@ -30,6 +30,11 @@ urlpatterns = [ ] if settings.DEBUG: - urlpatterns += [path("__reload__/", include("django_browser_reload.urls"))] + # Only when the app is actually installed. It is a dev dependency, and the production + # image installs with --no-dev, so DEBUG=True in a container must not take the whole + # site down over a package that is only there to refresh a browser tab. + if settings.BROWSER_RELOAD_AVAILABLE: + urlpatterns += [path("__reload__/", include("django_browser_reload.urls"))] + # Club logos are uploads: runserver has to serve MEDIA_ROOT itself. urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) From 2d43b0b90397b009fa4ab4bd6bd548f685255786 Mon Sep 17 00:00:00 2001 From: Bernard Siebens Date: Tue, 14 Jul 2026 23:06:31 +0200 Subject: [PATCH 073/137] Make the control panel a real app shell with a fixed sidebar The navbar and the menu stay put; only the content scrolls. That is a property of the layout, not a `sticky` class: the body is a flex column pinned to the viewport with overflow hidden, and
is the single scrolling region. The sidebar is a SIBLING of
, not inside it, so it sits outside that region and cannot drift by construction. A sticky sidebar in a scrollable body still slides on a long page, which is the failure this avoids. The tabs become a daisyUI menu with an active indicator (`menu-active`) and the hover/focus states `.menu` already provides. Sidebar below `lg` would be a menu nobody can reach, so the same items render as a horizontal menu there -- from ONE partial, because two copies of a link list is how a new section ends up reachable on a desktop and invisible on a phone. Auth pages leave the menu block empty and
simply takes the full width. Verified in a browser: content scrolled 800px, sidebar and navbar moved 0px, and the body is not scrollable at all. Below `lg` the sidebar hides and all five links are still there. Co-Authored-By: Claude Opus 4.8 --- .../templates/controlpanel/_nav_items.html | 37 + controlpanel/templates/controlpanel/base.html | 37 +- static/css/app.css | 5026 +---------------- templates/_base.html | 57 +- 4 files changed, 98 insertions(+), 5059 deletions(-) create mode 100644 controlpanel/templates/controlpanel/_nav_items.html diff --git a/controlpanel/templates/controlpanel/_nav_items.html b/controlpanel/templates/controlpanel/_nav_items.html new file mode 100644 index 0000000..fb638c6 --- /dev/null +++ b/controlpanel/templates/controlpanel/_nav_items.html @@ -0,0 +1,37 @@ +{% load lucide %} + +{% comment %} + The panel's navigation, in one place: the sidebar renders it on a wide screen and the + collapsed menu renders it on a narrow one. Two copies of a link list is how a new section + ends up reachable on a desktop and invisible on a phone. + + `menu-active` is daisyUI 5's active state; hover and focus come with `.menu` itself. +{% endcomment %} +
  • + + {% lucide "layout-dashboard" size=16 %} Dashboard + +
  • +
  • + + {% lucide "building-2" size=16 %} Clubs + +
  • +
  • + + {% lucide "receipt-euro" size=16 %} Billing + +
  • +
  • + + {% lucide "toggle-right" size=16 %} Features + +
  • +{% if user.is_superuser %} + {# Superusers only, exactly as the view is gated: a link staff cannot follow is a lie. #} +
  • + + {% lucide "user-cog" size=16 %} Admins + +
  • +{% endif %} diff --git a/controlpanel/templates/controlpanel/base.html b/controlpanel/templates/controlpanel/base.html index a08eb18..6fafdeb 100644 --- a/controlpanel/templates/controlpanel/base.html +++ b/controlpanel/templates/controlpanel/base.html @@ -5,15 +5,28 @@ {% block panel_title %}Control panel{% endblock panel_title %} · RosterChief {% endblock title %} +{% block menu %} + {% comment %} + Outside
    , so it never scrolls with the content. Its own overflow-y-auto is for + the day the menu itself grows taller than the screen. + {% endcomment %} + +{% endblock menu %} + {% block main %} {% if maintenance_on %}
    {% lucide "wrench" size=20 %} - The platform is closed for maintenance. - Clubs see a maintenance page and the scheduled jobs are standing down. - - Reopen + The platform is currently closed for maintenance. + Clubs see a maintenance page and the scheduled jobs are standing down. + + {% lucide "unlock" size=16 %} Reopen platform
    {% endif %}
    @@ -28,15 +41,11 @@ {% block actions %}{% endblock actions %}
    - - + + {# Below `lg` the sidebar is hidden, so the same links appear here rather than nowhere. #} + + {% block panel %}{% endblock panel %} {% endblock main %} diff --git a/static/css/app.css b/static/css/app.css index e1ea44b..fcb4df2 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -1,5026 +1,2 @@ /*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */ -@layer properties; -@layer theme, base, components, utilities; -@layer theme { - :root, :host { - --font-sans: "Ubuntu", ui-sans-serif, system-ui, sans-serif; - --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace; - --color-sky-500: oklch(68.5% 0.169 237.323); - --color-black: #000; - --color-white: #fff; - --spacing: 0.25rem; - --container-xs: 20rem; - --container-xl: 36rem; - --text-xs: 0.75rem; - --text-xs--line-height: calc(1 / 0.75); - --text-sm: 0.875rem; - --text-sm--line-height: calc(1.25 / 0.875); - --text-base: 1rem; - --text-base--line-height: calc(1.5 / 1); - --text-lg: 1.125rem; - --text-lg--line-height: calc(1.75 / 1.125); - --text-xl: 1.25rem; - --text-xl--line-height: calc(1.75 / 1.25); - --text-2xl: 1.5rem; - --text-2xl--line-height: calc(2 / 1.5); - --text-3xl: 1.875rem; - --text-3xl--line-height: calc(2.25 / 1.875); - --text-4xl: 2.25rem; - --text-4xl--line-height: calc(2.5 / 2.25); - --font-weight-medium: 500; - --font-weight-semibold: 600; - --font-weight-bold: 700; - --tracking-wider: 0.05em; - --ease-out: cubic-bezier(0, 0, 0.2, 1); - --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); - --default-transition-duration: 150ms; - --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - --default-font-family: var(--font-sans); - --default-mono-font-family: var(--font-mono); - --font-ubuntu: "Ubuntu", ui-sans-serif, system-ui, sans-serif; - --font-roboto: "Roboto", ui-sans-serif, system-ui, sans-serif; - --font-tourney: "Tourney", ui-sans-serif, system-ui, sans-serif; - } -} -@layer base { - *, ::after, ::before, ::backdrop, ::file-selector-button { - box-sizing: border-box; - margin: 0; - padding: 0; - border: 0 solid; - } - html, :host { - line-height: 1.5; - -webkit-text-size-adjust: 100%; - tab-size: 4; - font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); - font-feature-settings: var(--default-font-feature-settings, normal); - font-variation-settings: var(--default-font-variation-settings, normal); - -webkit-tap-highlight-color: transparent; - } - hr { - height: 0; - color: inherit; - border-top-width: 1px; - } - abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - } - h1, h2, h3, h4, h5, h6 { - font-size: inherit; - font-weight: inherit; - } - a { - color: inherit; - -webkit-text-decoration: inherit; - text-decoration: inherit; - } - b, strong { - font-weight: bolder; - } - code, kbd, samp, pre { - font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); - font-feature-settings: var(--default-mono-font-feature-settings, normal); - font-variation-settings: var(--default-mono-font-variation-settings, normal); - font-size: 1em; - } - small { - font-size: 80%; - } - sub, sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; - } - sub { - bottom: -0.25em; - } - sup { - top: -0.5em; - } - table { - text-indent: 0; - border-color: inherit; - border-collapse: collapse; - } - :-moz-focusring { - outline: auto; - } - progress { - vertical-align: baseline; - } - summary { - display: list-item; - } - ol, ul, menu { - list-style: none; - } - img, svg, video, canvas, audio, iframe, embed, object { - display: block; - vertical-align: middle; - } - img, video { - max-width: 100%; - height: auto; - } - button, input, select, optgroup, textarea, ::file-selector-button { - font: inherit; - font-feature-settings: inherit; - font-variation-settings: inherit; - letter-spacing: inherit; - color: inherit; - border-radius: 0; - background-color: transparent; - opacity: 1; - } - :where(select:is([multiple], [size])) optgroup { - font-weight: bolder; - } - :where(select:is([multiple], [size])) optgroup option { - padding-inline-start: 20px; - } - ::file-selector-button { - margin-inline-end: 4px; - } - ::placeholder { - opacity: 1; - } - @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) { - ::placeholder { - color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, currentcolor 50%, transparent); - } - } - } - textarea { - resize: vertical; - } - ::-webkit-search-decoration { - -webkit-appearance: none; - } - ::-webkit-date-and-time-value { - min-height: 1lh; - text-align: inherit; - } - ::-webkit-datetime-edit { - display: inline-flex; - } - ::-webkit-datetime-edit-fields-wrapper { - padding: 0; - } - ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { - padding-block: 0; - } - ::-webkit-calendar-picker-indicator { - line-height: 1; - } - :-moz-ui-invalid { - box-shadow: none; - } - button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button { - appearance: button; - } - ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { - height: auto; - } - [hidden]:where(:not([hidden="until-found"])) { - display: none !important; - } -} -@layer utilities { - .diff { - @layer daisyui.l1.l2 { - position: relative; - display: grid; - width: 100%; - overflow: hidden; - webkit-user-select: none; - user-select: none; - align-items: normal; - grid-template-rows: 1fr 1.8rem 1fr; - direction: ltr; - container-type: inline-size; - grid-template-columns: auto 1fr; - &:focus-visible, &:has(.diff-item-1:focus-visible) { - outline-style: var(--tw-outline-style); - outline-width: 2px; - outline-offset: 1px; - outline-color: var(--color-base-content); - } - &:focus-visible { - outline-style: var(--tw-outline-style); - outline-width: 2px; - outline-offset: 1px; - outline-color: var(--color-base-content); - .diff-resizer { - min-width: 95cqi; - max-width: 95cqi; - } - } - &:has(.diff-item-1:focus-visible) { - outline-style: var(--tw-outline-style); - outline-width: 2px; - outline-offset: 1px; - .diff-resizer { - min-width: 5cqi; - max-width: 5cqi; - } - } - &:hover { - .diff-item-2 { - &::after { - height: 2.4rem; - } - } - } - @supports (-webkit-overflow-scrolling: touch) and (overflow: -webkit-paged-x) { - &:focus { - .diff-resizer { - min-width: 5cqi; - max-width: 5cqi; - } - } - &:has(.diff-item-1:focus) { - .diff-resizer { - min-width: 95cqi; - max-width: 95cqi; - } - } - } - } - } - .tooltip { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - --tt-bg: var(--color-neutral); - --tt-off: calc(100% + 0.5rem); - --tt-tail: calc(100% + 1px + 0.25rem); - --tt-tail-off: 0.5rem; - & > .tooltip-content, &[data-tip]:before { - position: absolute; - max-width: 20rem; - border-radius: var(--radius-field); - padding-inline: calc(0.25rem * 2); - padding-block: 0.25rem; - text-align: center; - white-space: normal; - color: var(--color-neutral-content); - opacity: 0%; - font-size: 0.875rem; - line-height: 1.25; - background-color: var(--tt-bg); - width: max-content; - pointer-events: none; - z-index: 2; - --tw-content: attr(data-tip); - content: var(--tw-content); - } - &:after { - opacity: 0%; - background-color: var(--tt-bg); - content: ""; - pointer-events: none; - width: 0.625rem; - height: 0.25rem; - display: block; - position: absolute; - mask-repeat: no-repeat; - mask-position: -1px 0; - --mask-tooltip: url("data:image/svg+xml,%3Csvg width='10' height='4' viewBox='0 0 8 4' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.500009 1C3.5 1 3.00001 4 5.00001 4C7 4 6.5 1 9.5 1C10 1 10 0.499897 10 0H0C-1.99338e-08 0.5 0 1 0.500009 1Z' fill='black'/%3E%3C/svg%3E%0A"); - mask-image: var(--mask-tooltip); - } - @media (prefers-reduced-motion: no-preference) { - & > .tooltip-content, &[data-tip]:before, &:after { - transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1) 75ms, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1) 75ms; - } - } - &:is([data-tip]:not([data-tip=""]), :has(.tooltip-content:not(:empty))) { - &.tooltip-open, &:hover, &:has(:focus-visible) { - & > .tooltip-content, &[data-tip]:before, &:after { - opacity: 100%; - --tt-pos: 0rem; - @media (prefers-reduced-motion: no-preference) { - transition: opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1) 0s, transform 0.2s cubic-bezier(0.4, 0, 0.2, 1) 0s; - } - } - } - } - } - @layer daisyui.l1.l2 { - > .tooltip-content, &[data-tip]:before { - transform: translateX(var(--tt-trans, -50%)) translateY(var(--tt-pos, 0.25rem)); - inset: auto auto var(--tt-off) 50%; - } - &:after { - transform: translateX(var(--tt-trans, -50%)) translateY(var(--tt-pos, 0.25rem)); - inset: auto auto var(--tt-tail) 50%; - } - } - } - .tab { - @layer daisyui.l1.l2.l3 { - &:is(.tabs > .tab) { - position: relative; - display: inline-flex; - cursor: pointer; - appearance: none; - flex-wrap: wrap; - align-items: center; - justify-content: center; - text-align: center; - webkit-user-select: none; - user-select: none; - &:hover { - @media (hover: hover) { - color: var(--color-base-content); - } - } - --tab-p: 0.75rem; - --tab-bg: var(--color-base-100); - --tab-border-color: var(--color-base-300); - --tab-radius-ss: 0; - --tab-radius-se: 0; - --tab-radius-es: 0; - --tab-radius-ee: 0; - --tab-order: 0; - --tab-radius-min: calc(0.75rem - var(--border)); - --tab-radius-limit: min(var(--radius-field), var(--tab-radius-min)); - --tab-radius-grad: #0000 calc(69% - var(--border)), var(--tab-border-color) calc(69% - var(--border) + 0.25px), - var(--tab-border-color) 69%, var(--tab-bg) calc(69% + 0.25px); - border-color: #0000; - order: var(--tab-order); - height: var(--tab-height); - font-size: 0.875rem; - padding-inline: var(--tab-p); - &:is(input[type="radio"]) { - min-width: fit-content; - &:after { - --tw-content: attr(aria-label); - content: var(--tw-content); - } - } - &:is(label) { - position: relative; - input { - position: absolute; - inset: 0; - cursor: pointer; - appearance: none; - opacity: 0%; - } - } - &:checked, &:is(label:has(:checked)), &:is(.tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"]) { - & + .tab-content { - display: block; - } - } - &:not(:checked, label:has(:checked), :hover, .tab-active, [aria-selected="true"], [aria-current="true"], [aria-current="page"] ) { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 50%, transparent); - } - } - &:not(input):empty { - flex-grow: 1; - cursor: default; - } - &:focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:focus-visible, &:is(label:has(:checked:focus-visible)) { - outline: 2px solid currentColor; - outline-offset: -5px; - } - &[disabled] { - pointer-events: none; - opacity: 40%; - } - } - } - } - .menu { - @layer daisyui.l1.l2.l3 { - display: flex; - width: fit-content; - flex-direction: column; - flex-wrap: wrap; - padding: calc(0.25rem * 2); - --menu-active-fg: var(--color-neutral-content); - --menu-active-bg: var(--color-neutral); - font-size: 0.875rem; - :where(li ul, li menu) { - position: relative; - margin-inline-start: calc(0.25rem * 4); - padding-inline-start: calc(0.25rem * 2); - white-space: nowrap; - &:before { - position: absolute; - inset-inline-start: calc(0.25rem * 0); - top: calc(0.25rem * 3); - bottom: calc(0.25rem * 3); - background-color: var(--color-base-content); - opacity: 10%; - width: var(--border); - content: ""; - } - } - :where(li > .menu-dropdown:not(.menu-dropdown-show)) { - display: none; - } - :where(li:not(.menu-title) > *:not(ul, menu, details, .menu-title, .btn)), :where(li:not(.menu-title) > details > summary:not(.menu-title)) { - display: grid; - grid-auto-flow: column; - align-content: flex-start; - align-items: center; - gap: calc(0.25rem * 2); - border-radius: var(--radius-field); - padding-inline: calc(0.25rem * 3); - padding-block: calc(0.25rem * 1.5); - text-align: start; - transition-property: color, background-color, box-shadow; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - grid-auto-columns: minmax(auto, max-content) auto max-content; - user-select: none; - } - :where(li > details > summary) { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - &::-webkit-details-marker { - display: none; - } - } - :where(li > details > summary), :where(li > .menu-dropdown-toggle) { - &:after { - justify-self: flex-end; - display: block; - height: 0.375rem; - width: 0.375rem; - rotate: -135deg; - translate: 0 -1px; - transition-property: rotate, translate; - transition-duration: 0.2s; - content: ""; - transform-origin: 50% 50%; - box-shadow: 2px 2px inset; - pointer-events: none; - } - } - details { - overflow: hidden; - interpolate-size: allow-keywords; - } - details::details-content { - block-size: 0; - @media (prefers-reduced-motion: no-preference) { - transition-behavior: allow-discrete; - transition-property: block-size, content-visibility; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - } - } - details[open]::details-content { - block-size: auto; - } - :where(li > details[open] > summary):after, :where(li > .menu-dropdown-toggle.menu-dropdown-show):after { - rotate: 45deg; - translate: 0 1px; - } - :where(li:not(.menu-title, .disabled) > *:not(ul, menu, details, .menu-title), li:not(.menu-title, .disabled) > details > summary:not(.menu-title) ):not(.menu-active, :active, .btn) { - &.menu-focus, &:focus-visible { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - color: var(--color-base-content); - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - } - :where(li:not(.menu-title, .disabled) > *:not(ul, menu, details, .menu-title):not(.menu-active, :active, .btn):hover, li:not(.menu-title, .disabled) > details > summary:not(.menu-title):not(.menu-active, :active, .btn):hover ) { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - box-shadow: 0 1px oklch(0% 0 0 / 0.01) inset, 0 -1px oklch(100% 0 0 / 0.01) inset; - } - :where(li:empty) { - background-color: var(--color-base-content); - opacity: 10%; - margin: 0.5rem 1rem; - height: 1px; - } - :where(li) { - position: relative; - display: flex; - flex-shrink: 0; - flex-direction: column; - flex-wrap: wrap; - align-items: stretch; - .badge { - justify-self: flex-end; - } - & > *:not(ul, menu, .menu-title, details, .btn):active, & > *:not(ul, menu, .menu-title, details, .btn).menu-active, & > details > summary:active { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - color: var(--menu-active-fg); - background-color: var(--menu-active-bg); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - &:not(&:active) { - box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg); - } - } - &.menu-disabled, [disabled] { - pointer-events: none; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - } - } - .dropdown:focus-within { - .menu-dropdown-toggle:after { - rotate: 45deg; - translate: 0 1px; - } - } - .dropdown-content { - margin-top: calc(0.25rem * 2); - padding: calc(0.25rem * 2); - &:before { - display: none; - } - } - } - } - .otp { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - font-family: var(--font-mono); - clip-path: inset(-3.5px 3.5px -3.5px -3.5px); - border-radius: var(--radius-field); - font-size: 1.75rem; - gap: var(--otp-gap); - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --otp-ch: 1ch; - @supports (font: -apple-system-body) { - --otp-ch: 0.618164em; - } - --otp-gap: calc(var(--otp-ch) * 0.5); - --otp-w: calc(var(--otp-ch) * 2); - --otp-size: calc(var(--size-field, 0.25rem) * 10); - --stride: calc(var(--otp-w) + var(--otp-gap)); - > input { - pointer-events: none; - inset-inline-start: calc(0.25rem * 0); - z-index: 1; - margin: 0; - appearance: none; - border-style: var(--tw-border-style); - border-width: 0px; - background-color: transparent; - padding: 0; - outline-style: var(--tw-outline-style); - outline-width: 0px; - field-sizing: content; - padding-inline-start: calc(var(--otp-ch) * 0.5 + var(--border)); - line-height: 1; - letter-spacing: calc(var(--stride) - var(--otp-ch)); - font-variant-numeric: tabular-nums; - &::selection { - background-color: transparent; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - } - &:valid { - caret-color: transparent; - } - } - &:has( > span:nth-child(1)) { - width: calc(var(--stride) * 1); - } - &:has( > span:nth-child(2)) { - width: calc(var(--stride) * 2); - } - &:has( > span:nth-child(3)) { - width: calc(var(--stride) * 3); - } - &:has( > span:nth-child(4)) { - width: calc(var(--stride) * 4); - } - &:has( > span:nth-child(5)) { - width: calc(var(--stride) * 5); - } - &:has( > span:nth-child(6)) { - width: calc(var(--stride) * 6); - } - &:has( > span:nth-child(7)) { - width: calc(var(--stride) * 7); - } - &:has( > span:nth-child(8)) { - width: calc(var(--stride) * 8); - } - > span { - position: absolute; - display: flex; - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - transition: border-color 0.2s; - inline-size: var(--otp-w); - block-size: var(--otp-size); - background-color: var(--color-base-100); - border: var(--border) solid var(--input-color); - border-radius: inherit; - outline: 2px solid #0000; - outline-offset: 1px; - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - &:nth-child(1) { - left: 1px; - @supports (font: -apple-system-body) { - left: 2px; - } - @supports (-moz-appearance: none) { - left: 0px; - } - } - &:nth-child(2) { - left: calc(var(--stride) * 1); - transition-delay: 0.02s; - } - &:nth-child(3) { - left: calc(var(--stride) * 2); - transition-delay: 0.04s; - } - &:nth-child(4) { - left: calc(var(--stride) * 3); - transition-delay: 0.06s; - } - &:nth-child(5) { - left: calc(var(--stride) * 4); - transition-delay: 0.08s; - } - &:nth-child(6) { - left: calc(var(--stride) * 5); - transition-delay: 0.1s; - } - &:nth-child(7) { - left: calc(var(--stride) * 6); - transition-delay: 0.12s; - } - &:nth-child(8) { - left: calc(var(--stride) * 7); - transition-delay: 0.14s; - } - } - &:has(input:valid:focus) { - > span { - outline: 2px solid var(--input-color); - outline-offset: 1px; - } - &:after { - opacity: 0; - } - } - &:after { - flex-shrink: 0; - content: ""; - width: var(--otp-w); - height: var(--otp-size); - border-radius: var(--radius-field); - outline: 2px solid #0000; - outline-offset: 1px; - z-index: 10; - margin-inline-start: calc(-1 * (var(--otp-gap) + var(--otp-ch) * 0.5 + var(--border))); - } - &:focus-within { - isolation: isolate; - --input-color: var(--color-base-content); - > span { - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - } - &:after { - outline-color: var(--input-color); - } - } - } - } - .collapse-arrow { - @layer daisyui.l1.l2 { - > .collapse-title:after { - position: absolute; - display: block; - height: 0.5rem; - width: 0.5rem; - transform: translateY(-100%) rotate(45deg); - @media (prefers-reduced-motion: no-preference) { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 0.2s; - } - top: 50%; - inset-inline-end: 1.4rem; - content: ""; - transform-origin: 75% 75%; - box-shadow: 2px 2px; - pointer-events: none; - } - } - } - .collapse-plus { - @layer daisyui.l1.l2 { - > .collapse-title:after { - position: absolute; - display: block; - height: 0.5rem; - width: 0.5rem; - @media (prefers-reduced-motion: no-preference) { - transition-property: all; - transition-duration: 300ms; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - top: 0.9rem; - inset-inline-end: 1.4rem; - --tw-content: "+"; - content: var(--tw-content); - pointer-events: none; - } - } - } - .diff-item-2 { - @layer daisyui.l1.l2.l3 { - position: relative; - grid-column-start: 1; - grid-row: span 3 / span 3; - grid-row-start: 1; - &:after { - pointer-events: none; - position: absolute; - top: calc(1 / 2 * 100%); - right: 1px; - bottom: 0; - z-index: 2; - border-radius: calc(infinity * 1px); - background-color: var(--color-base-100); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-100) 98%, transparent); - } - width: 1.2rem; - height: 1.8rem; - border: 2px solid var(--color-base-100); - content: ""; - box-shadow: 0 0 0 2px #0000002a; - outline: 2px solid var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - outline: 2px solid color-mix(in oklab, var(--color-base-content) 10%, #0000); - } - outline-offset: -3px; - translate: 50% -50%; - transition: height 0.3s linear(0, 0.931 13.8%, 1.196 21.4%, 1.343 29.8%, 1.378 36%, 1.365 43.2%, 1.059 78%, 1); - } - > * { - pointer-events: none; - position: absolute; - top: 0; - bottom: 0; - left: 0; - height: 100%; - width: 100cqi; - max-width: none; - object-fit: cover; - object-position: center; - } - @supports (-webkit-overflow-scrolling: touch) and (overflow: -webkit-paged-x) { - &:after { - --tw-content: none; - content: var(--tw-content); - } - } - } - } - .dropdown { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - position-area: var(--anchor-v, bottom) var(--anchor-h, span-right); - & > *:not(:has( ~ [class*="dropdown-content"])):focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - .dropdown-content { - position: absolute; - } - &.dropdown-close .dropdown-content, &:not(details, .dropdown-open, .dropdown-hover:hover, :focus-within) .dropdown-content, &.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible) ~ .dropdown-content { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - &[popover], .dropdown-content { - z-index: 999; - @media (prefers-reduced-motion: no-preference) { - animation: dropdown 0.2s; - transition-property: opacity, scale, display, overlay; - transition-behavior: allow-discrete; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - } - } - @starting-style { - &[popover], .dropdown-content { - scale: 95%; - opacity: 0; - } - } - &:not(.dropdown-close) { - &.dropdown-open, &:not(.dropdown-hover):focus, &:focus-within { - > [tabindex]:first-child { - pointer-events: none; - } - .dropdown-content { - opacity: 100%; - scale: 100%; - } - } - &.dropdown-hover:hover { - .dropdown-content { - opacity: 100%; - scale: 100%; - } - } - } - &:is(details) { - overflow: revert-layer; - summary { - &::-webkit-details-marker { - display: none; - } - } - } - &:where([popover]) { - background: #0000; - } - &[popover] { - position: fixed; - color: inherit; - @supports not (position-area: bottom) { - margin: auto; - &.dropdown-close, &.dropdown-open:not(:popover-open) { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - &::backdrop { - background-color: color-mix(in oklab, #000 30%, #0000); - } - } - &.dropdown-close, &:not(.dropdown-open, :popover-open) { - display: none; - transform-origin: top; - opacity: 0%; - scale: 95%; - } - } - } - } - .btn { - :where(&) { - @layer daisyui.l1.l2.l3 { - width: unset; - } - } - .prose :where(a&:not(.btn-link)):not(:where([class~="not-prose"], [class~="not-prose"] *)) { - text-decoration-line: none; - } - @layer daisyui.l1.l2.l3 { - --size: calc(var(--size-field, 0.25rem) * 10); - --btn-p: 1rem; - --btn-fg: var(--color-base-content); - display: inline-flex; - flex-shrink: 0; - cursor: pointer; - flex-wrap: nowrap; - align-items: center; - justify-content: center; - gap: calc(0.25rem * 1.5); - text-align: center; - vertical-align: middle; - outline-offset: 2px; - webkit-user-select: none; - user-select: none; - font-weight: 600; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - border-width: var(--border); - touch-action: manipulation; - transition-property: color, background-color, border-color, box-shadow, transform; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-duration: 0.2s; - --btn-bg: var(--btn-color, var(--color-base-200)); - --btn-border: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix( - in oklab, - var(--btn-color, var(--color-base-200)), - #000 calc(var(--depth) * 5%) - ); - } - --btn-soft-bg: initial; - --btn-shadow: 0 3px 2px -2px var(--btn-bg), - 0 4px 3px -2px var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), - 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); - } - --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); - height: var(--size); - padding-inline: var(--btn-p); - font-size: var(--fontsize, 0.875rem); - background-color: var(--btn-bg); - color: var(--btn-fg); - border-color: var(--btn-border); - border-style: var(--btn-border-style, solid); - outline-color: var(--btn-color, var(--color-base-content)); - --tw-prose-links: var(--btn-fg); - background-image: none, var(--fx-noise); - background-size: auto, calc(var(--noise, 0) * 100%); - text-shadow: 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 0.15)); - box-shadow: var(--btn-inset) inset, var(--btn-shadow); - &:is([type="checkbox"], [type="radio"]) { - appearance: none; - &[aria-label]::after { - --tw-content: attr(aria-label); - content: var(--tw-content); - } - } - } - @layer daisyui.l1 { - &:where(:checked:not(.filter [type="radio"].btn)) { - --btn-color: var(--color-primary); - --btn-fg: var(--color-primary-content); - } - @media (hover: hover) { - &:hover { - --btn-bg: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-bg: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 7%); - } - color: var(--btn-fg); - --btn-border: var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%)); - } - --btn-border-style: solid; - --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); - --btn-shadow: 0 3px 2px -2px var(--btn-bg), - 0 4px 3px -2px var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), - 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); - } - } - } - &:active:not(.btn-active) { - translate: 0 0.5px; - --btn-bg: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-bg: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 5%); - } - color: var(--btn-fg, var(--color-base-content)); - --btn-border: var(--btn-color, var(--color-base-200)); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix(in oklab, var(--btn-color, var(--color-base-200)), #000 7%); - } - --btn-border-style: solid; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - } - &:where(:checked:not(.filter [type="radio"].btn), :not([type="radio"], [type="checkbox"]):focus-visible ) { - --btn-bg: var(--btn-color, var(--color-base-200)); - color: var(--btn-fg, var(--color-base-content)); - --btn-border: var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%)); - } - --btn-border-style: solid; - --btn-inset: 0 0.5px 0 0.5px oklch(100% 0 0 / calc(var(--depth) * 6%)); - --btn-shadow: 0 3px 2px -2px var(--btn-bg), - 0 4px 3px -2px var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), - 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000); - } - isolation: isolate; - } - &:focus-visible, &:has(:focus-visible) { - outline-width: 2px; - outline-style: solid; - isolation: isolate; - } - } - &:is(:disabled, [disabled], [aria-disabled="true"]) { - @layer daisyui { - pointer-events: none; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklch, var(--color-base-content) 20%, #0000); - } - --btn-bg: #0000; - --btn-border: #0000; - background-image: none; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - &:not(.btn-link, .btn-ghost) { - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - } - } - } - } - .loading { - @layer daisyui.l1.l2.l3 { - pointer-events: none; - display: inline-block; - aspect-ratio: 1 / 1; - background-color: currentcolor; - vertical-align: middle; - width: calc(var(--size-selector, 0.25rem) * 6); - mask-size: 100%; - mask-repeat: no-repeat; - mask-position: center; - mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='8s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='6s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='6s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); - @media (prefers-reduced-motion: no-preference) { - mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='black' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg transform-origin='center'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3' stroke-linecap='round'%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 12 12' to='360 12 12' dur='2s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dasharray' values='0,150;42,150;42,150' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3Canimate attributeName='stroke-dashoffset' values='0;-16;-59' keyTimes='0;0.475;1' dur='1.5s' repeatCount='indefinite'/%3E%3C/circle%3E%3C/g%3E%3C/svg%3E"); - } - } - } - .collapse { - &:not(td, tr, colgroup) { - visibility: revert-layer; - } - @layer daisyui.l1.l2.l3 { - display: grid; - position: relative; - border-radius: var(--radius-box, 1rem); - width: 100%; - grid-template-rows: max-content 0fr; - grid-template-columns: minmax(0, 1fr); - isolation: isolate; - @media (prefers-reduced-motion: no-preference) { - transition: grid-template-rows 0.2s; - } - > input:is([type="checkbox"], [type="radio"]) { - grid-column-start: 1; - grid-row-start: 1; - appearance: none; - opacity: 0; - z-index: 1; - width: 100%; - padding: 1rem; - padding-inline-end: 3rem; - min-height: 1lh; - transition: background-color 0.2s ease-out; - } - &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ), &:not(.collapse-close):has( > input:is([type="checkbox"], [type="radio"]):checked) { - grid-template-rows: max-content 1fr; - } - &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ) > .collapse-content, &:not(.collapse-close) > :where(input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-content) { - --overflow-delay: 0.2s; - overflow: revert-layer; - content-visibility: visible; - min-height: fit-content; - @supports not (content-visibility: visible) { - visibility: visible; - } - } - &:focus-visible, &:has( > input:is([type="checkbox"], [type="radio"]):focus-visible), &:has(summary:focus-visible) { - outline-color: var(--color-base-content); - outline-style: solid; - outline-width: 2px; - outline-offset: 2px; - } - &:not(.collapse-close) { - > input[type="checkbox"], > input[type="radio"]:not(:checked), > .collapse-title { - cursor: pointer; - } - } - &[tabindex]:focus:not(.collapse-close, .collapse[open]), &[tabindex]:focus-within:not(.collapse-close, .collapse[open]) { - > .collapse-title { - cursor: unset; - } - } - &:is([open], [tabindex]:focus:not(.collapse-close), [tabindex]:focus-within:not(.collapse-close) ) > :where(.collapse-content), &:not(.collapse-close) > :where(input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-content) { - padding-bottom: 1rem; - } - &:is(details) { - width: 100%; - &::details-content { - --overflow-delay: 0s; - overflow: clip; - height: 0; - } - &:where([open])::details-content { - overflow: revert-layer; - height: auto; - } - @media (prefers-reduced-motion: no-preference) { - &::details-content { - transition: overflow 0.2s allow-discrete var(--overflow-delay), content-visibility 0.2s allow-discrete, visibility 0.2s allow-discrete, min-height 0.2s ease-out allow-discrete, padding 0.1s ease-out 20ms, background-color 0.2s ease-out, height 0.2s; - interpolate-size: allow-keywords; - } - &:where([open])::details-content { - --overflow-delay: 0.2s; - } - } - > summary { - position: relative; - display: block; - outline: none; - &::-webkit-details-marker { - display: none; - } - } - } - } - @layer daisyui.l1.l2 { - &:is([open]) { - &.collapse-arrow { - > .collapse-title:after { - @media (prefers-reduced-motion: no-preference) { - transform: translateY(-50%) rotate(225deg); - } - } - } - } - &.collapse-open { - &.collapse-arrow { - > .collapse-title:after { - @media (prefers-reduced-motion: no-preference) { - transform: translateY(-50%) rotate(225deg); - } - } - } - &.collapse-plus { - > .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - } - &[tabindex].collapse-arrow:focus:not(.collapse-close), &.collapse-arrow[tabindex]:focus-within:not(.collapse-close) { - > .collapse-title:after { - transform: translateY(-50%) rotate(225deg); - } - } - &.collapse-arrow:not(.collapse-close) { - > input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-title:after { - transform: translateY(-50%) rotate(225deg); - } - } - &[open] { - &.collapse-plus { - > .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - } - &[tabindex].collapse-plus:focus:not(.collapse-close) { - > .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - &.collapse-plus:not(.collapse-close) { - > input:is([type="checkbox"], [type="radio"]):checked ~ .collapse-title:after { - --tw-content: "−"; - content: var(--tw-content); - } - } - } - } - .collapse-content { - @layer daisyui.l1.l2.l3 { - --overflow-delay: 0s; - content-visibility: hidden; - overflow: clip; - grid-column-start: 1; - grid-row-start: 2; - min-height: 0; - padding-left: 1rem; - padding-right: 1rem; - cursor: unset; - @supports not (content-visibility: hidden) { - visibility: hidden; - } - @media (prefers-reduced-motion: no-preference) { - transition: overflow 0.2s allow-discrete var(--overflow-delay), content-visibility 0.2s allow-discrete, visibility 0.2s allow-discrete, min-height 0.2s ease-out allow-discrete, padding 0.1s ease-out 20ms, background-color 0.2s ease-out; - } - details > & { - content-visibility: visible; - } - } - } - .\!filter { - @layer daisyui.l1.l2.l3 { - display: flex !important; - flex-wrap: wrap !important; - [type="radio"] { - width: auto !important; - } - input { - overflow: hidden !important; - opacity: 100% !important; - scale: 1 !important; - transition: visibility 0.1s allow-discrete, margin 0.1s, opacity 0.3s, padding 0.3s, border-width 0.1s !important; - &.filter-reset { - aspect-ratio: 1 / 1 !important; - &::after { - --tw-content: "×" !important; - content: var(--tw-content) !important; - } - } - } - > input:not(:last-child), > :not(:last-child) input { - margin-inline-end: 0.25rem !important; - } - } - @layer daisyui.l1 { - &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible) { - visibility: hidden !important; - } - &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible), &:not(:has(:focus-visible)):has(:checked:not(.filter-reset, [type="checkbox"])) :is(input, button):not(:checked, .filter-reset, [type="reset"]) { - margin-inline: 0 !important; - width: 0 !important; - padding-inline: 0 !important; - opacity: 0% !important; - scale: 0 !important; - border-width: 0 !important; - } - } - } - .filter { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-wrap: wrap; - [type="radio"] { - width: auto; - } - input { - overflow: hidden; - opacity: 100%; - scale: 1; - transition: visibility 0.1s allow-discrete, margin 0.1s, opacity 0.3s, padding 0.3s, border-width 0.1s; - &.filter-reset { - aspect-ratio: 1 / 1; - &::after { - --tw-content: "×"; - content: var(--tw-content); - } - } - } - > input:not(:last-child), > :not(:last-child) input { - margin-inline-end: 0.25rem; - } - } - @layer daisyui.l1 { - &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible) { - visibility: hidden; - } - &:not(:has(:checked:not(.filter-reset))) :is(.filter-reset, [type="reset"]):not(:focus-visible), &:not(:has(:focus-visible)):has(:checked:not(.filter-reset, [type="checkbox"])) :is(input, button):not(:checked, .filter-reset, [type="reset"]) { - margin-inline: 0; - width: 0; - padding-inline: 0; - opacity: 0%; - scale: 0; - border-width: 0; - } - } - } - .validator-hint { - @layer daisyui.l1.l2.l3 { - visibility: hidden; - margin-top: calc(0.25rem * 2); - font-size: 0.75rem; - } - } - .validator { - @layer daisyui.l1.l2.l3 { - &:user-valid, &:has(:user-valid) { - &, &:focus, &:checked, &[aria-checked="true"], &:focus-within { - --input-color: var(--color-success); - } - } - &:user-invalid, &:has(:user-invalid), &[aria-invalid]:not([aria-invalid="false"]), &:has([aria-invalid]:not([aria-invalid="false"])) { - &, &:focus, &:checked, &[aria-checked="true"], &:focus-within { - --input-color: var(--color-error); - } - & ~ .validator-hint { - visibility: visible; - color: var(--color-error); - } - } - } - &:user-invalid, &:has(:user-invalid), &[aria-invalid]:not([aria-invalid="false"]), &:has([aria-invalid]:not([aria-invalid="false"])) { - & ~ .validator-hint { - display: revert-layer; - } - } - } - .collapse-open { - @layer daisyui.l1.l2 { - grid-template-rows: max-content 1fr; - > .collapse-content { - --overflow-delay: 0.2s; - overflow: revert-layer; - content-visibility: visible; - min-height: fit-content; - padding-bottom: 1rem; - @supports not (content-visibility: visible) { - visibility: visible; - } - } - } - } - .collapse { - visibility: collapse; - } - .invisible { - visibility: hidden; - } - .visible { - visibility: visible; - } - .list { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-direction: column; - font-size: 0.875rem; - .list-row { - --list-grid-cols: minmax(0, auto) 1fr; - position: relative; - display: grid; - grid-auto-flow: column; - gap: calc(0.25rem * 4); - border-radius: var(--radius-box); - padding: calc(0.25rem * 4); - word-break: break-word; - grid-template-columns: var(--list-grid-cols); - } - & > :not(:last-child) { - &.list-row, .list-row { - &:after { - content: ""; - border-bottom: var(--border) solid; - inset-inline: var(--radius-box); - position: absolute; - bottom: 0; - border-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-base-content) 5%, transparent); - } - } - } - } - } - @layer daisyui.l1.l2 { - .list-row { - &:has(.list-col-grow:nth-child(1)) { - --list-grid-cols: 1fr; - } - &:has(.list-col-grow:nth-child(2)) { - --list-grid-cols: minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(3)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(4)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(5)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr; - } - &:has(.list-col-grow:nth-child(6)) { - --list-grid-cols: minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) - minmax(0, auto) 1fr; - } - > * { - grid-row-start: 1; - } - } - } - } - .toast { - @layer daisyui.l1.l2.l3 { - position: fixed; - inset-inline-start: auto; - inset-inline-end: calc(0.25rem * 4); - top: auto; - bottom: calc(0.25rem * 4); - display: flex; - flex-direction: column; - gap: calc(0.25rem * 2); - background-color: transparent; - translate: var(--toast-x, 0) var(--toast-y, 0); - width: max-content; - max-width: calc(100vw - 2rem); - & > * { - @media (prefers-reduced-motion: no-preference) { - animation: toast 0.25s ease-out; - } - } - } - } - .toggle { - @layer daisyui.l1.l2.l3 { - border: var(--border) solid currentColor; - color: var(--input-color); - position: relative; - display: inline-grid; - flex-shrink: 0; - cursor: pointer; - appearance: none; - place-content: center; - vertical-align: middle; - webkit-user-select: none; - user-select: none; - grid-template-columns: 0fr 1fr 1fr; - --radius-selector-max: calc( - var(--radius-selector) + var(--radius-selector) + var(--radius-selector) - ); - border-radius: calc( var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)) ); - padding: var(--toggle-p); - box-shadow: 0 1px currentColor inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset; - } - transition: color 0.3s, grid-template-columns 0.2s; - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --toggle-p: calc(var(--size) * 0.125); - --size: calc(var(--size-selector, 0.25rem) * 6); - width: calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2); - height: var(--size); - > * { - z-index: 1; - grid-column: span 1 / span 1; - grid-column-start: 2; - grid-row-start: 1; - height: 100%; - cursor: pointer; - appearance: none; - background-color: transparent; - padding: calc(0.25rem * 0.5); - transition: opacity 0.2s, rotate 0.4s; - border: none; - &:focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:nth-child(2) { - color: var(--color-base-100); - rotate: 0deg; - } - &:nth-child(3) { - color: var(--color-base-100); - opacity: 0%; - rotate: -15deg; - } - } - &:has(:checked) { - > :nth-child(2) { - opacity: 0%; - rotate: 15deg; - } - > :nth-child(3) { - opacity: 100%; - rotate: 0deg; - } - } - &:before { - position: relative; - inset-inline-start: calc(0.25rem * 0); - grid-column-start: 2; - grid-row-start: 1; - aspect-ratio: 1 / 1; - height: 100%; - width: 100%; - border-radius: var(--radius-selector); - background-color: currentcolor; - translate: 0; - --tw-content: ""; - content: var(--tw-content); - transition: background-color 0.1s, translate 0.2s, inset-inline-start 0.2s; - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px currentColor; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000); - } - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - } - @media (forced-colors: active) { - &:before { - outline-style: var(--tw-outline-style); - outline-width: 1px; - outline-offset: calc(1px * -1); - } - } - @media print { - &:before { - outline: 0.25rem solid; - outline-offset: -1rem; - } - } - &:focus-visible, &:has(:focus-visible) { - outline: 2px solid currentColor; - outline-offset: 2px; - } - &:checked, &[aria-checked="true"], &:has( > input:checked) { - grid-template-columns: 1fr 1fr 0fr; - background-color: var(--color-base-100); - --input-color: var(--color-base-content); - &:before { - background-color: currentcolor; - } - @starting-style { - &:before { - opacity: 0; - } - } - } - &:indeterminate { - grid-template-columns: 0.5fr 1fr 0.5fr; - } - &:disabled { - cursor: not-allowed; - opacity: 30%; - &:before { - background-color: transparent; - border: var(--border) solid currentColor; - } - } - } - } - .input { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - flex-shrink: 1; - appearance: none; - align-items: center; - gap: calc(0.25rem * 2); - background-color: var(--color-base-100); - padding-inline: calc(0.25rem * 3); - vertical-align: middle; - white-space: nowrap; - --size: calc(var(--size-field, 0.25rem) * var(--in-size-mul)); - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - cursor: text; - width: clamp(3rem, 20rem, 100%); - height: var(--size); - font-size: max(var(--font-size, 0rem), var(--font-size-min)); - touch-action: manipulation; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - border: var(--border) solid var(--input-color, #0000); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - input { - height: 100%; - width: 100%; - appearance: none; - background-color: transparent; - border: none; - &::placeholder { - color: var(--color-base-content); - opacity: 50%; - } - &:focus, &:focus-within { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &::-webkit-calendar-picker-indicator { - inset-inline-end: -0.15em; - } - } - &::-webkit-inner-spin-button { - margin-inline-end: -10px; - } - &::-webkit-calendar-picker-indicator { - inset-inline-end: 0.75em; - } - input&, input { - position: relative; - display: inline-flex; - &[type="url"], &[type="email"] { - text-align: left; - } - &::-webkit-datetime-edit, &::-webkit-date-and-time-value { - display: grid; - min-height: 100%; - align-items: center; - text-align: inherit; - } - &::-webkit-inner-spin-button { - margin-block: calc(0.25rem * var(--spin-my)); - } - &::-webkit-calendar-picker-indicator { - position: absolute; - width: 1em; - height: 1em; - cursor: pointer; - } - &::-webkit-color-swatch-wrapper { - padding-block: 0.25rem; - } - } - &:focus, &:focus-within { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - } - @media (pointer: coarse) { - @supports (-webkit-touch-callout: none) { - &:focus, &:focus-within { - --font-size: 1rem; - } - } - } - &:has( > input[disabled]), &:is(:disabled, [disabled]), fieldset:disabled & { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - &:is(input), :is(input) { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 40%, transparent); - } - } - box-shadow: none; - &::placeholder, ::placeholder { - color: var(--color-base-content); - opacity: 20%; - } - } - &:has( > input[disabled]) > input[disabled] { - cursor: not-allowed; - } - } - @layer daisyui.l1.l2 { - --in-size-mul: 10; - --font-size-min: 0.875rem; - --spin-my: -3; - .floating-label:has(&) { - --top-mul: 5; - --font-size: 0.875rem; - } - } - } - .range { - @layer daisyui.l1.l2.l3 { - appearance: none; - webkit-appearance: none; - --range-thumb: var(--color-base-100); - --range-thumb-size: calc(var(--size-selector, 0.25rem) * 6); - --range-progress: currentColor; - --range-fill: 1; - --range-p: 0.25rem; - --range-bg: currentColor; - @supports (color: color-mix(in lab, red, red)) { - --range-bg: color-mix(in oklab, currentColor 10%, #0000); - } - --range-fill-x: calc( - (var(--range-dir, 1) * -100cqw) - (var(--range-dir, 1) * var(--range-thumb-size) / 2) - ); - --range-fill-y: 0; - --range-fill-spread: calc(100cqw * var(--range-fill)); - cursor: pointer; - overflow: hidden; - background-color: transparent; - vertical-align: middle; - width: clamp(3rem, 20rem, 100%); - --radius-selector-max: calc( - var(--radius-selector) + var(--radius-selector) + var(--radius-selector) - ); - border-radius: calc(var(--radius-selector) + min(var(--range-p), var(--radius-selector-max))); - border: none; - height: var(--range-thumb-size); - [dir="rtl"] & { - --range-dir: -1; - } - &:focus { - outline: none; - } - &:focus-visible { - outline: 2px solid; - outline-offset: 2px; - } - &::-webkit-slider-runnable-track { - width: 100%; - background-color: var(--range-bg); - border-radius: var(--radius-selector); - height: calc(var(--range-thumb-size) * 0.5); - } - @media (forced-colors: active) { - &::-webkit-slider-runnable-track { - border: 1px solid; - } - } - @media (forced-colors: active) { - &::-moz-range-track { - border: 1px solid; - } - } - &::-webkit-slider-thumb { - position: relative; - box-sizing: border-box; - border-radius: calc(var(--radius-selector) + min(var(--range-p), var(--radius-selector-max))); - background-color: var(--range-thumb); - height: var(--range-thumb-size); - width: var(--range-thumb-size); - border: var(--range-p) solid; - appearance: none; - webkit-appearance: none; - inset-block-start: 50%; - color: var(--range-progress); - transform: translateY(-50%); - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px currentColor, 0 0 0 2rem var(--range-thumb) inset, var(--range-fill-x) var(--range-fill-y) 0 var(--range-fill-spread); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000), 0 0 0 2rem var(--range-thumb) inset, var(--range-fill-x) var(--range-fill-y) 0 var(--range-fill-spread); - } - } - &::-moz-range-track { - width: 100%; - background-color: var(--range-bg); - border-radius: var(--radius-selector); - height: calc(var(--range-thumb-size) * 0.5); - } - &::-moz-range-thumb { - position: relative; - box-sizing: border-box; - border-radius: calc(var(--radius-selector) + min(var(--range-p), var(--radius-selector-max))); - background-color: currentColor; - height: var(--range-thumb-size); - width: var(--range-thumb-size); - border: var(--range-p) solid; - color: var(--range-progress); - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px currentColor, 0 0 0 2rem var(--range-thumb) inset, var(--range-fill-x) var(--range-fill-y) 0 var(--range-fill-spread); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000), 0 0 0 2rem var(--range-thumb) inset, var(--range-fill-x) var(--range-fill-y) 0 var(--range-fill-spread); - } - } - &:disabled { - cursor: not-allowed; - opacity: 30%; - } - } - } - .indicator { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - width: max-content; - :where(.indicator-item) { - z-index: 1; - position: absolute; - white-space: nowrap; - top: var(--indicator-t, 0); - bottom: var(--indicator-b, auto); - left: var(--indicator-s, auto); - right: var(--indicator-e, 0); - translate: var(--indicator-x, 50%) var(--indicator-y, -50%); - } - } - } - .table { - @layer daisyui.l1.l2.l3 { - font-size: 0.875rem; - position: relative; - width: 100%; - border-collapse: separate; - --tw-border-spacing-x: 0; - --tw-border-spacing-y: 0; - border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y); - border-radius: var(--radius-box); - text-align: left; - &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *) { - text-align: right; - } - tr.row-hover { - &, &:nth-child(even) { - &:hover { - @media (hover: hover) { - background-color: var(--color-base-200); - } - } - } - } - :where(th, td) { - padding-inline: calc(0.25rem * 4); - padding-block: calc(0.25rem * 3); - vertical-align: middle; - } - :where(thead, tfoot) { - white-space: nowrap; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 60%, transparent); - } - font-size: 0.875rem; - font-weight: 600; - } - :where(tfoot tr:first-child :is(td, th)) { - border-top: var(--border) solid var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-top: var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000); - } - } - :where(.table-pin-rows thead tr) { - position: sticky; - top: 0; - z-index: 1; - background-color: var(--color-base-100); - } - :where(.table-pin-rows tfoot tr) { - position: sticky; - bottom: 0; - z-index: 1; - background-color: var(--color-base-100); - } - :where(.table-pin-cols tr th) { - position: sticky; - right: 0; - left: 0; - background-color: var(--color-base-100); - } - :where(thead tr :is(td, th), tbody tr:not(:last-child) :is(td, th)) { - border-bottom: var(--border) solid var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-bottom: var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000); - } - } - } - } - .aura { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - --aura-padding: 0.125rem; - padding: var(--aura-padding); - border-radius: calc(var(--aura-padding) + var(--aura-radius, var(--radius-box))); - animation: aura var(--tw-duration, 6s) linear infinite; - background-image: conic-gradient(from var(--aura-angle), transparent 225deg, currentColor); - &:has( > .card, > .alert) { - --aura-radius: var(--radius-box); - } - &:has( > .btn, > .input, > .select) { - --aura-radius: var(--radius-field); - } - &:has( > .checkbox, > .toggle, > .badge) { - --aura-radius: var(--radius-selector); - } - &:before, &:after { - animation: inherit; - background-color: inherit; - background-image: inherit; - border-radius: inherit; - position: absolute; - top: calc(1 / 2 * 100%); - left: calc(1 / 2 * 100%); - z-index: 0; - display: block; - opacity: 70%; - filter: blur(0.25rem); - translate: -50% -50%; - width: 100%; - height: 100%; - content: ""; - } - &:after { - opacity: 30%; - filter: blur(1rem); - } - & > * { - position: relative; - z-index: 1; - } - } - } - .steps { - @layer daisyui.l1.l2.l3 { - display: inline-grid; - grid-auto-flow: column; - overflow: hidden; - overflow-x: auto; - counter-reset: step; - grid-auto-columns: 1fr; - .step { - display: grid; - grid-template-columns: repeat(1, minmax(0, 1fr)); - grid-template-columns: auto; - grid-template-rows: repeat(2, minmax(0, 1fr)); - grid-template-rows: 40px 1fr; - place-items: center; - text-align: center; - min-width: 4rem; - --step-bg: var(--color-base-300); - --step-fg: var(--color-base-content); - &:before { - top: 0; - grid-column-start: 1; - grid-row-start: 1; - height: calc(0.25rem * 2); - width: 100%; - border: 1px solid; - color: var(--step-bg); - background-color: var(--step-bg); - content: ""; - margin-inline-start: -100%; - } - > .step-icon, &:not(:has(.step-icon)):after { - --tw-content: counter(step); - content: var(--tw-content); - counter-increment: step; - z-index: 1; - color: var(--step-fg); - background-color: var(--step-bg); - border: 1px solid var(--step-bg); - position: relative; - grid-column-start: 1; - grid-row-start: 1; - display: grid; - height: calc(0.25rem * 8); - width: calc(0.25rem * 8); - place-items: center; - place-self: center; - border-radius: calc(infinity * 1px); - } - &:first-child:before { - --tw-content: none; - content: var(--tw-content); - } - &[data-content]:after { - --tw-content: attr(data-content); - content: var(--tw-content); - } - } - } - @layer daisyui.l1.l2 { - .step-neutral { - + .step-neutral:before, &:after, > .step-icon { - --step-bg: var(--color-neutral); - --step-fg: var(--color-neutral-content); - } - } - .step-primary { - + .step-primary:before, &:after, > .step-icon { - --step-bg: var(--color-primary); - --step-fg: var(--color-primary-content); - } - } - .step-secondary { - + .step-secondary:before, &:after, > .step-icon { - --step-bg: var(--color-secondary); - --step-fg: var(--color-secondary-content); - } - } - .step-accent { - + .step-accent:before, &:after, > .step-icon { - --step-bg: var(--color-accent); - --step-fg: var(--color-accent-content); - } - } - .step-info { - + .step-info:before, &:after, > .step-icon { - --step-bg: var(--color-info); - --step-fg: var(--color-info-content); - } - } - .step-success { - + .step-success:before, &:after, > .step-icon { - --step-bg: var(--color-success); - --step-fg: var(--color-success-content); - } - } - .step-warning { - + .step-warning:before, &:after, > .step-icon { - --step-bg: var(--color-warning); - --step-fg: var(--color-warning-content); - } - } - .step-error { - + .step-error:before, &:after, > .step-icon { - --step-bg: var(--color-error); - --step-fg: var(--color-error-content); - } - } - } - } - .diff-resizer { - @layer daisyui.l1.l2.l3 { - position: relative; - isolation: isolate; - z-index: 2; - grid-column-start: 1; - grid-row-start: 2; - height: calc(0.25rem * 3); - width: 50cqi; - max-width: calc(100cqi - 1rem); - min-width: 1rem; - resize: horizontal; - overflow: hidden; - opacity: 0%; - transform: scaleY(5) translate(0.32rem, 50%); - cursor: ew-resize; - transform-origin: 100% 100%; - clip-path: inset(calc(100% - 0.75rem) 0 0 calc(100% - 0.75rem)); - transition: min-width 0.3s ease-out, max-width 0.3s ease-out; - } - } - .select { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - flex-shrink: 1; - appearance: none; - align-items: center; - gap: calc(0.25rem * 1.5); - background-color: var(--color-base-100); - padding-inline-start: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 7); - vertical-align: middle; - --size: calc(var(--size-field, 0.25rem) * var(--sl-size-mul)); - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - width: clamp(3rem, 20rem, 100%); - height: var(--size); - font-size: max(var(--font-size, 0rem), var(--font-size-min)); - touch-action: manipulation; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - background-image: linear-gradient(45deg, #0000 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, #0000 50%); - background-position: calc(100% - 20px) calc(1px + 50%), calc(100% - 16.1px) calc(1px + 50%); - background-size: 4px 4px, 4px 4px; - background-repeat: no-repeat; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - border: var(--border) solid var(--input-color, #0000); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - [dir="rtl"] & { - background-position: calc(0% + 12px) calc(1px + 50%), calc(0% + 16px) calc(1px + 50%); - &::picker(select), select::picker(select) { - translate: 0.5rem 0; - } - } - &[multiple] { - height: auto; - overflow: auto; - padding-block: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 3); - background-image: none; - } - select { - margin-inline-start: calc(0.25rem * -3); - margin-inline-end: calc(0.25rem * -7); - width: calc(100% + 2.75rem); - appearance: none; - padding-inline-start: calc(0.25rem * 3); - padding-inline-end: calc(0.25rem * 7); - height: calc(100% - calc(var(--border) * 2)); - align-items: center; - background: inherit; - border-radius: inherit; - border-style: none; - &::placeholder { - color: var(--color-base-content); - opacity: 50%; - } - &:focus, &:focus-within { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:not(:last-child) { - margin-inline-end: calc(0.25rem * -5.5); - background-image: none; - } - } - &:focus, &:focus-within, &:open { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - background-image: linear-gradient(135deg, #0000 50%, currentColor 50%), linear-gradient(45deg, currentColor 50%, #0000 50%); - } - &:has( > select[disabled]), &:is(:disabled, [disabled]), fieldset:disabled & { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - &:is(select), :is(select) { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 40%, transparent); - } - } - box-shadow: none; - &::placeholder, ::placeholder { - color: var(--color-base-content); - opacity: 20%; - } - } - &:has( > select[disabled]) > select[disabled] { - cursor: not-allowed; - } - &, & select { - @supports (appearance: base-select) { - &, &::picker(select) { - appearance: base-select; - } - } - &::picker(select) { - color: inherit; - max-height: min(24rem, 70dvh); - margin-inline: 0.5rem; - translate: -0.5rem 0; - border: var(--border) solid var(--color-base-200); - margin-block: calc(0.25rem * 2); - border-radius: var(--radius-box); - padding: calc(0.25rem * 2); - background-color: inherit; - box-shadow: 0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/0.2); - box-shadow: 0 20px 25px -5px rgb(0 0 0 / calc(var(--depth) * 0.1)), 0 8px 10px -6px rgb(0 0 0 / calc(var(--depth) * 0.1)); - } - &::picker-icon { - display: none; - } - selectedcontent { - width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - optgroup { - padding-top: 0.5em; - option { - &:nth-child(1) { - margin-top: 0.5em; - } - } - } - option { - border-radius: var(--radius-field); - padding-block: calc(0.25rem * 1.5); - padding-inline: calc(0.25rem * var(--option-px)); - transition-property: color, background-color; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - white-space: normal; - &:not(:disabled) { - &:hover, &:focus-visible { - cursor: pointer; - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:active { - background-color: var(--color-neutral); - color: var(--color-neutral-content); - box-shadow: 0 2px calc(var(--depth) * 3px) -2px var(--color-neutral); - } - } - } - } - } - @layer daisyui.l1.l2 { - --sl-size-mul: 10; - --font-size-min: 0.875rem; - --option-px: 3; - .floating-label:has(&) { - --top-mul: 5; - --font-size: 0.875rem; - } - } - } - .swap { - @layer daisyui.l1.l2 { - position: relative; - display: inline-grid; - cursor: pointer; - place-content: center; - vertical-align: middle; - webkit-user-select: none; - user-select: none; - input { - appearance: none; - border: none; - } - > * { - grid-column-start: 1; - grid-row-start: 1; - @media (prefers-reduced-motion: no-preference) { - transition-property: transform, rotate, opacity; - transition-duration: 0.2s; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - } - } - .swap-on, .swap-indeterminate, input:indeterminate ~ .swap-on { - opacity: 0%; - } - input:is(:checked, :indeterminate) { - & ~ .swap-off { - opacity: 0%; - } - } - input:checked ~ .swap-on, input:indeterminate ~ .swap-indeterminate { - opacity: 100%; - backface-visibility: visible; - } - } - } - .collapse-title { - @layer daisyui.l1.l2.l3 { - grid-column-start: 1; - grid-row-start: 1; - position: relative; - width: 100%; - padding: 1rem; - padding-inline-end: 3rem; - min-height: 1lh; - transition: background-color 0.2s ease-out; - } - } - .sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip-path: inset(50%); - white-space: nowrap; - border-width: 0; - } - .avatar { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - vertical-align: middle; - & > div { - display: block; - aspect-ratio: 1 / 1; - overflow: hidden; - } - img { - height: 100%; - width: 100%; - object-fit: cover; - } - } - } - .checkbox { - @layer daisyui.l1.l2.l3 { - border: var(--border) solid var(--input-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - border: var(--border) solid var(--input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000)); - } - position: relative; - display: inline-block; - flex-shrink: 0; - cursor: pointer; - appearance: none; - border-radius: var(--radius-selector); - padding: 0.25rem; - vertical-align: middle; - color: var(--color-base-content); - box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 0 #0000 inset, 0 0 #0000; - transition: background-color 0.2s, box-shadow 0.2s; - --size: calc(var(--size-selector, 0.25rem) * 6); - width: var(--size); - height: var(--size); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - &:before { - --tw-content: ""; - content: var(--tw-content); - display: block; - width: 100%; - height: 100%; - rotate: 45deg; - background-color: currentcolor; - opacity: 0%; - transition: clip-path 0.3s, opacity 0.1s, rotate 0.3s, translate 0.3s; - transition-delay: 0.1s; - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 70% 80%, 70% 100%); - box-shadow: 0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - font-size: 1rem; - line-height: 0.75; - } - &:focus-visible { - outline: 2px solid var(--input-color, currentColor); - outline-offset: 2px; - } - &:checked, &[aria-checked="true"] { - background-color: var(--input-color, #0000); - box-shadow: 0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); - &:before { - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 0%, 70% 0%, 70% 100%); - opacity: 100%; - } - @media (forced-colors: active) { - &:before { - rotate: 0deg; - background-color: transparent; - --tw-content: "✔︎"; - clip-path: none; - } - } - @media print { - &:before { - rotate: 0deg; - background-color: transparent; - --tw-content: "✔︎"; - clip-path: none; - } - } - } - &:indeterminate { - background-color: var(--input-color, var(--color-base-content) ); - @supports (color: color-mix(in lab, red, red)) { - background-color: var(--input-color, color-mix(in oklab, var(--color-base-content) 20%, #0000) ); - } - &:before { - rotate: 0deg; - opacity: 100%; - translate: 0 -35%; - clip-path: polygon(20% 100%, 20% 80%, 50% 80%, 50% 80%, 80% 80%, 80% 100%); - } - } - } - &:disabled { - @layer daisyui.l1.l2 { - cursor: not-allowed; - opacity: 20%; - } - } - } - .radio { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-block; - flex-shrink: 0; - cursor: pointer; - appearance: none; - border-radius: calc(infinity * 1px); - padding: 0.25rem; - vertical-align: middle; - border: var(--border) solid var(--input-color, currentColor); - @supports (color: color-mix(in lab, red, red)) { - border: var(--border) solid var(--input-color, color-mix(in srgb, currentColor 20%, #0000)); - } - box-shadow: 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset; - --size: calc(var(--size-selector, 0.25rem) * 6); - width: var(--size); - height: var(--size); - color: var(--input-color, currentColor); - &:before { - display: block; - width: 100%; - height: 100%; - border-radius: calc(infinity * 1px); - --tw-content: ""; - content: var(--tw-content); - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - } - &:focus-visible { - outline: 2px solid currentColor; - } - &:checked, &[aria-checked="true"] { - border-color: currentcolor; - background-color: var(--color-base-100); - @media (prefers-reduced-motion: no-preference) { - animation: radio 0.2s ease-out; - } - &:before { - background-color: currentcolor; - box-shadow: 0 -1px oklch(0% 0 0 / calc(var(--depth) * 0.1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * 0.1)); - } - @media (forced-colors: active) { - &:before { - outline-style: var(--tw-outline-style); - outline-width: 1px; - outline-offset: calc(1px * -1); - } - } - @media print { - &:before { - outline: 0.25rem solid; - outline-offset: -1rem; - } - } - } - } - &:disabled { - @layer daisyui.l1.l2 { - cursor: not-allowed; - opacity: 20%; - } - } - } - .rating { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-flex; - vertical-align: middle; - --size: var(--size-selector, 0.25rem) * 6; - input { - cursor: pointer; - appearance: none; - } - * { - border-radius: 0; - background-color: var(--color-base-content); - opacity: 20%; - width: calc(var(--size) * 1); - height: calc(var(--size)); - @media (prefers-reduced-motion: no-preference) { - animation: rating 0.25s ease-out; - } - } - .rating-hidden { - width: calc(0.25rem * 2); - background-color: transparent; - } - :checked, [aria-checked="true"], [aria-current="true"], :has( ~ :checked, ~ [aria-checked="true"], ~ [aria-current="true"]) { - opacity: 100%; - } - :focus-visible { - scale: 1.1; - @media (prefers-reduced-motion: no-preference) { - transition: scale 0.2s ease-out; - } - } - :active:focus { - animation: none; - scale: 1.1; - } - } - @layer daisyui.l1.l2 { - --size: var(--size-selector, 0.25rem) * 6; - } - } - .navbar { - @layer daisyui.l1.l2.l3 { - display: flex; - width: 100%; - align-items: center; - padding: 0.5rem; - min-height: 4rem; - } - :where(&) { - @layer daisyui.l1.l2 { - position: relative; - } - } - } - .card { - @layer daisyui.l1.l2.l3 { - position: relative; - display: flex; - flex-direction: column; - border-radius: var(--radius-box); - transition: outline 0.2s ease-in-out; - outline: 2px solid #0000; - outline-offset: 2px; - &:focus-visible, &[aria-checked="true"], &:has( > :checked, > :is([type="checkbox"], [type="radio"]):focus-visible) { - outline-color: currentColor; - } - &:has( > :checked:focus-visible), &[aria-checked="true"]:focus-visible, &[aria-checked="true"]:has( > :is([type="checkbox"], [type="radio"]):focus-visible) { - outline-width: 4px; - } - &:has( > :is([type="checkbox"], [type="radio"])) { - cursor: pointer; - user-select: none; - } - > :is([type="checkbox"], [type="radio"]) { - appearance: none; - } - } - @layer daisyui.l1.l2.l3.l4 { - figure:first-child { - overflow: hidden; - border-start-start-radius: inherit; - border-start-end-radius: inherit; - border-end-start-radius: unset; - border-end-end-radius: unset; - } - figure:last-child { - overflow: hidden; - border-start-start-radius: unset; - border-start-end-radius: unset; - border-end-start-radius: inherit; - border-end-end-radius: inherit; - } - figure { - display: flex; - align-items: center; - justify-content: center; - } - } - } - .stats { - @layer daisyui.l1.l2.l3 { - position: relative; - display: inline-grid; - grid-auto-flow: column; - overflow-x: auto; - border-radius: var(--radius-box); - } - } - .progress { - @layer daisyui.l1.l2.l3 { - position: relative; - height: calc(0.25rem * 2); - width: 100%; - appearance: none; - overflow: hidden; - border-radius: var(--radius-box); - background-color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, currentcolor 20%, transparent); - } - color: var(--color-base-content); - &:indeterminate { - background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); - background-size: 200%; - background-position-x: 15%; - @media (prefers-reduced-motion: no-preference) { - animation: progress 5s ease-in-out infinite; - } - @supports (-moz-appearance: none) { - &::-moz-progress-bar { - background-color: transparent; - @media (prefers-reduced-motion: no-preference) { - animation: progress 5s ease-in-out infinite; - background-image: repeating-linear-gradient( 90deg, currentColor -1%, currentColor 10%, #0000 10%, #0000 90% ); - background-size: 200%; - background-position-x: 15%; - } - } - } - } - @supports (-moz-appearance: none) { - &::-moz-progress-bar { - border-radius: var(--radius-box); - background-color: currentcolor; - @media (prefers-reduced-motion: no-preference) { - transition: inline-size 0.3s ease; - } - } - } - @supports (-webkit-appearance: none) { - &::-webkit-progress-bar { - border-radius: var(--radius-box); - background-color: transparent; - } - &::-webkit-progress-value { - border-radius: var(--radius-box); - background-color: currentColor; - @media (prefers-reduced-motion: no-preference) { - transition: inline-size 0.3s ease; - } - } - } - } - } - .absolute { - position: absolute; - } - .fixed { - position: fixed; - } - .relative { - position: relative; - } - .static { - position: static; - } - .sticky { - position: sticky; - } - .dropdown-right { - @layer daisyui.l1.l2 { - --anchor-h: right; - --anchor-v: span-bottom; - .dropdown-content { - inset-inline-start: 100%; - top: 0; - bottom: auto; - transform-origin: 0; - } - } - } - .dropdown-left { - @layer daisyui.l1.l2 { - --anchor-h: left; - --anchor-v: span-bottom; - .dropdown-content { - inset-inline-end: 100%; - top: 0; - bottom: auto; - transform-origin: 100%; - } - } - } - .dropdown-end { - @layer daisyui.l1.l2 { - --anchor-h: span-left; - :where(.dropdown-content) { - inset-inline-end: calc(0.25rem * 0); - translate: 0 0; - [dir="rtl"] & { - translate: 0 0; - } - } - &.dropdown-left { - --anchor-h: left; - --anchor-v: span-top; - .dropdown-content { - top: auto; - bottom: 0; - } - } - &.dropdown-right { - --anchor-h: right; - --anchor-v: span-top; - .dropdown-content { - top: auto; - bottom: 0; - } - } - } - } - .file-input { - @layer daisyui.l1.l2.l3 { - cursor: pointer; - cursor: pointer; - border: var(--border) solid #0000; - display: inline-flex; - appearance: none; - align-items: center; - background-color: var(--color-base-100); - vertical-align: middle; - webkit-user-select: none; - user-select: none; - width: clamp(3rem, 20rem, 100%); - height: var(--size); - padding-inline-end: 0.75rem; - font-size: 0.875rem; - line-height: 2; - border-start-start-radius: var(--join-ss, var(--radius-field)); - border-start-end-radius: var(--join-se, var(--radius-field)); - border-end-start-radius: var(--join-es, var(--radius-field)); - border-end-end-radius: var(--join-ee, var(--radius-field)); - border-color: var(--input-color); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - --size: calc(var(--size-field, 0.25rem) * 10); - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - &::file-selector-button { - margin-inline-end: calc(0.25rem * 4); - cursor: pointer; - padding-inline: calc(0.25rem * 4); - webkit-user-select: none; - user-select: none; - height: calc(100% + var(--border) * 2); - margin-block: calc(var(--border) * -1); - margin-inline-start: calc(var(--border) * -1); - font-size: 0.875rem; - color: var(--btn-fg); - border-width: var(--border); - border-style: solid; - border-color: var(--btn-border); - border-start-start-radius: calc(var(--join-ss, var(--radius-field) - var(--border))); - border-end-start-radius: calc(var(--join-es, var(--radius-field) - var(--border))); - font-weight: 600; - background-color: var(--btn-bg); - background-size: calc(var(--noise) * 100%); - background-image: var(--fx-noise); - text-shadow: 0 0.5px oklch(1 0 0 / calc(var(--depth) * 0.15)); - box-shadow: 0 0.5px 0 0.5px white inset, var(--btn-shadow); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 0.5px 0 0.5px color-mix( in oklab, color-mix(in oklab, white 30%, var(--btn-bg)) calc(var(--depth) * 20%), #0000 ) inset, var(--btn-shadow); - } - --size: calc(var(--size-field, 0.25rem) * 10); - --btn-bg: var(--btn-color, var(--color-base-200)); - --btn-fg: var(--color-base-content); - --btn-border: var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-border: color-mix(in oklab, var(--btn-bg), #000 5%); - } - --btn-shadow: 0 3px 2px -2px var(--btn-bg), - 0 4px 3px -2px var(--btn-bg); - @supports (color: color-mix(in lab, red, red)) { - --btn-shadow: 0 3px 2px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000), - 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000); - } - } - &:focus { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) 10%, #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - } - &:has( > input[disabled]), &:is(:disabled, [disabled]) { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - &::placeholder { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - } - box-shadow: none; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklch, var(--color-base-content) 20%, #0000); - } - &::file-selector-button { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - --btn-border: #0000; - background-image: none; - --btn-fg: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --btn-fg: color-mix(in oklch, var(--color-base-content) 20%, #0000); - } - } - } - } - } - .textarea { - @layer daisyui.l1.l2.l3 { - min-height: calc(0.25rem * 20); - flex-shrink: 1; - appearance: none; - border-radius: var(--radius-field); - background-color: var(--color-base-100); - padding-inline: calc(0.25rem * 3); - padding-block: calc(0.25rem * 2); - vertical-align: middle; - --input-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --input-color: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - width: clamp(3rem, 20rem, 100%); - font-size: max(var(--font-size, 0rem), var(--font-size-min)); - touch-action: manipulation; - border: var(--border) solid var(--input-color, #0000); - box-shadow: 0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * 0.1)) inset; - } - textarea { - appearance: none; - background-color: transparent; - border: none; - &::placeholder { - color: var(--color-base-content); - opacity: 50%; - } - &:focus, &:focus-within { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - } - &:focus, &:focus-within { - --input-color: var(--color-base-content); - box-shadow: 0 1px var(--input-color); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000); - } - outline: 2px solid var(--input-color); - outline-offset: 2px; - isolation: isolate; - } - @media (pointer: coarse) { - @supports (-webkit-touch-callout: none) { - &:focus, &:focus-within { - --font-size: 1rem; - } - } - } - &:has( > textarea[disabled]), &:is(:disabled, [disabled]) { - cursor: not-allowed; - border-color: var(--color-base-200); - background-color: var(--color-base-200); - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 40%, transparent); - } - box-shadow: none; - &::placeholder, ::placeholder { - color: var(--color-base-content); - opacity: 20%; - } - } - &:has( > textarea[disabled]) > textarea[disabled] { - cursor: not-allowed; - } - } - @layer daisyui.l1.l2 { - --font-size-min: 0.875rem; - .floating-label:has(&) { - --top-mul: 5; - --font-size: 0.875rem; - } - } - } - .stack { - @layer daisyui.l1.l2.l3 { - display: inline-grid; - grid-template-columns: 3px 4px 1fr 4px 3px; - grid-template-rows: 3px 4px 1fr 4px 3px; - & > * { - height: 100%; - width: 100%; - &:nth-child(n + 2) { - width: 100%; - opacity: 70%; - } - &:nth-child(2) { - z-index: 2; - opacity: 90%; - } - &:nth-child(1) { - z-index: 3; - width: 100%; - } - } - } - @layer daisyui.l1.l2 { - &, &.stack-bottom { - > * { - grid-column: 3 / 4; - grid-row: 3 / 6; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 1 / 6; - grid-row: 1 / 4; - } - } - } - &.stack-top { - > * { - grid-column: 3 / 4; - grid-row: 1 / 4; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 1 / 6; - grid-row: 3 / 6; - } - } - } - &.stack-start { - > * { - grid-column: 1 / 4; - grid-row: 3 / 4; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 3 / 6; - grid-row: 1 / 6; - } - } - } - &.stack-end { - > * { - grid-column: 3 / 6; - grid-row: 3 / 4; - &:nth-child(2) { - grid-column: 2 / 5; - grid-row: 2 / 5; - } - &:nth-child(1) { - grid-column: 1 / 4; - grid-row: 1 / 6; - } - } - } - } - } - .stack\! { - @layer daisyui.l1.l2.l3 { - display: inline-grid !important; - grid-template-columns: 3px 4px 1fr 4px 3px !important; - grid-template-rows: 3px 4px 1fr 4px 3px !important; - & > * { - height: 100% !important; - width: 100% !important; - &:nth-child(n + 2) { - width: 100% !important; - opacity: 70% !important; - } - &:nth-child(2) { - z-index: 2 !important; - opacity: 90% !important; - } - &:nth-child(1) { - z-index: 3 !important; - width: 100% !important; - } - } - } - @layer daisyui.l1.l2 { - &, &.stack-bottom { - > * { - grid-column: 3 / 4 !important; - grid-row: 3 / 6 !important; - &:nth-child(2) { - grid-column: 2 / 5 !important; - grid-row: 2 / 5 !important; - } - &:nth-child(1) { - grid-column: 1 / 6 !important; - grid-row: 1 / 4 !important; - } - } - } - &.stack-top { - > * { - grid-column: 3 / 4 !important; - grid-row: 1 / 4 !important; - &:nth-child(2) { - grid-column: 2 / 5 !important; - grid-row: 2 / 5 !important; - } - &:nth-child(1) { - grid-column: 1 / 6 !important; - grid-row: 3 / 6 !important; - } - } - } - &.stack-start { - > * { - grid-column: 1 / 4 !important; - grid-row: 3 / 4 !important; - &:nth-child(2) { - grid-column: 2 / 5 !important; - grid-row: 2 / 5 !important; - } - &:nth-child(1) { - grid-column: 3 / 6 !important; - grid-row: 1 / 6 !important; - } - } - } - &.stack-end { - > * { - grid-column: 3 / 6 !important; - grid-row: 3 / 4 !important; - &:nth-child(2) { - grid-column: 2 / 5 !important; - grid-row: 2 / 5 !important; - } - &:nth-child(1) { - grid-column: 1 / 4 !important; - grid-row: 1 / 6 !important; - } - } - } - } - } - .z-10 { - z-index: 10; - } - .tab-content { - @layer daisyui.l1.l2.l3 { - order: var(--tabcontent-order); - display: none; - border-color: transparent; - --tabcontent-radius-ss: var(--radius-box); - --tabcontent-radius-se: var(--radius-box); - --tabcontent-radius-es: var(--radius-box); - --tabcontent-radius-ee: var(--radius-box); - --tabcontent-order: 1; - width: 100%; - height: calc(100% - var(--tab-height) + var(--border)); - margin: var(--tabcontent-margin); - border-width: var(--border); - border-start-start-radius: var(--tabcontent-radius-ss); - border-start-end-radius: var(--tabcontent-radius-se); - border-end-start-radius: var(--tabcontent-radius-es); - border-end-end-radius: var(--tabcontent-radius-ee); - } - } - .hero { - @layer daisyui.l1.l2.l3 { - display: grid; - width: 100%; - place-items: center; - background-size: cover; - background-position: center; - & > * { - grid-column-start: 1; - grid-row-start: 1; - } - } - } - .stat-value { - @layer daisyui.l1.l2.l3 { - grid-column-start: 1; - white-space: nowrap; - font-size: 2rem; - font-weight: 800; - } - } - .stat-desc { - @layer daisyui.l1.l2.l3 { - grid-column-start: 1; - white-space: nowrap; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 60%, transparent); - } - font-size: 0.75rem; - } - } - .stat-title { - @layer daisyui.l1.l2.l3 { - grid-column-start: 1; - white-space: nowrap; - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 60%, transparent); - } - font-size: 0.75rem; - } - } - .container { - width: 100%; - @media (width >= 40rem) { - max-width: 40rem; - } - @media (width >= 48rem) { - max-width: 48rem; - } - @media (width >= 64rem) { - max-width: 64rem; - } - @media (width >= 80rem) { - max-width: 80rem; - } - @media (width >= 96rem) { - max-width: 96rem; - } - } - .divider { - @layer daisyui.l1.l2.l3 { - display: flex; - height: calc(0.25rem * 4); - flex-direction: row; - align-items: center; - align-self: stretch; - white-space: nowrap; - margin: var(--divider-m, 1rem 0); - --divider-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --divider-color: color-mix(in oklab, var(--color-base-content) 10%, transparent); - } - &:before, &:after { - content: ""; - height: calc(0.25rem * 0.5); - width: 100%; - flex-grow: 1; - background-color: var(--divider-color); - } - @media print { - &:before, &:after { - border: 0.5px solid; - } - } - &:not(:empty) { - gap: calc(0.25rem * 4); - } - } - } - .mx-auto { - margin-inline: auto; - } - .my-2 { - margin-block: calc(var(--spacing) * 2); - } - .my-3 { - margin-block: calc(var(--spacing) * 3); - } - .my-4 { - margin-block: calc(var(--spacing) * 4); - } - .my-6 { - margin-block: calc(var(--spacing) * 6); - } - .breadcrumbs { - @layer daisyui.l1.l2.l3 { - max-width: 100%; - overflow-x: auto; - padding-block: calc(0.25rem * 2); - > menu, > ul, > ol { - display: flex; - min-height: min-content; - align-items: center; - white-space: nowrap; - > li { - display: flex; - align-items: center; - > * { - display: flex; - cursor: pointer; - align-items: center; - gap: calc(0.25rem * 2); - &:hover { - @media (hover: hover) { - text-decoration-line: underline; - } - } - &:focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:focus-visible { - outline: 2px solid currentColor; - outline-offset: 2px; - } - } - & + *:before { - content: ""; - margin-inline-start: calc(0.25rem * 2); - margin-inline-end: calc(0.25rem * 3); - display: block; - height: calc(0.25rem * 1.5); - width: calc(0.25rem * 1.5); - opacity: 40%; - rotate: 45deg; - border-top: 1px solid; - border-right: 1px solid; - background-color: #0000; - } - [dir="rtl"] & + *:before { - rotate: -135deg; - } - } - } - } - } - .label { - @layer daisyui.l1.l2.l3 { - display: inline-flex; - align-items: center; - gap: calc(0.25rem * 1.5); - white-space: nowrap; - color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, currentcolor 60%, transparent); - } - &:has(input) { - cursor: pointer; - } - &:is(.input > *, .select > *) { - display: flex; - height: calc(100% - 0.5rem); - align-items: center; - padding-inline: calc(0.25rem * 3); - white-space: nowrap; - font-size: inherit; - &:first-child { - margin-inline-start: calc(0.25rem * -3); - margin-inline-end: calc(0.25rem * 3); - border-inline-end: var(--border) solid currentColor; - @supports (color: color-mix(in lab, red, red)) { - border-inline-end: var(--border) solid color-mix(in oklab, currentColor 10%, #0000); - } - } - &:last-child { - margin-inline-start: calc(0.25rem * 3); - margin-inline-end: calc(0.25rem * -3); - border-inline-start: var(--border) solid currentColor; - @supports (color: color-mix(in lab, red, red)) { - border-inline-start: var(--border) solid color-mix(in oklab, currentColor 10%, #0000); - } - } - } - } - } - .mt-1 { - margin-top: var(--spacing); - } - .mt-2 { - margin-top: calc(var(--spacing) * 2); - } - .mt-3 { - margin-top: calc(var(--spacing) * 3); - } - .mt-4 { - margin-top: calc(var(--spacing) * 4); - } - .mt-6 { - margin-top: calc(var(--spacing) * 6); - } - .mt-8 { - margin-top: calc(var(--spacing) * 8); - } - .mt-10 { - margin-top: calc(var(--spacing) * 10); - } - .mb-1 { - margin-bottom: var(--spacing); - } - .mb-2 { - margin-bottom: calc(var(--spacing) * 2); - } - .mb-4 { - margin-bottom: calc(var(--spacing) * 4); - } - .mb-6 { - margin-bottom: calc(var(--spacing) * 6); - } - .mb-10 { - margin-bottom: calc(var(--spacing) * 10); - } - .ml-1 { - margin-left: var(--spacing); - } - .ml-2 { - margin-left: calc(var(--spacing) * 2); - } - .status { - @layer daisyui.l1.l2.l3 { - display: inline-block; - aspect-ratio: 1 / 1; - width: calc(0.25rem * 2); - height: calc(0.25rem * 2); - border-radius: var(--radius-selector); - background-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - background-position: center; - background-repeat: no-repeat; - vertical-align: middle; - color: color-mix(in srgb, #000 30%, transparent); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-black) 30%, transparent); - } - background-image: radial-gradient( circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * 0.5)), #0000 ); - box-shadow: 0 2px 3px -1px currentColor; - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000); - } - } - } - .badge { - @layer daisyui.l1.l2.l3 { - display: inline-flex; - align-items: center; - justify-content: center; - gap: calc(0.25rem * 2); - border-radius: var(--radius-selector); - vertical-align: middle; - color: var(--badge-fg); - border: var(--border) solid var(--badge-color, var(--color-base-200)); - font-size: 0.875rem; - width: fit-content; - background-size: auto, calc(var(--noise) * 100%); - background-image: none, var(--fx-noise); - background-color: var(--badge-bg); - --badge-bg: var(--badge-color, var(--color-base-100)); - --badge-fg: var(--color-base-content); - --size: calc(var(--size-selector, 0.25rem) * 6); - height: var(--size); - padding-inline: calc(var(--size) / 2 - var(--border)); - } - } - .tabs { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-wrap: wrap; - --tabs-height: auto; - --tabs-direction: row; - --tab-height: calc(var(--size-field, 0.25rem) * 10); - height: var(--tabs-height); - flex-direction: var(--tabs-direction); - } - } - .footer { - @layer daisyui.l1.l2.l3 { - display: grid; - width: 100%; - grid-auto-flow: row; - place-items: start; - column-gap: calc(0.25rem * 4); - row-gap: calc(0.25rem * 10); - font-size: 0.875rem; - line-height: 1.25rem; - & > *:not(script, style, template) { - display: grid; - place-items: start; - gap: calc(0.25rem * 2); - } - &.footer-center { - grid-auto-flow: column dense; - place-items: center; - text-align: center; - & > *:not(script, style, template) { - place-items: center; - } - } - } - } - .stat { - @layer daisyui.l1.l2.l3 { - display: inline-grid; - width: 100%; - column-gap: calc(0.25rem * 4); - padding-inline: calc(0.25rem * 6); - padding-block: calc(0.25rem * 4); - grid-template-columns: repeat(1, 1fr); - &:not(:last-child) { - border-inline-end: var(--border) dashed currentColor; - @supports (color: color-mix(in lab, red, red)) { - border-inline-end: var(--border) dashed color-mix(in oklab, currentColor 10%, #0000); - } - border-block-end: none; - } - } - } - .card-body { - @layer daisyui.l1.l2.l3 { - display: flex; - flex: auto; - flex-direction: column; - gap: calc(0.25rem * 2); - padding: var(--card-p, 1.5rem); - font-size: var(--card-fs, 0.875rem); - } - @layer daisyui.l1.l2.l3.l4 { - p { - flex-grow: 1; - } - } - } - .alert { - border-width: var(--border); - border-color: var(--alert-border-color, var(--color-base-200)); - @layer daisyui.l1.l2.l3 { - border-style: solid; - --alert-border-color: var(--color-base-200); - display: grid; - align-items: center; - gap: calc(0.25rem * 4); - border-radius: var(--radius-box); - padding-inline: calc(0.25rem * 4); - padding-block: calc(0.25rem * 3); - color: var(--color-base-content); - background-color: var(--alert-color, var(--color-base-200)); - justify-content: start; - justify-items: start; - grid-auto-flow: column; - grid-template-columns: auto; - text-align: start; - font-size: 0.875rem; - line-height: 1.25rem; - background-size: auto, calc(var(--noise) * 33%); - background-image: none, var(--fx-noise); - box-shadow: 0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * 0.08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * 0.08)); - @supports (color: color-mix(in lab, red, red)) { - box-shadow: 0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * 0.08)) inset, 0 1px color-mix( in oklab, color-mix(in oklab, #000 20%, var(--alert-color, var(--color-base-200))) calc(var(--depth) * 20%), #0000 ), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * 0.08)); - } - &:has(:nth-child(2)) { - grid-template-columns: auto minmax(auto, 1fr); - } - } - } - .fieldset { - @layer daisyui.l1.l2.l3 { - display: grid; - gap: calc(0.25rem * 1.5); - padding-block: 0.25rem; - font-size: 0.75rem; - grid-template-columns: 1fr; - grid-auto-rows: max-content; - } - } - .card-actions { - @layer daisyui.l1.l2.l3 { - display: flex; - flex-wrap: wrap; - align-items: flex-start; - gap: calc(0.25rem * 2); - } - } - .avatar-placeholder { - @layer daisyui.l1.l2 { - & > div { - display: flex; - align-items: center; - justify-content: center; - } - } - } - .card-title { - @layer daisyui.l1.l2.l3 { - display: flex; - align-items: center; - gap: calc(0.25rem * 2); - font-size: var(--cardtitle-fs, 1.125rem); - font-weight: 600; - } - } - .prose { - :root & { - --tw-prose-body: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-body: color-mix(in oklab, var(--color-base-content) 80%, #0000); - } - --tw-prose-headings: var(--color-base-content); - --tw-prose-lead: var(--color-base-content); - --tw-prose-links: var(--color-base-content); - --tw-prose-bold: var(--color-base-content); - --tw-prose-counters: var(--color-base-content); - --tw-prose-bullets: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-bullets: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-hr: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-hr: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-quotes: var(--color-base-content); - --tw-prose-quote-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-quote-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-captions: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-captions: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-code: var(--color-base-content); - --tw-prose-pre-code: var(--color-neutral-content); - --tw-prose-pre-bg: var(--color-neutral); - --tw-prose-th-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-th-borders: color-mix(in oklab, var(--color-base-content) 50%, #0000); - } - --tw-prose-td-borders: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-td-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); - } - --tw-prose-kbd: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - --tw-prose-kbd: color-mix(in oklab, var(--color-base-content) 80%, #0000); - } - :where(code):not(pre > code) { - background-color: var(--color-base-200); - border-radius: var(--radius-selector); - border: var(--border) solid var(--color-base-300); - padding-inline: 0.5em; - padding-block: 0.2em; - font-weight: inherit; - &:before, &:after { - display: none; - } - } - } - } - .\!hidden { - display: none !important; - } - .block { - display: block; - } - .contents { - display: contents; - } - .flex { - display: flex; - } - .grid { - display: grid; - } - .hidden { - display: none; - } - .inline { - display: inline; - } - .inline-block { - display: inline-block; - } - .inline-flex { - display: inline-flex; - } - .inline-grid { - display: inline-grid; - } - .table { - display: table; - } - .table-caption { - display: table-caption; - } - .table-cell { - display: table-cell; - } - .h-16 { - height: calc(var(--spacing) * 16); - } - .h-56 { - height: calc(var(--spacing) * 56); - } - .min-h-screen { - min-height: 100vh; - } - .w-16 { - width: calc(var(--spacing) * 16); - } - .w-24 { - width: calc(var(--spacing) * 24); - } - .w-60 { - width: calc(var(--spacing) * 60); - } - .w-fit { - width: fit-content; - } - .w-full { - width: 100%; - } - .max-w-none { - max-width: none; - } - .max-w-xl { - max-width: var(--container-xl); - } - .max-w-xs { - max-width: var(--container-xs); - } - .flex-1 { - flex: 1; - } - .flex-shrink { - flex-shrink: 1; - } - .flex-grow { - flex-grow: 1; - } - .grow { - flex-grow: 1; - } - .border-collapse { - border-collapse: collapse; - } - .transform { - transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); - } - .skeleton { - @layer daisyui.l1.l2.l3 { - border-radius: var(--radius-box); - background-color: var(--color-base-300); - @media (prefers-reduced-motion: reduce) { - transition-duration: 15s; - } - will-change: background-position; - background-image: linear-gradient( 105deg, #0000 0% 40%, var(--color-base-100) 50%, #0000 60% 100% ); - background-size: 200% auto; - background-position-x: -50%; - @media (prefers-reduced-motion: no-preference) { - animation: skeleton 1.8s ease-in-out infinite; - } - } - } - .aura-glow { - @layer daisyui.l1.l2 { - animation: none; - background-image: radial-gradient(closest-corner at center, currentColor 0%, transparent 90%); - &:before { - animation: aura-glow var(--tw-duration, 6s) ease-out infinite; - } - &:after { - animation: aura-glow-after var(--tw-duration, 6s) ease-out infinite; - } - } - } - .link { - @layer daisyui.l1.l2.l3 { - cursor: pointer; - text-decoration-line: underline; - &:focus { - --tw-outline-style: none; - outline-style: none; - @media (forced-colors: active) { - outline: 2px solid transparent; - outline-offset: 2px; - } - } - &:focus-visible { - outline: 2px solid currentColor; - outline-offset: 2px; - } - } - } - .cursor-pointer { - cursor: pointer; - } - .resize { - resize: both; - } - .flex-col { - flex-direction: column; - } - .flex-row { - flex-direction: row; - } - .flex-wrap { - flex-wrap: wrap; - } - .items-center { - align-items: center; - } - .items-start { - align-items: flex-start; - } - .justify-between { - justify-content: space-between; - } - .justify-center { - justify-content: center; - } - .justify-end { - justify-content: flex-end; - } - .justify-start { - justify-content: flex-start; - } - .gap-1 { - gap: var(--spacing); - } - .gap-2 { - gap: calc(var(--spacing) * 2); - } - .gap-3 { - gap: calc(var(--spacing) * 3); - } - .gap-4 { - gap: calc(var(--spacing) * 4); - } - .space-y-2 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse))); - } - } - .space-y-3 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse))); - } - } - .divide-y { - :where(& > :not(:last-child)) { - --tw-divide-y-reverse: 0; - border-bottom-style: var(--tw-border-style); - border-top-style: var(--tw-border-style); - border-top-width: calc(1px * var(--tw-divide-y-reverse)); - border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - } - } - .divide-base-200 { - :where(& > :not(:last-child)) { - border-color: var(--color-base-200); - } - } - .truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .overflow-x-auto { - overflow-x: auto; - } - .rounded { - border-radius: 0.25rem; - } - .rounded-box { - border-radius: var(--radius-box); - } - .rounded-box { - border-radius: var(--radius-box); - } - .rounded-full { - border-radius: calc(infinity * 1px); - } - .border { - border-style: var(--tw-border-style); - border-width: 1px; - } - .border-2 { - border-style: var(--tw-border-style); - border-width: 2px; - } - .border-b { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - } - .border-l-4 { - border-left-style: var(--tw-border-style); - border-left-width: 4px; - } - .badge-ghost { - @layer daisyui.l1.l2 { - border-color: var(--color-base-200); - background-color: var(--color-base-200); - color: var(--color-base-content); - background-image: none; - } - } - .border-base-300 { - border-color: var(--color-base-300); - } - .border-base-content { - border-color: var(--color-base-content); - } - .border-base-content\/20 { - border-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-base-content) 20%, transparent); - } - } - .border-base-content\/70 { - border-color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-base-content) 70%, transparent); - } - } - .border-error { - border-color: var(--color-error); - } - .border-warning { - border-color: var(--color-warning); - } - .border-white { - border-color: var(--color-white); - } - .bg-base-100 { - background-color: var(--color-base-100); - } - .bg-base-200 { - background-color: var(--color-base-200); - } - .bg-primary { - background-color: var(--color-primary); - } - .bg-white { - background-color: var(--color-white); - } - .btn-link { - @layer daisyui { - text-decoration-line: underline; - --btn-bg: #0000; - color: var(--btn-color, var(--color-primary)); - --btn-border: #0000; - background-image: none; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - } - } - .alert-soft { - @layer daisyui.l1 { - color: var(--alert-color, var(--color-base-content)); - background: var(--alert-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - background: color-mix( in oklab, var(--alert-color, var(--color-base-content)) 8%, var(--color-base-100) ); - } - --alert-border-color: var(--alert-color, var(--color-base-content)); - @supports (color: color-mix(in lab, red, red)) { - --alert-border-color: color-mix( - in oklab, - var(--alert-color, var(--color-base-content)) 10%, - var(--color-base-100) - ); - } - box-shadow: none; - background-image: none; - } - } - .btn-outline { - @layer daisyui.l1.l2.l3 { - --btn-bg: #0000; - color: var(--btn-rest-fg, var(--btn-color, var(--color-base-content))); - --btn-border: var(--btn-color, var(--color-base-content)); - --btn-border-style: solid; - background-image: none; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - } - } - .btn-ghost { - @layer daisyui.l1.l2.l3 { - --btn-bg: #0000; - color: var(--btn-rest-fg, var(--btn-color, var(--color-base-content, currentColor))); - --btn-border: #0000; - background-image: none; - --btn-inset: 0 0 0 0 oklch(0% 0 0/0); - --btn-shadow: 0 0 0 0 oklch(0% 0 0/0); - } - } - .mask-repeat { - mask-repeat: repeat; - } - .object-contain { - object-fit: contain; - } - .p-2 { - padding: calc(var(--spacing) * 2); - } - .p-3 { - padding: calc(var(--spacing) * 3); - } - .p-4 { - padding: calc(var(--spacing) * 4); - } - .table-sm { - @layer daisyui.l1.l2 { - :not(thead, tfoot) tr { - font-size: 0.75rem; - } - :where(th, td) { - padding-inline: calc(0.25rem * 3); - padding-block: calc(0.25rem * 2); - } - } - } - .px-4 { - padding-inline: calc(var(--spacing) * 4); - } - .px-6 { - padding-inline: calc(var(--spacing) * 6); - } - .px-8 { - padding-inline: calc(var(--spacing) * 8); - } - .py-2 { - padding-block: calc(var(--spacing) * 2); - } - .py-4 { - padding-block: calc(var(--spacing) * 4); - } - .pt-2 { - padding-top: calc(var(--spacing) * 2); - } - .pt-3 { - padding-top: calc(var(--spacing) * 3); - } - .pb-2 { - padding-bottom: calc(var(--spacing) * 2); - } - .pl-8 { - padding-left: calc(var(--spacing) * 8); - } - .text-center { - text-align: center; - } - .text-right { - text-align: right; - } - .font-mono { - font-family: var(--font-mono); - } - .font-roboto { - font-family: var(--font-roboto); - } - .text-2xl { - font-size: var(--text-2xl); - line-height: var(--tw-leading, var(--text-2xl--line-height)); - } - .text-3xl { - font-size: var(--text-3xl); - line-height: var(--tw-leading, var(--text-3xl--line-height)); - } - .text-4xl { - font-size: var(--text-4xl); - line-height: var(--tw-leading, var(--text-4xl--line-height)); - } - .text-base { - font-size: var(--text-base); - line-height: var(--tw-leading, var(--text-base--line-height)); - } - .text-lg { - font-size: var(--text-lg); - line-height: var(--tw-leading, var(--text-lg--line-height)); - } - .text-sm { - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - } - .text-xl { - font-size: var(--text-xl); - line-height: var(--tw-leading, var(--text-xl--line-height)); - } - .text-xs { - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - } - .badge-sm { - @layer daisyui.l1.l2 { - --size: calc(var(--size-selector, 0.25rem) * 5); - font-size: 0.75rem; - } - } - .badge-xs { - @layer daisyui.l1.l2 { - --size: calc(var(--size-selector, 0.25rem) * 4); - font-size: 0.625rem; - } - } - .otp-lg { - @layer daisyui.l1.l2 { - font-size: 2rem; - --otp-size: calc(var(--size-field, 0.25rem) * 12); - } - } - .font-bold { - --tw-font-weight: var(--font-weight-bold); - font-weight: var(--font-weight-bold); - } - .font-medium { - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - } - .font-semibold { - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - } - .tracking-wider { - --tw-tracking: var(--tracking-wider); - letter-spacing: var(--tracking-wider); - } - .alert-error { - @layer daisyui.l1.l2 { - color: var(--color-error-content); - --alert-border-color: var(--color-error); - --alert-color: var(--color-error); - } - } - .alert-info { - @layer daisyui.l1.l2 { - color: var(--color-info-content); - --alert-border-color: var(--color-info); - --alert-color: var(--color-info); - } - } - .alert-success { - @layer daisyui.l1.l2 { - color: var(--color-success-content); - --alert-border-color: var(--color-success); - --alert-color: var(--color-success); - } - } - .alert-warning { - @layer daisyui.l1.l2 { - color: var(--color-warning-content); - --alert-border-color: var(--color-warning); - --alert-color: var(--color-warning); - } - } - .progress-primary { - @layer daisyui.l1.l2 { - color: var(--color-primary); - } - } - .text-base-content { - color: var(--color-base-content); - } - .text-base-content\/50 { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 50%, transparent); - } - } - .text-base-content\/70 { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 70%, transparent); - } - } - .text-base-content\/80 { - color: var(--color-base-content); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-base-content) 80%, transparent); - } - } - .text-error { - color: var(--color-error); - } - .text-primary-content { - color: var(--color-primary-content); - } - .text-sky-500 { - color: var(--color-sky-500); - } - .text-warning { - color: var(--color-warning); - } - .lowercase { - text-transform: lowercase; - } - .uppercase { - text-transform: uppercase; - } - .tabular-nums { - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,); - } - .link-hover { - @layer daisyui.l1.l2 { - text-decoration-line: none; - &:hover { - @media (hover: hover) { - text-decoration-line: underline; - } - } - } - } - .prose { - & :where(a.btn:not(.btn-link)):not(:where([class~="not-prose"], [class~="not-prose"] *)) { - text-decoration-line: none; - } - } - .underline { - text-decoration-line: underline; - } - .opacity-50 { - opacity: 50%; - } - .opacity-60 { - opacity: 60%; - } - .opacity-70 { - opacity: 70%; - } - .opacity-80 { - opacity: 80%; - } - .shadow { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .shadow-sm { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .shadow-xl { - --tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .outline { - outline-style: var(--tw-outline-style); - outline-width: 1px; - } - .blur { - --tw-blur: blur(8px); - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); - } - .invert { - --tw-invert: invert(100%); - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); - } - .\!filter { - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,) !important; - } - .filter { - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); - } - .transition { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .ease-in-out { - --tw-ease: var(--ease-in-out); - transition-timing-function: var(--ease-in-out); - } - .ease-out { - --tw-ease: var(--ease-out); - transition-timing-function: var(--ease-out); - } - .input-lg { - @layer daisyui.l1.l2 { - --in-size-mul: 12; - --font-size-min: 1.125rem; - --spin-my: -3; - .floating-label:has(&) { - --top-mul: 6; - --font-size: 1.125rem; - } - } - } - .btn-neutral { - @layer daisyui.l1.l2 { - --btn-color: var(--color-neutral); - --btn-fg: var(--color-neutral-content); - --btn-soft-bg: var(--color-neutral-content) 80%; - --btn-rest-fg: initial; - } - } - .btn-accent { - @layer daisyui.l1.l2 { - --btn-color: var(--color-accent); - --btn-fg: var(--color-accent-content); - --btn-soft-bg: initial; - } - } - .btn-error { - @layer daisyui.l1.l2 { - --btn-color: var(--color-error); - --btn-fg: var(--color-error-content); - --btn-soft-bg: initial; - } - } - .btn-primary { - @layer daisyui.l1.l2 { - --btn-color: var(--color-primary); - --btn-fg: var(--color-primary-content); - --btn-soft-bg: initial; - } - } - .btn-sm { - @layer daisyui.l1.l2 { - --fontsize: 0.75rem; - --btn-p: 0.75rem; - --size: calc(var(--size-field, 0.25rem) * 8); - } - } - .btn-success { - @layer daisyui.l1.l2 { - --btn-color: var(--color-success); - --btn-fg: var(--color-success-content); - --btn-soft-bg: initial; - } - } - .btn-warning { - @layer daisyui.l1.l2 { - --btn-color: var(--color-warning); - --btn-fg: var(--color-warning-content); - --btn-soft-bg: initial; - } - } - .btn-xs { - @layer daisyui.l1.l2 { - --fontsize: 0.6875rem; - --btn-p: 0.5rem; - --size: calc(var(--size-field, 0.25rem) * 6); - } - } - .badge-error { - @layer daisyui.l1.l2 { - --badge-color: var(--color-error); - --badge-fg: var(--color-error-content); - } - } - .badge-info { - @layer daisyui.l1.l2 { - --badge-color: var(--color-info); - --badge-fg: var(--color-info-content); - } - } - .badge-neutral { - @layer daisyui.l1.l2 { - --badge-color: var(--color-neutral); - --badge-fg: var(--color-neutral-content); - } - } - .badge-success { - @layer daisyui.l1.l2 { - --badge-color: var(--color-success); - --badge-fg: var(--color-success-content); - } - } - .badge-warning { - @layer daisyui.l1.l2 { - --badge-color: var(--color-warning); - --badge-fg: var(--color-warning-content); - } - } - .input-error { - @layer daisyui.l1.l2 { - &, &:focus, &:focus-within { - --input-color: var(--color-error); - } - } - } - .sm\:grid-cols-2 { - @media (width >= 40rem) { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } - .md\:grid-cols-2 { - @media (width >= 48rem) { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } - .lg\:grid-cols-2 { - @media (width >= 64rem) { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } - .lg\:grid-cols-3 { - @media (width >= 64rem) { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - } - .lg\:grid-cols-4 { - @media (width >= 64rem) { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - } -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url("../fonts/ubuntu-latin-400-normal.woff2") format("woff2"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url("../fonts/ubuntu-latin-ext-400-normal.woff2") format("woff2"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "Ubuntu"; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url("../fonts/ubuntu-latin-400-italic.woff2") format("woff2"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Ubuntu"; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url("../fonts/ubuntu-latin-ext-400-italic.woff2") format("woff2"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url("../fonts/ubuntu-latin-500-normal.woff2") format("woff2"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url("../fonts/ubuntu-latin-ext-500-normal.woff2") format("woff2"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url("../fonts/ubuntu-latin-700-normal.woff2") format("woff2"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Ubuntu"; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url("../fonts/ubuntu-latin-ext-700-normal.woff2") format("woff2"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "JetBrains Mono"; - font-style: normal; - font-weight: 100 800; - font-display: swap; - src: url("../fonts/jetbrains-mono-latin-wght-normal.woff2") format("woff2-variations"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "JetBrains Mono"; - font-style: normal; - font-weight: 100 800; - font-display: swap; - src: url("../fonts/jetbrains-mono-latin-ext-wght-normal.woff2") format("woff2-variations"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "Roboto"; - font-style: normal; - font-weight: 100 900; - font-display: swap; - src: url("../fonts/roboto-latin-standard-normal.woff2") format("woff2-variations"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Roboto"; - font-style: normal; - font-weight: 100 900; - font-display: swap; - src: url("../fonts/roboto-latin-ext-standard-normal.woff2") format("woff2-variations"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -@font-face { - font-family: "Tourney"; - font-style: normal; - font-weight: 100 900; - font-stretch: 75% 125%; - font-display: swap; - src: url("../fonts/tourney-latin-standard-normal.woff2") format("woff2-variations"); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} -@font-face { - font-family: "Tourney"; - font-style: normal; - font-weight: 100 900; - font-stretch: 75% 125%; - font-display: swap; - src: url("../fonts/tourney-latin-ext-standard-normal.woff2") format("woff2-variations"); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; -} -.logo { - background-image: var(--logo-dark); - background-position: center; - background-repeat: no-repeat; - background-size: contain; -} -@media (prefers-color-scheme: dark) { - .logo { - background-image: var(--logo-light); - } -} -[data-theme="light"] .logo { - background-image: var(--logo-dark); -} -[data-theme="dark"] .logo { - background-image: var(--logo-light); -} -@layer base { - :where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] { - color-scheme: light; - --color-base-100: oklch(100% 0 0); - --color-base-200: oklch(98% 0 0); - --color-base-300: oklch(95% 0 0); - --color-base-content: oklch(21% 0.006 285.885); - --color-primary: oklch(45% 0.24 277.023); - --color-primary-content: oklch(93% 0.034 272.788); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } -} -@layer base { - @media (prefers-color-scheme: dark) { - :root:not([data-theme]) { - color-scheme: dark; - --color-base-100: oklch(25.33% 0.016 252.42); - --color-base-200: oklch(23.26% 0.014 253.1); - --color-base-300: oklch(21.15% 0.012 254.09); - --color-base-content: oklch(97.807% 0.029 256.847); - --color-primary: oklch(58% 0.233 277.117); - --color-primary-content: oklch(96% 0.018 272.314); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } - } -} -@layer base { - :root:has(input.theme-controller[value=light]:checked),[data-theme=light] { - color-scheme: light; - --color-base-100: oklch(100% 0 0); - --color-base-200: oklch(98% 0 0); - --color-base-300: oklch(95% 0 0); - --color-base-content: oklch(21% 0.006 285.885); - --color-primary: oklch(45% 0.24 277.023); - --color-primary-content: oklch(93% 0.034 272.788); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } -} -@layer base { - :root:has(input.theme-controller[value=dark]:checked),[data-theme=dark] { - color-scheme: dark; - --color-base-100: oklch(25.33% 0.016 252.42); - --color-base-200: oklch(23.26% 0.014 253.1); - --color-base-300: oklch(21.15% 0.012 254.09); - --color-base-content: oklch(97.807% 0.029 256.847); - --color-primary: oklch(58% 0.233 277.117); - --color-primary-content: oklch(96% 0.018 272.314); - --color-secondary: oklch(65% 0.241 354.308); - --color-secondary-content: oklch(94% 0.028 342.258); - --color-accent: oklch(77% 0.152 181.912); - --color-accent-content: oklch(38% 0.063 188.416); - --color-neutral: oklch(14% 0.005 285.823); - --color-neutral-content: oklch(92% 0.004 286.32); - --color-info: oklch(74% 0.16 232.661); - --color-info-content: oklch(29% 0.066 243.157); - --color-success: oklch(76% 0.177 163.223); - --color-success-content: oklch(37% 0.077 168.94); - --color-warning: oklch(82% 0.189 84.429); - --color-warning-content: oklch(41% 0.112 45.904); - --color-error: oklch(71% 0.194 13.428); - --color-error-content: oklch(27% 0.105 12.094); - --radius-selector: 0.5rem; - --radius-field: 0.25rem; - --radius-box: 0.5rem; - --size-selector: 0.25rem; - --size-field: 0.25rem; - --border: 1px; - --depth: 1; - --noise: 0; - } -} -@layer base { - @property --radialprogress { - syntax: ""; - inherits: true; - initial-value: 0%; - } - @property --aura-angle { - syntax: ""; - inherits: false; - initial-value: 0deg; - } -} -@layer base { - :root { - scrollbar-color: currentColor #0000; - @supports (color: color-mix(in lab, red, red)) { - scrollbar-color: color-mix(in oklch, currentColor 35%, #0000) #0000; - } - } -} -@layer base { - :root { - --fx-noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E"); - } -} -@layer base { - :root { - --page-scroll-lock: initial; - --page-overflow: var(--page-scroll-lock) hidden; - } - :root:not(span) { - overflow: var(--page-overflow); - } -} -@layer base { - :root { - --page-has-backdrop: var(--page-scroll-lock) 1; - --page-scroll-bg: var(--page-scroll-lock) - var(--root-bg, #0000); - @supports (color: color-mix(in lab, red, red)) { - --page-scroll-bg: var(--page-scroll-lock) - color-mix(in srgb, var(--root-bg, #0000), oklch(0% 0 0) calc(var(--page-has-backdrop, 0) * 40%)); - } - background-image: var(--page-scroll-lock) linear-gradient(var(--root-bg, #0000), var(--root-bg, #0000)); - transition: var(--page-scroll-lock) background-color 0.3s ease-out; - animation: var(--page-scroll-lock) set-page-has-scroll forwards; - animation-timeline: var(--page-scroll-lock) scroll(); - --page-has-scroll: initial; - scrollbar-gutter: var(--page-has-scroll) var(--page-scroll-lock) stable; - } - @keyframes set-page-has-scroll { - 0%, to { - --page-has-scroll: ; - } - } -} -@layer base { - :root, [data-theme] { - background-color: var(--root-bg); - color: var(--color-base-content); - } - :root { - background-color: var(--page-scroll-bg, var(--root-bg)); - } - :where(:root, [data-theme]) { - --root-bg: var(--color-base-100); - } -} -@keyframes dropdown { - 0% { - opacity: 0; - } -} -@keyframes rotator { - 89.9999%, 100% { - --first-item-position: 0 0%; - } - 90%, 99.9999% { - --first-item-position: 0 calc(var(--items) * 100%); - } - 100% { - translate: 0 -100%; - } -} -@keyframes radio { - 0% { - padding: 5px; - } - 50% { - padding: 3px; - } -} -@keyframes toast { - 0% { - scale: 0.9; - opacity: 0; - } - 100% { - scale: 1; - opacity: 1; - } -} -@keyframes skeleton { - 0% { - background-position: 150%; - } - 100% { - background-position: -50%; - } -} -@keyframes progress { - 50% { - background-position-x: -115%; - } -} -@keyframes rating { - 0%, 40% { - scale: 1.1; - filter: brightness(1.05) contrast(1.05); - } -} -@keyframes aura { - to { - --aura-angle: 360deg; - transform: translateZ(1px); - } -} -@keyframes aura-glow { - 20%, 80% { - opacity: 0.7; - filter: blur(0.25rem); - } - 50% { - opacity: 1; - filter: blur(0.75rem); - } -} -@keyframes aura-glow-after { - 20%, 80% { - opacity: 0.3; - filter: blur(1rem); - } - 50% { - opacity: 0.6; - filter: blur(1.5rem); - } -} -@keyframes menu { - 0% { - opacity: 0; - } -} -@property --tw-rotate-x { - syntax: "*"; - inherits: false; -} -@property --tw-rotate-y { - syntax: "*"; - inherits: false; -} -@property --tw-rotate-z { - syntax: "*"; - inherits: false; -} -@property --tw-skew-x { - syntax: "*"; - inherits: false; -} -@property --tw-skew-y { - syntax: "*"; - inherits: false; -} -@property --tw-space-y-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-divide-y-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-border-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} -@property --tw-font-weight { - syntax: "*"; - inherits: false; -} -@property --tw-tracking { - syntax: "*"; - inherits: false; -} -@property --tw-ordinal { - syntax: "*"; - inherits: false; -} -@property --tw-slashed-zero { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-figure { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-spacing { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-fraction { - syntax: "*"; - inherits: false; -} -@property --tw-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-inset-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-inset-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-inset-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-ring-color { - syntax: "*"; - inherits: false; -} -@property --tw-ring-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-inset-ring-color { - syntax: "*"; - inherits: false; -} -@property --tw-inset-ring-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-ring-inset { - syntax: "*"; - inherits: false; -} -@property --tw-ring-offset-width { - syntax: ""; - inherits: false; - initial-value: 0px; -} -@property --tw-ring-offset-color { - syntax: "*"; - inherits: false; - initial-value: #fff; -} -@property --tw-ring-offset-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-outline-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} -@property --tw-blur { - syntax: "*"; - inherits: false; -} -@property --tw-brightness { - syntax: "*"; - inherits: false; -} -@property --tw-contrast { - syntax: "*"; - inherits: false; -} -@property --tw-grayscale { - syntax: "*"; - inherits: false; -} -@property --tw-hue-rotate { - syntax: "*"; - inherits: false; -} -@property --tw-invert { - syntax: "*"; - inherits: false; -} -@property --tw-opacity { - syntax: "*"; - inherits: false; -} -@property --tw-saturate { - syntax: "*"; - inherits: false; -} -@property --tw-sepia { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-drop-shadow-size { - syntax: "*"; - inherits: false; -} -@property --tw-ease { - syntax: "*"; - inherits: false; -} -@layer properties { - @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { - *, ::before, ::after, ::backdrop { - --tw-rotate-x: initial; - --tw-rotate-y: initial; - --tw-rotate-z: initial; - --tw-skew-x: initial; - --tw-skew-y: initial; - --tw-space-y-reverse: 0; - --tw-divide-y-reverse: 0; - --tw-border-style: solid; - --tw-font-weight: initial; - --tw-tracking: initial; - --tw-ordinal: initial; - --tw-slashed-zero: initial; - --tw-numeric-figure: initial; - --tw-numeric-spacing: initial; - --tw-numeric-fraction: initial; - --tw-shadow: 0 0 #0000; - --tw-shadow-color: initial; - --tw-shadow-alpha: 100%; - --tw-inset-shadow: 0 0 #0000; - --tw-inset-shadow-color: initial; - --tw-inset-shadow-alpha: 100%; - --tw-ring-color: initial; - --tw-ring-shadow: 0 0 #0000; - --tw-inset-ring-color: initial; - --tw-inset-ring-shadow: 0 0 #0000; - --tw-ring-inset: initial; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-offset-shadow: 0 0 #0000; - --tw-outline-style: solid; - --tw-blur: initial; - --tw-brightness: initial; - --tw-contrast: initial; - --tw-grayscale: initial; - --tw-hue-rotate: initial; - --tw-invert: initial; - --tw-opacity: initial; - --tw-saturate: initial; - --tw-sepia: initial; - --tw-drop-shadow: initial; - --tw-drop-shadow-color: initial; - --tw-drop-shadow-alpha: 100%; - --tw-drop-shadow-size: initial; - --tw-ease: initial; - } - } -} +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:"Ubuntu", ui-sans-serif, system-ui, sans-serif;--font-mono:"JetBrains Mono", ui-monospace, SFMono-Regular, monospace;--color-red-500:oklch(63.7% .237 25.331);--color-sky-500:oklch(68.5% .169 237.323);--color-blue-500:oklch(62.3% .214 259.815);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-xl:36rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wider:.05em;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--font-roboto:"Roboto", ui-sans-serif, system-ui, sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}}:root:has(input.theme-controller[value=light]:checked),[data-theme=light]{color-scheme:light;--color-base-100:oklch(100% 0 0);--color-base-200:oklch(98% 0 0);--color-base-300:oklch(95% 0 0);--color-base-content:oklch(21% .006 285.885);--color-primary:oklch(45% .24 277.023);--color-primary-content:oklch(93% .034 272.788);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}:root:has(input.theme-controller[value=dark]:checked),[data-theme=dark]{color-scheme:dark;--color-base-100:oklch(25.33% .016 252.42);--color-base-200:oklch(23.26% .014 253.1);--color-base-300:oklch(21.15% .012 254.09);--color-base-content:oklch(97.807% .029 256.847);--color-primary:oklch(58% .233 277.117);--color-primary-content:oklch(96% .018 272.314);--color-secondary:oklch(65% .241 354.308);--color-secondary-content:oklch(94% .028 342.258);--color-accent:oklch(77% .152 181.912);--color-accent-content:oklch(38% .063 188.416);--color-neutral:oklch(14% .005 285.823);--color-neutral-content:oklch(92% .004 286.32);--color-info:oklch(74% .16 232.661);--color-info-content:oklch(29% .066 243.157);--color-success:oklch(76% .177 163.223);--color-success-content:oklch(37% .077 168.94);--color-warning:oklch(82% .189 84.429);--color-warning-content:oklch(41% .112 45.904);--color-error:oklch(71% .194 13.428);--color-error-content:oklch(27% .105 12.094);--radius-selector:.5rem;--radius-field:.25rem;--radius-box:.5rem;--size-selector:.25rem;--size-field:.25rem;--border:1px;--depth:1;--noise:0}@property --radialprogress{syntax:"";inherits:true;initial-value:0%}@property --aura-angle{syntax:"";inherits:false;initial-value:0deg}:root{scrollbar-color:currentColor #0000}@supports (color:color-mix(in lab, red, red)){:root{scrollbar-color:color-mix(in oklch, currentColor 35%, #0000) #0000}}:root{--fx-noise:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cfilter id='a'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.34' numOctaves='4' stitchTiles='stitch'%3E%3C/feTurbulence%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23a)' opacity='0.2'%3E%3C/rect%3E%3C/svg%3E");--page-scroll-lock:initial;--page-overflow:var(--page-scroll-lock) hidden}:root:not(span){overflow:var(--page-overflow)}:root{--page-has-backdrop:var(--page-scroll-lock) 1;--page-scroll-bg:var(--page-scroll-lock) var(--root-bg,#0000)}@supports (color:color-mix(in lab, red, red)){:root{--page-scroll-bg:var(--page-scroll-lock) color-mix(in srgb, var(--root-bg,#0000), oklch(0% 0 0) calc(var(--page-has-backdrop,0) * 40%))}}:root{background-image:var(--page-scroll-lock) linear-gradient(var(--root-bg,#0000), var(--root-bg,#0000));transition:var(--page-scroll-lock) background-color .3s ease-out;animation:var(--page-scroll-lock) set-page-has-scroll forwards;animation-timeline:var(--page-scroll-lock) scroll();--page-has-scroll:initial;scrollbar-gutter:var(--page-has-scroll) var(--page-scroll-lock) stable}@keyframes set-page-has-scroll{0%,to{--page-has-scroll: }}:root,[data-theme]{background-color:var(--root-bg);color:var(--color-base-content)}:root{background-color:var(--page-scroll-bg,var(--root-bg))}:where(:root,[data-theme]){--root-bg:var(--color-base-100)}}@layer components;@layer utilities{@layer daisyui.l1.l2.l3{.tooltip{--tt-bg:var(--color-neutral);--tt-off:calc(100% + .5rem);--tt-tail:calc(100% + 1px + .25rem);--tt-tail-off:.5rem;display:inline-block;position:relative}.tooltip>.tooltip-content,.tooltip[data-tip]:before{border-radius:var(--radius-field);text-align:center;white-space:normal;max-width:20rem;color:var(--color-neutral-content);opacity:0;background-color:var(--tt-bg);pointer-events:none;z-index:2;--tw-content:attr(data-tip);content:var(--tw-content);width:max-content;padding-block:.25rem;padding-inline:.5rem;font-size:.875rem;line-height:1.25;position:absolute}.tooltip:after{opacity:0;background-color:var(--tt-bg);content:"";pointer-events:none;--mask-tooltip:url("data:image/svg+xml,%3Csvg width='10' height='4' viewBox='0 0 8 4' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.500009 1C3.5 1 3.00001 4 5.00001 4C7 4 6.5 1 9.5 1C10 1 10 0.499897 10 0H0C-1.99338e-08 0.5 0 1 0.500009 1Z' fill='black'/%3E%3C/svg%3E%0A");width:.625rem;height:.25rem;-webkit-mask-position:-1px 0;mask-position:-1px 0;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-image:var(--mask-tooltip);-webkit-mask-image:var(--mask-tooltip);mask-image:var(--mask-tooltip);display:block;position:absolute}@media (prefers-reduced-motion:no-preference){.tooltip>.tooltip-content,.tooltip[data-tip]:before,.tooltip:after{transition:opacity .2s cubic-bezier(.4,0,.2,1) 75ms,transform .2s cubic-bezier(.4,0,.2,1) 75ms}}:is(.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))).tooltip-open,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):hover,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):has(:focus-visible))>.tooltip-content,:is(.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))).tooltip-open,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):hover,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):has(:focus-visible))[data-tip]:before,:is(.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))).tooltip-open,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):hover,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):has(:focus-visible)):after{opacity:1;--tt-pos:0rem}@media (prefers-reduced-motion:no-preference){:is(.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))).tooltip-open,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):hover,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):has(:focus-visible))>.tooltip-content,:is(.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))).tooltip-open,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):hover,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):has(:focus-visible))[data-tip]:before,:is(.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))).tooltip-open,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):hover,.tooltip:is([data-tip]:not([data-tip=""]),:has(.tooltip-content:not(:empty))):has(:focus-visible)):after{transition:opacity .2s cubic-bezier(.4,0,.2,1),transform .2s cubic-bezier(.4,0,.2,1)}}.menu{--menu-active-fg:var(--color-neutral-content);--menu-active-bg:var(--color-neutral);flex-flow:column wrap;width:fit-content;padding:.5rem;font-size:.875rem;display:flex}.menu :where(li ul,li menu){white-space:nowrap;margin-inline-start:1rem;padding-inline-start:.5rem;position:relative}.menu :where(li ul,li menu):before{background-color:var(--color-base-content);opacity:.1;width:var(--border);content:"";inset-inline-start:0;position:absolute;top:.75rem;bottom:.75rem}.menu :where(li>.menu-dropdown:not(.menu-dropdown-show)){display:none}.menu :where(li:not(.menu-title)>:not(ul,menu,details,.menu-title,.btn)),.menu :where(li:not(.menu-title)>details>summary:not(.menu-title)){border-radius:var(--radius-field);text-align:start;-webkit-user-select:none;user-select:none;grid-auto-columns:minmax(auto,max-content) auto max-content;grid-auto-flow:column;align-content:flex-start;align-items:center;gap:.5rem;padding-block:.375rem;padding-inline:.75rem;transition-property:color,background-color,box-shadow;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:grid}.menu :where(li>details>summary){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li>details>summary){outline-offset:2px;outline:2px solid #0000}}.menu :where(li>details>summary)::-webkit-details-marker{display:none}:is(.menu :where(li>details>summary),.menu :where(li>.menu-dropdown-toggle)):after{content:"";transform-origin:50%;pointer-events:none;justify-self:flex-end;width:.375rem;height:.375rem;transition-property:rotate,translate;transition-duration:.2s;display:block;translate:0 -1px;rotate:-135deg;box-shadow:inset 2px 2px}.menu details{interpolate-size:allow-keywords;overflow:hidden}.menu details::details-content{block-size:0}@media (prefers-reduced-motion:no-preference){.menu details::details-content{transition-behavior:allow-discrete;transition-property:block-size,content-visibility;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}}.menu details[open]::details-content{block-size:auto}.menu :where(li>details[open]>summary):after,.menu :where(li>.menu-dropdown-toggle.menu-dropdown-show):after{translate:0 1px;rotate:45deg}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{color:var(--color-base-content);--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn).menu-focus,.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title),li:not(.menu-title,.disabled)>details>summary:not(.menu-title)):not(.menu-active,:active,.btn):focus-visible{outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){outline-offset:2px;outline:2px solid #0000}}.menu :where(li:not(.menu-title,.disabled)>:not(ul,menu,details,.menu-title):not(.menu-active,:active,.btn):hover,li:not(.menu-title,.disabled)>details>summary:not(.menu-title):not(.menu-active,:active,.btn):hover){box-shadow:inset 0 1px oklch(0% 0 0/.01),inset 0 -1px oklch(100% 0 0/.01)}.menu :where(li:empty){background-color:var(--color-base-content);opacity:.1;height:1px;margin:.5rem 1rem}.menu :where(li){flex-flow:column wrap;flex-shrink:0;align-items:stretch;display:flex;position:relative}.menu :where(li) .badge{justify-self:flex-end}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{outline-offset:2px;outline:2px solid #0000}}.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):not(:is(.menu :where(li)>:not(ul,menu,.menu-title,details,.btn):active,.menu :where(li)>:not(ul,menu,.menu-title,details,.btn).menu-active,.menu :where(li)>details>summary:active):active){box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--menu-active-bg)}.menu :where(li).menu-disabled,.menu :where(li) [disabled]{pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.menu :where(li).menu-disabled,.menu :where(li) [disabled]{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.menu .dropdown:focus-within .menu-dropdown-toggle:after{translate:0 1px;rotate:45deg}.menu .dropdown-content{margin-top:.5rem;padding:.5rem}.menu .dropdown-content:before{display:none}.otp{font-family:var(--font-mono);clip-path:inset(-3.5px 3.5px -3.5px -3.5px);border-radius:var(--radius-field);gap:var(--otp-gap);--input-color:var(--color-base-content);font-size:1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.otp{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.otp{--otp-ch:1ch}@supports (font:-apple-system-body){.otp{--otp-ch:.618164em}}.otp{--otp-gap:calc(var(--otp-ch) * .5);--otp-w:calc(var(--otp-ch) * 2);--otp-size:calc(var(--size-field,.25rem) * 10);--stride:calc(var(--otp-w) + var(--otp-gap))}.otp>input{pointer-events:none;z-index:1;appearance:none;border-style:var(--tw-border-style);outline-style:var(--tw-outline-style);field-sizing:content;letter-spacing:calc(var(--stride) - var(--otp-ch));font-variant-numeric:tabular-nums;background-color:#0000;border-width:0;outline-width:0;margin:0;padding:0;padding-inline-start:calc(var(--otp-ch) * .5 + var(--border));line-height:1;inset-inline-start:0}.otp>input::selection{color:var(--color-base-content);background-color:#0000}@supports (color:color-mix(in lab, red, red)){.otp>input::selection{color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.otp>input:valid{caret-color:#0000}.otp:has(>span:first-child){width:calc(var(--stride) * 1)}.otp:has(>span:nth-child(2)){width:calc(var(--stride) * 2)}.otp:has(>span:nth-child(3)){width:calc(var(--stride) * 3)}.otp:has(>span:nth-child(4)){width:calc(var(--stride) * 4)}.otp:has(>span:nth-child(5)){width:calc(var(--stride) * 5)}.otp:has(>span:nth-child(6)){width:calc(var(--stride) * 6)}.otp:has(>span:nth-child(7)){width:calc(var(--stride) * 7)}.otp:has(>span:nth-child(8)){width:calc(var(--stride) * 8)}.otp>span{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));inline-size:var(--otp-w);block-size:var(--otp-size);background-color:var(--color-base-100);border:var(--border) solid var(--input-color);border-radius:inherit;outline-offset:1px;box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;outline:2px solid #0000;transition:border-color .2s;display:flex;position:absolute}@supports (color:color-mix(in lab, red, red)){.otp>span{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.otp>span:first-child{left:1px}@supports (font:-apple-system-body){.otp>span:first-child{left:2px}}@supports ((-moz-appearance:none)){.otp>span:first-child{left:0}}.otp>span:nth-child(2){left:calc(var(--stride) * 1);transition-delay:20ms}.otp>span:nth-child(3){left:calc(var(--stride) * 2);transition-delay:40ms}.otp>span:nth-child(4){left:calc(var(--stride) * 3);transition-delay:60ms}.otp>span:nth-child(5){left:calc(var(--stride) * 4);transition-delay:80ms}.otp>span:nth-child(6){left:calc(var(--stride) * 5);transition-delay:.1s}.otp>span:nth-child(7){left:calc(var(--stride) * 6);transition-delay:.12s}.otp>span:nth-child(8){left:calc(var(--stride) * 7);transition-delay:.14s}.otp:has(input:valid:focus)>span{outline:2px solid var(--input-color);outline-offset:1px}.otp:has(input:valid:focus):after{opacity:0}.otp:after{content:"";width:var(--otp-w);height:var(--otp-size);border-radius:var(--radius-field);outline-offset:1px;z-index:10;outline:2px solid #0000;flex-shrink:0;margin-inline-start:calc(-1 * (var(--otp-gap) + var(--otp-ch) * .5 + var(--border)))}.otp:focus-within{isolation:isolate;--input-color:var(--color-base-content)}.otp:focus-within>span{box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.otp:focus-within>span{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.otp:focus-within:after{outline-color:var(--input-color)}.dropdown{position-area:var(--anchor-v,bottom) var(--anchor-h,span-right);display:inline-block;position:relative}.dropdown>:not(:has(~[class*=dropdown-content])):focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.dropdown>:not(:has(~[class*=dropdown-content])):focus{outline-offset:2px;outline:2px solid #0000}}.dropdown .dropdown-content{position:absolute}.dropdown.dropdown-close .dropdown-content,.dropdown:not(details,.dropdown-open,.dropdown-hover:hover,:focus-within) .dropdown-content,.dropdown.dropdown-hover:not(:hover) [tabindex]:first-child:focus:not(:focus-visible)~.dropdown-content{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover],.dropdown .dropdown-content{z-index:999}@media (prefers-reduced-motion:no-preference){.dropdown[popover],.dropdown .dropdown-content{transition-behavior:allow-discrete;transition-property:opacity,scale,display,overlay;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s dropdown}}@starting-style{.dropdown[popover],.dropdown .dropdown-content{opacity:0;scale:.95}}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within)>[tabindex]:first-child{pointer-events:none}:is(.dropdown:not(.dropdown-close).dropdown-open,.dropdown:not(.dropdown-close):not(.dropdown-hover):focus,.dropdown:not(.dropdown-close):focus-within) .dropdown-content,.dropdown:not(.dropdown-close).dropdown-hover:hover .dropdown-content{opacity:1;scale:1}.dropdown:is(details){overflow:revert-layer}.dropdown:is(details) summary::-webkit-details-marker{display:none}.dropdown:where([popover]){background:0 0}.dropdown[popover]{color:inherit;position:fixed}@supports not (position-area:bottom){.dropdown[popover]{margin:auto}.dropdown[popover].dropdown-close{transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover].dropdown-open:not(:popover-open){transform-origin:top;opacity:0;display:none;scale:.95}.dropdown[popover]::backdrop{background-color:oklab(0% none none/.3)}}:is(.dropdown[popover].dropdown-close,.dropdown[popover]:not(.dropdown-open,:popover-open)){transform-origin:top;opacity:0;display:none;scale:.95}:where(.btn){width:unset}.btn{--size:calc(var(--size-field,.25rem) * 10);--btn-p:1rem;--btn-fg:var(--color-base-content);cursor:pointer;text-align:center;vertical-align:middle;outline-offset:2px;webkit-user-select:none;-webkit-user-select:none;user-select:none;border-width:var(--border);touch-action:manipulation;--btn-bg:var(--btn-color,var(--color-base-200));--btn-border:var(--btn-color,var(--color-base-200));border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));flex-wrap:nowrap;flex-shrink:0;justify-content:center;align-items:center;gap:.375rem;font-weight:600;transition-property:color,background-color,border-color,box-shadow,transform;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1);display:inline-flex}@supports (color:color-mix(in lab, red, red)){.btn{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 calc(var(--depth) * 5%))}}.btn{--btn-soft-bg:initial;--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn{--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));height:var(--size);padding-inline:var(--btn-p);font-size:var(--fontsize,.875rem);background-color:var(--btn-bg);color:var(--btn-fg);border-color:var(--btn-border);border-style:var(--btn-border-style,solid);outline-color:var(--btn-color,var(--color-base-content));--tw-prose-links:var(--btn-fg);background-image:none, var(--fx-noise);background-size:auto, calc(var(--noise,0) * 100%);text-shadow:0 .5px oklch(100% 0 0 / calc(var(--depth) * .15));box-shadow:var(--btn-inset) inset, var(--btn-shadow)}.btn:is([type=checkbox],[type=radio]){appearance:none}.btn:is([type=checkbox],[type=radio])[aria-label]:after{--tw-content:attr(aria-label);content:var(--tw-content)}.collapse{border-radius:var(--radius-box,1rem);isolation:isolate;grid-template-rows:max-content 0fr;grid-template-columns:minmax(0,1fr);width:100%;display:grid;position:relative}@media (prefers-reduced-motion:no-preference){.collapse{transition:grid-template-rows .2s}}.collapse>input:is([type=checkbox],[type=radio]){appearance:none;opacity:0;z-index:1;grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close)),.collapse:not(.collapse-close):has(>input:is([type=checkbox],[type=radio]):checked){grid-template-rows:max-content 1fr}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){--overflow-delay:.2s;overflow:revert-layer;content-visibility:visible;min-height:fit-content}@supports not (content-visibility:visible){.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>.collapse-content,.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){visibility:visible}}.collapse:focus-visible,.collapse:has(>input:is([type=checkbox],[type=radio]):focus-visible),.collapse:has(summary:focus-visible){outline-color:var(--color-base-content);outline-offset:2px;outline-width:2px;outline-style:solid}.collapse:not(.collapse-close)>input[type=checkbox],.collapse:not(.collapse-close)>input[type=radio]:not(:checked),.collapse:not(.collapse-close)>.collapse-title{cursor:pointer}:is(.collapse[tabindex]:focus:not(.collapse-close,.collapse[open]),.collapse[tabindex]:focus-within:not(.collapse-close,.collapse[open]))>.collapse-title{cursor:unset}.collapse:is([open],[tabindex]:focus:not(.collapse-close),[tabindex]:focus-within:not(.collapse-close))>:where(.collapse-content),.collapse:not(.collapse-close)>:where(input:is([type=checkbox],[type=radio]):checked~.collapse-content){padding-bottom:1rem}.collapse:is(details){width:100%}.collapse:is(details)::details-content{--overflow-delay:0s;height:0;overflow:clip}.collapse:is(details):where([open])::details-content{overflow:revert-layer;height:auto}@media (prefers-reduced-motion:no-preference){.collapse:is(details)::details-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out, height .2s;interpolate-size:allow-keywords}.collapse:is(details):where([open])::details-content{--overflow-delay:.2s}}.collapse:is(details)>summary{outline:none;display:block;position:relative}.collapse:is(details)>summary::-webkit-details-marker{display:none}.collapse-content{--overflow-delay:0s;content-visibility:hidden;min-height:0;cursor:unset;grid-row-start:2;grid-column-start:1;padding-left:1rem;padding-right:1rem;overflow:clip}@supports not (content-visibility:hidden){.collapse-content{visibility:hidden}}@media (prefers-reduced-motion:no-preference){.collapse-content{transition:overflow .2s allow-discrete var(--overflow-delay), content-visibility .2s allow-discrete, visibility .2s allow-discrete, min-height .2s ease-out allow-discrete, padding .1s ease-out 20ms, background-color .2s ease-out}}details>.collapse-content{content-visibility:visible}.filter{flex-wrap:wrap;display:flex}.filter [type=radio]{width:auto}.filter input{opacity:1;transition:visibility .1s allow-discrete, margin .1s, opacity .3s, padding .3s, border-width .1s;overflow:hidden;scale:1}.filter input.filter-reset{aspect-ratio:1}.filter input.filter-reset:after{--tw-content:"×";content:var(--tw-content)}.filter>input:not(:last-child),.filter>:not(:last-child) input{margin-inline-end:.25rem}.list{flex-direction:column;font-size:.875rem;display:flex}.list .list-row{--list-grid-cols:minmax(0, auto) 1fr;border-radius:var(--radius-box);word-break:break-word;grid-auto-flow:column;grid-template-columns:var(--list-grid-cols);gap:1rem;padding:1rem;display:grid;position:relative}:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{content:"";border-bottom:var(--border) solid;inset-inline:var(--radius-box);border-color:var(--color-base-content);position:absolute;bottom:0}@supports (color:color-mix(in lab, red, red)){:is(.list>:not(:last-child).list-row,.list>:not(:last-child) .list-row):after{border-color:color-mix(in oklab, var(--color-base-content) 5%, transparent)}}.toggle{border:var(--border) solid currentColor;color:var(--input-color);cursor:pointer;appearance:none;vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;--radius-selector-max:calc(var(--radius-selector) + var(--radius-selector) + var(--radius-selector));border-radius:calc(var(--radius-selector) + min(var(--toggle-p), var(--radius-selector-max)) + min(var(--border), var(--radius-selector-max)));padding:var(--toggle-p);flex-shrink:0;grid-template-columns:0fr 1fr 1fr;place-content:center;display:inline-grid;position:relative;box-shadow:inset 0 1px}@supports (color:color-mix(in lab, red, red)){.toggle{box-shadow:0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000) inset}}.toggle{--input-color:var(--color-base-content);transition:color .3s,grid-template-columns .2s}@supports (color:color-mix(in lab, red, red)){.toggle{--input-color:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}.toggle{--toggle-p:calc(var(--size) * .125);--size:calc(var(--size-selector,.25rem) * 6);width:calc((var(--size) * 2) - (var(--border) + var(--toggle-p)) * 2);height:var(--size)}.toggle>*{z-index:1;cursor:pointer;appearance:none;background-color:#0000;border:none;grid-column:2/span 1;grid-row-start:1;height:100%;padding:.125rem;transition:opacity .2s,rotate .4s}.toggle>:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.toggle>:focus{outline-offset:2px;outline:2px solid #0000}}.toggle>:nth-child(2){color:var(--color-base-100);rotate:0deg}.toggle>:nth-child(3){color:var(--color-base-100);opacity:0;rotate:-15deg}.toggle:has(:checked)>:nth-child(2){opacity:0;rotate:15deg}.toggle:has(:checked)>:nth-child(3){opacity:1;rotate:0deg}.toggle:before{aspect-ratio:1;border-radius:var(--radius-selector);--tw-content:"";content:var(--tw-content);width:100%;height:100%;box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px currentColor;background-color:currentColor;grid-row-start:1;grid-column-start:2;transition:background-color .1s,translate .2s,inset-inline-start .2s;position:relative;inset-inline-start:0;translate:0}@supports (color:color-mix(in lab, red, red)){.toggle:before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px color-mix(in oklab, currentColor calc(var(--depth) * 10%), #0000)}}.toggle:before{background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}@media (forced-colors:active){.toggle:before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{.toggle:before{outline-offset:-1rem;outline:.25rem solid}}.toggle:focus-visible,.toggle:has(:focus-visible){outline-offset:2px;outline:2px solid}.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked){background-color:var(--color-base-100);--input-color:var(--color-base-content);grid-template-columns:1fr 1fr 0fr}:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{background-color:currentColor}@starting-style{:is(.toggle:checked,.toggle[aria-checked=true],.toggle:has(>input:checked)):before{opacity:0}}.toggle:indeterminate{grid-template-columns:.5fr 1fr .5fr}.toggle:disabled{cursor:not-allowed;opacity:.3}.toggle:disabled:before{border:var(--border) solid currentColor;background-color:#0000}.input{appearance:none;background-color:var(--color-base-100);vertical-align:middle;white-space:nowrap;--size:calc(var(--size-field,.25rem) * var(--in-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.5rem;padding-inline:.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.input{cursor:text;width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field))}@supports (color:color-mix(in lab, red, red)){.input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.input input{appearance:none;background-color:#0000;border:none;width:100%;height:100%}.input input::placeholder{color:var(--color-base-content);opacity:.5}.input input:focus,.input input:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.input input:focus,.input input:focus-within{outline-offset:2px;outline:2px solid #0000}}.input input::-webkit-calendar-picker-indicator{inset-inline-end:-.15em}.input::-webkit-inner-spin-button{margin-inline-end:-10px}.input::-webkit-calendar-picker-indicator{inset-inline-end:.75em}input.input,.input input{display:inline-flex;position:relative}:is(input.input,.input input)[type=url],:is(input.input,.input input)[type=email]{text-align:left}:is(input.input,.input input)::-webkit-datetime-edit{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-date-and-time-value{min-height:100%;text-align:inherit;align-items:center;display:grid}:is(input.input,.input input)::-webkit-inner-spin-button{margin-block:calc(.25rem * var(--spin-my))}:is(input.input,.input input)::-webkit-calendar-picker-indicator{cursor:pointer;width:1em;height:1em;position:absolute}:is(input.input,.input input)::-webkit-color-swatch-wrapper{padding-block:.25rem}.input:focus,.input:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.input:focus,.input:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.input:focus,.input:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.input:focus,.input:focus-within{--font-size:1rem}}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input):is(input),:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) :is(input){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input{box-shadow:none}:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input)::placeholder,:is(.input:has(>input[disabled]),.input:is(:disabled,[disabled]),fieldset:disabled .input) ::placeholder{color:var(--color-base-content);opacity:.2}.input:has(>input[disabled])>input[disabled]{cursor:not-allowed}.indicator{width:max-content;display:inline-flex;position:relative}.indicator :where(.indicator-item){z-index:1;white-space:nowrap;top:var(--indicator-t,0);bottom:var(--indicator-b,auto);left:var(--indicator-s,auto);right:var(--indicator-e,0);translate:var(--indicator-x,50%) var(--indicator-y,-50%);position:absolute}.table{border-collapse:separate;--tw-border-spacing-x:0;--tw-border-spacing-y:0;width:100%;border-spacing:var(--tw-border-spacing-x) var(--tw-border-spacing-y);border-radius:var(--radius-box);text-align:left;font-size:.875rem;position:relative}.table:where(:dir(rtl),[dir=rtl],[dir=rtl] *){text-align:right}@media (hover:hover){:is(.table tr.row-hover,.table tr.row-hover:nth-child(2n)):hover{background-color:var(--color-base-200)}}.table :where(th,td){vertical-align:middle;padding-block:.75rem;padding-inline:1rem}.table :where(thead,tfoot){white-space:nowrap;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead,tfoot){color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.table :where(thead,tfoot){font-size:.875rem;font-weight:600}.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(tfoot tr:first-child :is(td,th)){border-top:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.table :where(.table-pin-rows thead tr){z-index:1;background-color:var(--color-base-100);position:sticky;top:0}.table :where(.table-pin-rows tfoot tr){z-index:1;background-color:var(--color-base-100);position:sticky;bottom:0}.table :where(.table-pin-cols tr th){background-color:var(--color-base-100);position:sticky;left:0;right:0}.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.table :where(thead tr :is(td,th),tbody tr:not(:last-child) :is(td,th)){border-bottom:var(--border) solid color-mix(in oklch, var(--color-base-content) 5%, #0000)}}.steps{counter-reset:step;grid-auto-columns:1fr;grid-auto-flow:column;display:inline-grid;overflow:auto hidden}.steps .step{text-align:center;--step-bg:var(--color-base-300);--step-fg:var(--color-base-content);grid-template-rows:40px 1fr;grid-template-columns:auto;place-items:center;min-width:4rem;display:grid}.steps .step:before{width:100%;height:.5rem;color:var(--step-bg);background-color:var(--step-bg);content:"";border:1px solid;grid-row-start:1;grid-column-start:1;margin-inline-start:-100%;top:0}.steps .step>.step-icon,.steps .step:not(:has(.step-icon)):after{--tw-content:counter(step);content:var(--tw-content);counter-increment:step;z-index:1;color:var(--step-fg);background-color:var(--step-bg);border:1px solid var(--step-bg);border-radius:3.40282e38px;grid-row-start:1;grid-column-start:1;place-self:center;place-items:center;width:2rem;height:2rem;display:grid;position:relative}.steps .step:first-child:before{--tw-content:none;content:var(--tw-content)}.steps .step[data-content]:after{--tw-content:attr(data-content);content:var(--tw-content)}.select{appearance:none;background-color:var(--color-base-100);vertical-align:middle;--size:calc(var(--size-field,.25rem) * var(--sl-size-mul));--input-color:var(--color-base-content);flex-shrink:1;align-items:center;gap:.375rem;padding-inline:.75rem 1.75rem;display:inline-flex;position:relative}@supports (color:color-mix(in lab, red, red)){.select{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.select{width:clamp(3rem,20rem,100%);height:var(--size);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;white-space:nowrap;text-overflow:ellipsis;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-image:linear-gradient(45deg,#0000 50%,currentColor 50%),linear-gradient(135deg,currentColor 50%,#0000 50%);background-position:calc(100% - 20px) calc(1px + 50%),calc(100% - 16.1px) calc(1px + 50%);background-repeat:no-repeat;background-size:4px 4px,4px 4px;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));overflow:hidden}@supports (color:color-mix(in lab, red, red)){.select{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}[dir=rtl] .select{background-position:12px calc(1px + 50%),16px calc(1px + 50%)}[dir=rtl] .select::picker(select){translate:.5rem}[dir=rtl] .select select::picker(select){translate:.5rem}.select[multiple]{background-image:none;height:auto;padding-block:.75rem;padding-inline-end:.75rem;overflow:auto}.select select{appearance:none;width:calc(100% + 2.75rem);height:calc(100% - calc(var(--border) * 2));background:inherit;border-radius:inherit;border-style:none;align-items:center;margin-inline:-.75rem -1.75rem;padding-inline:.75rem 1.75rem}.select select::placeholder{color:var(--color-base-content);opacity:.5}.select select:focus,.select select:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.select select:focus,.select select:focus-within{outline-offset:2px;outline:2px solid #0000}}.select select:not(:last-child){background-image:none;margin-inline-end:-1.375rem}:is(.select:focus,.select:focus-within,.select:open){--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){:is(.select:focus,.select:focus-within,.select:open){box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}:is(.select:focus,.select:focus-within,.select:open){outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate;background-image:linear-gradient(135deg,#0000 50%,currentColor 50%),linear-gradient(45deg,currentColor 50%,#0000 50%)}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select):is(select),:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) :is(select){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select{box-shadow:none}:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select)::placeholder,:is(.select:has(>select[disabled]),.select:is(:disabled,[disabled]),fieldset:disabled .select) ::placeholder{color:var(--color-base-content);opacity:.2}.select:has(>select[disabled])>select[disabled]{cursor:not-allowed}@supports (appearance:base-select){:is(.select,.select select){appearance:base-select}:is(.select,.select select)::picker(select){appearance:base-select}}:is(.select,.select select)::picker(select){color:inherit;border:var(--border) solid var(--color-base-200);border-radius:var(--radius-box);background-color:inherit;max-height:min(24rem,70dvh);box-shadow:0 2px calc(var(--depth) * 3px) -2px oklch(0% 0 0/.2);box-shadow:0 20px 25px -5px rgb(0 0 0/calc(var(--depth) * .1)), 0 8px 10px -6px rgb(0 0 0/calc(var(--depth) * .1));margin-block:.5rem;margin-inline:.5rem;padding:.5rem;translate:-.5rem}:is(.select,.select select)::picker-icon{display:none}:is(.select,.select select) selectedcontent{text-overflow:ellipsis;white-space:nowrap;width:100%;overflow:hidden}:is(.select,.select select) optgroup{padding-top:.5em}:is(.select,.select select) optgroup option:first-child{margin-top:.5em}:is(.select,.select select) option{border-radius:var(--radius-field);padding-block:.375rem;padding-inline:calc(.25rem * var(--option-px));white-space:normal;transition-property:color,background-color;transition-duration:.2s;transition-timing-function:cubic-bezier(0,0,.2,1)}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{cursor:pointer;background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:is(.select,.select select) option:not(:disabled):hover,:is(.select,.select select) option:not(:disabled):focus-visible{outline-offset:2px;outline:2px solid #0000}}:is(.select,.select select) option:not(:disabled):active{background-color:var(--color-neutral);color:var(--color-neutral-content);box-shadow:0 2px calc(var(--depth) * 3px) -2px var(--color-neutral)}.collapse-title{grid-row-start:1;grid-column-start:1;width:100%;min-height:1lh;padding:1rem;padding-inline-end:3rem;transition:background-color .2s ease-out;position:relative}.avatar{vertical-align:middle;display:inline-flex;position:relative}.avatar>div{aspect-ratio:1;display:block;overflow:hidden}.avatar img{object-fit:cover;width:100%;height:100%}.checkbox{border:var(--border) solid var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox{border:var(--border) solid var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox{cursor:pointer;appearance:none;border-radius:var(--radius-selector);vertical-align:middle;color:var(--color-base-content);box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 0 #0000 inset, 0 0 #0000;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);flex-shrink:0;padding:.25rem;transition:background-color .2s,box-shadow .2s;display:inline-block;position:relative}.checkbox:before{--tw-content:"";content:var(--tw-content);opacity:0;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,70% 80%,70% 100%);width:100%;height:100%;box-shadow:0px 3px 0 0px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;background-color:currentColor;font-size:1rem;line-height:.75;transition:clip-path .3s .1s,opacity .1s .1s,rotate .3s .1s,translate .3s .1s;display:block;rotate:45deg}.checkbox:focus-visible{outline:2px solid var(--input-color,currentColor);outline-offset:2px}.checkbox:checked,.checkbox[aria-checked=true]{background-color:var(--input-color,#0000);box-shadow:0 0 #0000 inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1))}:is(.checkbox:checked,.checkbox[aria-checked=true]):before{clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 0%,70% 0%,70% 100%);opacity:1}@media (forced-colors:active){:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}@media print{:is(.checkbox:checked,.checkbox[aria-checked=true]):before{--tw-content:"✔︎";clip-path:none;background-color:#0000;rotate:0deg}}.checkbox:indeterminate{background-color:var(--input-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.checkbox:indeterminate{background-color:var(--input-color,color-mix(in oklab, var(--color-base-content) 20%, #0000))}}.checkbox:indeterminate:before{opacity:1;clip-path:polygon(20% 100%,20% 80%,50% 80%,50% 80%,80% 80%,80% 100%);translate:0 -35%;rotate:0deg}.radio{cursor:pointer;appearance:none;vertical-align:middle;border:var(--border) solid var(--input-color,currentColor);border-radius:3.40282e38px;flex-shrink:0;padding:.25rem;display:inline-block;position:relative}@supports (color:color-mix(in lab, red, red)){.radio{border:var(--border) solid var(--input-color,color-mix(in srgb, currentColor 20%, #0000))}}.radio{box-shadow:0 1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset;--size:calc(var(--size-selector,.25rem) * 6);width:var(--size);height:var(--size);color:var(--input-color,currentColor)}.radio:before{--tw-content:"";content:var(--tw-content);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);border-radius:3.40282e38px;width:100%;height:100%;display:block}.radio:focus-visible{outline:2px solid}.radio:checked,.radio[aria-checked=true]{background-color:var(--color-base-100);border-color:currentColor}@media (prefers-reduced-motion:no-preference){.radio:checked,.radio[aria-checked=true]{animation:.2s ease-out radio}}:is(.radio:checked,.radio[aria-checked=true]):before{box-shadow:0 -1px oklch(0% 0 0 / calc(var(--depth) * .1)) inset, 0 8px 0 -4px oklch(100% 0 0 / calc(var(--depth) * .1)) inset, 0 1px oklch(0% 0 0 / calc(var(--depth) * .1));background-color:currentColor}@media (forced-colors:active){:is(.radio:checked,.radio[aria-checked=true]):before{outline-style:var(--tw-outline-style);outline-offset:calc(1px * -1);outline-width:1px}}@media print{:is(.radio:checked,.radio[aria-checked=true]):before{outline-offset:-1rem;outline:.25rem solid}}.navbar{align-items:center;width:100%;min-height:4rem;padding:.5rem;display:flex}.card{border-radius:var(--radius-box);outline-offset:2px;outline:2px solid #0000;flex-direction:column;transition:outline .2s ease-in-out;display:flex;position:relative}.card:focus-visible,.card[aria-checked=true],.card:has(>:checked,>:is([type=checkbox],[type=radio]):focus-visible){outline-color:currentColor}.card:has(>:checked:focus-visible),.card[aria-checked=true]:focus-visible,.card[aria-checked=true]:has(>:is([type=checkbox],[type=radio]):focus-visible){outline-width:4px}.card:has(>:is([type=checkbox],[type=radio])){cursor:pointer;-webkit-user-select:none;user-select:none}.card>:is([type=checkbox],[type=radio]){appearance:none}.stats{border-radius:var(--radius-box);grid-auto-flow:column;display:inline-grid;position:relative;overflow-x:auto}.progress{appearance:none;border-radius:var(--radius-box);background-color:currentColor;width:100%;height:.5rem;position:relative;overflow:hidden}@supports (color:color-mix(in lab, red, red)){.progress{background-color:color-mix(in oklab, currentcolor 20%, transparent)}}.progress{color:var(--color-base-content)}.progress:indeterminate{background-image:repeating-linear-gradient(90deg,currentColor -1% 10%,#0000 10% 90%);background-position-x:15%;background-size:200%}@media (prefers-reduced-motion:no-preference){.progress:indeterminate{animation:5s ease-in-out infinite progress}}@supports ((-moz-appearance:none)){.progress:indeterminate::-moz-progress-bar{background-color:#0000}@media (prefers-reduced-motion:no-preference){.progress:indeterminate::-moz-progress-bar{background-image:repeating-linear-gradient(90deg,currentColor -1% 10%,#0000 10% 90%);background-position-x:15%;background-size:200%;animation:5s ease-in-out infinite progress}}.progress::-moz-progress-bar{border-radius:var(--radius-box);background-color:currentColor}@media (prefers-reduced-motion:no-preference){.progress::-moz-progress-bar{transition:inline-size .3s}}}@supports ((-webkit-appearance:none)){.progress::-webkit-progress-bar{border-radius:var(--radius-box);background-color:#0000}.progress::-webkit-progress-value{border-radius:var(--radius-box);background-color:currentColor}@media (prefers-reduced-motion:no-preference){.progress::-webkit-progress-value{transition:inline-size .3s}}}.file-input{cursor:pointer;cursor:pointer;border:var(--border) solid #0000;appearance:none;background-color:var(--color-base-100);vertical-align:middle;webkit-user-select:none;-webkit-user-select:none;user-select:none;width:clamp(3rem,20rem,100%);height:var(--size);border-color:var(--input-color);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset;border-start-start-radius:var(--join-ss,var(--radius-field));border-start-end-radius:var(--join-se,var(--radius-field));border-end-end-radius:var(--join-ee,var(--radius-field));border-end-start-radius:var(--join-es,var(--radius-field));align-items:center;padding-inline-end:.75rem;font-size:.875rem;line-height:2;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.file-input{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.file-input{--size:calc(var(--size-field,.25rem) * 10);--input-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.file-input::file-selector-button{cursor:pointer;webkit-user-select:none;-webkit-user-select:none;user-select:none;height:calc(100% + var(--border) * 2);margin-inline-end:1rem;margin-block:calc(var(--border) * -1);color:var(--btn-fg);border-width:var(--border);border-style:solid;border-color:var(--btn-border);background-color:var(--btn-bg);background-size:calc(var(--noise) * 100%);background-image:var(--fx-noise);text-shadow:0 .5px oklch(1 0 0 / calc(var(--depth) * .15));box-shadow:0 .5px 0 .5px white inset, var(--btn-shadow);border-start-start-radius:calc(var(--join-ss,var(--radius-field) - var(--border)));border-end-start-radius:calc(var(--join-es,var(--radius-field) - var(--border)));margin-inline-start:calc(var(--border) * -1);padding-inline:1rem;font-size:.875rem;font-weight:600}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{box-shadow:0 .5px 0 .5px color-mix(in oklab, color-mix(in oklab, white 30%, var(--btn-bg)) calc(var(--depth) * 20%), #0000) inset, var(--btn-shadow)}}.file-input::file-selector-button{--size:calc(var(--size-field,.25rem) * 10);--btn-bg:var(--btn-color,var(--color-base-200));--btn-fg:var(--color-base-content);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-border:color-mix(in oklab, var(--btn-bg), #000 5%)}}.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.file-input::file-selector-button{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) 30%, #0000)}}.file-input:focus{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.file-input:focus{box-shadow:0 1px color-mix(in oklab, var(--input-color) 10%, #0000)}}.file-input:focus{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200)}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::placeholder{color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){box-shadow:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);--btn-border:#0000;--btn-fg:var(--color-base-content);background-image:none}@supports (color:color-mix(in lab, red, red)){:is(.file-input:has(>input[disabled]),.file-input:is(:disabled,[disabled]))::file-selector-button{--btn-fg:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.textarea{appearance:none;border-radius:var(--radius-field);background-color:var(--color-base-100);vertical-align:middle;--input-color:var(--color-base-content);flex-shrink:1;min-height:5rem;padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.textarea{--input-color:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}.textarea{width:clamp(3rem,20rem,100%);font-size:max(var(--font-size,0rem), var(--font-size-min));touch-action:manipulation;border:var(--border) solid var(--input-color,#0000);box-shadow:0 1px var(--input-color) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}@supports (color:color-mix(in lab, red, red)){.textarea{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000) inset, 0 -1px oklch(100% 0 0 / calc(var(--depth) * .1)) inset}}.textarea textarea{appearance:none;background-color:#0000;border:none}.textarea textarea::placeholder{color:var(--color-base-content);opacity:.5}.textarea textarea:focus,.textarea textarea:focus-within{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.textarea textarea:focus,.textarea textarea:focus-within{outline-offset:2px;outline:2px solid #0000}}.textarea:focus,.textarea:focus-within{--input-color:var(--color-base-content);box-shadow:0 1px var(--input-color)}@supports (color:color-mix(in lab, red, red)){.textarea:focus,.textarea:focus-within{box-shadow:0 1px color-mix(in oklab, var(--input-color) calc(var(--depth) * 10%), #0000)}}.textarea:focus,.textarea:focus-within{outline:2px solid var(--input-color);outline-offset:2px;isolation:isolate}@media (pointer:coarse){@supports (-webkit-touch-callout:none){.textarea:focus,.textarea:focus-within{--font-size:1rem}}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){cursor:not-allowed;border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]){box-shadow:none}:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled]))::placeholder,:is(.textarea:has(>textarea[disabled]),.textarea:is(:disabled,[disabled])) ::placeholder{color:var(--color-base-content);opacity:.2}.textarea:has(>textarea[disabled])>textarea[disabled]{cursor:not-allowed}.stack{grid-template-rows:3px 4px 1fr 4px 3px;grid-template-columns:3px 4px 1fr 4px 3px;display:inline-grid}.stack>*{width:100%;height:100%}.stack>:nth-child(n+2){opacity:.7;width:100%}.stack>:nth-child(2){z-index:2;opacity:.9}.stack>:first-child{z-index:3;width:100%}.stack\!{grid-template-rows:3px 4px 1fr 4px 3px!important;grid-template-columns:3px 4px 1fr 4px 3px!important;display:inline-grid!important}.stack\!>*{width:100%!important;height:100%!important}.stack\!>:nth-child(n+2){opacity:.7!important;width:100%!important}.stack\!>:nth-child(2){z-index:2!important;opacity:.9!important}.stack\!>:first-child{z-index:3!important;width:100%!important}.hero{background-position:50%;background-size:cover;place-items:center;width:100%;display:grid}.hero>*{grid-row-start:1;grid-column-start:1}.stat-value{white-space:nowrap;grid-column-start:1;font-size:2rem;font-weight:800}.stat-desc{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-desc{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-desc{font-size:.75rem}.stat-title{white-space:nowrap;color:var(--color-base-content);grid-column-start:1}@supports (color:color-mix(in lab, red, red)){.stat-title{color:color-mix(in oklab, var(--color-base-content) 60%, transparent)}}.stat-title{font-size:.75rem}.divider{white-space:nowrap;height:1rem;margin:var(--divider-m,1rem 0);--divider-color:var(--color-base-content);flex-direction:row;align-self:stretch;align-items:center;display:flex}@supports (color:color-mix(in lab, red, red)){.divider{--divider-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.divider:before,.divider:after{content:"";background-color:var(--divider-color);flex-grow:1;width:100%;height:.125rem}@media print{.divider:before,.divider:after{border:.5px solid}}.divider:not(:empty){gap:1rem}.label{white-space:nowrap;color:currentColor;align-items:center;gap:.375rem;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.label{color:color-mix(in oklab, currentcolor 60%, transparent)}}.label:has(input){cursor:pointer}.label:is(.input>*,.select>*){white-space:nowrap;height:calc(100% - .5rem);font-size:inherit;align-items:center;padding-inline:.75rem;display:flex}.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid currentColor;margin-inline:-.75rem .75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):first-child{border-inline-end:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid currentColor;margin-inline:.75rem -.75rem}@supports (color:color-mix(in lab, red, red)){.label:is(.input>*,.select>*):last-child{border-inline-start:var(--border) solid color-mix(in oklab, currentColor 10%, #0000)}}.status{aspect-ratio:1;border-radius:var(--radius-selector);background-color:var(--color-base-content);width:.5rem;height:.5rem;display:inline-block}@supports (color:color-mix(in lab, red, red)){.status{background-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.status{vertical-align:middle;color:#0000004d;background-position:50%;background-repeat:no-repeat}@supports (color:color-mix(in lab, red, red)){.status{color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.status{background-image:radial-gradient(circle at 35% 30%, oklch(1 0 0 / calc(var(--depth) * .5)), #0000);box-shadow:0 2px 3px -1px}@supports (color:color-mix(in lab, red, red)){.status{box-shadow:0 2px 3px -1px color-mix(in oklab, currentColor calc(var(--depth) * 100%), #0000)}}.badge{border-radius:var(--radius-selector);vertical-align:middle;color:var(--badge-fg);border:var(--border) solid var(--badge-color,var(--color-base-200));background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise);background-color:var(--badge-bg);--badge-bg:var(--badge-color,var(--color-base-100));--badge-fg:var(--color-base-content);--size:calc(var(--size-selector,.25rem) * 6);width:fit-content;height:var(--size);padding-inline:calc(var(--size) / 2 - var(--border));justify-content:center;align-items:center;gap:.5rem;font-size:.875rem;display:inline-flex}.footer{grid-auto-flow:row;place-items:start;gap:2.5rem 1rem;width:100%;font-size:.875rem;line-height:1.25rem;display:grid}.footer>:not(script,style,template){place-items:start;gap:.5rem;display:grid}.footer.footer-center{text-align:center;grid-auto-flow:column dense;place-items:center}.footer.footer-center>:not(script,style,template){place-items:center}.stat{grid-template-columns:repeat(1,1fr);column-gap:1rem;width:100%;padding-block:1rem;padding-inline:1.5rem;display:inline-grid}.stat:not(:last-child){border-inline-end:var(--border) dashed currentColor}@supports (color:color-mix(in lab, red, red)){.stat:not(:last-child){border-inline-end:var(--border) dashed color-mix(in oklab, currentColor 10%, #0000)}}.stat:not(:last-child){border-block-end:none}.card-body{padding:var(--card-p,1.5rem);font-size:var(--card-fs,.875rem);flex-direction:column;flex:auto;gap:.5rem;display:flex}.alert{--alert-border-color:var(--color-base-200);border-radius:var(--radius-box);color:var(--color-base-content);background-color:var(--alert-color,var(--color-base-200));text-align:start;background-size:auto, calc(var(--noise) * 33%);background-image:none, var(--fx-noise);box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px #000, 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08));border-style:solid;grid-template-columns:auto;grid-auto-flow:column;justify-content:start;place-items:center start;gap:1rem;padding-block:.75rem;padding-inline:1rem;font-size:.875rem;line-height:1.25rem;display:grid}@supports (color:color-mix(in lab, red, red)){.alert{box-shadow:0 3px 0 -2px oklch(100% 0 0 / calc(var(--depth) * .08)) inset, 0 1px color-mix(in oklab, color-mix(in oklab, #000 20%, var(--alert-color,var(--color-base-200))) calc(var(--depth) * 20%), #0000), 0 4px 3px -2px oklch(0% 0 0 / calc(var(--depth) * .08))}}.alert:has(:nth-child(2)){grid-template-columns:auto minmax(auto,1fr)}.card-actions{flex-wrap:wrap;align-items:flex-start;gap:.5rem;display:flex}.card-title{font-size:var(--cardtitle-fs,1.125rem);align-items:center;gap:.5rem;font-weight:600;display:flex}.link{cursor:pointer;text-decoration-line:underline}.link:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.link:focus{outline-offset:2px;outline:2px solid #0000}}.link:focus-visible{outline-offset:2px;outline:2px solid}.btn-outline{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content)));--btn-border:var(--btn-color,var(--color-base-content));--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn-soft{--btn-bg:var(--btn-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.btn-soft{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-content)) 8%, var(--btn-soft-bg,var(--color-base-100)))}}.btn-soft{color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content)));--btn-border:var(--btn-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.btn-soft{--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-content)) 10%, var(--btn-soft-bg,var(--color-base-100)))}}.btn-soft{--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn-ghost{--btn-bg:#0000;color:var(--btn-rest-fg,var(--btn-color,var(--color-base-content,currentColor)));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.menu-title{color:var(--color-base-content);padding-block:.5rem;padding-inline:.75rem}@supports (color:color-mix(in lab, red, red)){.menu-title{color:color-mix(in oklab, var(--color-base-content) 40%, transparent)}}.menu-title{font-size:.875rem;font-weight:600}}@layer daisyui.l1.l2{.tooltip>.tooltip-content,.tooltip[data-tip]:before{transform:translateX(var(--tt-trans,-50%)) translateY(var(--tt-pos,.25rem));inset:auto auto var(--tt-off) 50%}.tooltip:after{transform:translateX(var(--tt-trans,-50%)) translateY(var(--tt-pos,.25rem));inset:auto auto var(--tt-tail) 50%}.collapse-arrow>.collapse-title:after{width:.5rem;height:.5rem;display:block;position:absolute;transform:translateY(-100%)rotate(45deg)}@media (prefers-reduced-motion:no-preference){.collapse-arrow>.collapse-title:after{transition-property:all;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1)}}.collapse-arrow>.collapse-title:after{content:"";transform-origin:75% 75%;pointer-events:none;top:50%;inset-inline-end:1.4rem;box-shadow:2px 2px}@media (prefers-reduced-motion:no-preference){.collapse[open].collapse-arrow>.collapse-title:after,.collapse.collapse-open.collapse-arrow>.collapse-title:after{transform:translateY(-50%)rotate(225deg)}}.collapse.collapse-open.collapse-plus>.collapse-title:after{--tw-content:"−";content:var(--tw-content)}:is(.collapse[tabindex].collapse-arrow:focus:not(.collapse-close),.collapse.collapse-arrow[tabindex]:focus-within:not(.collapse-close))>.collapse-title:after,.collapse.collapse-arrow:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{transform:translateY(-50%)rotate(225deg)}.collapse[open].collapse-plus>.collapse-title:after,.collapse[tabindex].collapse-plus:focus:not(.collapse-close)>.collapse-title:after,.collapse.collapse-plus:not(.collapse-close)>input:is([type=checkbox],[type=radio]):checked~.collapse-title:after{--tw-content:"−";content:var(--tw-content)}.list .list-row:has(.list-col-grow:first-child){--list-grid-cols:1fr}.list .list-row:has(.list-col-grow:nth-child(2)){--list-grid-cols:minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(3)){--list-grid-cols:minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(4)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(5)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row:has(.list-col-grow:nth-child(6)){--list-grid-cols:minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) minmax(0, auto) 1fr}.list .list-row>*{grid-row-start:1}.input{--in-size-mul:10;--font-size-min:.875rem;--spin-my:-3}.floating-label:has(.input){--top-mul:5;--font-size:.875rem}.steps .step-neutral+.step-neutral:before,.steps .step-neutral:after,.steps .step-neutral>.step-icon{--step-bg:var(--color-neutral);--step-fg:var(--color-neutral-content)}.steps .step-primary+.step-primary:before,.steps .step-primary:after,.steps .step-primary>.step-icon{--step-bg:var(--color-primary);--step-fg:var(--color-primary-content)}.steps .step-secondary+.step-secondary:before,.steps .step-secondary:after,.steps .step-secondary>.step-icon{--step-bg:var(--color-secondary);--step-fg:var(--color-secondary-content)}.steps .step-accent+.step-accent:before,.steps .step-accent:after,.steps .step-accent>.step-icon{--step-bg:var(--color-accent);--step-fg:var(--color-accent-content)}.steps .step-info+.step-info:before,.steps .step-info:after,.steps .step-info>.step-icon{--step-bg:var(--color-info);--step-fg:var(--color-info-content)}.steps .step-success+.step-success:before,.steps .step-success:after,.steps .step-success>.step-icon{--step-bg:var(--color-success);--step-fg:var(--color-success-content)}.steps .step-warning+.step-warning:before,.steps .step-warning:after,.steps .step-warning>.step-icon{--step-bg:var(--color-warning);--step-fg:var(--color-warning-content)}.steps .step-error+.step-error:before,.steps .step-error:after,.steps .step-error>.step-icon{--step-bg:var(--color-error);--step-fg:var(--color-error-content)}.select{--sl-size-mul:10;--font-size-min:.875rem;--option-px:3}.floating-label:has(.select){--top-mul:5;--font-size:.875rem}.menu-horizontal{flex-direction:row;align-items:center;display:inline-flex}.menu-horizontal>li:not(.menu-title)>details>:is(ul,menu){transform-origin:top;border-radius:var(--radius-box);background-color:var(--color-base-100);opacity:0;margin-inline-start:0;margin-top:1rem;padding-block:.5rem;padding-inline-end:.5rem;position:absolute;scale:.95;box-shadow:0 1px 3px oklch(0% 0 0/.1),0 1px 2px -1px oklch(0% 0 0/.1)}@media (prefers-reduced-motion:no-preference){@starting-style{.menu-horizontal>li:not(.menu-title)>details>:is(ul,menu){opacity:0;scale:.95}}.menu-horizontal>li:not(.menu-title)>details>:is(ul,menu){transition-behavior:allow-discrete;transition-property:opacity,scale,display;transition-duration:.2s;transition-timing-function:cubic-bezier(.4,0,.2,1);animation:.2s menu}}.menu-horizontal>li:not(.menu-title)>details[open]>:is(ul,menu){opacity:1;scale:1}.menu-horizontal>li>details>:is(ul,menu):before{--tw-content:none;content:var(--tw-content)}.checkbox:disabled,.radio:disabled{cursor:not-allowed;opacity:.2}:where(.navbar){position:relative}.dropdown-end{--anchor-h:span-left}.dropdown-end :where(.dropdown-content){inset-inline-end:0;translate:0}[dir=rtl] :is(.dropdown-end :where(.dropdown-content)){translate:0}.dropdown-end.dropdown-left{--anchor-h:left;--anchor-v:span-top}.dropdown-end.dropdown-left .dropdown-content{top:auto;bottom:0}.dropdown-end.dropdown-right{--anchor-h:right;--anchor-v:span-top}.dropdown-end.dropdown-right .dropdown-content{top:auto;bottom:0}.textarea{--font-size-min:.875rem}.floating-label:has(.textarea){--top-mul:5;--font-size:.875rem}:is(.stack,.stack.stack-bottom)>*{grid-area:3/3/6/4}:is(.stack,.stack.stack-bottom)>:nth-child(2){grid-area:2/2/5/5}:is(.stack,.stack.stack-bottom)>:first-child{grid-area:1/1/4/6}.stack.stack-top>*{grid-area:1/3/4/4}.stack.stack-top>:nth-child(2){grid-area:2/2/5/5}.stack.stack-top>:first-child{grid-area:3/1/6/6}.stack.stack-start>*{grid-area:3/1/4/4}.stack.stack-start>:nth-child(2){grid-area:2/2/5/5}.stack.stack-start>:first-child{grid-area:1/3/6/6}.stack.stack-end>*{grid-area:3/3/4/6}.stack.stack-end>:nth-child(2){grid-area:2/2/5/5}.stack.stack-end>:first-child{grid-area:1/1/6/4}:is(.stack\!,.stack\!.stack-bottom)>*{grid-area:3/3/6/4!important}:is(.stack\!,.stack\!.stack-bottom)>:nth-child(2){grid-area:2/2/5/5!important}:is(.stack\!,.stack\!.stack-bottom)>:first-child{grid-area:1/1/4/6!important}.stack\!.stack-top>*{grid-area:1/3/4/4!important}.stack\!.stack-top>:nth-child(2){grid-area:2/2/5/5!important}.stack\!.stack-top>:first-child{grid-area:3/1/6/6!important}.stack\!.stack-start>*{grid-area:3/1/4/4!important}.stack\!.stack-start>:nth-child(2){grid-area:2/2/5/5!important}.stack\!.stack-start>:first-child{grid-area:1/3/6/6!important}.stack\!.stack-end>*{grid-area:3/3/4/6!important}.stack\!.stack-end>:nth-child(2){grid-area:2/2/5/5!important}.stack\!.stack-end>:first-child{grid-area:1/1/6/4!important}.avatar-placeholder>div{justify-content:center;align-items:center;display:flex}.badge-ghost{border-color:var(--color-base-200);background-color:var(--color-base-200);color:var(--color-base-content);background-image:none}:where(:not(ul,menu,details,.menu-title,.btn)).menu-active{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){:where(:not(ul,menu,details,.menu-title,.btn)).menu-active{outline-offset:2px;outline:2px solid #0000}}:where(:not(ul,menu,details,.menu-title,.btn)).menu-active{color:var(--menu-active-fg);background-color:var(--menu-active-bg);background-size:auto, calc(var(--noise) * 100%);background-image:none, var(--fx-noise)}.table-sm :not(thead,tfoot) tr{font-size:.75rem}.table-sm :where(th,td){padding-block:.5rem;padding-inline:.75rem}.badge-sm{--size:calc(var(--size-selector,.25rem) * 5);font-size:.75rem}.badge-xs{--size:calc(var(--size-selector,.25rem) * 4);font-size:.625rem}.otp-lg{--otp-size:calc(var(--size-field,.25rem) * 12);font-size:2rem}.alert-error{color:var(--color-error-content);--alert-border-color:var(--color-error);--alert-color:var(--color-error)}.alert-info{color:var(--color-info-content);--alert-border-color:var(--color-info);--alert-color:var(--color-info)}.alert-success{color:var(--color-success-content);--alert-border-color:var(--color-success);--alert-color:var(--color-success)}.alert-warning{color:var(--color-warning-content);--alert-border-color:var(--color-warning);--alert-color:var(--color-warning)}.progress-primary{color:var(--color-primary)}.link-hover{text-decoration-line:none}@media (hover:hover){.link-hover:hover{text-decoration-line:underline}}.input-lg{--in-size-mul:12;--font-size-min:1.125rem;--spin-my:-3}.floating-label:has(.input-lg){--top-mul:6;--font-size:1.125rem}.btn-neutral{--btn-color:var(--color-neutral);--btn-fg:var(--color-neutral-content);--btn-soft-bg:var(--color-neutral-content) 80%;--btn-rest-fg:initial}.btn-accent{--btn-color:var(--color-accent);--btn-fg:var(--color-accent-content);--btn-soft-bg:initial}.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}.btn-primary{--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content);--btn-soft-bg:initial}.btn-sm{--fontsize:.75rem;--btn-p:.75rem;--size:calc(var(--size-field,.25rem) * 8)}.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}.btn-warning{--btn-color:var(--color-warning);--btn-fg:var(--color-warning-content);--btn-soft-bg:initial}.btn-xs{--fontsize:.6875rem;--btn-p:.5rem;--size:calc(var(--size-field,.25rem) * 6)}.badge-error{--badge-color:var(--color-error);--badge-fg:var(--color-error-content)}.badge-info{--badge-color:var(--color-info);--badge-fg:var(--color-info-content)}.badge-neutral{--badge-color:var(--color-neutral);--badge-fg:var(--color-neutral-content)}.badge-success{--badge-color:var(--color-success);--badge-fg:var(--color-success-content)}.badge-warning{--badge-color:var(--color-warning);--badge-fg:var(--color-warning-content)}.input-error,.input-error:focus,.input-error:focus-within{--input-color:var(--color-error)}}@layer daisyui.l1{.btn:where(:checked:not(.filter [type=radio].btn)){--btn-color:var(--color-primary);--btn-fg:var(--color-primary-content)}@media (hover:hover){.btn:hover{--btn-bg:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:hover{color:var(--btn-fg);--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:hover{--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:hover{--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}}.btn:active:not(.btn-active){--btn-bg:var(--btn-color,var(--color-base-200));translate:0 .5px}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-bg:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 5%)}}.btn:active:not(.btn-active){color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-color,var(--color-base-200))}@supports (color:color-mix(in lab, red, red)){.btn:active:not(.btn-active){--btn-border:color-mix(in oklab, var(--btn-color,var(--color-base-200)), #000 7%)}}.btn:active:not(.btn-active){--btn-border-style:solid;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0)}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-bg:var(--btn-color,var(--color-base-200));color:var(--btn-fg,var(--color-base-content));--btn-border:var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border:color-mix(in oklab, var(--btn-bg), #000 calc(var(--depth) * 5%))}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-border-style:solid;--btn-inset:0 .5px 0 .5px oklch(100% 0 0 / calc(var(--depth) * 6%));--btn-shadow:0 3px 2px -2px var(--btn-bg), 0 4px 3px -2px var(--btn-bg)}@supports (color:color-mix(in lab, red, red)){.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){--btn-shadow:0 3px 2px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000), 0 4px 3px -2px color-mix(in oklab, var(--btn-bg) calc(var(--depth) * 30%), #0000)}}.btn:where(:checked:not(.filter [type=radio].btn),:not([type=radio],[type=checkbox]):focus-visible){isolation:isolate}.btn:focus-visible,.btn:has(:focus-visible){isolation:isolate;outline-width:2px;outline-style:solid}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible){visibility:hidden}.filter:not(:has(:checked:not(.filter-reset))) :is(.filter-reset,[type=reset]):not(:focus-visible),.filter:not(:has(:focus-visible)):has(:checked:not(.filter-reset,[type=checkbox])) :is(input,button):not(:checked,.filter-reset,[type=reset]){opacity:0;border-width:0;width:0;margin-inline:0;padding-inline:0;scale:0}.alert-soft{color:var(--alert-color,var(--color-base-content));background:var(--alert-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.alert-soft{background:color-mix(in oklab, var(--alert-color,var(--color-base-content)) 8%, var(--color-base-100))}}.alert-soft{--alert-border-color:var(--alert-color,var(--color-base-content))}@supports (color:color-mix(in lab, red, red)){.alert-soft{--alert-border-color:color-mix(in oklab, var(--alert-color,var(--color-base-content)) 10%, var(--color-base-100))}}.alert-soft{box-shadow:none;background-image:none}}@layer daisyui{.btn:is(:disabled,[disabled],[aria-disabled=true]){pointer-events:none;color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]){color:color-mix(in oklch, var(--color-base-content) 20%, #0000)}}.btn:is(:disabled,[disabled],[aria-disabled=true]){--btn-bg:#0000;--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none}.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.btn:is(:disabled,[disabled],[aria-disabled=true]):not(.btn-link,.btn-ghost){background-color:color-mix(in oklab, var(--color-base-content) 10%, transparent)}}.btn-link{--btn-bg:#0000;color:var(--btn-color,var(--color-primary));--btn-border:#0000;--btn-inset:0 0 0 0 oklch(0% 0 0/0);--btn-shadow:0 0 0 0 oklch(0% 0 0/0);background-image:none;text-decoration-line:underline}}.collapse:not(td,tr,colgroup){visibility:revert-layer}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@layer daisyui.l1.l2.l3.l4{.card figure:first-child{border-start-start-radius:inherit;border-start-end-radius:inherit;border-end-end-radius:unset;border-end-start-radius:unset;overflow:hidden}.card figure:last-child{border-start-start-radius:unset;border-start-end-radius:unset;border-end-end-radius:inherit;border-end-start-radius:inherit;overflow:hidden}.card figure{justify-content:center;align-items:center;display:flex}.card-body p{flex-grow:1}}.fixed{position:fixed}.static{position:static}.sticky{position:sticky}.z-10{z-index:10}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.my-4{margin-block:calc(var(--spacing) * 4)}.my-6{margin-block:calc(var(--spacing) * 6)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-10{margin-top:calc(var(--spacing) * 10)}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.ml-1{margin-left:var(--spacing)}.ml-2{margin-left:calc(var(--spacing) * 2)}.alert{border-width:var(--border);border-color:var(--alert-border-color,var(--color-base-200))}:root .prose{--tw-prose-body:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-body:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose{--tw-prose-headings:var(--color-base-content);--tw-prose-lead:var(--color-base-content);--tw-prose-links:var(--color-base-content);--tw-prose-bold:var(--color-base-content);--tw-prose-counters:var(--color-base-content);--tw-prose-bullets:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-bullets:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-hr:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-hr:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-quotes:var(--color-base-content);--tw-prose-quote-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-quote-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-captions:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-captions:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-code:var(--color-base-content);--tw-prose-pre-code:var(--color-neutral-content);--tw-prose-pre-bg:var(--color-neutral);--tw-prose-th-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-th-borders:color-mix(in oklab, var(--color-base-content) 50%, #0000)}}:root .prose{--tw-prose-td-borders:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-td-borders:color-mix(in oklab, var(--color-base-content) 20%, #0000)}}:root .prose{--tw-prose-kbd:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){:root .prose{--tw-prose-kbd:color-mix(in oklab, var(--color-base-content) 80%, #0000)}}:root .prose :where(code):not(pre>code){background-color:var(--color-base-200);border-radius:var(--radius-selector);border:var(--border) solid var(--color-base-300);font-weight:inherit;padding-block:.2em;padding-inline:.5em}:root .prose :where(code):not(pre>code):before,:root .prose :where(code):not(pre>code):after{display:none}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-16{height:calc(var(--spacing) * 16)}.h-56{height:calc(var(--spacing) * 56)}.h-screen{height:100vh}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-60{width:calc(var(--spacing) * 60)}.w-64{width:calc(var(--spacing) * 64)}.w-full{width:100%}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.cursor-pointer{cursor:pointer}.resize{resize:both}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-base-200>:not(:last-child)){border-color:var(--color-base-200)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-box{border-radius:var(--radius-box);border-radius:var(--radius-box)}.rounded-full{border-radius:3.40282e38px}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-base-300{border-color:var(--color-base-300)}.border-base-content,.border-base-content\/20{border-color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.border-base-content\/20{border-color:color-mix(in oklab, var(--color-base-content) 20%, transparent)}}.border-error{border-color:var(--color-error)}.border-warning{border-color:var(--color-warning)}.bg-base-100{background-color:var(--color-base-100)}.bg-base-200{background-color:var(--color-base-200)}.bg-primary{background-color:var(--color-primary)}.bg-white{background-color:var(--color-white)}.object-contain{object-fit:contain}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-6{padding-block:calc(var(--spacing) * 6)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pl-8{padding-left:calc(var(--spacing) * 8)}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-roboto{font-family:var(--font-roboto)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-base-content\/50{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/50{color:color-mix(in oklab, var(--color-base-content) 50%, transparent)}}.text-base-content\/70{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/70{color:color-mix(in oklab, var(--color-base-content) 70%, transparent)}}.text-base-content\/80{color:var(--color-base-content)}@supports (color:color-mix(in lab, red, red)){.text-base-content\/80{color:color-mix(in oklab, var(--color-base-content) 80%, transparent)}}.text-error{color:var(--color-error)}.text-primary-content{color:var(--color-primary-content)}.text-sky-500{color:var(--color-sky-500)}.text-warning{color:var(--color-warning)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.prose :where(a.btn:not(.btn-link)):not(:where([class~=not-prose],[class~=not-prose] *)){text-decoration-line:none}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}@media (min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:64rem){.lg\:block{display:block}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-400-italic.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:italic;font-weight:400;font-display:swap;src:url(../fonts/ubuntu-latin-ext-400-italic.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-500-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:500;font-display:swap;src:url(../fonts/ubuntu-latin-ext-500-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-700-normal.woff2)format("woff2");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Ubuntu;font-style:normal;font-weight:700;font-display:swap;src:url(../fonts/ubuntu-latin-ext-700-normal.woff2)format("woff2");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:100 800;font-display:swap;src:url(../fonts/jetbrains-mono-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Roboto;font-style:normal;font-weight:100 900;font-display:swap;src:url(../fonts/roboto-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-standard-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Tourney;font-style:normal;font-weight:100 900;font-stretch:75% 125%;font-display:swap;src:url(../fonts/tourney-latin-ext-standard-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}.logo{background-image:var(--logo-dark);background-position:50%;background-repeat:no-repeat;background-size:contain}@media (prefers-color-scheme:dark){.logo{background-image:var(--logo-light)}}[data-theme=light] .logo{background-image:var(--logo-dark)}[data-theme=dark] .logo{background-image:var(--logo-light)}@keyframes dropdown{0%{opacity:0}}@keyframes rotator{89.9999%,to{--first-item-position:0 0%}90%,99.9999%{--first-item-position:0 calc(var(--items) * 100%)}to{translate:0 -100%}}@keyframes radio{0%{padding:5px}50%{padding:3px}}@keyframes toast{0%{opacity:0;scale:.9}to{opacity:1;scale:1}}@keyframes skeleton{0%{background-position:150%}to{background-position:-50%}}@keyframes progress{50%{background-position-x:-115%}}@keyframes rating{0%,40%{filter:brightness(1.05)contrast(1.05);scale:1.1}}@keyframes aura{to{--aura-angle:360deg;transform:translateZ(1px)}}@keyframes aura-glow{20%,80%{opacity:.7;filter:blur(.25rem)}50%{opacity:1;filter:blur(.75rem)}}@keyframes aura-glow-after{20%,80%{opacity:.3;filter:blur(1rem)}50%{opacity:.6;filter:blur(1.5rem)}}@keyframes menu{0%{opacity:0}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false} \ No newline at end of file diff --git a/templates/_base.html b/templates/_base.html index 7bc4ab4..2dbdc5e 100644 --- a/templates/_base.html +++ b/templates/_base.html @@ -33,8 +33,14 @@ {% block extra %}{% endblock extra %} - -