﻿/* =========================================================
PROJECT: Walanda platform
FILE: layout.css
PATH: /src/styles/layout.css
MODULE: ui / design system
SCOPE: styling (global / component / page)
PURPOSE: controls shared header, footer, grid, and utility layout styles
AUTHOR: Simasiku Nasilele
STANDARD: Walanda pro ui system
VERSION: 1.0.0
LAST UPDATED: 2026
========================================================= */
@import url("./variables.css");

/* =========================================================
SECTION: variables
DESCRIPTION: root variables and theme tokens
========================================================= */


/* =========================================================
SECTION: reset
DESCRIPTION: base resets and normalization
========================================================= */


/* =========================================================
SECTION: layout
DESCRIPTION: global layout structure
========================================================= */

.grid {
    display: grid;
    gap: 24px;
}

.grid-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.grid-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.grid-4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}


/* =========================================================
SECTION: components
DESCRIPTION: reusable ui components (cards, buttons)
========================================================= */

.topnav {
    /* Sticky AND the anchor for the absolutely-centred .nav-links —
       so the browse cluster sits dead-centre of the viewport
       regardless of how wide the logo or the auth zone grow. */
    position: sticky;
    top: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 18px 60px;
    background: var(--card);
    box-shadow: var(--shadow-sm);
    transition: padding var(--transition-fast),
                box-shadow var(--transition-fast),
                background var(--transition-fast);
}

/* Sticky-on-scroll evolution. Toggled by bindScrollState in
   layout.js when window.scrollY > 12. Tightens the bar, deepens
   the shadow, adds a subtle backdrop blur — premium retail
   pattern, not generic SaaS flat. */
.topnav.is-scrolled {
    padding-block: 12px;
    box-shadow: var(--shadow-md);
    background: color-mix(in srgb, var(--card) 92%, transparent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

.logo img {
    height: 50px;
    transition: transform var(--transition-fast);
}
.logo:hover img { transform: scale(1.03); }

/* Navbar zones (desktop):
     [Logo]                                    [browse links][auth]
       ↑                                                 ↑
   flush-left, normal flow                    flush-right, single
                                              flex cluster — both
                                              .nav-links and
                                              .nav-account-zone now
                                              flow in #nav-menu's
                                              justify-content:flex-end
                                              container so the right
                                              side reads as one
                                              cohesive zone. Mobile
                                              overrides below restore
                                              static stacking for the
                                              slide-out drawer. */
#nav-menu {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-md);
}

.nav-links {
    display: inline-flex;
    align-items: center;
    /* Fluid gap: scales with viewport width. Floor at --space-md
       (16px) for narrow desktops so links don't collide; ceiling
       at --space-lg (32px) on big monitors — the cluster now sits
       on the right next to auth, so it doesn't need the dramatic
       gap the centred layout used to. */
    gap: clamp(var(--space-md), 1.5vw, var(--space-lg));
}

.nav-account-zone {
    display: inline-flex;
    align-items: center;
    gap: var(--space-sm);
    padding-left: var(--space-md);
    border-left: 1px solid var(--border);
}

/* Auth chip — sits next to the browse links and matches their
   vertical rhythm. Default state is an outlined neutral chip; the
   active state (set by setActiveNav when data-nav matches the
   current page's data-page) flips to filled-primary + white text so
   the buyer can see at a glance which destination corresponds to
   the page they're already on.
   Scoped under #nav-menu so the rules out-specificity the generic
   `#nav-menu a { color: var(--text-dark); }` rule above — without
   that, the active chip's `color: #fff` would lose the cascade and
   render the label in dark text on a green background. */
#nav-menu .nav-auth-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    justify-content: center;
    padding: 5px 14px;
    min-height: 28px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: transparent;
    color: var(--text-dark);
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
    transition: var(--transition-fast);
}

#nav-menu .nav-auth-btn:hover {
    border-color: var(--primary);
    color: var(--primary);
}

