/*
 * Styles for the Valorous Dashboard
 *
 * The design is inspired by Apple’s Liquid Glass aesthetic, a
 * translucent material that reflects and refracts its surroundings.
 * Cards, navigation and overlays use a semi‑transparent backdrop
 * combined with subtle borders and drop shadows to create depth.  A
 * colorful gradient background sets the stage for the glass panels
 * without distracting from the content.
 */

:root {
    /* After Dark palette inspired by iOS 26: deep blacks and ultraviolet hues */
    /*
     * Palette derived from the external Authlib console theme.  These values
     * increase the opacity of glass elements and adjust borders/shadows to
     * create a more pronounced frosted effect.  Copying these variables
     * ensures the dashboard and console share a unified look and feel.
     */
    /*
     * Overhauled colour palette for a liquid/frosted glass forum style.
     * Deep purples and violets blend into a radiant gradient that evokes
     * Minecraft/CS:GO UI aesthetics.  Bright accent colours highlight
     * interactive elements and ensure good contrast.
     */
    --bg-gradient-start: #020306;
    --bg-gradient-end: #310061;
    --accent-color: #a36be2;
    --text-color: #eae6f9;
    /* Frosted glass panels: high opacity to ensure readability on a dark
       backdrop.  A richer shadow adds depth while subtle borders
       delineate panels from the background. */
    --glass-bg: rgba(30, 0, 50, 0.4);
    --glass-border: rgba(200, 150, 255, 0.3);
    --glass-shadow: rgba(0, 0, 0, 0.7);
    /* Slower transitions produce smoother, more cinematic animations. */
    --transition-speed: 0.5s;
    /* Audio reactivity level.  Updated by JavaScript based on music amplitude. */
    --audio-level: 0;

    /* Low frequency amplitude for bass‑reactive glow walls.  This value is
       updated by the audio analyser in main.js.  It ranges from 0 to 1
       and should be used sparingly as it can spike quickly with bass hits. */
    --bass-level: 0;

    /* Define primary and secondary text colours for convenience.  Some
       components (e.g. location and user cards) reference these
       variables.  If not defined they fallback to inherit, leading to
       unintended colours. */
    --text-primary: var(--text-color);
    --text-secondary: rgba(234, 230, 249, 0.7);

    /* Base brightness of the background.  This variable is animated in
       effects.js to gently oscillate between darker and lighter values,
       giving the backdrop a living, ambient feel reminiscent of Windows 11.
       It combines with --audio-level in the filter below to react to
       music peaks without overpowering the subtle breathing motion. */
    --ambient-brightness: 0.8;

    /* Additional theme variables imported from the external console CSS. */
    --dropdown-bg: rgba(30, 0, 50, 0.8);
    --status-connected: #4caf50;
    --status-error: #e74c3c;
    --status-working: #f1c40f;
    --log-animation-duration: 0.4s;

    /* Hover variant of the accent colour.  This lighter hue is used on
       buttons and interactive elements when hovered.  Adjust as
       necessary to complement the primary accent colour. */
    --accent-color-hover: #c49df8;
}

html, body {
    margin: 0;
    padding: 0;
    font-family: 'Inter', sans-serif;
    color: var(--text-color);
    height: 100%;
    overflow: hidden;
}

#background {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    /*
     * Composite Windows 11‑style backdrop consisting of several radial gradients
     * layered over a shifting linear gradient. The radial gradients create
     * soft ambient blobs reminiscent of the Task Manager/File Explorer
     * background, while the base gradient animates slowly to add life. The
     * background-position variables (--bg-parallax-x/y) nudge the entire
     * backdrop for subtle parallax without revealing edges.
     */
    background:
        /* Custom ambient blobs tinted to match the new colour palette. */
        radial-gradient(circle at 25% 25%, rgba(100, 50, 160, 0.45), transparent 70%),
        radial-gradient(circle at 75% 70%, rgba(140, 70, 220, 0.35), transparent 70%),
        radial-gradient(circle at 85% 15%, rgba(80, 20, 180, 0.25), transparent 70%),
        /* Base gradient uses the variables defined in :root for easy theming. */
        linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%);
    /* Use a large background-size so the moving linear gradient has space to
       animate across the screen; radial layers default to cover */
    background-size: 400% 400%, 400% 400%, 400% 400%, 400% 400%;
    /* Parallax offsets applied via CSS variables set in effects.js */
    background-position: calc(50% + var(--bg-parallax-x, 0px)) calc(50% + var(--bg-parallax-y, 0px));
    animation: gradientShift 20s ease infinite;
    z-index: -2;

    /* React to audio by brightening/dimming the background as the music plays
       and combine with a slowly oscillating ambient brightness.  The
       --ambient-brightness variable is updated in effects.js via a
       requestAnimationFrame loop, creating a subtle breathing effect. */
    filter: brightness(calc(var(--ambient-brightness) + var(--audio-level) * 0.6));
}

/*
 * Glow walls: soft, blurred bands at the top and bottom of the page that
 * react to bass frequencies.  They are positioned behind the UI but above
 * the gradient background and are driven by the --bass-level variable.  A
 * stronger bass level increases the opacity of the glow, making it pulse
 * with the beat.  The blur radius and gradient create a diffused, neon
 * effect reminiscent of a glow spilling onto the glass panels.
 */
.glow-wall {
    position: fixed;
    left: 0;
    width: 100%;
    height: 22vh;
    pointer-events: none;
    z-index: -1;
    /* Use the accent colour for the glow but fade to transparent towards
       the centre.  The radial gradient produces a smooth falloff. */
    background: radial-gradient(ellipse at center, rgba(163, 107, 226, 0.7), rgba(163, 107, 226, 0.0) 70%);
    /* Blur softens the edges, making the glow appear as a diffused band */
    filter: blur(40px);
    /* The opacity is controlled by the bass level.  Clamp ensures it never
       exceeds 1; values beyond 0.5 create a strong glow during heavy bass. */
    opacity: calc(min(var(--bass-level) * 2, 1));
    transition: opacity 0.15s ease;
}

#top-glow {
    top: 0;
    transform: translateY(-40%);
}

#bottom-glow {
    /* Hide the bottom glow wall.  Only the top glow remains active. */
    display: none;
}

/*
 * Full-screen effects canvas used by the ambient particle system. It is fixed
 * positioned and sits just above the gradient background and below the rest
 * of the UI. Pointer events are disabled so it never intercepts clicks.
 */
#effects-canvas {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: -1;
    pointer-events: none;
}

@keyframes gradientShift {
    0% { background-position: 0% 50%; }
    50% { background-position: 100% 50%; }
    100% { background-position: 0% 50%; }
}

.centered {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

.login-logo {
  display: block;
  margin: 0 auto 16px;   /* center + space below */
  max-width: 120px;      /* or whatever fits */
  height: auto;
  /* if you want a bit of a glow under audio reactivity, you could mirror the glass-card shadow: */
  /* box-shadow: 0 0 10px rgba(163, 107, 226, calc(var(--audio-level) * 0.6)); */
}

/* Remember me checkbox styling */
.remember-option {
    margin: 8px 0;
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 14px;
}
.remember-option label {
    cursor: pointer;
}

/* Verification screen styles */
#verify-screen .login-card {
    text-align: center;
}
#verify-screen input {
    margin-bottom: 12px;
}

