/*
 * Loading-state styling for real user data (name, email, initials, balance,
 * etc). Every element that will be populated from /api/me starts in this
 * state instead of containing fake placeholder text — so if the populating
 * script is ever slow, blocked, or fails, the person sees a loading
 * indicator, never another person's (fake) name or balance.
 */

[data-user-field] {
    position: relative;
}

[data-user-field].skeleton {
    color: transparent !important;
    background-color: rgba(148, 163, 184, 0.25);
    border-radius: 4px;
    animation: skeleton-pulse 1.4s ease-in-out infinite;
    user-select: none;
}

/* Avatar circles keep their shape while loading, just pulse instead of
   showing placeholder initials. */
div[data-user-field].skeleton {
    border-radius: 9999px;
}

/* Any avatar image waiting on the real user's data is hidden (pulsing)
   by default, on every page, automatically -- no need to manually add a
   class to each individual instance. Revealed only once
   session-init.js removes this marker, which it only does after the
   REAL, corrected image has actually finished loading -- not before.
   This is a dedicated marker rather than keying off onerror content on
   purpose: nothing about matching "is this an avatar" should depend on
   what happens to load in the meantime. */
img[data-avatar-pending]:not([data-avatar-ready]) {
    opacity: 0;
    background-color: rgba(148, 163, 184, 0.25);
    animation: skeleton-pulse 1.4s ease-in-out infinite;
}

/*
 * Nav dark/light toggle icon. dark-mode-init.js sets html.dark
 * synchronously, before any deferred script (including Alpine) runs --
 * that's what already keeps the page background correct from the first
 * paint. This uses that same already-correct signal for the icon too,
 * instead of waiting on Alpine, which is what caused it to render empty
 * (nothing to show yet) or briefly wrong (a guessed default) before.
 * Alpine's x-show on these same elements still runs once it loads, but
 * by then it's just confirming what's already showing, not changing it
 * -- except after an actual click, when it's the only thing driving it.
 *
 * The element type in the selector (i.nav-theme-icon-sun, not just
 * .nav-theme-icon-sun) is deliberate, not decorative: Font Awesome's own
 * base rule is a single class selector, confirmed by checking its actual
 * source. Adding the element type gives these a strictly higher
 * specificity, so they win on their own merits regardless of which
 * stylesheet loads second -- no !important needed.
 */
i.nav-theme-icon-sun { display: none; }
i.nav-theme-icon-moon { display: inline-block; }
html.dark i.nav-theme-icon-sun { display: inline-block; }
html.dark i.nav-theme-icon-moon { display: none; }

[x-cloak] { display: none !important; }

/*
 * Every marketing page already opts into cross-document View Transitions
 * (<meta name="view-transition" content="same-origin"> in each <head>),
 * but that alone only gets the browser's default: the ENTIRE old page
 * (nav included) fades out and the entire new page fades in -- which
 * still reads as "the page just refreshed," just with a fade instead of
 * a hard cut, because nothing tells the browser the nav on both pages is
 * the same element rather than two unrelated boxes.
 *
 * Naming it here does exactly that: the browser matches this element
 * across the two documents by name and gives it its own transition
 * instead of lumping it into the whole-page cross-fade. Killing that
 * transition's animation makes it hold still instead of even morphing --
 * the actual page content still transitions underneath, but the nav and
 * footer never visibly disappear/reappear, so navigating stops feeling
 * like a reload despite genuinely being one. Unsupported browsers (no
 * cross-document View Transitions support yet) just navigate normally,
 * exactly as before -- this is additive, nothing to fall back from.
 */
nav { view-transition-name: site-nav; }
footer { view-transition-name: site-footer; }

::view-transition-old(site-nav),
::view-transition-new(site-nav),
::view-transition-old(site-footer),
::view-transition-new(site-footer) {
    animation: none;
}

@media (prefers-reduced-motion: reduce) {
    ::view-transition-group(*),
    ::view-transition-old(*),
    ::view-transition-new(*) {
        animation: none !important;
    }
}

@keyframes skeleton-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}

/*
 * Marketing-page nav auth state (Login/Open Account vs My Account).
 * Was inlined in every page's <head> for a while, on the reasoning that
 * an external stylesheet blocks first paint -- true, but this app
 * already accepts that exact cost for the identical problem everywhere
 * else (see the skeleton rules above), so keeping this one inline was
 * inconsistent, not actually necessary. One shared file, same as
 * everything else in assets/js/lib/.
 *
 * Exactly one of .ui-guest / .ui-authed is on <html> by the time this
 * page is ever seen -- node-backend/middleware/marketingNavAuth.js
 * decides that server-side and writes the class in before the response
 * leaves the server, so there's no longer a real "neither yet" moment to
 * design a loading state for.
 *
 * What's left is a different problem: #desktopGuestButtons ("Login /
 * Open Account") and #desktopAccountButton ("My Account") are not the
 * same width, and they used to be switched with display:none/block --
 * which removes whichever one is inactive from layout entirely, so the
 * nav's shared justify-between row resized around whichever one was
 * showing. That's what made the links beside it visibly shift sideways
 * depending on login state. Fixed below by stacking both on the same
 * CSS Grid cell (.nav-auth-slot, wrapping them in the nav markup) and
 * switching with visibility instead of display: a visibility:hidden grid
 * child still counts toward the cell's size, so the slot is always as
 * wide as the wider of the two, and nothing beside it ever has to move
 * when the visible one changes.
 *
 * Mobile's version of these two lives inside the hamburger dropdown as a
 * vertical stack, not this shared horizontal row, so it doesn't have the
 * same problem and stays on plain display toggling.
 */
#desktopGuestButtons, #desktopAccountButton { visibility: hidden; }
#mobileGuestButtonsMenu, #mobileAccountButtonMenu { display: none; }

html.ui-guest #desktopGuestButtons { visibility: visible; }
html.ui-guest #mobileGuestButtonsMenu { display: block; }

html.ui-authed #desktopAccountButton { visibility: visible; }
html.ui-authed #mobileAccountButtonMenu { display: block; }

.nav-auth-slot { display: grid; }
.nav-auth-slot > * { grid-area: 1 / 1; }

/*
 * Same guest-only logic for the mobile fixed Login/Register bar
 * (#mobileFixedGuestButtons) that several marketing pages render below
 * the fold -- it has its own default/media-query display rule in each
 * page's own <style> block (mobile-only, position: fixed), but nothing
 * previously hid it once a visitor was actually logged in, so it kept
 * nagging authenticated users to log in or register on every page,
 * under their own already-visible "My Account" nav button.
 */
html.ui-authed #mobileFixedGuestButtons { display: none; }

/*
 * Marketing-page floating support chat: guest visitors get a "log in or
 * message us" gate (#publicChatWidget); signed-in visitors get the real
 * live chat instead (#marketingSupportChatWidget), the same widget the
 * dashboard uses. Same gating mechanism as the nav block above.
 *
 * #marketingSupportChatWidget is a wrapper unique to the marketing pages,
 * not the dashboard's own #supportChatWidget -- the dashboard is always
 * authenticated and never carries the ui-authed class, so gating on
 * #supportChatWidget directly here would hide it there by default.
 */
#publicChatWidget, #marketingSupportChatWidget { display: none; }

html.ui-guest #publicChatWidget { display: flex; }
html.ui-authed #marketingSupportChatWidget { display: block; }

.skeleton-pulse-block {
    display: inline-block;
    background-color: rgba(148, 163, 184, 0.25);
    animation: skeleton-pulse 1.4s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
    .skeleton-pulse-block {
        animation: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    [data-user-field].skeleton {
        animation: none;
    }
}