#nav-menu .nav-auth-btn.active,
#nav-menu .nav-auth-btn.active:hover {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}

/* Login is the primary CTA in the guest header — filled green by
   default with white text. Register stays as the outlined chip beside
   it so the visual hierarchy reads: take action / sign up. */
#nav-menu .nav-auth-btn[data-nav="login"],
#nav-menu .nav-auth-btn[data-nav="login"]:hover,
#nav-menu .nav-auth-btn[data-nav="login"].active {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}
#nav-menu .nav-auth-btn[data-nav="login"]:hover {
    filter: brightness(0.95);
}

/* Logout chip — filled green by default with white text, matching
   the Login CTA pattern (same primary fill, same shape, same font)
   so the signed-in auth button has the same visual weight as the
   signed-out one. */
#nav-menu .nav-auth-btn--logout,
#nav-menu .nav-auth-btn--logout:hover,
#nav-menu .nav-auth-btn--logout.active {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
}
#nav-menu .nav-auth-btn--logout:hover {
    filter: brightness(0.95);
}
#nav-menu .nav-auth-btn--logout i {
    font-size: 12px;
}

.nav-auth {
    display: inline-flex;
    align-items: center;
    gap: var(--space-sm);
}

/* Auth zone visibility — driven by html.is-authed / html.is-guest
   set synchronously at the top of layout.js BEFORE the layout
   template injects. Eliminates the FOUC where Login + Register
   would briefly flash next to the avatar dropdown on cold load. */
[data-auth-guest], [data-auth-user] { display: none; }
html.is-guest  [data-auth-guest] { display: inline-flex; }
html.is-authed [data-auth-user]  { display: inline-flex; }

#nav-menu a {
    text-decoration: none;
    color: var(--text-dark);
    font-size: 14px;
    transition: color var(--transition-fast);
}

/* Browse-link hover/active — animated underline that slides in
   from the left. CSS-only via a ::after pseudo with
   transform-origin:left + scaleX(0→1). The active state (set by
   setActiveNav) gets the same underline permanently in --primary,
   so the visual language for "you're here" matches "you can go
   here on hover". */
.nav-links a {
    position: relative;
    padding: 6px 2px;
    font-weight: 500;
    letter-spacing: 0.01em;
}
.nav-links a::after {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: -2px;
    height: 2px;
    background: var(--primary);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--transition-fast);
    border-radius: 2px;
}
.nav-links a:hover,
.nav-links a.active { color: var(--primary); }
.nav-links a:hover::after,
.nav-links a.active::after { transform: scaleX(1); }

.menu-toggle i {
    font-size: 26px;
}

/* Navbar cart icon. Sits between the bookstore link and the auth
   buttons. Visible to everyone (the cart works pre-auth too). The
   live count badge is hydrated by layout.js#bindCartCountBadge. */
#nav-menu .nav-cart-link {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 999px;
    color: var(--text-dark);
    text-decoration: none;
    font-size: 1rem;
    transition: background var(--transition-fast),
                color      var(--transition-fast),
                transform  var(--transition-fast);
}
#nav-menu .nav-cart-link:hover {
    background: var(--background);
    color: var(--primary);
}
#nav-menu .nav-cart-link:active { transform: scale(0.95); }

.nav-cart-count {
    position: absolute;
    top: -2px;
    right: -4px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--status-danger-fg);
    color: var(--card);
    font-size: 10px;
    font-weight: 800;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 0 0 2px var(--card);
    transform-origin: center;
}
.nav-cart-count[hidden] { display: none; }
/* Pulse on count increase — bindCartCountBadge in layout.js
   adds .is-bumping when count grows, then removes it on
   animationend for the next round. */
.nav-cart-count.is-bumping { animation: navCartBump 240ms cubic-bezier(0.34, 1.56, 0.64, 1); }
@keyframes navCartBump {
    0%   { transform: scale(1); }
    50%  { transform: scale(1.3); }
    100% { transform: scale(1); }
}