/* 1. Frosted-glass overlay */
#frosted-overlay {
  position: fixed; top:0; left:0; width:100%; height:100%;
  pointer-events: none;
  /* blur everything behind this element */
  backdrop-filter: blur(20px) saturate(1.2);
  -webkit-backdrop-filter: blur(20px) saturate(1.2);
  background: rgba(255,255,255,0.03); /* subtle whitish tint */
  z-index: -1; /* sits above #background (−2) but below effects-canvas & UI */
  animation: frostedPulse 15s ease-in-out infinite;
}

@keyframes frostedPulse {
  0%,100% { backdrop-filter: blur(18px) saturate(1.1); }
  50%    { backdrop-filter: blur(24px) saturate(1.3); }
}

/* 2. Logo watermark (hidden until login) */
#logo-watermark {
  position: fixed; top:50%; left:50%;
  transform: translate(-50%,-50%);
  width: 80vmin; height: 80vmin;
  background: url('Valor_Varient2_Blank.png') no-repeat center/contain;
  opacity: 0; /* start invisible */
  filter: blur(2px);
  pointer-events: none;
  z-index: -1;
  transition: opacity 1s ease;
  animation: watermarkPulse 20s ease-in-out infinite;
}

#logo-watermark.visible {
  opacity: 0.04; /* very faint imprint */
}

@keyframes watermarkPulse {
  0%,100% { transform: translate(-50%,-50%) scale(1); }
  50%     { transform: translate(-50%,-50%) scale(1.02); }
}

/*
 * Hide an element completely from the layout.  Using only `visibility: hidden` and
 * `opacity: 0` leaves the element in the document flow, which can cause full‑screen
 * containers like the login panel to continue taking up space and push other
 * content offscreen.  Adding `display: none` collapses the element entirely.
 */
.hidden {
    display: none;
    visibility: hidden;
    opacity: 0;
    pointer-events: none;
}

/* Glass card base styling */
.glass-card {
    /*
     * Adopt the frosted glass styling from the live console theme.  The
     * increased blur and saturation create a more convincing glass pane,
     * while a softer border and deeper shadow enhance depth.  The neon
     * glow tied to the audio level remains for reactive lighting.  Cards
     * smoothly animate between states using the unified transition speed.
     */
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 16px;
    padding: 20px;
    /* Blend multiple shadows: a base blur for depth and an audio‑reactive
       highlight around the edges. */
    box-shadow: 0 8px 24px var(--glass-shadow), 0 0 20px 4px rgba(163, 107, 226, calc(var(--audio-level) * 0.8));
    backdrop-filter: blur(18px) saturate(1.2);
    -webkit-backdrop-filter: blur(18px) saturate(1.2);
    transition: var(--transition-speed), box-shadow var(--transition-speed), border var(--transition-speed);
}

.glass-card:hover {
    /* Intensify the hover effect with a slightly brighter background and
       deeper shadow to accentuate depth. */
    background: rgba(50, 0, 70, 0.55);
    box-shadow: 0 12px 48px var(--glass-shadow);
}

/* Hero card: large introductory card on the dashboard.  Provides a welcome
   message and uses more generous padding, center alignment and deeper
   shadows.  It inherits glass-card styling but adds its own enhancements. */
.hero-card {
    margin-bottom: 24px;
    text-align: center;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 20px;
    padding: 32px 24px;
    box-shadow: 0 8px 32px var(--glass-shadow), 0 0 30px 6px rgba(163, 107, 226, calc(var(--audio-level) * 0.8));
    backdrop-filter: blur(20px) saturate(1.2);
    -webkit-backdrop-filter: blur(20px) saturate(1.2);
    transition: var(--transition-speed), box-shadow var(--transition-speed);
}
.hero-card h2 {
    margin-top: 0;
    margin-bottom: 12px;
    font-size: 2rem;
}
.hero-card p {
    margin: 0;
    font-size: 1.1rem;
    color: var(--text-secondary);
}

/* Login card specific styling */
.login-card {
    width: 360px;
    text-align: center;
}

.app-title {
    font-size: 2rem;
    margin-bottom: 10px;
}

.subtitle {
    margin-bottom: 20px;
    color: rgba(255, 255, 255, 0.8);
}

input[type="text"], input[type="password"], input[type="number"] {
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    padding: 12px 14px;
    margin-bottom: 14px;
    border: 1px solid var(--glass-border);
    border-radius: 12px;
    background: rgba(255, 255, 255, 0.08);
    color: var(--text-color);
    outline: none;
    font-size: 1rem;
    transition: border var(--transition-speed), background var(--transition-speed);
}

input[type="text"]:focus {
    border-color: var(--accent-color);
    background: rgba(255, 255, 255, 0.12);
}

/*
 * Base styling for all buttons across the site.  Buttons now follow the
 * frosted‑glass aesthetic rather than a solid accent colour.  They use a
 * semi‑transparent background combined with a subtle border and blur to
 * blend into the glass panels.  The accent colour still shines through
 * via opacity, providing continuity with the rest of the UI.  Hovering
 * slightly brightens the background and lifts the button.
 */
button {
    background: rgba(163, 107, 226, 0.25);
    color: var(--text-color);
    border: 1px solid var(--glass-border);
    padding: 10px 20px;
    border-radius: 12px;
    cursor: pointer;
    font-weight: 600;
    font-size: 1rem;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    box-shadow: 0 2px 8px var(--glass-shadow), 0 0 6px 1px rgba(163, 107, 226, calc(var(--audio-level) * 0.5));
    transition: background var(--transition-speed), transform var(--transition-speed), box-shadow var(--transition-speed);
    outline: none;
}

/* Smooth transitions for pages */
.page {
    /* Disable slide and fade transitions between pages.  Pages are either visible or hidden via the
       .hidden class. */
    opacity: 1;
    transform: none;
    transition: none;
}

/* Enhanced card animations */
.stat-card, .avatar-card {
    transition: transform var(--transition-speed) ease, background var(--transition-speed) ease, box-shadow var(--transition-speed) ease;
}
.stat-card:hover, .avatar-card:hover {
    transform: translateY(-4px) scale(1.02);
}

/* Modal fade in/out */
.modal {
    animation: fadeInModal var(--transition-speed) ease forwards;
}
@keyframes fadeInModal {
    from { opacity: 0; }
    to   { opacity: 1; }
}

button:hover {
    background: rgba(163, 107, 226, 0.35);
    transform: translateY(-2px);
    box-shadow: 0 4px 12px var(--glass-shadow), 0 0 8px 2px rgba(163, 107, 226, 0.7);
}

.error {
    margin-top: 8px;
    color: #ff7b7b;
    font-size: 0.9rem;
}

/* Success messages share the same font size as error messages but use
   the accent colour defined for small messages.  When a license or
   reset action succeeds, the corresponding element receives this
   class to indicate success. */
.success {
    margin-top: 8px;
    color: #8bd7bd;
    font-size: 0.9rem;
}

.nav-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 14px 28px;
    margin: 20px;
    /* Frosted nav bar: sits slightly above the content with a semi‑opaque
       background, subtle border and soft shadow. */
    background: var(--glass-bg);
    backdrop-filter: blur(14px) saturate(1.3);
    -webkit-backdrop-filter: blur(14px) saturate(1.3);
    border: 1px solid var(--glass-border);
    border-radius: 12px;
    box-shadow: 0 6px 20px var(--glass-shadow);
}

