diff --git a/MidrandBookshop/Components/App.razor b/MidrandBookshop/Components/App.razor index c39d234..4e4351f 100644 --- a/MidrandBookshop/Components/App.razor +++ b/MidrandBookshop/Components/App.razor @@ -11,13 +11,14 @@ - + - + + diff --git a/MidrandBookshop/Components/BookCard.razor b/MidrandBookshop/Components/BookCard.razor new file mode 100644 index 0000000..c3aec62 --- /dev/null +++ b/MidrandBookshop/Components/BookCard.razor @@ -0,0 +1,26 @@ +
+
+ +
+ New + +
+ +
+ @Title +
+ +
+
+ @Title + $@Price +
+
+ +@code { + [Parameter] public string Title { get; set; } = ""; + [Parameter] public decimal Price { get; set; } + [Parameter] public string BookImageUrl { get; set; } = ""; +} \ No newline at end of file diff --git a/MidrandBookshop/Components/BookCard.razor.css b/MidrandBookshop/Components/BookCard.razor.css new file mode 100644 index 0000000..49a2369 --- /dev/null +++ b/MidrandBookshop/Components/BookCard.razor.css @@ -0,0 +1,9 @@ +.book-shadow { + filter: drop-shadow(5px 10px 15px rgba(0, 0, 0, 0.15)) drop-shadow(1px 2px 4px rgba(0, 0, 0, 0.1)); +} + +.sm-icon { + width: 14px; + height: 14px; + vertical-align: middle; +} diff --git a/MidrandBookshop/Components/Layout/MainLayout.razor b/MidrandBookshop/Components/Layout/MainLayout.razor index b7d6acb..6039b30 100644 --- a/MidrandBookshop/Components/Layout/MainLayout.razor +++ b/MidrandBookshop/Components/Layout/MainLayout.razor @@ -1,13 +1,318 @@ @inherits LayoutComponentBase +@inject NavigationManager Navigation -
-
-
-

Midrand Books