/* =========================================================
SECTION: profile dropdown (authed user)
DESCRIPTION: Single avatar trigger replacing the old multi-button
             auth row. Initials slot is filled by layout.js#renderAuthState.
             Menu items reuse the brand primary on hover so the
             interaction language matches the rest of the navbar.
========================================================= */

.nav-profile-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px 4px 4px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card);
    cursor: pointer;
    transition: border-color var(--transition-fast),
                background   var(--transition-fast),
                box-shadow   var(--transition-fast),
                transform    var(--transition-fast);
}
.nav-profile-trigger:hover {
    border-color: var(--primary);
    background: color-mix(in srgb, var(--primary) 6%, var(--card));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}
.nav-profile-trigger:focus-visible {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 28%, transparent);
}
.nav-profile-trigger:active { transform: scale(0.97); }
.nav-profile-trigger[aria-expanded="true"] {
    border-color: var(--primary);
    background: color-mix(in srgb, var(--primary) 8%, var(--card));
}

.nav-profile-avatar {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    /* Subtle gradient on the brand colour — premium retail signal
       without straying from the design language. */
    background: linear-gradient(135deg, var(--primary), var(--primary-deep, var(--primary-hover)));
    color: var(--card);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.02em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.nav-profile-caret {
    font-size: 10px;
    color: var(--muted);
    transition: transform var(--transition-fast);
}
.nav-profile-trigger[aria-expanded="true"] .nav-profile-caret {
    transform: rotate(180deg);
}

.nav-profile-menu {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    min-width: 220px;
    padding: 6px;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    z-index: 1100;
    display: flex;
    flex-direction: column;
    gap: 2px;
    /* Animated open: starts off-position + transparent, lands on
       open via .is-open. The hidden attribute still gates visibility
       at the structural level (display:none) so screen readers don't
       see the menu when it's not really there. */
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity var(--transition-fast),
                transform var(--transition-fast);
}
.nav-profile-menu.is-open {
    opacity: 1;
    transform: translateY(0);
}
.nav-profile-menu[hidden] { display: none; }

.nav-profile-menu a,
.nav-profile-menu button {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    border-radius: var(--radius-md);
    background: transparent;
    border: 0;
    color: var(--text-dark);
    text-decoration: none;
    font-size: 0.9rem;
    text-align: left;
    cursor: pointer;
    transition: background var(--transition-fast),
                color      var(--transition-fast);
}
.nav-profile-menu a:hover,
.nav-profile-menu button:hover {
    background: var(--background);
    color: var(--primary);
}
.nav-profile-menu i {
    width: 16px;
    text-align: center;
    color: var(--muted);
}
.nav-profile-menu a:hover i,
.nav-profile-menu button:hover i {
    color: var(--primary);
}
.nav-profile-menu .nav-profile-logout {
    border-top: 1px solid var(--border);
    margin-top: 4px;
    padding-top: 11px;
    color: var(--status-danger-fg);
}
.nav-profile-menu .nav-profile-logout i { color: var(--status-danger-fg); }

/* Position the dropdown anchored to the trigger. */
.nav-auth-user {
    position: relative;
}

/* "Hi, <name>" greeting in the authed nav cluster. Reads as a quiet
   label sat to the left of the Dashboard + Logout chips so the user
   can see which account is signed in without obscuring the CTAs. */
.nav-auth-greeting {
    display: inline-flex;
    align-items: center;
    padding: 0 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-secondary, var(--muted));
    white-space: nowrap;
    max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Dashboard chip in the authed header — outlined neutral by default
   like every other browse link, flips to filled-primary when on
   dashboard.html (setActiveNav matches data-nav="dashboard" against
   <html data-page="dashboard">). */
#nav-menu .nav-auth-btn[data-nav="dashboard"] i {
    font-size: 12px;
}

.menu-toggle {
    display: none;
    background: transparent;
    border: 0;
    cursor: pointer;
}

