diff --git a/.claude/settings.local.json b/.claude/settings.local.json index d5921f3..3b07a8b 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -3,7 +3,8 @@ "allow": [ "Bash(cd:*)", "Bash(ls:*)", - "Bash(dotnet build:*)" + "Bash(dotnet build:*)", + "Bash(find:*)" ] } } diff --git a/PTWork.code-workspace b/PTWork.code-workspace index 2ac8eb2..fd2c04e 100644 --- a/PTWork.code-workspace +++ b/PTWork.code-workspace @@ -8,6 +8,7 @@ } ], "settings": { - "liveServer.settings.port": 5501 + "liveServer.settings.port": 5501, + "liveServer.settings.multiRootWorkspaceName": "Calendar" } } \ No newline at end of file diff --git a/PlanTempus.Application/Features/Accounts/Models/PlanInfo.cs b/PlanTempus.Application/Features/Accounts/Models/PlanInfo.cs new file mode 100644 index 0000000..edb3d43 --- /dev/null +++ b/PlanTempus.Application/Features/Accounts/Models/PlanInfo.cs @@ -0,0 +1,101 @@ +namespace PlanTempus.Application.Features.Accounts.Models; + +public record PlanInfo( + string Key, + string Name, + string UserRange, + decimal? PricePerMonth, + string BadgeText, + string BadgeClass, + string BadgeIcon, + IReadOnlyList Features, + bool RequiresPayment, + bool IsContactSales +) +{ + public bool IsFree => PricePerMonth == 0; + public string PriceDisplay => PricePerMonth.HasValue ? $"{PricePerMonth:0}" : "Kontakt os"; +} + +public static class PlanCatalog +{ + private static readonly Dictionary Plans = new() + { + ["basis"] = new PlanInfo( + Key: "basis", + Name: "Basis", + UserRange: "1-3 brugere", + PricePerMonth: 0, + BadgeText: "Gratis", + BadgeClass: "free", + BadgeIcon: "ph-gift", + Features: new List + { + "Op til 3 brugere", + "Online booking", + "Kalender & aftalestyring", + "Kundekartotek", + "SMS-påmindelser" + }, + RequiresPayment: false, + IsContactSales: false + ), + + ["pro"] = new PlanInfo( + Key: "pro", + Name: "Pro", + UserRange: "4-8 brugere", + PricePerMonth: 599, + BadgeText: "Mest populære", + BadgeClass: "popular", + BadgeIcon: "ph-star", + Features: new List + { + "Op til 8 brugere", + "Alt fra Basis", + "Lagerstyring", + "Avancerede rapporter", + "Gavekort & klippekort", + "Prioriteret support" + }, + RequiresPayment: true, + IsContactSales: false + ), + + ["enterprise"] = new PlanInfo( + Key: "enterprise", + Name: "Enterprise", + UserRange: "8+ brugere", + PricePerMonth: null, + BadgeText: "Enterprise", + BadgeClass: "enterprise", + BadgeIcon: "ph-buildings", + Features: new List + { + "Ubegrænset antal brugere", + "Alt fra Pro", + "Flere lokationer", + "Tilpasset integration", + "Dedikeret kontaktperson", + "SLA & uptime garanti" + }, + RequiresPayment: false, + IsContactSales: true + ) + }; + + public static PlanInfo GetPlan(string key) + { + if (Plans.TryGetValue(key.ToLowerInvariant(), out var plan)) + return plan; + + // Default to Pro if invalid key + return Plans["pro"]; + } + + public static IEnumerable GetAllPlans() => Plans.Values; + + public static PlanInfo Basis => Plans["basis"]; + public static PlanInfo Pro => Plans["pro"]; + public static PlanInfo Enterprise => Plans["enterprise"]; +} diff --git a/PlanTempus.Application/Features/Accounts/Pages/Payment.cshtml b/PlanTempus.Application/Features/Accounts/Pages/Payment.cshtml new file mode 100644 index 0000000..23417a6 --- /dev/null +++ b/PlanTempus.Application/Features/Accounts/Pages/Payment.cshtml @@ -0,0 +1,219 @@ +@page "/payment" +@model PlanTempus.Application.Features.Accounts.Pages.PaymentModel +@{ + ViewData["Title"] = "Betaling"; + Layout = "/Features/_Shared/Pages/_AuthLayout.cshtml"; + + var plan = Model.SelectedPlan; +} + + + + + + + PlanTempus + + + Din ordre + + + + + @plan.BadgeText + + @plan.Name + @plan.UserRange + @if (plan.PricePerMonth.HasValue) + { + + @plan.PriceDisplay + kr/md + + } + + + @foreach (var feature in plan.Features) + { + + + @feature + + } + + + + + @plan.Name abonnement + @(plan.PricePerMonth.HasValue ? $"{plan.PriceDisplay} kr" : "–") + + + 14 dages gratis prøve + -@(plan.PricePerMonth.HasValue ? $"{plan.PriceDisplay} kr" : "–") + + + + At betale i dag + 0 kr + + + + + + + + Skift abonnement + + + + + + + + + Betalingsoplysninger + Dit kort debiteres først efter prøveperioden + + + + + Kortnummer + + + + + + + + + + + Udløbsdato + + + + + + + CVV + + + + + + + + Navn på kort + + + + + + + + Sikker betaling med SSL-kryptering + + + + Start gratis prøveperiode + + + + + + + + + + + + + Ved at fortsætte accepterer du vores vilkår + + + + + + +@section Scripts { + +} diff --git a/PlanTempus.Application/Features/Accounts/Pages/Payment.cshtml.cs b/PlanTempus.Application/Features/Accounts/Pages/Payment.cshtml.cs new file mode 100644 index 0000000..fae5c8a --- /dev/null +++ b/PlanTempus.Application/Features/Accounts/Pages/Payment.cshtml.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; +using PlanTempus.Application.Features.Accounts.Models; + +namespace PlanTempus.Application.Features.Accounts.Pages; + +public class PaymentModel : PageModel +{ + public PlanInfo SelectedPlan { get; private set; } = PlanCatalog.Pro; + + public void OnGet(string plan = "pro") + { + SelectedPlan = PlanCatalog.GetPlan(plan); + } +} diff --git a/PlanTempus.Application/Features/Accounts/Pages/Pricing.cshtml b/PlanTempus.Application/Features/Accounts/Pages/Pricing.cshtml new file mode 100644 index 0000000..5808513 --- /dev/null +++ b/PlanTempus.Application/Features/Accounts/Pages/Pricing.cshtml @@ -0,0 +1,83 @@ +@page "/pricing" +@model PlanTempus.Application.Features.Accounts.Pages.PricingModel +@{ + ViewData["Title"] = "Vælg abonnement"; + Layout = "/Features/_Shared/Pages/_AuthLayout.cshtml"; +} + + + + + + PlanTempus + + + Vælg dit abonnement + Start med 14 dages gratis prøveperiode. Ingen binding. + + + + @foreach (var plan in Model.Plans) + { + var cardClass = plan.Key switch + { + "pro" => "popular", + "enterprise" => "enterprise", + _ => "" + }; + + var actionUrl = plan.RequiresPayment + ? $"/payment?plan={plan.Key}" + : $"/signup?plan={plan.Key}"; + + var buttonText = plan.Key switch + { + "basis" => "Kom i gang gratis", + "enterprise" => "Kontakt salg", + _ => $"Vælg {plan.Name}" + }; + + var buttonClass = plan.IsContactSales ? "outline" : "primary"; + + + + + @plan.BadgeText + + @plan.Name + @plan.UserRange + + @if (plan.PricePerMonth.HasValue) + { + @plan.PriceDisplay + kr/md + } + else + { + Kontakt os + } + + + @foreach (var feature in plan.Features) + { + + + @feature + + } + + + + @buttonText + + + + } + + + + + Har du allerede en konto? Log ind + + + diff --git a/PlanTempus.Application/Features/Accounts/Pages/Pricing.cshtml.cs b/PlanTempus.Application/Features/Accounts/Pages/Pricing.cshtml.cs new file mode 100644 index 0000000..6b9bc9d --- /dev/null +++ b/PlanTempus.Application/Features/Accounts/Pages/Pricing.cshtml.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; +using PlanTempus.Application.Features.Accounts.Models; + +namespace PlanTempus.Application.Features.Accounts.Pages; + +public class PricingModel : PageModel +{ + public IEnumerable Plans { get; private set; } = Enumerable.Empty(); + + public void OnGet() + { + Plans = PlanCatalog.GetAllPlans(); + } +} diff --git a/PlanTempus.Application/Features/Accounts/Pages/Signup.cshtml b/PlanTempus.Application/Features/Accounts/Pages/Signup.cshtml new file mode 100644 index 0000000..efcc2d1 --- /dev/null +++ b/PlanTempus.Application/Features/Accounts/Pages/Signup.cshtml @@ -0,0 +1,283 @@ +@page "/signup" +@model PlanTempus.Application.Features.Accounts.Pages.SignupModel +@{ + ViewData["Title"] = Model.SelectedPlan.IsContactSales ? "Kontakt salg" : "Opret konto"; + Layout = "/Features/_Shared/Pages/_AuthLayout.cshtml"; + + var plan = Model.SelectedPlan; + var badgeClass = plan.IsContactSales ? plan.BadgeClass : (plan.IsFree ? plan.BadgeClass : "selected"); + var badgeIcon = plan.IsContactSales ? plan.BadgeIcon : (plan.IsFree ? plan.BadgeIcon : "ph-check"); + var badgeText = plan.IsContactSales ? plan.BadgeText : (plan.IsFree ? plan.BadgeText : "Valgt"); +} + + + + + + + PlanTempus + + + @(plan.IsContactSales ? "Enterprise løsning" : "Dit valgte abonnement") + + + + + @badgeText + + @plan.Name + @plan.UserRange + @if (plan.PricePerMonth.HasValue) + { + + @plan.PriceDisplay + kr/md + + } + + @foreach (var feature in plan.Features) + { + + + @feature + + } + + + + + + + Skift abonnement + + + + + + + + @if (plan.IsContactSales) + { + + + Kontakt vores salgsteam + Vi vender tilbage inden for 24 timer + + + + + Virksomhedsnavn + + + + + + + Antal ansatte + + + + + + + Kontaktperson + + + + + + + + Email + + + + + + + Telefon + + + + + + + + Besked (valgfrit) + + + + + + + Send forespørgsel + + + + + + + Eller ring til os på 70 12 34 56 + + + } + else + { + + + Opret din konto + + @(plan.IsFree ? "Kom i gang gratis - ingen kreditkort påkrævet" : "Start din gratis 14-dages prøveperiode") + + + + + + MJ + + Maria Jensen + maria@example.dk + + + + + + + + + Virksomhedsnavn + + + + + + + Vælg adgangskode + + + + + + + + + + + + + + + + + + Jeg accepterer vilkår og betingelser + samt privatlivspolitikken + + + + + Opret konto + + + + + + + Har du allerede en konto? Log ind + + + } + + + + +@if (!Model.SelectedPlan.IsContactSales) +{ + @section Scripts { + + } +} diff --git a/PlanTempus.Application/Features/Accounts/Pages/Signup.cshtml.cs b/PlanTempus.Application/Features/Accounts/Pages/Signup.cshtml.cs new file mode 100644 index 0000000..03059d2 --- /dev/null +++ b/PlanTempus.Application/Features/Accounts/Pages/Signup.cshtml.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; +using PlanTempus.Application.Features.Accounts.Models; + +namespace PlanTempus.Application.Features.Accounts.Pages; + +public class SignupModel : PageModel +{ + public PlanInfo SelectedPlan { get; private set; } = PlanCatalog.Pro; + + public void OnGet(string plan = "pro") + { + SelectedPlan = PlanCatalog.GetPlan(plan); + } +} diff --git a/PlanTempus.Application/Features/_Shared/Pages/_AuthLayout.cshtml b/PlanTempus.Application/Features/_Shared/Pages/_AuthLayout.cshtml new file mode 100644 index 0000000..cd6fcf8 --- /dev/null +++ b/PlanTempus.Application/Features/_Shared/Pages/_AuthLayout.cshtml @@ -0,0 +1,22 @@ + + + + + + @ViewData["Title"] - PlanTempus + + + + + + + + + @await RenderSectionAsync("Styles", required: false) + + + @RenderBody() + + @await RenderSectionAsync("Scripts", required: false) + + diff --git a/PlanTempus.Application/wwwroot/css/auth.css b/PlanTempus.Application/wwwroot/css/auth.css new file mode 100644 index 0000000..714f7cb --- /dev/null +++ b/PlanTempus.Application/wwwroot/css/auth.css @@ -0,0 +1,1143 @@ +/** + * Auth Styles - Authentication Pages + * + * Login, Signup, Password Reset etc. + * Split-screen SaaS layout pattern + */ + +/* =========================================== + AUTH LAYOUT - Split Screen + =========================================== */ +swp-auth-layout { + display: grid; + grid-template-columns: 45fr 55fr; + min-height: 100vh; +} + +/* =========================================== + BRANDING PANEL (Left Side) + =========================================== */ +swp-auth-branding { + display: flex; + flex-direction: column; + justify-content: center; + padding: var(--spacing-24); + background: linear-gradient(135deg, var(--color-teal) 0%, #00695c 100%); + color: white; + position: relative; + overflow: hidden; +} + +/* Decorative background pattern */ +swp-auth-branding::before { + content: ''; + position: absolute; + top: -50%; + right: -50%; + width: 100%; + height: 100%; + background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%); + pointer-events: none; +} + +swp-auth-branding::after { + content: ''; + position: absolute; + bottom: -30%; + left: -30%; + width: 80%; + height: 80%; + background: radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 60%); + pointer-events: none; +} + +swp-auth-logo { + display: flex; + align-items: center; + gap: var(--spacing-3); + margin-bottom: var(--spacing-16); + position: relative; + z-index: 1; +} + +swp-auth-logo i { + font-size: 32px; +} + +swp-auth-logo span { + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-bold); + letter-spacing: -0.5px; +} + +swp-auth-tagline { + display: block; + font-size: var(--font-size-3xl); + font-weight: var(--font-weight-semibold); + line-height: var(--line-height-tight); + margin-bottom: var(--spacing-12); + position: relative; + z-index: 1; + max-width: 400px; +} + +swp-auth-subtitle { + display: block; + font-size: var(--font-size-lg); + opacity: 0.9; + margin-bottom: var(--spacing-16); + position: relative; + z-index: 1; + max-width: 380px; + line-height: var(--line-height-relaxed); +} + +/* =========================================== + PLAN PANEL (Left Side - Alternative) + =========================================== */ +swp-auth-plan-panel { + display: flex; + flex-direction: column; + justify-content: center; + padding: var(--spacing-24); + background: var(--color-background); + position: relative; +} + +swp-auth-plan-panel swp-auth-logo { + color: var(--color-text); + margin-bottom: var(--spacing-12); +} + +swp-auth-plan-panel swp-auth-logo i { + color: var(--color-teal); +} + +swp-selected-plan-header { + display: block; + font-size: var(--font-size-sm); + font-weight: var(--font-weight-semibold); + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--color-text-secondary); + margin-bottom: var(--spacing-5); +} + +/* =========================================== + PLAN CARD (Selected Plan) + =========================================== */ +swp-plan-card { + display: flex; + flex-direction: column; + background: var(--color-surface); + border: 2px solid var(--color-border); + border-radius: var(--radius-xl); + padding: var(--spacing-10); + transition: all var(--transition-normal); +} + +swp-plan-card.selected { + border-color: var(--color-teal); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-teal) 15%, transparent); +} + +swp-plan-badge { + display: inline-flex; + align-items: center; + gap: var(--spacing-2); + padding: var(--spacing-2) var(--spacing-3); + font-size: var(--font-size-xs); + font-weight: var(--font-weight-semibold); + text-transform: uppercase; + letter-spacing: 0.5px; + border-radius: var(--radius-pill); + width: fit-content; + margin-bottom: var(--spacing-4); +} + +swp-plan-badge.selected { + background: color-mix(in srgb, var(--color-teal) 15%, transparent); + color: var(--color-teal); +} + +swp-plan-badge i { + font-size: 14px; +} + +swp-plan-name { + display: block; + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-bold); + color: var(--color-text); + margin-bottom: var(--spacing-1); +} + +swp-plan-users { + display: block; + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + margin-bottom: var(--spacing-5); +} + +swp-plan-price { + display: flex; + align-items: baseline; + gap: var(--spacing-1); + margin-bottom: var(--spacing-6); + min-height: 44px; +} + +swp-plan-price-amount { + font-size: 36px; + font-weight: var(--font-weight-bold); + color: var(--color-text); + line-height: 1; +} + +swp-plan-price-amount.contact { + font-size: var(--font-size-xl); + color: var(--color-purple); +} + +swp-plan-price-period { + font-size: var(--font-size-base); + color: var(--color-text-secondary); +} + +swp-plan-features { + display: flex; + flex-direction: column; + gap: var(--spacing-3); +} + +swp-plan-feature { + display: flex; + align-items: center; + gap: var(--spacing-3); + font-size: var(--font-size-sm); + color: var(--color-text); +} + +swp-plan-feature i { + font-size: 18px; + color: var(--color-green); + flex-shrink: 0; +} + +/* =========================================== + CHANGE PLAN LINK + =========================================== */ +swp-change-plan-link { + display: block; + margin-top: var(--spacing-6); +} + +swp-change-plan-link a { + display: inline-flex; + align-items: center; + gap: var(--spacing-2); + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + text-decoration: none; + transition: color var(--transition-fast); +} + +swp-change-plan-link a:hover { + color: var(--color-teal); +} + +swp-change-plan-link i { + font-size: 16px; +} + +/* =========================================== + FEATURE LIST + =========================================== */ +swp-auth-features { + display: flex; + flex-direction: column; + gap: var(--spacing-5); + position: relative; + z-index: 1; +} + +swp-auth-feature { + display: flex; + align-items: center; + gap: var(--spacing-3); + font-size: var(--font-size-base); +} + +swp-auth-feature i { + font-size: 20px; + opacity: 0.9; +} + +/* =========================================== + FORM PANEL (Right Side) + =========================================== */ +swp-auth-form-panel { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: var(--spacing-24); + background: var(--color-surface); +} + +swp-auth-form-container { + width: 100%; + max-width: 420px; +} + +swp-auth-header { + display: block; + margin-bottom: var(--spacing-12); + text-align: center; +} + +swp-auth-title { + display: block; + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-semibold); + color: var(--color-text); + margin-bottom: var(--spacing-2); +} + +swp-auth-description { + display: block; + font-size: var(--font-size-base); + color: var(--color-text-secondary); +} + +/* =========================================== + USER INFO CARD (Pre-verified user) + =========================================== */ +swp-user-info-card { + display: flex; + align-items: center; + gap: var(--spacing-4); + padding: var(--spacing-4); + background: var(--color-background); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + margin-bottom: var(--spacing-6); +} + +swp-user-avatar { + width: 44px; + height: 44px; + display: flex; + align-items: center; + justify-content: center; + background: color-mix(in srgb, var(--color-teal) 15%, transparent); + color: var(--color-teal); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-semibold); + border-radius: 50%; + flex-shrink: 0; +} + +swp-user-details { + display: flex; + flex-direction: column; + gap: var(--spacing-1); + flex: 1; + min-width: 0; +} + +swp-user-name { + display: block; + font-size: var(--font-size-base); + font-weight: var(--font-weight-medium); + color: var(--color-text); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +swp-user-email { + display: block; + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +swp-user-verified { + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + color: var(--color-green); + flex-shrink: 0; +} + +swp-user-verified i { + font-size: 22px; +} + +/* =========================================== + FORM ELEMENTS + =========================================== */ +swp-auth-form { + display: flex; + flex-direction: column; + gap: var(--spacing-5); +} + +swp-form-group { + display: flex; + flex-direction: column; + gap: var(--spacing-2); +} + +swp-form-label { + display: block; + font-size: var(--font-size-sm); + font-weight: var(--font-weight-medium); + color: var(--color-text); +} + +swp-form-input { + position: relative; +} + +swp-form-input input { + width: 100%; + padding: var(--spacing-3) var(--spacing-4); + font-size: var(--font-size-base); + font-family: var(--font-family); + color: var(--color-text); + background: var(--color-background); + border: 1px solid var(--color-border); + border-radius: var(--border-radius); + transition: border-color var(--transition-fast), box-shadow var(--transition-fast); +} + +swp-form-input input::placeholder { + color: var(--color-text-muted); +} + +swp-form-input input:focus, +swp-form-input select:focus, +swp-form-input textarea:focus { + outline: none; + border-color: var(--color-teal); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-teal) 15%, transparent); +} + +swp-form-input select { + width: 100%; + padding: var(--spacing-3) var(--spacing-4); + font-size: var(--font-size-base); + font-family: var(--font-family); + color: var(--color-text); + background: var(--color-background); + border: 1px solid var(--color-border); + border-radius: var(--border-radius); + cursor: pointer; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 12px center; + padding-right: 40px; +} + +swp-form-input select option { + padding: var(--spacing-2); +} + +swp-form-input textarea { + width: 100%; + padding: var(--spacing-3) var(--spacing-4); + font-size: var(--font-size-base); + font-family: var(--font-family); + color: var(--color-text); + background: var(--color-background); + border: 1px solid var(--color-border); + border-radius: var(--border-radius); + resize: vertical; + min-height: 80px; +} + +swp-form-input textarea::placeholder { + color: var(--color-text-muted); +} + +swp-form-label .optional { + font-weight: var(--font-weight-normal); + color: var(--color-text-muted); +} + +/* Input with icon */ +swp-form-input.has-icon input { + padding-right: 44px; +} + +swp-form-input .input-icon { + position: absolute; + right: var(--spacing-3); + top: 50%; + transform: translateY(-50%); + color: var(--color-text-muted); + cursor: pointer; + padding: var(--spacing-2); + border-radius: var(--radius-sm); + transition: color var(--transition-fast); +} + +swp-form-input .input-icon:hover { + color: var(--color-text-secondary); +} + +swp-form-input .input-icon i { + font-size: 20px; + display: block; +} + +/* =========================================== + PASSWORD STRENGTH INDICATOR + =========================================== */ +swp-password-strength { + display: flex; + align-items: center; + gap: var(--spacing-3); + margin-top: var(--spacing-2); +} + +swp-strength-bar { + flex: 1; + height: 4px; + background: var(--color-border); + border-radius: 2px; + overflow: hidden; +} + +swp-strength-fill { + height: 100%; + width: 0%; + border-radius: 2px; + transition: width var(--transition-normal), background var(--transition-normal); +} + +swp-strength-fill.weak { + width: 25%; + background: var(--color-red); +} + +swp-strength-fill.fair { + width: 50%; + background: var(--color-amber); +} + +swp-strength-fill.good { + width: 75%; + background: var(--color-blue); +} + +swp-strength-fill.strong { + width: 100%; + background: var(--color-green); +} + +swp-strength-text { + font-size: var(--font-size-xs); + color: var(--color-text-muted); + min-width: 50px; +} + +swp-strength-text.weak { color: var(--color-red); } +swp-strength-text.fair { color: var(--color-amber); } +swp-strength-text.good { color: var(--color-blue); } +swp-strength-text.strong { color: var(--color-green); } + +/* =========================================== + CHECKBOX + =========================================== */ +swp-form-checkbox { + display: flex; + align-items: flex-start; + gap: var(--spacing-3); + cursor: pointer; +} + +swp-form-checkbox input[type="checkbox"] { + width: 18px; + height: 18px; + margin: 0; + cursor: pointer; + accent-color: var(--color-teal); + flex-shrink: 0; + margin-top: 2px; +} + +swp-form-checkbox span { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + line-height: var(--line-height-normal); +} + +swp-form-checkbox a { + color: var(--color-teal); + text-decoration: none; +} + +swp-form-checkbox a:hover { + text-decoration: underline; +} + +/* =========================================== + BUTTONS + =========================================== */ +swp-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--spacing-2); + padding: var(--spacing-3) var(--spacing-6); + font-size: var(--font-size-base); + font-weight: var(--font-weight-medium); + font-family: var(--font-family); + border-radius: var(--border-radius); + border: 1px solid transparent; + cursor: pointer; + transition: all var(--transition-fast); + text-decoration: none; +} + +swp-btn.primary { + background: var(--color-teal); + color: white; + border-color: var(--color-teal); +} + +swp-btn.primary:hover { + background: #00796b; + border-color: #00796b; +} + +swp-btn.primary:active { + transform: translateY(1px); +} + +swp-btn.full-width { + width: 100%; +} + +swp-btn.lg { + padding: var(--spacing-4) var(--spacing-8); + font-size: var(--font-size-lg); +} + +swp-btn i { + font-size: 18px; +} + +/* =========================================== + AUTH FOOTER + =========================================== */ +swp-auth-footer { + display: block; + margin-top: var(--spacing-10); + text-align: center; +} + +swp-auth-footer-text { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); +} + +swp-auth-footer-text a { + color: var(--color-teal); + font-weight: var(--font-weight-medium); + text-decoration: none; +} + +swp-auth-footer-text a:hover { + text-decoration: underline; +} + +/* =========================================== + DIVIDER + =========================================== */ +swp-auth-divider { + display: flex; + align-items: center; + gap: var(--spacing-4); + margin: var(--spacing-6) 0; +} + +swp-auth-divider::before, +swp-auth-divider::after { + content: ''; + flex: 1; + height: 1px; + background: var(--color-border); +} + +swp-auth-divider span { + font-size: var(--font-size-sm); + color: var(--color-text-muted); +} + +/* =========================================== + SOCIAL LOGIN + =========================================== */ +swp-social-buttons { + display: flex; + flex-direction: column; + gap: var(--spacing-3); +} + +swp-btn.social { + background: var(--color-background); + color: var(--color-text); + border-color: var(--color-border); +} + +swp-btn.social:hover { + background: var(--color-background-hover); + border-color: var(--color-text-muted); +} + +swp-btn.social img { + width: 20px; + height: 20px; +} + +/* =========================================== + FORM MESSAGES + =========================================== */ +swp-form-error { + display: flex; + align-items: center; + gap: var(--spacing-2); + padding: var(--spacing-3) var(--spacing-4); + background: color-mix(in srgb, var(--color-red) 10%, transparent); + border: 1px solid color-mix(in srgb, var(--color-red) 30%, transparent); + border-radius: var(--border-radius); + color: var(--color-red); + font-size: var(--font-size-sm); +} + +swp-form-error i { + font-size: 18px; +} + +swp-form-success { + display: flex; + align-items: center; + gap: var(--spacing-2); + padding: var(--spacing-3) var(--spacing-4); + background: color-mix(in srgb, var(--color-green) 10%, transparent); + border: 1px solid color-mix(in srgb, var(--color-green) 30%, transparent); + border-radius: var(--border-radius); + color: var(--color-green); + font-size: var(--font-size-sm); +} + +swp-form-success i { + font-size: 18px; +} + +/* =========================================== + PRICING PAGE + =========================================== */ +swp-pricing-page { + display: flex; + flex-direction: column; + align-items: center; + min-height: 100vh; + padding: var(--spacing-16) var(--spacing-6); + background: var(--color-background); +} + +swp-pricing-header { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + margin-bottom: var(--spacing-16); +} + +swp-pricing-header swp-auth-logo { + margin-bottom: var(--spacing-10); + color: var(--color-text); +} + +swp-pricing-header swp-auth-logo i { + color: var(--color-teal); +} + +swp-pricing-title { + display: block; + font-size: var(--font-size-3xl); + font-weight: var(--font-weight-bold); + color: var(--color-text); + margin-bottom: var(--spacing-3); +} + +swp-pricing-subtitle { + display: block; + font-size: var(--font-size-lg); + color: var(--color-text-secondary); +} + +swp-pricing-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: var(--spacing-6); + max-width: 1100px; + width: 100%; + align-items: stretch; +} + +/* Plan cards in pricing grid */ +swp-pricing-grid swp-plan-card { + padding: var(--spacing-8); + position: relative; + display: flex; + flex-direction: column; + height: 100%; +} + +swp-pricing-grid swp-plan-features { + flex: 1; +} + +swp-pricing-grid swp-plan-action { + margin-top: auto; + padding-top: var(--spacing-6); +} + +swp-pricing-grid swp-plan-card.popular { + border-color: var(--color-teal); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-teal) 15%, transparent); + transform: scale(1.02); +} + +swp-pricing-grid swp-plan-card.enterprise { + background: linear-gradient(135deg, var(--color-surface) 0%, color-mix(in srgb, var(--color-purple) 5%, var(--color-surface)) 100%); + border-color: var(--color-purple); +} + +swp-plan-badge.popular { + background: color-mix(in srgb, var(--color-teal) 15%, transparent); + color: var(--color-teal); +} + +swp-plan-badge.enterprise { + background: color-mix(in srgb, var(--color-purple) 15%, transparent); + color: var(--color-purple); +} + +swp-plan-badge.free { + background: color-mix(in srgb, var(--color-green) 15%, transparent); + color: var(--color-green); +} + +swp-pricing-grid swp-plan-action { + margin-top: var(--spacing-6); +} + +/* Button styles for pricing */ +.swp-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--spacing-2); + padding: var(--spacing-3) var(--spacing-6); + font-size: var(--font-size-base); + font-weight: var(--font-weight-medium); + font-family: var(--font-family); + border-radius: var(--border-radius); + border: 1px solid transparent; + cursor: pointer; + transition: all var(--transition-fast); + text-decoration: none; +} + +.swp-btn.primary { + background: var(--color-teal); + color: white; + border-color: var(--color-teal); +} + +.swp-btn.primary:hover { + background: #00796b; + border-color: #00796b; +} + +.swp-btn.outline { + background: transparent; + color: var(--color-purple); + border-color: var(--color-purple); +} + +.swp-btn.outline:hover { + background: color-mix(in srgb, var(--color-purple) 10%, transparent); +} + +.swp-btn.full-width { + width: 100%; +} + +swp-pricing-footer { + margin-top: var(--spacing-12); + text-align: center; +} + +swp-pricing-footer-text { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); +} + +swp-pricing-footer-text a { + color: var(--color-teal); + font-weight: var(--font-weight-medium); + text-decoration: none; +} + +swp-pricing-footer-text a:hover { + text-decoration: underline; +} + +/* =========================================== + PAYMENT PAGE + =========================================== */ +swp-order-summary { + display: flex; + flex-direction: column; + gap: var(--spacing-3); + margin-top: var(--spacing-5); + padding-top: var(--spacing-5); + border-top: 1px solid var(--color-border); +} + +swp-order-line { + display: flex; + justify-content: space-between; + font-size: var(--font-size-sm); + color: var(--color-text); +} + +swp-order-line.discount { + color: var(--color-green); +} + +swp-order-line.total { + font-weight: var(--font-weight-semibold); + font-size: var(--font-size-base); +} + +swp-order-divider { + height: 1px; + background: var(--color-border); + margin: var(--spacing-2) 0; +} + +swp-form-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--spacing-4); +} + +swp-payment-secure { + display: flex; + align-items: center; + justify-content: center; + gap: var(--spacing-2); + padding: var(--spacing-3); + background: color-mix(in srgb, var(--color-green) 8%, transparent); + border-radius: var(--border-radius); + font-size: var(--font-size-sm); + color: var(--color-green); +} + +swp-payment-secure i { + font-size: 16px; +} + +swp-payment-processing { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--spacing-5); + padding: var(--spacing-16) 0; +} + +swp-spinner { + width: 48px; + height: 48px; + border: 3px solid var(--color-border); + border-top-color: var(--color-teal); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +swp-processing-text { + font-size: var(--font-size-base); + color: var(--color-text-secondary); +} + +swp-payment-success { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--spacing-4); + padding: var(--spacing-16) 0; +} + +swp-success-icon { + width: 64px; + height: 64px; + display: flex; + align-items: center; + justify-content: center; + background: color-mix(in srgb, var(--color-green) 15%, transparent); + border-radius: 50%; + color: var(--color-green); +} + +swp-success-icon i { + font-size: 32px; +} + +swp-success-title { + font-size: var(--font-size-xl); + font-weight: var(--font-weight-semibold); + color: var(--color-text); +} + +swp-success-text { + font-size: var(--font-size-base); + color: var(--color-text-secondary); +} + +/* =========================================== + RESPONSIVE DESIGN + =========================================== */ +@media (max-width: 1024px) { + swp-auth-layout { + grid-template-columns: 40fr 60fr; + } + + swp-auth-branding { + padding: var(--spacing-16); + } + + swp-auth-plan-panel { + padding: var(--spacing-16); + } + + swp-auth-tagline { + font-size: var(--font-size-2xl); + } + + swp-pricing-grid { + grid-template-columns: 1fr; + max-width: 400px; + } + + swp-pricing-grid swp-plan-card.popular { + transform: none; + order: -1; + } +} + +@media (max-width: 768px) { + swp-auth-layout { + grid-template-columns: 1fr; + grid-template-rows: auto 1fr; + } + + swp-auth-branding { + padding: var(--spacing-10) var(--spacing-6); + text-align: center; + align-items: center; + } + + swp-auth-plan-panel { + padding: var(--spacing-6); + } + + swp-auth-plan-panel swp-auth-logo { + display: none; + } + + swp-selected-plan-header { + text-align: center; + } + + swp-plan-card { + padding: var(--spacing-5); + } + + swp-plan-price-amount { + font-size: 28px; + } + + swp-auth-logo { + margin-bottom: var(--spacing-6); + justify-content: center; + } + + swp-auth-tagline { + font-size: var(--font-size-xl); + margin-bottom: var(--spacing-4); + } + + swp-auth-subtitle { + display: none; + } + + swp-auth-features { + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + gap: var(--spacing-4); + } + + swp-auth-feature { + font-size: var(--font-size-sm); + } + + swp-auth-form-panel { + padding: var(--spacing-10) var(--spacing-6); + } + + swp-auth-form-container { + max-width: 100%; + } +} + +@media (max-width: 480px) { + swp-auth-branding { + padding: var(--spacing-8) var(--spacing-5); + } + + swp-auth-plan-panel { + padding: var(--spacing-5); + } + + swp-auth-form-panel { + padding: var(--spacing-8) var(--spacing-5); + } + + swp-auth-features { + display: none; + } + + swp-change-plan-link { + text-align: center; + } +} diff --git a/PlanTempus.Application/wwwroot/css/design-system.css b/PlanTempus.Application/wwwroot/css/design-system.css index beb61ec..0e82c08 100644 --- a/PlanTempus.Application/wwwroot/css/design-system.css +++ b/PlanTempus.Application/wwwroot/css/design-system.css @@ -1,163 +1,104 @@ /** - * SWP Design System - CSS Variables + * SWP Design System * - * Dette er den centrale definition af alle design tokens. - * Alle farver, fonts og layout-variabler defineres her. + * Entry point for all design tokens and base styles. + * Import this file in your layout to get the complete design system. */ +@import url('design-tokens.css'); + /* =========================================== - COLOR PALETTE - Light Mode (Default) + BASE RESETS & DEFAULTS =========================================== */ -:root { - /* Surfaces */ - --color-surface: #fff; - --color-background: #f5f5f5; - --color-background-hover: #f0f0f0; - --color-background-alt: #fafafa; +*, *::before, *::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} - /* Borders */ - --color-border: #e0e0e0; - --color-border-light: #f0f0f0; +html { + font-size: var(--font-size-base); + line-height: var(--line-height-normal); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} - /* Text */ - --color-text: #333; - --color-text-secondary: #666; - --color-text-muted: #999; - - /* Brand Colors */ - --color-teal: #00897b; - --color-teal-light: color-mix(in srgb, var(--color-teal) 10%, transparent); - - /* Semantic Colors */ - --color-blue: #1976d2; - --color-green: #43a047; - --color-amber: #f59e0b; - --color-red: #e53935; - --color-purple: #8b5cf6; +body { + font-family: var(--font-family); + color: var(--color-text); + background-color: var(--color-background); } /* =========================================== - COLOR PALETTE - Dark Mode (System) + TYPOGRAPHY DEFAULTS =========================================== */ -@media (prefers-color-scheme: dark) { - :root:not(.light-mode) { - --color-surface: #1e1e1e; - --color-background: #121212; - --color-background-hover: #2a2a2a; - --color-background-alt: #1a1a1a; +h1, h2, h3, h4, h5, h6 { + font-weight: var(--font-weight-semibold); + line-height: var(--line-height-tight); + color: var(--color-text); +} - --color-border: #333; - --color-border-light: #2a2a2a; +h1 { font-size: var(--font-size-3xl); } +h2 { font-size: var(--font-size-2xl); } +h3 { font-size: var(--font-size-xl); } +h4 { font-size: var(--font-size-lg); } +h5 { font-size: var(--font-size-base); } +h6 { font-size: var(--font-size-md); } - --color-text: #e0e0e0; - --color-text-secondary: #999; - --color-text-muted: #666; +p { + margin-bottom: var(--spacing-8); + color: var(--color-text-secondary); +} - --color-teal: #26a69a; - --color-blue: #42a5f5; - --color-green: #66bb6a; - --color-amber: #ffb74d; - --color-red: #ef5350; - --color-purple: #a78bfa; - } +a { + color: var(--color-teal); + text-decoration: none; + transition: color var(--transition-fast); +} + +a:hover { + color: var(--color-primary); } /* =========================================== - COLOR PALETTE - Dark Mode (Manual) + FOCUS STATES =========================================== */ -:root.dark-mode { - --color-surface: #1e1e1e; - --color-background: #121212; - --color-background-hover: #2a2a2a; - --color-background-alt: #1a1a1a; - - --color-border: #333; - --color-border-light: #2a2a2a; - - --color-text: #e0e0e0; - --color-text-secondary: #999; - --color-text-muted: #666; - - --color-teal: #26a69a; - --color-blue: #42a5f5; - --color-green: #66bb6a; - --color-amber: #ffb74d; - --color-red: #ef5350; - --color-purple: #a78bfa; +:focus-visible { + outline: 2px solid var(--color-teal); + outline-offset: 2px; } /* =========================================== - TYPOGRAPHY + SCROLLBAR STYLING =========================================== */ -:root { - --font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - --font-mono: 'JetBrains Mono', monospace; +::-webkit-scrollbar { + width: 8px; + height: 8px; +} - /* Font Sizes */ - --font-size-xs: 11px; - --font-size-sm: 12px; - --font-size-base: 14px; - --font-size-md: 13px; - --font-size-lg: 16px; - --font-size-xl: 22px; +::-webkit-scrollbar-track { + background: var(--color-background); +} - /* Line Heights */ - --line-height-tight: 1.25; - --line-height-normal: 1.5; - --line-height-relaxed: 1.75; +::-webkit-scrollbar-thumb { + background: var(--color-border); + border-radius: var(--radius-md); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--color-text-muted); +} + +/* Firefox */ +* { + scrollbar-width: thin; + scrollbar-color: var(--color-border) var(--color-background); } /* =========================================== - SPACING + SELECTION =========================================== */ -:root { - --spacing-1: 4px; - --spacing-2: 8px; - --spacing-3: 12px; - --spacing-4: 16px; - --spacing-5: 20px; - --spacing-6: 24px; - --spacing-8: 32px; -} - -/* =========================================== - LAYOUT - =========================================== */ -:root { - --side-menu-width: 240px; - --side-menu-width-collapsed: 64px; - --topbar-height: 56px; - --page-max-width: 1400px; - --border-radius: 6px; - --border-radius-lg: 8px; -} - -/* =========================================== - TRANSITIONS - =========================================== */ -:root { - --transition-fast: 150ms ease; - --transition-normal: 200ms ease; - --transition-slow: 300ms ease; -} - -/* =========================================== - Z-INDEX LAYERS - =========================================== */ -:root { - --z-dropdown: 100; - --z-sticky: 200; - --z-overlay: 900; - --z-drawer: 1000; - --z-modal: 1100; - --z-tooltip: 1200; -} - -/* =========================================== - SHADOWS - =========================================== */ -:root { - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); - --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.1); - --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.15); +::selection { + background: color-mix(in srgb, var(--color-teal) 30%, transparent); + color: var(--color-text); } diff --git a/PlanTempus.Application/wwwroot/css/design-tokens.css b/PlanTempus.Application/wwwroot/css/design-tokens.css new file mode 100644 index 0000000..346e5a9 --- /dev/null +++ b/PlanTempus.Application/wwwroot/css/design-tokens.css @@ -0,0 +1,311 @@ +/** + * SWP Design System - Unified Design Tokens + * + * Konsolideret fra Calendar POC og PlanTempus Application + * Brug disse tokens konsistent på tværs af alle projekter. + */ + +/* =========================================== + COLOR PALETTE - Light Mode (Default) + =========================================== */ +:root { + /* -------- Surfaces & Backgrounds -------- */ + --color-surface: #fff; + --color-background: #f5f5f5; + --color-background-hover: #f0f0f0; + --color-background-alt: #fafafa; + + /* -------- Borders -------- */ + --color-border: #e0e0e0; + --color-border-light: #f0f0f0; + + /* -------- Text -------- */ + --color-text: #333; + --color-text-secondary: #666; + --color-text-muted: #999; + + /* -------- Primary Brand -------- */ + --color-primary: #1976d2; + --color-teal: #00897b; + --color-teal-light: color-mix(in srgb, var(--color-teal) 10%, transparent); + + /* -------- Semantic / Status -------- */ + --color-blue: #1976d2; + --color-green: #43a047; + --color-amber: #f59e0b; + --color-red: #e53935; + --color-purple: #8b5cf6; + + /* -------- Team/Resource -------- */ + --color-team-bg: #e3f2fd; + --color-team-text: #1565c0; + + /* -------- Grid Lines -------- */ + --color-hour-line: rgba(0, 0, 0, 0.2); + --color-grid-line-light: rgba(0, 0, 0, 0.05); + --color-unavailable: rgba(0, 0, 0, 0.02); +} + +/* =========================================== + COLOR PALETTE - Dark Mode (System) + =========================================== */ +@media (prefers-color-scheme: dark) { + :root:not(.light-mode) { + --color-surface: #1e1e1e; + --color-background: #121212; + --color-background-hover: #2a2a2a; + --color-background-alt: #1a1a1a; + + --color-border: #333; + --color-border-light: #2a2a2a; + + --color-text: #e0e0e0; + --color-text-secondary: #999; + --color-text-muted: #666; + + --color-teal: #26a69a; + --color-blue: #42a5f5; + --color-green: #66bb6a; + --color-amber: #ffb74d; + --color-red: #ef5350; + --color-purple: #a78bfa; + } +} + +/* =========================================== + COLOR PALETTE - Dark Mode (Manual Toggle) + =========================================== */ +:root.dark-mode { + --color-surface: #1e1e1e; + --color-background: #121212; + --color-background-hover: #2a2a2a; + --color-background-alt: #1a1a1a; + + --color-border: #333; + --color-border-light: #2a2a2a; + + --color-text: #e0e0e0; + --color-text-secondary: #999; + --color-text-muted: #666; + + --color-teal: #26a69a; + --color-blue: #42a5f5; + --color-green: #66bb6a; + --color-amber: #ffb74d; + --color-red: #ef5350; + --color-purple: #a78bfa; +} + +/* =========================================== + EVENT COLOR PALETTE (18 Colors) + =========================================== */ +:root { + --b-color-red: #e53935; + --b-color-pink: #d81b60; + --b-color-magenta: #c200c2; + --b-color-purple: #8e24aa; + --b-color-violet: #5e35b1; + --b-color-deep-purple: #4527a0; + --b-color-indigo: #3949ab; + --b-color-blue: #1e88e5; + --b-color-light-blue: #03a9f4; + --b-color-cyan: #3bc9db; + --b-color-teal: #00897b; + --b-color-green: #43a047; + --b-color-light-green: #8bc34a; + --b-color-lime: #c0ca33; + --b-color-yellow: #fdd835; + --b-color-amber: #ffb300; + --b-color-orange: #fb8c00; + --b-color-deep-orange: #f4511e; + + /* For color-mix calculations */ + --b-mix: #fff; +} + +/* =========================================== + TYPOGRAPHY + =========================================== */ +:root { + /* -------- Font Families -------- */ + --font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'JetBrains Mono', monospace; + + /* -------- Font Sizes -------- */ + --font-size-xs: 11px; + --font-size-sm: 12px; + --font-size-md: 13px; + --font-size-base: 14px; + --font-size-lg: 16px; + --font-size-xl: 18px; + --font-size-2xl: 20px; + --font-size-3xl: 22px; + + /* -------- Font Weights -------- */ + --font-weight-light: 300; + --font-weight-regular: 400; + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + + /* -------- Line Heights -------- */ + --line-height-tight: 1.25; + --line-height-snug: 1.3; + --line-height-normal: 1.5; + --line-height-relaxed: 1.6; +} + +/* =========================================== + SPACING + =========================================== */ +:root { + --spacing-0: 0; + --spacing-1: 2px; + --spacing-2: 4px; + --spacing-3: 6px; + --spacing-4: 8px; + --spacing-5: 10px; + --spacing-6: 12px; + --spacing-7: 14px; + --spacing-8: 16px; + --spacing-10: 20px; + --spacing-12: 24px; + --spacing-16: 32px; + --spacing-24: 48px; +} + +/* =========================================== + LAYOUT + =========================================== */ +:root { + /* -------- Sidebars & Navigation -------- */ + --side-menu-width: 240px; + --side-menu-width-collapsed: 64px; + --topbar-height: 56px; + + /* -------- Content Containers -------- */ + --page-max-width: 1400px; + --container-max-width-sm: 900px; + --container-max-width-md: 1200px; + --container-max-width-lg: 1400px; + + /* -------- Calendar Grid -------- */ + --hour-height: 64px; + --time-axis-width: 60px; + --grid-columns: 7; + --day-column-min-width: 200px; + --day-start-hour: 6; + --day-end-hour: 18; + --header-height: 70px; +} + +/* =========================================== + BORDERS & RADIUS + =========================================== */ +:root { + /* -------- Border Radius -------- */ + --radius-sm: 3px; + --radius-md: 4px; + --radius-lg: 6px; + --radius-xl: 8px; + --radius-2xl: 12px; + --radius-pill: 20px; + --radius-full: 50%; + + /* Legacy aliases */ + --border-radius: 6px; + --border-radius-lg: 8px; +} + +/* =========================================== + SHADOWS + =========================================== */ +:root { + --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05); + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08); + --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.15); + --shadow-xl: 0 4px 20px rgba(0, 0, 0, 0.15); + + /* Specific use-cases */ + --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.08); + --shadow-dropdown: 0 4px 12px rgba(0, 0, 0, 0.15); + --shadow-drawer: 0 4px 20px rgba(0, 0, 0, 0.1); + --shadow-dragging: 0 4px 12px rgba(0, 0, 0, 0.15); +} + +/* =========================================== + TRANSITIONS + =========================================== */ +:root { + --transition-fast: 150ms ease; + --transition-normal: 200ms ease; + --transition-slow: 300ms ease; + + /* Specific timing functions */ + --ease-out: ease-out; + --ease-in-out: ease-in-out; + --ease-spring: cubic-bezier(0.4, 0, 0.2, 1); +} + +/* =========================================== + Z-INDEX LAYERS + =========================================== */ +:root { + --z-base: 1; + --z-grid: 2; + --z-event: 10; + --z-event-hover: 20; + --z-resize-handle: 25; + --z-dropdown: 100; + --z-sticky: 200; + --z-overlay: 900; + --z-drawer: 1000; + --z-modal: 1100; + --z-tooltip: 1200; + --z-dragging: 999999; +} + +/* =========================================== + BREAKPOINTS (as CSS custom properties) + For reference - use @media queries in CSS + =========================================== */ +/* + --breakpoint-sm: 600px; + --breakpoint-md: 768px; + --breakpoint-lg: 1024px; + --breakpoint-xl: 1100px; + --breakpoint-2xl: 1400px; +*/ + +/* =========================================== + UTILITY CLASSES - Event Colors + =========================================== */ +.is-red { --b-primary: var(--b-color-red); } +.is-pink { --b-primary: var(--b-color-pink); } +.is-magenta { --b-primary: var(--b-color-magenta); } +.is-purple { --b-primary: var(--b-color-purple); } +.is-violet { --b-primary: var(--b-color-violet); } +.is-deep-purple { --b-primary: var(--b-color-deep-purple); } +.is-indigo { --b-primary: var(--b-color-indigo); } +.is-blue { --b-primary: var(--b-color-blue); } +.is-light-blue { --b-primary: var(--b-color-light-blue); } +.is-cyan { --b-primary: var(--b-color-cyan); } +.is-teal { --b-primary: var(--b-color-teal); } +.is-green { --b-primary: var(--b-color-green); } +.is-light-green { --b-primary: var(--b-color-light-green); } +.is-lime { --b-primary: var(--b-color-lime); } +.is-yellow { --b-primary: var(--b-color-yellow); } +.is-amber { --b-primary: var(--b-color-amber); } +.is-orange { --b-primary: var(--b-color-orange); } +.is-deep-orange { --b-primary: var(--b-color-deep-orange); } + +/* =========================================== + UTILITY CLASSES - Status Badges + =========================================== */ +.status-confirmed { background: color-mix(in srgb, var(--color-green) 15%, transparent); color: var(--color-green); } +.status-pending { background: color-mix(in srgb, var(--color-amber) 15%, transparent); color: var(--color-amber); } +.status-inprogress { background: color-mix(in srgb, var(--color-blue) 15%, transparent); color: var(--color-blue); } +.status-error { background: color-mix(in srgb, var(--color-red) 15%, transparent); color: var(--color-red); } +.status-active { background: color-mix(in srgb, var(--color-green) 15%, transparent); color: var(--color-green); } +.status-inactive { background: var(--color-background); color: var(--color-text-muted); } diff --git a/PlanTempus.Application/wwwroot/css/drawers.css b/PlanTempus.Application/wwwroot/css/drawers.css index 2805da5..218d3fd 100644 --- a/PlanTempus.Application/wwwroot/css/drawers.css +++ b/PlanTempus.Application/wwwroot/css/drawers.css @@ -101,11 +101,11 @@ swp-profile-section { swp-profile-avatar-large { width: 64px; height: 64px; - border-radius: 50%; + border-radius: var(--radius-full); background: var(--color-teal); color: white; - font-size: 24px; - font-weight: 600; + font-size: var(--font-size-3xl); + font-weight: var(--font-weight-semibold); display: flex; align-items: center; justify-content: center; @@ -114,7 +114,7 @@ swp-profile-avatar-large { swp-profile-name-large { font-size: var(--font-size-lg); - font-weight: 600; + font-weight: var(--font-weight-semibold); color: var(--color-text); margin-bottom: var(--spacing-1); } diff --git a/PlanTempus.Application/wwwroot/css/page.css b/PlanTempus.Application/wwwroot/css/page.css index cf4b9c7..dceab74 100644 --- a/PlanTempus.Application/wwwroot/css/page.css +++ b/PlanTempus.Application/wwwroot/css/page.css @@ -25,8 +25,8 @@ swp-page-header { } swp-page-title h1 { - font-size: 24px; - font-weight: 600; + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-semibold); color: var(--color-text); margin-bottom: var(--spacing-1); } @@ -65,12 +65,12 @@ swp-card-title { align-items: center; gap: var(--spacing-2); font-size: var(--font-size-base); - font-weight: 600; + font-weight: var(--font-weight-semibold); color: var(--color-text); } swp-card-title i { - font-size: 20px; + font-size: var(--font-size-xl); color: var(--color-text-secondary); } @@ -127,12 +127,12 @@ swp-ai-header { gap: var(--spacing-2); margin-bottom: var(--spacing-2); font-size: var(--font-size-sm); - font-weight: 500; + font-weight: var(--font-weight-medium); color: var(--color-purple); } swp-ai-header i { - font-size: 16px; + font-size: var(--font-size-base); } swp-ai-text { @@ -172,7 +172,7 @@ swp-quick-action-btn:hover { } swp-quick-action-btn i { - font-size: 18px; + font-size: var(--font-size-lg); } /* =========================================== diff --git a/PlanTempus.Application/wwwroot/css/sidebar.css b/PlanTempus.Application/wwwroot/css/sidebar.css index cc9354e..71b637c 100644 --- a/PlanTempus.Application/wwwroot/css/sidebar.css +++ b/PlanTempus.Application/wwwroot/css/sidebar.css @@ -23,7 +23,7 @@ swp-side-menu { swp-side-menu-header { display: flex; align-items: center; - gap: 10px; + gap: var(--spacing-5); height: var(--topbar-height); padding: 0 var(--spacing-4); border-bottom: 1px solid var(--color-border); @@ -100,7 +100,7 @@ a[is="swp-side-menu-item"] { display: flex; align-items: center; gap: var(--spacing-3); - padding: 10px var(--spacing-4); + padding: var(--spacing-5) var(--spacing-4); color: var(--color-text); cursor: pointer; transition: all var(--transition-fast); @@ -146,7 +146,7 @@ swp-side-menu-action { align-items: center; justify-content: center; gap: var(--spacing-2); - padding: 10px; + padding: var(--spacing-5); font-size: var(--font-size-md); color: var(--color-text-secondary); background: transparent; diff --git a/PlanTempus.Application/wwwroot/css/stats.css b/PlanTempus.Application/wwwroot/css/stats.css index 2d50a5f..eb4ed7a 100644 --- a/PlanTempus.Application/wwwroot/css/stats.css +++ b/PlanTempus.Application/wwwroot/css/stats.css @@ -49,8 +49,8 @@ swp-stat-box { =========================================== */ swp-stat-value { display: block; - font-size: 22px; - font-weight: 600; + font-size: var(--font-size-3xl); + font-weight: var(--font-weight-semibold); font-family: var(--font-mono); color: var(--color-text); line-height: var(--line-height-tight); @@ -59,8 +59,8 @@ swp-stat-value { /* Larger variant for emphasis */ swp-stat-card swp-stat-value, swp-stat-box swp-stat-value { - font-size: 22px; - font-weight: 600; + font-size: var(--font-size-3xl); + font-weight: var(--font-weight-semibold); font-family: var(--font-mono); color: var(--color-text); } @@ -76,8 +76,8 @@ swp-stat-label { } swp-stat-box swp-stat-label { - font-size: 11px; - font-weight: 500; + font-size: var(--font-size-xs); + font-weight: var(--font-weight-medium); text-transform: uppercase; letter-spacing: 0.5px; color: var(--color-text-secondary); @@ -88,7 +88,7 @@ swp-stat-box swp-stat-label { =========================================== */ swp-stat-subtitle { display: block; - font-size: 11px; + font-size: var(--font-size-xs); color: var(--color-text-muted); margin-top: var(--spacing-1); } @@ -201,11 +201,11 @@ swp-quick-stat { } swp-quick-stat swp-stat-value { - font-size: 18px; + font-size: var(--font-size-xl); } swp-quick-stat swp-stat-label { - font-size: 11px; + font-size: var(--font-size-xs); margin-top: var(--spacing-1); } @@ -215,15 +215,15 @@ swp-quick-stat swp-stat-label { swp-stat-item { display: flex; flex-direction: column; - gap: 2px; + gap: var(--spacing-1); padding: var(--spacing-2) var(--spacing-3); background: var(--color-background); border-radius: var(--border-radius); } swp-stat-item swp-stat-value { - font-size: 16px; - font-weight: 600; + font-size: var(--font-size-lg); + font-weight: var(--font-weight-semibold); } swp-stat-item swp-stat-value.mono { @@ -231,7 +231,7 @@ swp-stat-item swp-stat-value.mono { } swp-stat-item swp-stat-label { - font-size: 11px; + font-size: var(--font-size-xs); margin-top: 0; } diff --git a/PlanTempus.Application/wwwroot/css/topbar.css b/PlanTempus.Application/wwwroot/css/topbar.css index 6c9d182..723a45d 100644 --- a/PlanTempus.Application/wwwroot/css/topbar.css +++ b/PlanTempus.Application/wwwroot/css/topbar.css @@ -25,7 +25,7 @@ swp-app-topbar { swp-topbar-search { display: flex; align-items: center; - gap: 10px; + gap: var(--spacing-5); padding: var(--spacing-2) var(--spacing-3); background: var(--color-background); border: 1px solid var(--color-border); @@ -133,8 +133,8 @@ swp-topbar-divider { swp-topbar-profile { display: flex; align-items: center; - gap: 10px; - padding: 6px var(--spacing-3) 6px 6px; + gap: var(--spacing-5); + padding: var(--spacing-3) var(--spacing-3) var(--spacing-3) var(--spacing-3); background: transparent; border: 1px solid transparent; border-radius: var(--border-radius);