From 5b1e97c0cbe083532f9d747c6a0115981a1571be Mon Sep 17 00:00:00 2001 From: Xenne <144433308+Xenne93@users.noreply.github.com> Date: Wed, 19 Aug 2026 15:55:50 +0200 Subject: [PATCH] Fix Discord emoji encoding, add instant purge, mandatory display names, email change - Fix mojibake in Discord embed messages: the offline/online status emoji had been mangled into a UTF-8-as-Windows-1252 double-encoding at some point in the source file, showing as garbled characters in Discord - Panel Settings: add a "Delete All Now" button next to the existing older-than-X-days purge control, for immediately clearing every record in a category on demand - Add a mandatory Display Name, shown across the panel (navbar, moderator lists, audit log) instead of the user's email address: - Renamed the existing (optional) ApplicationUser.Nickname field to DisplayName, with a migration and all call sites updated - Required at initial admin setup - Editable from Security Settings - Existing accounts created before this field existed are prompted with a blocking modal on next login until they set one - Add email address changes from Security Settings, gated behind the current password. Also revokes the current session and clears the auth cookie server-side as part of the change - SecurityBindingMiddleware re-resolves the user by their email claim on every request, so leaving the old session's cookie in place would 401 all further requests (including the follow-up logout call and even loading the login page) until the browser's cookies were cleared manually - TwModal: add an optional ZIndexClass parameter so the new mandatory display-name gate can render above other in-app modals (e.g. the first-run "no servers yet" welcome dialog) instead of stacking behind them --- .../Controllers/AccountController.cs | 31 +- .../Controllers/AuthController.cs | 6 + .../Controllers/ModeratorController.cs | 8 +- .../Controllers/PanelSettingsController.cs | 11 +- .../Controllers/SecurityController.cs | 71 + ...43_RenameNicknameToDisplayName.Designer.cs | 2284 +++++++++++++++++ ...60819133143_RenameNicknameToDisplayName.cs | 28 + .../Migrations/AppDbContextModelSnapshot.cs | 6 +- .../Models/ApplicationUser.cs | 8 +- .../Services/DiscordWebhookService.cs | 24 +- .../Account/RequireDisplayNameGate.razor | 85 + .../Layout/Navigation/TopNavbar.razor | 9 +- .../Components/UI/TwModal.razor | 9 +- .../Layout/MainLayout.razor | 5 + .../Pages/PanelSettings.razor | 50 + .../Pages/Security.razor | 139 + .../Pages/Setup.razor | 16 + .../wwwroot/css/tailwind-output.css | 2 +- .../Account/Account_AccountInformationDTO.cs | 2 +- .../Account/Account_SetDisplayNameDTO.cs | 6 + .../Authorization_SetupRequestDTO.cs | 1 + .../Scheduler/PanelSettingsDto.cs | 5 + .../Security/Security_ChangeEmailDTO.cs | 7 + 23 files changed, 2781 insertions(+), 32 deletions(-) create mode 100644 RustRconServerManager.Backend/Migrations/20260819133143_RenameNicknameToDisplayName.Designer.cs create mode 100644 RustRconServerManager.Backend/Migrations/20260819133143_RenameNicknameToDisplayName.cs create mode 100644 RustRconServerManager.Frontend/Components/Account/RequireDisplayNameGate.razor create mode 100644 RustRconServerManager.Shared/Account/Account_SetDisplayNameDTO.cs create mode 100644 RustRconServerManager.Shared/Security/Security_ChangeEmailDTO.cs diff --git a/RustRconServerManager.Backend/Controllers/AccountController.cs b/RustRconServerManager.Backend/Controllers/AccountController.cs index 20acf48..d67a0c8 100644 --- a/RustRconServerManager.Backend/Controllers/AccountController.cs +++ b/RustRconServerManager.Backend/Controllers/AccountController.cs @@ -42,7 +42,7 @@ public IActionResult GetBasicInfo() return Ok(new { Email = user.Email, - Nickname = user.Nickname, + DisplayName = user.DisplayName, IsModerator = user.IsModerator, isAdmin = user.isAdmin }); @@ -94,7 +94,7 @@ public IActionResult AccountInformation() Account_AccountInformationDTO dto = new Account_AccountInformationDTO(); dto.Email = accountInformation.Email; - dto.Nickname = accountInformation.Nickname; + dto.DisplayName = accountInformation.DisplayName; dto.CreatedAt = accountInformation.CreatedAt; dto.isAdmin = accountInformation.isAdmin; dto.IsModerator = accountInformation.IsModerator; @@ -104,8 +104,35 @@ public IActionResult AccountInformation() return Ok(dto); } + /// + /// Sets the current user's display name - shown across the panel (navbar, moderator + /// lists, audit log) instead of their email address. Required for every account. + /// + [HttpPut("DisplayName")] + public async Task SetDisplayName([FromBody] Account_SetDisplayNameDTO dto) + { + var trimmed = dto.DisplayName?.Trim() ?? string.Empty; + + if (string.IsNullOrEmpty(trimmed)) + return BadRequest("Display name is required."); + + if (trimmed.Length > 50) + return BadRequest("Display name must be 50 characters or fewer."); + + var userEmail = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value; + if (string.IsNullOrEmpty(userEmail)) + return Unauthorized("No email claim found"); + var user = await _dbContext.Users.FirstOrDefaultAsync(x => x.Email == userEmail); + if (user == null) + return NotFound(); + + user.DisplayName = trimmed; + await _dbContext.SaveChangesAsync(); + + return Ok(new { DisplayName = user.DisplayName }); + } } } \ No newline at end of file diff --git a/RustRconServerManager.Backend/Controllers/AuthController.cs b/RustRconServerManager.Backend/Controllers/AuthController.cs index d4b639d..332302f 100644 --- a/RustRconServerManager.Backend/Controllers/AuthController.cs +++ b/RustRconServerManager.Backend/Controllers/AuthController.cs @@ -73,6 +73,11 @@ public async Task Setup(Authorization_SetupRequestDTO model) return BadRequest(new { message = "Passwords do not match" }); } + if (string.IsNullOrWhiteSpace(model.DisplayName)) + { + return BadRequest(new { message = "Display name is required" }); + } + // Get or create default SystemProfile var systemProfile = await _dbContext.SystemProfiles.FirstOrDefaultAsync(); if (systemProfile == null) @@ -108,6 +113,7 @@ public async Task Setup(Authorization_SetupRequestDTO model) { UserName = model.Email, Email = model.Email, + DisplayName = model.DisplayName.Trim(), SystemProfileId = systemProfile.Id, isAdmin = true, EmailConfirmed = true, // Auto-confirm email for initial admin diff --git a/RustRconServerManager.Backend/Controllers/ModeratorController.cs b/RustRconServerManager.Backend/Controllers/ModeratorController.cs index cc88559..64a985f 100644 --- a/RustRconServerManager.Backend/Controllers/ModeratorController.cs +++ b/RustRconServerManager.Backend/Controllers/ModeratorController.cs @@ -47,7 +47,7 @@ public async Task List() { Id = m.Id, Username = m.UserName ?? string.Empty, - DisplayName = m.Nickname, + DisplayName = m.DisplayName, Email = m.Email, IsActive = !m.isLoginBlocked, CreatedAt = m.CreatedAt, @@ -117,7 +117,7 @@ public async Task Create([FromBody] Moderator_CreateDTO dto) { UserName = dto.Username, Email = dto.Email ?? $"{dto.Username}@moderator.local", - Nickname = dto.DisplayName, + DisplayName = dto.DisplayName, SystemProfileId = profile.Id, IsModerator = true, isLoginBlocked = false, @@ -150,7 +150,7 @@ public async Task Create([FromBody] Moderator_CreateDTO dto) { Id = moderator.Id, Username = moderator.UserName ?? string.Empty, - DisplayName = moderator.Nickname, + DisplayName = moderator.DisplayName, Email = moderator.Email, IsActive = !moderator.isLoginBlocked, LastLoginAt = moderator.LastLoginAt, @@ -204,7 +204,7 @@ public async Task Update(string id, [FromBody] Moderator_UpdateDT } // Update moderator properties - moderator.Nickname = dto.DisplayName; + moderator.DisplayName = dto.DisplayName; moderator.Email = dto.Email; moderator.isLoginBlocked = !dto.IsActive; diff --git a/RustRconServerManager.Backend/Controllers/PanelSettingsController.cs b/RustRconServerManager.Backend/Controllers/PanelSettingsController.cs index 5688bb0..9d2d2fd 100644 --- a/RustRconServerManager.Backend/Controllers/PanelSettingsController.cs +++ b/RustRconServerManager.Backend/Controllers/PanelSettingsController.cs @@ -155,7 +155,7 @@ public async Task PurgeData([FromBody] PurgeDataRequestDto reques { try { - if (request.OlderThanDays < 1) + if (!request.Instant && request.OlderThanDays < 1) return BadRequest(new { error = "OlderThanDays must be at least 1" }); var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; @@ -172,7 +172,9 @@ public async Task PurgeData([FromBody] PurgeDataRequestDto reques .Select(s => s.Id) .ToListAsync(); - var cutoff = DateTime.UtcNow.AddDays(-request.OlderThanDays); + // When Instant is set, cutoff is "now" so every existing record older-than check + // still holds true for anything already in the database. + var cutoff = request.Instant ? DateTime.UtcNow : DateTime.UtcNow.AddDays(-request.OlderThanDays); int deleted = 0; switch (request.Category) @@ -217,8 +219,9 @@ public async Task PurgeData([FromBody] PurgeDataRequestDto reques return BadRequest(new { error = $"Unknown or non-purgeable category: {request.Category}" }); } - _logger.LogInformation("[PanelSettingsController] User {UserId} purged {Count} records from {Category} (older than {Days} days)", - userId, deleted, request.Category?.Replace("\r", "").Replace("\n", ""), request.OlderThanDays); + _logger.LogInformation("[PanelSettingsController] User {UserId} purged {Count} records from {Category} ({Scope})", + userId, deleted, request.Category?.Replace("\r", "").Replace("\n", ""), + request.Instant ? "instant - all records" : $"older than {request.OlderThanDays} days"); return Ok(new { deleted, category = request.Category }); } diff --git a/RustRconServerManager.Backend/Controllers/SecurityController.cs b/RustRconServerManager.Backend/Controllers/SecurityController.cs index 026c69e..b877dc0 100644 --- a/RustRconServerManager.Backend/Controllers/SecurityController.cs +++ b/RustRconServerManager.Backend/Controllers/SecurityController.cs @@ -1,11 +1,13 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using RustRconServerManager.Backend.Database; using RustRconServerManager.Backend.Extensions; using RustRconServerManager.Backend.Models; using RustRconServerManager.Backend.Helpers; using RustRconServerManager.Shared.Security; +using System.Net.Mail; using System.Text; using System.Text.Encodings.Web; @@ -78,6 +80,75 @@ public async Task ChangePassword([FromBody] Security_ChangePasswo } } + [HttpPost("change-email")] + public async Task ChangeEmail([FromBody] Security_ChangeEmailDTO dto) + { + try + { + var user = await User.GetUser(_dbContext); + + if (string.IsNullOrWhiteSpace(dto.NewEmail) || !MailAddress.TryCreate(dto.NewEmail, out _)) + { + return BadRequest("A valid email address is required."); + } + + var passwordValid = await _userManager.CheckPasswordAsync(user, dto.CurrentPassword); + if (!passwordValid) + { + return BadRequest("Current password is incorrect."); + } + + var existingUser = await _userManager.FindByEmailAsync(dto.NewEmail); + if (existingUser != null && existingUser.Id != user.Id) + { + return BadRequest("This email address is already in use."); + } + + var setEmailResult = await _userManager.SetEmailAsync(user, dto.NewEmail); + if (!setEmailResult.Succeeded) + { + return BadRequest(string.Join(", ", setEmailResult.Errors.Select(e => e.Description))); + } + + await _userManager.SetUserNameAsync(user, dto.NewEmail); + user.EmailConfirmed = true; + await _userManager.UpdateAsync(user); + + // The current session's JWT still carries the OLD email claim, and + // SecurityBindingMiddleware re-resolves the user by that claim on every + // request (including the frontend's follow-up logout call and even loading + // the login page itself) - once the email no longer matches any user, that + // middleware would 401 all of those with a raw, unstyled error response + // instead of letting the app load. Revoke the session and clear the cookie + // here so the browser has no stale credential left to send. + var tokenHash = User.FindFirst(System.Security.Claims.ClaimTypes.Hash)?.Value; + if (!string.IsNullOrEmpty(tokenHash)) + { + var userSession = await _dbContext.UserSessions + .FirstOrDefaultAsync(s => s.UserId == user.Id && s.SessionHash == tokenHash); + if (userSession != null) + { + userSession.IsRevoked = true; + await _dbContext.SaveChangesAsync(); + } + } + + Response.Cookies.Delete("rrsm_auth", new CookieOptions + { + HttpOnly = true, + Secure = Request.IsHttps, + SameSite = SameSiteMode.Strict, + Path = "/" + }); + + return Ok(new { message = "Email changed successfully. Please log in again with your new email address." }); + } + catch (Exception ex) + { + return BadRequest(ApiErrorHelper.FormatError("Failed to change email", ex)); + } + } + [HttpGet("2fa/status")] public async Task Get2FAStatus() { diff --git a/RustRconServerManager.Backend/Migrations/20260819133143_RenameNicknameToDisplayName.Designer.cs b/RustRconServerManager.Backend/Migrations/20260819133143_RenameNicknameToDisplayName.Designer.cs new file mode 100644 index 0000000..501b248 --- /dev/null +++ b/RustRconServerManager.Backend/Migrations/20260819133143_RenameNicknameToDisplayName.Designer.cs @@ -0,0 +1,2284 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RustRconServerManager.Backend.Database; + +#nullable disable + +namespace RustRconServerManager.Backend.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260819133143_RenameNicknameToDisplayName")] + partial class RenameNicknameToDisplayName + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.13") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("RoleId") + .HasColumnType("varchar(255)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.AggregatedStat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AggregationType") + .HasColumnType("int"); + + b.Property("Avg") + .HasColumnType("double"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Max") + .HasColumnType("double"); + + b.Property("Min") + .HasColumnType("double"); + + b.Property("SampleCount") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Stat") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId", "Stat", "Timestamp", "AggregationType"); + + b.ToTable("AggregatedStats"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DiscordId") + .HasColumnType("longtext"); + + b.Property("DisplayName") + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsModerator") + .HasColumnType("tinyint(1)"); + + b.Property("LastLoginAt") + .HasColumnType("datetime(6)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PasswordResetCode") + .HasColumnType("longtext"); + + b.Property("PasswordResetCodeExpiry") + .HasColumnType("datetime(6)"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("SelectedServerId") + .HasColumnType("int"); + + b.Property("SessionHash") + .HasColumnType("longtext"); + + b.Property("SteamId") + .HasColumnType("longtext"); + + b.Property("SystemProfileId") + .HasColumnType("int"); + + b.Property("Theme") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Website") + .HasColumnType("longtext"); + + b.Property("isAdmin") + .HasColumnType("tinyint(1)"); + + b.Property("isLoginBlocked") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("SystemProfileId"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Details") + .HasColumnType("longtext"); + + b.Property("IpAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Role") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("StatusCode") + .HasColumnType("int"); + + b.Property("UserEmail") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ChatMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Channel") + .HasColumnType("longtext"); + + b.Property("IsFlagged") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("PlayerName") + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("SteamId") + .HasColumnType("longtext"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("ChatMessages"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.DeveloperVacBanOverride", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DaysSinceLastBan") + .HasColumnType("int"); + + b.Property("NumberOfVACBans") + .HasColumnType("int"); + + b.Property("SteamId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("VACBanned") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("SteamId") + .IsUnique(); + + b.ToTable("DeveloperVacBanOverrides"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.DevicePushToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("LastUsedAt") + .HasColumnType("datetime(6)"); + + b.Property("Platform") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Token") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("Token") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("DevicePushTokens"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.IpVpnCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CheckedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime(6)"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("varchar(45)"); + + b.Property("IsVpn") + .HasColumnType("tinyint(1)"); + + b.Property("Provider") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ProxyType") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ExpiresAt"); + + b.HasIndex("IpAddress") + .IsUnique(); + + b.ToTable("IpVpnCache"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.LegalConsent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AcceptedAt") + .HasColumnType("datetime(6)"); + + b.Property("AcceptedPrivacyPolicy") + .HasColumnType("tinyint(1)"); + + b.Property("AcceptedTermsAndConditions") + .HasColumnType("tinyint(1)"); + + b.Property("ConsentAnonymousMetrics") + .HasColumnType("tinyint(1)"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("varchar(45)"); + + b.Property("PrivacyVersion") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("TermsVersion") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("LegalConsents"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.MapData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ImageData") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("ImageHeight") + .HasColumnType("int"); + + b.Property("ImagePath") + .HasColumnType("longtext"); + + b.Property("ImageWidth") + .HasColumnType("int"); + + b.Property("MapSeed") + .HasColumnType("int"); + + b.Property("MapSize") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId") + .IsUnique(); + + b.ToTable("MapData"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ModeratorPagePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("GrantedAt") + .HasColumnType("datetime(6)"); + + b.Property("PageRoute") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "PageRoute") + .IsUnique(); + + b.ToTable("ModeratorPagePermissions"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ModeratorServerPermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("GrantedAt") + .HasColumnType("datetime(6)"); + + b.Property("RconServerId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("RconServerId"); + + b.HasIndex("UserId", "RconServerId") + .IsUnique(); + + b.ToTable("ModeratorServerPermissions"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PanelSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AnalyticsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("AutoUpdateEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DeveloperModeEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LastAnalyticsSentAt") + .HasColumnType("datetime(6)"); + + b.Property("MinimumLogLevel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SteamApiKeyEncrypted") + .HasColumnType("longtext"); + + b.Property("SystemProfileId") + .HasColumnType("int"); + + b.Property("TimezoneId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("SystemProfileId") + .IsUnique(); + + b.ToTable("PanelSettings"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PlayerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Expiry") + .HasColumnType("bigint"); + + b.Property("Group") + .HasColumnType("longtext"); + + b.Property("InternalNote") + .HasColumnType("longtext"); + + b.Property("IsGlobalBan") + .HasColumnType("tinyint(1)"); + + b.Property("IsLifted") + .HasColumnType("tinyint(1)"); + + b.Property("LiftedAt") + .HasColumnType("datetime(6)"); + + b.Property("LiftedBy") + .HasColumnType("longtext"); + + b.Property("Notes") + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("SteamId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PlayerBans"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PlayerBanHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BannedAt") + .HasColumnType("datetime(6)"); + + b.Property("BannedBy") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DurationHours") + .HasColumnType("bigint"); + + b.Property("ExpiryDate") + .HasColumnType("datetime(6)"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("IsGlobalBan") + .HasColumnType("tinyint(1)"); + + b.Property("LiftReason") + .HasColumnType("longtext"); + + b.Property("LiftedAt") + .HasColumnType("datetime(6)"); + + b.Property("LiftedBy") + .HasColumnType("longtext"); + + b.Property("PlayerBanId") + .HasColumnType("int"); + + b.Property("PlayerName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("SteamId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("PlayerBanHistories"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PlayerIpHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Country") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("FirstUsed") + .HasColumnType("datetime(6)"); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("varchar(45)"); + + b.Property("IsVpn") + .HasColumnType("tinyint(1)"); + + b.Property("LastUsed") + .HasColumnType("datetime(6)"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("SteamId") + .IsRequired() + .HasMaxLength(17) + .HasColumnType("varchar(17)"); + + b.Property("TimesUsed") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("SteamId", "ServerId"); + + b.HasIndex("SteamId", "ServerId", "IpAddress") + .IsUnique(); + + b.ToTable("PlayerIpHistory"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PlayerKillLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsPVP") + .HasColumnType("tinyint(1)"); + + b.Property("KilledById") + .HasColumnType("longtext"); + + b.Property("KilledByName") + .HasColumnType("longtext"); + + b.Property("KilledPlayerId") + .HasColumnType("longtext"); + + b.Property("KilledPlayerName") + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("PlayerKillLogs"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PlayerNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("varchar(450)"); + + b.Property("Note") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("SteamId") + .IsRequired() + .HasMaxLength(17) + .HasColumnType("varchar(17)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("PlayerNotes"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PlayerReport", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AdminNotes") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsArchived") + .HasColumnType("tinyint(1)"); + + b.Property("Message") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("ReportedId") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ReportedName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ReporterId") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("ReporterName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ReviewedAt") + .HasColumnType("datetime(6)"); + + b.Property("ReviewedByUserId") + .HasColumnType("varchar(255)"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("ReviewedByUserId"); + + b.ToTable("PlayerReports"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PluginVersionCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CachedAt") + .HasColumnType("datetime(6)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime(6)"); + + b.Property("LatestVersion") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("PluginName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("PluginUrl") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("UmodRateLimitRemaining") + .HasColumnType("int"); + + b.Property("UmodRateLimitTotal") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ExpiresAt"); + + b.HasIndex("PluginName"); + + b.ToTable("PluginVersionCache"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PresetCommand", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Command") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("IsGlobal") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RconServerId") + .HasColumnType("int"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("RconServerId"); + + b.ToTable("PresetCommands"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.RconLogEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("RconLogEntries"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.RconServer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("EncryptedHost") + .HasColumnType("longtext"); + + b.Property("EncryptedPassword") + .HasColumnType("longtext"); + + b.Property("EnvironmentSecret") + .HasColumnType("longtext"); + + b.Property("GamePort") + .HasColumnType("int"); + + b.Property("LastSeen") + .HasColumnType("datetime(6)"); + + b.Property("LatestEntityCount") + .HasColumnType("int"); + + b.Property("LatestFpsCount") + .HasColumnType("int"); + + b.Property("LatestJoiningPlayers") + .HasColumnType("int"); + + b.Property("LatestMap") + .HasColumnType("longtext"); + + b.Property("LatestMemoryUsage") + .HasColumnType("int"); + + b.Property("LatestPlayerCount") + .HasColumnType("int"); + + b.Property("LatestQueuedPlayers") + .HasColumnType("int"); + + b.Property("LatestServerProtocol") + .HasColumnType("longtext"); + + b.Property("LatestServerVersion") + .HasColumnType("int"); + + b.Property("LatestUptime") + .HasColumnType("int"); + + b.Property("ModFramework") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("QueryPort") + .HasColumnType("int"); + + b.Property("RconPort") + .HasColumnType("int"); + + b.Property("RrsmModInitialized") + .HasColumnType("tinyint(1)"); + + b.Property("RustRconServerManagerModInstalled") + .HasColumnType("tinyint(1)"); + + b.Property("ServerHeaderImageData") + .HasColumnType("longblob"); + + b.Property("ServerHostname") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerLogoImageData") + .HasColumnType("longblob"); + + b.Property("SystemProfileId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("SystemProfileId"); + + b.ToTable("RconServers"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.RustItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ItemId") + .HasColumnType("int"); + + b.Property("ShortName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("StackSize") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("RustItems"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ScheduledCommand", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Command") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DayOfMonth") + .HasColumnType("int"); + + b.Property("DaysOfWeek") + .HasColumnType("longtext"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ExecuteAt") + .HasColumnType("datetime(6)"); + + b.Property("ExecutionCount") + .HasColumnType("int"); + + b.Property("ExecutionHour") + .HasColumnType("int"); + + b.Property("ExecutionMinute") + .HasColumnType("int"); + + b.Property("IntervalHours") + .HasColumnType("int"); + + b.Property("IntervalMinutes") + .HasColumnType("int"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LastExecutedAt") + .HasColumnType("datetime(6)"); + + b.Property("LastExecutionError") + .HasColumnType("longtext"); + + b.Property("LastExecutionSuccess") + .HasColumnType("tinyint(1)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NextExecutionAt") + .HasColumnType("datetime(6)"); + + b.Property("RconServerId") + .HasColumnType("int"); + + b.Property("ScheduleType") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("UtcOffsetMinutes") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RconServerId"); + + b.ToTable("ScheduledCommands"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ServerPluginSource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("PluginName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("RustServerId") + .HasColumnType("int"); + + b.Property("Source") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("RustServerId", "PluginName") + .IsUnique(); + + b.ToTable("ServerPluginSources"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ServerProtectionSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BanDurationMinutes") + .HasColumnType("int"); + + b.Property("BlockPrivateSteamProfiles") + .HasColumnType("tinyint(1)"); + + b.Property("CountryFilterMode") + .HasColumnType("int"); + + b.Property("CountryList") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EnablePublicBanProtection") + .HasColumnType("tinyint(1)"); + + b.Property("EnableVacBanProtection") + .HasColumnType("tinyint(1)"); + + b.Property("EnableVpnCheck") + .HasColumnType("tinyint(1)"); + + b.Property("EnableVpnProtection") + .HasColumnType("tinyint(1)"); + + b.Property("EnableWhitelistOnly") + .HasColumnType("tinyint(1)"); + + b.Property("MaxPublicBans") + .HasColumnType("int"); + + b.Property("MaxVacBans") + .HasColumnType("int"); + + b.Property("MinDaysSinceLastVACBan") + .HasColumnType("int"); + + b.Property("PrivateSteamProfileAction") + .HasColumnType("int"); + + b.Property("PublicBanAction") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("VacBanAction") + .HasColumnType("int"); + + b.Property("VpnProtectionAction") + .HasColumnType("int"); + + b.Property("WhitelistOnlyKickMessage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WhitelistedSteamIds") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerProtectionSettings"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ServerWebhookSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EnablePlayerBanWebhook") + .HasColumnType("tinyint(1)"); + + b.Property("EnablePlayerConnectWebhook") + .HasColumnType("tinyint(1)"); + + b.Property("EnablePlayerDisconnectWebhook") + .HasColumnType("tinyint(1)"); + + b.Property("EnablePlayerKickWebhook") + .HasColumnType("tinyint(1)"); + + b.Property("EnablePlayerKillWebhook") + .HasColumnType("tinyint(1)"); + + b.Property("EnablePlayerReportWebhook") + .HasColumnType("tinyint(1)"); + + b.Property("EnableServerOfflineWebhook") + .HasColumnType("tinyint(1)"); + + b.Property("EnableServerOnlineWebhook") + .HasColumnType("tinyint(1)"); + + b.Property("EnableServerProtectionWebhook") + .HasColumnType("tinyint(1)"); + + b.Property("PlayerBanCustomContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerBanFormat") + .HasColumnType("int"); + + b.Property("PlayerBanTextContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerBanWebhookUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerConnectCustomContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerConnectFormat") + .HasColumnType("int"); + + b.Property("PlayerConnectTextContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerConnectWebhookUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerDisconnectCustomContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerDisconnectFormat") + .HasColumnType("int"); + + b.Property("PlayerDisconnectTextContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerDisconnectWebhookUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerKickCustomContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerKickFormat") + .HasColumnType("int"); + + b.Property("PlayerKickTextContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerKickWebhookUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerKillCustomContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerKillFormat") + .HasColumnType("int"); + + b.Property("PlayerKillTextContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerKillWebhookUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerReportCustomContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerReportFormat") + .HasColumnType("int"); + + b.Property("PlayerReportTextContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PlayerReportWebhookUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("ServerOfflineCustomContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerOfflineFormat") + .HasColumnType("int"); + + b.Property("ServerOfflineTextContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerOfflineWebhookUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerOnlineCustomContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerOnlineFormat") + .HasColumnType("int"); + + b.Property("ServerOnlineTextContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerOnlineWebhookUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerProtectionCustomContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerProtectionFormat") + .HasColumnType("int"); + + b.Property("ServerProtectionTextContent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ServerProtectionWebhookUrl") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.ToTable("ServerWebhookSettings"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.SleepingBagData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerSteamId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PositionX") + .HasColumnType("float"); + + b.Property("PositionY") + .HasColumnType("float"); + + b.Property("PositionZ") + .HasColumnType("float"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("SleepingBags"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.StatsHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("Stat") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ServerId", "Stat", "CreatedAt"); + + b.ToTable("StatsHistories"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.SteamPlayer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Avatar") + .HasColumnType("longtext"); + + b.Property("AvatarLastUpdated") + .HasColumnType("datetime(6)"); + + b.Property("Country") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DaysSinceLastVACBan") + .HasColumnType("int"); + + b.Property("FirstSeen") + .HasColumnType("datetime(6)"); + + b.Property("IsOnline") + .HasColumnType("tinyint(1)"); + + b.Property("LastIp") + .HasColumnType("longtext"); + + b.Property("LastSeen") + .HasColumnType("datetime(6)"); + + b.Property("LatestHealth") + .HasColumnType("float"); + + b.Property("LatestPing") + .HasColumnType("int"); + + b.Property("LatestPositionX") + .HasColumnType("float"); + + b.Property("LatestPositionY") + .HasColumnType("float"); + + b.Property("LatestPositionZ") + .HasColumnType("float"); + + b.Property("LatestTeamId") + .HasColumnType("int"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("NumberOfVACBans") + .HasColumnType("int"); + + b.Property("ProfileVisibility") + .HasColumnType("int"); + + b.Property("RustPlaytimeMinutes") + .HasColumnType("int"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("SteamAccountCreated") + .HasColumnType("datetime(6)"); + + b.Property("SteamId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("VACBanned") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("SteamPlayers"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.SystemProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Secret") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("SystemProfiles"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ToolCupboardData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AuthorizedPlayers") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerSteamId") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PositionX") + .HasColumnType("float"); + + b.Property("PositionY") + .HasColumnType("float"); + + b.Property("PositionZ") + .HasColumnType("float"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("ToolCupboards"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.Trigger", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ActionType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ActionValue") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ChatConditionType") + .HasColumnType("longtext"); + + b.Property("ChatConditionValue") + .HasColumnType("longtext"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DelaySeconds") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsActive") + .HasColumnType("tinyint(1)"); + + b.Property("LastExecutionError") + .HasColumnType("longtext"); + + b.Property("LastExecutionSuccess") + .HasColumnType("tinyint(1)"); + + b.Property("LastTriggeredAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RconServerId") + .HasColumnType("int"); + + b.Property("TriggerCount") + .HasColumnType("int"); + + b.Property("TriggerEvent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.Property("WebhookUrl") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("RconServerId"); + + b.ToTable("Triggers"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.UserNotificationPreference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("PlayerBanned") + .HasColumnType("tinyint(1)"); + + b.Property("PlayerOffline") + .HasColumnType("tinyint(1)"); + + b.Property("PlayerOnline") + .HasColumnType("tinyint(1)"); + + b.Property("PlayerReported") + .HasColumnType("tinyint(1)"); + + b.Property("ServerId") + .HasColumnType("int"); + + b.Property("ServerOffline") + .HasColumnType("tinyint(1)"); + + b.Property("ServerOnline") + .HasColumnType("tinyint(1)"); + + b.Property("ServerProtection") + .HasColumnType("tinyint(1)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("ServerId"); + + b.HasIndex("UserId", "ServerId") + .IsUnique(); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.UserSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("DeviceName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ExpiresAt") + .HasColumnType("datetime(6)"); + + b.Property("IpAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsRevoked") + .HasColumnType("tinyint(1)"); + + b.Property("LastActivityAt") + .HasColumnType("datetime(6)"); + + b.Property("SessionHash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserAgent") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId", "IsRevoked"); + + b.ToTable("UserSessions"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("RustRconServerManager.Backend.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("RustRconServerManager.Backend.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("RustRconServerManager.Backend.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("RustRconServerManager.Backend.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ApplicationUser", b => + { + b.HasOne("RustRconServerManager.Backend.Models.SystemProfile", "SystemProfile") + .WithMany() + .HasForeignKey("SystemProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SystemProfile"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.DevicePushToken", b => + { + b.HasOne("RustRconServerManager.Backend.Models.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.LegalConsent", b => + { + b.HasOne("RustRconServerManager.Backend.Models.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.MapData", b => + { + b.HasOne("RustRconServerManager.Backend.Models.RconServer", "Server") + .WithOne() + .HasForeignKey("RustRconServerManager.Backend.Models.MapData", "ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ModeratorPagePermission", b => + { + b.HasOne("RustRconServerManager.Backend.Models.ApplicationUser", "User") + .WithMany("PagePermissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ModeratorServerPermission", b => + { + b.HasOne("RustRconServerManager.Backend.Models.RconServer", "RconServer") + .WithMany("ModeratorPermissions") + .HasForeignKey("RconServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("RustRconServerManager.Backend.Models.ApplicationUser", "User") + .WithMany("ServerPermissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RconServer"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PanelSettings", b => + { + b.HasOne("RustRconServerManager.Backend.Models.SystemProfile", "SystemProfile") + .WithOne("PanelSettings") + .HasForeignKey("RustRconServerManager.Backend.Models.PanelSettings", "SystemProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SystemProfile"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PlayerReport", b => + { + b.HasOne("RustRconServerManager.Backend.Models.ApplicationUser", "ReviewedBy") + .WithMany() + .HasForeignKey("ReviewedByUserId"); + + b.Navigation("ReviewedBy"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.PresetCommand", b => + { + b.HasOne("RustRconServerManager.Backend.Models.RconServer", "RconServer") + .WithMany() + .HasForeignKey("RconServerId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RconServer"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.RconServer", b => + { + b.HasOne("RustRconServerManager.Backend.Models.SystemProfile", "SystemProfile") + .WithMany() + .HasForeignKey("SystemProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SystemProfile"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ScheduledCommand", b => + { + b.HasOne("RustRconServerManager.Backend.Models.RconServer", "RconServer") + .WithMany() + .HasForeignKey("RconServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RconServer"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ServerPluginSource", b => + { + b.HasOne("RustRconServerManager.Backend.Models.RconServer", "RconServer") + .WithMany() + .HasForeignKey("RustServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RconServer"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ServerProtectionSettings", b => + { + b.HasOne("RustRconServerManager.Backend.Models.RconServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ServerWebhookSettings", b => + { + b.HasOne("RustRconServerManager.Backend.Models.RconServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.Trigger", b => + { + b.HasOne("RustRconServerManager.Backend.Models.RconServer", "RconServer") + .WithMany() + .HasForeignKey("RconServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RconServer"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.UserNotificationPreference", b => + { + b.HasOne("RustRconServerManager.Backend.Models.RconServer", "Server") + .WithMany() + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("RustRconServerManager.Backend.Models.ApplicationUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Server"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.UserSession", b => + { + b.HasOne("RustRconServerManager.Backend.Models.ApplicationUser", "User") + .WithMany("Sessions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.ApplicationUser", b => + { + b.Navigation("PagePermissions"); + + b.Navigation("ServerPermissions"); + + b.Navigation("Sessions"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.RconServer", b => + { + b.Navigation("ModeratorPermissions"); + }); + + modelBuilder.Entity("RustRconServerManager.Backend.Models.SystemProfile", b => + { + b.Navigation("PanelSettings") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RustRconServerManager.Backend/Migrations/20260819133143_RenameNicknameToDisplayName.cs b/RustRconServerManager.Backend/Migrations/20260819133143_RenameNicknameToDisplayName.cs new file mode 100644 index 0000000..cde7740 --- /dev/null +++ b/RustRconServerManager.Backend/Migrations/20260819133143_RenameNicknameToDisplayName.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace RustRconServerManager.Backend.Migrations +{ + /// + public partial class RenameNicknameToDisplayName : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Nickname", + table: "AspNetUsers", + newName: "DisplayName"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "DisplayName", + table: "AspNetUsers", + newName: "Nickname"); + } + } +} diff --git a/RustRconServerManager.Backend/Migrations/AppDbContextModelSnapshot.cs b/RustRconServerManager.Backend/Migrations/AppDbContextModelSnapshot.cs index 8a61dee..71ff333 100644 --- a/RustRconServerManager.Backend/Migrations/AppDbContextModelSnapshot.cs +++ b/RustRconServerManager.Backend/Migrations/AppDbContextModelSnapshot.cs @@ -215,6 +215,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("DiscordId") .HasColumnType("longtext"); + b.Property("DisplayName") + .HasColumnType("longtext"); + b.Property("Email") .HasMaxLength(256) .HasColumnType("varchar(256)"); @@ -234,9 +237,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("LockoutEnd") .HasColumnType("datetime(6)"); - b.Property("Nickname") - .HasColumnType("longtext"); - b.Property("NormalizedEmail") .HasMaxLength(256) .HasColumnType("varchar(256)"); diff --git a/RustRconServerManager.Backend/Models/ApplicationUser.cs b/RustRconServerManager.Backend/Models/ApplicationUser.cs index dd533b5..9d51da9 100644 --- a/RustRconServerManager.Backend/Models/ApplicationUser.cs +++ b/RustRconServerManager.Backend/Models/ApplicationUser.cs @@ -10,7 +10,13 @@ public class ApplicationUser:IdentityUser public SystemProfile SystemProfile { get; set; } public string Theme { get; set; } = "light"; - public string? Nickname { get; set; } + + /// + /// Name shown across the panel (navbar, moderator lists, audit log) instead of the + /// email address. Required for every account going forward - existing accounts created + /// before this field existed are prompted to set one on next login (see MainLayout). + /// + public string? DisplayName { get; set; } public string? Website { get; set; } public string? DiscordId { get; set; } public string? SteamId { get; set; } diff --git a/RustRconServerManager.Backend/Services/DiscordWebhookService.cs b/RustRconServerManager.Backend/Services/DiscordWebhookService.cs index 22ced84..a190c35 100644 --- a/RustRconServerManager.Backend/Services/DiscordWebhookService.cs +++ b/RustRconServerManager.Backend/Services/DiscordWebhookService.cs @@ -443,7 +443,7 @@ public async Task SendServerOfflineAsync(int serverId, string serverName) // Text message format - use custom text content if provided, otherwise default var textMessage = string.IsNullOrWhiteSpace(settings.ServerOfflineTextContent) - ? $"🔴 **{serverName}** is offline!" + ? $"🔴 **{serverName}** is offline!" : ReplaceVariables(settings.ServerOfflineTextContent, variables); // Default embed format @@ -453,13 +453,13 @@ public async Task SendServerOfflineAsync(int serverId, string serverName) { new { - title = "🔴 Server Offline", + title = "🔴 Server Offline", description = $"**{serverName}** is no longer reachable", color = 15158332, // Red fields = new[] { new { name = "Server", value = serverName, inline = true }, - new { name = "Status", value = "❌ Offline", inline = true }, + new { name = "Status", value = "❌ Offline", inline = true }, new { name = "Time", value = $"", inline = false } }, footer = new { text = "Server Status Monitor" }, @@ -528,7 +528,7 @@ public async Task SendServerOnlineAsync(int serverId, string serverName, int dow // Text message format - use custom text content if provided, otherwise default var textMessage = string.IsNullOrWhiteSpace(settings.ServerOnlineTextContent) - ? $"✅ **{serverName}** is back online! (Downtime: {downtimeText})" + ? $"✅ **{serverName}** is back online! (Downtime: {downtimeText})" : ReplaceVariables(settings.ServerOnlineTextContent, variables); // Default embed format @@ -538,13 +538,13 @@ public async Task SendServerOnlineAsync(int serverId, string serverName, int dow { new { - title = "✅ Server Online", + title = "✅ Server Online", description = $"**{serverName}** is back online!", color = 3066993, // Green fields = new[] { new { name = "Server", value = serverName, inline = true }, - new { name = "Status", value = "✅ Online", inline = true }, + new { name = "Status", value = "✅ Online", inline = true }, new { name = "Downtime", value = downtimeText, inline = false }, new { name = "Back Online At", value = $"", inline = false } }, @@ -856,13 +856,13 @@ public async Task SendTestWebhookAsync(int serverId, string webhookUrl, WebhookE { new { - title = "🔴 Server Offline (TEST)", + title = "🔴 Server Offline (TEST)", description = $"**{serverName}** is no longer reachable", color = 15158332, fields = new[] { new { name = "Server", value = serverName, inline = true }, - new { name = "Status", value = "❌ Offline", inline = true }, + new { name = "Status", value = "❌ Offline", inline = true }, new { name = "Time", value = $"", inline = false } }, footer = new { text = "Server Status Monitor" }, @@ -870,7 +870,7 @@ public async Task SendTestWebhookAsync(int serverId, string webhookUrl, WebhookE } } }, - $"🔴 **{serverName}** is offline!" + $"🔴 **{serverName}** is offline!" ), WebhookEventType.ServerOnline => ( @@ -889,13 +889,13 @@ public async Task SendTestWebhookAsync(int serverId, string webhookUrl, WebhookE { new { - title = "✅ Server Online (TEST)", + title = "✅ Server Online (TEST)", description = $"**{serverName}** is back online!", color = 3066993, fields = new[] { new { name = "Server", value = serverName, inline = true }, - new { name = "Status", value = "✅ Online", inline = true }, + new { name = "Status", value = "✅ Online", inline = true }, new { name = "Downtime", value = "15 minutes", inline = false }, new { name = "Back Online At", value = $"", inline = false } }, @@ -904,7 +904,7 @@ public async Task SendTestWebhookAsync(int serverId, string webhookUrl, WebhookE } } }, - $"✅ **{serverName}** is back online! (Downtime: 15 minutes)" + $"✅ **{serverName}** is back online! (Downtime: 15 minutes)" ), WebhookEventType.ServerProtection => ( diff --git a/RustRconServerManager.Frontend/Components/Account/RequireDisplayNameGate.razor b/RustRconServerManager.Frontend/Components/Account/RequireDisplayNameGate.razor new file mode 100644 index 0000000..37891f7 --- /dev/null +++ b/RustRconServerManager.Frontend/Components/Account/RequireDisplayNameGate.razor @@ -0,0 +1,85 @@ +@* Blocks the panel with a non-dismissible modal until the current user has a display name set. + Only ever triggers for accounts created before display names became mandatory. *@ +@using RustRconServerManager.Shared.Account +@inject HttpClient Http +@inject NavigationManager NavigationManager + +@if (needsDisplayName) +{ + + +

+ Please choose a display name. It's shown across the panel (navbar, moderator lists, audit log) instead of your email address. +

+ + + + @if (errorMessage != null) + { +
@errorMessage
+ } +
+ + Save + +
+} + +@code { + private bool needsDisplayName = false; + private string displayName = string.Empty; + private bool isSaving = false; + private string? errorMessage; + + protected override async Task OnInitializedAsync() + { + try + { + var info = await Http.GetFromJsonAsync("/api/Account/BasicInfo"); + var currentDisplayName = info.TryGetProperty("displayName", out var dn) ? dn.GetString() : null; + needsDisplayName = string.IsNullOrWhiteSpace(currentDisplayName); + } + catch (Exception ex) + { + Console.WriteLine($"[RequireDisplayNameGate] Error checking display name: {ex.Message}"); + } + } + + private async Task Save() + { + if (string.IsNullOrWhiteSpace(displayName)) + { + errorMessage = "Please enter a display name."; + return; + } + + try + { + isSaving = true; + errorMessage = null; + + var response = await Http.PutAsJsonAsync("/api/Account/DisplayName", new Account_SetDisplayNameDTO { DisplayName = displayName }); + + if (response.IsSuccessStatusCode) + { + needsDisplayName = false; + // Force a full reload so the navbar (and anything else that already + // fetched BasicInfo before the display name existed) picks it up. + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + else + { + errorMessage = await response.Content.ReadAsStringAsync(); + } + } + catch (Exception ex) + { + errorMessage = $"Error: {ex.Message}"; + } + finally + { + isSaving = false; + } + } +} diff --git a/RustRconServerManager.Frontend/Components/Layout/Navigation/TopNavbar.razor b/RustRconServerManager.Frontend/Components/Layout/Navigation/TopNavbar.razor index 4a3e4fb..46f06df 100644 --- a/RustRconServerManager.Frontend/Components/Layout/Navigation/TopNavbar.razor +++ b/RustRconServerManager.Frontend/Components/Layout/Navigation/TopNavbar.razor @@ -119,7 +119,7 @@ @@ -134,8 +134,9 @@ } else { -
- @(UserEmail ?? "Unknown") +
+
@(UserDisplayName ?? "Unknown")
+
@(UserEmail ?? "Unknown")
@if (!IsModerator) @@ -177,6 +178,7 @@ private bool IsLoading = true; private bool IsLoadingUserInfo = true; private string? UserEmail = null; + private string? UserDisplayName = null; private bool IsModerator = false; private bool ActiveServerIsConnected => Servers?.FirstOrDefault(s => s.ServerId == ActiveServerId)?.IsConnected == true; @@ -309,6 +311,7 @@ if (response.ValueKind != System.Text.Json.JsonValueKind.Undefined) { UserEmail = response.GetProperty("email").GetString(); + UserDisplayName = response.TryGetProperty("displayName", out var dn) ? dn.GetString() : null; IsModerator = response.GetProperty("isModerator").GetBoolean(); } } diff --git a/RustRconServerManager.Frontend/Components/UI/TwModal.razor b/RustRconServerManager.Frontend/Components/UI/TwModal.razor index 698ec6b..931c266 100644 --- a/RustRconServerManager.Frontend/Components/UI/TwModal.razor +++ b/RustRconServerManager.Frontend/Components/UI/TwModal.razor @@ -2,7 +2,7 @@ @if (IsOpen) { -
+
@* Backdrop *@
@@ -53,6 +53,13 @@ [Parameter] public string Size { get; set; } = "default"; [Parameter] public string? MaxHeight { get; set; } + /// + /// Tailwind z-index class for the backdrop/modal wrapper. Raise this above the default + /// for modals that must take priority over other in-app modals (e.g. mandatory + /// account-setup gates that should block onboarding dialogs, not stack behind them). + /// + [Parameter] public string ZIndexClass { get; set; } = "z-40"; + public async Task Open() { IsOpen = true; diff --git a/RustRconServerManager.Frontend/Layout/MainLayout.razor b/RustRconServerManager.Frontend/Layout/MainLayout.razor index 6299667..8e99c34 100644 --- a/RustRconServerManager.Frontend/Layout/MainLayout.razor +++ b/RustRconServerManager.Frontend/Layout/MainLayout.razor @@ -1,11 +1,16 @@ @inherits LayoutComponentBase @using RustRconServerManager.Frontend.Components.Layout.Navigation @using RustRconServerManager.Frontend.Components +@using RustRconServerManager.Frontend.Components.Account @using RustRconServerManager.Frontend.Components.Footer @using Microsoft.AspNetCore.Components @inject NavigationManager NavigationManager
+ @* Blocks the panel with a one-time modal for accounts created before display names + became mandatory, until they set one. *@ + + @* Top Navbar *@ diff --git a/RustRconServerManager.Frontend/Pages/PanelSettings.razor b/RustRconServerManager.Frontend/Pages/PanelSettings.razor index 26bf9d2..be62592 100644 --- a/RustRconServerManager.Frontend/Pages/PanelSettings.razor +++ b/RustRconServerManager.Frontend/Pages/PanelSettings.razor @@ -127,6 +127,12 @@ } Purge +
} else if (!entry.CanPurge) @@ -690,6 +696,50 @@ } } + private async Task PurgeDataInstant(string category) + { + var confirmed = await JSRuntime.InvokeAsync("confirm", + $"Delete ALL {FormatCategoryName(category).ToLower()} records right now? This cannot be undone."); + + if (!confirmed) + return; + + try + { + purgingCategory = category; + purgeSuccessMessage = ""; + + var request = new PurgeDataRequestDto { Category = category, Instant = true }; + + var response = await Http.SendAsync(new HttpRequestMessage(HttpMethod.Delete, "/api/panelsettings/purge-data") + { + Content = JsonContent.Create(request) + }); + + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync(); + var deleted = result.GetProperty("deleted").GetInt32(); + purgeSuccessMessage = $"Successfully deleted all {deleted:N0} {FormatCategoryName(category).ToLower()} records."; + + // Refresh stats + await LoadDatabaseStats(); + + // Clear message after 5 seconds + _ = Task.Delay(5000).ContinueWith(_ => { purgeSuccessMessage = ""; InvokeAsync(StateHasChanged); }); + } + } + catch (Exception ex) + { + Console.WriteLine($"Error purging data: {ex.Message}"); + } + finally + { + purgingCategory = null; + StateHasChanged(); + } + } + private static string FormatCategoryName(string category) => category switch { "ChatMessages" => "Chat Messages", diff --git a/RustRconServerManager.Frontend/Pages/Security.razor b/RustRconServerManager.Frontend/Pages/Security.razor index 09969aa..9504a66 100644 --- a/RustRconServerManager.Frontend/Pages/Security.razor +++ b/RustRconServerManager.Frontend/Pages/Security.razor @@ -1,14 +1,61 @@ @page "/Security" @using Microsoft.AspNetCore.Authorization @using RustRconServerManager.Frontend.Components.Page +@using RustRconServerManager.Shared.Account @using RustRconServerManager.Shared.Security +@using RustRconServerManager.Frontend.Services @inject HttpClient Http @inject IJSRuntime JS +@inject NavigationHelper NavHelper @attribute [Authorize] Security Settings
+ + + @if (!string.IsNullOrEmpty(accountMessage)) + { +
+ @accountMessage + +
+ } + +
+
+ +
+ + Save +
+ Shown across the panel instead of your email address. +
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + Change Email + + You'll be logged out and need to sign in again with your new email. +
+
+ @if (!string.IsNullOrEmpty(passwordMessage)) @@ -157,6 +204,7 @@ @code { private Security_ChangePasswordDTO changePasswordDto = new(); + private Security_ChangeEmailDTO changeEmailDto = new(); private Security_Enable2FADTO enable2FADto = new(); private Security_Disable2FADTO disable2FADto = new(); @@ -165,6 +213,8 @@ private bool show2FASetup = false; private bool showDisable2FA = false; private bool isChangingPassword = false; + private bool isChangingEmail = false; + private bool isSavingDisplayName = false; private bool isEnabling2FA = false; private bool isDisabling2FA = false; @@ -172,6 +222,11 @@ private bool passwordError; private string? twoFactorMessage; private bool twoFactorError; + private string? accountMessage; + private bool accountError; + + private string currentEmail = string.Empty; + private string displayName = string.Empty; private string qrCodeUri = string.Empty; private string manualEntryKey = string.Empty; @@ -180,6 +235,90 @@ protected override async Task OnInitializedAsync() { await Load2FAStatus(); + await LoadAccountInfo(); + } + + private async Task LoadAccountInfo() + { + try + { + var info = await Http.GetFromJsonAsync("/api/Account/BasicInfo"); + currentEmail = info.GetProperty("email").GetString() ?? string.Empty; + displayName = info.TryGetProperty("displayName", out var dn) ? dn.GetString() ?? string.Empty : string.Empty; + } + catch (Exception ex) + { + Console.WriteLine($"Error loading account info: {ex.Message}"); + } + } + + private async Task SaveDisplayName() + { + try + { + isSavingDisplayName = true; + accountMessage = null; + + var response = await Http.PutAsJsonAsync("/api/Account/DisplayName", new Account_SetDisplayNameDTO { DisplayName = displayName }); + + if (response.IsSuccessStatusCode) + { + accountMessage = "Display name updated successfully!"; + accountError = false; + } + else + { + accountMessage = await response.Content.ReadAsStringAsync(); + accountError = true; + } + } + catch (Exception ex) + { + accountMessage = $"Error: {ex.Message}"; + accountError = true; + } + finally + { + isSavingDisplayName = false; + } + } + + private async Task ChangeEmail() + { + try + { + isChangingEmail = true; + accountMessage = null; + + var response = await Http.PostAsJsonAsync("/api/Security/change-email", changeEmailDto); + + if (response.IsSuccessStatusCode) + { + accountMessage = "Email changed successfully! Logging you out..."; + accountError = false; + changeEmailDto = new(); + + // The backend already revoked this session and cleared the auth cookie + // as part of the email change itself - calling /api/Auth/logout here + // would just fail, since the (now stale) credential it needs is already gone. + await Task.Delay(2000); + NavHelper.NavigateTo("login", true); + } + else + { + accountMessage = await response.Content.ReadAsStringAsync(); + accountError = true; + } + } + catch (Exception ex) + { + accountMessage = $"Error: {ex.Message}"; + accountError = true; + } + finally + { + isChangingEmail = false; + } } protected override async Task OnAfterRenderAsync(bool firstRender) diff --git a/RustRconServerManager.Frontend/Pages/Setup.razor b/RustRconServerManager.Frontend/Pages/Setup.razor index c5cfdfb..a31dfc8 100644 --- a/RustRconServerManager.Frontend/Pages/Setup.razor +++ b/RustRconServerManager.Frontend/Pages/Setup.razor @@ -52,6 +52,13 @@ else if (currentStep == SetupStep.AccountSetup) }
+
+ + +
+
:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem*var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-2\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.625rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.625rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-slate-700\/50>:not([hidden])~:not([hidden]){border-color:rgb(var(--slate-700)/.5)}.self-end{align-self:flex-end}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;white-space:nowrap}.text-ellipsis,.truncate{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-none{border-radius:0}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:.75rem}.rounded-b-lg{border-bottom-right-radius:var(--radius)}.rounded-b-lg,.rounded-l-lg{border-bottom-left-radius:var(--radius)}.rounded-l-lg{border-top-left-radius:var(--radius)}.rounded-r-lg{border-bottom-right-radius:var(--radius)}.rounded-r-lg,.rounded-t-lg{border-top-right-radius:var(--radius)}.rounded-t-lg{border-top-left-radius:var(--radius)}.rounded-t-xl{border-top-left-radius:.75rem;border-top-right-radius:.75rem}.border{border-width:1px}.border-0{border-width:0}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-0{border-left-width:0}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-r-0{border-right-width:0}.border-t{border-top-width:1px}.border-amber-500{--tw-border-opacity:1;border-color:rgb(245 158 11/var(--tw-border-opacity,1))}.border-amber-500\/20{border-color:rgba(245,158,11,.2)}.border-amber-500\/30{border-color:rgba(245,158,11,.3)}.border-amber-500\/40{border-color:rgba(245,158,11,.4)}.border-amber-500\/50{border-color:rgba(245,158,11,.5)}.border-blue-500\/20{border-color:rgba(59,130,246,.2)}.border-blue-500\/30{border-color:rgba(59,130,246,.3)}.border-cyan-500\/20{border-color:rgba(6,182,212,.2)}.border-cyan-500\/30{border-color:rgba(6,182,212,.3)}.border-emerald-500\/20{border-color:rgba(16,185,129,.2)}.border-emerald-500\/30{border-color:rgba(16,185,129,.3)}.border-emerald-500\/50{border-color:rgba(16,185,129,.5)}.border-green-500\/30{border-color:rgba(34,197,94,.3)}.border-orange-400{--tw-border-opacity:1;border-color:rgb(251 146 60/var(--tw-border-opacity,1))}.border-orange-500{--tw-border-opacity:1;border-color:rgb(249 115 22/var(--tw-border-opacity,1))}.border-orange-500\/20{border-color:rgba(249,115,22,.2)}.border-orange-500\/30{border-color:rgba(249,115,22,.3)}.border-orange-500\/50{border-color:rgba(249,115,22,.5)}.border-purple-500\/20{border-color:rgba(168,85,247,.2)}.border-red-500{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.border-red-500\/20{border-color:rgba(239,68,68,.2)}.border-red-500\/30{border-color:rgba(239,68,68,.3)}.border-red-500\/50{border-color:rgba(239,68,68,.5)}.border-red-600{--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity,1))}.border-slate-500\/30{border-color:rgb(var(--slate-500)/.3)}.border-slate-600{--tw-border-opacity:1;border-color:rgb(var(--slate-600)/var(--tw-border-opacity,1))}.border-slate-600\/50{border-color:rgb(var(--slate-600)/.5)}.border-slate-700{--tw-border-opacity:1;border-color:rgb(var(--slate-700)/var(--tw-border-opacity,1))}.border-slate-700\/30{border-color:rgb(var(--slate-700)/.3)}.border-slate-700\/40{border-color:rgb(var(--slate-700)/.4)}.border-slate-700\/50{border-color:rgb(var(--slate-700)/.5)}.border-slate-800\/50{border-color:rgb(var(--slate-800)/.5)}.border-slate-900{--tw-border-opacity:1;border-color:rgb(var(--slate-900)/var(--tw-border-opacity,1))}.border-transparent{border-color:transparent}.border-yellow-500\/30{border-color:rgba(234,179,8,.3)}.border-t-amber-300{--tw-border-opacity:1;border-top-color:rgb(252 211 77/var(--tw-border-opacity,1))}.bg-\[rgb\(var\(--body-bg\)\)\]{background-color:rgb(var(--body-bg))}.bg-\[rgb\(var\(--body-bg\)\/50\%\)\]{background-color:rgb(var(--body-bg)/50%)}.bg-\[rgb\(var\(--body-bg\)\/80\%\)\]{background-color:rgb(var(--body-bg)/80%)}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity,1))}.bg-amber-500\/10{background-color:rgba(245,158,11,.1)}.bg-amber-500\/15{background-color:rgba(245,158,11,.15)}.bg-amber-500\/20{background-color:rgba(245,158,11,.2)}.bg-amber-500\/5{background-color:rgba(245,158,11,.05)}.bg-amber-900\/20{background-color:rgba(120,53,15,.2)}.bg-black\/50{background-color:rgba(0,0,0,.5)}.bg-black\/80{background-color:rgba(0,0,0,.8)}.bg-blue-500\/10{background-color:rgba(59,130,246,.1)}.bg-blue-500\/20{background-color:rgba(59,130,246,.2)}.bg-blue-500\/5{background-color:rgba(59,130,246,.05)}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-cyan-500\/10{background-color:rgba(6,182,212,.1)}.bg-cyan-500\/20{background-color:rgba(6,182,212,.2)}.bg-emerald-400{--tw-bg-opacity:1;background-color:rgb(52 211 153/var(--tw-bg-opacity,1))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-emerald-500\/10{background-color:rgba(16,185,129,.1)}.bg-emerald-500\/15{background-color:rgba(16,185,129,.15)}.bg-emerald-500\/20{background-color:rgba(16,185,129,.2)}.bg-emerald-500\/5{background-color:rgba(16,185,129,.05)}.bg-green-500\/20{background-color:rgba(34,197,94,.2)}.bg-orange-500{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity,1))}.bg-orange-500\/10{background-color:rgba(249,115,22,.1)}.bg-orange-500\/15{background-color:rgba(249,115,22,.15)}.bg-orange-500\/20{background-color:rgba(249,115,22,.2)}.bg-orange-500\/30{background-color:rgba(249,115,22,.3)}.bg-orange-500\/5{background-color:rgba(249,115,22,.05)}.bg-purple-500\/10{background-color:rgba(168,85,247,.1)}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-red-500\/10{background-color:rgba(239,68,68,.1)}.bg-red-500\/20{background-color:rgba(239,68,68,.2)}.bg-red-500\/5{background-color:rgba(239,68,68,.05)}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.bg-rose-500\/10{background-color:rgba(244,63,94,.1)}.bg-slate-500{--tw-bg-opacity:1;background-color:rgb(var(--slate-500)/var(--tw-bg-opacity,1))}.bg-slate-500\/10{background-color:rgb(var(--slate-500)/.1)}.bg-slate-500\/20{background-color:rgb(var(--slate-500)/.2)}.bg-slate-600{--tw-bg-opacity:1;background-color:rgb(var(--slate-600)/var(--tw-bg-opacity,1))}.bg-slate-700{--tw-bg-opacity:1;background-color:rgb(var(--slate-700)/var(--tw-bg-opacity,1))}.bg-slate-700\/40{background-color:rgb(var(--slate-700)/.4)}.bg-slate-700\/50{background-color:rgb(var(--slate-700)/.5)}.bg-slate-800{--tw-bg-opacity:1;background-color:rgb(var(--slate-800)/var(--tw-bg-opacity,1))}.bg-slate-800\/20{background-color:rgb(var(--slate-800)/.2)}.bg-slate-800\/30{background-color:rgb(var(--slate-800)/.3)}.bg-slate-800\/40{background-color:rgb(var(--slate-800)/.4)}.bg-slate-800\/50{background-color:rgb(var(--slate-800)/.5)}.bg-slate-800\/60{background-color:rgb(var(--slate-800)/.6)}.bg-slate-800\/80{background-color:rgb(var(--slate-800)/.8)}.bg-slate-800\/90{background-color:rgb(var(--slate-800)/.9)}.bg-slate-900\/30{background-color:rgb(var(--slate-900)/.3)}.bg-slate-900\/50{background-color:rgb(var(--slate-900)/.5)}.bg-slate-900\/80{background-color:rgb(var(--slate-900)/.8)}.bg-slate-950\/30{background-color:rgb(var(--slate-950)/.3)}.bg-slate-950\/50{background-color:rgb(var(--slate-950)/.5)}.bg-white{--tw-bg-opacity:1;background-color:rgb(var(--color-white)/var(--tw-bg-opacity,1))}.bg-yellow-500\/20{background-color:rgba(234,179,8,.2)}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-orange-500{--tw-gradient-from:#f97316 var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,115,22,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-500\/30{--tw-gradient-from:rgba(249,115,22,.3) var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,115,22,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-600\/30{--tw-gradient-from:rgb(var(--slate-600)/0.3) var(--tw-gradient-from-position);--tw-gradient-to:rgb(var(--slate-600)/0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-700{--tw-gradient-from:rgb(var(--slate-700)/1) var(--tw-gradient-from-position);--tw-gradient-to:rgb(var(--slate-700)/0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-900\/80{--tw-gradient-from:rgb(var(--slate-900)/0.8) var(--tw-gradient-from-position);--tw-gradient-to:rgb(var(--slate-900)/0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-amber-600{--tw-gradient-to:#d97706 var(--tw-gradient-to-position)}.to-amber-600\/30{--tw-gradient-to:rgba(217,119,6,.3) var(--tw-gradient-to-position)}.to-slate-700\/30{--tw-gradient-to:rgb(var(--slate-700)/0.3) var(--tw-gradient-to-position)}.to-slate-800{--tw-gradient-to:rgb(var(--slate-800)/1) var(--tw-gradient-to-position)}.to-slate-950\/80{--tw-gradient-to:rgb(var(--slate-950)/0.8) var(--tw-gradient-to-position)}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.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-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-4{padding-bottom:1rem}.pb-px{padding-bottom:1px}.pl-10{padding-left:2.5rem}.pl-3{padding-left:.75rem}.pl-8{padding-left:2rem}.pl-9{padding-left:2.25rem}.pr-10{padding-right:2.5rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pr-8{padding-right:2rem}.ps-3{padding-inline-start:.75rem}.pt-16{padding-top:4rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-end{text-align:end}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.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-5xl{font-size:3rem;line-height:1}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[8px\]{font-size:8px}.text-\[9px\]{font-size:9px}.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-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.\!text-green-400{--tw-text-opacity:1!important;color:rgb(74 222 128/var(--tw-text-opacity,1))!important}.\!text-yellow-400{--tw-text-opacity:1!important;color:rgb(250 204 21/var(--tw-text-opacity,1))!important}.text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.text-amber-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.text-cyan-400{--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity,1))}.text-emerald-400{--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.text-muted{color:hsl(var(--muted))}.text-orange-200{--tw-text-opacity:1;color:rgb(254 215 170/var(--tw-text-opacity,1))}.text-orange-300{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.text-orange-400{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.text-orange-400\/80{color:rgba(251,146,60,.8)}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.text-primary{color:hsl(var(--primary))}.text-purple-400{--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-red-400\/80{color:hsla(0,91%,71%,.8)}.text-rose-400{--tw-text-opacity:1;color:rgb(251 113 133/var(--tw-text-opacity,1))}.text-slate-200{--tw-text-opacity:1;color:rgb(var(--slate-200)/var(--tw-text-opacity,1))}.text-slate-300{--tw-text-opacity:1;color:rgb(var(--slate-300)/var(--tw-text-opacity,1))}.text-slate-400{--tw-text-opacity:1;color:rgb(var(--slate-400)/var(--tw-text-opacity,1))}.text-slate-500{--tw-text-opacity:1;color:rgb(var(--slate-500)/var(--tw-text-opacity,1))}.text-slate-600{--tw-text-opacity:1;color:rgb(var(--slate-600)/var(--tw-text-opacity,1))}.text-violet-400{--tw-text-opacity:1;color:rgb(167 139 250/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.no-underline{text-decoration-line:none}.placeholder-slate-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(var(--slate-500)/var(--tw-placeholder-opacity,1))}.placeholder-slate-500::placeholder{--tw-placeholder-opacity:1;color:rgb(var(--slate-500)/var(--tw-placeholder-opacity,1))}.accent-orange-500{accent-color:#f97316}.accent-violet-500{accent-color:#8b5cf6}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--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 rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(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)}.outline{outline-style:solid}.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-slate-700{--tw-ring-opacity:1;--tw-ring-color:rgb(var(--slate-700)/var(--tw-ring-opacity,1))}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px rgba(0,0,0,.1)) drop-shadow(0 1px 1px rgba(0,0,0,.06))}.drop-shadow,.drop-shadow-lg{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)}.drop-shadow-lg{--tw-drop-shadow:drop-shadow(0 10px 8px rgba(0,0,0,.04)) drop-shadow(0 4px 3px rgba(0,0,0,.1))}.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{--tw-backdrop-blur:blur(8px)}.backdrop-blur,.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)}.backdrop-blur-xl{--tw-backdrop-blur:blur(24px);-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(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:rgba(148,163,184,.2);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:rgba(148,163,184,.3)}@keyframes spin{to{transform:rotate(1turn)}}.tw-spinner{height:1.25rem;width:1.25rem;border-radius:9999px;border-width:2px;border-color:rgb(var(--slate-600)/var(--tw-border-opacity,1));--tw-border-opacity:1;border-top-color:rgb(249 115 22/var(--tw-border-opacity,1));animation:spin .8s linear infinite}.placeholder\:text-slate-500::-moz-placeholder{--tw-text-opacity:1;color:rgb(var(--slate-500)/var(--tw-text-opacity,1))}.placeholder\:text-slate-500::placeholder{--tw-text-opacity:1;color:rgb(var(--slate-500)/var(--tw-text-opacity,1))}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:left-\[2px\]:after{content:var(--tw-content);left:2px}.after\:top-\[2px\]:after{content:var(--tw-content);top:2px}.after\:h-4:after{content:var(--tw-content);height:1rem}.after\:w-4:after{content:var(--tw-content);width:1rem}.after\:rounded-full:after{content:var(--tw-content);border-radius:9999px}.after\:border:after{content:var(--tw-content);border-width:1px}.after\:border-gray-300:after{content:var(--tw-content);--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.after\:bg-white:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(var(--color-white)/var(--tw-bg-opacity,1))}.after\:transition-all:after{content:var(--tw-content);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.last\:border-b-0:last-child{border-bottom-width:0}.hover\:border-orange-500\/50:hover{border-color:rgba(249,115,22,.5)}.hover\:border-slate-600:hover{--tw-border-opacity:1;border-color:rgb(var(--slate-600)/var(--tw-border-opacity,1))}.hover\:border-slate-600\/50:hover{border-color:rgb(var(--slate-600)/.5)}.hover\:bg-amber-500\/10:hover{background-color:rgba(245,158,11,.1)}.hover\:bg-amber-500\/30:hover{background-color:rgba(245,158,11,.3)}.hover\:bg-amber-600:hover{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity,1))}.hover\:bg-blue-500\/30:hover{background-color:rgba(59,130,246,.3)}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-emerald-500\/10:hover{background-color:rgba(16,185,129,.1)}.hover\:bg-emerald-500\/30:hover{background-color:rgba(16,185,129,.3)}.hover\:bg-emerald-600:hover{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.hover\:bg-orange-500\/10:hover{background-color:rgba(249,115,22,.1)}.hover\:bg-orange-500\/20:hover{background-color:rgba(249,115,22,.2)}.hover\:bg-orange-600:hover{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity,1))}.hover\:bg-red-500\/10:hover{background-color:rgba(239,68,68,.1)}.hover\:bg-red-500\/20:hover{background-color:rgba(239,68,68,.2)}.hover\:bg-red-500\/30:hover{background-color:rgba(239,68,68,.3)}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.hover\:bg-red-700:hover{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity,1))}.hover\:bg-slate-600:hover{--tw-bg-opacity:1;background-color:rgb(var(--slate-600)/var(--tw-bg-opacity,1))}.hover\:bg-slate-700:hover{--tw-bg-opacity:1;background-color:rgb(var(--slate-700)/var(--tw-bg-opacity,1))}.hover\:bg-slate-700\/30:hover{background-color:rgb(var(--slate-700)/.3)}.hover\:bg-slate-700\/40:hover{background-color:rgb(var(--slate-700)/.4)}.hover\:bg-slate-700\/50:hover{background-color:rgb(var(--slate-700)/.5)}.hover\:bg-slate-800:hover{--tw-bg-opacity:1;background-color:rgb(var(--slate-800)/var(--tw-bg-opacity,1))}.hover\:bg-slate-800\/20:hover{background-color:rgb(var(--slate-800)/.2)}.hover\:bg-slate-800\/30:hover{background-color:rgb(var(--slate-800)/.3)}.hover\:bg-slate-800\/40:hover{background-color:rgb(var(--slate-800)/.4)}.hover\:bg-slate-800\/50:hover{background-color:rgb(var(--slate-800)/.5)}.hover\:bg-slate-800\/60:hover{background-color:rgb(var(--slate-800)/.6)}.hover\:text-blue-400:hover{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.hover\:text-orange-300:hover{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.hover\:text-orange-400:hover{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.hover\:text-red-300:hover{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.hover\:text-red-400:hover{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.hover\:text-slate-300:hover{--tw-text-opacity:1;color:rgb(var(--slate-300)/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1))}.hover\:opacity-70:hover{opacity:.7}.focus\:border-cyan-500:focus{--tw-border-opacity:1;border-color:rgb(6 182 212/var(--tw-border-opacity,1))}.focus\:border-emerald-500:focus{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.focus\:border-orange-500:focus{--tw-border-opacity:1;border-color:rgb(249 115 22/var(--tw-border-opacity,1))}.focus\:border-orange-500\/50:focus{border-color:rgba(249,115,22,.5)}.focus\:border-red-500:focus{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1: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(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.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)}.focus\:ring-cyan-500\/50:focus{--tw-ring-color:rgba(6,182,212,.5)}.focus\:ring-emerald-500\/50:focus{--tw-ring-color:rgba(16,185,129,.5)}.focus\:ring-orange-500\/50:focus{--tw-ring-color:rgba(249,115,22,.5)}.focus\:ring-red-500\/50:focus{--tw-ring-color:rgba(239,68,68,.5)}.focus\:ring-offset-0:focus{--tw-ring-offset-width:0px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}.peer:checked~.peer-checked\:bg-cyan-500{--tw-bg-opacity:1;background-color:rgb(6 182 212/var(--tw-bg-opacity,1))}.peer:checked~.peer-checked\:bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.peer:checked~.peer-checked\:bg-orange-500{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity,1))}.peer:checked~.peer-checked\:bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.peer:checked~.peer-checked\:after\:translate-x-full:after{content:var(--tw-content);--tw-translate-x: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))}.peer:checked~.peer-checked\:after\:border-white:after{content:var(--tw-content);--tw-border-opacity:1;border-color:rgb(var(--color-white)/var(--tw-border-opacity,1))}.peer:focus~.peer-focus\:outline-none{outline:2px solid transparent;outline-offset:2px}@media (min-width:640px){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:mt-0{margin-top:0}.sm\:block{display:block}.sm\:table-cell{display:table-cell}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}}@media (min-width:768px){.md\:col-span-1{grid-column:span 1/span 1}.md\:col-span-2{grid-column:span 2/span 2}.md\:flex{display:flex}.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))}}@media (min-width:1024px){.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-span-8{grid-column:span 8/span 8}.lg\:mb-0{margin-bottom:0}.lg\:flex{display:flex}.lg\:table-cell{display:table-cell}.lg\:hidden{display:none}.lg\:w-80{width:20rem}.lg\:flex-shrink-0{flex-shrink:0}.lg\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:flex-row-reverse{flex-direction:row-reverse}.lg\:p-6{padding:1.5rem}.lg\:px-6{padding-left:1.5rem;padding-right:1.5rem}.lg\:pl-64{padding-left:16rem}}@media (min-width:1280px){.xl\:col-span-1{grid-column:span 1/span 1}.xl\:col-span-2{grid-column:span 2/span 2}.xl\:table-cell{display:table-cell}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}@media (min-width:1536px){.\32xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}} \ No newline at end of file +*,: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:rgba(59,130,246,.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:rgba(59,130,246,.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: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;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-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color: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:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-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{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--background:222 47% 6%;--foreground:210 40% 98%;--card:222 47% 8%;--card-foreground:210 40% 98%;--popover:222 47% 8%;--popover-foreground:210 40% 98%;--primary:24 95% 53%;--primary-foreground:210 40% 98%;--secondary:217 33% 17%;--secondary-foreground:210 40% 98%;--muted:217 33% 17%;--muted-foreground:215 20% 65%;--accent:24 95% 53%;--accent-foreground:210 40% 98%;--destructive:0 84% 60%;--destructive-foreground:210 40% 98%;--border:217 33% 17%;--input:217 33% 17%;--ring:24 95% 53%;--radius:0.5rem;--color-white:255 255 255;--slate-50:248 250 252;--slate-100:241 245 249;--slate-200:226 232 240;--slate-300:203 213 225;--slate-400:148 163 184;--slate-500:100 116 139;--slate-600:71 85 105;--slate-700:51 65 85;--slate-800:30 41 59;--slate-900:15 23 42;--slate-950:2 6 23;--body-bg:15 15 23}:root[data-theme=light]{--color-white:15 23 42;--slate-50:2 6 23;--slate-100:15 23 42;--slate-200:30 41 59;--slate-300:51 65 85;--slate-400:71 85 105;--slate-500:100 116 139;--slate-600:148 163 184;--slate-700:203 213 225;--slate-800:226 232 240;--slate-900:241 245 249;--slate-950:248 250 252;--body-bg:248 250 252}*{border-color:hsl(var(--border))}body{background-color:rgb(var(--body-bg));color:hsl(var(--foreground));font-family:Inter,Ubuntu Sans,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,sans-serif}.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}}.tw-card{overflow:hidden;border-radius:.75rem;border-width:1px;border-color:rgb(var(--slate-700)/.5);background-image:linear-gradient(to bottom right,var(--tw-gradient-stops));--tw-gradient-from:rgb(var(--slate-900)/0.8) var(--tw-gradient-from-position);--tw-gradient-to:rgb(var(--slate-900)/0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to);--tw-gradient-to:rgb(var(--slate-950)/0.8) var(--tw-gradient-to-position);--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)}.tw-card-header{display:flex;align-items:center;justify-content:space-between;border-bottom-width:1px;border-color:rgb(var(--slate-700)/.5);background-color:rgb(var(--slate-800)/.3);padding:.75rem 1rem}.tw-card-body{padding:1rem}.tw-btn-primary{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border-radius:var(--radius);padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.tw-btn-primary: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:hsl(var(--ring));--tw-ring-offset-width:2px;--tw-ring-offset-color:hsl(var(--background))}.tw-btn-primary:disabled{pointer-events:none;opacity:.5}.tw-btn-primary{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1))}.tw-btn-primary:hover{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity,1))}.tw-btn-secondary{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border-radius:var(--radius);padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.tw-btn-secondary: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:hsl(var(--ring));--tw-ring-offset-width:2px;--tw-ring-offset-color:hsl(var(--background))}.tw-btn-secondary:disabled{pointer-events:none;opacity:.5}.tw-btn-secondary{--tw-bg-opacity:1;background-color:rgb(var(--slate-700)/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1))}.tw-btn-secondary:hover{--tw-bg-opacity:1;background-color:rgb(var(--slate-600)/var(--tw-bg-opacity,1))}.tw-btn-outline{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border-radius:var(--radius);padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.tw-btn-outline: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:hsl(var(--ring));--tw-ring-offset-width:2px;--tw-ring-offset-color:hsl(var(--background))}.tw-btn-outline:disabled{pointer-events:none;opacity:.5}.tw-btn-outline{border-width:1px;--tw-border-opacity:1;border-color:rgb(var(--slate-700)/var(--tw-border-opacity,1));background-color:rgb(var(--slate-800)/.5);--tw-text-opacity:1;color:rgb(var(--slate-300)/var(--tw-text-opacity,1))}.tw-btn-outline:hover{--tw-border-opacity:1;border-color:rgb(var(--slate-600)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(var(--slate-800)/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1))}.tw-btn-ghost{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border-radius:var(--radius);padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.tw-btn-ghost: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:hsl(var(--ring));--tw-ring-offset-width:2px;--tw-ring-offset-color:hsl(var(--background))}.tw-btn-ghost:disabled{pointer-events:none;opacity:.5}.tw-btn-ghost{background-color:transparent;--tw-text-opacity:1;color:rgb(var(--slate-400)/var(--tw-text-opacity,1))}.tw-btn-ghost:hover{background-color:rgb(var(--slate-800)/.5);--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1))}.tw-btn-danger{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border-radius:var(--radius);padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.tw-btn-danger: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:hsl(var(--ring));--tw-ring-offset-width:2px;--tw-ring-offset-color:hsl(var(--background))}.tw-btn-danger:disabled{pointer-events:none;opacity:.5}.tw-btn-danger{border-width:1px;border-color:rgba(239,68,68,.3);background-color:rgba(239,68,68,.1);--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.tw-btn-danger:hover{background-color:rgba(239,68,68,.2)}.tw-btn-warning{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border-radius:var(--radius);padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.tw-btn-warning: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:hsl(var(--ring));--tw-ring-offset-width:2px;--tw-ring-offset-color:hsl(var(--background))}.tw-btn-warning:disabled{pointer-events:none;opacity:.5}.tw-btn-warning{border-width:1px;border-color:rgba(245,158,11,.3);background-color:rgba(245,158,11,.1);--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.tw-btn-warning:hover{background-color:rgba(245,158,11,.2)}.tw-btn-success{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border-radius:var(--radius);padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.tw-btn-success: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:hsl(var(--ring));--tw-ring-offset-width:2px;--tw-ring-offset-color:hsl(var(--background))}.tw-btn-success:disabled{pointer-events:none;opacity:.5}.tw-btn-success{border-width:1px;border-color:rgba(16,185,129,.3);background-color:rgba(16,185,129,.1);--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.tw-btn-success:hover{background-color:rgba(16,185,129,.2)}.tw-btn-sm{padding:.375rem .75rem;font-size:.75rem;line-height:1rem}.tw-btn-lg{padding:.75rem 1.5rem;font-size:1rem;line-height:1.5rem}.tw-btn-icon{padding:.5rem}.tw-input{width:100%;border-radius:var(--radius);border-width:1px;--tw-border-opacity:1;border-color:rgb(var(--slate-700)/var(--tw-border-opacity,1));background-color:rgb(var(--slate-800)/.5);padding:.5rem .75rem;--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1));transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.tw-input::-moz-placeholder{--tw-text-opacity:1;color:rgb(var(--slate-500)/var(--tw-text-opacity,1))}.tw-input::placeholder{--tw-text-opacity:1;color:rgb(var(--slate-500)/var(--tw-text-opacity,1))}.tw-input:focus{border-color:rgba(249,115,22,.5);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:rgba(249,115,22,.5)}.tw-select{width:100%;border-radius:var(--radius);border-width:1px;--tw-border-opacity:1;border-color:rgb(var(--slate-700)/var(--tw-border-opacity,1));background-color:rgb(var(--slate-800)/.5);padding:.5rem .75rem;--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1));transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.tw-select::-moz-placeholder{--tw-text-opacity:1;color:rgb(var(--slate-500)/var(--tw-text-opacity,1))}.tw-select::placeholder{--tw-text-opacity:1;color:rgb(var(--slate-500)/var(--tw-text-opacity,1))}.tw-select:focus{border-color:rgba(249,115,22,.5);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:rgba(249,115,22,.5)}.tw-select{cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-position:100%;background-repeat:no-repeat;padding-right:2.5rem;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-size:1.5em 1.5em}.tw-badge-default{display:inline-flex;align-items:center;border-radius:9999px;padding:.125rem .5rem;font-size:.75rem;line-height:1rem;font-weight:500;background-color:rgb(var(--slate-700)/.5);--tw-text-opacity:1;color:rgb(var(--slate-300)/var(--tw-text-opacity,1))}.tw-badge-primary{border-radius:9999px;border-width:1px;border-color:rgba(249,115,22,.3);background-color:rgba(249,115,22,.2);color:rgb(251 146 60/var(--tw-text-opacity,1))}.tw-badge-primary,.tw-badge-success{display:inline-flex;align-items:center;padding:.125rem .5rem;font-size:.75rem;line-height:1rem;font-weight:500;--tw-text-opacity:1}.tw-badge-success{border-radius:9999px;border-width:1px;border-color:rgba(16,185,129,.3);background-color:rgba(16,185,129,.2);color:rgb(52 211 153/var(--tw-text-opacity,1))}.tw-badge-warning{border-radius:9999px;border-width:1px;border-color:rgba(245,158,11,.3);background-color:rgba(245,158,11,.2);color:rgb(251 191 36/var(--tw-text-opacity,1))}.tw-badge-danger,.tw-badge-warning{display:inline-flex;align-items:center;padding:.125rem .5rem;font-size:.75rem;line-height:1rem;font-weight:500;--tw-text-opacity:1}.tw-badge-danger{border-radius:9999px;border-width:1px;border-color:rgba(239,68,68,.3);background-color:rgba(239,68,68,.2);color:rgb(248 113 113/var(--tw-text-opacity,1))}.tw-badge-info{display:inline-flex;align-items:center;border-radius:9999px;padding:.125rem .5rem;font-size:.75rem;line-height:1rem;font-weight:500;border-width:1px;border-color:rgba(6,182,212,.3);background-color:rgba(6,182,212,.2);--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity,1))}.tw-stat-card-default{position:relative;overflow:hidden;border-radius:.75rem;border-width:1px;background-image:linear-gradient(to bottom right,var(--tw-gradient-stops));padding:1.25rem;--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(.4,0,.2,1);transition-duration:.3s}.tw-stat-card-default:hover{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(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-shadow-color:rgba(0,0,0,.2);--tw-shadow:var(--tw-shadow-colored)}.tw-stat-card-default{border-color:rgb(var(--slate-700)/.5);--tw-gradient-from:rgb(var(--slate-800)/0.5) var(--tw-gradient-from-position);--tw-gradient-to:rgb(var(--slate-800)/0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to);--tw-gradient-to:rgb(var(--slate-900)/0.5) var(--tw-gradient-to-position)}.tw-stat-card-default:hover{border-color:rgb(var(--slate-600)/.5)}.tw-stat-card-success{position:relative;overflow:hidden;border-radius:.75rem;border-width:1px;background-image:linear-gradient(to bottom right,var(--tw-gradient-stops));padding:1.25rem;--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(.4,0,.2,1);transition-duration:.3s}.tw-stat-card-success:hover{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(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-shadow-color:rgba(0,0,0,.2);--tw-shadow:var(--tw-shadow-colored)}.tw-stat-card-success{border-color:rgba(16,185,129,.3);--tw-gradient-from:rgba(6,78,59,.3) var(--tw-gradient-from-position);--tw-gradient-to:rgba(6,78,59,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to);--tw-gradient-to:rgb(var(--slate-900)/0.5) var(--tw-gradient-to-position)}.tw-stat-card-success:hover{border-color:rgba(16,185,129,.5)}.tw-stat-card-warning{position:relative;overflow:hidden;border-radius:.75rem;border-width:1px;background-image:linear-gradient(to bottom right,var(--tw-gradient-stops));padding:1.25rem;--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(.4,0,.2,1);transition-duration:.3s}.tw-stat-card-warning:hover{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(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-shadow-color:rgba(0,0,0,.2);--tw-shadow:var(--tw-shadow-colored)}.tw-stat-card-warning{border-color:rgba(245,158,11,.3);--tw-gradient-from:rgba(120,53,15,.3) var(--tw-gradient-from-position);--tw-gradient-to:rgba(120,53,15,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to);--tw-gradient-to:rgb(var(--slate-900)/0.5) var(--tw-gradient-to-position)}.tw-stat-card-warning:hover{border-color:rgba(245,158,11,.5)}.tw-stat-card-danger{position:relative;overflow:hidden;border-radius:.75rem;border-width:1px;background-image:linear-gradient(to bottom right,var(--tw-gradient-stops));padding:1.25rem;--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(.4,0,.2,1);transition-duration:.3s}.tw-stat-card-danger:hover{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(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-shadow-color:rgba(0,0,0,.2);--tw-shadow:var(--tw-shadow-colored)}.tw-stat-card-danger{border-color:rgba(239,68,68,.3);--tw-gradient-from:rgba(127,29,29,.3) var(--tw-gradient-from-position);--tw-gradient-to:rgba(127,29,29,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to);--tw-gradient-to:rgb(var(--slate-900)/0.5) var(--tw-gradient-to-position)}.tw-stat-card-danger:hover{border-color:rgba(239,68,68,.5)}.tw-stat-card-primary{position:relative;overflow:hidden;border-radius:.75rem;border-width:1px;background-image:linear-gradient(to bottom right,var(--tw-gradient-stops));padding:1.25rem;--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(.4,0,.2,1);transition-duration:.3s}.tw-stat-card-primary:hover{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(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-shadow-color:rgba(0,0,0,.2);--tw-shadow:var(--tw-shadow-colored)}.tw-stat-card-primary{border-color:rgba(249,115,22,.3);--tw-gradient-from:rgba(124,45,18,.3) var(--tw-gradient-from-position);--tw-gradient-to:rgba(124,45,18,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to);--tw-gradient-to:rgb(var(--slate-900)/0.5) var(--tw-gradient-to-position)}.tw-stat-card-primary:hover{border-color:rgba(249,115,22,.5)}.tw-nav-link{display:flex;align-items:center;gap:.75rem;border-radius:var(--radius);padding:.75rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;--tw-text-opacity:1;color:rgb(var(--slate-400)/var(--tw-text-opacity,1));transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.tw-nav-link:hover{background-color:rgb(var(--slate-800)/.5);--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1))}.tw-nav-link-active{display:flex;align-items:center;gap:.75rem;border-radius:var(--radius);padding:.75rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;color:rgb(var(--slate-400)/var(--tw-text-opacity,1));transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.tw-nav-link-active:hover{background-color:rgb(var(--slate-800)/.5);--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1))}.tw-nav-link-active{border-width:1px;border-color:rgba(249,115,22,.2);background-color:rgba(249,115,22,.1);--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.tw-dropdown-content{position:absolute;z-index:50;margin-top:.5rem;min-width:8rem;overflow:hidden;border-radius:var(--radius);border-width:1px;--tw-border-opacity:1;border-color:rgb(var(--slate-700)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(var(--slate-800)/var(--tw-bg-opacity,1));padding:.25rem;--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(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-shadow-color:rgba(0,0,0,.2);--tw-shadow:var(--tw-shadow-colored)}.tw-dropdown-item{display:flex;width:100%;cursor:pointer;align-items:center;gap:.5rem;border-radius:calc(var(--radius) - 2px);padding:.5rem .75rem;font-size:.875rem;line-height:1.25rem;--tw-text-opacity:1;color:rgb(var(--slate-300)/var(--tw-text-opacity,1));transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.tw-dropdown-item:hover{--tw-bg-opacity:1;--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1))}.tw-dropdown-item:hover,.tw-dropdown-separator{background-color:rgb(var(--slate-700)/var(--tw-bg-opacity,1))}.tw-dropdown-separator{margin-top:.25rem;margin-bottom:.25rem;height:1px;--tw-bg-opacity:1}.tw-modal-backdrop{position:fixed;inset:0;z-index:40;background-color:rgba(0,0,0,.5);--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)}.tw-modal{position:fixed;left:50%;top:50%;z-index:50;width:100%;max-width:32rem;--tw-translate-x:-50%;--tw-translate-y:-50%;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));border-radius:.75rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(var(--slate-700)/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(var(--slate-900)/var(--tw-bg-opacity,1));--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(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-shadow-color:rgba(0,0,0,.3);--tw-shadow:var(--tw-shadow-colored)}.tw-modal-header{display:flex;align-items:center;justify-content:space-between;border-bottom-width:1px;--tw-border-opacity:1;border-color:rgb(var(--slate-700)/var(--tw-border-opacity,1));padding:1rem 1.5rem}.tw-modal-body{padding:1.5rem}.tw-modal-footer{display:flex;align-items:center;justify-content:flex-end;gap:.75rem;border-top-width:1px;--tw-border-opacity:1;border-color:rgb(var(--slate-700)/var(--tw-border-opacity,1));padding:1rem 1.5rem}.tw-table{width:100%;text-align:left;font-size:.875rem;line-height:1.25rem}.tw-table thead{background-color:rgb(var(--slate-800)/.5);font-size:.75rem;line-height:1rem;text-transform:uppercase;--tw-text-opacity:1;color:rgb(var(--slate-400)/var(--tw-text-opacity,1))}.tw-table th{padding:.75rem 1rem;font-weight:500}.tw-table td{border-bottom-width:1px;border-color:rgb(var(--slate-700)/.5);padding:.75rem 1rem}.tw-table tbody tr{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.tw-table tbody tr:hover{background-color:rgb(var(--slate-800)/.3)}.tw-glow{position:absolute;top:-6rem;right:-6rem;height:12rem;width:12rem;border-radius:9999px;background-image:linear-gradient(to bottom right,var(--tw-gradient-stops));--tw-gradient-from:rgba(249,115,22,.05) var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,115,22,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to);--tw-gradient-to:transparent var(--tw-gradient-to-position);--tw-blur:blur(64px);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)}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.-bottom-0\.5{bottom:-.125rem}.-right-0\.5{right:-.125rem}.bottom-0{bottom:0}.bottom-0\.5{bottom:.125rem}.left-0{left:0}.left-1\/2{left:50%}.left-3{left:.75rem}.right-0{right:0}.right-1{right:.25rem}.right-1\.5{right:.375rem}.right-2{right:.5rem}.top-0{top:0}.top-1\.5{top:.375rem}.top-1\/2{top:50%}.top-16{top:4rem}.z-10{z-index:10}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[1050\]{z-index:1050}.z-\[1060\]{z-index:1060}.z-\[60\]{z-index:60}.m-0{margin:0}.-mx-4{margin-left:-1rem;margin-right:-1rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.-mb-4{margin-bottom:-1rem}.-mb-px{margin-bottom:-1px}.mb-0{margin-bottom:0}.mb-0\.5{margin-bottom:.125rem}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.me-2{margin-inline-end:.5rem}.ml-0\.5{margin-left:.125rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-6{margin-left:1.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.mt-auto{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-square{aspect-ratio:1/1}.h-0\.5{height:.125rem}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-24{height:6rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-full{height:100%}.max-h-64{max-height:16rem}.max-h-96{max-height:24rem}.max-h-\[200px\]{max-height:200px}.max-h-\[250px\]{max-height:250px}.max-h-\[400px\]{max-height:400px}.max-h-\[500px\]{max-height:500px}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-32{width:8rem}.w-4{width:1rem}.w-44{width:11rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-56{width:14rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-fit{width:-moz-fit-content;width:fit-content}.w-full{width:100%}.min-w-0{min-width:0}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-\[160px\]{max-width:160px}.max-w-\[200px\]{max-width:200px}.max-w-\[250px\]{max-width:250px}.max-w-\[300px\]{max-width:300px}.max-w-\[60\%\]{max-width:60%}.max-w-\[90vw\]{max-width:90vw}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink{flex-shrink:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-1\/2,.-translate-y-1\/2{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))}.-translate-y-1\/2{--tw-translate-y:-50%}.rotate-180{--tw-rotate:180deg}.rotate-180,.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))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes slide-in-left{0%{transform:translateX(-100%)}to{transform:translateX(0)}}.animate-slide-in-left{animation:slide-in-left .3s ease-out}.cursor-help{cursor:help}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-\[repeat\(6\2c 3rem\)\]{grid-template-columns:repeat(6,3rem)}.grid-cols-\[repeat\(7\2c 3rem\)\]{grid-template-columns:repeat(7,3rem)}.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}.items-baseline{align-items:baseline}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-y-2{row-gap:.5rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem*var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-2\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.625rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.625rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-slate-700\/50>:not([hidden])~:not([hidden]){border-color:rgb(var(--slate-700)/.5)}.self-end{align-self:flex-end}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.truncate{overflow:hidden;white-space:nowrap}.text-ellipsis,.truncate{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--radius)}.rounded-none{border-radius:0}.rounded-sm{border-radius:calc(var(--radius) - 4px)}.rounded-xl{border-radius:.75rem}.rounded-b-lg{border-bottom-right-radius:var(--radius)}.rounded-b-lg,.rounded-l-lg{border-bottom-left-radius:var(--radius)}.rounded-l-lg{border-top-left-radius:var(--radius)}.rounded-r-lg{border-bottom-right-radius:var(--radius)}.rounded-r-lg,.rounded-t-lg{border-top-right-radius:var(--radius)}.rounded-t-lg{border-top-left-radius:var(--radius)}.rounded-t-xl{border-top-left-radius:.75rem;border-top-right-radius:.75rem}.border{border-width:1px}.border-0{border-width:0}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-0{border-left-width:0}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-r-0{border-right-width:0}.border-t{border-top-width:1px}.border-amber-500{--tw-border-opacity:1;border-color:rgb(245 158 11/var(--tw-border-opacity,1))}.border-amber-500\/20{border-color:rgba(245,158,11,.2)}.border-amber-500\/30{border-color:rgba(245,158,11,.3)}.border-amber-500\/40{border-color:rgba(245,158,11,.4)}.border-amber-500\/50{border-color:rgba(245,158,11,.5)}.border-blue-500\/20{border-color:rgba(59,130,246,.2)}.border-blue-500\/30{border-color:rgba(59,130,246,.3)}.border-cyan-500\/20{border-color:rgba(6,182,212,.2)}.border-cyan-500\/30{border-color:rgba(6,182,212,.3)}.border-emerald-500\/20{border-color:rgba(16,185,129,.2)}.border-emerald-500\/30{border-color:rgba(16,185,129,.3)}.border-emerald-500\/50{border-color:rgba(16,185,129,.5)}.border-green-500\/30{border-color:rgba(34,197,94,.3)}.border-orange-400{--tw-border-opacity:1;border-color:rgb(251 146 60/var(--tw-border-opacity,1))}.border-orange-500{--tw-border-opacity:1;border-color:rgb(249 115 22/var(--tw-border-opacity,1))}.border-orange-500\/20{border-color:rgba(249,115,22,.2)}.border-orange-500\/30{border-color:rgba(249,115,22,.3)}.border-orange-500\/50{border-color:rgba(249,115,22,.5)}.border-purple-500\/20{border-color:rgba(168,85,247,.2)}.border-red-500{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.border-red-500\/20{border-color:rgba(239,68,68,.2)}.border-red-500\/30{border-color:rgba(239,68,68,.3)}.border-red-500\/50{border-color:rgba(239,68,68,.5)}.border-red-600{--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity,1))}.border-slate-500\/30{border-color:rgb(var(--slate-500)/.3)}.border-slate-600{--tw-border-opacity:1;border-color:rgb(var(--slate-600)/var(--tw-border-opacity,1))}.border-slate-600\/50{border-color:rgb(var(--slate-600)/.5)}.border-slate-700{--tw-border-opacity:1;border-color:rgb(var(--slate-700)/var(--tw-border-opacity,1))}.border-slate-700\/30{border-color:rgb(var(--slate-700)/.3)}.border-slate-700\/40{border-color:rgb(var(--slate-700)/.4)}.border-slate-700\/50{border-color:rgb(var(--slate-700)/.5)}.border-slate-800\/50{border-color:rgb(var(--slate-800)/.5)}.border-slate-900{--tw-border-opacity:1;border-color:rgb(var(--slate-900)/var(--tw-border-opacity,1))}.border-transparent{border-color:transparent}.border-yellow-500\/30{border-color:rgba(234,179,8,.3)}.border-t-amber-300{--tw-border-opacity:1;border-top-color:rgb(252 211 77/var(--tw-border-opacity,1))}.bg-\[rgb\(var\(--body-bg\)\)\]{background-color:rgb(var(--body-bg))}.bg-\[rgb\(var\(--body-bg\)\/50\%\)\]{background-color:rgb(var(--body-bg)/50%)}.bg-\[rgb\(var\(--body-bg\)\/80\%\)\]{background-color:rgb(var(--body-bg)/80%)}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity,1))}.bg-amber-500\/10{background-color:rgba(245,158,11,.1)}.bg-amber-500\/15{background-color:rgba(245,158,11,.15)}.bg-amber-500\/20{background-color:rgba(245,158,11,.2)}.bg-amber-500\/5{background-color:rgba(245,158,11,.05)}.bg-amber-900\/20{background-color:rgba(120,53,15,.2)}.bg-black\/50{background-color:rgba(0,0,0,.5)}.bg-black\/80{background-color:rgba(0,0,0,.8)}.bg-blue-500\/10{background-color:rgba(59,130,246,.1)}.bg-blue-500\/20{background-color:rgba(59,130,246,.2)}.bg-blue-500\/5{background-color:rgba(59,130,246,.05)}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-cyan-500\/10{background-color:rgba(6,182,212,.1)}.bg-cyan-500\/20{background-color:rgba(6,182,212,.2)}.bg-emerald-400{--tw-bg-opacity:1;background-color:rgb(52 211 153/var(--tw-bg-opacity,1))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-emerald-500\/10{background-color:rgba(16,185,129,.1)}.bg-emerald-500\/15{background-color:rgba(16,185,129,.15)}.bg-emerald-500\/20{background-color:rgba(16,185,129,.2)}.bg-emerald-500\/5{background-color:rgba(16,185,129,.05)}.bg-green-500\/20{background-color:rgba(34,197,94,.2)}.bg-orange-500{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity,1))}.bg-orange-500\/10{background-color:rgba(249,115,22,.1)}.bg-orange-500\/15{background-color:rgba(249,115,22,.15)}.bg-orange-500\/20{background-color:rgba(249,115,22,.2)}.bg-orange-500\/30{background-color:rgba(249,115,22,.3)}.bg-orange-500\/5{background-color:rgba(249,115,22,.05)}.bg-purple-500\/10{background-color:rgba(168,85,247,.1)}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-red-500\/10{background-color:rgba(239,68,68,.1)}.bg-red-500\/20{background-color:rgba(239,68,68,.2)}.bg-red-500\/30{background-color:rgba(239,68,68,.3)}.bg-red-500\/5{background-color:rgba(239,68,68,.05)}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.bg-rose-500\/10{background-color:rgba(244,63,94,.1)}.bg-slate-500{--tw-bg-opacity:1;background-color:rgb(var(--slate-500)/var(--tw-bg-opacity,1))}.bg-slate-500\/10{background-color:rgb(var(--slate-500)/.1)}.bg-slate-500\/20{background-color:rgb(var(--slate-500)/.2)}.bg-slate-600{--tw-bg-opacity:1;background-color:rgb(var(--slate-600)/var(--tw-bg-opacity,1))}.bg-slate-700{--tw-bg-opacity:1;background-color:rgb(var(--slate-700)/var(--tw-bg-opacity,1))}.bg-slate-700\/40{background-color:rgb(var(--slate-700)/.4)}.bg-slate-700\/50{background-color:rgb(var(--slate-700)/.5)}.bg-slate-800{--tw-bg-opacity:1;background-color:rgb(var(--slate-800)/var(--tw-bg-opacity,1))}.bg-slate-800\/20{background-color:rgb(var(--slate-800)/.2)}.bg-slate-800\/30{background-color:rgb(var(--slate-800)/.3)}.bg-slate-800\/40{background-color:rgb(var(--slate-800)/.4)}.bg-slate-800\/50{background-color:rgb(var(--slate-800)/.5)}.bg-slate-800\/60{background-color:rgb(var(--slate-800)/.6)}.bg-slate-800\/80{background-color:rgb(var(--slate-800)/.8)}.bg-slate-800\/90{background-color:rgb(var(--slate-800)/.9)}.bg-slate-900\/30{background-color:rgb(var(--slate-900)/.3)}.bg-slate-900\/50{background-color:rgb(var(--slate-900)/.5)}.bg-slate-900\/80{background-color:rgb(var(--slate-900)/.8)}.bg-slate-950\/30{background-color:rgb(var(--slate-950)/.3)}.bg-slate-950\/50{background-color:rgb(var(--slate-950)/.5)}.bg-white{--tw-bg-opacity:1;background-color:rgb(var(--color-white)/var(--tw-bg-opacity,1))}.bg-yellow-500\/20{background-color:rgba(234,179,8,.2)}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-orange-500{--tw-gradient-from:#f97316 var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,115,22,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-500\/30{--tw-gradient-from:rgba(249,115,22,.3) var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,115,22,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-600\/30{--tw-gradient-from:rgb(var(--slate-600)/0.3) var(--tw-gradient-from-position);--tw-gradient-to:rgb(var(--slate-600)/0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-700{--tw-gradient-from:rgb(var(--slate-700)/1) var(--tw-gradient-from-position);--tw-gradient-to:rgb(var(--slate-700)/0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-900\/80{--tw-gradient-from:rgb(var(--slate-900)/0.8) var(--tw-gradient-from-position);--tw-gradient-to:rgb(var(--slate-900)/0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-amber-600{--tw-gradient-to:#d97706 var(--tw-gradient-to-position)}.to-amber-600\/30{--tw-gradient-to:rgba(217,119,6,.3) var(--tw-gradient-to-position)}.to-slate-700\/30{--tw-gradient-to:rgb(var(--slate-700)/0.3) var(--tw-gradient-to-position)}.to-slate-800{--tw-gradient-to:rgb(var(--slate-800)/1) var(--tw-gradient-to-position)}.to-slate-950\/80{--tw-gradient-to:rgb(var(--slate-950)/0.8) var(--tw-gradient-to-position)}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.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-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-4{padding-bottom:1rem}.pb-px{padding-bottom:1px}.pl-10{padding-left:2.5rem}.pl-3{padding-left:.75rem}.pl-8{padding-left:2rem}.pl-9{padding-left:2.25rem}.pr-10{padding-right:2.5rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pr-8{padding-right:2rem}.ps-3{padding-inline-start:.75rem}.pt-16{padding-top:4rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-end{text-align:end}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.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-5xl{font-size:3rem;line-height:1}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[8px\]{font-size:8px}.text-\[9px\]{font-size:9px}.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-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.\!text-green-400{--tw-text-opacity:1!important;color:rgb(74 222 128/var(--tw-text-opacity,1))!important}.\!text-yellow-400{--tw-text-opacity:1!important;color:rgb(250 204 21/var(--tw-text-opacity,1))!important}.text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.text-amber-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.text-cyan-400{--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity,1))}.text-emerald-400{--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.text-muted{color:hsl(var(--muted))}.text-orange-200{--tw-text-opacity:1;color:rgb(254 215 170/var(--tw-text-opacity,1))}.text-orange-300{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.text-orange-400{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.text-orange-400\/80{color:rgba(251,146,60,.8)}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.text-primary{color:hsl(var(--primary))}.text-purple-400{--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-red-400\/80{color:hsla(0,91%,71%,.8)}.text-rose-400{--tw-text-opacity:1;color:rgb(251 113 133/var(--tw-text-opacity,1))}.text-slate-200{--tw-text-opacity:1;color:rgb(var(--slate-200)/var(--tw-text-opacity,1))}.text-slate-300{--tw-text-opacity:1;color:rgb(var(--slate-300)/var(--tw-text-opacity,1))}.text-slate-400{--tw-text-opacity:1;color:rgb(var(--slate-400)/var(--tw-text-opacity,1))}.text-slate-500{--tw-text-opacity:1;color:rgb(var(--slate-500)/var(--tw-text-opacity,1))}.text-slate-600{--tw-text-opacity:1;color:rgb(var(--slate-600)/var(--tw-text-opacity,1))}.text-violet-400{--tw-text-opacity:1;color:rgb(167 139 250/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.no-underline{text-decoration-line:none}.placeholder-slate-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(var(--slate-500)/var(--tw-placeholder-opacity,1))}.placeholder-slate-500::placeholder{--tw-placeholder-opacity:1;color:rgb(var(--slate-500)/var(--tw-placeholder-opacity,1))}.accent-orange-500{accent-color:#f97316}.accent-violet-500{accent-color:#8b5cf6}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--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 rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(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)}.outline{outline-style:solid}.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-slate-700{--tw-ring-opacity:1;--tw-ring-color:rgb(var(--slate-700)/var(--tw-ring-opacity,1))}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px rgba(0,0,0,.1)) drop-shadow(0 1px 1px rgba(0,0,0,.06))}.drop-shadow,.drop-shadow-lg{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)}.drop-shadow-lg{--tw-drop-shadow:drop-shadow(0 10px 8px rgba(0,0,0,.04)) drop-shadow(0 4px 3px rgba(0,0,0,.1))}.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{--tw-backdrop-blur:blur(8px)}.backdrop-blur,.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)}.backdrop-blur-xl{--tw-backdrop-blur:blur(24px);-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(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:rgba(148,163,184,.2);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:rgba(148,163,184,.3)}@keyframes spin{to{transform:rotate(1turn)}}.tw-spinner{height:1.25rem;width:1.25rem;border-radius:9999px;border-width:2px;border-color:rgb(var(--slate-600)/var(--tw-border-opacity,1));--tw-border-opacity:1;border-top-color:rgb(249 115 22/var(--tw-border-opacity,1));animation:spin .8s linear infinite}.placeholder\:text-slate-500::-moz-placeholder{--tw-text-opacity:1;color:rgb(var(--slate-500)/var(--tw-text-opacity,1))}.placeholder\:text-slate-500::placeholder{--tw-text-opacity:1;color:rgb(var(--slate-500)/var(--tw-text-opacity,1))}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:left-\[2px\]:after{content:var(--tw-content);left:2px}.after\:top-\[2px\]:after{content:var(--tw-content);top:2px}.after\:h-4:after{content:var(--tw-content);height:1rem}.after\:w-4:after{content:var(--tw-content);width:1rem}.after\:rounded-full:after{content:var(--tw-content);border-radius:9999px}.after\:border:after{content:var(--tw-content);border-width:1px}.after\:border-gray-300:after{content:var(--tw-content);--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.after\:bg-white:after{content:var(--tw-content);--tw-bg-opacity:1;background-color:rgb(var(--color-white)/var(--tw-bg-opacity,1))}.after\:transition-all:after{content:var(--tw-content);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.last\:border-b-0:last-child{border-bottom-width:0}.hover\:border-orange-500\/50:hover{border-color:rgba(249,115,22,.5)}.hover\:border-slate-600:hover{--tw-border-opacity:1;border-color:rgb(var(--slate-600)/var(--tw-border-opacity,1))}.hover\:border-slate-600\/50:hover{border-color:rgb(var(--slate-600)/.5)}.hover\:bg-amber-500\/10:hover{background-color:rgba(245,158,11,.1)}.hover\:bg-amber-500\/30:hover{background-color:rgba(245,158,11,.3)}.hover\:bg-amber-600:hover{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity,1))}.hover\:bg-blue-500\/30:hover{background-color:rgba(59,130,246,.3)}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-emerald-500\/10:hover{background-color:rgba(16,185,129,.1)}.hover\:bg-emerald-500\/30:hover{background-color:rgba(16,185,129,.3)}.hover\:bg-emerald-600:hover{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.hover\:bg-orange-500\/10:hover{background-color:rgba(249,115,22,.1)}.hover\:bg-orange-500\/20:hover{background-color:rgba(249,115,22,.2)}.hover\:bg-orange-600:hover{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity,1))}.hover\:bg-red-500\/10:hover{background-color:rgba(239,68,68,.1)}.hover\:bg-red-500\/20:hover{background-color:rgba(239,68,68,.2)}.hover\:bg-red-500\/30:hover{background-color:rgba(239,68,68,.3)}.hover\:bg-red-500\/40:hover{background-color:rgba(239,68,68,.4)}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.hover\:bg-red-700:hover{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity,1))}.hover\:bg-slate-600:hover{--tw-bg-opacity:1;background-color:rgb(var(--slate-600)/var(--tw-bg-opacity,1))}.hover\:bg-slate-700:hover{--tw-bg-opacity:1;background-color:rgb(var(--slate-700)/var(--tw-bg-opacity,1))}.hover\:bg-slate-700\/30:hover{background-color:rgb(var(--slate-700)/.3)}.hover\:bg-slate-700\/40:hover{background-color:rgb(var(--slate-700)/.4)}.hover\:bg-slate-700\/50:hover{background-color:rgb(var(--slate-700)/.5)}.hover\:bg-slate-800:hover{--tw-bg-opacity:1;background-color:rgb(var(--slate-800)/var(--tw-bg-opacity,1))}.hover\:bg-slate-800\/20:hover{background-color:rgb(var(--slate-800)/.2)}.hover\:bg-slate-800\/30:hover{background-color:rgb(var(--slate-800)/.3)}.hover\:bg-slate-800\/40:hover{background-color:rgb(var(--slate-800)/.4)}.hover\:bg-slate-800\/50:hover{background-color:rgb(var(--slate-800)/.5)}.hover\:bg-slate-800\/60:hover{background-color:rgb(var(--slate-800)/.6)}.hover\:text-blue-400:hover{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.hover\:text-orange-300:hover{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.hover\:text-orange-400:hover{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.hover\:text-red-300:hover{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.hover\:text-red-400:hover{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.hover\:text-slate-300:hover{--tw-text-opacity:1;color:rgb(var(--slate-300)/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(var(--color-white)/var(--tw-text-opacity,1))}.hover\:opacity-70:hover{opacity:.7}.focus\:border-cyan-500:focus{--tw-border-opacity:1;border-color:rgb(6 182 212/var(--tw-border-opacity,1))}.focus\:border-emerald-500:focus{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.focus\:border-orange-500:focus{--tw-border-opacity:1;border-color:rgb(249 115 22/var(--tw-border-opacity,1))}.focus\:border-orange-500\/50:focus{border-color:rgba(249,115,22,.5)}.focus\:border-red-500:focus{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1: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(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.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)}.focus\:ring-cyan-500\/50:focus{--tw-ring-color:rgba(6,182,212,.5)}.focus\:ring-emerald-500\/50:focus{--tw-ring-color:rgba(16,185,129,.5)}.focus\:ring-orange-500\/50:focus{--tw-ring-color:rgba(249,115,22,.5)}.focus\:ring-red-500\/50:focus{--tw-ring-color:rgba(239,68,68,.5)}.focus\:ring-offset-0:focus{--tw-ring-offset-width:0px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-100{opacity:1}.peer:checked~.peer-checked\:bg-cyan-500{--tw-bg-opacity:1;background-color:rgb(6 182 212/var(--tw-bg-opacity,1))}.peer:checked~.peer-checked\:bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.peer:checked~.peer-checked\:bg-orange-500{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity,1))}.peer:checked~.peer-checked\:bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.peer:checked~.peer-checked\:after\:translate-x-full:after{content:var(--tw-content);--tw-translate-x: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))}.peer:checked~.peer-checked\:after\:border-white:after{content:var(--tw-content);--tw-border-opacity:1;border-color:rgb(var(--color-white)/var(--tw-border-opacity,1))}.peer:focus~.peer-focus\:outline-none{outline:2px solid transparent;outline-offset:2px}@media (min-width:640px){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:mt-0{margin-top:0}.sm\:block{display:block}.sm\:table-cell{display:table-cell}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}}@media (min-width:768px){.md\:col-span-1{grid-column:span 1/span 1}.md\:col-span-2{grid-column:span 2/span 2}.md\:flex{display:flex}.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))}}@media (min-width:1024px){.lg\:col-span-4{grid-column:span 4/span 4}.lg\:col-span-8{grid-column:span 8/span 8}.lg\:mb-0{margin-bottom:0}.lg\:flex{display:flex}.lg\:table-cell{display:table-cell}.lg\:hidden{display:none}.lg\:w-80{width:20rem}.lg\:flex-shrink-0{flex-shrink:0}.lg\:grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:flex-row-reverse{flex-direction:row-reverse}.lg\:p-6{padding:1.5rem}.lg\:px-6{padding-left:1.5rem;padding-right:1.5rem}.lg\:pl-64{padding-left:16rem}}@media (min-width:1280px){.xl\:col-span-1{grid-column:span 1/span 1}.xl\:col-span-2{grid-column:span 2/span 2}.xl\:table-cell{display:table-cell}.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}@media (min-width:1536px){.\32xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}} \ No newline at end of file diff --git a/RustRconServerManager.Shared/Account/Account_AccountInformationDTO.cs b/RustRconServerManager.Shared/Account/Account_AccountInformationDTO.cs index a50d7a8..6447b2a 100644 --- a/RustRconServerManager.Shared/Account/Account_AccountInformationDTO.cs +++ b/RustRconServerManager.Shared/Account/Account_AccountInformationDTO.cs @@ -4,7 +4,7 @@ public class Account_AccountInformationDTO { public string Email { get; set; } - public string? Nickname { get; set; } + public string? DisplayName { get; set; } public string? Theme { get; set; } public bool isAdmin { get; set; } public bool IsModerator { get; set; } diff --git a/RustRconServerManager.Shared/Account/Account_SetDisplayNameDTO.cs b/RustRconServerManager.Shared/Account/Account_SetDisplayNameDTO.cs new file mode 100644 index 0000000..98a0289 --- /dev/null +++ b/RustRconServerManager.Shared/Account/Account_SetDisplayNameDTO.cs @@ -0,0 +1,6 @@ +namespace RustRconServerManager.Shared.Account; + +public class Account_SetDisplayNameDTO +{ + public string DisplayName { get; set; } = string.Empty; +} diff --git a/RustRconServerManager.Shared/Authorization/Authorization_SetupRequestDTO.cs b/RustRconServerManager.Shared/Authorization/Authorization_SetupRequestDTO.cs index 722ce5d..d841a7f 100644 --- a/RustRconServerManager.Shared/Authorization/Authorization_SetupRequestDTO.cs +++ b/RustRconServerManager.Shared/Authorization/Authorization_SetupRequestDTO.cs @@ -3,6 +3,7 @@ namespace RustRconServerManager.Shared.Authorization; public class Authorization_SetupRequestDTO { public string Email { get; set; } + public string DisplayName { get; set; } public string Password { get; set; } public string ConfirmPassword { get; set; } diff --git a/RustRconServerManager.Shared/Scheduler/PanelSettingsDto.cs b/RustRconServerManager.Shared/Scheduler/PanelSettingsDto.cs index 603c3e5..af3a7df 100644 --- a/RustRconServerManager.Shared/Scheduler/PanelSettingsDto.cs +++ b/RustRconServerManager.Shared/Scheduler/PanelSettingsDto.cs @@ -33,6 +33,11 @@ public class PurgeDataRequestDto { public string Category { get; set; } = string.Empty; public int OlderThanDays { get; set; } + + /// + /// When true, deletes every record in the category immediately, ignoring OlderThanDays. + /// + public bool Instant { get; set; } = false; } public class SetLogLevelDto diff --git a/RustRconServerManager.Shared/Security/Security_ChangeEmailDTO.cs b/RustRconServerManager.Shared/Security/Security_ChangeEmailDTO.cs new file mode 100644 index 0000000..7e0bc49 --- /dev/null +++ b/RustRconServerManager.Shared/Security/Security_ChangeEmailDTO.cs @@ -0,0 +1,7 @@ +namespace RustRconServerManager.Shared.Security; + +public class Security_ChangeEmailDTO +{ + public string NewEmail { get; set; } = string.Empty; + public string CurrentPassword { get; set; } = string.Empty; +}