/* ---------------------------------------------------------------------------
   Position-aware ad zones (shared across ad-supported pages). A page wraps its
   content in .ad-zoned with a top banner, a .ad-zoned__row ([left][main][right])
   and a bottom banner; mountAdZones() fills each zone and toggles has-left /
   has-right so empty side columns collapse and the main content reclaims the
   space. Side zones display only when not [hidden] so an empty zone is removed
   from the grid entirely.
--------------------------------------------------------------------------- */
.ad-zoned {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 24px;
    align-items: start;
    /* Span the screen (not the narrow .container) so the wasted edge
       whitespace becomes the Left/Right ad columns; capped at the platform's
       1920 standard with a small gutter. */
    width: min(100% - 2 * var(--space-md), 1920px);
    margin-inline: auto;
}
.ad-zoned.has-left { grid-template-columns: var(--ad-side, 300px) minmax(0, 1fr); }
.ad-zoned.has-right { grid-template-columns: minmax(0, 1fr) var(--ad-side, 300px); }
.ad-zoned.has-left.has-right { grid-template-columns: var(--ad-side, 300px) minmax(0, 1fr) var(--ad-side, 300px); }
/* Centre column stacks Top ad -> page content -> Bottom ad so all three share
   the exact same width boundaries (the top banner lines up with the content). */
.ad-zoned__center { display: grid; gap: var(--space-md); min-width: 0; }
.ad-zone--left:not([hidden]),
.ad-zone--right:not([hidden]) {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
    /* Flow with the page (NOT sticky): a sticky column taller than the viewport
       pins at the top and buries the ads stacked below the fold. Natural flow
       lets every stacked side ad scroll into view. */
}
.ad-zone--top:not([hidden]),
.ad-zone--bottom:not([hidden]) {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
    /* Fixed banner height so a rotating ad never resizes the box and shifts the
       catalogue/footer below it. The single ad inside fills it; overflow clips. */
    height: var(--ad-banner-h);
    overflow: hidden;
}
.ad-zone--top > .walanda-ad-slot,
.ad-zone--bottom > .walanda-ad-slot { height: 100%; }

/* Stacked sidebar ad cards (built by fillSidebar): a fixed-height container so
   cards keep a uniform height and a rotating swap never resizes the column. The
   inner slot fills it; the image is capped and overflowing copy is clipped. */
.walanda-ad-card {
    height: var(--ad-card-h);
    overflow: hidden;
}
.walanda-ad-card > .walanda-ad-slot {
    height: 100%;
    display: flex;
    flex-direction: column;
}
.walanda-ad-card .walanda-ad-shell {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    padding: 14px;
}
.walanda-ad-card .walanda-sponsored-card {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}
.walanda-ad-card .walanda-sponsored-card img,
.walanda-ad-card .walanda-sponsored-media {
    height: 150px;
    flex-shrink: 0;
}
.walanda-ad-card .walanda-sponsored-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    padding: 12px 14px;
}
.walanda-ad-card .walanda-slot-fallback {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

@media (max-width: 1100px) {
    .ad-zoned,
    .ad-zoned.has-left,
    .ad-zoned.has-right,
    .ad-zoned.has-left.has-right {
        grid-template-columns: 1fr;
        width: min(92%, var(--container-width));
    }
}

/* Each rotation swaps the slot's innerHTML → a fresh .walanda-ad-slot, so this
   animation replays as a gentle cross-fade between cycled ads. */
@keyframes walandaAdFade { from { opacity: 0; } to { opacity: 1; } }

.walanda-ad-slot {
    position: relative;
    background: linear-gradient(180deg, #fff 0%, #fbfdfd 100%);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    animation: walandaAdFade var(--transition-medium) ease both;
}

.walanda-ad-shell {
    padding: 18px;
}

.walanda-sponsored-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 12px;
    color: var(--muted);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.walanda-sponsored-label i {
    color: var(--primary);
    font-size: 10px;
}

.walanda-sponsored-card {
    display: block;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 16px;
    overflow: hidden;
    text-decoration: none;
    color: inherit;
}

.walanda-sponsored-card img {
    width: 100%;
    height: 220px;
    object-fit: cover;
}

.walanda-sponsored-body {
    padding: 18px;
}

.walanda-sponsored-body h3 {
    margin-bottom: 10px;
    font-size: 1.125rem;
}

.walanda-sponsored-body p {
    margin-bottom: 14px;
    color: var(--muted);
    line-height: 1.7;
}

.walanda-sponsored-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 14px;
}

.walanda-sponsored-meta-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 10px;
    border-radius: 999px;
    background: rgba(8, 31, 56, 0.05);
    color: var(--text);
    font-size: 12px;
    line-height: 1.4;
}