+
+ +
+
+
+
+ YOUR CART (@CartItems.Sum(i => i.Quantity)) +
+
-
- @Body -
-
+
+ @if (!CartItems.Any()) + { +
+ + + + Your collection is empty. +
+ } + else + { +
+ @foreach (var item in CartItems) + { +
+
+ [ COVER ] +
+
+
@item.Title
+

by @item.Author

+
+
+ + @item.Quantity + +
+ R @(item.Price * item.Quantity) +
+
+ +
+ } +
+ } +
+ + @if (CartItems.Any()) + { + + } +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + @Body + +
+
+ + + +@code { + private string GlobalSearchQuery { get; set; } = string.Empty; + private bool IsSearchActive { get; set; } = false; + private bool IsCartOpen { get; set; } = false; + + private List CartItems = new() + { + new CartItem { Id = 1, Title = "Letters from M/M (Paris)", Author = "M/M Paris", Price = 720, Quantity = 1 }, + new CartItem { Id = 2, Title = "Daan Paans: Floating Signifiers", Author = "Daan Paans", Price = 540, Quantity = 1 }, + new CartItem { Id = 3, Title = "Album Architectures, Maputo", Author = "Guedes Archive", Price = 350, Quantity = 1 } + }; + + private void ToggleGlobalSearch() => IsSearchActive = !IsSearchActive; + private void ToggleCart() => IsCartOpen = !IsCartOpen; + + private void OnSearchInput(ChangeEventArgs e) + { + GlobalSearchQuery = e.Value?.ToString() ?? string.Empty; + } + + private void ChangeQuantity(CartItem item, int delta) + { + item.Quantity += delta; + if (item.Quantity <= 0) + { + CartItems.Remove(item); + } + } + + private void RemoveFromCart(CartItem item) => CartItems.Remove(item); + private int GetCartTotal() => CartItems.Sum(item => item.Price * item.Quantity); + + private void RedirectToCart() + { + IsCartOpen = false; + Navigation.NavigateTo("/cart"); + } + + private void RedirectToCheckout() + { + IsCartOpen = false; + Navigation.NavigateTo("/checkout"); + } + + public class CartItem + { + public int Id { get; set; } + public string Title { get; set; } = string.Empty; + public string Author { get; set; } = string.Empty; + public int Price { get; set; } + public int Quantity { get; set; } + } +} \ No newline at end of file diff --git a/MidrandBookshop/Components/Layout/MainLayout.razor.css b/MidrandBookshop/Components/Layout/MainLayout.razor.css index 38d1f25..8d04c5d 100644 --- a/MidrandBookshop/Components/Layout/MainLayout.razor.css +++ b/MidrandBookshop/Components/Layout/MainLayout.razor.css @@ -1,98 +1,84 @@ -.page { - position: relative; - display: flex; - flex-direction: column; +/* --- Midrand Books Sliding Cart Extensions --- */ + +/* Dimmed backdrop background blur styling */ +.cart-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.25); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + z-index: 1040; + opacity: 0; + pointer-events: none; + transition: opacity 0.35s cubic-bezier(0.16, 1, 0.3, 1); } -main { - flex: 1; + .cart-overlay.is-visible { + opacity: 1; + pointer-events: auto; + } + +/* Slide-out Sidebar Panel layout specification */ +.cart-drawer { + position: fixed; + top: 0; + right: -420px; + width: 100%; + max-width: 400px; + height: 100vh; + z-index: 1050; + pointer-events: none; /* Block layout actions while hidden off-screen */ + transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1); } -.sidebar { - background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%); -} + .cart-drawer.is-open { + transform: translateX(-420px); + pointer-events: auto; /* Allow complete drawer clicks once slid forward */ + } -.top-row { - background-color: #f7f7f7; - border-bottom: 1px solid #d6d5d5; - justify-content: flex-end; - height: 3.5rem; +/* FIXED: Prevent badge rendering from stealing button-down mouse highlights */ +.cart-badge { + position: absolute; + top: 2px; + right: 2px; + background-color: #1A1A1A; + color: #FFF; + font-size: 0.62rem; + font-weight: 700; + border-radius: 50%; + width: 15px; + height: 15px; display: flex; align-items: center; + justify-content: center; + pointer-events: none; } - .top-row ::deep a, .top-row ::deep .btn-link { - white-space: nowrap; - margin-left: 1.5rem; - text-decoration: none; - } - - .top-row ::deep a:hover, .top-row ::deep .btn-link:hover { - text-decoration: underline; - } - - .top-row ::deep a:first-child { - overflow: hidden; - text-overflow: ellipsis; - } - -@media (max-width: 640.98px) { - .top-row { - justify-content: space-between; - } - - .top-row ::deep a, .top-row ::deep .btn-link { - margin-left: 0; - } +/* Micro typography utility sizes */ +.xx-small { + font-size: 0.68rem; } -@media (min-width: 641px) { - .page { - flex-direction: row; - } - - .sidebar { - width: 250px; - height: 100vh; - position: sticky; - top: 0; - } - - .top-row { - position: sticky; - top: 0; - z-index: 1; - } - - .top-row.auth ::deep a:first-child { - flex: 1; - text-align: right; - width: 0; - } - - .top-row, article { - padding-left: 2rem !important; - padding-right: 1.5rem !important; - } +/* Clean dashed divider lines for item items listing styling */ +.border-bottom-dashed { + border-bottom: 1px dashed rgba(0, 0, 0, 0.12); } -#blazor-error-ui { - color-scheme: light only; - background: lightyellow; - bottom: 0; - box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); - box-sizing: border-box; - display: none; - left: 0; - padding: 0.6rem 1.25rem 0.7rem 1.25rem; - position: fixed; - width: 100%; - z-index: 1000; +/* Custom quantity container inline metrics layout structure */ +.quantity-picker { + padding: 2px 4px; } - #blazor-error-ui .dismiss { - cursor: pointer; - position: absolute; - right: 0.75rem; - top: 0.5rem; + .quantity-picker button { + font-size: 0.85rem; + font-weight: 600; + line-height: 1; } + + .quantity-picker button:hover { + background-color: rgba(0,0,0,0.05); + border-radius: 50%; + } diff --git a/MidrandBookshop/Components/Pages/Home.razor b/MidrandBookshop/Components/Pages/Home.razor index a4ad784..f6a7aa4 100644 --- a/MidrandBookshop/Components/Pages/Home.razor +++ b/MidrandBookshop/Components/Pages/Home.razor @@ -1,7 +1,325 @@ @page "/" +@rendermode InteractiveServer -Midrand Books +
+

+ Discover thoughtfully curated
books for every reader. +

+

+ Explorations into books, reading culture, and the art of thoughtful curation from Midrand to the world. +

+
-

midrandbooks.co.za

+
+
-Welcome to the Midrand Books online bookstore! We are passionate about providing a wide selection of books to readers of all ages and interests. Whether you're looking for the latest bestsellers, classic literature, or educational resources, we have something for everyone. +
+
+ @foreach (var category in MainCategories) + { + var catName = category; + + } + + +
+
+ +
+
+ + + +
+ + +
+ +
+
+
+ + @if (ShowExpandedCategories) + { +
+ @foreach (var category in DynamicExtendedCategories) + { + var catName = category; + + } +
+ } + + @if (ShowFilterMenu) + { +
+
+
+

SORT ORDER

+
+ + + + +
+
+
+

PRICE RANGE

+
+ + + + +
+
+
+

RELEASE AVAILABILITY

+
+ + +
+ +
+
+
+ } + +
+
+
+
+
+ +
+ + @if (!PaginatedBooks.Any()) + { +
+

NO PRODUCTS MATCH YOUR TARGET SELECTION SPECIFICATIONS

+
+ } + else if (CurrentViewMode == ViewMode.Grid) + { +
+ @foreach (var book in PaginatedBooks) + { +
+
+
+ @if (book.IsNew) + { + New + } + else + { +
+ } + +
+
+
+ @book.Category.ToUpper()
EDITION +
+
+
+
+
+

@book.Title

+

by @book.Author

+
+ R @book.Price.ToString("N0") +
+
+ } +
+ } + else + { +
+ @foreach (var book in PaginatedBooks) + { +
+
+ @book.Title + by @book.Author + @book.Category.ToUpper() +
+
+ @if (book.IsNew) + { + NEW + } + R @book.Price.ToString("N0") + +
+
+ } +
+ } + + @if (HasMoreItems) + { +
+ +
+ } +
+ +@code { + public enum ViewMode { Grid, List } + private ViewMode CurrentViewMode = ViewMode.Grid; + + [CascadingParameter] + public string SharedSearchQuery { get; set; } = string.Empty; + + private string ActiveCategory = "All"; + private bool ShowExpandedCategories = false; + private bool ShowFilterMenu = false; + + private string SelectedSortOption = "default"; + private string ActivePriceFilter = "all"; + private bool OnlyShowNew = false; + + private List MainCategories = new() { "All", "Graphic Design", "Product Design", "Architecture" }; + private List DynamicExtendedCategories = new(); + + private int ItemsPerPage = 12; + private int VisibleCount = 12; + + public class BookItem + { + public string Title { get; set; } = string.Empty; + public string Author { get; set; } = string.Empty; + public decimal Price { get; set; } + public string Category { get; set; } = string.Empty; + public bool IsNew { get; set; } + public string Isbn { get; set; } = string.Empty; + } + + private List BooksCollection = new(); + + private IEnumerable FilteredData + { + get + { + var data = BooksCollection.AsEnumerable(); + + if (!string.IsNullOrWhiteSpace(SharedSearchQuery)) + { + var q = SharedSearchQuery.Trim(); + data = data.Where(b => + b.Title.Contains(q, StringComparison.OrdinalIgnoreCase) || + b.Author.Contains(q, StringComparison.OrdinalIgnoreCase) || + b.Isbn.Contains(q, StringComparison.OrdinalIgnoreCase) + ); + } + + if (ActiveCategory != "All") + { + data = data.Where(b => b.Category.Equals(ActiveCategory, StringComparison.OrdinalIgnoreCase)); + } + + if (OnlyShowNew) { data = data.Where(b => b.IsNew); } + + data = ActivePriceFilter switch + { + "under-500" => data.Where(b => b.Price < 500), + "500-1000" => data.Where(b => b.Price >= 500 && b.Price <= 1000), + "over-1000" => data.Where(b => b.Price > 1000), + _ => data + }; + + return data; + } + } + + private IEnumerable SortedAndFilteredBooks => SelectedSortOption switch + { + "price-low" => FilteredData.OrderBy(b => b.Price), + "price-high" => FilteredData.OrderByDescending(b => b.Price), + "title-asc" => FilteredData.OrderBy(b => b.Title), + _ => FilteredData + }; + + private IEnumerable PaginatedBooks => SortedAndFilteredBooks.Take(VisibleCount); + private int TotalFilteredCount => FilteredData.Count(); + private bool HasMoreItems => VisibleCount < TotalFilteredCount; + + protected override void OnInitialized() + { + var extraSourceCategories = new[] { "Fine Arts", "Science", "Photography", "Typography", "Interior Design", "Industrialism", "Fashion", "Curation Studies" }; + DynamicExtendedCategories.AddRange(extraSourceCategories); + + BooksCollection.Add(new BookItem { Title = "Letters from M/M (Paris)", Author = "M/M Paris", Price = 720, Category = "Graphic Design", IsNew = true, Isbn = "9782915173" }); + BooksCollection.Add(new BookItem { Title = "Daan Paans: Floating Signifiers", Author = "Daan Paans", Price = 540, Category = "Product Design", IsNew = true, Isbn = "9789492051" }); + BooksCollection.Add(new BookItem { Title = "Album Architectures, Maputo", Author = "Guedes Archive", Price = 350, Category = "Architecture", IsNew = true, Isbn = "9780620751" }); + + var designPrefixes = new[] { "Minimalist", "Monolithic", "Architectural", "Japanese", "Scandinavian" }; + var designNouns = new[] { "Structures", "Typologies", "Forms & Spaces", "Systems Matrix", "Graphic Ephemera" }; + var designers = new[] { "J. Morrison", "K. Fujita", "Studio Bouroullec", "Es Devlin", "Kenya Hara" }; + + var entireCategoryPool = MainCategories.Concat(DynamicExtendedCategories).Where(c => c != "All").ToArray(); + var random = new Random(42); + + for (int i = 4; i <= 60; i++) + { + BooksCollection.Add(new BookItem + { + Title = $"{designPrefixes[random.Next(designPrefixes.Length)]} {designNouns[random.Next(designNouns.Length)]} (Vol. {random.Next(1, 4)})", + Author = designers[random.Next(designers.Length)], + Price = random.Next(25, 135) * 10, + Category = entireCategoryPool[random.Next(entireCategoryPool.Length)], + IsNew = random.NextDouble() > 0.7, + Isbn = $"978000000{i}" + }); + } + } + + private void SetViewMode(ViewMode targetMode) => CurrentViewMode = targetMode; + private void SelectCategory(string categoryName) { ActiveCategory = categoryName; VisibleCount = ItemsPerPage; } + private void ToggleExtraCategories() => ShowExpandedCategories = !ShowExpandedCategories; + private void ToggleFilterMenu() => ShowFilterMenu = !ShowFilterMenu; + private void ChangeSort(string sortOption) => SelectedSortOption = sortOption; + private void ChangePriceFilter(string priceBracket) { ActivePriceFilter = priceBracket; VisibleCount = ItemsPerPage; } + private void ToggleNewArrivalsOnly(ChangeEventArgs e) { OnlyShowNew = e.Value is bool b && b; VisibleCount = ItemsPerPage; } + private void ResetFilters() { SelectedSortOption = "default"; ActivePriceFilter = "all"; OnlyShowNew = false; VisibleCount = ItemsPerPage; } + private void LoadNextPage() { if (HasMoreItems) VisibleCount += ItemsPerPage; } +} \ No newline at end of file diff --git a/MidrandBookshop/Components/Pages/Home.razor.css b/MidrandBookshop/Components/Pages/Home.razor.css new file mode 100644 index 0000000..c3c5838 --- /dev/null +++ b/MidrandBookshop/Components/Pages/Home.razor.css @@ -0,0 +1,141 @@ +/* Expanding Search Input Controls */ +.search-input-container { + max-width: 0; + opacity: 0; + overflow: hidden; + transition: max-width 0.35s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.25s ease; +} + + .search-input-container.is-active { + max-width: 280px; + opacity: 1; + } + +.custom-search-field { + font-size: 0.8rem; + height: 32px; + box-shadow: none !important; +} + +/* Structural Layout Typography */ +.branding-logo { + font-size: 1.1rem; + letter-spacing: -0.5px; +} + +.text-hero-wrapper { + margin-top: 5rem; + margin-bottom: 3.5rem; +} + +.master-headline { + letter-spacing: -1.5px; + font-weight: 400; + line-height: 1.15; +} + +.sub-headline { + max-width: 520px; + font-size: 0.95rem; + font-weight: 300; + line-height: 1.6; +} + +/* Filtering Dropdown Drawer Settings */ +.filter-dropdown-panel { + border-radius: 12px; +} + +.panel-section-heading { + letter-spacing: 0.5px; + font-size: 0.7rem; +} + +.reset-link-btn { + font-size: 0.7rem; +} + +/* Custom Minimal Separator Rule Layout */ +.custom-milled-line { + height: 35px; + margin-top: 5px; + margin-bottom: 25px; + z-index: 0; + pointer-events: none; +} + +.center-bloom-shadow { + bottom: 15px; + width: 55%; + height: 50px; + background: radial-gradient(ellipse at bottom, rgba(20, 20, 20, 0.07) 0%, rgba(40, 40, 40, 0.02) 60%, rgba(255, 255, 255, 0) 100%); + filter: blur(10px); +} + +.core-horizontal-rule { + bottom: 15px; + height: 1px; + background: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,0.15) 50%, rgba(0,0,0,0) 100%); +} + +/* View Engine Layout Presentation Variants: Grid Cards */ +.book-grid-card { + background-color: #F1F1F1; + border-radius: 12px; + min-height: 380px; +} + +.badge-new-arrival { + background-color: #E63946; + font-size: 0.75rem; +} + +.book-spine-fallback { + width: 130px; + height: 185px; + font-size: 0.65rem; + letter-spacing: 1px; + border-radius: 2px; +} + +.product-card-title { + font-size: 0.95rem; + font-weight: 500; +} + +/* View Engine Layout Presentation Variants: List Rows */ +.list-row-item { + border-bottom: 1px solid rgba(0,0,0,0.06); + transition: background-color 0.2s ease; +} + + .list-row-item:hover { + background-color: rgba(0,0,0,0.01); + } + +.structural-list-left { + flex: 2; +} + +.list-item-title { + font-size: 0.95rem; + min-width: 260px; +} + +.list-item-author { + min-width: 160px; +} + +.list-item-tag { + font-size: 0.65rem; +} + +.list-new-badge { + font-size: 0.65rem; +} + +.list-item-price { + font-size: 0.9rem; + min-width: 80px; + text-align: right; +} diff --git a/MidrandBookshop/wwwroot/app.css b/MidrandBookshop/wwwroot/app.css index 73a69d6..37a58b9 100644 --- a/MidrandBookshop/wwwroot/app.css +++ b/MidrandBookshop/wwwroot/app.css @@ -1,60 +1,135 @@ -html, body { - font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@300;400;500;600&display=swap'); + +:root { + /* Fonts */ + --font-heading: 'Instrument Serif', serif; + --font-ui: 'Inter', sans-serif; + /* Color Palette */ + --mb-bg: #F9F9F9; + --mb-card-bg: #FFFFFF; + --mb-text-dark: #1A1A1A; + --mb-text-muted: #666666; + --mb-accent-red: #E63946; + --mb-radius: 12px; + /* High-Visibility Machined White Metal Core Surface Definition */ + /* Sharp linear reflection channels paired with micro-milled density grids */ + --brushed-metal-bg: linear-gradient(90deg, rgba(255, 255, 255, 1) 0%, rgba(240, 241, 245, 0.95) 25%, rgba(255, 255, 255, 1) 50%, rgba(238, 240, 244, 0.95) 75%, rgba(255, 255, 255, 1) 100% ), repeating-linear-gradient( 0deg, rgba(0, 0, 0, 0.012) 0px, rgba(0, 0, 0, 0.012) 1px, transparent 1px, transparent 2px ), + repeating-linear-gradient( 90deg, rgba(255, 255, 255, 0.9) 0px, rgba(255, 255, 255, 0.9) 1px, transparent 1px, transparent 3px ), #EDEFF4; /* Definitive satin platinum breakout base fallback */ } -a, .btn-link { - color: #006bb7; +/* Global Reset & Core Variables Mapping */ +body { + background-color: var(--mb-bg); + color: var(--mb-text-dark); + font-family: var(--font-ui); + -webkit-font-smoothing: antialiased; } -.btn-primary { - color: #fff; - background-color: #1b6ec2; - border-color: #1861ac; +h1, h2, h3, .display-font { + font-family: var(--font-heading); + font-weight: 400; } -.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { - box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb; +/* Custom Navigation Pill Styling */ +.nav-pill-wrapper { + background-color: #FFFFFF; + border: 1px solid rgba(0, 0, 0, 0.06); + padding: 6px; + border-radius: 50px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.02); } -.content { - padding-top: 1.1rem; +.nav-pill-link { + font-size: 0.9rem; + font-weight: 500; + color: var(--mb-text-muted); + padding: 8px 20px; + border-radius: 50px; + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); + display: inline-flex; + align-items: center; + gap: 8px; + text-decoration: none; } -h1:focus { - outline: none; -} - -.valid.modified:not([type=checkbox]) { - outline: 1px solid #26b050; -} - -.invalid { - outline: 1px solid #e50000; -} - -.validation-message { - color: #e50000; -} - -.blazor-error-boundary { - background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; - padding: 1rem 1rem 1rem 3.7rem; - color: white; -} - - .blazor-error-boundary::after { - content: "An error has occurred." + .nav-pill-link i { + width: 16px; + height: 16px; + stroke-width: 1.5; } -.darker-border-checkbox.form-check-input { - border-color: #929292; + /* Navigation Pill States */ + .nav-pill-link:hover { + color: var(--mb-text-dark); + background-color: #F1F1F1; + } + + .nav-pill-link.active { + color: #FFFFFF !important; + background-color: var(--mb-text-dark); + } + + .nav-pill-link.active svg { + stroke: #FFFFFF; + } + +/* Utilities & Component Support Layouts */ +.cart-badge { + position: absolute; + top: -5px; + right: -5px; + background-color: var(--mb-text-dark); + color: #FFFFFF; + font-size: 0.7rem; + font-weight: 600; + width: 18px; + height: 18px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + border: 2px solid var(--mb-bg); } -.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder { - color: var(--bs-secondary-color); - text-align: end; +.transition-smooth { + transition: color 0.2s ease; } -.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder { - text-align: start; -} \ No newline at end of file + .transition-smooth:hover { + color: var(--mb-text-dark) !important; + } + +.py-1-5 { + padding-top: 0.35rem !important; + padding-bottom: 0.35rem !important; +} + +/* Global Focus Ring Corrections */ +h1:focus, h2:focus, h3:focus, h4:focus, p:focus, div:focus, span:focus { + outline: none !important; + box-shadow: none !important; +} + +[tabindex="-1"]:focus { + outline: none !important; +} + +/* Polished White Metal Footer Enhancements */ +footer .text-muted { + color: #555555 !important; +} + +footer .text-dark { + color: #1A1A1A !important; + font-weight: 600; +} + +footer span.text-muted, +footer a.text-muted { + text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6); + transition: color 0.2s cubic-bezier(0.4, 0, 0.2, 1); +} + + footer a.text-muted:hover { + color: #1A1A1A !important; + }