/* Navigation left side: align logo and title horizontally */
.nav-left {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* Logo inside the nav bar.  Sized small and clickable. */
.nav-logo {
    width: 32px;
    height: 32px;
    object-fit: contain;
    border-radius: 4px;
}

/* Navigation right side holds the user avatar, name and logout button */
.nav-right {
    display: flex;
    align-items: center;
    gap: 12px;
}

/* Small circular avatar displayed in the nav bar */
.nav-user-avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    object-fit: cover;
    border: 1px solid var(--glass-border);
}

/* Align icons next to text across the nav and card headers */
.nav-links li a i,
h2 i,
h3 i {
    margin-right: 6px;
    vertical-align: middle;
}


.nav-title {
    font-size: 1.4rem;
    font-weight: 600;
}

.nav-links {
    list-style: none;
    display: flex;
    gap: 24px;
    margin: 0;
    padding: 0;
}

.nav-links li a {
    text-decoration: none;
    color: var(--text-color);
    font-weight: 500;
    position: relative;
    padding: 4px 0;
    transition: color var(--transition-speed);
}

.nav-links li a.active,
.nav-links li a:hover {
    color: var(--accent-color);
}

.nav-links li a.active::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: -6px;
    width: 100%;
    height: 2px;
    background: var(--accent-color);
    border-radius: 1px;
}

/* Stack nav icons above their labels.  Override the default horizontal
   alignment used for other headings so that icons in the nav bar sit
   centered above their text. */
.nav-links li a {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
}
.nav-links li a i {
    margin-right: 0;
    margin-bottom: 4px;
}

/* Hide admin nav item until authorised */
#nav-admin {
    display: none;
}

/* Hide admin-only nav items by default; JS will show them when authorised */
#nav-sessions {
    display: none;
}
#nav-licenses {
    display: none;
}

.logout-btn {
    margin-left: 16px;
}

/* Style the optional "Login with Discord" button on the login screen.  This
   button appears below the license login button and shares the same
   typography and colour palette.  A small top margin separates it from
   the licence button. */
.discord-login-btn {
    margin-top: 12px;
    width: 100%;
    padding: 12px;
    border: none;
    border-radius: 8px;
    background: var(--accent-colour, #5865F2);
    color: white;
    font-size: 1rem;
    cursor: pointer;
    transition: background 0.2s ease;
}
.discord-login-btn:hover {
    background: var(--accent-colour-hover, #4752C4);
}

/* Modal styles for avatar details */
.modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.6);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition-speed);
}
.modal:not(.hidden) {
    opacity: 1;
    pointer-events: auto;
}
.modal-content {
    max-width: 600px;
    max-height: 80vh;
    overflow-y: auto;
    position: relative;
}
.modal-close {
    position: absolute;
    top: 4px;
    right: 8px;
    background: transparent;
    border: none;
    color: var(--text-color);
    font-size: 1.5rem;
    cursor: pointer;
}

/* Avatar modal custom styling */
.avatar-modal-header {
    display: flex;
    align-items: center;
    margin-bottom: 8px;
}
.avatar-modal-image {
    width: 96px;
    height: 96px;
    border-radius: 8px;
    object-fit: cover;
    margin-right: 16px;
}
.avatar-modal-title h3 {
    margin: 0;
    font-size: 1.4rem;
    color: var(--text-color);
}
.avatar-modal-author {
    font-size: 0.9rem;
    color: var(--text-secondary, #bbb);
}
.avatar-modal-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 8px;
}
.avatar-tag {
    padding: 2px 6px;
    border-radius: 4px;
    background: rgba(255, 255, 255, 0.1);
    font-size: 0.75rem;
    color: var(--text-color);
}
.avatar-modal-tabs {
    display: flex;
    gap: 10px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    margin-bottom: 8px;
}
.avatar-modal-tabs .tab-btn {
    background: transparent;
    border: none;
    color: var(--text-secondary, #bbb);
    padding: 6px 10px;
    cursor: pointer;
    font-size: 0.9rem;
    transition: color var(--transition-speed), border-bottom var(--transition-speed);
}
.avatar-modal-tabs .tab-btn.active {
    color: var(--accent-color);
    border-bottom: 2px solid var(--accent-color);
}
.tab-content {
    display: none;
}
.tab-content.active {
    display: block;
}
.avatar-info-list {
    list-style: none;
    padding-left: 0;
    margin: 0;
}
.avatar-info-list li {
    margin-bottom: 6px;
    color: var(--text-color);
}
.copy-id-btn,
.copy-link-btn,
.download-json-btn {
    background: none;
    border: none;
    color: var(--accent-color);
    cursor: pointer;
    margin-left: 4px;
    font-size: 0.9rem;
}
.copy-id-btn:hover,
.copy-link-btn:hover,
.download-json-btn:hover {
    text-decoration: underline;
}

/* Fade-out animation for login screen */
.fade-out {
    animation: fadeOut 0.5s forwards;
}

@keyframes fadeOut {
    from { opacity: 1; }
    to { opacity: 0; }
}

.page {
    padding: 20px 40px;
    overflow-y: auto;
    height: calc(100vh - 80px);
}

.stats-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 24px;
}

.stat-card {
    flex: 1 1 300px;
}

ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

li {
    margin-bottom: 8px;
}

.avatars-grid {
    display: grid;
    gap: 24px;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    margin-top: 16px;
}

.avatar-card {
    /* New avatar card styling inspired by modern catalog grids.  Each card
       has a dark frosted background, rounded corners and a subtle
       shadow.  The image fills the top portion of the card and an
       overlay containing the name/author appears on hover. */
    position: relative;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: rgba(25, 10, 45, 0.6);
    border: 1px solid var(--glass-border);
    border-radius: 16px;
    box-shadow: 0 4px 12px var(--glass-shadow);
    /* prepare for animation */
    opacity: 0;
    animation: fadeInUp 0.6s ease forwards;
}

.avatar-card img {
    width: 100%;
    height: 200px;
    object-fit: cover;
    display: block;
    border-top-left-radius: 16px;
    border-top-right-radius: 16px;
}

.avatar-info {
    /* Overlay information sits at the bottom of the card.  Use an
       absolute positioning to overlap the image.  A semi‑transparent
       gradient creates a smooth fade into the artwork. */
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 10px 14px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(25, 10, 45, 0.8) 80%);
}

.avatar-name {
    font-weight: 600;
    font-size: 1rem;
    color: var(--text-color);
    line-height: 1.2;
}

.avatar-author {
    font-size: 0.8rem;
    color: rgba(255, 255, 255, 0.8);
    line-height: 1.2;
}

.small-message {
    margin-top: 8px;
    font-size: 0.9rem;
    color: #8bd7bd;
}

.license-form {
    margin-bottom: 24px;
}

.search-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 24px;
    /* Frosted search container: matches the glass aesthetic used across
       the dashboard.  Provides padding, border and blur so the search
       controls feel like a floating panel rather than bare inputs. */
    padding: 12px 16px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 14px;
    backdrop-filter: blur(14px) saturate(1.3);
    -webkit-backdrop-filter: blur(14px) saturate(1.3);
    box-shadow: 0 4px 16px var(--glass-shadow);
}

/* Ensure each input and button in the search bar consumes equal horizontal
   space and wraps gracefully.  Assign a base flex size and minimum width
   to prevent elements from shrinking excessively when the viewport is
   narrow. */