.walanda-sponsored-meta-chip strong {
    font-weight: 700;
}

.walanda-slot-fallback {
    display: block;
    border: 1px dashed #d6e4e2;
    border-radius: 14px;
    background: #fff;
    padding: 22px 20px;
    text-align: center;
    /* It's now an <a> (advertise CTA) — render as the card, not a text link. */
    text-decoration: none;
    color: inherit;
    cursor: pointer;
}

.walanda-slot-fallback h4 {
    margin-bottom: 8px;
}

.walanda-slot-fallback p {
    margin-bottom: 14px;
    color: var(--muted);
    line-height: 1.6;
}

.walanda-sponsored-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 10px 16px;
    border-radius: 999px;
    background: rgba(31, 167, 138, 0.12);
    color: var(--primary);
    font-size: 13px;
    font-weight: 700;
}

/* =========================================================
SECTION: pages
DESCRIPTION: page-specific styles
========================================================= */


/* =========================================================
SECTION: responsive
DESCRIPTION: media queries and breakpoints
========================================================= */

@media (max-width: 992px) {
    .topnav {
        padding: 15px 30px;
    }
    .topnav.is-scrolled { padding-block: 10px; }

    .menu-toggle {
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Mobile slide-out panel. Drops the desktop grid in favour of a
       single flex column so the slide-out drawer doesn't try to
       balance three columns inside a 280px width. Stays mounted in
       the DOM but offset off-screen + transparent until .active is
       added by initMenu(). */
    #nav-menu {
        position: absolute;
        top: 80px;
        right: 0;
        display: flex;
        grid-template-columns: none;
        flex-direction: column;
        align-items: stretch;
        background: var(--card);
        padding: 25px;
        box-shadow: var(--shadow-md);
        width: 280px;
        border-radius: var(--radius-md);
        gap: var(--space-sm);
        opacity: 0;
        transform: translateX(8px);
        pointer-events: none;
        transition: opacity var(--transition-fast),
                    transform var(--transition-fast);
    }

    /* Mobile: undo desktop's absolute centering so the links flow
       inside the drawer column. */
    .nav-links {
        position: static;
        transform: none;
        left: auto;
        top: auto;
    }

    .nav-links,
    .nav-account-zone {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        justify-self: stretch;
        border-left: 0;
        padding-left: 0;
        gap: var(--space-sm);
        grid-column: auto;
    }

    .nav-auth {
        flex-direction: column;
        align-items: stretch;
    }

    #nav-menu a {
        display: block;
        margin: 0;
    }

    /* The avatar dropdown's absolute positioning would land it
       off-screen inside a narrow drawer. Anchor it to flow with
       the column on mobile. */
    .nav-profile-menu {
        position: static;
        min-width: 0;
        margin-top: var(--space-sm);
    }

    #nav-menu.active {
        opacity: 1;
        transform: translateX(0);
        pointer-events: auto;
    }
}

@media (max-width: 768px) {
    .grid-2,
    .grid-3,
    .grid-4 {
        grid-template-columns: 1fr;
    }
}
/* =========================================================
END OF FILE
FILE: layout.css
PATH: /src/styles/layout.css
MODULE: ui / design system
STATUS: stable
MAINTAINED BY: Walanda core system
COPYRIGHT: © 2026 Walanda Inc. Ltd. All rights reserved.
========================================================= */


