Removed vulnerabilities
This commit is contained in:
@@ -1,299 +1,53 @@
|
||||
@using Blazored.Toast
|
||||
@inherits LayoutComponentBase
|
||||
@inject NavigationManager Navigation
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<div class="position-relative vh-100 d-flex flex-column justify-content-between overflow-hidden" style="background-color: #F9F9F9;">
|
||||
<div class="cart-overlay @(IsCartOpen ? "is-visible" : "")" @onclick="ToggleCart"></div>
|
||||
<div class="cart-drawer @(IsCartOpen ? "is-open" : "") d-flex flex-column bg-white shadow-lg">
|
||||
<div class="cart-header d-flex align-items-center justify-content-between p-4 border-bottom">
|
||||
<h5 class="fw-bold m-0 text-dark tracking-tight" style="font-family: 'Inter', sans-serif; font-size: 1.1rem;">
|
||||
YOUR CART (@ShoppingCart.Items.Count())
|
||||
</h5>
|
||||
<button class="btn btn-sm text-dark p-1 border-0" @onclick="ToggleCart" type="button">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="water-background">
|
||||
|
||||
<div class="cart-body flex-grow-1 overflow-y-auto p-4">
|
||||
@if (!ShoppingCart.Items.Any())
|
||||
{
|
||||
<div class="h-100 d-flex flex-column align-items-center justify-content-center text-muted py-5">
|
||||
<svg class="mb-3 opacity-50" width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z M3 6h18 M16 10a4 4 0 0 1-8 0" />
|
||||
<div class="blotch blotch-light-main"></div>
|
||||
<div class="blotch blotch-dark-main"></div>
|
||||
<div class="blotch blotch-accent-1"></div>
|
||||
<div class="blotch blotch-accent-2"></div>
|
||||
|
||||
<div class="noise-overlay"></div>
|
||||
|
||||
<div class="glass-panel">
|
||||
|
||||
<header class="top-bar">
|
||||
<span class="brand-text">midrandbooks.co.za</span>
|
||||
|
||||
<nav class="top-nav-icons">
|
||||
<button class="icon-btn" title="Ebooks" aria-label="Ebooks">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20" />
|
||||
</svg>
|
||||
<span class="small tracking-wide">Your collection is empty.</span>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="d-flex flex-column gap-4">
|
||||
@foreach (var item in ShoppingCart.Items)
|
||||
{
|
||||
<div class="cart-item d-flex gap-3 align-items-start pb-3 border-bottom-dashed">
|
||||
<div class="cart-item-thumb bg-dark text-white-50 d-flex align-items-center justify-content-center px-2 text-center" style="width: 54px; height: 74px; font-size: 0.45rem; letter-spacing: 0.5px;">
|
||||
@if (!string.IsNullOrWhiteSpace(item.Product!.ImageUrl))
|
||||
{
|
||||
<img src="@item.Product!.ImageUrl" class="img-fluid book-shadow" style="max-height: 240px; object-fit: contain;" alt="@item.Product.Name" />
|
||||
}
|
||||
else
|
||||
{
|
||||
@:[COVER]
|
||||
}
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="text-dark small fw-bold mb-0 text-truncate" style="max-width: 180px;">@item.Product!.Name</h6>
|
||||
<p class="text-muted xx-small mb-2">by @($"{item.Author!.Name} {item.Author.LastName}")</p>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="quantity-picker d-flex align-items-center border rounded-pill bg-light">
|
||||
<button class="btn btn-sm py-0 px-2 text-dark border-0" @onclick="() => ChangeQuantity(item, -1)" type="button">-</button>
|
||||
<span class="px-1 text-dark fw-medium" style="font-size: 0.75rem;">@ShoppingCart.Items.FirstOrDefault(i => i.Price!.Id == item.Price!.Id)!.Quantity</span>
|
||||
<button class="btn btn-sm py-0 px-2 text-dark border-0" @onclick="() => ChangeQuantity(item, 1)" type="button">+</button>
|
||||
</div>
|
||||
<span class="small fw-semibold text-dark">R @(item.Price!.Amount * item.Quantity)</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn text-muted p-0 border-0 mt-1 align-self-start" style="background: none;" @onclick="() => RemoveFromCart(item)" type="button">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@if (ShoppingCart.Items.Any())
|
||||
{
|
||||
<div class="cart-footer p-4 bg-light border-top mt-auto">
|
||||
<div class="d-flex align-items-center justify-content-between mb-4">
|
||||
<span class="text-secondary small fw-medium tracking-wider">TOTAL DUE</span>
|
||||
<span class="text-dark h5 fw-bold mb-0">R @GetCartTotal()</span>
|
||||
</div>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<button class="btn btn-dark w-100 rounded-pill py-2.5 fw-medium shadow-sm small tracking-wide" @onclick="RedirectToCheckout" type="button">
|
||||
Proceed to Checkout
|
||||
</button>
|
||||
<button class="btn btn-outline-dark w-100 rounded-pill py-2.5 fw-medium bg-white small tracking-wide" @onclick="RedirectToCart" type="button">
|
||||
Go to Cart Page
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="w-100 position-relative flex-shrink-0" style="z-index: 1020;">
|
||||
<div class="position-absolute top-0 start-0 overflow-hidden d-none d-md-block" style="z-index: -1; pointer-events: none; opacity: 0.08; transform: translate(-10%, -10%);">
|
||||
<svg width="480" height="480" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="100" cy="100" r="98" stroke="#1A1A1A" stroke-width="0.25" stroke-dasharray="0.5 1.5" />
|
||||
<circle cx="100" cy="100" r="92" stroke="#1A1A1A" stroke-width="0.4" />
|
||||
<circle cx="100" cy="100" r="86" stroke="#1A1A1A" stroke-width="0.2" stroke-dasharray="2 1" />
|
||||
<circle cx="100" cy="100" r="80" stroke="#1A1A1A" stroke-width="0.3" />
|
||||
<path d="M100 20 A80 80 0 0 1 180 100 A80 80 0 0 1 100 180 A80 80 0 0 1 20 100 A80 80 0 0 1 100 20" stroke="#1A1A1A" stroke-width="0.15" />
|
||||
<path d="M100 20 A80 80 0 0 0 180 100 A80 80 0 0 0 100 180 A80 80 0 0 0 20 100 A80 80 0 0 0 100 20" stroke="#1A1A1A" stroke-width="0.15" />
|
||||
<line x1="40" y1="100" x2="160" y2="100" stroke="#1A1A1A" stroke-width="0.2" stroke-dasharray="1 4" />
|
||||
<line x1="100" y1="40" x2="100" y2="160" stroke="#1A1A1A" stroke-width="0.2" stroke-dasharray="1 4" />
|
||||
<g transform="translate(65, 82)" stroke="#1A1A1A" stroke-width="0.6" stroke-linecap="round" stroke-linejoin="round" fill="none">
|
||||
<path d="M16 18 C 16 10, 24 10, 24 18" stroke-width="0.5" />
|
||||
<line x1="20" y1="10" x2="20" y2="4" />
|
||||
<path d="M28 18 C 28 14, 34 14, 34 18" stroke-width="0.5" />
|
||||
<line x1="31" y1="14" x2="31" y2="7" />
|
||||
<rect x="3" y="8" width="5" height="10" stroke-width="0.5" />
|
||||
<rect x="11" y="5" width="5" height="13" stroke-width="0.5" />
|
||||
<rect x="38" y="3" width="7" height="15" stroke-width="0.5" />
|
||||
<polygon points="38,3 41.5,0 45,3" stroke-width="0.4" />
|
||||
<rect x="49" y="9" width="6" height="9" stroke-width="0.5" />
|
||||
<line x1="0" y1="18" x2="70" y2="18" stroke-width="0.75" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<nav class="navbar navbar-expand-lg py-3" style="pointer-events: auto;">
|
||||
<div class="container-fluid px-md-5">
|
||||
<a class="navbar-brand d-flex align-items-center" href="/" style="transform: scale(1.6); transform-origin: left center; margin-right: 4rem;">
|
||||
<svg class="me-2" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#1A1A1A" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 17c-2.5 0-4.5-1.8-4.5-4s2-4 4.5-4 4.5 1.8 4.5 4-2 4-4.5 4Z" />
|
||||
<path d="M12 9c2.2 0 4-1.6 4-3.5S14.2 2 12 2 8 3.6 8 5.5 9.8 9 12 9Z" />
|
||||
<path d="M2 12h20" stroke-width="1" stroke-dasharray="1 2" opacity="0.5" />
|
||||
<button class="icon-btn" title="Audiobooks" aria-label="Audiobooks">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 18v-6a9 9 0 0 1 18 0v6" />
|
||||
<path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z" />
|
||||
</svg>
|
||||
<span class="fw-bold tracking-tight text-dark" style="font-size: 0.65rem; letter-spacing: 0.8px; font-family: 'Inter', sans-serif;">MIDRAND BOOKS</span>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse justify-content-center" id="navbarNav">
|
||||
<div class="nav-pill-wrapper d-flex align-items-center">
|
||||
<a class="nav-pill-link active" href="/">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" /><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" /></svg>
|
||||
Books
|
||||
</a>
|
||||
<a class="nav-pill-link" href="/audiobooks">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 18v-6a9 9 0 0 1 18 0v6" /><path d="M21 19a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3zM3 19a2 2 0 0 0 2 2h1a2 2 0 0 0 2-2v-3a2 2 0 0 0-2-2H3z" /></svg>
|
||||
Audiobooks
|
||||
</a>
|
||||
<a class="nav-pill-link" href="/ebooks">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="2" width="14" height="20" rx="2" ry="2" /><line x1="12" y1="18" x2="12.01" y2="18" /></svg>
|
||||
E-Books
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<button class="icon-btn" title="Library" aria-label="Library">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="3" width="7" height="7" />
|
||||
<rect x="14" y="3" width="7" height="7" />
|
||||
<rect x="14" y="14" width="7" height="7" />
|
||||
<rect x="3" y="14" width="7" height="7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="d-flex align-items-center gap-2 position-relative">
|
||||
<div class="search-input-container d-flex align-items-center @(IsSearchActive ? "is-active" : "")">
|
||||
<input type="text"
|
||||
class="form-control form-control-sm rounded-pill border-light-subtle px-3 text-dark bg-light custom-search-field"
|
||||
placeholder="Search by ISBN, Author, Title..."
|
||||
@bind="SearchInputBuffer"
|
||||
@bind:event="oninput"
|
||||
@onkeydown="HandleSearchKeyDown" />
|
||||
</div>
|
||||
<button class="icon-btn" title="My Collection" aria-label="My Collection">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z" />
|
||||
</svg>
|
||||
</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<button class="btn btn-link text-dark p-1 me-2 border-0 header-action-btn"
|
||||
@onclick="ToggleGlobalSearch"
|
||||
type="button"
|
||||
aria-label="Toggle Search">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button class="btn d-flex align-items-center justify-content-center p-2 position-relative border-0"
|
||||
style="background: transparent; color: #1A1A1A; width: 36px; height: 36px;"
|
||||
@onclick="ToggleCart"
|
||||
type="button">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75">
|
||||
<path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z M3 6h18 M16 10a4 4 0 0 1-8 0" />
|
||||
</svg>
|
||||
@if (ShoppingCart.Items.Any())
|
||||
{
|
||||
<span class="cart-badge">@ShoppingCart.Items.Count</span>
|
||||
}
|
||||
</button>
|
||||
|
||||
<div class="dropdown header-account-dropdown">
|
||||
<button class="btn btn-sm d-flex align-items-center gap-1.5 p-1 text-dark border-0 dropdown-toggle no-caret"
|
||||
type="button"
|
||||
id="accountDropdownMenu"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="flex-shrink-0">
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
<span class="d-none d-md-inline font-monospace text-uppercase" style="font-size: 0.8rem; letter-spacing: 0.05em;">Account</span>
|
||||
</button>
|
||||
|
||||
<ul class="dropdown-menu dropdown-menu-end brand-dropdown-pane p-0 m-0" aria-labelledby="accountDropdownMenu">
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<li class="dropdown-header-identity p-3 border-bottom bg-light">
|
||||
<span class="d-block text-muted text-uppercase font-monospace xx-small" style="font-size: 0.6rem; letter-spacing: 0.1em;">Active Identity</span>
|
||||
<span class="fw-bold text-dark text-truncate d-block" style="font-size: 0.85rem;">@(context.User.FindFirst("given_name")?.Value ?? context.User.Identity?.Name ?? "Reader")</span>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item p-2.5 font-monospace text-uppercase text-dark d-flex align-items-center gap-2" href="/account">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
||||
My Orders
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item p-2.5 font-monospace text-uppercase text-dark d-flex align-items-center gap-2" href="https://sts.security.khongisa.co.za/Manage/Index?returnUrl=https://midrandbooks.co.za/account" target="_blank">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>
|
||||
Security Center
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider m-0" /></li>
|
||||
<li>
|
||||
<a class="dropdown-item p-2.5 font-monospace text-uppercase text-danger d-flex align-items-center gap-2" href="javascript:void(0)" @onclick:preventDefault @onclick="HandleLogout">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||||
<polyline points="16 17 21 12 16 7"></polyline>
|
||||
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||
</svg>
|
||||
Logout
|
||||
</a>
|
||||
</li>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<li>
|
||||
<a class="dropdown-item p-2.5 font-monospace text-uppercase text-dark d-flex align-items-center gap-2" href="javascript:void(0)" @onclick:preventDefault @onclick="HandleLogin">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h4"></path><polyline points="10 17 15 12 10 7"></polyline><line x1="15" y1="12" x2="3" y2="12"></line></svg>
|
||||
Sign In
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item p-2.5 font-monospace text-uppercase text-muted d-flex align-items-center gap-2" href="javascript:void(0)" @onclick:preventDefault @onclick="RedirectToAccount">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
|
||||
Order History
|
||||
</a>
|
||||
</li>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="w-100 flex-grow-1 overflow-y-auto d-flex flex-column justify-content-between">
|
||||
<main class="position-relative" style="z-index: 5;">
|
||||
<CascadingValue Value="GlobalSearchQuery">
|
||||
@Body
|
||||
</CascadingValue>
|
||||
<main class="content-area">
|
||||
@Body
|
||||
</main>
|
||||
|
||||
<footer class="custom-site-footer border-top mt-auto">
|
||||
<div class="container-fluid px-md-5">
|
||||
<div class="row g-4 pb-4 justify-content-between footer-content-section">
|
||||
<div class="col-12 col-md-5">
|
||||
<span class="footer-brand-title d-block mb-2">MIDRAND BOOKS</span>
|
||||
<p class="footer-desc mb-3">An architectural destination for curated print, thoughtful reading culture, and global design perspectives.</p>
|
||||
<div class="d-flex flex-column gap-2">
|
||||
<a href="mailto:info@midrandbooks.co.za" class="footer-contact-link">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><rect width="20" height="16" x="2" y="4" rx="2" /><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" /></svg>
|
||||
info@midrandbooks.co.za
|
||||
</a>
|
||||
<a href="tel:+27872650198" class="footer-contact-link">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 .2.81.7A2 2 0 0 1 22 16.92z" /></svg>
|
||||
+27 87 265 9463
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-5 text-start">
|
||||
<div class="row row-cols-2 justify-content-md-end g-3">
|
||||
<div style="max-width: 160px;">
|
||||
<span class="footer-section-heading d-block mb-2">PLATFORM</span>
|
||||
<ul class="list-unstyled d-flex flex-column gap-1.5 m-0">
|
||||
<li><a href="/about" class="footer-nav-link">About Us</a></li>
|
||||
<li><a href="/" class="footer-nav-link">Browse Catalog</a></li>
|
||||
<li><a href="/contact" class="footer-nav-link">Contact Us</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style="max-width: 210px;">
|
||||
<span class="footer-section-heading d-block mb-2">ENTERPRISE</span>
|
||||
<ul class="list-unstyled d-flex flex-column gap-1.5 m-0">
|
||||
<li class="footer-meta-item d-flex align-items-center gap-1.5">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" /></svg>
|
||||
Lite Charms (PTY) Ltd
|
||||
</li>
|
||||
<li><a href="/terms" class="footer-nav-link">Terms & Conditions</a></li>
|
||||
<li><a href="/privacy" class="footer-nav-link">Privacy Policy</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-center pt-3 footer-copyright-section">
|
||||
<div class="col-12 text-center text-md-start">
|
||||
<span class="footer-copyright">
|
||||
© @DateTime.Now.Year Midrand Books. All rights reserved.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<BlazoredToasts />
|
||||
</div>
|
||||
@@ -6,6 +6,7 @@ namespace MidrandBookshop.Components.Layout;
|
||||
public partial class MainLayout(CartService cartService) : IDisposable
|
||||
{
|
||||
[Inject] public IToastService ToastService { get; set; } = default!;
|
||||
[Inject] private NavigationManager Navigation { get; set; } = default!;
|
||||
|
||||
private Cart ShoppingCart => cartService.ShoppingCart;
|
||||
|
||||
@@ -16,13 +17,13 @@ public partial class MainLayout(CartService cartService) : IDisposable
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Navigation.LocationChanged += OnLocationChanged;
|
||||
cartService.OnCartChanged += CartService_OnCartChanged;
|
||||
//Navigation.LocationChanged += OnLocationChanged;
|
||||
//cartService.OnCartChanged += CartService_OnCartChanged;
|
||||
|
||||
if (cartService.ShoppingCart.Items.Count == 0)
|
||||
await cartService.LoadCartFromStorageAsync();
|
||||
//if (cartService.ShoppingCart.Items.Count == 0)
|
||||
// await cartService.LoadCartFromStorageAsync();
|
||||
|
||||
SyncSearchQueryFromUrl();
|
||||
//SyncSearchQueryFromUrl();
|
||||
}
|
||||
|
||||
private async void CartService_OnCartChanged() => await InvokeAsync(StateHasChanged);
|
||||
|
||||
@@ -1,225 +1,199 @@
|
||||
.sticky-top {
|
||||
position: -webkit-sticky !important;
|
||||
position: sticky !important;
|
||||
top: 0 !important;
|
||||
z-index: 1000 !important;
|
||||
}
|
||||
/* MainLayout.razor.css */
|
||||
|
||||
.cart-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
.water-background {
|
||||
height: 100vh;
|
||||
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);
|
||||
}
|
||||
|
||||
.cart-overlay.is-visible {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.cart-drawer {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: -420px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
height: 100vh;
|
||||
z-index: 1050;
|
||||
pointer-events: none;
|
||||
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.cart-drawer.is-open {
|
||||
transform: translateX(-420px);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.xx-small {
|
||||
font-size: 0.68rem;
|
||||
}
|
||||
|
||||
.border-bottom-dashed {
|
||||
border-bottom: 1px dashed rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.quantity-picker {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
.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%;
|
||||
}
|
||||
|
||||
.custom-site-footer {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
background-color: #1A1A1A !important;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||
padding: 3rem 0 2rem 0;
|
||||
background-color: #1e2d4a; /* Base slate */
|
||||
}
|
||||
|
||||
.footer-content-section {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08) !important;
|
||||
}
|
||||
|
||||
.footer-brand-title {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.footer-desc {
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.6;
|
||||
max-width: 360px;
|
||||
color: #A0AEC0 !important;
|
||||
}
|
||||
|
||||
.footer-contact-link {
|
||||
text-decoration: none;
|
||||
font-size: 0.8rem;
|
||||
color: #A0AEC0 !important;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.footer-contact-link:hover {
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.footer-section-heading {
|
||||
font-size: 0.65rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.2px;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.footer-nav-link {
|
||||
text-decoration: none;
|
||||
font-size: 0.8rem;
|
||||
color: #A0AEC0 !important;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.footer-nav-link:hover {
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.footer-meta-item {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.footer-copyright-section {
|
||||
color: #718096 !important;
|
||||
}
|
||||
|
||||
.footer-copyright {
|
||||
font-size: 0.72rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.2px;
|
||||
}
|
||||
|
||||
.btn-back-to-top {
|
||||
/* Base class for all floating organic shapes */
|
||||
.blotch {
|
||||
position: absolute;
|
||||
bottom: 2rem;
|
||||
right: 2rem;
|
||||
width: 42px !important;
|
||||
min-width: 42px !important;
|
||||
max-width: 42px !important;
|
||||
height: 42px !important;
|
||||
border-radius: 50% !important;
|
||||
background-color: #1A1A1A;
|
||||
color: #FFFFFF;
|
||||
border-radius: 50%;
|
||||
filter: blur(80px);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
/* 1. Main Light Blotch - Now anchored Top-Left */
|
||||
.blotch-light-main {
|
||||
width: 60vw;
|
||||
height: 60vh;
|
||||
background: #8892b0;
|
||||
top: -10%;
|
||||
left: -10%;
|
||||
/* Sped up by ~0.9% from 25s to 24.7s */
|
||||
animation: driftLight 24.7s infinite ease-in-out alternate;
|
||||
}
|
||||
|
||||
/* 2. Main Dark Blotch - Now anchored Bottom-Right */
|
||||
.blotch-dark-main {
|
||||
width: 70vw;
|
||||
height: 70vh;
|
||||
background: #0a192f;
|
||||
bottom: -10%;
|
||||
right: -10%;
|
||||
/* Sped up by ~0.9% from 20s to 19.8s */
|
||||
animation: driftDark 19.8s infinite ease-in-out alternate;
|
||||
}
|
||||
|
||||
/* 3. New Accent Blotch - Mid-size, mid-navy */
|
||||
.blotch-accent-1 {
|
||||
width: 40vw;
|
||||
height: 40vh;
|
||||
background: #172a45;
|
||||
top: 30%;
|
||||
right: 20%;
|
||||
animation: driftAccentOne 22s infinite ease-in-out alternate;
|
||||
}
|
||||
|
||||
/* 4. New Accent Blotch - Smaller, lighter shade for depth */
|
||||
.blotch-accent-2 {
|
||||
width: 30vw;
|
||||
height: 35vh;
|
||||
background: #2c3e50;
|
||||
bottom: 20%;
|
||||
left: 20%;
|
||||
animation: driftAccentTwo 28s infinite ease-in-out alternate;
|
||||
}
|
||||
|
||||
/* Denser Pixel Packing (SVG Noise) */
|
||||
.noise-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 1; /* Sits above the blotches, below the glass */
|
||||
opacity: 0.06; /* Keep this low; just enough to texture the gradient */
|
||||
/* An inline SVG generating fractal noise */
|
||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
|
||||
pointer-events: none; /* Ensures clicks pass through to the UI */
|
||||
}
|
||||
|
||||
/* The Glass Layer */
|
||||
.glass-panel {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
box-shadow: inset 0 0 80px rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(30px);
|
||||
-webkit-backdrop-filter: blur(30px);
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.content-area {
|
||||
z-index: 3;
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow-y: hidden;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
/* --- Animations --- */
|
||||
@keyframes driftLight {
|
||||
0% {
|
||||
transform: translate(0, 0) scale(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(10%, 15%) scale(1.15);
|
||||
}
|
||||
/* Pushes down and right */
|
||||
}
|
||||
|
||||
@keyframes driftDark {
|
||||
0% {
|
||||
transform: translate(0, 0) scale(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-15%, -10%) scale(1.1);
|
||||
}
|
||||
/* Pushes up and left */
|
||||
}
|
||||
|
||||
@keyframes driftAccentOne {
|
||||
0% {
|
||||
transform: translate(0, 0) scale(0.9);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(-20%, 20%) scale(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes driftAccentTwo {
|
||||
0% {
|
||||
transform: translate(0, 0) scale(1);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(25%, -15%) scale(0.85);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Floating Top Bar --- */
|
||||
.top-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 60px;
|
||||
padding: 0 2rem;
|
||||
/* The Floating Magic */
|
||||
margin: 1rem 1rem 0 1rem; /* 1.5rem top gap, 2rem left/right gap, 0 bottom gap */
|
||||
border-radius: 10px; /* Smooth, rounded corners for the island effect */
|
||||
/* The Glass Effect */
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.2);
|
||||
/* A subtle frosted edge all the way around to define the shape */
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.brand-text {
|
||||
color: #f8f9fa;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* --- Top Navigation Icons --- */
|
||||
.top-nav-icons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.5rem; /* Even spacing between icons */
|
||||
margin-left: auto; /* Pushes the nav block to the right */
|
||||
}
|
||||
|
||||
/* The transparent button wrappers */
|
||||
.icon-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
/* Set the icon color to a slightly muted off-white */
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
z-index: 1010;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s ease, background-color 0.2s ease;
|
||||
border-radius: 8px; /* For a subtle hover background if needed later */
|
||||
/* Smooth out the hover animations */
|
||||
transition: color 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-back-to-top:hover {
|
||||
background-color: #333333;
|
||||
/* Hover interactions */
|
||||
.icon-btn:hover {
|
||||
/* Brightens to pure white and floats up slightly when moused over */
|
||||
color: #ffffff;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-back-to-top:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.no-caret::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.brand-dropdown-pane {
|
||||
border-radius: 0px !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15) !important;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05) !important;
|
||||
min-width: 190px;
|
||||
z-index: 1050;
|
||||
}
|
||||
|
||||
.brand-dropdown-pane .dropdown-item {
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.02em;
|
||||
transition: background-color 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
|
||||
.brand-dropdown-pane .dropdown-item:hover {
|
||||
background-color: #1A1A1A !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.brand-dropdown-pane .dropdown-item.text-danger:hover {
|
||||
background-color: #DC3545 !important;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
|
||||
.dropdown-header-identity {
|
||||
user-select: none;
|
||||
}
|
||||
/* Ensure the SVG scales neatly */
|
||||
.icon-btn svg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
/* This ensures the stroke/fill matches the .icon-btn color */
|
||||
stroke: currentColor;
|
||||
}
|
||||
@@ -3,56 +3,54 @@
|
||||
<dialog id="components-reconnect-modal" data-nosnippet>
|
||||
<div class="glassine-page-jacket"></div>
|
||||
|
||||
<div class="literary-sync-strip">
|
||||
<div class="strip-container">
|
||||
<div class="reconnect-content">
|
||||
|
||||
<div class="sync-status-indicator">
|
||||
<svg class="literary-helix-loader" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||
<path class="book-base-spine" d="M12 5v14M12 5c-1.5-2-4.5-2-7-2H2v14h3c2.5 0 5.5 0 7 2M12 5c1.5-2 4.5-2 7-2h3v14h-3c-2.5 0-5.5 0-7 2" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path class="flipping-leaf-vector" d="M12 5c-1-1.5-3-2-5-2H3v14h4c2 0 4 .5 5 2" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
<span class="font-monospace text-uppercase small-tracking">Catalog Circuit Sync</span>
|
||||
</div>
|
||||
<svg class="waveform-svg" viewBox="0 0 54 50" width="60" height="60" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect class="bar bar-1" x="2" y="15" width="4" height="20" rx="2" fill="currentColor" />
|
||||
<rect class="bar bar-2" x="14" y="5" width="4" height="40" rx="2" fill="currentColor" />
|
||||
<rect class="bar bar-3" x="26" y="20" width="4" height="10" rx="2" fill="currentColor" />
|
||||
<rect class="bar bar-4" x="38" y="10" width="4" height="30" rx="2" fill="currentColor" />
|
||||
<rect class="bar bar-5" x="50" y="18" width="4" height="14" rx="2" fill="currentColor" />
|
||||
</svg>
|
||||
|
||||
<div class="sync-message-body">
|
||||
<span class="components-reconnect-first-attempt-visible">
|
||||
Connection interrupted. Re-indexing active reading stack...
|
||||
</span>
|
||||
<div class="sync-message-body font-monospace text-uppercase small-tracking">
|
||||
<span class="components-reconnect-first-attempt-visible">
|
||||
Losing signal...
|
||||
</span>
|
||||
|
||||
<span class="components-reconnect-repeated-attempt-visible">
|
||||
Sync delayed. Re-aligning database archives in <span id="components-seconds-to-next-attempt" class="fw-bold font-monospace">0</span>s...
|
||||
</span>
|
||||
<span class="components-reconnect-repeated-attempt-visible">
|
||||
reconnecting in <span id="components-seconds-to-next-attempt" class="fw-bold">0</span>s...
|
||||
</span>
|
||||
|
||||
<span class="components-reconnect-failed-visible text-crimson">
|
||||
Archival path blocked. Automated sync offline.
|
||||
</span>
|
||||
<span class="components-reconnect-failed-visible text-crimson">
|
||||
Playback disconnected.
|
||||
</span>
|
||||
|
||||
<span class="components-pause-visible">
|
||||
Reading layout paused by host environment node.
|
||||
</span>
|
||||
|
||||
<span class="components-resume-failed-visible text-crimson">
|
||||
State alignment broken.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="sync-action-node">
|
||||
<button id="components-reconnect-button" class="btn-strip-action components-reconnect-failed-visible">
|
||||
<span>Retry Sync</span>
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67" />
|
||||
</svg>
|
||||
</button>
|
||||
<button id="components-resume-button" class="btn-strip-action components-pause-visible components-resume-failed-visible">
|
||||
<span>Reload Session</span>
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"></polyline>
|
||||
<polyline points="1 20 1 14 7 14"></polyline>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<span class="components-pause-visible">
|
||||
Reading layout paused.
|
||||
</span>
|
||||
|
||||
<span class="components-resume-failed-visible text-crimson">
|
||||
State alignment broken.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="sync-action-node">
|
||||
<button id="components-reconnect-button" class="btn-glass-action components-reconnect-failed-visible">
|
||||
<span>Retry Sync</span>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67" />
|
||||
</svg>
|
||||
</button>
|
||||
<button id="components-resume-button" class="btn-glass-action components-pause-visible components-resume-failed-visible">
|
||||
<span>Reload Session</span>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"></polyline>
|
||||
<polyline points="1 20 1 14 7 14"></polyline>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</dialog>
|
||||
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
Midrand Books — Glassine Architectural Veil & Ribbon Strip
|
||||
Midrand Books — Disconnect Overlay (Media Player Aesthetic)
|
||||
========================================================================== */
|
||||
|
||||
/* --- Native Dialog Element Layout Overrides --- */
|
||||
@@ -17,165 +17,151 @@
|
||||
background: transparent;
|
||||
z-index: 99999;
|
||||
overflow: hidden;
|
||||
/* Center the content */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
/* Blazor handles visibility, but we handle the fade */
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
/* Remove default browser modal backdrop blockout to allow custom layering below */
|
||||
#components-reconnect-modal::backdrop {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* --- Glassine Page Jacket Layer ---
|
||||
Frosted translucent shield that preserves context visibility while blocking mouse actions
|
||||
*/
|
||||
/* Show the modal smoothly when Blazor attaches state classes */
|
||||
#components-reconnect-modal.components-reconnect-show,
|
||||
#components-reconnect-modal.components-reconnect-failed,
|
||||
#components-reconnect-modal.components-reconnect-rejected {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
#components-reconnect-modal.components-reconnect-hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* --- Dark Frosted Jacket Layer --- */
|
||||
.glassine-page-jacket {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(251, 251, 250, 0.4); /* Premium warm paper tint */
|
||||
backdrop-filter: blur(5px); /* Elegant frosted glass sweep */
|
||||
inset: 0;
|
||||
background-color: rgba(10, 25, 47, 0.4); /* Deep navy tint */
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
cursor: wait;
|
||||
animation: glassFadeIn 0.35s ease-out forwards;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@keyframes glassFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
backdrop-filter: blur(0px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- The Sliding Ribbon Banner --- */
|
||||
.literary-sync-strip {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #FFFFFF;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05), 0 1px 3px rgba(0, 0, 0, 0.02);
|
||||
padding: 1.1rem 2rem;
|
||||
box-sizing: border-box;
|
||||
/* --- Centered Content Wrapper --- */
|
||||
.reconnect-content {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
/* Animation kinematics: smooth physical drop slide */
|
||||
transform: translateY(-100%);
|
||||
animation: stripSlideDown 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
|
||||
@keyframes stripSlideDown {
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Ribbon Layout Matrix Grid --- */
|
||||
.strip-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: center;
|
||||
gap: 2.5rem;
|
||||
}
|
||||
|
||||
/* Left Node Ticker Elements & Animated Vector */
|
||||
.sync-status-indicator {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.85rem;
|
||||
border-right: 1px solid rgba(0, 0, 0, 0.08);
|
||||
padding-right: 2rem;
|
||||
gap: 1.5rem;
|
||||
color: #f8f9fa;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.small-tracking {
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.12em;
|
||||
color: #111111;
|
||||
font-weight: 600;
|
||||
/* --- SVG Waveform Animation --- */
|
||||
.waveform-svg {
|
||||
shape-rendering: geometricPrecision;
|
||||
color: #f8f9fa;
|
||||
}
|
||||
|
||||
/* Animated Book Helix SVG */
|
||||
.literary-helix-loader {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: #111111;
|
||||
.bar {
|
||||
transform-origin: center;
|
||||
animation: wavePulse 1.2s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.flipping-leaf-vector {
|
||||
transform-origin: 12px 12px;
|
||||
animation: svgLeafFlip 1.6s infinite cubic-bezier(0.4, 0, 0.2, 1);
|
||||
.bar-1 {
|
||||
animation-delay: 0.0s;
|
||||
}
|
||||
|
||||
@keyframes svgLeafFlip {
|
||||
.bar-2 {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.bar-3 {
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.bar-4 {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.bar-5 {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
@keyframes wavePulse {
|
||||
0% {
|
||||
transform: scaleX(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scaleX(0);
|
||||
opacity: 0.3;
|
||||
transform: scaleY(0.3);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scaleX(-1);
|
||||
opacity: 0;
|
||||
transform: scaleY(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Center Node Text Content */
|
||||
.sync-message-body {
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
font-size: 0.95rem;
|
||||
color: #333333;
|
||||
font-style: italic;
|
||||
/* Stop the waveform if the connection completely fails */
|
||||
#components-reconnect-modal.components-reconnect-failed .bar,
|
||||
#components-reconnect-modal.components-reconnect-rejected .bar,
|
||||
#components-reconnect-modal.components-reconnect-paused .bar {
|
||||
animation: none;
|
||||
transform: scaleY(0.1);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/* --- Typography --- */
|
||||
.small-tracking {
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.15em;
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.text-crimson {
|
||||
color: #A34843;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
color: #ff6b6b; /* Softened red for dark mode */
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Right Node Fine-Press Button Trigger */
|
||||
.btn-strip-action {
|
||||
background: #111111;
|
||||
color: #FFFFFF;
|
||||
border: 1px solid #111111;
|
||||
padding: 0.45rem 1.25rem;
|
||||
/* --- Glass Buttons --- */
|
||||
.sync-action-node {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.btn-glass-action {
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
color: #f8f9fa;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 0.6rem 1.5rem;
|
||||
font-family: var(--bs-font-monospace);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.08em;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
border-radius: 4px;
|
||||
transition: all 0.2s ease;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-strip-action:hover {
|
||||
background: transparent;
|
||||
color: #111111;
|
||||
.btn-glass-action:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-strip-action svg {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-strip-action:hover svg {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
/* --- Display Mechanics Matrix Controllers --- */
|
||||
/* --- Display Mechanics Matrix Controllers (Preserved) --- */
|
||||
.components-reconnect-first-attempt-visible,
|
||||
.components-reconnect-repeated-attempt-visible,
|
||||
.components-reconnect-failed-visible,
|
||||
@@ -192,27 +178,8 @@
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
#components-reconnect-modal.components-reconnect-failed .btn-strip-action,
|
||||
#components-reconnect-modal.components-reconnect-paused .btn-strip-action,
|
||||
#components-reconnect-modal.components-reconnect-resume-failed .btn-strip-action {
|
||||
#components-reconnect-modal.components-reconnect-failed .btn-glass-action,
|
||||
#components-reconnect-modal.components-reconnect-paused .btn-glass-action,
|
||||
#components-reconnect-modal.components-reconnect-resume-failed .btn-glass-action {
|
||||
display: inline-flex !important;
|
||||
}
|
||||
|
||||
/* Tablet Parameters Response Collapse Matrix */
|
||||
@media (max-width: 768px) {
|
||||
.strip-container {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.65rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sync-status-indicator {
|
||||
border-right: none;
|
||||
padding-right: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sync-action-node {
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -101,20 +101,23 @@ public partial class Home : ComponentBase
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
if (CartService.ShoppingCart.Items.Count == 0)
|
||||
await CartService.LoadCartFromStorageAsync();
|
||||
//if (CartService.ShoppingCart.Items.Count == 0)
|
||||
// await CartService.LoadCartFromStorageAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender == false && HydrationService.CartHydrated == false)
|
||||
{
|
||||
if(!CartService.ShoppingCart.CustomerId.HasValue)
|
||||
await HydrationService.EnsureCustomerExistsAsync(CancellationToken);
|
||||
}
|
||||
//if (firstRender == false && HydrationService.CartHydrated == false)
|
||||
//{
|
||||
// if(!CartService.ShoppingCart.CustomerId.HasValue)
|
||||
// await HydrationService.EnsureCustomerExistsAsync(CancellationToken);
|
||||
//}
|
||||
}
|
||||
|
||||
protected override async Task OnParametersSetAsync() => await LoadCatalogDataAsync();
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
//await LoadCatalogDataAsync();
|
||||
}
|
||||
|
||||
private async Task LoadCatalogDataAsync()
|
||||
{
|
||||
|
||||
@@ -1,177 +1,77 @@
|
||||
/* 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;
|
||||
/* --- Centering Container --- */
|
||||
.home-portal-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.search-input-container.is-active {
|
||||
max-width: 280px;
|
||||
opacity: 1;
|
||||
/* --- Invisible Wrapper --- */
|
||||
.artistic-portal {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
/* Make the whole block interactive */
|
||||
cursor: pointer;
|
||||
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.artistic-portal:hover {
|
||||
transform: translateY(-5px) scale(1.02);
|
||||
}
|
||||
|
||||
.custom-search-field {
|
||||
font-size: 0.8rem;
|
||||
height: 32px;
|
||||
box-shadow: none !important;
|
||||
/* --- The Illustration --- */
|
||||
.portal-illustration {
|
||||
width: 100%;
|
||||
max-width: 450px; /* Scales nicely on larger screens */
|
||||
height: auto;
|
||||
/* Adds a beautiful glass-like shadow behind the vector lines */
|
||||
filter: drop-shadow(0 15px 25px rgba(0, 0, 0, 0.3));
|
||||
transition: filter 0.4s ease;
|
||||
}
|
||||
|
||||
/* Structural Layout Typography */
|
||||
.branding-logo {
|
||||
font-size: 1.1rem;
|
||||
letter-spacing: -0.5px;
|
||||
.artistic-portal:hover .portal-illustration {
|
||||
filter: drop-shadow(0 25px 35px rgba(0, 0, 0, 0.4)) drop-shadow(0 0 15px rgba(255, 255, 255, 0.1));
|
||||
}
|
||||
|
||||
.text-hero-wrapper {
|
||||
margin-top: 5rem;
|
||||
margin-bottom: 3.5rem;
|
||||
/* --- Ambient Animations inside the SVG --- */
|
||||
.float-slow {
|
||||
animation: floatVertical 6s ease-in-out infinite;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.master-headline {
|
||||
letter-spacing: -1.5px;
|
||||
font-weight: 400;
|
||||
line-height: 1.15;
|
||||
.float-medium {
|
||||
animation: floatVertical 4s ease-in-out infinite reverse;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.sub-headline {
|
||||
max-width: 520px;
|
||||
font-size: 0.95rem;
|
||||
@keyframes floatVertical {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-12px);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- The Text --- */
|
||||
.read-text {
|
||||
color: #f8f9fa;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-size: 2rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.6;
|
||||
letter-spacing: 0.35em;
|
||||
margin-left: 0.35em; /* Compensates for the extra tracking on the right side */
|
||||
/* Soft glow */
|
||||
text-shadow: 0 4px 15px rgba(248, 249, 250, 0.2);
|
||||
transition: all 0.4s ease;
|
||||
}
|
||||
|
||||
/* Filtering Dropdown Drawer Settings */
|
||||
.filter-dropdown-panel {
|
||||
border-radius: 12px;
|
||||
.artistic-portal:hover .read-text {
|
||||
color: #ffffff;
|
||||
letter-spacing: 0.4em;
|
||||
text-shadow: 0 4px 20px rgba(248, 249, 250, 0.5);
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
/* Enable native smooth scrolling page-wide */
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* Back to Top Icon Layout Presentation */
|
||||
.back-to-top-btn {
|
||||
position: fixed !important;
|
||||
bottom: 32px;
|
||||
right: 32px;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 50% !important;
|
||||
text-decoration: none !important;
|
||||
/* Dual-tone contrast visibility profile */
|
||||
background-color: #ffffff !important;
|
||||
color: #1a1a1a !important;
|
||||
border: 2px solid #1a1a1a !important;
|
||||
box-shadow: 0 0 0 2px #ffffff, 0 4px 12px rgba(0, 0, 0, 0.15) !important;
|
||||
z-index: 2147483647 !important;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.back-to-top-btn:hover {
|
||||
transform: translateY(-4px);
|
||||
background-color: #1a1a1a !important;
|
||||
color: #ffffff !important;
|
||||
border-color: #ffffff !important;
|
||||
box-shadow: 0 0 0 2px #1a1a1a, 0 6px 16px rgba(0, 0, 0, 0.25) !important;
|
||||
}
|
||||
|
||||
/* Direct the smooth scroll action straight to the layout viewport boundary */
|
||||
#top-target {
|
||||
scroll-margin-top: 100vh;
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="KubernetesClient" Version="19.0.2" />
|
||||
<PackageReference Include="LiteCharms.Features" Version="1.137.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -25,6 +26,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ANM.Blazored.Toast" Version="0.1.1" />
|
||||
<PackageReference Include="LiteCharms.Features.MidrandBooks" Version="1.137.0" />
|
||||
<PackageReference Include="Microsoft.OpenApi" Version="2.11.0" />
|
||||
|
||||
<!-- Global Usings -->
|
||||
<Using Include="Blazored.Toast.Services" />
|
||||
|
||||
@@ -1,224 +1,14 @@
|
||||
@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 */
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
h1, h2, h3, .display-font {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.nav-pill-link i {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
.transition-smooth {
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Global Toast Notification Framework Extensions
|
||||
========================================================================== */
|
||||
|
||||
.blazored-toast-container {
|
||||
position: fixed;
|
||||
/* 🛠️ Shift anchors from top-right to bottom-left */
|
||||
bottom: 24px;
|
||||
left: 24px;
|
||||
top: auto;
|
||||
right: auto;
|
||||
z-index: 2000 !important;
|
||||
display: flex;
|
||||
flex-direction: column-reverse; /* 💡 Newest toasts will now stack cleanly on top of old ones */
|
||||
gap: 12px;
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.blazored-toast {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 20px;
|
||||
border-radius: var(--mb-radius);
|
||||
background-color: var(--mb-card-bg);
|
||||
color: var(--mb-text-dark);
|
||||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.04);
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
font-family: var(--font-ui);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
animation: toastFadeIn 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.125) forwards;
|
||||
}
|
||||
|
||||
/* Success Toast Core Variants */
|
||||
.blazored-toast-success {
|
||||
border-left: 4px solid var(--mb-text-dark);
|
||||
}
|
||||
|
||||
/* Error Toast Core Variants */
|
||||
.blazored-toast-error {
|
||||
border-left: 4px solid var(--mb-accent-red);
|
||||
color: var(--mb-accent-red);
|
||||
}
|
||||
|
||||
.blazored-toast-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Entry Transition Keyframes */
|
||||
@keyframes toastFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-12px) scale(0.96);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
/* 🛠️ Micro-interactions for the header icon placement */
|
||||
.btn-cart-icon:hover {
|
||||
transform: scale(1.08);
|
||||
background-color: var(--mb-text-dark) !important;
|
||||
}
|
||||
|
||||
.btn-cart-icon:hover svg {
|
||||
stroke: #FFFFFF !important;
|
||||
}
|
||||
|
||||
@keyframes toastFadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-24px) scale(0.95); /* Slide rightward into view */
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.blazored-toast button.blazored-toast-close,
|
||||
.blazored-toast-close-icon {
|
||||
display: none !important;
|
||||
visibility: hidden !important;
|
||||
opacity: 0 !important;
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
Reference in New Issue
Block a user