.search-bar input,
/* Style individual inputs and buttons inside the search bar.  Inputs
   feature a subtle backdrop tint and rounded corners, while buttons
   inherit the accent colour to stand out as call‑to‑action elements. */
.search-bar input,
.search-bar button {
    flex: 1 1 180px;
    min-width: 140px;
    padding: 6px 10px;
    border-radius: 8px;
    font-size: 0.9rem;
}

.search-bar input {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid var(--glass-border);
    color: var(--text-color);
    outline: none;
    transition: background var(--transition-speed), border var(--transition-speed);
}

.search-bar input::placeholder {
    color: rgba(255, 255, 255, 0.4);
}

.search-bar input:focus {
    background: rgba(255, 255, 255, 0.12);
    border-color: var(--accent-color);
}

.search-bar button {
    background: var(--accent-color);
    border: none;
    color: #ffffff;
    cursor: pointer;
    transition: background var(--transition-speed), transform var(--transition-speed);
}

.search-bar button:hover {
    /* Use a precomputed hover colour rather than the unsupported lighten() function.
       This ensures cross-browser compatibility while preserving the intended
       brightness bump for hover states. */
    background: var(--accent-color-hover);
    transform: translateY(-2px);
}

.search-bar button:active {
    transform: translateY(0);
}

.avatars-description {
    margin: 0 0 16px 0;
    color: var(--text-secondary);
    font-size: 0.9rem;
}

/* Controls bar for the avatars page.  Mirrors the look and feel of the search bar
   used previously but tailored to the new simplified interface with sorting.
   Inputs, select boxes and buttons share equal width and wrap gracefully. */
.avatars-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 24px;
    padding: 12px 16px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 14px;
    backdrop-filter: blur(14px) saturate(1.3);
    -webkit-backdrop-filter: blur(14px) saturate(1.3);
    box-shadow: 0 4px 16px var(--glass-shadow);
}
.avatars-controls input,
.avatars-controls select,
.avatars-controls button {
    flex: 1 1 180px;
    min-width: 140px;
    padding: 6px 10px;
    border-radius: 8px;
    font-size: 0.9rem;
}
.avatars-controls input {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid var(--glass-border);
    color: var(--text-color);
    outline: none;
    transition: background var(--transition-speed), border var(--transition-speed);
}
.avatars-controls input::placeholder {
    color: rgba(255, 255, 255, 0.4);
}
.avatars-controls input:focus {
    background: rgba(255, 255, 255, 0.12);
    border-color: var(--accent-color);
}
.avatars-controls select {
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid var(--glass-border);
    color: var(--text-color);
    outline: none;
    transition: background var(--transition-speed), border var(--transition-speed);
    appearance: none;
}
.avatars-controls select:focus {
    background: rgba(255, 255, 255, 0.12);
    border-color: var(--accent-color);
}
.avatars-controls button {
    background: var(--accent-color);
    border: none;
    color: #ffffff;
    cursor: pointer;
    transition: background var(--transition-speed), transform var(--transition-speed);
}
.avatars-controls button:hover {
    background: var(--accent-color-hover);
    transform: translateY(-2px);
}
.avatars-controls button:active {
    transform: translateY(0);
}

/* ---------------------------------------------------------------------------
 * Sidebar layout for the avatars page
 *
 * When browsing avatars the search/filter controls are moved from a horizontal
 * bar at the top of the page into a vertical sidebar on the left.  The
 * avatars-container wraps both the sidebar and the results list.  The sidebar
 * uses the same glass panel styling as other cards and remains fixed in
 * width, while the content area grows to fill the remaining space.  On
 * smaller screens the sidebar stacks above the content for a natural
 * responsive layout.
 */
.avatars-container {
    display: flex;
    gap: 24px;
    align-items: flex-start;
}

/* Sidebar holds the controls; its width is constrained to a reasonable size
   and it inherits the glass appearance used throughout the site. */
.avatars-sidebar {
    flex: 0 0 260px;
    max-width: 320px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 14px;
    backdrop-filter: blur(14px) saturate(1.3);
    -webkit-backdrop-filter: blur(14px) saturate(1.3);
    box-shadow: 0 4px 16px var(--glass-shadow);
    padding: 16px;
}

/* Stack the controls vertically within the sidebar. */
.avatars-sidebar .avatars-controls {
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    gap: 12px;
}

/* Inputs, selects and buttons inside the sidebar controls should span the full width. */
.avatars-sidebar .avatars-controls input,
.avatars-sidebar .avatars-controls select,
.avatars-sidebar .avatars-controls button {
    flex: none;
    width: 100%;
}

/* Main content area grows to fill available space. */
.avatars-content {
    flex: 1;
}

/* On small screens, collapse the sidebar above the content. */
@media (max-width: 800px) {
    .avatars-container {
        flex-direction: column;
    }
    .avatars-sidebar {
        width: 100%;
        max-width: none;
        margin-bottom: 16px;
    }
}

.pagination {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-top: 20px;
}

.pagination button {
    background: var(--glass-bg);
    color: var(--text-color);
    border: 1px solid var(--glass-border);
    backdrop-filter: blur(10px);
    padding: 6px 12px;
    border-radius: 8px;
    min-width: 40px;
}

.pagination button.active {
    /* Active pagination buttons adopt a stronger frosted accent tint rather than
       a fully opaque colour to stay consistent with other glass buttons. */
    background: rgba(163, 107, 226, 0.35);
    border-color: rgba(163, 107, 226, 0.45);
    color: var(--text-color);
}

/* Additional styling for pagination ellipsis */
.pagination .pagination-ellipsis {
    display: flex;
    align-items: center;
    padding: 0 4px;
    opacity: 0.7;
}

@media (max-width: 640px) {
    .nav-links {
        display: none;
    }
    .nav-right {
        flex: 1;
        justify-content: flex-end;
        display: flex;
    }
    .nav-bar {
        flex-wrap: wrap;
        gap: 12px;
    }
    .page {
        padding: 20px;
    }
}

