diff --git a/.DS_Store b/.DS_Store index da933278..85171157 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index a4d7c12f..89fe0466 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ venv /venv ./venv -venv/ \ No newline at end of file +venv/sour \ No newline at end of file diff --git a/accounts/__pycache__/__init__.cpython-311.pyc b/accounts/__pycache__/__init__.cpython-311.pyc index 198f25e4..60b8cd42 100644 Binary files a/accounts/__pycache__/__init__.cpython-311.pyc and b/accounts/__pycache__/__init__.cpython-311.pyc differ diff --git a/accounts/__pycache__/__init__.cpython-312.pyc b/accounts/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..d4a56524 Binary files /dev/null and b/accounts/__pycache__/__init__.cpython-312.pyc differ diff --git a/accounts/__pycache__/adapters.cpython-311.pyc b/accounts/__pycache__/adapters.cpython-311.pyc index 0ec59dd3..697fea07 100644 Binary files a/accounts/__pycache__/adapters.cpython-311.pyc and b/accounts/__pycache__/adapters.cpython-311.pyc differ diff --git a/accounts/__pycache__/adapters.cpython-312.pyc b/accounts/__pycache__/adapters.cpython-312.pyc new file mode 100644 index 00000000..e52b18ef Binary files /dev/null and b/accounts/__pycache__/adapters.cpython-312.pyc differ diff --git a/accounts/__pycache__/admin.cpython-311.pyc b/accounts/__pycache__/admin.cpython-311.pyc index de6c0215..1f5f85dc 100644 Binary files a/accounts/__pycache__/admin.cpython-311.pyc and b/accounts/__pycache__/admin.cpython-311.pyc differ diff --git a/accounts/__pycache__/admin.cpython-312.pyc b/accounts/__pycache__/admin.cpython-312.pyc new file mode 100644 index 00000000..e0133594 Binary files /dev/null and b/accounts/__pycache__/admin.cpython-312.pyc differ diff --git a/accounts/__pycache__/apps.cpython-311.pyc b/accounts/__pycache__/apps.cpython-311.pyc index 018195b2..a3517fb0 100644 Binary files a/accounts/__pycache__/apps.cpython-311.pyc and b/accounts/__pycache__/apps.cpython-311.pyc differ diff --git a/accounts/__pycache__/apps.cpython-312.pyc b/accounts/__pycache__/apps.cpython-312.pyc new file mode 100644 index 00000000..45a571c7 Binary files /dev/null and b/accounts/__pycache__/apps.cpython-312.pyc differ diff --git a/accounts/__pycache__/models.cpython-311.pyc b/accounts/__pycache__/models.cpython-311.pyc index 511086e2..a5b213a9 100644 Binary files a/accounts/__pycache__/models.cpython-311.pyc and b/accounts/__pycache__/models.cpython-311.pyc differ diff --git a/accounts/__pycache__/models.cpython-312.pyc b/accounts/__pycache__/models.cpython-312.pyc new file mode 100644 index 00000000..04a23932 Binary files /dev/null and b/accounts/__pycache__/models.cpython-312.pyc differ diff --git a/accounts/__pycache__/signals.cpython-311.pyc b/accounts/__pycache__/signals.cpython-311.pyc index 329bc299..2a72af8e 100644 Binary files a/accounts/__pycache__/signals.cpython-311.pyc and b/accounts/__pycache__/signals.cpython-311.pyc differ diff --git a/accounts/__pycache__/signals.cpython-312.pyc b/accounts/__pycache__/signals.cpython-312.pyc new file mode 100644 index 00000000..b4fd878d Binary files /dev/null and b/accounts/__pycache__/signals.cpython-312.pyc differ diff --git a/accounts/__pycache__/urls.cpython-311.pyc b/accounts/__pycache__/urls.cpython-311.pyc index 63058ecf..81f0f4de 100644 Binary files a/accounts/__pycache__/urls.cpython-311.pyc and b/accounts/__pycache__/urls.cpython-311.pyc differ diff --git a/accounts/__pycache__/urls.cpython-312.pyc b/accounts/__pycache__/urls.cpython-312.pyc new file mode 100644 index 00000000..af5a1369 Binary files /dev/null and b/accounts/__pycache__/urls.cpython-312.pyc differ diff --git a/accounts/__pycache__/views.cpython-311.pyc b/accounts/__pycache__/views.cpython-311.pyc index 24856166..c7545def 100644 Binary files a/accounts/__pycache__/views.cpython-311.pyc and b/accounts/__pycache__/views.cpython-311.pyc differ diff --git a/accounts/__pycache__/views.cpython-312.pyc b/accounts/__pycache__/views.cpython-312.pyc new file mode 100644 index 00000000..a659fdbc Binary files /dev/null and b/accounts/__pycache__/views.cpython-312.pyc differ diff --git a/accounts/management/commands/__pycache__/reset_db.cpython-311.pyc b/accounts/management/commands/__pycache__/reset_db.cpython-311.pyc new file mode 100644 index 00000000..d478fae0 Binary files /dev/null and b/accounts/management/commands/__pycache__/reset_db.cpython-311.pyc differ diff --git a/accounts/management/commands/reset_db.py b/accounts/management/commands/reset_db.py new file mode 100644 index 00000000..b5445a06 --- /dev/null +++ b/accounts/management/commands/reset_db.py @@ -0,0 +1,85 @@ +from django.core.management.base import BaseCommand +from django.db import connection +from django.contrib.auth.hashers import make_password +import uuid + +class Command(BaseCommand): + help = 'Reset database and create admin user' + + def handle(self, *args, **options): + self.stdout.write('Resetting database...') + + # Drop all tables + with connection.cursor() as cursor: + cursor.execute(""" + DO $$ DECLARE + r RECORD; + BEGIN + FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = current_schema()) LOOP + EXECUTE 'DROP TABLE IF EXISTS ' || quote_ident(r.tablename) || ' CASCADE'; + END LOOP; + END $$; + """) + + # Reset sequences + cursor.execute(""" + DO $$ DECLARE + r RECORD; + BEGIN + FOR r IN (SELECT sequencename FROM pg_sequences WHERE schemaname = current_schema()) LOOP + EXECUTE 'ALTER SEQUENCE ' || quote_ident(r.sequencename) || ' RESTART WITH 1'; + END LOOP; + END $$; + """) + + self.stdout.write('All tables dropped and sequences reset.') + + # Run migrations + from django.core.management import call_command + call_command('migrate') + + self.stdout.write('Migrations applied.') + + # Create superuser using raw SQL + try: + with connection.cursor() as cursor: + # Create user + user_id = str(uuid.uuid4())[:10] + cursor.execute(""" + INSERT INTO accounts_user ( + username, password, email, is_superuser, is_staff, + is_active, date_joined, user_id, first_name, + last_name, role, is_banned, ban_reason, + theme_preference + ) VALUES ( + 'admin', %s, 'admin@thrillwiki.com', true, true, + true, NOW(), %s, '', '', 'SUPERUSER', false, '', + 'light' + ) RETURNING id; + """, [make_password('admin'), user_id]) + + user_db_id = cursor.fetchone()[0] + + # Create profile + profile_id = str(uuid.uuid4())[:10] + cursor.execute(""" + INSERT INTO accounts_userprofile ( + profile_id, display_name, pronouns, bio, + twitter, instagram, youtube, discord, + coaster_credits, dark_ride_credits, + flat_ride_credits, water_ride_credits, + user_id, avatar + ) VALUES ( + %s, 'Admin', 'they/them', 'ThrillWiki Administrator', + '', '', '', '', + 0, 0, 0, 0, + %s, '' + ); + """, [profile_id, user_db_id]) + + self.stdout.write('Superuser created.') + except Exception as e: + self.stdout.write(self.style.ERROR(f'Error creating superuser: {str(e)}')) + raise + + self.stdout.write(self.style.SUCCESS('Database reset complete.')) diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py index dd670f52..7a48d0eb 100644 --- a/accounts/migrations/0001_initial.py +++ b/accounts/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.2 on 2024-10-28 20:17 +# Generated by Django 5.1.2 on 2024-10-28 21:50 import django.contrib.auth.models import django.contrib.auth.validators @@ -13,121 +13,345 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), - ('contenttypes', '0002_remove_content_type_name'), + ("auth", "0012_alter_user_first_name_max_length"), + ("contenttypes", "0002_remove_content_type_name"), ] operations = [ migrations.CreateModel( - name='User', + 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')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('user_id', models.CharField(editable=False, help_text='Unique identifier for this user that remains constant even if the username changes', max_length=10, unique=True)), - ('first_name', models.CharField(default='', max_length=150, verbose_name='first name')), - ('last_name', models.CharField(default='', max_length=150, verbose_name='last name')), - ('role', models.CharField(choices=[('USER', 'User'), ('MODERATOR', 'Moderator'), ('ADMIN', 'Admin'), ('SUPERUSER', 'Superuser')], default='USER', max_length=10)), - ('is_banned', models.BooleanField(default=False)), - ('ban_reason', models.TextField(blank=True)), - ('ban_date', models.DateTimeField(blank=True, null=True)), - ('pending_email', models.EmailField(blank=True, max_length=254, null=True)), - ('theme_preference', models.CharField(choices=[('light', 'Light'), ('dark', 'Dark')], default='light', max_length=5)), - ('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')), + ( + "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", + ), + ), + ( + "username", + models.CharField( + error_messages={ + "unique": "A user with that username already exists." + }, + help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", + max_length=150, + unique=True, + validators=[ + django.contrib.auth.validators.UnicodeUsernameValidator() + ], + verbose_name="username", + ), + ), + ( + "email", + models.EmailField( + blank=True, max_length=254, verbose_name="email address" + ), + ), + ( + "is_staff", + models.BooleanField( + default=False, + help_text="Designates whether the user can log into this admin site.", + verbose_name="staff status", + ), + ), + ( + "is_active", + models.BooleanField( + default=True, + help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", + verbose_name="active", + ), + ), + ( + "date_joined", + models.DateTimeField( + default=django.utils.timezone.now, verbose_name="date joined" + ), + ), + ( + "user_id", + models.CharField( + editable=False, + help_text="Unique identifier for this user that remains constant even if the username changes", + max_length=10, + unique=True, + ), + ), + ( + "first_name", + models.CharField( + default="", max_length=150, verbose_name="first name" + ), + ), + ( + "last_name", + models.CharField( + default="", max_length=150, verbose_name="last name" + ), + ), + ( + "role", + models.CharField( + choices=[ + ("USER", "User"), + ("MODERATOR", "Moderator"), + ("ADMIN", "Admin"), + ("SUPERUSER", "Superuser"), + ], + default="USER", + max_length=10, + ), + ), + ("is_banned", models.BooleanField(default=False)), + ("ban_reason", models.TextField(blank=True)), + ("ban_date", models.DateTimeField(blank=True, null=True)), + ( + "pending_email", + models.EmailField(blank=True, max_length=254, null=True), + ), + ( + "theme_preference", + models.CharField( + choices=[("light", "Light"), ("dark", "Dark")], + default="light", + max_length=5, + ), + ), + ( + "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={ - 'verbose_name': 'user', - 'verbose_name_plural': 'users', - 'abstract': False, + "verbose_name": "user", + "verbose_name_plural": "users", + "abstract": False, }, managers=[ - ('objects', django.contrib.auth.models.UserManager()), + ("objects", django.contrib.auth.models.UserManager()), ], ), migrations.CreateModel( - name='EmailVerification', + name="EmailVerification", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('token', models.CharField(max_length=64, unique=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('last_sent', models.DateTimeField(auto_now_add=True)), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("token", models.CharField(max_length=64, unique=True)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("last_sent", models.DateTimeField(auto_now_add=True)), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'verbose_name': 'Email Verification', - 'verbose_name_plural': 'Email Verifications', + "verbose_name": "Email Verification", + "verbose_name_plural": "Email Verifications", }, ), migrations.CreateModel( - name='PasswordReset', + name="PasswordReset", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('token', models.CharField(max_length=64)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('expires_at', models.DateTimeField()), - ('used', models.BooleanField(default=False)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("token", models.CharField(max_length=64)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("expires_at", models.DateTimeField()), + ("used", models.BooleanField(default=False)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'verbose_name': 'Password Reset', - 'verbose_name_plural': 'Password Resets', + "verbose_name": "Password Reset", + "verbose_name_plural": "Password Resets", }, ), migrations.CreateModel( - name='TopList', + name="TopList", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=100)), - ('category', models.CharField(choices=[('RC', 'Roller Coaster'), ('DR', 'Dark Ride'), ('FR', 'Flat Ride'), ('WR', 'Water Ride'), ('PK', 'Park')], max_length=2)), - ('description', models.TextField(blank=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='top_lists', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=100)), + ( + "category", + models.CharField( + choices=[ + ("RC", "Roller Coaster"), + ("DR", "Dark Ride"), + ("FR", "Flat Ride"), + ("WR", "Water Ride"), + ("PK", "Park"), + ], + max_length=2, + ), + ), + ("description", models.TextField(blank=True)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="top_lists", + to=settings.AUTH_USER_MODEL, + ), + ), ], options={ - 'ordering': ['-updated_at'], + "ordering": ["-updated_at"], }, ), migrations.CreateModel( - name='UserProfile', + name="UserProfile", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('profile_id', models.CharField(editable=False, help_text='Unique identifier for this profile that remains constant', max_length=10, unique=True)), - ('display_name', models.CharField(help_text='This is the name that will be displayed on the site', max_length=50, unique=True)), - ('avatar', models.ImageField(blank=True, upload_to='avatars/')), - ('pronouns', models.CharField(blank=True, max_length=50)), - ('bio', models.TextField(blank=True, max_length=500)), - ('twitter', models.URLField(blank=True)), - ('instagram', models.URLField(blank=True)), - ('youtube', models.URLField(blank=True)), - ('discord', models.CharField(blank=True, max_length=100)), - ('coaster_credits', models.IntegerField(default=0)), - ('dark_ride_credits', models.IntegerField(default=0)), - ('flat_ride_credits', models.IntegerField(default=0)), - ('water_ride_credits', models.IntegerField(default=0)), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "profile_id", + models.CharField( + editable=False, + help_text="Unique identifier for this profile that remains constant", + max_length=10, + unique=True, + ), + ), + ( + "display_name", + models.CharField( + help_text="This is the name that will be displayed on the site", + max_length=50, + unique=True, + ), + ), + ("avatar", models.ImageField(blank=True, upload_to="avatars/")), + ("pronouns", models.CharField(blank=True, max_length=50)), + ("bio", models.TextField(blank=True, max_length=500)), + ("twitter", models.URLField(blank=True)), + ("instagram", models.URLField(blank=True)), + ("youtube", models.URLField(blank=True)), + ("discord", models.CharField(blank=True, max_length=100)), + ("coaster_credits", models.IntegerField(default=0)), + ("dark_ride_credits", models.IntegerField(default=0)), + ("flat_ride_credits", models.IntegerField(default=0)), + ("water_ride_credits", models.IntegerField(default=0)), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="profile", + to=settings.AUTH_USER_MODEL, + ), + ), ], ), migrations.CreateModel( - name='TopListItem', + name="TopListItem", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('object_id', models.PositiveIntegerField()), - ('rank', models.PositiveIntegerField()), - ('notes', models.TextField(blank=True)), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), - ('top_list', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='accounts.toplist')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("object_id", models.PositiveIntegerField()), + ("rank", models.PositiveIntegerField()), + ("notes", models.TextField(blank=True)), + ( + "content_type", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="contenttypes.contenttype", + ), + ), + ( + "top_list", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="items", + to="accounts.toplist", + ), + ), ], options={ - 'ordering': ['rank'], - 'unique_together': {('top_list', 'rank')}, + "ordering": ["rank"], + "unique_together": {("top_list", "rank")}, }, ), ] diff --git a/accounts/migrations/__pycache__/0001_initial.cpython-311.pyc b/accounts/migrations/__pycache__/0001_initial.cpython-311.pyc index f2a7857a..8a8b4629 100644 Binary files a/accounts/migrations/__pycache__/0001_initial.cpython-311.pyc and b/accounts/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/accounts/migrations/__pycache__/0001_initial.cpython-312.pyc b/accounts/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 00000000..89351e6f Binary files /dev/null and b/accounts/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/accounts/migrations/__pycache__/0002_update_user_fields.cpython-311.pyc b/accounts/migrations/__pycache__/0002_update_user_fields.cpython-311.pyc deleted file mode 100644 index 9bab5f61..00000000 Binary files a/accounts/migrations/__pycache__/0002_update_user_fields.cpython-311.pyc and /dev/null differ diff --git a/accounts/migrations/__pycache__/0003_add_password_reset.cpython-311.pyc b/accounts/migrations/__pycache__/0003_add_password_reset.cpython-311.pyc deleted file mode 100644 index d5c4fdd9..00000000 Binary files a/accounts/migrations/__pycache__/0003_add_password_reset.cpython-311.pyc and /dev/null differ diff --git a/accounts/migrations/__pycache__/0004_remove_passwordreset_accounts_pas_token_e8e509_idx_and_more.cpython-311.pyc b/accounts/migrations/__pycache__/0004_remove_passwordreset_accounts_pas_token_e8e509_idx_and_more.cpython-311.pyc deleted file mode 100644 index 23717e9e..00000000 Binary files a/accounts/migrations/__pycache__/0004_remove_passwordreset_accounts_pas_token_e8e509_idx_and_more.cpython-311.pyc and /dev/null differ diff --git a/accounts/migrations/__pycache__/0005_add_readonly_ids.cpython-311.pyc b/accounts/migrations/__pycache__/0005_add_readonly_ids.cpython-311.pyc deleted file mode 100644 index 4ed12197..00000000 Binary files a/accounts/migrations/__pycache__/0005_add_readonly_ids.cpython-311.pyc and /dev/null differ diff --git a/accounts/migrations/__pycache__/__init__.cpython-311.pyc b/accounts/migrations/__pycache__/__init__.cpython-311.pyc index 4273f8af..26f6cabe 100644 Binary files a/accounts/migrations/__pycache__/__init__.cpython-311.pyc and b/accounts/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/accounts/migrations/__pycache__/__init__.cpython-312.pyc b/accounts/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..f96dc728 Binary files /dev/null and b/accounts/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/assets/css/src/input.css b/assets/css/src/input.css new file mode 100644 index 00000000..ed73972d --- /dev/null +++ b/assets/css/src/input.css @@ -0,0 +1,120 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer components { + .btn-primary { + @apply inline-flex items-center px-6 py-2.5 border border-transparent rounded-full shadow-md text-sm font-medium text-white bg-gradient-to-r from-primary to-secondary hover:from-primary/90 hover:to-secondary/90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary/50 transform hover:scale-105 transition-all; + } + + .btn-secondary { + @apply inline-flex items-center px-6 py-2.5 border border-gray-200 dark:border-gray-700 rounded-full shadow-md text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary/50 transform hover:scale-105 transition-all; + } + + .nav-link { + @apply flex items-center px-6 py-2.5 rounded-lg font-medium text-gray-700 dark:text-gray-200 hover:bg-primary/10 dark:hover:bg-primary/20 hover:text-primary dark:hover:text-primary transition-all border border-transparent hover:border-primary/20 dark:hover:border-primary/30; + } + + .nav-link i { + @apply mr-3 text-lg text-gray-500 transition-colors dark:text-gray-400; + } + + .nav-link:hover i { + @apply text-primary; + } + + .mobile-nav-link { + @apply flex items-center px-6 py-3 text-gray-700 transition-all border border-transparent rounded-lg dark:text-gray-200 hover:bg-primary/10 dark:hover:bg-primary/20 hover:text-primary dark:hover:text-primary hover:border-primary/20 dark:hover:border-primary/30; + } + + .mobile-nav-link i { + @apply mr-3 text-lg text-gray-500 dark:text-gray-400; + } + + .menu-item { + @apply flex items-center w-full px-4 py-3 text-sm text-gray-700 transition-all dark:text-gray-200 hover:bg-primary/10 dark:hover:bg-primary/20 hover:text-primary dark:hover:text-primary first:rounded-t-lg last:rounded-b-lg; + } + + .menu-item i { + @apply mr-3 text-base text-gray-500 dark:text-gray-400; + } + + .form-input { + @apply w-full px-4 py-3 text-gray-900 transition-all border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 bg-white/70 dark:bg-gray-800/70 backdrop-blur-sm dark:text-white focus:ring-2 focus:ring-primary/50 focus:border-primary; + } + + .form-label { + @apply block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5; + } + + .form-hint { + @apply mt-2 space-y-1 text-sm text-gray-500 dark:text-gray-400; + } + + .form-error { + @apply mt-2 text-sm text-red-600 dark:text-red-400; + } + + /* Auth Card Styles */ + .auth-card { + @apply w-full max-w-md p-8 mx-auto border shadow-xl bg-white/90 dark:bg-gray-800/90 rounded-2xl backdrop-blur-sm border-gray-200/50 dark:border-gray-700/50; + } + + .auth-title { + @apply mb-8 text-2xl font-bold text-center text-transparent bg-gradient-to-r from-primary to-secondary bg-clip-text; + } + + .auth-divider { + @apply relative my-6 text-center; + } + + .auth-divider::before, + .auth-divider::after { + @apply absolute top-1/2 w-1/3 border-t border-gray-200 dark:border-gray-700 content-['']; + } + + .auth-divider::before { + @apply left-0; + } + + .auth-divider::after { + @apply right-0; + } + + .auth-divider span { + @apply px-4 text-sm text-gray-500 dark:text-gray-400 bg-white/90 dark:bg-gray-800/90; + } + + /* Social Login Buttons */ + .btn-social { + @apply w-full flex items-center justify-center px-6 py-3 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary/50 transform hover:scale-[1.02] transition-all mb-3; + } + + .btn-discord { + @apply bg-[#5865F2] hover:bg-[#4752C4] text-white border-transparent shadow-[#5865F2]/20; + } + + .btn-google { + @apply text-gray-700 bg-white border-gray-200 hover:bg-gray-50 shadow-gray-200/50 dark:shadow-gray-900/50; + } + + .alert { + @apply p-4 mb-4 shadow-lg rounded-xl backdrop-blur-sm; + } + + .alert-success { + @apply text-green-800 border border-green-200 bg-green-100/90 dark:bg-green-800/30 dark:text-green-100 dark:border-green-700; + } + + .alert-error { + @apply text-red-800 border border-red-200 bg-red-100/90 dark:bg-red-800/30 dark:text-red-100 dark:border-red-700; + } + + .alert-warning { + @apply text-yellow-800 border border-yellow-200 bg-yellow-100/90 dark:bg-yellow-800/30 dark:text-yellow-100 dark:border-yellow-700; + } + + .alert-info { + @apply text-blue-800 border border-blue-200 bg-blue-100/90 dark:bg-blue-800/30 dark:text-blue-100 dark:border-blue-700; + } +} diff --git a/companies/__pycache__/__init__.cpython-311.pyc b/companies/__pycache__/__init__.cpython-311.pyc index 0cd0abc5..7e9e42f6 100644 Binary files a/companies/__pycache__/__init__.cpython-311.pyc and b/companies/__pycache__/__init__.cpython-311.pyc differ diff --git a/companies/__pycache__/__init__.cpython-312.pyc b/companies/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..8004fd30 Binary files /dev/null and b/companies/__pycache__/__init__.cpython-312.pyc differ diff --git a/companies/__pycache__/admin.cpython-311.pyc b/companies/__pycache__/admin.cpython-311.pyc index af8ff319..248f63f0 100644 Binary files a/companies/__pycache__/admin.cpython-311.pyc and b/companies/__pycache__/admin.cpython-311.pyc differ diff --git a/companies/__pycache__/admin.cpython-312.pyc b/companies/__pycache__/admin.cpython-312.pyc new file mode 100644 index 00000000..09543aff Binary files /dev/null and b/companies/__pycache__/admin.cpython-312.pyc differ diff --git a/companies/__pycache__/apps.cpython-311.pyc b/companies/__pycache__/apps.cpython-311.pyc index f0a3e108..0e4f2747 100644 Binary files a/companies/__pycache__/apps.cpython-311.pyc and b/companies/__pycache__/apps.cpython-311.pyc differ diff --git a/companies/__pycache__/apps.cpython-312.pyc b/companies/__pycache__/apps.cpython-312.pyc new file mode 100644 index 00000000..d5f34eaa Binary files /dev/null and b/companies/__pycache__/apps.cpython-312.pyc differ diff --git a/companies/__pycache__/models.cpython-311.pyc b/companies/__pycache__/models.cpython-311.pyc index d98f02be..bebbf074 100644 Binary files a/companies/__pycache__/models.cpython-311.pyc and b/companies/__pycache__/models.cpython-311.pyc differ diff --git a/companies/__pycache__/models.cpython-312.pyc b/companies/__pycache__/models.cpython-312.pyc new file mode 100644 index 00000000..103ea70d Binary files /dev/null and b/companies/__pycache__/models.cpython-312.pyc differ diff --git a/companies/__pycache__/signals.cpython-311.pyc b/companies/__pycache__/signals.cpython-311.pyc index 89670d86..1e2aa6db 100644 Binary files a/companies/__pycache__/signals.cpython-311.pyc and b/companies/__pycache__/signals.cpython-311.pyc differ diff --git a/companies/__pycache__/signals.cpython-312.pyc b/companies/__pycache__/signals.cpython-312.pyc new file mode 100644 index 00000000..0aa4c1b2 Binary files /dev/null and b/companies/__pycache__/signals.cpython-312.pyc differ diff --git a/companies/__pycache__/urls.cpython-311.pyc b/companies/__pycache__/urls.cpython-311.pyc index 53f62cbf..a4a100ce 100644 Binary files a/companies/__pycache__/urls.cpython-311.pyc and b/companies/__pycache__/urls.cpython-311.pyc differ diff --git a/companies/__pycache__/urls.cpython-312.pyc b/companies/__pycache__/urls.cpython-312.pyc new file mode 100644 index 00000000..e8162d78 Binary files /dev/null and b/companies/__pycache__/urls.cpython-312.pyc differ diff --git a/companies/__pycache__/views.cpython-311.pyc b/companies/__pycache__/views.cpython-311.pyc index 47152e2a..609dd24d 100644 Binary files a/companies/__pycache__/views.cpython-311.pyc and b/companies/__pycache__/views.cpython-311.pyc differ diff --git a/companies/__pycache__/views.cpython-312.pyc b/companies/__pycache__/views.cpython-312.pyc new file mode 100644 index 00000000..6a0d5223 Binary files /dev/null and b/companies/__pycache__/views.cpython-312.pyc differ diff --git a/companies/admin.py b/companies/admin.py index a6169206..ac3be2e9 100644 --- a/companies/admin.py +++ b/companies/admin.py @@ -4,14 +4,14 @@ from .models import Company, Manufacturer @admin.register(Company) class CompanyAdmin(SimpleHistoryAdmin): - list_display = ('name', 'headquarters', 'website', 'created_at') + list_display = ('id', 'name', 'headquarters', 'website', 'created_at') search_fields = ('name', 'headquarters', 'description') prepopulated_fields = {'slug': ('name',)} readonly_fields = ('created_at', 'updated_at') @admin.register(Manufacturer) class ManufacturerAdmin(SimpleHistoryAdmin): - list_display = ('name', 'headquarters', 'website', 'created_at') + list_display = ('id', 'name', 'headquarters', 'website', 'created_at') search_fields = ('name', 'headquarters', 'description') prepopulated_fields = {'slug': ('name',)} readonly_fields = ('created_at', 'updated_at') diff --git a/companies/migrations/0002_company_total_parks_company_total_rides_and_more.py b/companies/migrations/0002_stats_fields.py similarity index 97% rename from companies/migrations/0002_company_total_parks_company_total_rides_and_more.py rename to companies/migrations/0002_stats_fields.py index b06b687e..86585c78 100644 --- a/companies/migrations/0002_company_total_parks_company_total_rides_and_more.py +++ b/companies/migrations/0002_stats_fields.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.2 on 2024-10-28 20:25 +# Generated manually from django.db import migrations, models @@ -31,22 +31,22 @@ class Migration(migrations.Migration): field=models.PositiveIntegerField(default=0), ), migrations.AddField( - model_name='historicalmanufacturer', + model_name='manufacturer', name='total_rides', field=models.PositiveIntegerField(default=0), ), migrations.AddField( - model_name='historicalmanufacturer', + model_name='manufacturer', name='total_roller_coasters', field=models.PositiveIntegerField(default=0), ), migrations.AddField( - model_name='manufacturer', + model_name='historicalmanufacturer', name='total_rides', field=models.PositiveIntegerField(default=0), ), migrations.AddField( - model_name='manufacturer', + model_name='historicalmanufacturer', name='total_roller_coasters', field=models.PositiveIntegerField(default=0), ), diff --git a/companies/migrations/0003_remove_total_parks.py b/companies/migrations/0003_remove_total_parks.py new file mode 100644 index 00000000..bbfb1ed2 --- /dev/null +++ b/companies/migrations/0003_remove_total_parks.py @@ -0,0 +1,13 @@ +from django.db import migrations + +class Migration(migrations.Migration): + dependencies = [ + ('companies', '0002_stats_fields'), + ] + + operations = [ + migrations.RemoveField( + model_name='company', + name='total_parks', + ), + ] diff --git a/companies/migrations/0004_add_total_parks.py b/companies/migrations/0004_add_total_parks.py new file mode 100644 index 00000000..57fce622 --- /dev/null +++ b/companies/migrations/0004_add_total_parks.py @@ -0,0 +1,14 @@ +from django.db import migrations, models + +class Migration(migrations.Migration): + dependencies = [ + ('companies', '0003_remove_total_parks'), + ] + + operations = [ + migrations.AddField( + model_name='company', + name='total_parks', + field=models.PositiveIntegerField(default=0), + ), + ] diff --git a/companies/migrations/__pycache__/0001_initial.cpython-311.pyc b/companies/migrations/__pycache__/0001_initial.cpython-311.pyc index 620946e1..b84640c0 100644 Binary files a/companies/migrations/__pycache__/0001_initial.cpython-311.pyc and b/companies/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/companies/migrations/__pycache__/0001_initial.cpython-312.pyc b/companies/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 00000000..15d9dae8 Binary files /dev/null and b/companies/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/companies/migrations/__pycache__/0002_company_total_parks_company_total_rides_and_more.cpython-311.pyc b/companies/migrations/__pycache__/0002_company_total_parks_company_total_rides_and_more.cpython-311.pyc index ce62a5d1..4eda3e64 100644 Binary files a/companies/migrations/__pycache__/0002_company_total_parks_company_total_rides_and_more.cpython-311.pyc and b/companies/migrations/__pycache__/0002_company_total_parks_company_total_rides_and_more.cpython-311.pyc differ diff --git a/companies/migrations/__pycache__/0002_stats_fields.cpython-311.pyc b/companies/migrations/__pycache__/0002_stats_fields.cpython-311.pyc new file mode 100644 index 00000000..f23431f6 Binary files /dev/null and b/companies/migrations/__pycache__/0002_stats_fields.cpython-311.pyc differ diff --git a/companies/migrations/__pycache__/0002_stats_fields.cpython-312.pyc b/companies/migrations/__pycache__/0002_stats_fields.cpython-312.pyc new file mode 100644 index 00000000..7935fd44 Binary files /dev/null and b/companies/migrations/__pycache__/0002_stats_fields.cpython-312.pyc differ diff --git a/companies/migrations/__pycache__/0003_remove_total_parks.cpython-311.pyc b/companies/migrations/__pycache__/0003_remove_total_parks.cpython-311.pyc new file mode 100644 index 00000000..d4209608 Binary files /dev/null and b/companies/migrations/__pycache__/0003_remove_total_parks.cpython-311.pyc differ diff --git a/companies/migrations/__pycache__/0003_remove_total_parks.cpython-312.pyc b/companies/migrations/__pycache__/0003_remove_total_parks.cpython-312.pyc new file mode 100644 index 00000000..3c298d02 Binary files /dev/null and b/companies/migrations/__pycache__/0003_remove_total_parks.cpython-312.pyc differ diff --git a/companies/migrations/__pycache__/0004_add_total_parks.cpython-311.pyc b/companies/migrations/__pycache__/0004_add_total_parks.cpython-311.pyc new file mode 100644 index 00000000..00040ad9 Binary files /dev/null and b/companies/migrations/__pycache__/0004_add_total_parks.cpython-311.pyc differ diff --git a/companies/migrations/__pycache__/0004_add_total_parks.cpython-312.pyc b/companies/migrations/__pycache__/0004_add_total_parks.cpython-312.pyc new file mode 100644 index 00000000..b969bc71 Binary files /dev/null and b/companies/migrations/__pycache__/0004_add_total_parks.cpython-312.pyc differ diff --git a/companies/migrations/__pycache__/__init__.cpython-311.pyc b/companies/migrations/__pycache__/__init__.cpython-311.pyc index 59a54cc0..b2cf3be8 100644 Binary files a/companies/migrations/__pycache__/__init__.cpython-311.pyc and b/companies/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/companies/migrations/__pycache__/__init__.cpython-312.pyc b/companies/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..2bd391c2 Binary files /dev/null and b/companies/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/core/__pycache__/__init__.cpython-311.pyc b/core/__pycache__/__init__.cpython-311.pyc index f3118674..f9eb6c31 100644 Binary files a/core/__pycache__/__init__.cpython-311.pyc and b/core/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/__pycache__/__init__.cpython-312.pyc b/core/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..a6113216 Binary files /dev/null and b/core/__pycache__/__init__.cpython-312.pyc differ diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index e67ad06f..c0701156 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/admin.cpython-312.pyc b/core/__pycache__/admin.cpython-312.pyc new file mode 100644 index 00000000..52c56b60 Binary files /dev/null and b/core/__pycache__/admin.cpython-312.pyc differ diff --git a/core/__pycache__/apps.cpython-311.pyc b/core/__pycache__/apps.cpython-311.pyc index a335ca0a..ff82bbd8 100644 Binary files a/core/__pycache__/apps.cpython-311.pyc and b/core/__pycache__/apps.cpython-311.pyc differ diff --git a/core/__pycache__/apps.cpython-312.pyc b/core/__pycache__/apps.cpython-312.pyc new file mode 100644 index 00000000..b5d14627 Binary files /dev/null and b/core/__pycache__/apps.cpython-312.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 807bc00f..08bd20f8 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-312.pyc b/core/__pycache__/models.cpython-312.pyc new file mode 100644 index 00000000..b4b571a1 Binary files /dev/null and b/core/__pycache__/models.cpython-312.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index af48e1d2..24456149 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-312.pyc b/core/__pycache__/views.cpython-312.pyc new file mode 100644 index 00000000..d3f05fea Binary files /dev/null and b/core/__pycache__/views.cpython-312.pyc differ diff --git a/core/migrations/__pycache__/0001_initial.cpython-311.pyc b/core/migrations/__pycache__/0001_initial.cpython-311.pyc index da987705..4e5a67b9 100644 Binary files a/core/migrations/__pycache__/0001_initial.cpython-311.pyc and b/core/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0001_initial.cpython-312.pyc b/core/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 00000000..07214e14 Binary files /dev/null and b/core/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/core/migrations/__pycache__/__init__.cpython-311.pyc b/core/migrations/__pycache__/__init__.cpython-311.pyc index 8e3b021b..834cd77d 100644 Binary files a/core/migrations/__pycache__/__init__.cpython-311.pyc and b/core/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/__init__.cpython-312.pyc b/core/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..0c9c3c18 Binary files /dev/null and b/core/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/email_service/__pycache__/__init__.cpython-311.pyc b/email_service/__pycache__/__init__.cpython-311.pyc index a45323cb..b73b36b6 100644 Binary files a/email_service/__pycache__/__init__.cpython-311.pyc and b/email_service/__pycache__/__init__.cpython-311.pyc differ diff --git a/email_service/__pycache__/__init__.cpython-312.pyc b/email_service/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..52aef96b Binary files /dev/null and b/email_service/__pycache__/__init__.cpython-312.pyc differ diff --git a/email_service/__pycache__/admin.cpython-311.pyc b/email_service/__pycache__/admin.cpython-311.pyc index 895e3ab1..a8211637 100644 Binary files a/email_service/__pycache__/admin.cpython-311.pyc and b/email_service/__pycache__/admin.cpython-311.pyc differ diff --git a/email_service/__pycache__/admin.cpython-312.pyc b/email_service/__pycache__/admin.cpython-312.pyc new file mode 100644 index 00000000..3b6e3097 Binary files /dev/null and b/email_service/__pycache__/admin.cpython-312.pyc differ diff --git a/email_service/__pycache__/apps.cpython-311.pyc b/email_service/__pycache__/apps.cpython-311.pyc index 7a17674b..ebed18da 100644 Binary files a/email_service/__pycache__/apps.cpython-311.pyc and b/email_service/__pycache__/apps.cpython-311.pyc differ diff --git a/email_service/__pycache__/apps.cpython-312.pyc b/email_service/__pycache__/apps.cpython-312.pyc new file mode 100644 index 00000000..d7e6f400 Binary files /dev/null and b/email_service/__pycache__/apps.cpython-312.pyc differ diff --git a/email_service/__pycache__/models.cpython-311.pyc b/email_service/__pycache__/models.cpython-311.pyc index 50f67455..8af8464d 100644 Binary files a/email_service/__pycache__/models.cpython-311.pyc and b/email_service/__pycache__/models.cpython-311.pyc differ diff --git a/email_service/__pycache__/models.cpython-312.pyc b/email_service/__pycache__/models.cpython-312.pyc new file mode 100644 index 00000000..b5e582e8 Binary files /dev/null and b/email_service/__pycache__/models.cpython-312.pyc differ diff --git a/email_service/__pycache__/services.cpython-311.pyc b/email_service/__pycache__/services.cpython-311.pyc index ef592198..88155807 100644 Binary files a/email_service/__pycache__/services.cpython-311.pyc and b/email_service/__pycache__/services.cpython-311.pyc differ diff --git a/email_service/__pycache__/services.cpython-312.pyc b/email_service/__pycache__/services.cpython-312.pyc new file mode 100644 index 00000000..7479f935 Binary files /dev/null and b/email_service/__pycache__/services.cpython-312.pyc differ diff --git a/email_service/migrations/__pycache__/0001_initial.cpython-311.pyc b/email_service/migrations/__pycache__/0001_initial.cpython-311.pyc index 797be11b..13f5c8cc 100644 Binary files a/email_service/migrations/__pycache__/0001_initial.cpython-311.pyc and b/email_service/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/email_service/migrations/__pycache__/0001_initial.cpython-312.pyc b/email_service/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 00000000..02adc10a Binary files /dev/null and b/email_service/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/email_service/migrations/__pycache__/__init__.cpython-311.pyc b/email_service/migrations/__pycache__/__init__.cpython-311.pyc index 09b6fd61..c7ff3b9a 100644 Binary files a/email_service/migrations/__pycache__/__init__.cpython-311.pyc and b/email_service/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/email_service/migrations/__pycache__/__init__.cpython-312.pyc b/email_service/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..88d49355 Binary files /dev/null and b/email_service/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/media/__pycache__/__init__.cpython-311.pyc b/media/__pycache__/__init__.cpython-311.pyc index 77164fe6..c5b65526 100644 Binary files a/media/__pycache__/__init__.cpython-311.pyc and b/media/__pycache__/__init__.cpython-311.pyc differ diff --git a/media/__pycache__/__init__.cpython-312.pyc b/media/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..b302c4b2 Binary files /dev/null and b/media/__pycache__/__init__.cpython-312.pyc differ diff --git a/media/__pycache__/admin.cpython-311.pyc b/media/__pycache__/admin.cpython-311.pyc index 2c599875..669ce76c 100644 Binary files a/media/__pycache__/admin.cpython-311.pyc and b/media/__pycache__/admin.cpython-311.pyc differ diff --git a/media/__pycache__/admin.cpython-312.pyc b/media/__pycache__/admin.cpython-312.pyc new file mode 100644 index 00000000..fb754fc3 Binary files /dev/null and b/media/__pycache__/admin.cpython-312.pyc differ diff --git a/media/__pycache__/apps.cpython-311.pyc b/media/__pycache__/apps.cpython-311.pyc index f6b6590d..8226f8b1 100644 Binary files a/media/__pycache__/apps.cpython-311.pyc and b/media/__pycache__/apps.cpython-311.pyc differ diff --git a/media/__pycache__/apps.cpython-312.pyc b/media/__pycache__/apps.cpython-312.pyc new file mode 100644 index 00000000..e06c3b81 Binary files /dev/null and b/media/__pycache__/apps.cpython-312.pyc differ diff --git a/media/__pycache__/models.cpython-311.pyc b/media/__pycache__/models.cpython-311.pyc index fb448c0d..a2b07ce1 100644 Binary files a/media/__pycache__/models.cpython-311.pyc and b/media/__pycache__/models.cpython-311.pyc differ diff --git a/media/__pycache__/models.cpython-312.pyc b/media/__pycache__/models.cpython-312.pyc new file mode 100644 index 00000000..642149ba Binary files /dev/null and b/media/__pycache__/models.cpython-312.pyc differ diff --git a/media/migrations/__pycache__/0001_initial.cpython-311.pyc b/media/migrations/__pycache__/0001_initial.cpython-311.pyc index bff55197..9d08367f 100644 Binary files a/media/migrations/__pycache__/0001_initial.cpython-311.pyc and b/media/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/media/migrations/__pycache__/0001_initial.cpython-312.pyc b/media/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 00000000..edcb46eb Binary files /dev/null and b/media/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/media/migrations/__pycache__/__init__.cpython-311.pyc b/media/migrations/__pycache__/__init__.cpython-311.pyc index 1a445eaf..efff5312 100644 Binary files a/media/migrations/__pycache__/__init__.cpython-311.pyc and b/media/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/media/migrations/__pycache__/__init__.cpython-312.pyc b/media/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..7a6116ed Binary files /dev/null and b/media/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/parks/__pycache__/__init__.cpython-311.pyc b/parks/__pycache__/__init__.cpython-311.pyc index ced9e454..589c3763 100644 Binary files a/parks/__pycache__/__init__.cpython-311.pyc and b/parks/__pycache__/__init__.cpython-311.pyc differ diff --git a/parks/__pycache__/__init__.cpython-312.pyc b/parks/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..91a2dbaf Binary files /dev/null and b/parks/__pycache__/__init__.cpython-312.pyc differ diff --git a/parks/__pycache__/admin.cpython-311.pyc b/parks/__pycache__/admin.cpython-311.pyc index c7f19b51..f0e57c7f 100644 Binary files a/parks/__pycache__/admin.cpython-311.pyc and b/parks/__pycache__/admin.cpython-311.pyc differ diff --git a/parks/__pycache__/admin.cpython-312.pyc b/parks/__pycache__/admin.cpython-312.pyc new file mode 100644 index 00000000..34f175df Binary files /dev/null and b/parks/__pycache__/admin.cpython-312.pyc differ diff --git a/parks/__pycache__/apps.cpython-311.pyc b/parks/__pycache__/apps.cpython-311.pyc index 63c7aae7..e242a989 100644 Binary files a/parks/__pycache__/apps.cpython-311.pyc and b/parks/__pycache__/apps.cpython-311.pyc differ diff --git a/parks/__pycache__/apps.cpython-312.pyc b/parks/__pycache__/apps.cpython-312.pyc new file mode 100644 index 00000000..35edef8d Binary files /dev/null and b/parks/__pycache__/apps.cpython-312.pyc differ diff --git a/parks/__pycache__/models.cpython-311.pyc b/parks/__pycache__/models.cpython-311.pyc index d82cffde..8fc0d415 100644 Binary files a/parks/__pycache__/models.cpython-311.pyc and b/parks/__pycache__/models.cpython-311.pyc differ diff --git a/parks/__pycache__/models.cpython-312.pyc b/parks/__pycache__/models.cpython-312.pyc new file mode 100644 index 00000000..da0e11bd Binary files /dev/null and b/parks/__pycache__/models.cpython-312.pyc differ diff --git a/parks/__pycache__/signals.cpython-311.pyc b/parks/__pycache__/signals.cpython-311.pyc index 45431e29..c944e2d8 100644 Binary files a/parks/__pycache__/signals.cpython-311.pyc and b/parks/__pycache__/signals.cpython-311.pyc differ diff --git a/parks/__pycache__/signals.cpython-312.pyc b/parks/__pycache__/signals.cpython-312.pyc new file mode 100644 index 00000000..d46b9eef Binary files /dev/null and b/parks/__pycache__/signals.cpython-312.pyc differ diff --git a/parks/__pycache__/urls.cpython-311.pyc b/parks/__pycache__/urls.cpython-311.pyc index 150c8dfa..5578de9e 100644 Binary files a/parks/__pycache__/urls.cpython-311.pyc and b/parks/__pycache__/urls.cpython-311.pyc differ diff --git a/parks/__pycache__/urls.cpython-312.pyc b/parks/__pycache__/urls.cpython-312.pyc new file mode 100644 index 00000000..83596390 Binary files /dev/null and b/parks/__pycache__/urls.cpython-312.pyc differ diff --git a/parks/__pycache__/views.cpython-311.pyc b/parks/__pycache__/views.cpython-311.pyc index 959d8d64..eae60237 100644 Binary files a/parks/__pycache__/views.cpython-311.pyc and b/parks/__pycache__/views.cpython-311.pyc differ diff --git a/parks/__pycache__/views.cpython-312.pyc b/parks/__pycache__/views.cpython-312.pyc new file mode 100644 index 00000000..87a8b9f9 Binary files /dev/null and b/parks/__pycache__/views.cpython-312.pyc differ diff --git a/parks/admin.py b/parks/admin.py index bad2124b..faebaac1 100644 --- a/parks/admin.py +++ b/parks/admin.py @@ -8,7 +8,7 @@ class ParkAdmin(SimpleHistoryAdmin): list_filter = ('status', 'owner') search_fields = ('name', 'location', 'description') prepopulated_fields = {'slug': ('name',)} - readonly_fields = ('created_at', 'updated_at') + readonly_fields = ('id', 'created_at', 'updated_at') @admin.register(ParkArea) class ParkAreaAdmin(SimpleHistoryAdmin): @@ -16,4 +16,4 @@ class ParkAreaAdmin(SimpleHistoryAdmin): list_filter = ('park',) search_fields = ('name', 'description') prepopulated_fields = {'slug': ('name',)} - readonly_fields = ('created_at', 'updated_at') + readonly_fields = ('id', 'created_at', 'updated_at') diff --git a/parks/management/commands/__pycache__/seed_data.cpython-311.pyc b/parks/management/commands/__pycache__/seed_data.cpython-311.pyc new file mode 100644 index 00000000..71ab1da1 Binary files /dev/null and b/parks/management/commands/__pycache__/seed_data.cpython-311.pyc differ diff --git a/parks/management/commands/seed_data.py b/parks/management/commands/seed_data.py index 9b3b0693..9c2c64da 100644 --- a/parks/management/commands/seed_data.py +++ b/parks/management/commands/seed_data.py @@ -1,12 +1,14 @@ import os import json import random +import uuid from datetime import datetime from django.core.management.base import BaseCommand -from django.contrib.auth import get_user_model +from django.contrib.auth.hashers import make_password from django.core.files import File from django.utils.text import slugify from django.contrib.contenttypes.models import ContentType +from django.db import connection from faker import Faker import requests from io import BytesIO @@ -42,70 +44,68 @@ class Command(BaseCommand): def create_users(self, count): self.stdout.write('Creating users...') users = [] + + # Get existing admin user + admin_user = User.objects.get(username='admin') + users.append(admin_user) + self.stdout.write('Added existing admin user') + + # Create regular users using raw SQL roles = ['USER'] * 20 + ['MODERATOR'] * 3 + ['ADMIN'] * 2 - - # Create or get superuser - try: - superuser = User.objects.get(username='admin') - self.stdout.write('Superuser already exists') - except User.DoesNotExist: - superuser = User.objects.create_superuser( - username='admin', - email='admin@thrillwiki.com', - password='admin', - role='SUPERUSER' - ) - UserProfile.objects.create( - user=superuser, - display_name='Admin', - pronouns='they/them', - bio='ThrillWiki Administrator' - ) - self.stdout.write('Created superuser') - - users.append(superuser) - - # Delete existing non-superuser users if any - User.objects.exclude(username='admin').delete() - self.stdout.write('Deleted existing users') - - for _ in range(count): - username = fake.user_name() - while User.objects.filter(username=username).exists(): + with connection.cursor() as cursor: + for _ in range(count): + # Create user username = fake.user_name() - - user = User.objects.create_user( - username=username, - email=fake.email(), - password='password123', - role=random.choice(roles) - ) - - # Create user profile - profile = UserProfile.objects.create( - user=user, - display_name=fake.name(), - pronouns=random.choice(['he/him', 'she/her', 'they/them', '']), - bio=fake.text(max_nb_chars=200), - twitter=fake.url() if random.choice([True, False]) else '', - instagram=fake.url() if random.choice([True, False]) else '', - youtube=fake.url() if random.choice([True, False]) else '', - discord=fake.user_name() if random.choice([True, False]) else '', - coaster_credits=random.randint(0, 500), - dark_ride_credits=random.randint(0, 200), - flat_ride_credits=random.randint(0, 300), - water_ride_credits=random.randint(0, 100) - ) - - # Add avatar - img_url = f'https://picsum.photos/200/200?random={fake.random_number(5)}' - filename, file = self.download_and_save_image(img_url, 'avatar') - if filename and file: - profile.avatar.save(filename, file, save=True) - - users.append(user) - self.stdout.write(f'Created user: {username}') - + while User.objects.filter(username=username).exists(): + username = fake.user_name() + + user_id = str(uuid.uuid4())[:10] + cursor.execute(""" + INSERT INTO accounts_user ( + username, password, email, is_superuser, is_staff, + is_active, date_joined, user_id, first_name, + last_name, role, is_banned, ban_reason, + theme_preference + ) VALUES ( + %s, %s, %s, false, false, + true, NOW(), %s, '', '', + %s, false, '', 'light' + ) RETURNING id; + """, [username, make_password('password123'), fake.email(), user_id, random.choice(roles)]) + + user_db_id = cursor.fetchone()[0] + + # Create profile + profile_id = str(uuid.uuid4())[:10] + display_name = f"{fake.first_name()}_{fake.last_name()}_{fake.random_number(digits=4)}" + cursor.execute(""" + INSERT INTO accounts_userprofile ( + profile_id, display_name, pronouns, bio, + twitter, instagram, youtube, discord, + coaster_credits, dark_ride_credits, + flat_ride_credits, water_ride_credits, + user_id, avatar + ) VALUES ( + %s, %s, %s, %s, + %s, %s, %s, %s, + %s, %s, %s, %s, + %s, '' + ); + """, [ + profile_id, display_name, random.choice(['he/him', 'she/her', 'they/them', '']), + fake.text(max_nb_chars=200), + fake.url() if random.choice([True, False]) else '', + fake.url() if random.choice([True, False]) else '', + fake.url() if random.choice([True, False]) else '', + fake.user_name() if random.choice([True, False]) else '', + random.randint(0, 500), random.randint(0, 200), + random.randint(0, 300), random.randint(0, 100), + user_db_id + ]) + + users.append(User.objects.get(id=user_db_id)) + self.stdout.write(f'Created user: {username}') + return users def create_companies(self): diff --git a/parks/migrations/0002_add_country_field.py b/parks/migrations/0002_add_country_field.py new file mode 100644 index 00000000..c3368472 --- /dev/null +++ b/parks/migrations/0002_add_country_field.py @@ -0,0 +1,25 @@ +# Generated manually + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('parks', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='park', + name='country', + field=models.CharField(max_length=2, default='US', help_text='Two-letter country code (ISO 3166-1 alpha-2)'), + preserve_default=False, + ), + migrations.AddField( + model_name='historicalpark', + name='country', + field=models.CharField(max_length=2, default='US', help_text='Two-letter country code (ISO 3166-1 alpha-2)'), + preserve_default=False, + ), + ] diff --git a/parks/migrations/__pycache__/0001_initial.cpython-311.pyc b/parks/migrations/__pycache__/0001_initial.cpython-311.pyc index 84939235..2dd8faf8 100644 Binary files a/parks/migrations/__pycache__/0001_initial.cpython-311.pyc and b/parks/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/parks/migrations/__pycache__/0001_initial.cpython-312.pyc b/parks/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 00000000..3599b5af Binary files /dev/null and b/parks/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/parks/migrations/__pycache__/0002_add_country_field.cpython-311.pyc b/parks/migrations/__pycache__/0002_add_country_field.cpython-311.pyc new file mode 100644 index 00000000..2280f731 Binary files /dev/null and b/parks/migrations/__pycache__/0002_add_country_field.cpython-311.pyc differ diff --git a/parks/migrations/__pycache__/0002_add_country_field.cpython-312.pyc b/parks/migrations/__pycache__/0002_add_country_field.cpython-312.pyc new file mode 100644 index 00000000..f9985088 Binary files /dev/null and b/parks/migrations/__pycache__/0002_add_country_field.cpython-312.pyc differ diff --git a/parks/migrations/__pycache__/0002_create_missing_columns.cpython-311.pyc b/parks/migrations/__pycache__/0002_create_missing_columns.cpython-311.pyc new file mode 100644 index 00000000..a822fa63 Binary files /dev/null and b/parks/migrations/__pycache__/0002_create_missing_columns.cpython-311.pyc differ diff --git a/parks/migrations/__pycache__/__init__.cpython-311.pyc b/parks/migrations/__pycache__/__init__.cpython-311.pyc index 23ac5f4d..f3dfdb5b 100644 Binary files a/parks/migrations/__pycache__/__init__.cpython-311.pyc and b/parks/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/parks/migrations/__pycache__/__init__.cpython-312.pyc b/parks/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..cc71322a Binary files /dev/null and b/parks/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/parks/models.py b/parks/models.py index ed6b2d4a..6dbfcc60 100644 --- a/parks/models.py +++ b/parks/models.py @@ -2,6 +2,7 @@ from django.db import models from django.contrib.contenttypes.fields import GenericRelation from django.utils.text import slugify from simple_history.models import HistoricalRecords +import pycountry class Park(models.Model): STATUS_CHOICES = [ @@ -15,6 +16,7 @@ class Park(models.Model): name = models.CharField(max_length=255) slug = models.SlugField(max_length=255, unique=True) location = models.CharField(max_length=255) + country = models.CharField(max_length=2, help_text='Two-letter country code (ISO 3166-1 alpha-2)') description = models.TextField(blank=True) owner = models.ForeignKey( 'companies.Company', @@ -73,6 +75,14 @@ class Park(models.Model): return cls.objects.get(id=history.id), True raise cls.DoesNotExist("No park found with this slug") + def get_country_name(self): + """Get the full country name from the country code""" + try: + country = pycountry.countries.get(alpha_2=self.country) + return country.name if country else self.country + except: + return self.country + class ParkArea(models.Model): name = models.CharField(max_length=255) slug = models.SlugField(max_length=255) diff --git a/parks/views.py b/parks/views.py index 76790ba8..2f50be63 100644 --- a/parks/views.py +++ b/parks/views.py @@ -1,5 +1,7 @@ from django.views.generic import DetailView, ListView from django.shortcuts import get_object_or_404 +from django.core.serializers.json import DjangoJSONEncoder +from django.urls import reverse from .models import Park, ParkArea from rides.models import Ride from core.views import SlugRedirectMixin @@ -64,28 +66,43 @@ class ParkListView(ListView): model = Park template_name = 'parks/park_list.html' context_object_name = 'parks' - paginate_by = 12 def get_queryset(self): - queryset = Park.objects.select_related('owner') + queryset = Park.objects.select_related('owner').prefetch_related('photos', 'rides') - # Filter by location if specified - location = self.request.GET.get('location') + # Apply filters + search = self.request.GET.get('search', '').strip() + location = self.request.GET.get('location', '').strip() + status = self.request.GET.get('status', '').strip() + + if search: + queryset = queryset.filter(name__icontains=search) | queryset.filter(location__icontains=search) if location: - queryset = queryset.filter(location__icontains=location) - - # Filter by status if specified - status = self.request.GET.get('status') + queryset = queryset.filter(location=location) if status: queryset = queryset.filter(status=status) - return queryset.order_by('name') + return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - # Add list of locations for the filter dropdown - context['locations'] = Park.objects.values_list( - 'location', flat=True - ).distinct().order_by('location') - context['selected_location'] = self.request.GET.get('location', '') + + # Get unique locations for filter dropdown + context['locations'] = list(Park.objects.values_list('location', flat=True) + .distinct().order_by('location')) + + # Add current filter values to context + context['current_filters'] = { + 'search': self.request.GET.get('search', ''), + 'location': self.request.GET.get('location', ''), + 'status': self.request.GET.get('status', '') + } + return context + + def get(self, request, *args, **kwargs): + # Check if this is an HTMX request + if request.htmx: + # If it is, return just the parks list partial + self.template_name = 'parks/partials/park_list.html' + return super().get(request, *args, **kwargs) diff --git a/requirements.txt b/requirements.txt index 27c4c59a..2fdf44f4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,8 @@ django-cors-headers==4.5.0 django-allauth==65.1.0 django-oauth-toolkit==3.0.1 dj-rest-auth==6.0.0 # Added for REST authentication endpoints +pyjwt==2.9.0 + # Database psycopg2-binary==2.9.10 @@ -39,3 +41,4 @@ daphne==4.1.0 # React and Material UI will be handled via npm in the frontend directory django-simple-history==3.7.0 +django-tailwind-cli==2.18.1 diff --git a/reviews/__pycache__/__init__.cpython-311.pyc b/reviews/__pycache__/__init__.cpython-311.pyc index 8d8ccd3b..fecaecf7 100644 Binary files a/reviews/__pycache__/__init__.cpython-311.pyc and b/reviews/__pycache__/__init__.cpython-311.pyc differ diff --git a/reviews/__pycache__/__init__.cpython-312.pyc b/reviews/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..1ac03f7e Binary files /dev/null and b/reviews/__pycache__/__init__.cpython-312.pyc differ diff --git a/reviews/__pycache__/admin.cpython-311.pyc b/reviews/__pycache__/admin.cpython-311.pyc index 84ebec93..69bec2ca 100644 Binary files a/reviews/__pycache__/admin.cpython-311.pyc and b/reviews/__pycache__/admin.cpython-311.pyc differ diff --git a/reviews/__pycache__/admin.cpython-312.pyc b/reviews/__pycache__/admin.cpython-312.pyc new file mode 100644 index 00000000..3fe97122 Binary files /dev/null and b/reviews/__pycache__/admin.cpython-312.pyc differ diff --git a/reviews/__pycache__/apps.cpython-311.pyc b/reviews/__pycache__/apps.cpython-311.pyc index 9fccf09b..64107d70 100644 Binary files a/reviews/__pycache__/apps.cpython-311.pyc and b/reviews/__pycache__/apps.cpython-311.pyc differ diff --git a/reviews/__pycache__/apps.cpython-312.pyc b/reviews/__pycache__/apps.cpython-312.pyc new file mode 100644 index 00000000..f36bb9f9 Binary files /dev/null and b/reviews/__pycache__/apps.cpython-312.pyc differ diff --git a/reviews/__pycache__/models.cpython-311.pyc b/reviews/__pycache__/models.cpython-311.pyc index 02188e15..a660f72c 100644 Binary files a/reviews/__pycache__/models.cpython-311.pyc and b/reviews/__pycache__/models.cpython-311.pyc differ diff --git a/reviews/__pycache__/models.cpython-312.pyc b/reviews/__pycache__/models.cpython-312.pyc new file mode 100644 index 00000000..403383c5 Binary files /dev/null and b/reviews/__pycache__/models.cpython-312.pyc differ diff --git a/reviews/__pycache__/signals.cpython-311.pyc b/reviews/__pycache__/signals.cpython-311.pyc index 0d216ed9..1fdda68e 100644 Binary files a/reviews/__pycache__/signals.cpython-311.pyc and b/reviews/__pycache__/signals.cpython-311.pyc differ diff --git a/reviews/__pycache__/signals.cpython-312.pyc b/reviews/__pycache__/signals.cpython-312.pyc new file mode 100644 index 00000000..cf56a78b Binary files /dev/null and b/reviews/__pycache__/signals.cpython-312.pyc differ diff --git a/reviews/__pycache__/urls.cpython-311.pyc b/reviews/__pycache__/urls.cpython-311.pyc index 321b1f7e..923c2c60 100644 Binary files a/reviews/__pycache__/urls.cpython-311.pyc and b/reviews/__pycache__/urls.cpython-311.pyc differ diff --git a/reviews/__pycache__/urls.cpython-312.pyc b/reviews/__pycache__/urls.cpython-312.pyc new file mode 100644 index 00000000..67df6d7d Binary files /dev/null and b/reviews/__pycache__/urls.cpython-312.pyc differ diff --git a/reviews/__pycache__/views.cpython-311.pyc b/reviews/__pycache__/views.cpython-311.pyc index dea019e8..bc609c2d 100644 Binary files a/reviews/__pycache__/views.cpython-311.pyc and b/reviews/__pycache__/views.cpython-311.pyc differ diff --git a/reviews/__pycache__/views.cpython-312.pyc b/reviews/__pycache__/views.cpython-312.pyc new file mode 100644 index 00000000..820d030d Binary files /dev/null and b/reviews/__pycache__/views.cpython-312.pyc differ diff --git a/reviews/migrations/__pycache__/0001_initial.cpython-311.pyc b/reviews/migrations/__pycache__/0001_initial.cpython-311.pyc index c4ef3bc1..58fb35da 100644 Binary files a/reviews/migrations/__pycache__/0001_initial.cpython-311.pyc and b/reviews/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/reviews/migrations/__pycache__/0001_initial.cpython-312.pyc b/reviews/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 00000000..4740bd87 Binary files /dev/null and b/reviews/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/reviews/migrations/__pycache__/__init__.cpython-311.pyc b/reviews/migrations/__pycache__/__init__.cpython-311.pyc index a3a104f2..ff5f7791 100644 Binary files a/reviews/migrations/__pycache__/__init__.cpython-311.pyc and b/reviews/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/reviews/migrations/__pycache__/__init__.cpython-312.pyc b/reviews/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..7840d92e Binary files /dev/null and b/reviews/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/rides/__pycache__/__init__.cpython-311.pyc b/rides/__pycache__/__init__.cpython-311.pyc index fad5ed11..e75e6db9 100644 Binary files a/rides/__pycache__/__init__.cpython-311.pyc and b/rides/__pycache__/__init__.cpython-311.pyc differ diff --git a/rides/__pycache__/__init__.cpython-312.pyc b/rides/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..2172b0aa Binary files /dev/null and b/rides/__pycache__/__init__.cpython-312.pyc differ diff --git a/rides/__pycache__/admin.cpython-311.pyc b/rides/__pycache__/admin.cpython-311.pyc index ce216f6c..e4c1fc62 100644 Binary files a/rides/__pycache__/admin.cpython-311.pyc and b/rides/__pycache__/admin.cpython-311.pyc differ diff --git a/rides/__pycache__/admin.cpython-312.pyc b/rides/__pycache__/admin.cpython-312.pyc new file mode 100644 index 00000000..330f4bb3 Binary files /dev/null and b/rides/__pycache__/admin.cpython-312.pyc differ diff --git a/rides/__pycache__/apps.cpython-311.pyc b/rides/__pycache__/apps.cpython-311.pyc index 852fa6d3..ef34a3be 100644 Binary files a/rides/__pycache__/apps.cpython-311.pyc and b/rides/__pycache__/apps.cpython-311.pyc differ diff --git a/rides/__pycache__/apps.cpython-312.pyc b/rides/__pycache__/apps.cpython-312.pyc new file mode 100644 index 00000000..47f4d90e Binary files /dev/null and b/rides/__pycache__/apps.cpython-312.pyc differ diff --git a/rides/__pycache__/models.cpython-311.pyc b/rides/__pycache__/models.cpython-311.pyc index 2d870fea..eceb9e75 100644 Binary files a/rides/__pycache__/models.cpython-311.pyc and b/rides/__pycache__/models.cpython-311.pyc differ diff --git a/rides/__pycache__/models.cpython-312.pyc b/rides/__pycache__/models.cpython-312.pyc new file mode 100644 index 00000000..b64e7ade Binary files /dev/null and b/rides/__pycache__/models.cpython-312.pyc differ diff --git a/rides/__pycache__/signals.cpython-311.pyc b/rides/__pycache__/signals.cpython-311.pyc index 3d78b8f2..fbb81d19 100644 Binary files a/rides/__pycache__/signals.cpython-311.pyc and b/rides/__pycache__/signals.cpython-311.pyc differ diff --git a/rides/__pycache__/signals.cpython-312.pyc b/rides/__pycache__/signals.cpython-312.pyc new file mode 100644 index 00000000..9aa81e85 Binary files /dev/null and b/rides/__pycache__/signals.cpython-312.pyc differ diff --git a/rides/__pycache__/urls.cpython-311.pyc b/rides/__pycache__/urls.cpython-311.pyc index 73207427..901449a4 100644 Binary files a/rides/__pycache__/urls.cpython-311.pyc and b/rides/__pycache__/urls.cpython-311.pyc differ diff --git a/rides/__pycache__/urls.cpython-312.pyc b/rides/__pycache__/urls.cpython-312.pyc new file mode 100644 index 00000000..d6b81071 Binary files /dev/null and b/rides/__pycache__/urls.cpython-312.pyc differ diff --git a/rides/__pycache__/views.cpython-311.pyc b/rides/__pycache__/views.cpython-311.pyc index 511f0dc9..9d0a9ca2 100644 Binary files a/rides/__pycache__/views.cpython-311.pyc and b/rides/__pycache__/views.cpython-311.pyc differ diff --git a/rides/__pycache__/views.cpython-312.pyc b/rides/__pycache__/views.cpython-312.pyc new file mode 100644 index 00000000..1ee394de Binary files /dev/null and b/rides/__pycache__/views.cpython-312.pyc differ diff --git a/rides/admin.py b/rides/admin.py index fd7f6a85..f62ea0a9 100644 --- a/rides/admin.py +++ b/rides/admin.py @@ -32,12 +32,12 @@ class RollerCoasterStatsInline(admin.StackedInline): @admin.register(Ride) class RideAdmin(SimpleHistoryAdmin): - list_display = ('name', 'park', 'category', 'get_status', 'manufacturer', 'opening_date', 'get_avg_rating') + list_display = ('id', 'name', 'park', 'category', 'get_status', 'manufacturer', 'opening_date', 'get_avg_rating') list_filter = ('status', 'category', 'manufacturer', 'park') search_fields = ('name', 'park__name', 'manufacturer__name', 'description') prepopulated_fields = {'slug': ('name',)} inlines = [RollerCoasterStatsInline] - readonly_fields = ('created_at', 'updated_at') + readonly_fields = ('id', 'created_at', 'updated_at') history_list_display = ['status', 'manufacturer'] actions = ['mark_as_operating', 'mark_as_closed', 'mark_as_under_maintenance', 'mark_as_removed'] @@ -80,18 +80,19 @@ class RideAdmin(SimpleHistoryAdmin): ) }), ('Metadata', { - 'fields': ('created_at', 'updated_at'), + 'fields': ('id', 'created_at', 'updated_at'), 'classes': ('collapse',) }), ) def get_status(self, obj): status_colors = { - 'operating': 'green', - 'closed': 'red', - 'under_maintenance': 'orange', - 'under_construction': 'blue', - 'removed': 'grey' + 'OPERATING': 'green', + 'CLOSED_TEMP': 'orange', + 'CLOSED_PERM': 'red', + 'UNDER_CONSTRUCTION': 'blue', + 'DEMOLISHED': 'grey', + 'RELOCATED': 'purple' } return format_html( '{}', @@ -101,43 +102,44 @@ class RideAdmin(SimpleHistoryAdmin): get_status.short_description = 'Status' def get_avg_rating(self, obj): - avg = obj.reviews.filter(status='approved').aggregate(avg_rating=Avg('rating'))['avg_rating'] + avg = obj.reviews.filter(is_published=True).aggregate(avg_rating=Avg('rating'))['avg_rating'] if avg: + rating_str = '{:.1f}'.format(float(avg)) return format_html( - '★ {:.1f}', - 'gold', - avg + '★ {}', + rating_str ) return '-' get_avg_rating.short_description = 'Rating' def mark_as_operating(self, request, queryset): - queryset.update(status='operating') + queryset.update(status='OPERATING') mark_as_operating.short_description = "Mark selected rides as operating" def mark_as_closed(self, request, queryset): - queryset.update(status='closed') - mark_as_closed.short_description = "Mark selected rides as closed" + queryset.update(status='CLOSED_TEMP') + mark_as_closed.short_description = "Mark selected rides as temporarily closed" def mark_as_under_maintenance(self, request, queryset): - queryset.update(status='under_maintenance') + queryset.update(status='CLOSED_TEMP') mark_as_under_maintenance.short_description = "Mark selected rides as under maintenance" def mark_as_removed(self, request, queryset): - queryset.update(status='removed') - mark_as_removed.short_description = "Mark selected rides as removed" + queryset.update(status='DEMOLISHED') + mark_as_removed.short_description = "Mark selected rides as demolished" @admin.register(RollerCoasterStats) class RollerCoasterStatsAdmin(SimpleHistoryAdmin): list_display = ('ride', 'height_ft', 'length_ft', 'speed_mph', 'inversions', 'get_capacity') list_filter = ('launch_type', 'track_type', 'train_style') search_fields = ('ride__name', 'track_type') - readonly_fields = ('ride',) + readonly_fields = ('id', 'ride') history_list_display = ['height_ft', 'length_ft', 'speed_mph', 'inversions'] fieldsets = ( ('Basic Stats', { 'fields': ( + 'id', 'ride', ('height_ft', 'length_ft'), ('speed_mph', 'inversions'), @@ -163,7 +165,7 @@ class RollerCoasterStatsAdmin(SimpleHistoryAdmin): capacity = obj.trains_count * obj.cars_per_train * obj.seats_per_car return format_html( '{} seats total', - capacity + str(capacity) ) return '-' get_capacity.short_description = 'Total Capacity' diff --git a/rides/migrations/0001_initial.py b/rides/migrations/0001_initial.py index 0bdc1696..3e847248 100644 --- a/rides/migrations/0001_initial.py +++ b/rides/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.2 on 2024-10-28 20:17 +# Generated by Django 5.1.2 on 2024-10-28 21:53 import django.db.models.deletion import simple_history.models @@ -11,130 +11,400 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('companies', '0001_initial'), - ('parks', '0001_initial'), + ("companies", "0002_stats_fields"), + ("parks", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( - name='HistoricalRide', + name="HistoricalRide", fields=[ - ('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('slug', models.SlugField(max_length=255)), - ('description', models.TextField(blank=True)), - ('category', models.CharField(choices=[('RC', 'Roller Coaster'), ('DR', 'Dark Ride'), ('FR', 'Flat Ride'), ('WR', 'Water Ride'), ('TR', 'Transport'), ('OT', 'Other')], default='OT', max_length=2)), - ('model_name', models.CharField(blank=True, max_length=255)), - ('status', models.CharField(choices=[('OPERATING', 'Operating'), ('CLOSED_TEMP', 'Temporarily Closed'), ('CLOSED_PERM', 'Permanently Closed'), ('UNDER_CONSTRUCTION', 'Under Construction'), ('DEMOLISHED', 'Demolished'), ('RELOCATED', 'Relocated')], default='OPERATING', max_length=20)), - ('opening_date', models.DateField(blank=True, null=True)), - ('closing_date', models.DateField(blank=True, null=True)), - ('status_since', models.DateField(blank=True, null=True)), - ('min_height_in', models.PositiveIntegerField(blank=True, null=True)), - ('max_height_in', models.PositiveIntegerField(blank=True, null=True)), - ('accessibility_options', models.TextField(blank=True)), - ('capacity_per_hour', models.PositiveIntegerField(blank=True, null=True)), - ('ride_duration_seconds', models.PositiveIntegerField(blank=True, null=True)), - ('average_rating', models.DecimalField(blank=True, decimal_places=2, max_digits=3, null=True)), - ('created_at', models.DateTimeField(blank=True, editable=False)), - ('updated_at', models.DateTimeField(blank=True, editable=False)), - ('history_id', models.AutoField(primary_key=True, serialize=False)), - ('history_date', models.DateTimeField(db_index=True)), - ('history_change_reason', models.CharField(max_length=100, null=True)), - ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), - ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), - ('manufacturer', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='companies.manufacturer')), - ('park', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='parks.park')), - ('park_area', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='parks.parkarea')), + ( + "id", + models.BigIntegerField( + auto_created=True, blank=True, db_index=True, verbose_name="ID" + ), + ), + ("name", models.CharField(max_length=255)), + ("slug", models.SlugField(max_length=255)), + ("description", models.TextField(blank=True)), + ( + "category", + models.CharField( + choices=[ + ("RC", "Roller Coaster"), + ("DR", "Dark Ride"), + ("FR", "Flat Ride"), + ("WR", "Water Ride"), + ("TR", "Transport"), + ("OT", "Other"), + ], + default="OT", + max_length=2, + ), + ), + ("model_name", models.CharField(blank=True, max_length=255)), + ( + "status", + models.CharField( + choices=[ + ("OPERATING", "Operating"), + ("CLOSED_TEMP", "Temporarily Closed"), + ("CLOSED_PERM", "Permanently Closed"), + ("UNDER_CONSTRUCTION", "Under Construction"), + ("DEMOLISHED", "Demolished"), + ("RELOCATED", "Relocated"), + ], + default="OPERATING", + max_length=20, + ), + ), + ("opening_date", models.DateField(blank=True, null=True)), + ("closing_date", models.DateField(blank=True, null=True)), + ("status_since", models.DateField(blank=True, null=True)), + ("min_height_in", models.PositiveIntegerField(blank=True, null=True)), + ("max_height_in", models.PositiveIntegerField(blank=True, null=True)), + ("accessibility_options", models.TextField(blank=True)), + ( + "capacity_per_hour", + models.PositiveIntegerField(blank=True, null=True), + ), + ( + "ride_duration_seconds", + models.PositiveIntegerField(blank=True, null=True), + ), + ( + "average_rating", + models.DecimalField( + blank=True, decimal_places=2, max_digits=3, null=True + ), + ), + ("created_at", models.DateTimeField(blank=True, editable=False)), + ("updated_at", models.DateTimeField(blank=True, editable=False)), + ("history_id", models.AutoField(primary_key=True, serialize=False)), + ("history_date", models.DateTimeField(db_index=True)), + ("history_change_reason", models.CharField(max_length=100, null=True)), + ( + "history_type", + models.CharField( + choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], + max_length=1, + ), + ), + ( + "history_user", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "manufacturer", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="companies.manufacturer", + ), + ), + ( + "park", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="parks.park", + ), + ), + ( + "park_area", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="parks.parkarea", + ), + ), ], options={ - 'verbose_name': 'historical ride', - 'verbose_name_plural': 'historical rides', - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': ('history_date', 'history_id'), + "verbose_name": "historical ride", + "verbose_name_plural": "historical rides", + "ordering": ("-history_date", "-history_id"), + "get_latest_by": ("history_date", "history_id"), }, bases=(simple_history.models.HistoricalChanges, models.Model), ), migrations.CreateModel( - name='Ride', + name="Ride", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('slug', models.SlugField(max_length=255)), - ('description', models.TextField(blank=True)), - ('category', models.CharField(choices=[('RC', 'Roller Coaster'), ('DR', 'Dark Ride'), ('FR', 'Flat Ride'), ('WR', 'Water Ride'), ('TR', 'Transport'), ('OT', 'Other')], default='OT', max_length=2)), - ('model_name', models.CharField(blank=True, max_length=255)), - ('status', models.CharField(choices=[('OPERATING', 'Operating'), ('CLOSED_TEMP', 'Temporarily Closed'), ('CLOSED_PERM', 'Permanently Closed'), ('UNDER_CONSTRUCTION', 'Under Construction'), ('DEMOLISHED', 'Demolished'), ('RELOCATED', 'Relocated')], default='OPERATING', max_length=20)), - ('opening_date', models.DateField(blank=True, null=True)), - ('closing_date', models.DateField(blank=True, null=True)), - ('status_since', models.DateField(blank=True, null=True)), - ('min_height_in', models.PositiveIntegerField(blank=True, null=True)), - ('max_height_in', models.PositiveIntegerField(blank=True, null=True)), - ('accessibility_options', models.TextField(blank=True)), - ('capacity_per_hour', models.PositiveIntegerField(blank=True, null=True)), - ('ride_duration_seconds', models.PositiveIntegerField(blank=True, null=True)), - ('average_rating', models.DecimalField(blank=True, decimal_places=2, max_digits=3, null=True)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ('manufacturer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rides', to='companies.manufacturer')), - ('park', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rides', to='parks.park')), - ('park_area', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rides', to='parks.parkarea')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ("slug", models.SlugField(max_length=255)), + ("description", models.TextField(blank=True)), + ( + "category", + models.CharField( + choices=[ + ("RC", "Roller Coaster"), + ("DR", "Dark Ride"), + ("FR", "Flat Ride"), + ("WR", "Water Ride"), + ("TR", "Transport"), + ("OT", "Other"), + ], + default="OT", + max_length=2, + ), + ), + ("model_name", models.CharField(blank=True, max_length=255)), + ( + "status", + models.CharField( + choices=[ + ("OPERATING", "Operating"), + ("CLOSED_TEMP", "Temporarily Closed"), + ("CLOSED_PERM", "Permanently Closed"), + ("UNDER_CONSTRUCTION", "Under Construction"), + ("DEMOLISHED", "Demolished"), + ("RELOCATED", "Relocated"), + ], + default="OPERATING", + max_length=20, + ), + ), + ("opening_date", models.DateField(blank=True, null=True)), + ("closing_date", models.DateField(blank=True, null=True)), + ("status_since", models.DateField(blank=True, null=True)), + ("min_height_in", models.PositiveIntegerField(blank=True, null=True)), + ("max_height_in", models.PositiveIntegerField(blank=True, null=True)), + ("accessibility_options", models.TextField(blank=True)), + ( + "capacity_per_hour", + models.PositiveIntegerField(blank=True, null=True), + ), + ( + "ride_duration_seconds", + models.PositiveIntegerField(blank=True, null=True), + ), + ( + "average_rating", + models.DecimalField( + blank=True, decimal_places=2, max_digits=3, null=True + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "manufacturer", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="rides", + to="companies.manufacturer", + ), + ), + ( + "park", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="rides", + to="parks.park", + ), + ), + ( + "park_area", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="rides", + to="parks.parkarea", + ), + ), ], options={ - 'ordering': ['name'], - 'unique_together': {('park', 'slug')}, + "ordering": ["name"], + "unique_together": {("park", "slug")}, }, ), migrations.CreateModel( - name='HistoricalRollerCoasterStats', + name="HistoricalRollerCoasterStats", fields=[ - ('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), - ('height_ft', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True)), - ('length_ft', models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)), - ('speed_mph', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True)), - ('inversions', models.PositiveIntegerField(default=0)), - ('ride_time_seconds', models.PositiveIntegerField(blank=True, null=True)), - ('track_type', models.CharField(blank=True, max_length=255)), - ('launch_type', models.CharField(choices=[('CHAIN', 'Chain Lift'), ('CABLE', 'Cable Launch'), ('HYDRAULIC', 'Hydraulic Launch'), ('LSM', 'Linear Synchronous Motor'), ('LIM', 'Linear Induction Motor'), ('GRAVITY', 'Gravity'), ('OTHER', 'Other')], default='CHAIN', max_length=20)), - ('train_style', models.CharField(blank=True, max_length=255)), - ('trains_count', models.PositiveIntegerField(blank=True, null=True)), - ('cars_per_train', models.PositiveIntegerField(blank=True, null=True)), - ('seats_per_car', models.PositiveIntegerField(blank=True, null=True)), - ('history_id', models.AutoField(primary_key=True, serialize=False)), - ('history_date', models.DateTimeField(db_index=True)), - ('history_change_reason', models.CharField(max_length=100, null=True)), - ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), - ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), - ('ride', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='rides.ride')), + ( + "id", + models.BigIntegerField( + auto_created=True, blank=True, db_index=True, verbose_name="ID" + ), + ), + ( + "height_ft", + models.DecimalField( + blank=True, decimal_places=2, max_digits=6, null=True + ), + ), + ( + "length_ft", + models.DecimalField( + blank=True, decimal_places=2, max_digits=7, null=True + ), + ), + ( + "speed_mph", + models.DecimalField( + blank=True, decimal_places=2, max_digits=5, null=True + ), + ), + ("inversions", models.PositiveIntegerField(default=0)), + ( + "ride_time_seconds", + models.PositiveIntegerField(blank=True, null=True), + ), + ("track_type", models.CharField(blank=True, max_length=255)), + ( + "launch_type", + models.CharField( + choices=[ + ("CHAIN", "Chain Lift"), + ("CABLE", "Cable Launch"), + ("HYDRAULIC", "Hydraulic Launch"), + ("LSM", "Linear Synchronous Motor"), + ("LIM", "Linear Induction Motor"), + ("GRAVITY", "Gravity"), + ("OTHER", "Other"), + ], + default="CHAIN", + max_length=20, + ), + ), + ("train_style", models.CharField(blank=True, max_length=255)), + ("trains_count", models.PositiveIntegerField(blank=True, null=True)), + ("cars_per_train", models.PositiveIntegerField(blank=True, null=True)), + ("seats_per_car", models.PositiveIntegerField(blank=True, null=True)), + ("history_id", models.AutoField(primary_key=True, serialize=False)), + ("history_date", models.DateTimeField(db_index=True)), + ("history_change_reason", models.CharField(max_length=100, null=True)), + ( + "history_type", + models.CharField( + choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], + max_length=1, + ), + ), + ( + "history_user", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "ride", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + to="rides.ride", + ), + ), ], options={ - 'verbose_name': 'historical Roller Coaster Statistics', - 'verbose_name_plural': 'historical Roller Coaster Statistics', - 'ordering': ('-history_date', '-history_id'), - 'get_latest_by': ('history_date', 'history_id'), + "verbose_name": "historical Roller Coaster Statistics", + "verbose_name_plural": "historical Roller Coaster Statistics", + "ordering": ("-history_date", "-history_id"), + "get_latest_by": ("history_date", "history_id"), }, bases=(simple_history.models.HistoricalChanges, models.Model), ), migrations.CreateModel( - name='RollerCoasterStats', + name="RollerCoasterStats", fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('height_ft', models.DecimalField(blank=True, decimal_places=2, max_digits=6, null=True)), - ('length_ft', models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)), - ('speed_mph', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True)), - ('inversions', models.PositiveIntegerField(default=0)), - ('ride_time_seconds', models.PositiveIntegerField(blank=True, null=True)), - ('track_type', models.CharField(blank=True, max_length=255)), - ('launch_type', models.CharField(choices=[('CHAIN', 'Chain Lift'), ('CABLE', 'Cable Launch'), ('HYDRAULIC', 'Hydraulic Launch'), ('LSM', 'Linear Synchronous Motor'), ('LIM', 'Linear Induction Motor'), ('GRAVITY', 'Gravity'), ('OTHER', 'Other')], default='CHAIN', max_length=20)), - ('train_style', models.CharField(blank=True, max_length=255)), - ('trains_count', models.PositiveIntegerField(blank=True, null=True)), - ('cars_per_train', models.PositiveIntegerField(blank=True, null=True)), - ('seats_per_car', models.PositiveIntegerField(blank=True, null=True)), - ('ride', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='coaster_stats', to='rides.ride')), + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "height_ft", + models.DecimalField( + blank=True, decimal_places=2, max_digits=6, null=True + ), + ), + ( + "length_ft", + models.DecimalField( + blank=True, decimal_places=2, max_digits=7, null=True + ), + ), + ( + "speed_mph", + models.DecimalField( + blank=True, decimal_places=2, max_digits=5, null=True + ), + ), + ("inversions", models.PositiveIntegerField(default=0)), + ( + "ride_time_seconds", + models.PositiveIntegerField(blank=True, null=True), + ), + ("track_type", models.CharField(blank=True, max_length=255)), + ( + "launch_type", + models.CharField( + choices=[ + ("CHAIN", "Chain Lift"), + ("CABLE", "Cable Launch"), + ("HYDRAULIC", "Hydraulic Launch"), + ("LSM", "Linear Synchronous Motor"), + ("LIM", "Linear Induction Motor"), + ("GRAVITY", "Gravity"), + ("OTHER", "Other"), + ], + default="CHAIN", + max_length=20, + ), + ), + ("train_style", models.CharField(blank=True, max_length=255)), + ("trains_count", models.PositiveIntegerField(blank=True, null=True)), + ("cars_per_train", models.PositiveIntegerField(blank=True, null=True)), + ("seats_per_car", models.PositiveIntegerField(blank=True, null=True)), + ( + "ride", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name="coaster_stats", + to="rides.ride", + ), + ), ], options={ - 'verbose_name': 'Roller Coaster Statistics', - 'verbose_name_plural': 'Roller Coaster Statistics', + "verbose_name": "Roller Coaster Statistics", + "verbose_name_plural": "Roller Coaster Statistics", }, ), ] diff --git a/rides/migrations/0002_alter_ride_manufacturer.py b/rides/migrations/0002_alter_ride_manufacturer.py new file mode 100644 index 00000000..d06d7183 --- /dev/null +++ b/rides/migrations/0002_alter_ride_manufacturer.py @@ -0,0 +1,25 @@ +# Generated by Django 5.1.2 on 2024-10-29 02:02 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("companies", "0004_add_total_parks"), + ("rides", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="ride", + name="manufacturer", + field=models.ForeignKey( + default=1, + on_delete=django.db.models.deletion.CASCADE, + to="companies.manufacturer", + ), + preserve_default=False, + ), + ] diff --git a/rides/migrations/__pycache__/0001_initial.cpython-311.pyc b/rides/migrations/__pycache__/0001_initial.cpython-311.pyc index 3719dd5a..300a4e08 100644 Binary files a/rides/migrations/__pycache__/0001_initial.cpython-311.pyc and b/rides/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/rides/migrations/__pycache__/0001_initial.cpython-312.pyc b/rides/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 00000000..d143e721 Binary files /dev/null and b/rides/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/rides/migrations/__pycache__/0002_add_readonly_ids.cpython-311.pyc b/rides/migrations/__pycache__/0002_add_readonly_ids.cpython-311.pyc deleted file mode 100644 index 8e9db798..00000000 Binary files a/rides/migrations/__pycache__/0002_add_readonly_ids.cpython-311.pyc and /dev/null differ diff --git a/rides/migrations/__pycache__/0002_alter_ride_manufacturer.cpython-312.pyc b/rides/migrations/__pycache__/0002_alter_ride_manufacturer.cpython-312.pyc new file mode 100644 index 00000000..836e0583 Binary files /dev/null and b/rides/migrations/__pycache__/0002_alter_ride_manufacturer.cpython-312.pyc differ diff --git a/rides/migrations/__pycache__/__init__.cpython-311.pyc b/rides/migrations/__pycache__/__init__.cpython-311.pyc index 716207dd..35733396 100644 Binary files a/rides/migrations/__pycache__/__init__.cpython-311.pyc and b/rides/migrations/__pycache__/__init__.cpython-311.pyc differ diff --git a/rides/migrations/__pycache__/__init__.cpython-312.pyc b/rides/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 00000000..d03c09fb Binary files /dev/null and b/rides/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/rides/models.py b/rides/models.py index d117715d..6fdebbfc 100644 --- a/rides/models.py +++ b/rides/models.py @@ -43,12 +43,10 @@ class Ride(models.Model): default='OT' ) manufacturer = models.ForeignKey( - 'companies.Manufacturer', - on_delete=models.SET_NULL, - related_name='rides', - null=True, - blank=True - ) + 'companies.manufacturer', on_delete=models.CASCADE, null=False, blank=False + ) + # other fields... + model_name = models.CharField(max_length=255, blank=True) status = models.CharField( max_length=20, diff --git a/rides/views.py b/rides/views.py index 9d7442a1..c0216a69 100644 --- a/rides/views.py +++ b/rides/views.py @@ -1,5 +1,8 @@ from django.views.generic import DetailView, ListView from django.shortcuts import get_object_or_404 +from django.core.serializers.json import DjangoJSONEncoder +from django.urls import reverse +from django.db.models import Q from .models import Ride, RollerCoasterStats from parks.models import Park from core.views import SlugRedirectMixin @@ -40,31 +43,56 @@ class RideListView(ListView): model = Ride template_name = 'rides/ride_list.html' context_object_name = 'rides' - paginate_by = 12 def get_queryset(self): - queryset = Ride.objects.select_related('park', 'coaster_stats') + queryset = Ride.objects.select_related('park', 'coaster_stats', 'manufacturer').prefetch_related('photos') - # Filter by category if specified - category = self.request.GET.get('category') + search = self.request.GET.get('search', '').strip() or None + category = self.request.GET.get('category', '').strip() or None + status = self.request.GET.get('status', '').strip() or None + manufacturer = self.request.GET.get('manufacturer', '').strip() or None + + if search: + queryset = queryset.filter( + Q(name__icontains=search) | + Q(park__name__icontains=search) + ) if category: queryset = queryset.filter(category=category) - - # Filter by status if specified - status = self.request.GET.get('status') if status: queryset = queryset.filter(status=status) - - # Filter by manufacturer if specified - manufacturer = self.request.GET.get('manufacturer') if manufacturer: - queryset = queryset.filter(manufacturer=manufacturer) - - return queryset.order_by('name') + # Example: Exclude null manufacturers in RideListView + queryset = Ride.objects.exclude(manufacturer__isnull=True) + + return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['manufacturers'] = Ride.objects.values_list( - 'manufacturer', flat=True - ).distinct().order_by('manufacturer') + + # Exclude cases where manufacturer is null or an empty string + context['manufacturers'] = list( + Ride.objects.exclude(manufacturer__isnull=True) + .exclude(manufacturer__isnull=True) + .values_list('manufacturer__name', flat=True) + .distinct().order_by('manufacturer__name') + ) + + # Add current filter values to context + context['current_filters'] = { + 'search': self.request.GET.get('search', ''), + 'category': self.request.GET.get('category', ''), + 'status': self.request.GET.get('status', ''), + 'manufacturer': self.request.GET.get('manufacturer', '') + } + return context + + + + def get(self, request, *args, **kwargs): + # Check if this is an HTMX request + if request.htmx: + # If it is, return just the rides list partial + self.template_name = 'rides/partials/ride_list.html' + return super().get(request, *args, **kwargs) diff --git a/static/css/styles.css b/static/css/styles.css deleted file mode 100644 index 87ef0ff3..00000000 --- a/static/css/styles.css +++ /dev/null @@ -1,199 +0,0 @@ -/* Base styles */ -:root { - --primary-color: #2563eb; - --secondary-color: #4b5563; - --success-color: #10b981; - --danger-color: #ef4444; - --warning-color: #f59e0b; -} - -/* Dark mode variables */ -.dark { - --bg-primary: #1f2937; - --bg-secondary: #374151; - --text-primary: #f3f4f6; - --text-secondary: #9ca3af; -} - -/* Light mode variables */ -:root:not(.dark) { - --bg-primary: #ffffff; - --bg-secondary: #f3f4f6; - --text-primary: #1f2937; - --text-secondary: #4b5563; -} - -/* Typography */ -body { - font-family: 'Roboto', sans-serif; - line-height: 1.5; - color: var(--text-primary); - background-color: var(--bg-secondary); -} - -/* Buttons */ -.btn-primary { - @apply inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500; -} - -.btn-secondary { - @apply inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500; -} - -/* Navigation */ -.nav-link { - @apply inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium; -} - -.nav-link:not(.active) { - @apply border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700; -} - -.nav-link.active { - @apply border-blue-500 text-gray-900; -} - -.mobile-nav-link { - @apply block pl-3 pr-4 py-2 border-l-4 text-base font-medium; -} - -.mobile-nav-link:not(.active) { - @apply border-transparent text-gray-600 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-800; -} - -.mobile-nav-link.active { - @apply border-blue-500 text-blue-700 bg-blue-50; -} - -/* Forms */ -.form-input { - @apply mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm; -} - -.form-select { - @apply mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm; -} - -.form-checkbox { - @apply h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded; -} - -/* Cards */ -.card { - @apply bg-white dark:bg-gray-800 overflow-hidden shadow rounded-lg; -} - -.card-header { - @apply px-4 py-5 sm:px-6; -} - -.card-body { - @apply px-4 py-5 sm:p-6; -} - -.card-footer { - @apply px-4 py-4 sm:px-6; -} - -/* Alerts */ -.alert { - @apply rounded-md p-4 mb-4; -} - -.alert-success { - @apply bg-green-50 text-green-800; -} - -.alert-error { - @apply bg-red-50 text-red-800; -} - -.alert-warning { - @apply bg-yellow-50 text-yellow-800; -} - -.alert-info { - @apply bg-blue-50 text-blue-800; -} - -/* Pagination */ -.pagination-link { - @apply relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50; -} - -.pagination-current { - @apply relative inline-flex items-center px-4 py-2 border border-blue-500 bg-blue-50 text-sm font-medium text-blue-600; -} - -/* Menu Items */ -.menu-item { - @apply block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700; -} - -/* Transitions */ -.transition-all { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -/* Responsive Images */ -.img-responsive { - @apply w-full h-full object-cover; -} - -/* Stats Display */ -.stat-value { - @apply text-4xl font-bold text-gray-900 dark:text-white; -} - -.stat-label { - @apply text-sm font-medium text-gray-500 dark:text-gray-400; -} - -/* Review Stars */ -.stars { - @apply flex items-center; -} - -.star { - @apply text-yellow-400; -} - -.star-empty { - @apply text-gray-300; -} - -/* Status Badges */ -.status-badge { - @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium; -} - -.status-operating { - @apply bg-green-100 text-green-800; -} - -.status-closed { - @apply bg-red-100 text-red-800; -} - -.status-construction { - @apply bg-yellow-100 text-yellow-800; -} - -/* User Roles */ -.role-badge { - @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium; -} - -.role-admin { - @apply bg-purple-100 text-purple-800; -} - -.role-moderator { - @apply bg-blue-100 text-blue-800; -} - -.role-user { - @apply bg-gray-100 text-gray-800; -} diff --git a/static/css/tailwind.css b/static/css/tailwind.css new file mode 100644 index 00000000..c97ed66d --- /dev/null +++ b/static/css/tailwind.css @@ -0,0 +1,3046 @@ +/* +! tailwindcss v3.4.11 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +html, +:host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: Poppins, sans-serif; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + letter-spacing: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +input:where([type='button']), +input:where([type='reset']), +input:where([type='submit']) { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +[type='text'],input:where(:not([type])),[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 #0000; +} + +[type='text']:focus, input:where(:not([type])):focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #2563eb; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} + +input::placeholder,textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; + text-align: inherit; +} + +::-webkit-datetime-edit { + display: inline-flex; +} + +::-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-top: 0; + padding-bottom: 0; +} + +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +[multiple],[size]:where(select:not([size="1"])) { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: unset; +} + +[type='checkbox'],[type='radio'] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #2563eb; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + --tw-shadow: 0 0 #0000; +} + +[type='checkbox'] { + border-radius: 0px; +} + +[type='radio'] { + border-radius: 100%; +} + +[type='checkbox']:focus,[type='radio']:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); +} + +[type='checkbox']:checked,[type='radio']:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='checkbox']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='radio']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='radio']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='checkbox']:indeterminate { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +@media (forced-colors: active) { + [type='checkbox']:indeterminate { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='file'] { + background: unset; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: unset; + line-height: inherit; +} + +[type='file']:focus { + outline: 1px solid ButtonText; + outline: 1px auto -webkit-focus-ring-color; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.prose { + color: var(--tw-prose-body); + max-width: 65ch; +} + +.prose :where(p):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.25em; + margin-bottom: 1.25em; +} + +.prose :where([class~="lead"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-lead); + font-size: 1.25em; + line-height: 1.6; + margin-top: 1.2em; + margin-bottom: 1.2em; +} + +.prose :where(a):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-links); + text-decoration: underline; + font-weight: 500; +} + +.prose :where(strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-bold); + font-weight: 600; +} + +.prose :where(a strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(blockquote strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(thead th strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: decimal; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-inline-start: 1.625em; +} + +.prose :where(ol[type="A"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-alpha; +} + +.prose :where(ol[type="a"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-alpha; +} + +.prose :where(ol[type="A" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-alpha; +} + +.prose :where(ol[type="a" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-alpha; +} + +.prose :where(ol[type="I"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-roman; +} + +.prose :where(ol[type="i"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-roman; +} + +.prose :where(ol[type="I" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: upper-roman; +} + +.prose :where(ol[type="i" s]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: lower-roman; +} + +.prose :where(ol[type="1"]):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: decimal; +} + +.prose :where(ul):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + list-style-type: disc; + margin-top: 1.25em; + margin-bottom: 1.25em; + padding-inline-start: 1.625em; +} + +.prose :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *))::marker { + font-weight: 400; + color: var(--tw-prose-counters); +} + +.prose :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *))::marker { + color: var(--tw-prose-bullets); +} + +.prose :where(dt):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + margin-top: 1.25em; +} + +.prose :where(hr):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-color: var(--tw-prose-hr); + border-top-width: 1px; + margin-top: 3em; + margin-bottom: 3em; +} + +.prose :where(blockquote):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 500; + font-style: italic; + color: var(--tw-prose-quotes); + border-inline-start-width: 0.25rem; + border-inline-start-color: var(--tw-prose-quote-borders); + quotes: "\201C""\201D""\2018""\2019"; + margin-top: 1.6em; + margin-bottom: 1.6em; + padding-inline-start: 1em; +} + +.prose :where(blockquote p:first-of-type):not(:where([class~="not-prose"],[class~="not-prose"] *))::before { + content: open-quote; +} + +.prose :where(blockquote p:last-of-type):not(:where([class~="not-prose"],[class~="not-prose"] *))::after { + content: close-quote; +} + +.prose :where(h1):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 800; + font-size: 2.25em; + margin-top: 0; + margin-bottom: 0.8888889em; + line-height: 1.1111111; +} + +.prose :where(h1 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 900; + color: inherit; +} + +.prose :where(h2):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 700; + font-size: 1.5em; + margin-top: 2em; + margin-bottom: 1em; + line-height: 1.3333333; +} + +.prose :where(h2 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 800; + color: inherit; +} + +.prose :where(h3):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + font-size: 1.25em; + margin-top: 1.6em; + margin-bottom: 0.6em; + line-height: 1.6; +} + +.prose :where(h3 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 700; + color: inherit; +} + +.prose :where(h4):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + margin-top: 1.5em; + margin-bottom: 0.5em; + line-height: 1.5; +} + +.prose :where(h4 strong):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 700; + color: inherit; +} + +.prose :where(img):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; +} + +.prose :where(picture):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + display: block; + margin-top: 2em; + margin-bottom: 2em; +} + +.prose :where(video):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; +} + +.prose :where(kbd):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + font-weight: 500; + font-family: inherit; + color: var(--tw-prose-kbd); + box-shadow: 0 0 0 1px rgb(var(--tw-prose-kbd-shadows) / 10%), 0 3px 0 rgb(var(--tw-prose-kbd-shadows) / 10%); + font-size: 0.875em; + border-radius: 0.3125rem; + padding-top: 0.1875em; + padding-inline-end: 0.375em; + padding-bottom: 0.1875em; + padding-inline-start: 0.375em; +} + +.prose :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-code); + font-weight: 600; + font-size: 0.875em; +} + +.prose :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *))::before { + content: "`"; +} + +.prose :where(code):not(:where([class~="not-prose"],[class~="not-prose"] *))::after { + content: "`"; +} + +.prose :where(a code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(h1 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(h2 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; + font-size: 0.875em; +} + +.prose :where(h3 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; + font-size: 0.9em; +} + +.prose :where(h4 code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(blockquote code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(thead th code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: inherit; +} + +.prose :where(pre):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-pre-code); + background-color: var(--tw-prose-pre-bg); + overflow-x: auto; + font-weight: 400; + font-size: 0.875em; + line-height: 1.7142857; + margin-top: 1.7142857em; + margin-bottom: 1.7142857em; + border-radius: 0.375rem; + padding-top: 0.8571429em; + padding-inline-end: 1.1428571em; + padding-bottom: 0.8571429em; + padding-inline-start: 1.1428571em; +} + +.prose :where(pre code):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + background-color: transparent; + border-width: 0; + border-radius: 0; + padding: 0; + font-weight: inherit; + color: inherit; + font-size: inherit; + font-family: inherit; + line-height: inherit; +} + +.prose :where(pre code):not(:where([class~="not-prose"],[class~="not-prose"] *))::before { + content: none; +} + +.prose :where(pre code):not(:where([class~="not-prose"],[class~="not-prose"] *))::after { + content: none; +} + +.prose :where(table):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + width: 100%; + table-layout: auto; + margin-top: 2em; + margin-bottom: 2em; + font-size: 0.875em; + line-height: 1.7142857; +} + +.prose :where(thead):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-th-borders); +} + +.prose :where(thead th):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-headings); + font-weight: 600; + vertical-align: bottom; + padding-inline-end: 0.5714286em; + padding-bottom: 0.5714286em; + padding-inline-start: 0.5714286em; +} + +.prose :where(tbody tr):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-bottom-width: 1px; + border-bottom-color: var(--tw-prose-td-borders); +} + +.prose :where(tbody tr:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-bottom-width: 0; +} + +.prose :where(tbody td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + vertical-align: baseline; +} + +.prose :where(tfoot):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + border-top-width: 1px; + border-top-color: var(--tw-prose-th-borders); +} + +.prose :where(tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + vertical-align: top; +} + +.prose :where(th, td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + text-align: start; +} + +.prose :where(figure > *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; +} + +.prose :where(figcaption):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + color: var(--tw-prose-captions); + font-size: 0.875em; + line-height: 1.4285714; + margin-top: 0.8571429em; +} + +.prose { + --tw-prose-body: #374151; + --tw-prose-headings: #111827; + --tw-prose-lead: #4b5563; + --tw-prose-links: #111827; + --tw-prose-bold: #111827; + --tw-prose-counters: #6b7280; + --tw-prose-bullets: #d1d5db; + --tw-prose-hr: #e5e7eb; + --tw-prose-quotes: #111827; + --tw-prose-quote-borders: #e5e7eb; + --tw-prose-captions: #6b7280; + --tw-prose-kbd: #111827; + --tw-prose-kbd-shadows: 17 24 39; + --tw-prose-code: #111827; + --tw-prose-pre-code: #e5e7eb; + --tw-prose-pre-bg: #1f2937; + --tw-prose-th-borders: #d1d5db; + --tw-prose-td-borders: #e5e7eb; + --tw-prose-invert-body: #d1d5db; + --tw-prose-invert-headings: #fff; + --tw-prose-invert-lead: #9ca3af; + --tw-prose-invert-links: #fff; + --tw-prose-invert-bold: #fff; + --tw-prose-invert-counters: #9ca3af; + --tw-prose-invert-bullets: #4b5563; + --tw-prose-invert-hr: #374151; + --tw-prose-invert-quotes: #f3f4f6; + --tw-prose-invert-quote-borders: #374151; + --tw-prose-invert-captions: #9ca3af; + --tw-prose-invert-kbd: #fff; + --tw-prose-invert-kbd-shadows: 255 255 255; + --tw-prose-invert-code: #fff; + --tw-prose-invert-pre-code: #d1d5db; + --tw-prose-invert-pre-bg: rgb(0 0 0 / 50%); + --tw-prose-invert-th-borders: #4b5563; + --tw-prose-invert-td-borders: #374151; + font-size: 1rem; + line-height: 1.75; +} + +.prose :where(picture > img):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; + margin-bottom: 0; +} + +.prose :where(li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +.prose :where(ol > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-start: 0.375em; +} + +.prose :where(ul > li):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-start: 0.375em; +} + +.prose :where(.prose > ul > li p):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} + +.prose :where(.prose > ul > li > p:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.25em; +} + +.prose :where(.prose > ul > li > p:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 1.25em; +} + +.prose :where(.prose > ol > li > p:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.25em; +} + +.prose :where(.prose > ol > li > p:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 1.25em; +} + +.prose :where(ul ul, ul ol, ol ul, ol ol):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.75em; + margin-bottom: 0.75em; +} + +.prose :where(dl):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 1.25em; + margin-bottom: 1.25em; +} + +.prose :where(dd):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0.5em; + padding-inline-start: 1.625em; +} + +.prose :where(hr + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(h2 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(h3 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(h4 + *):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(thead th:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-start: 0; +} + +.prose :where(thead th:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-end: 0; +} + +.prose :where(tbody td, tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-top: 0.5714286em; + padding-inline-end: 0.5714286em; + padding-bottom: 0.5714286em; + padding-inline-start: 0.5714286em; +} + +.prose :where(tbody td:first-child, tfoot td:first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-start: 0; +} + +.prose :where(tbody td:last-child, tfoot td:last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + padding-inline-end: 0; +} + +.prose :where(figure):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 2em; + margin-bottom: 2em; +} + +.prose :where(.prose > :first-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-top: 0; +} + +.prose :where(.prose > :last-child):not(:where([class~="not-prose"],[class~="not-prose"] *)) { + margin-bottom: 0; +} + +.form-input,.form-textarea,.form-select,.form-multiselect { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 #0000; +} + +.form-input:focus, .form-textarea:focus, .form-select:focus, .form-multiselect:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #2563eb; +} + +.form-input::-moz-placeholder, .form-textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} + +.form-input::placeholder,.form-textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +.form-input::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +.form-input::-webkit-date-and-time-value { + min-height: 1.5em; + text-align: inherit; +} + +.form-input::-webkit-datetime-edit { + display: inline-flex; +} + +.form-input::-webkit-datetime-edit,.form-input::-webkit-datetime-edit-year-field,.form-input::-webkit-datetime-edit-month-field,.form-input::-webkit-datetime-edit-day-field,.form-input::-webkit-datetime-edit-hour-field,.form-input::-webkit-datetime-edit-minute-field,.form-input::-webkit-datetime-edit-second-field,.form-input::-webkit-datetime-edit-millisecond-field,.form-input::-webkit-datetime-edit-meridiem-field { + padding-top: 0; + padding-bottom: 0; +} + +.form-select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +.form-select:where([size]:not([size="1"])) { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: unset; +} + +.aspect-h-9 { + --tw-aspect-h: 9; +} + +.aspect-w-16 { + position: relative; + padding-bottom: calc(var(--tw-aspect-h) / var(--tw-aspect-w) * 100%); + --tw-aspect-w: 16; +} + +.aspect-w-16 > * { + position: absolute; + height: 100%; + width: 100%; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.auth-card { + margin-left: auto; + margin-right: auto; + width: 100%; + max-width: 28rem; + border-radius: 1rem; + border-width: 1px; + border-color: rgb(229 231 235 / 0.5); + background-color: rgb(255 255 255 / 0.9); + padding: 2rem; + --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-backdrop-blur: blur(4px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} + +.auth-card:is(.dark *) { + border-color: rgb(55 65 81 / 0.5); + background-color: rgb(31 41 55 / 0.9); +} + +.auth-title { + margin-bottom: 2rem; + background-image: linear-gradient(to right, var(--tw-gradient-stops)); + --tw-gradient-from: #4f46e5 var(--tw-gradient-from-position); + --tw-gradient-to: rgb(79 70 229 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); + --tw-gradient-to: #e11d48 var(--tw-gradient-to-position); + -webkit-background-clip: text; + background-clip: text; + text-align: center; + font-size: 1.5rem; + line-height: 2rem; + font-weight: 700; + color: transparent; +} + +.auth-divider { + position: relative; + margin-top: 1.5rem; + margin-bottom: 1.5rem; + text-align: center; +} + +.auth-divider::before, .auth-divider::after { + position: absolute; + top: 50%; + width: 33.333333%; + border-top-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); + --tw-content: ''; + content: var(--tw-content); +} + +.auth-divider:is(.dark *)::before, .auth-divider:is(.dark *)::after { + --tw-border-opacity: 1; + border-color: rgb(55 65 81 / var(--tw-border-opacity)); +} + +.auth-divider::before { + left: 0px; +} + +.auth-divider::after { + right: 0px; +} + +.auth-divider span { + background-color: rgb(255 255 255 / 0.9); + padding-left: 1rem; + padding-right: 1rem; + font-size: 0.875rem; + line-height: 1.25rem; + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + +.auth-divider span:is(.dark *) { + background-color: rgb(31 41 55 / 0.9); + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.btn-social { + margin-bottom: 0.75rem; + display: flex; + width: 100%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + align-items: center; + justify-content: center; + border-radius: 0.5rem; + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); + padding-left: 1.5rem; + padding-right: 1.5rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.btn-social:hover { + --tw-scale-x: 1.02; + --tw-scale-y: 1.02; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + +.btn-social:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-color: rgb(79 70 229 / 0.5); + --tw-ring-offset-width: 2px; +} + +.btn-social:is(.dark *) { + --tw-border-opacity: 1; + border-color: rgb(55 65 81 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgb(229 231 235 / var(--tw-text-opacity)); +} + +.btn-social:hover:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)); +} + +.btn-discord { + border-color: transparent; + --tw-bg-opacity: 1; + background-color: rgb(88 101 242 / var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); + --tw-shadow-color: rgb(88 101 242 / 0.2); + --tw-shadow: var(--tw-shadow-colored); +} + +.btn-discord:hover { + --tw-bg-opacity: 1; + background-color: rgb(71 82 196 / var(--tw-bg-opacity)); +} + +.btn-google { + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); + --tw-shadow-color: rgb(229 231 235 / 0.5); + --tw-shadow: var(--tw-shadow-colored); +} + +.btn-google:hover { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + +.btn-google:is(.dark *) { + --tw-shadow-color: rgb(17 24 39 / 0.5); + --tw-shadow: var(--tw-shadow-colored); +} + +.password-requirements { + border-radius: 0.5rem; + border-width: 1px; + border-color: rgb(229 231 235 / 0.5); + background-color: rgb(249 250 251 / 0.8); + padding: 1rem; +} + +.password-requirements:is(.dark *) { + border-color: rgb(55 65 81 / 0.5); + background-color: rgb(31 41 55 / 0.8); +} + +.password-requirements ul > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} + +.password-requirements li { + display: flex; + align-items: center; +} + +.password-requirements li > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.password-requirements li { + font-size: 0.875rem; + line-height: 1.25rem; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 200ms; +} + +.password-requirements li i { + font-size: 0.75rem; + line-height: 1rem; + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 200ms; +} + +.password-requirements li.invalid { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + +.password-requirements li.invalid:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.password-requirements li.invalid i { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.password-requirements li.invalid i:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + +.password-requirements li.valid { + --tw-text-opacity: 1; + color: rgb(22 163 74 / var(--tw-text-opacity)); +} + +.password-requirements li.valid:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(74 222 128 / var(--tw-text-opacity)); +} + +.password-requirements li.valid i { + --tw-text-opacity: 1; + color: rgb(34 197 94 / var(--tw-text-opacity)); +} + +.password-requirements li.valid i:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(74 222 128 / var(--tw-text-opacity)); +} + +.form-input { + width: 100%; + border-radius: 0.5rem; + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); + background-color: rgb(255 255 255 / 0.7); + padding-left: 1rem; + padding-right: 1rem; + padding-top: 0.75rem; + padding-bottom: 0.75rem; + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity)); + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-backdrop-blur: blur(4px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.form-input:focus { + --tw-border-opacity: 1; + border-color: rgb(79 70 229 / var(--tw-border-opacity)); + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); + --tw-ring-color: rgb(79 70 229 / 0.5); +} + +.form-input:is(.dark *) { + --tw-border-opacity: 1; + border-color: rgb(55 65 81 / var(--tw-border-opacity)); + background-color: rgb(31 41 55 / 0.7); + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.form-label { + margin-bottom: 0.375rem; + display: block; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); +} + +.form-label:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(209 213 219 / var(--tw-text-opacity)); +} + +.form-error { + margin-top: 0.5rem; + font-size: 0.875rem; + line-height: 1.25rem; + --tw-text-opacity: 1; + color: rgb(220 38 38 / var(--tw-text-opacity)); +} + +.form-error:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(248 113 113 / var(--tw-text-opacity)); +} + +.alert { + margin-bottom: 1rem; + border-radius: 0.75rem; + padding: 1rem; + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + --tw-backdrop-blur: blur(4px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} + +.alert-error { + border-width: 1px; + --tw-border-opacity: 1; + border-color: rgb(254 202 202 / var(--tw-border-opacity)); + background-color: rgb(254 226 226 / 0.9); + --tw-text-opacity: 1; + color: rgb(153 27 27 / var(--tw-text-opacity)); +} + +.alert-error:is(.dark *) { + --tw-border-opacity: 1; + border-color: rgb(185 28 28 / var(--tw-border-opacity)); + background-color: rgb(153 27 27 / 0.3); + --tw-text-opacity: 1; + color: rgb(254 226 226 / var(--tw-text-opacity)); +} + +.static { + position: static; +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.right-3 { + right: 0.75rem; +} + +.top-1\/2 { + top: 50%; +} + +.top-2\.5 { + top: 0.625rem; +} + +.col-span-2 { + grid-column: span 2 / span 2; +} + +.col-span-full { + grid-column: 1 / -1; +} + +.mx-8 { + margin-left: 2rem; + margin-right: 2rem; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.my-6 { + margin-top: 1.5rem; + margin-bottom: 1.5rem; +} + +.mb-1 { + margin-bottom: 0.25rem; +} + +.mb-12 { + margin-bottom: 3rem; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mb-6 { + margin-bottom: 1.5rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.ml-6 { + margin-left: 1.5rem; +} + +.mr-1 { + margin-right: 0.25rem; +} + +.mr-2 { + margin-right: 0.5rem; +} + +.mr-3 { + margin-right: 0.75rem; +} + +.mt-1 { + margin-top: 0.25rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mt-6 { + margin-top: 1.5rem; +} + +.mt-auto { + margin-top: auto; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.grid { + display: grid; +} + +.hidden { + display: none; +} + +.h-24 { + height: 6rem; +} + +.h-4 { + height: 1rem; +} + +.h-48 { + height: 12rem; +} + +.h-5 { + height: 1.25rem; +} + +.h-8 { + height: 2rem; +} + +.min-h-\[calc\(100vh-16rem\)\] { + min-height: calc(100vh - 16rem); +} + +.min-h-screen { + min-height: 100vh; +} + +.w-1\/3 { + width: 33.333333%; +} + +.w-24 { + width: 6rem; +} + +.w-4 { + width: 1rem; +} + +.w-5 { + width: 1.25rem; +} + +.w-8 { + width: 2rem; +} + +.w-full { + width: 100%; +} + +.max-w-3xl { + max-width: 48rem; +} + +.max-w-md { + max-width: 28rem; +} + +.max-w-none { + max-width: none; +} + +.flex-1 { + flex: 1 1 0%; +} + +.flex-grow { + flex-grow: 1; +} + +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-start { + align-items: flex-start; +} + +.items-end { + align-items: flex-end; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-2 { + gap: 0.5rem; +} + +.gap-4 { + gap: 1rem; +} + +.gap-6 { + gap: 1.5rem; +} + +.space-x-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.5rem * var(--tw-space-x-reverse)); + margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1.5rem * var(--tw-space-x-reverse)); + margin-left: calc(1.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-8 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(2rem * var(--tw-space-x-reverse)); + margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} + +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} + +.space-y-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); +} + +.space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} + +.space-y-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); +} + +.overflow-hidden { + overflow: hidden; +} + +.rounded { + border-radius: 0.25rem; +} + +.rounded-2xl { + border-radius: 1rem; +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded-md { + border-radius: 0.375rem; +} + +.rounded-xl { + border-radius: 0.75rem; +} + +.border { + border-width: 1px; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-t { + border-top-width: 1px; +} + +.border-blue-200 { + --tw-border-opacity: 1; + border-color: rgb(191 219 254 / var(--tw-border-opacity)); +} + +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity)); +} + +.border-gray-200\/50 { + border-color: rgb(229 231 235 / 0.5); +} + +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgb(209 213 219 / var(--tw-border-opacity)); +} + +.border-green-200 { + --tw-border-opacity: 1; + border-color: rgb(187 247 208 / var(--tw-border-opacity)); +} + +.border-green-500 { + --tw-border-opacity: 1; + border-color: rgb(34 197 94 / var(--tw-border-opacity)); +} + +.border-red-200 { + --tw-border-opacity: 1; + border-color: rgb(254 202 202 / var(--tw-border-opacity)); +} + +.border-red-400 { + --tw-border-opacity: 1; + border-color: rgb(248 113 113 / var(--tw-border-opacity)); +} + +.border-red-500 { + --tw-border-opacity: 1; + border-color: rgb(239 68 68 / var(--tw-border-opacity)); +} + +.border-transparent { + border-color: transparent; +} + +.border-yellow-200 { + --tw-border-opacity: 1; + border-color: rgb(254 240 138 / var(--tw-border-opacity)); +} + +.bg-\[\#5865F2\] { + --tw-bg-opacity: 1; + background-color: rgb(88 101 242 / var(--tw-bg-opacity)); +} + +.bg-blue-100 { + --tw-bg-opacity: 1; + background-color: rgb(219 234 254 / var(--tw-bg-opacity)); +} + +.bg-blue-100\/90 { + background-color: rgb(219 234 254 / 0.9); +} + +.bg-blue-600 { + --tw-bg-opacity: 1; + background-color: rgb(37 99 235 / var(--tw-bg-opacity)); +} + +.bg-gray-100 { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); +} + +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)); +} + +.bg-gray-50 { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + +.bg-green-100 { + --tw-bg-opacity: 1; + background-color: rgb(220 252 231 / var(--tw-bg-opacity)); +} + +.bg-green-100\/90 { + background-color: rgb(220 252 231 / 0.9); +} + +.bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); +} + +.bg-red-100\/90 { + background-color: rgb(254 226 226 / 0.9); +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity)); +} + +.bg-white\/70 { + background-color: rgb(255 255 255 / 0.7); +} + +.bg-white\/90 { + background-color: rgb(255 255 255 / 0.9); +} + +.bg-yellow-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 249 195 / var(--tw-bg-opacity)); +} + +.bg-yellow-100\/90 { + background-color: rgb(254 249 195 / 0.9); +} + +.bg-gradient-to-br { + background-image: linear-gradient(to bottom right, var(--tw-gradient-stops)); +} + +.bg-gradient-to-r { + background-image: linear-gradient(to right, var(--tw-gradient-stops)); +} + +.from-primary { + --tw-gradient-from: #4f46e5 var(--tw-gradient-from-position); + --tw-gradient-to: rgb(79 70 229 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + +.from-white { + --tw-gradient-from: #fff var(--tw-gradient-from-position); + --tw-gradient-to: rgb(255 255 255 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + +.via-blue-50 { + --tw-gradient-to: rgb(239 246 255 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), #eff6ff var(--tw-gradient-via-position), var(--tw-gradient-to); +} + +.to-indigo-50 { + --tw-gradient-to: #eef2ff var(--tw-gradient-to-position); +} + +.to-secondary { + --tw-gradient-to: #e11d48 var(--tw-gradient-to-position); +} + +.bg-clip-text { + -webkit-background-clip: text; + background-clip: text; +} + +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} + +.p-2 { + padding: 0.5rem; +} + +.p-2\.5 { + padding: 0.625rem; +} + +.p-4 { + padding: 1rem; +} + +.p-6 { + padding: 1.5rem; +} + +.p-8 { + padding: 2rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.px-8 { + padding-left: 2rem; + padding-right: 2rem; +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +.py-12 { + padding-top: 3rem; + padding-bottom: 3rem; +} + +.py-16 { + padding-top: 4rem; + padding-bottom: 4rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-2\.5 { + padding-top: 0.625rem; + padding-bottom: 0.625rem; +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.py-6 { + padding-top: 1.5rem; + padding-bottom: 1.5rem; +} + +.py-8 { + padding-top: 2rem; + padding-bottom: 2rem; +} + +.pb-4 { + padding-bottom: 1rem; +} + +.text-center { + text-align: center; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.font-bold { + font-weight: 700; +} + +.font-medium { + font-weight: 500; +} + +.font-semibold { + font-weight: 600; +} + +.text-blue-500 { + --tw-text-opacity: 1; + color: rgb(59 130 246 / var(--tw-text-opacity)); +} + +.text-blue-600 { + --tw-text-opacity: 1; + color: rgb(37 99 235 / var(--tw-text-opacity)); +} + +.text-blue-800 { + --tw-text-opacity: 1; + color: rgb(30 64 175 / var(--tw-text-opacity)); +} + +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} + +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} + +.text-gray-700 { + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity)); +} + +.text-gray-800 { + --tw-text-opacity: 1; + color: rgb(31 41 55 / var(--tw-text-opacity)); +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity)); +} + +.text-green-800 { + --tw-text-opacity: 1; + color: rgb(22 101 52 / var(--tw-text-opacity)); +} + +.text-primary { + --tw-text-opacity: 1; + color: rgb(79 70 229 / var(--tw-text-opacity)); +} + +.text-red-600 { + --tw-text-opacity: 1; + color: rgb(220 38 38 / var(--tw-text-opacity)); +} + +.text-red-700 { + --tw-text-opacity: 1; + color: rgb(185 28 28 / var(--tw-text-opacity)); +} + +.text-red-800 { + --tw-text-opacity: 1; + color: rgb(153 27 27 / var(--tw-text-opacity)); +} + +.text-transparent { + color: transparent; +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.text-yellow-400 { + --tw-text-opacity: 1; + color: rgb(250 204 21 / var(--tw-text-opacity)); +} + +.text-yellow-500 { + --tw-text-opacity: 1; + color: rgb(234 179 8 / var(--tw-text-opacity)); +} + +.text-yellow-800 { + --tw-text-opacity: 1; + color: rgb(133 77 14 / var(--tw-text-opacity)); +} + +.shadow { + --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-md { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-xl { + --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-gray-200\/50 { + --tw-shadow-color: rgb(229 231 235 / 0.5); + --tw-shadow: var(--tw-shadow-colored); +} + +.ring-2 { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.ring-primary\/20 { + --tw-ring-color: rgb(79 70 229 / 0.2); +} + +.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); +} + +.backdrop-blur-lg { + --tw-backdrop-blur: blur(16px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} + +.backdrop-blur-sm { + --tw-backdrop-blur: blur(4px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} + +.transition { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, -webkit-backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter, -webkit-backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-colors { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-transform { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.content-\[\'\'\] { + --tw-content: ''; + content: var(--tw-content); +} + +.dark\:prose-invert:is(.dark *) { + --tw-prose-body: var(--tw-prose-invert-body); + --tw-prose-headings: var(--tw-prose-invert-headings); + --tw-prose-lead: var(--tw-prose-invert-lead); + --tw-prose-links: var(--tw-prose-invert-links); + --tw-prose-bold: var(--tw-prose-invert-bold); + --tw-prose-counters: var(--tw-prose-invert-counters); + --tw-prose-bullets: var(--tw-prose-invert-bullets); + --tw-prose-hr: var(--tw-prose-invert-hr); + --tw-prose-quotes: var(--tw-prose-invert-quotes); + --tw-prose-quote-borders: var(--tw-prose-invert-quote-borders); + --tw-prose-captions: var(--tw-prose-invert-captions); + --tw-prose-kbd: var(--tw-prose-invert-kbd); + --tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows); + --tw-prose-code: var(--tw-prose-invert-code); + --tw-prose-pre-code: var(--tw-prose-invert-pre-code); + --tw-prose-pre-bg: var(--tw-prose-invert-pre-bg); + --tw-prose-th-borders: var(--tw-prose-invert-th-borders); + --tw-prose-td-borders: var(--tw-prose-invert-td-borders); +} + +.first\:rounded-t-lg:first-child { + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; +} + +.last\:mb-0:last-child { + margin-bottom: 0px; +} + +.hover\:-translate-y-1:hover { + --tw-translate-y: -0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.hover\:translate-x-2:hover { + --tw-translate-x: 0.5rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.hover\:scale-105:hover { + --tw-scale-x: 1.05; + --tw-scale-y: 1.05; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.hover\:scale-\[1\.02\]:hover { + --tw-scale-x: 1.02; + --tw-scale-y: 1.02; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.hover\:border-primary\/20:hover { + border-color: rgb(79 70 229 / 0.2); +} + +.hover\:bg-\[\#4752C4\]:hover { + --tw-bg-opacity: 1; + background-color: rgb(71 82 196 / var(--tw-bg-opacity)); +} + +.hover\:bg-blue-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(29 78 216 / var(--tw-bg-opacity)); +} + +.hover\:bg-gray-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); +} + +.hover\:bg-gray-50:hover { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + +.hover\:bg-primary\/10:hover { + background-color: rgb(79 70 229 / 0.1); +} + +.hover\:from-primary\/90:hover { + --tw-gradient-from: rgb(79 70 229 / 0.9) var(--tw-gradient-from-position); + --tw-gradient-to: rgb(79 70 229 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + +.hover\:to-secondary\/90:hover { + --tw-gradient-to: rgb(225 29 72 / 0.9) var(--tw-gradient-to-position); +} + +.hover\:text-blue-500:hover { + --tw-text-opacity: 1; + color: rgb(59 130 246 / var(--tw-text-opacity)); +} + +.hover\:text-blue-600:hover { + --tw-text-opacity: 1; + color: rgb(37 99 235 / var(--tw-text-opacity)); +} + +.hover\:text-blue-700:hover { + --tw-text-opacity: 1; + color: rgb(29 78 216 / var(--tw-text-opacity)); +} + +.hover\:text-primary:hover { + --tw-text-opacity: 1; + color: rgb(79 70 229 / var(--tw-text-opacity)); +} + +.hover\:text-primary\/80:hover { + color: rgb(79 70 229 / 0.8); +} + +.hover\:underline:hover { + text-decoration-line: underline; +} + +.focus\:border-blue-500:focus { + --tw-border-opacity: 1; + border-color: rgb(59 130 246 / var(--tw-border-opacity)); +} + +.focus\:border-primary:focus { + --tw-border-opacity: 1; + border-color: rgb(79 70 229 / var(--tw-border-opacity)); +} + +.focus\:underline:focus { + text-decoration-line: underline; +} + +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.focus\:ring-blue-500:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); +} + +.focus\:ring-primary\/50:focus { + --tw-ring-color: rgb(79 70 229 / 0.5); +} + +.focus\:ring-offset-2:focus { + --tw-ring-offset-width: 2px; +} + +.dark\:inline:is(.dark *) { + display: inline; +} + +.dark\:hidden:is(.dark *) { + display: none; +} + +.dark\:border-gray-600:is(.dark *) { + --tw-border-opacity: 1; + border-color: rgb(75 85 99 / var(--tw-border-opacity)); +} + +.dark\:border-gray-600\/50:is(.dark *) { + border-color: rgb(75 85 99 / 0.5); +} + +.dark\:border-gray-700:is(.dark *) { + --tw-border-opacity: 1; + border-color: rgb(55 65 81 / var(--tw-border-opacity)); +} + +.dark\:border-gray-700\/50:is(.dark *) { + border-color: rgb(55 65 81 / 0.5); +} + +.dark\:bg-blue-400\/30:is(.dark *) { + background-color: rgb(96 165 250 / 0.3); +} + +.dark\:bg-blue-800\/30:is(.dark *) { + background-color: rgb(30 64 175 / 0.3); +} + +.dark\:bg-gray-600:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(75 85 99 / var(--tw-bg-opacity)); +} + +.dark\:bg-gray-700:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)); +} + +.dark\:bg-gray-700\/50:is(.dark *) { + background-color: rgb(55 65 81 / 0.5); +} + +.dark\:bg-gray-800:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity)); +} + +.dark\:bg-gray-800\/70:is(.dark *) { + background-color: rgb(31 41 55 / 0.7); +} + +.dark\:bg-gray-800\/90:is(.dark *) { + background-color: rgb(31 41 55 / 0.9); +} + +.dark\:bg-green-800\/30:is(.dark *) { + background-color: rgb(22 101 52 / 0.3); +} + +.dark\:bg-red-800\/30:is(.dark *) { + background-color: rgb(153 27 27 / 0.3); +} + +.dark\:bg-yellow-400\/30:is(.dark *) { + background-color: rgb(250 204 21 / 0.3); +} + +.dark\:bg-yellow-800\/30:is(.dark *) { + background-color: rgb(133 77 14 / 0.3); +} + +.dark\:from-gray-950:is(.dark *) { + --tw-gradient-from: #030712 var(--tw-gradient-from-position); + --tw-gradient-to: rgb(3 7 18 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + +.dark\:via-indigo-950:is(.dark *) { + --tw-gradient-to: rgb(30 27 75 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), #1e1b4b var(--tw-gradient-via-position), var(--tw-gradient-to); +} + +.dark\:to-purple-950:is(.dark *) { + --tw-gradient-to: #3b0764 var(--tw-gradient-to-position); +} + +.dark\:text-blue-100:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(219 234 254 / var(--tw-text-opacity)); +} + +.dark\:text-blue-200:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(191 219 254 / var(--tw-text-opacity)); +} + +.dark\:text-blue-400:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(96 165 250 / var(--tw-text-opacity)); +} + +.dark\:text-gray-200:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(229 231 235 / var(--tw-text-opacity)); +} + +.dark\:text-gray-300:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(209 213 219 / var(--tw-text-opacity)); +} + +.dark\:text-gray-400:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity)); +} + +.dark\:text-green-100:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(220 252 231 / var(--tw-text-opacity)); +} + +.dark\:text-red-100:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(254 226 226 / var(--tw-text-opacity)); +} + +.dark\:text-white:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} + +.dark\:text-yellow-100:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(254 249 195 / var(--tw-text-opacity)); +} + +.dark\:text-yellow-200:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(254 240 138 / var(--tw-text-opacity)); +} + +.dark\:text-yellow-300:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(253 224 71 / var(--tw-text-opacity)); +} + +.dark\:ring-1:is(.dark *) { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + +.dark\:ring-blue-400\/30:is(.dark *) { + --tw-ring-color: rgb(96 165 250 / 0.3); +} + +.dark\:ring-yellow-400\/30:is(.dark *) { + --tw-ring-color: rgb(250 204 21 / 0.3); +} + +.dark\:hover\:bg-gray-700:hover:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(55 65 81 / var(--tw-bg-opacity)); +} + +.dark\:hover\:bg-primary\/20:hover:is(.dark *) { + background-color: rgb(79 70 229 / 0.2); +} + +.dark\:hover\:text-blue-300:hover:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(147 197 253 / var(--tw-text-opacity)); +} + +.dark\:hover\:text-primary:hover:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(79 70 229 / var(--tw-text-opacity)); +} + +@media (min-width: 640px) { + .sm\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (min-width: 768px) { + .md\:mt-0 { + margin-top: 0px; + } + + .md\:flex { + display: flex; + } + + .md\:hidden { + display: none; + } + + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .md\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .md\:flex-row { + flex-direction: row; + } + + .md\:items-center { + align-items: center; + } + + .md\:text-2xl { + font-size: 1.5rem; + line-height: 2rem; + } + + .md\:text-5xl { + font-size: 3rem; + line-height: 1; + } +} + +@media (min-width: 1024px) { + .lg\:col-span-1 { + grid-column: span 1 / span 1; + } + + .lg\:col-span-2 { + grid-column: span 2 / span 2; + } + + .lg\:grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .lg\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .lg\:text-6xl { + font-size: 3.75rem; + line-height: 1; + } +} \ No newline at end of file diff --git a/staticfiles/css/styles.css b/staticfiles/css/styles.css deleted file mode 100644 index 87ef0ff3..00000000 --- a/staticfiles/css/styles.css +++ /dev/null @@ -1,199 +0,0 @@ -/* Base styles */ -:root { - --primary-color: #2563eb; - --secondary-color: #4b5563; - --success-color: #10b981; - --danger-color: #ef4444; - --warning-color: #f59e0b; -} - -/* Dark mode variables */ -.dark { - --bg-primary: #1f2937; - --bg-secondary: #374151; - --text-primary: #f3f4f6; - --text-secondary: #9ca3af; -} - -/* Light mode variables */ -:root:not(.dark) { - --bg-primary: #ffffff; - --bg-secondary: #f3f4f6; - --text-primary: #1f2937; - --text-secondary: #4b5563; -} - -/* Typography */ -body { - font-family: 'Roboto', sans-serif; - line-height: 1.5; - color: var(--text-primary); - background-color: var(--bg-secondary); -} - -/* Buttons */ -.btn-primary { - @apply inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500; -} - -.btn-secondary { - @apply inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500; -} - -/* Navigation */ -.nav-link { - @apply inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium; -} - -.nav-link:not(.active) { - @apply border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700; -} - -.nav-link.active { - @apply border-blue-500 text-gray-900; -} - -.mobile-nav-link { - @apply block pl-3 pr-4 py-2 border-l-4 text-base font-medium; -} - -.mobile-nav-link:not(.active) { - @apply border-transparent text-gray-600 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-800; -} - -.mobile-nav-link.active { - @apply border-blue-500 text-blue-700 bg-blue-50; -} - -/* Forms */ -.form-input { - @apply mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm; -} - -.form-select { - @apply mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm; -} - -.form-checkbox { - @apply h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded; -} - -/* Cards */ -.card { - @apply bg-white dark:bg-gray-800 overflow-hidden shadow rounded-lg; -} - -.card-header { - @apply px-4 py-5 sm:px-6; -} - -.card-body { - @apply px-4 py-5 sm:p-6; -} - -.card-footer { - @apply px-4 py-4 sm:px-6; -} - -/* Alerts */ -.alert { - @apply rounded-md p-4 mb-4; -} - -.alert-success { - @apply bg-green-50 text-green-800; -} - -.alert-error { - @apply bg-red-50 text-red-800; -} - -.alert-warning { - @apply bg-yellow-50 text-yellow-800; -} - -.alert-info { - @apply bg-blue-50 text-blue-800; -} - -/* Pagination */ -.pagination-link { - @apply relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50; -} - -.pagination-current { - @apply relative inline-flex items-center px-4 py-2 border border-blue-500 bg-blue-50 text-sm font-medium text-blue-600; -} - -/* Menu Items */ -.menu-item { - @apply block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700; -} - -/* Transitions */ -.transition-all { - transition-property: all; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - -/* Responsive Images */ -.img-responsive { - @apply w-full h-full object-cover; -} - -/* Stats Display */ -.stat-value { - @apply text-4xl font-bold text-gray-900 dark:text-white; -} - -.stat-label { - @apply text-sm font-medium text-gray-500 dark:text-gray-400; -} - -/* Review Stars */ -.stars { - @apply flex items-center; -} - -.star { - @apply text-yellow-400; -} - -.star-empty { - @apply text-gray-300; -} - -/* Status Badges */ -.status-badge { - @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium; -} - -.status-operating { - @apply bg-green-100 text-green-800; -} - -.status-closed { - @apply bg-red-100 text-red-800; -} - -.status-construction { - @apply bg-yellow-100 text-yellow-800; -} - -/* User Roles */ -.role-badge { - @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium; -} - -.role-admin { - @apply bg-purple-100 text-purple-800; -} - -.role-moderator { - @apply bg-blue-100 text-blue-800; -} - -.role-user { - @apply bg-gray-100 text-gray-800; -} diff --git a/staticfiles/css/tailwind.css b/staticfiles/css/tailwind.css new file mode 100644 index 00000000..680a00cc --- /dev/null +++ b/staticfiles/css/tailwind.css @@ -0,0 +1 @@ +/*! tailwindcss v3.4.11 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:Poppins,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}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;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],input:where(:not([type])),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,input:where(:not([type])):focus,select:focus,textarea:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);border-color:#2563eb;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}input::-moz-placeholder,textarea::-moz-placeholder{color:#6b7280;opacity:1}input::placeholder,textarea::placeholder{color:#6b7280;opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-bottom:0;padding-top:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;background-origin:border-box;border-color:#6b7280;border-width:1px;color:#2563eb;display:inline-block;flex-shrink:0;height:1rem;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;-webkit-user-select:none;-moz-user-select:none;user-select:none;vertical-align:middle;width:1rem;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{background-color:currentColor;background-position:50%;background-repeat:no-repeat;background-size:100% 100%;border-color:#0000}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0'/%3E%3C/svg%3E")}@media (forced-colors:active) {[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}@media (forced-colors:active) {[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=checkbox]:indeterminate,[type=radio]:checked:focus,[type=radio]:checked:hover{background-color:currentColor;border-color:#0000}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:100% 100%}@media (forced-colors:active) {[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{background-color:currentColor;border-color:#0000}[type=file]{background:unset;border-color:inherit;border-radius:0;border-width:0;font-size:unset;line-height:inherit;padding:0}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.prose{color:var(--tw-prose-body);max-width:65ch}.prose :where(p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em;margin-top:1.25em}.prose :where([class~=lead]):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-lead);font-size:1.25em;line-height:1.6;margin-bottom:1.2em;margin-top:1.2em}.prose :where(a):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-links);font-weight:500;text-decoration:underline}.prose :where(strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-bold);font-weight:600}.prose :where(a strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(ol):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal;margin-bottom:1.25em;margin-top:1.25em;padding-inline-start:1.625em}.prose :where(ol[type=A]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=A s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-alpha}.prose :where(ol[type=a s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-alpha}.prose :where(ol[type=I]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type=I s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:upper-roman}.prose :where(ol[type=i s]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:lower-roman}.prose :where(ol[type="1"]):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:decimal}.prose :where(ul):not(:where([class~=not-prose],[class~=not-prose] *)){list-style-type:disc;margin-bottom:1.25em;margin-top:1.25em;padding-inline-start:1.625em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-counters);font-weight:400}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *))::marker{color:var(--tw-prose-bullets)}.prose :where(dt):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;margin-top:1.25em}.prose :where(hr):not(:where([class~=not-prose],[class~=not-prose] *)){border-color:var(--tw-prose-hr);border-top-width:1px;margin-bottom:3em;margin-top:3em}.prose :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)){border-inline-start-color:var(--tw-prose-quote-borders);border-inline-start-width:.25rem;color:var(--tw-prose-quotes);font-style:italic;font-weight:500;margin-bottom:1.6em;margin-top:1.6em;padding-inline-start:1em;quotes:"\201C""\201D""\2018""\2019"}.prose :where(blockquote p:first-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:open-quote}.prose :where(blockquote p:last-of-type):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:close-quote}.prose :where(h1):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-size:2.25em;font-weight:800;line-height:1.1111111;margin-bottom:.8888889em;margin-top:0}.prose :where(h1 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:900}.prose :where(h2):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-size:1.5em;font-weight:700;line-height:1.3333333;margin-bottom:1em;margin-top:2em}.prose :where(h2 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:800}.prose :where(h3):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-size:1.25em;font-weight:600;line-height:1.6;margin-bottom:.6em;margin-top:1.6em}.prose :where(h3 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(h4):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;line-height:1.5;margin-bottom:.5em;margin-top:1.5em}.prose :where(h4 strong):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-weight:700}.prose :where(img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:2em;margin-top:2em}.prose :where(picture):not(:where([class~=not-prose],[class~=not-prose] *)){display:block;margin-bottom:2em;margin-top:2em}.prose :where(video):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:2em;margin-top:2em}.prose :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)){border-radius:.3125rem;box-shadow:0 0 0 1px rgb(var(--tw-prose-kbd-shadows)/10%),0 3px 0 rgb(var(--tw-prose-kbd-shadows)/10%);color:var(--tw-prose-kbd);font-family:inherit;font-size:.875em;font-weight:500;padding-inline-end:.375em;padding-bottom:.1875em;padding-top:.1875em;padding-inline-start:.375em}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-code);font-size:.875em;font-weight:600}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:"`"}.prose :where(code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:"`"}.prose :where(a code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h1 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(h2 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.875em}.prose :where(h3 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit;font-size:.9em}.prose :where(h4 code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(blockquote code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(thead th code):not(:where([class~=not-prose],[class~=not-prose] *)){color:inherit}.prose :where(pre):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:var(--tw-prose-pre-bg);border-radius:.375rem;color:var(--tw-prose-pre-code);font-size:.875em;font-weight:400;line-height:1.7142857;margin-bottom:1.7142857em;margin-top:1.7142857em;overflow-x:auto;padding-inline-end:1.1428571em;padding-bottom:.8571429em;padding-top:.8571429em;padding-inline-start:1.1428571em}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)){background-color:initial;border-radius:0;border-width:0;color:inherit;font-family:inherit;font-size:inherit;font-weight:inherit;line-height:inherit;padding:0}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):before{content:none}.prose :where(pre code):not(:where([class~=not-prose],[class~=not-prose] *)):after{content:none}.prose :where(table):not(:where([class~=not-prose],[class~=not-prose] *)){font-size:.875em;line-height:1.7142857;margin-bottom:2em;margin-top:2em;table-layout:auto;width:100%}.prose :where(thead):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-color:var(--tw-prose-th-borders);border-bottom-width:1px}.prose :where(thead th):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-headings);font-weight:600;padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-inline-start:.5714286em;vertical-align:bottom}.prose :where(tbody tr):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-color:var(--tw-prose-td-borders);border-bottom-width:1px}.prose :where(tbody tr:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){border-bottom-width:0}.prose :where(tbody td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:initial}.prose :where(tfoot):not(:where([class~=not-prose],[class~=not-prose] *)){border-top-color:var(--tw-prose-th-borders);border-top-width:1px}.prose :where(tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){vertical-align:top}.prose :where(th,td):not(:where([class~=not-prose],[class~=not-prose] *)){text-align:start}.prose :where(figure>*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0;margin-top:0}.prose :where(figcaption):not(:where([class~=not-prose],[class~=not-prose] *)){color:var(--tw-prose-captions);font-size:.875em;line-height:1.4285714;margin-top:.8571429em}.prose{--tw-prose-body:#374151;--tw-prose-headings:#111827;--tw-prose-lead:#4b5563;--tw-prose-links:#111827;--tw-prose-bold:#111827;--tw-prose-counters:#6b7280;--tw-prose-bullets:#d1d5db;--tw-prose-hr:#e5e7eb;--tw-prose-quotes:#111827;--tw-prose-quote-borders:#e5e7eb;--tw-prose-captions:#6b7280;--tw-prose-kbd:#111827;--tw-prose-kbd-shadows:17 24 39;--tw-prose-code:#111827;--tw-prose-pre-code:#e5e7eb;--tw-prose-pre-bg:#1f2937;--tw-prose-th-borders:#d1d5db;--tw-prose-td-borders:#e5e7eb;--tw-prose-invert-body:#d1d5db;--tw-prose-invert-headings:#fff;--tw-prose-invert-lead:#9ca3af;--tw-prose-invert-links:#fff;--tw-prose-invert-bold:#fff;--tw-prose-invert-counters:#9ca3af;--tw-prose-invert-bullets:#4b5563;--tw-prose-invert-hr:#374151;--tw-prose-invert-quotes:#f3f4f6;--tw-prose-invert-quote-borders:#374151;--tw-prose-invert-captions:#9ca3af;--tw-prose-invert-kbd:#fff;--tw-prose-invert-kbd-shadows:255 255 255;--tw-prose-invert-code:#fff;--tw-prose-invert-pre-code:#d1d5db;--tw-prose-invert-pre-bg:#00000080;--tw-prose-invert-th-borders:#4b5563;--tw-prose-invert-td-borders:#374151;font-size:1rem;line-height:1.75}.prose :where(picture>img):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0;margin-top:0}.prose :where(li):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:.5em;margin-top:.5em}.prose :where(ol>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(ul>li):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:.375em}.prose :where(.prose>ul>li p):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:.75em;margin-top:.75em}.prose :where(.prose>ul>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ul>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(.prose>ol>li>p:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:1.25em}.prose :where(.prose>ol>li>p:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em}.prose :where(ul ul,ul ol,ol ul,ol ol):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:.75em;margin-top:.75em}.prose :where(dl):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:1.25em;margin-top:1.25em}.prose :where(dd):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:.5em;padding-inline-start:1.625em}.prose :where(hr+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h2+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h3+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(h4+*):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(thead th:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(thead th:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(tbody td,tfoot td):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:.5714286em;padding-bottom:.5714286em;padding-top:.5714286em;padding-inline-start:.5714286em}.prose :where(tbody td:first-child,tfoot td:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-start:0}.prose :where(tbody td:last-child,tfoot td:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){padding-inline-end:0}.prose :where(figure):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:2em;margin-top:2em}.prose :where(.prose>:first-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-top:0}.prose :where(.prose>:last-child):not(:where([class~=not-prose],[class~=not-prose] *)){margin-bottom:0}.form-input,.form-multiselect,.form-select,.form-textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#6b7280;border-radius:0;border-width:1px;font-size:1rem;line-height:1.5rem;padding:.5rem .75rem;--tw-shadow:0 0 #0000}.form-input:focus,.form-multiselect:focus,.form-select:focus,.form-textarea:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);border-color:#2563eb;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.form-input::-moz-placeholder,.form-textarea::-moz-placeholder{color:#6b7280;opacity:1}.form-input::placeholder,.form-textarea::placeholder{color:#6b7280;opacity:1}.form-input::-webkit-datetime-edit-fields-wrapper{padding:0}.form-input::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}.form-input::-webkit-datetime-edit{display:inline-flex}.form-input::-webkit-datetime-edit,.form-input::-webkit-datetime-edit-day-field,.form-input::-webkit-datetime-edit-hour-field,.form-input::-webkit-datetime-edit-meridiem-field,.form-input::-webkit-datetime-edit-millisecond-field,.form-input::-webkit-datetime-edit-minute-field,.form-input::-webkit-datetime-edit-month-field,.form-input::-webkit-datetime-edit-second-field,.form-input::-webkit-datetime-edit-year-field{padding-bottom:0;padding-top:0}.form-select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}.form-select:where([size]:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}.aspect-h-9{--tw-aspect-h:9}.aspect-w-16{padding-bottom:calc(var(--tw-aspect-h)/var(--tw-aspect-w)*100%);position:relative;--tw-aspect-w:16}.aspect-w-16>*{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%}.auth-card{background-color:#ffffffe6;border-color:#e5e7eb80;border-radius:1rem;border-width:1px;margin-left:auto;margin-right:auto;max-width:28rem;padding:2rem;width:100%;--tw-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-backdrop-blur:blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.auth-card:is(.dark *){background-color:#1f2937e6;border-color:#37415180}.auth-title{background-image:linear-gradient(to right,var(--tw-gradient-stops));margin-bottom:2rem;--tw-gradient-from:#4f46e5 var(--tw-gradient-from-position);--tw-gradient-to:#4f46e500 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to);--tw-gradient-to:#e11d48 var(--tw-gradient-to-position);-webkit-background-clip:text;background-clip:text;color:#0000;font-size:1.5rem;font-weight:700;line-height:2rem}.auth-divider,.auth-title{text-align:center}.auth-divider{margin-bottom:1.5rem;margin-top:1.5rem;position:relative}.auth-divider:after,.auth-divider:before{border-top-width:1px;position:absolute;top:50%;width:33.333333%;--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity));--tw-content:"";content:var(--tw-content)}.auth-divider:is(.dark *):after,.auth-divider:is(.dark *):before{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}.auth-divider:before{left:0}.auth-divider:after{right:0}.auth-divider span{background-color:#ffffffe6;font-size:.875rem;line-height:1.25rem;padding-left:1rem;padding-right:1rem;--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.auth-divider span:is(.dark *){background-color:#1f2937e6;--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.btn-social{align-items:center;border-radius:.5rem;border-width:1px;display:flex;justify-content:center;margin-bottom:.75rem;width:100%;--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));font-size:.875rem;font-weight:500;line-height:1.25rem;padding:.75rem 1.5rem;--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity));--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.btn-social,.btn-social:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.btn-social:hover{--tw-scale-x:1.02;--tw-scale-y:1.02;--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.btn-social:focus{outline:2px solid #0000;outline-offset:2px;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:#4f46e580;--tw-ring-offset-width:2px}.btn-social:is(.dark *){--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.btn-social:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.btn-discord{border-color:#0000;--tw-bg-opacity:1;background-color:rgb(88 101 242/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity));--tw-shadow-color:#5865f233;--tw-shadow:var(--tw-shadow-colored)}.btn-discord:hover{--tw-bg-opacity:1;background-color:rgb(71 82 196/var(--tw-bg-opacity))}.btn-google{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity));--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity));--tw-shadow-color:#e5e7eb80;--tw-shadow:var(--tw-shadow-colored)}.btn-google:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.btn-google:is(.dark *){--tw-shadow-color:#11182780;--tw-shadow:var(--tw-shadow-colored)}.static{position:static}.absolute{position:absolute}.relative{position:relative}.right-3{right:.75rem}.top-1\/2{top:50%}.top-2\.5{top:.625rem}.col-span-2{grid-column:span 2/span 2}.col-span-full{grid-column:1/-1}.mx-8{margin-left:2rem;margin-right:2rem}.mx-auto{margin-left:auto;margin-right:auto}.my-6{margin-bottom:1.5rem;margin-top:1.5rem}.mb-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-6{margin-left:1.5rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-auto{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-24{height:6rem}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-8{height:2rem}.min-h-\[calc\(100vh-16rem\)\]{min-height:calc(100vh - 16rem)}.min-h-screen{min-height:100vh}.w-1\/3{width:33.333333%}.w-24{width:6rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-8{width:2rem}.w-full{width:100%}.max-w-3xl{max-width:48rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.flex-1{flex:1 1 0%}.flex-grow{flex-grow:1}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.75rem*var(--tw-space-x-reverse))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.5rem*var(--tw-space-x-reverse))}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2rem*var(--tw-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.5rem*var(--tw-space-y-reverse));margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)))}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.border-gray-200\/50{border-color:#e5e7eb80}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-green-200{--tw-border-opacity:1;border-color:rgb(187 247 208/var(--tw-border-opacity))}.border-green-500{--tw-border-opacity:1;border-color:rgb(34 197 94/var(--tw-border-opacity))}.border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.border-red-500{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity))}.border-transparent{border-color:#0000}.border-yellow-200{--tw-border-opacity:1;border-color:rgb(254 240 138/var(--tw-border-opacity))}.bg-\[\#5865F2\]{--tw-bg-opacity:1;background-color:rgb(88 101 242/var(--tw-bg-opacity))}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity))}.bg-blue-100\/90{background-color:#dbeafee6}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-green-100{--tw-bg-opacity:1;background-color:rgb(220 252 231/var(--tw-bg-opacity))}.bg-green-100\/90{background-color:#dcfce7e6}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-red-100\/90{background-color:#fee2e2e6}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-white\/70{background-color:#ffffffb3}.bg-white\/90{background-color:#ffffffe6}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity))}.bg-yellow-100\/90{background-color:#fef9c3e6}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-primary{--tw-gradient-from:#4f46e5 var(--tw-gradient-from-position);--tw-gradient-to:#4f46e500 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-white{--tw-gradient-from:#fff var(--tw-gradient-from-position);--tw-gradient-to:#fff0 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-blue-50{--tw-gradient-to:#eff6ff00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#eff6ff var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-indigo-50{--tw-gradient-to:#eef2ff var(--tw-gradient-to-position)}.to-secondary{--tw-gradient-to:#e11d48 var(--tw-gradient-to-position)}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.object-cover{-o-object-fit:cover;object-fit:cover}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-12{padding-bottom:3rem;padding-top:3rem}.py-16{padding-bottom:4rem;padding-top:4rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-2\.5{padding-bottom:.625rem;padding-top:.625rem}.py-3{padding-bottom:.75rem;padding-top:.75rem}.py-4{padding-bottom:1rem;padding-top:1rem}.py-6{padding-bottom:1.5rem;padding-top:1.5rem}.py-8{padding-bottom:2rem;padding-top:2rem}.pb-4{padding-bottom:1rem}.text-center{text-align:center}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.text-blue-800{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-green-800{--tw-text-opacity:1;color:rgb(22 101 52/var(--tw-text-opacity))}.text-primary{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity))}.text-transparent{color:#0000}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity))}.text-yellow-500{--tw-text-opacity:1;color:rgb(234 179 8/var(--tw-text-opacity))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity))}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-xl{--tw-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-gray-200\/50{--tw-shadow-color:#e5e7eb80;--tw-shadow:var(--tw-shadow-colored)}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-primary\/20{--tw-ring-color:#4f46e533}.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)}.backdrop-blur-lg{--tw-backdrop-blur:blur(16px)}.backdrop-blur-lg,.backdrop-blur-sm{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.content-\[\'\'\]{--tw-content:"";content:var(--tw-content)}.dark\:prose-invert:is(.dark *){--tw-prose-body:var(--tw-prose-invert-body);--tw-prose-headings:var(--tw-prose-invert-headings);--tw-prose-lead:var(--tw-prose-invert-lead);--tw-prose-links:var(--tw-prose-invert-links);--tw-prose-bold:var(--tw-prose-invert-bold);--tw-prose-counters:var(--tw-prose-invert-counters);--tw-prose-bullets:var(--tw-prose-invert-bullets);--tw-prose-hr:var(--tw-prose-invert-hr);--tw-prose-quotes:var(--tw-prose-invert-quotes);--tw-prose-quote-borders:var(--tw-prose-invert-quote-borders);--tw-prose-captions:var(--tw-prose-invert-captions);--tw-prose-kbd:var(--tw-prose-invert-kbd);--tw-prose-kbd-shadows:var(--tw-prose-invert-kbd-shadows);--tw-prose-code:var(--tw-prose-invert-code);--tw-prose-pre-code:var(--tw-prose-invert-pre-code);--tw-prose-pre-bg:var(--tw-prose-invert-pre-bg);--tw-prose-th-borders:var(--tw-prose-invert-th-borders);--tw-prose-td-borders:var(--tw-prose-invert-td-borders)}.first\:rounded-t-lg:first-child{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.last\:mb-0:last-child{margin-bottom:0}.hover\:-translate-y-1:hover{--tw-translate-y:-0.25rem}.hover\:-translate-y-1:hover,.hover\:translate-x-2:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:translate-x-2:hover{--tw-translate-x:0.5rem}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:scale-105:hover,.hover\:scale-\[1\.02\]:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-\[1\.02\]:hover{--tw-scale-x:1.02;--tw-scale-y:1.02}.hover\:border-primary\/20:hover{border-color:#4f46e533}.hover\:bg-\[\#4752C4\]:hover{--tw-bg-opacity:1;background-color:rgb(71 82 196/var(--tw-bg-opacity))}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.hover\:bg-primary\/10:hover{background-color:#4f46e51a}.hover\:from-primary\/90:hover{--tw-gradient-from:#4f46e5e6 var(--tw-gradient-from-position);--tw-gradient-to:#4f46e500 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:to-secondary\/90:hover{--tw-gradient-to:#e11d48e6 var(--tw-gradient-to-position)}.hover\:text-blue-500:hover{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity))}.hover\:text-blue-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.hover\:text-blue-700:hover{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity))}.hover\:text-primary:hover{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}.hover\:text-primary\/80:hover{color:#4f46e5cc}.hover\:underline:hover{text-decoration-line:underline}.focus\:border-blue-500:focus{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity))}.focus\:border-primary:focus{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity))}.focus\:underline:focus{text-decoration-line:underline}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity))}.focus\:ring-primary\/50:focus{--tw-ring-color:#4f46e580}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.dark\:inline:is(.dark *){display:inline}.dark\:hidden:is(.dark *){display:none}.dark\:border-gray-600:is(.dark *){--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity))}.dark\:border-gray-600\/50:is(.dark *){border-color:#4b556380}.dark\:border-gray-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity))}.dark\:border-gray-700\/50:is(.dark *){border-color:#37415180}.dark\:bg-blue-400\/30:is(.dark *){background-color:#60a5fa4d}.dark\:bg-blue-800\/30:is(.dark *){background-color:#1e40af4d}.dark\:bg-gray-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity))}.dark\:bg-gray-700:is(.dark *){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.dark\:bg-gray-700\/50:is(.dark *){background-color:#37415180}.dark\:bg-gray-800:is(.dark *){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity))}.dark\:bg-gray-800\/70:is(.dark *){background-color:#1f2937b3}.dark\:bg-gray-800\/90:is(.dark *){background-color:#1f2937e6}.dark\:bg-green-800\/30:is(.dark *){background-color:#1665344d}.dark\:bg-red-800\/30:is(.dark *){background-color:#991b1b4d}.dark\:bg-yellow-400\/30:is(.dark *){background-color:#facc154d}.dark\:bg-yellow-800\/30:is(.dark *){background-color:#854d0e4d}.dark\:from-gray-950:is(.dark *){--tw-gradient-from:#030712 var(--tw-gradient-from-position);--tw-gradient-to:#03071200 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:via-indigo-950:is(.dark *){--tw-gradient-to:#1e1b4b00 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1e1b4b var(--tw-gradient-via-position),var(--tw-gradient-to)}.dark\:to-purple-950:is(.dark *){--tw-gradient-to:#3b0764 var(--tw-gradient-to-position)}.dark\:text-blue-100:is(.dark *){--tw-text-opacity:1;color:rgb(219 234 254/var(--tw-text-opacity))}.dark\:text-blue-200:is(.dark *){--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity))}.dark\:text-blue-400:is(.dark *){--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity))}.dark\:text-gray-200:is(.dark *){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity))}.dark\:text-gray-300:is(.dark *){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity))}.dark\:text-gray-400:is(.dark *){--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.dark\:text-green-100:is(.dark *){--tw-text-opacity:1;color:rgb(220 252 231/var(--tw-text-opacity))}.dark\:text-red-100:is(.dark *){--tw-text-opacity:1;color:rgb(254 226 226/var(--tw-text-opacity))}.dark\:text-white:is(.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.dark\:text-yellow-100:is(.dark *){--tw-text-opacity:1;color:rgb(254 249 195/var(--tw-text-opacity))}.dark\:text-yellow-200:is(.dark *){--tw-text-opacity:1;color:rgb(254 240 138/var(--tw-text-opacity))}.dark\:text-yellow-300:is(.dark *){--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity))}.dark\:ring-1:is(.dark *){--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.dark\:ring-blue-400\/30:is(.dark *){--tw-ring-color:#60a5fa4d}.dark\:ring-yellow-400\/30:is(.dark *){--tw-ring-color:#facc154d}.dark\:hover\:bg-gray-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity))}.dark\:hover\:bg-primary\/20:hover:is(.dark *){background-color:#4f46e533}.dark\:hover\:text-blue-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity))}.dark\:hover\:text-primary:hover:is(.dark *){--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity))}@media (min-width:640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:768px){.md\:mt-0{margin-top:0}.md\:flex{display:flex}.md\:hidden{display:none}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:text-2xl{font-size:1.5rem;line-height:2rem}.md\:text-5xl{font-size:3rem;line-height:1}}@media (min-width:1024px){.lg\:col-span-1{grid-column:span 1/span 1}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:text-6xl{font-size:3.75rem;line-height:1}} \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 00000000..7e0aed3d --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,107 @@ +/** @type {import('tailwindcss').Config} */ +const plugin = require("tailwindcss/plugin"); + +module.exports = { + darkMode: 'class', + content: [ + './templates/**/*.html', + './assets/css/src/**/*.css', + ], + theme: { + extend: { + colors: { + primary: '#4f46e5', // Vibrant indigo + secondary: '#e11d48', // Vibrant rose + accent: '#8b5cf6' + }, + fontFamily: { + 'sans': ['Poppins', 'sans-serif'], + }, + }, + }, + plugins: [ + require("@tailwindcss/typography"), + require("@tailwindcss/forms"), + require("@tailwindcss/aspect-ratio"), + require("@tailwindcss/container-queries"), + plugin(function ({ addComponents }) { + addComponents({ + '.auth-card': { + '@apply w-full max-w-md p-8 mx-auto border shadow-xl bg-white/90 dark:bg-gray-800/90 rounded-2xl backdrop-blur-sm border-gray-200/50 dark:border-gray-700/50': {}, + }, + '.auth-title': { + '@apply mb-8 text-2xl font-bold text-center text-transparent bg-gradient-to-r from-primary to-secondary bg-clip-text': {}, + }, + '.auth-divider': { + '@apply relative my-6 text-center': {}, + '&::before, &::after': { + '@apply absolute top-1/2 w-1/3 border-t border-gray-200 dark:border-gray-700 content-[\'\']': {}, + }, + '&::before': { + '@apply left-0': {}, + }, + '&::after': { + '@apply right-0': {}, + }, + 'span': { + '@apply px-4 text-sm text-gray-500 dark:text-gray-400 bg-white/90 dark:bg-gray-800/90': {}, + }, + }, + '.btn-social': { + '@apply w-full flex items-center justify-center px-6 py-3 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary/50 transform hover:scale-[1.02] transition-all mb-3': {}, + }, + '.btn-discord': { + '@apply bg-[#5865F2] hover:bg-[#4752C4] text-white border-transparent shadow-[#5865F2]/20': {}, + }, + '.btn-google': { + '@apply text-gray-700 bg-white border-gray-200 hover:bg-gray-50 shadow-gray-200/50 dark:shadow-gray-900/50': {}, + }, + '.password-requirements': { + '@apply p-4 rounded-lg bg-gray-50/80 dark:bg-gray-800/80 border border-gray-200/50 dark:border-gray-700/50': {}, + 'ul': { + '@apply space-y-2': {}, + }, + 'li': { + '@apply flex items-center space-x-2 text-sm transition-colors duration-200': {}, + 'i': { + '@apply text-xs transition-all duration-200': {}, + }, + '&.invalid': { + '@apply text-gray-500 dark:text-gray-400': {}, + 'i': { + '@apply text-gray-400 dark:text-gray-500': {}, + }, + }, + '&.valid': { + '@apply text-green-600 dark:text-green-400': {}, + 'i': { + '@apply text-green-500 dark:text-green-400': {}, + }, + }, + }, + }, + '.form-input': { + '@apply w-full px-4 py-3 text-gray-900 transition-all border border-gray-200 rounded-lg shadow-sm dark:border-gray-700 bg-white/70 dark:bg-gray-800/70 backdrop-blur-sm dark:text-white focus:ring-2 focus:ring-primary/50 focus:border-primary': {}, + }, + '.form-label': { + '@apply block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5': {}, + }, + '.form-error': { + '@apply mt-2 text-sm text-red-600 dark:text-red-400': {}, + }, + '.alert': { + '@apply p-4 mb-4 shadow-lg rounded-xl backdrop-blur-sm': {}, + }, + '.alert-error': { + '@apply text-red-800 border border-red-200 bg-red-100/90 dark:bg-red-800/30 dark:text-red-100 dark:border-red-700': {}, + }, + }); + }), + plugin(function ({ addVariant }) { + addVariant("htmx-settling", ["&.htmx-settling", ".htmx-settling &"]); + addVariant("htmx-request", ["&.htmx-request", ".htmx-request &"]); + addVariant("htmx-swapping", ["&.htmx-swapping", ".htmx-swapping &"]); + addVariant("htmx-added", ["&.htmx-added", ".htmx-added &"]); + }), + ], +} diff --git a/templates/account/login.html b/templates/account/login.html index b6c498e0..f3cfde68 100644 --- a/templates/account/login.html +++ b/templates/account/login.html @@ -6,127 +6,110 @@ {% block title %}Login - ThrillWiki{% endblock %} {% block content %} -