/* Keyframes for fading items up when they appear */
@keyframes fadeInUp {
    from {
        opacity: 0;
        transform: translateY(12px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Toast notification container */
#toast-container {
    position: fixed;
    bottom: 20px;
    right: 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    z-index: 10000;
}

/* Individual toast styling */
.toast {
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    color: var(--text-color);
    padding: 12px 16px;
    border-radius: 12px;
    box-shadow: 0 4px 20px var(--glass-shadow);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    animation: toastFade 4s forwards;
    min-width: 220px;
    font-size: 0.9rem;
}

@keyframes toastFade {
    0% {
        opacity: 0;
        transform: translateY(10px);
    }
    10% {
        opacity: 1;
        transform: translateY(0);
    }
    90% {
        opacity: 1;
        transform: translateY(0);
    }
    100% {
        opacity: 0;
        transform: translateY(10px);
    }
}

/* ==========================================================================
   Live statistics update animation
   When a global statistic value changes the corresponding list item is given
   the ``stat-updated`` class.  This triggers a brief neon glow animation
   that draws the user's eye to the updated value without being overly
   distracting.
   ========================================================================== */
.stat-updated {
    animation: stat-update-glow 1.2s ease-in-out;
}

@keyframes stat-update-glow {
    0% {
        color: #00ffea;
        filter: brightness(1.1);
    }
    50% {
        color: #ffffff;
        filter: brightness(1.5);
    }
    100% {
        color: #00ffea;
        filter: brightness(1.1);
    }
}

/* ==========================================================================
   Page and modal transition animations
   These classes and keyframes create smooth fade/slide transitions when
   navigating between pages and opening modals.  The animations are
   inspired by the fluid iOS interface and provide a polished, modern feel.
   ========================================================================= */

/* Base transition for all pages */
.page {
    transition: opacity 0.4s ease, transform 0.4s ease;
}

/* Entering pages start from lower opacity and slight downward shift */
.page-enter {
    animation: pageEnter 0.4s ease forwards;
}

/* Leaving pages fade out and slide up */
.page-leave {
    animation: pageLeave 0.4s ease forwards;
}

@keyframes pageEnter {
    from {
        opacity: 0;
        transform: translateY(20px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes pageLeave {
    from {
        opacity: 1;
        transform: translateY(0);
    }
    to {
        opacity: 0;
        transform: translateY(-20px);
    }
}

/* Modal fade and scale effect */
.modal:not(.hidden) .modal-content {
    animation: modalFadeIn 0.4s ease forwards;
}

@keyframes modalFadeIn {
    from {
        opacity: 0;
        transform: scale(0.95);
    }
    to {
        opacity: 1;
        transform: scale(1);
    }
}

/* ==========================================================================
   Changelogs page styles

   The changelog page displays a list of update entries in a timeline.  Each
   entry is rendered as a glass card with a clickable header that toggles
   the visibility of its details.  Nested lists within the content are
   indented using additional margin and use alternate bullet styles to
   differentiate levels.  Headings and paragraphs inside the changelog
   content inherit the overall typography but adjust spacing for clarity.
   ========================================================================= */

/* Container for all changelog entries */
.changelogs-list {
    display: flex;
    flex-direction: column;
    gap: 16px;
    margin: 16px;
    overflow-y: auto;
    /* Remove the max-height so the list can grow naturally; the page container will
       handle overflow scrolling instead of constraining the list height. */
    max-height: none;
    /* Remove the bottom fade entirely to avoid cutting off entries. */
    mask-image: none;
}

/* Each changelog entry extends the glass card style.  We remove default
   padding because the header and content define their own spacing. */
.changelog-card {
    padding: 0;
    overflow: hidden;
    cursor: default;
    /* Animate cards in when the changelogs page is shown.  Each card
       starts slightly below and fully transparent, then slides up and
       fades into view.  The delay is controlled via the custom
       property --changelog-delay set in JavaScript for sequential
       staggering. */
    opacity: 0;
    transform: translateY(10px);
    animation: changelogCardEnter 0.5s ease forwards;
    animation-delay: var(--changelog-delay, 0s);
    /* Smooth hover effect; cards lift slightly and brighten when hovered. */
    transition: transform var(--transition-speed), background var(--transition-speed), box-shadow var(--transition-speed);
}

.changelog-card:hover {
    transform: translateY(-3px) scale(1.02);
    background: rgba(255, 255, 255, 0.02);
    box-shadow: 0 4px 16px var(--glass-shadow), 0 0 10px 2px rgba(163, 107, 226, 0.6);
}

/* Keyframes for changelog card entrance.  Mirrors the page transitions
   elsewhere on the site by fading and sliding from below. */
@keyframes changelogCardEnter {
    from {
        opacity: 0;
        transform: translateY(10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Header of a changelog entry.  Displays the title and an arrow indicator.
   The arrow rotates when the card is open. */
.changelog-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 16px;
    user-select: none;
    cursor: pointer;
    border-bottom: 1px solid rgba(200, 150, 255, 0.25);
    transition: background var(--transition-speed), color var(--transition-speed);
    transition-property: background, color, transform;
}

.changelog-header:hover {
    background: rgba(255, 255, 255, 0.05);
}

.changelog-header:active {
    transform: scale(0.98);
}

.changelog-card:hover .changelog-header {
    background: rgba(255, 255, 255, 0.03);
}

.changelog-title {
    font-weight: 600;
    font-size: 1.05rem;
    flex: 1;
}

.changelog-arrow {
    margin-left: 12px;
    transition: transform 0.3s ease;
    font-size: 1rem;
}

.changelog-card.open .changelog-arrow {
    transform: rotate(90deg);
}

/* Content area for a changelog entry.  Hidden by default and revealed
   when the card has the open class.  The padding matches the header. */
.changelog-content {
    /* Content is hidden by default via max-height and opacity.
       It expands smoothly when the card gains the "open" class. */
    padding: 16px;
    line-height: 1.5;
    overflow: hidden;
    max-height: 0;
    opacity: 0;
    transition: max-height 0.4s ease, opacity 0.4s ease;
}

.changelog-card.open .changelog-content {
    opacity: 1;
    /*
     * Limit the expanded changelog height to half the viewport.  This prevents
     * extremely long entries from overflowing the page and instead
     * introduces an internal scrollbar.  The JavaScript logic caps the
     * height further if needed, but these CSS values provide sensible
     * defaults.  Making the content scrollable ensures all text remains
     * accessible to the user.
     */
    max-height: 50vh;
    overflow-y: auto;
}

/* Typography tweaks inside changelog content */
.changelog-content h3,
.changelog-content h4 {
    margin-top: 0;
    margin-bottom: 0.5rem;
    font-weight: 600;
}

.changelog-content p {
    margin: 0.5rem 0;
}

.changelog-content ul {
    margin: 0.5rem 0 0.5rem 1.25rem;
    padding: 0;
    list-style: disc;
}

/* Nested lists use alternate bullet styles to indicate depth */
.changelog-content ul ul {
    list-style: circle;
    margin-left: 1.25rem;
}

/* Card appearance animation for login and welcome screens */
.login-card {
    animation: cardFadeIn 0.6s ease forwards;
}

@keyframes cardFadeIn {
    from {
        opacity: 0;
        transform: translateY(20px) scale(0.95);
    }
    to {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

/* ==========================================================================
   Loading overlay and progress bar styles
   This overlay is displayed after a successful login/continue to visualise
   the application initialisation process.  It presents a glassy card
   containing the provided Loading.gif, a status message and a progress
   bar with animated fill.  The bar uses a moving gradient and glow to
   echo the existing ambient effects.
   ========================================================================== */

#loading-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2000;
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    opacity: 1;
    transition: opacity var(--transition-speed) ease;
}

#loading-overlay.hidden {
    display: none;
    opacity: 0;
    pointer-events: none;
}

.loading-container {
    width: 380px;
    padding: 30px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 18px;
}

.loading-gif {
    width: 120px;
    height: 120px;
    object-fit: contain;
}

.loading-text {
    font-size: 1.1rem;
    text-align: center;
    color: var(--text-color);
}

.progress-bar {
    width: 100%;
    height: 14px;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid var(--glass-border);
    border-radius: 10px;
    overflow: hidden;
    position: relative;
}

/* --------------------------------------------------------------------------
 * Conversion cards and history styling
 *
 * These rules style the new conversion status cards, progress bars,
 * embedded previews and the history list introduced by the conversion UI
 * overhaul.  Each conversion card contains a stage text that animates
 * between states, a thin progress bar and a results section showing
 * before/after previews.  The history list uses similar styling but
 * wraps entries in their own cards with a header that displays the
 * conversion time.
 */

/* Main container for a conversion status item.  Use a subtle
   semi‑transparent background to distinguish each entry. */
.conversion-card {
    padding: 10px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.05);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* Stage text container.  Constrain the height so that transitions
   translate the text vertically without affecting layout. */
.conversion-card .status-stage-container {
    /* Increase the height slightly to provide room for the animated
       stage text and prevent overlap with the progress bar. */
    height: 26px;
    overflow: hidden;
}

/* Stage text itself.  The transitions are triggered in JavaScript by
   adjusting opacity and transform properties. */
.conversion-card .status-stage-text {
    font-size: 0.9rem;
    transition: opacity 0.25s ease, transform 0.25s ease;
    will-change: opacity, transform;
}

/* Thin progress bar shown beneath the stage text. */
.conversion-card .status-progress-bar {
    width: 100%;
    height: 6px;
    background: rgba(255, 255, 255, 0.1);
    border-radius: 4px;
    overflow: hidden;
    position: relative;
    /* Add a small margin to separate the bar from the stage text above */
    margin-top: 4px;
}

/* Filled portion of the progress bar.  The width is animated via
   JavaScript to reflect the estimated completion percentage. */
.conversion-card .status-progress-fill {
    height: 100%;
    width: 0%;
    background: linear-gradient(90deg, var(--accent-color), #6b3dd6, var(--accent-color));
    background-size: 200% 100%;
    animation: progressGlide 4s linear infinite;
    transition: width 0.4s ease;
}

/* Results container holds two preview columns for the original and
   converted files.  It wraps on smaller screens. */
/* Results container uses two columns (originals and output).  The
   container is a flex row and the columns inside stretch to fill the
   available width. */
/* Results container lays out its child cards in a row.  The row will wrap
   on smaller screens if needed. */
.conversion-card .status-results {
    display: flex;
    /* Keep all three cards on a single row. If the container becomes too
       narrow to hold them, allow horizontal scrolling rather than
       wrapping to a new line. */
    flex-wrap: nowrap;
    gap: 16px;
    margin-top: 12px;
    align-items: flex-start;
    overflow-x: auto;
}

/* Columns inside the results container.  Each column stacks its file
   cards vertically and distributes space evenly. */
/* A file-card displays a media preview and meta information.  It is
   limited to 35% of the available width so that three cards fit
   comfortably in one row. */
.conversion-card .file-card {
    /* Ensure three cards fit comfortably in a single row without
       overflowing. Give each a fixed basis of approximately one third of
       the container width. Do not allow them to grow beyond this.
       The width is overridden from the generic .file-card (100%) below. */
    flex: 0 0 32%;
    max-width: 32%;
    width: 100%;
}

/* Info card uses the same container styling as a file card but holds
   textual details about the conversion. */
.conversion-card .info-card {
    /* Give the info card the same basis and width as file cards so it
       aligns neatly in the row. */
    flex: 0 0 32%;
    max-width: 32%;
    width: 100%;
    background: rgba(255, 255, 255, 0.05);
    border-radius: 8px;
    padding: 8px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    box-sizing: border-box;
}

.conversion-card .info-card .info-title {
    font-weight: 600;
    margin-bottom: 4px;
}

.conversion-card .info-card .info-line {
    font-size: 0.75rem;
    color: var(--text-secondary);
    word-break: break-word;
}

/* Embedded media previews inside the results section. */
/* Media previews inside the results.  Use a 1:1 aspect ratio to create
   square previews and cover scaling to avoid letterboxing.  Audio
   players use their default height but still fill the width. */
.conversion-card .status-results img,
.conversion-card .status-results video {
    width: 100%;
    aspect-ratio: 1 / 1;
    height: auto;
    object-fit: cover;
    border-radius: 6px;
}
.conversion-card .status-results audio {
    width: 100%;
    /* Keep a reasonable height for audio controls */
    height: 64px;
}

/* Display file names under previews with subdued styling. */
.conversion-card .status-results .file-name {
    font-size: 0.75rem;
    margin-top: 4px;
    color: var(--text-secondary);
    word-break: break-all;
}

/* Generic styling for a file preview card.  Each original and converted
   file is wrapped in one of these containers.  It holds the media
   preview, the file name and metadata such as type and size. */
.file-card {
    background: rgba(255, 255, 255, 0.05);
    border-radius: 8px;
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    /* Allow file cards to stretch to fill their parent column. */
    width: 100%;
    box-sizing: border-box;
}

.file-card img,
/* media elements inside file cards */
.file-card video,
.file-card audio {
    width: 100%;
    border-radius: 4px;
}

.file-card img,
.file-card video {
    aspect-ratio: 1 / 1;
    height: auto;
    object-fit: cover;
}

.file-card audio {
    /* Keep audio controls compact */
    height: 64px;
    object-fit: contain;
}

.file-card .file-name {
    font-size: 0.8rem;
    font-weight: 500;
    overflow-wrap: anywhere;
}

.file-card .file-meta {
    font-size: 0.7rem;
    color: var(--text-secondary);
    overflow-wrap: anywhere;
}

/* History list container */
.history-list {
    display: flex;
    flex-direction: column;
    gap: 16px;
    margin-top: 12px;
}

/* Individual history entry styled similarly to conversion cards */
.history-card {
    padding: 14px;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.05);
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/*
 * Censored fields for license information.  Values such as IP and HWID
 * should not be displayed openly.  Instead we display a placeholder
 * and allow users to click to reveal the value.  This class applies
 * a subtle dotted underline and pointer cursor to indicate that the
 * text is interactive.  The colour uses the accent colour for
 * consistency with other interactive elements.
 */
.censored {
    color: var(--accent-color);
    text-decoration: underline dotted;
    cursor: pointer;
    user-select: none;
}

/* Header of the history card with time and tool name */
.history-card .history-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 0.9rem;
}

.history-card .history-time {
    font-size: 0.75rem;
    color: var(--text-secondary);
}

/* Body of history card showing previews */
.history-card .history-body {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.history-card .history-body > div {
    flex: 1 1 calc(50% - 8px);
    min-width: 120px;
}

.history-card .history-body img,
.history-card .history-body video,
.history-card .history-body audio {
    width: 100%;
    max-height: 200px;
    border-radius: 6px;
}

.history-card .history-body .file-name {
    font-size: 0.75rem;
    margin-top: 4px;
    color: var(--text-secondary);
    word-break: break-all;
}

.progress-fill {
    height: 100%;
    width: 0%;
    background: linear-gradient(90deg, var(--accent-color), #6b3dd6, var(--accent-color));
    background-size: 200% 100%;
    animation: progressGlide 4s linear infinite;
    box-shadow: 0 0 12px 4px rgba(163, 107, 226, 0.6);
    transition: width 1s ease-in-out;
}

@keyframes progressGlide {
    0% { background-position: 0% 50%; }
    100% { background-position: 200% 50%; }
}

/* Fade-out animation for the loading overlay */
#loading-overlay.fade-out {
    animation: fadeOutOverlay 0.6s forwards;
}

@keyframes fadeOutOverlay {
    from { opacity: 1; }
    to   { opacity: 0; }
}

/* ==========================================================================
   Dashboard loader styles
   This small overlay appears on the dashboard while data is fetching.
   ========================================================================== */

.dashboard-loader {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 1500;
    padding: 24px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 14px;
    box-shadow: 0 4px 20px var(--glass-shadow), 0 0 12px 4px rgba(163, 107, 226, 0.6);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    display: flex;
    align-items: center;
    justify-content: center;
}

.dashboard-loader.hidden {
    display: none;
}

.dashboard-loading-gif {
    width: 80px;
    height: 80px;
    object-fit: contain;
}

/* Search loader for avatar search */
.search-loader {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 1000;
    padding: 24px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-radius: 14px;
    box-shadow: 0 4px 20px var(--glass-shadow), 0 0 12px 4px rgba(163, 107, 226, 0.6);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    display: none;
    align-items: center;
    justify-content: center;
}
.search-loader.hidden {
    display: none;
}
.search-loading-gif {
    width: 60px;
    height: 60px;
    object-fit: contain;
}

/* Style for the VRChat open link button in avatar modals */
.open-vrchat-btn {
    background: var(--accent-color);
    color: #ffffff;
    border: none;
    padding: 4px 8px;
    border-radius: 4px;
    cursor: pointer;
    font-size: 0.9rem;
    margin-left: 4px;
    transition: background 0.15s ease;
}
.open-vrchat-btn:hover {
    opacity: 0.9;
}

/* ==========================================================================
   Management page styles
   The management page displays active locations, license management tools,
   and a Discord user lookup.  These styles build on the glass aesthetic.
   ========================================================================== */

.management-sections {
    display: flex;
    flex-wrap: wrap;
    gap: 24px;
    margin-top: 20px;
}

.admin-section {
    flex: 1 1 420px;
    padding: 16px;
}

.locations-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin-top: 12px;
}

.location-card {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border-radius: 12px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    box-shadow: 0 2px 10px var(--glass-shadow), 0 0 8px 2px rgba(163, 107, 226, 0.4);
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.location-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 4px 14px var(--glass-shadow), 0 0 12px 3px rgba(163, 107, 226, 0.6);
}

.location-avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    object-fit: cover;
}

.location-details {
    display: flex;
    flex-direction: column;
}

.location-username {
    font-weight: 600;
    font-size: 1rem;
    color: var(--text-primary);
}

.location-world-name {
    font-size: 0.9rem;
    color: var(--text-secondary);
}

.location-world-meta {
    font-size: 0.7rem;
    color: var(--text-secondary);
    line-height: 1.2;
}

.location-world-thumb {
    width: 60px;
    height: 60px;
    border-radius: 8px;
    object-fit: cover;
    margin-left: auto;
}

/* ==========================================================================
   Changelog management styles (admin)

   Forms and lists for creating, editing and deleting changelog entries in
   the management page.  Inputs and text areas follow the glass theme and
   align with the other management tools.  List items lay out the title and
   controls in a clean horizontal row.
   ========================================================================= */

.changelog-create-form input,
.changelog-create-form textarea {
    width: 100%;
    margin-bottom: 8px;
    border: 1px solid var(--glass-border);
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.05);
    color: var(--text-color);
    padding: 10px 12px;
    box-sizing: border-box;
    font-size: 0.9rem;
}

.changelog-create-form textarea {
    resize: vertical;
}

.changelog-create-form button {
    margin-top: 4px;
}

.changelog-admin-list {
    margin-top: 12px;
}

.changelog-admin-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 0;
    border-bottom: 1px solid rgba(200, 150, 255, 0.15);
}

.changelog-admin-item .changelog-admin-title {
    flex: 1;
    font-weight: 500;
    font-size: 0.95rem;
}

.changelog-admin-item button {
    margin-left: 8px;
    font-size: 0.8rem;
    padding: 4px 8px;
}

.lookup-form {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
}

.lookup-form input {
    flex: 1 1 auto;
    padding: 8px 12px;
    border-radius: 8px;
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
    color: var(--text-primary);
    outline: none;
}

.lookup-form input::placeholder {
    color: var(--text-secondary);
}

.lookup-form button {
    padding: 8px 16px;
    border-radius: 8px;
    border: 1px solid var(--glass-border);
    background: rgba(163, 107, 226, 0.25);
    color: var(--text-color);
    cursor: pointer;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    transition: background var(--transition-speed), box-shadow var(--transition-speed);
}

.lookup-form button:hover {
    background: rgba(163, 107, 226, 0.35);
    box-shadow: 0 3px 10px var(--glass-shadow), 0 0 6px 1px rgba(163, 107, 226, 0.6);
}

.user-result {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.user-card {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 16px;
    border-radius: 12px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    box-shadow: 0 2px 10px var(--glass-shadow), 0 0 8px 2px rgba(163, 107, 226, 0.4);
    max-width: 420px;
}

.user-card-header {
    display: flex;
    align-items: center;
    gap: 12px;
}

.user-avatar {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    object-fit: cover;
}

.user-basic-info {
    display: flex;
    flex-direction: column;
}

.user-name {
    font-size: 1.3rem;
    font-weight: 600;
    color: var(--text-primary);
}

.user-id {
    font-size: 0.9rem;
    color: var(--text-secondary);
}

.user-extra-info {
    padding: 12px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.05);
    color: var(--text-secondary);
    font-size: 0.9rem;
    word-break: break-all;
}

/*
 * Action panel for admin user operations.  Appears below the user card in
 * the management lookup section and provides controls for modifying a
 * user's license or resetting their lockouts.  Inputs and buttons here
 * follow the glass aesthetic consistent with the rest of the UI.
 */
.user-actions {
    margin-top: 12px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-width: 420px;
}
.user-actions input {
    padding: 8px 12px;
    border-radius: 8px;
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
    color: var(--text-primary);
    outline: none;
    width: 100%;
    box-sizing: border-box;
}
.user-actions input::placeholder {
    color: var(--text-secondary);
}
.user-actions button {
    align-self: flex-start;
    padding: 6px 14px;
    font-size: 0.9rem;
}

/* License management and generic form styles */
.license-form {
    margin-bottom: 16px;
}

.license-form h4 {
    margin-bottom: 8px;
    font-size: 1rem;
    font-weight: 500;
    color: var(--text-primary);
}

.form-group {
    display: flex;
    flex-direction: column;
    margin-bottom: 8px;
}

.form-group input {
    padding: 8px 12px;
    border-radius: 8px;
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
    color: var(--text-primary);
    outline: none;
}

.form-group input::placeholder {
    color: var(--text-secondary);
}

.license-form button {
    padding: 8px 16px;
    border-radius: 8px;
    border: 1px solid var(--glass-border);
    background: rgba(163, 107, 226, 0.25);
    color: var(--text-color);
    cursor: pointer;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    transition: background var(--transition-speed), box-shadow var(--transition-speed);
}

.license-form button:hover {
    background: rgba(163, 107, 226, 0.35);
    box-shadow: 0 3px 10px var(--glass-shadow), 0 0 6px 1px rgba(163, 107, 226, 0.6);
}

/* ==========================================================================
   Media player styles
   A collapsible music player docked to the bottom of the screen.  It holds
   the current track artwork, title, artist and playback controls.  The
   player uses the glass aesthetic and slides into view when hovered or
   expanded via JavaScript.  When collapsed it hides most of its content
   leaving only a small tab visible.
   ========================================================================== */

.media-player {
    position: fixed;
    left: 0;
    bottom: 0;
    width: 100%;
    /* Height of full player: adjust as needed */
    height: 100px;
    background: var(--glass-bg);
    border: 1px solid var(--glass-border);
    border-top-left-radius: 16px;
    border-top-right-radius: 16px;
    box-shadow: 0 -4px 20px var(--glass-shadow), 0 0 12px 4px rgba(163, 107, 226, calc(var(--audio-level) * 0.8));
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    display: flex;
    flex-direction: column;
    transition: transform 0.3s ease;
    z-index: 500; /* above other overlay elements but below modals */
    pointer-events: auto;
}

/* When collapsed the player is mostly hidden off screen; only the tab is visible */
.media-player.collapsed {
    transform: translateY(calc(100% - 20px));
}

/* Tab/handle area that the user hovers over to reveal the player */
.media-tab {
    width: 100%;
    height: 20px;
    cursor: pointer;
    position: relative;
}
/* Draw a small horizontal bar on the tab to indicate handle */
.media-tab::before {
    content: '';
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    width: 60px;
    height: 4px;
    border-radius: 2px;
    background: rgba(255, 255, 255, 0.3);
}

.media-content {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 16px;
}

.media-thumbnail {
    width: 60px;
    height: 60px;
    border-radius: 12px;
    object-fit: cover;
    flex-shrink: 0;
}

.media-info {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 4px;
    overflow: hidden;
}

.media-title {
    font-size: 1rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.media-artist {
    font-size: 0.8rem;
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.media-controls {
    display: flex;
    gap: 8px;
}

.media-btn {
    width: 44px;
    height: 36px;
    border-radius: 8px;
    border: 1px solid var(--glass-border);
    background: var(--glass-bg);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    color: var(--text-color);
    font-size: 1.1rem;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    box-shadow: 0 2px 8px var(--glass-shadow), 0 0 6px 1px rgba(163, 107, 226, calc(var(--audio-level) * 0.6));
    transition: background 0.2s ease, box-shadow 0.2s ease;
}

.media-btn:hover {
    background: rgba(50, 0, 70, 0.45);
    box-shadow: 0 4px 10px var(--glass-shadow), 0 0 8px 2px rgba(163, 107, 226, 0.8);
}

/* --------------------------------------------------------------------
   Conversion utilities styling
   -------------------------------------------------------------------- */
.conversion-flex {
    display: flex;
    flex-wrap: wrap;
    gap: 32px;
    margin-top: 16px;
}

.conversion-form {
    flex: 1 1 60%;
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding: 20px;
}

.conversion-status {
    flex: 1 1 40%;
    max-height: 600px;
    overflow-y: auto;
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.form-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* Custom select styling for dropdowns */
.select-input {
    /* Unified styling for all dropdowns in the conversion UI.  The
       appearance properties remove default OS styling and allow full
       customisation. */
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding: 8px 12px;
    border-radius: 8px;
    border: 1px solid var(--glass-border);
    /* Use a dark frosted base similar to other glass elements */
    background: rgba(50, 0, 70, 0.45);
    backdrop-filter: blur(8px);
    color: var(--text-color);
    outline: none;
    font-size: 0.9rem;
    transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
    cursor: pointer;
}

.select-input:hover {
    /* Lighten the background slightly on hover */
    background: rgba(50, 0, 70, 0.55);
}

.select-input:focus {
    /* Highlight the border and add a subtle glow when focused */
    border-color: var(--accent-color);
    background: rgba(50, 0, 70, 0.55);
    box-shadow: 0 0 0 2px rgba(163, 107, 226, 0.4);
}

/* File upload styling */
.file-upload {
    display: flex;
    align-items: center;
    gap: 10px;
    position: relative;
}

.file-upload-label {
    padding: 10px 18px;
    border-radius: 8px;
    cursor: pointer;
    background: rgba(255, 255, 255, 0.15);
    border: 1px solid var(--glass-border);
    color: var(--text-color);
    transition: background 0.2s ease, border-color 0.2s ease;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 120px;
}

.file-upload-label:hover {
    background: rgba(255, 255, 255, 0.2);
    border-color: var(--accent-color);
}

.file-upload input[type="file"] {
    display: none;
}

.file-name {
    font-size: 0.85rem;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 200px;
}

/* Convert button */
.convert-btn {
    padding: 10px 20px;
    border-radius: 8px;
    border: none;
    font-size: 1rem;
    cursor: pointer;
    background: rgba(255, 255, 255, 0.1);
    color: var(--text-color);
    transition: background 0.2s ease;
}

.convert-btn:hover:not([disabled]) {
    background: rgba(255, 255, 255, 0.15);
}

.convert-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Status list */
.status-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    font-size: 0.9rem;
}

.status-item {
    padding: 6px 8px;
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.05);
}

.status-link {
    color: var(--accent-color);
    text-decoration: underline;
    margin-left: 4px;
}

/* ======================================================================
   Global scrollbar styling

   The default scrollbars on many platforms clash with the glassy,
   ultraviolet aesthetic of Valorous.  To remedy this we define a set of
   CSS variables and selectors that customise scrollbars across the
   application.  These rules apply to any element that becomes
   scrollable, whether via explicit overflow settings or naturally due to
   content size.  The thumb colour uses the existing accent palette and
   darkens on hover and active states.  Firefox is handled via
   scrollbar-width and scrollbar-color; WebKit browsers use the
   ::-webkit-scrollbar pseudo-elements.
*/

:root {
    /* Size of the scrollbar track (width on vertical, height on horizontal). */
    --scrollbar-size: 8px;
    /* Transparent background keeps the track subtle behind our glass panels. */
    --scrollbar-bg: rgba(255, 255, 255, 0.05);
    /* Default thumb colour derived from our accent colour. */
    --scrollbar-thumb: rgba(163, 107, 226, 0.6);
    /* Darker shade for hover state. */
    --scrollbar-thumb-hover: rgba(163, 107, 226, 0.8);
    /* Full accent colour for active/grab state. */
    --scrollbar-thumb-active: rgba(163, 107, 226, 1);
}

/* Firefox customisation: use a thin scrollbar and set the thumb/track colours. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--scrollbar-thumb) transparent;
}

/* WebKit customisation: define the track and thumb appearance. */
*::-webkit-scrollbar {
    width: var(--scrollbar-size);
    height: var(--scrollbar-size);
}

*::-webkit-scrollbar-track {
    background: transparent;
}

*::-webkit-scrollbar-thumb {
    background-color: var(--scrollbar-thumb);
    border-radius: calc(var(--scrollbar-size) / 2);
    /* Create spacing around the thumb for a sleek look.  The
       transparent border ensures the thumb sits neatly inside the
       track and preserves the accent colour. */
    border: 2px solid transparent;
    background-clip: content-box;
}

*::-webkit-scrollbar-thumb:hover {
    background-color: var(--scrollbar-thumb-hover);
}

*::-webkit-scrollbar-thumb:active {
    background-color: var(--scrollbar-thumb-active);
}

/* Styles for the combined build information table.  This creates a
   condensed chart-like view for all builds on a single card. */
#builds-card table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 8px;
    color: var(--text-color);
    font-size: 0.9rem;
}

#builds-card th,
#builds-card td {
    padding: 8px 12px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    white-space: nowrap;
}

#builds-card th {
    text-align: left;
    font-weight: 600;
    background: rgba(255, 255, 255, 0.05);
}

#builds-card .builds-table-wrapper {
    overflow-x: auto;
}

#builds-card button {
    margin-top: 12px;
}
