﻿html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

/*
 * Global scrollbar — STRUCTURE ONLY. Colour lives in light.css / dark.css.
 *
 * This was a #1db954 track with a #8e44ad thumb AND matching arrow buttons: a saturated
 * green-and-purple stripe down the edge of every page, next to the cover art. It was the last
 * of the Spotify green and the loudest chrome on the site.
 *
 * ::-webkit-scrollbar-button is deleted rather than recoloured — those are the stepper arrows,
 * the worst of it, and no current platform draws them by default.
 */
::-webkit-scrollbar {
    width: 12px;
    height: 12px;
}

::-webkit-scrollbar-thumb {
    border-radius: 6px;
    /* A transparent border plus content-box clipping insets the thumb inside the 12px gutter,
       so the bar reads as a slim handle while keeping a full-width grab target. */
    border: 3px solid transparent;
    background-clip: content-box;
}

.e-sidebar {
    overflow-x: hidden;
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

.app-logo {
    height: 45px;
    margin-left: 10px;
}

.mobile-app-install-banner {
    position: sticky;
    top: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    gap: 12px;
    border-bottom: 1px solid;
}

.mobile-app-install-icon {
    flex: 0 0 auto;
    object-fit: contain;
}

.mobile-app-install-copy {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-width: 0;
}

.mobile-app-install-title,
.mobile-app-install-subtitle {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mobile-app-install-store-link {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
}

.mobile-app-install-store-badge {
    display: block;
    width: auto;
    object-fit: contain;
}

.mobile-app-install-store-text {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    font-weight: 700;
    line-height: 1;
    white-space: nowrap;
}

.mobile-app-install-dismiss {
    flex: 0 0 auto;
}

/* Admin menu header styling */
.e-menu-wrapper .e-menu .e-menu-item.e-disabled {
    opacity: 1 !important;
    font-weight: bold;
    cursor: default;
}

.streamtunes-menu-icon {
    font-family: "Segoe UI Symbol", "Noto Sans Symbols", "Segoe UI Emoji", sans-serif;
    font-style: normal;
    font-weight: 400;
    position: relative;
    text-align: center;
}

.streamtunes-menu-icon::before {
    display: inline-block;
    line-height: inherit;
}

.streamtunes-menu-icon-home::before {
    content: "\2302";
}

.streamtunes-menu-icon-semiquaver::before {
    content: "\266C";
}

.streamtunes-menu-icon-upload-music::before {
    content: "\2B06";
    font-size: 1.18em;
    font-weight: 800;
}

.streamtunes-menu-icon-check::before {
    content: "\2713";
}

.streamtunes-menu-icon-list::before {
    content: "\2630";
}

.streamtunes-menu-icon-account::before,
.streamtunes-menu-icon-account::after,
.streamtunes-menu-icon-song-list::before,
.streamtunes-menu-icon-song-list::after,
.streamtunes-menu-icon-personality::before,
.streamtunes-menu-icon-personality::after,
.streamtunes-menu-icon-masks::before,
.streamtunes-menu-icon-masks::after {
    box-sizing: border-box;
    display: block;
    position: absolute;
}

.streamtunes-menu-icon-account::before,
.streamtunes-menu-icon-account::after,
.streamtunes-menu-icon-song-list::after,
.streamtunes-menu-icon-personality::before,
.streamtunes-menu-icon-personality::after,
.streamtunes-menu-icon-masks::before,
.streamtunes-menu-icon-masks::after {
    content: "";
}

.streamtunes-menu-icon-account::before {
    border: 1.7px solid currentColor;
    border-radius: 50%;
    height: 0.36em;
    left: 50%;
    top: 46%;
    transform: translate(-50%, -75%);
    width: 0.36em;
}

.streamtunes-menu-icon-account::after {
    border: 1.7px solid currentColor;
    border-bottom: 0;
    border-radius: 0.75em 0.75em 0 0;
    height: 0.42em;
    left: 50%;
    top: 56%;
    transform: translateX(-50%);
    width: 0.78em;
}

.streamtunes-menu-icon-song-list::before {
    content: "\266A";
    font-size: 0.9em;
    font-weight: 700;
    left: 0.03em;
    line-height: 1;
    top: 50%;
    transform: translateY(-52%);
}

.streamtunes-menu-icon-song-list::after {
    background:
        linear-gradient(currentColor, currentColor) 0 0 / 100% 1.6px no-repeat,
        linear-gradient(currentColor, currentColor) 0 50% / 100% 1.6px no-repeat,
        linear-gradient(currentColor, currentColor) 0 100% / 100% 1.6px no-repeat;
    height: 0.46em;
    left: 0.54em;
    top: 50%;
    transform: translateY(-50%);
    width: 0.42em;
}

.streamtunes-menu-icon-personality::before,
.streamtunes-menu-icon-personality::after {
    border: 1.45px solid currentColor;
    border-radius: 50% 50% 44% 44%;
    height: 0.68em;
    top: 50%;
    width: 0.46em;
}

.streamtunes-menu-icon-personality::before {
    background:
        radial-gradient(circle at 36% 39%, currentColor 0 1px, transparent 1.2px),
        radial-gradient(circle at 64% 39%, currentColor 0 1px, transparent 1.2px),
        linear-gradient(currentColor, currentColor) 50% 68% / 0.16em 1.25px no-repeat;
    left: 38%;
    transform: translate(-50%, -52%) rotate(-9deg);
}

.streamtunes-menu-icon-personality::after {
    background:
        radial-gradient(circle at 36% 39%, currentColor 0 1px, transparent 1.2px),
        radial-gradient(circle at 64% 39%, currentColor 0 1px, transparent 1.2px),
        linear-gradient(currentColor, currentColor) 50% 69% / 0.18em 1.25px no-repeat;
    left: 62%;
    transform: translate(-50%, -48%) rotate(9deg);
}

.streamtunes-menu-icon-users::before {
    content: "\25CC";
}

.streamtunes-menu-icon-dashboard::before {
    content: "\25A6";
}

.streamtunes-menu-icon-creator::before {
    content: "\2736";
}

.streamtunes-menu-icon-lock::before {
    content: "\26BF";
}

.streamtunes-menu-icon-mail::before {
    content: "\2709";
}

.streamtunes-menu-icon-inbox::before {
    content: "\25A3";
}

.streamtunes-menu-icon-status-history::before {
    content: "\231B";
}

.streamtunes-menu-icon-user-history::before {
    content: "\21BA";
    font-weight: 700;
}

.streamtunes-menu-icon-catalog::before {
    content: "\25A7";
}

.streamtunes-menu-icon-money::before {
    content: "$";
    font-weight: 700;
}

.streamtunes-menu-icon-warning::before {
    content: "!";
    font-weight: 700;
}

.streamtunes-menu-icon-flag::before {
    content: "\2691";
}

.streamtunes-menu-icon-document::before {
    content: "\25A4";
}

.streamtunes-menu-icon-settings::before {
    content: "\2699";
}

.streamtunes-menu-icon-masks::before,
.streamtunes-menu-icon-masks::after {
    border: 1.45px solid currentColor;
    border-radius: 50% 50% 45% 45%;
    height: 0.68em;
    top: 50%;
    width: 0.48em;
}

.streamtunes-menu-icon-masks::before {
    left: 38%;
    transform: translate(-50%, -52%) rotate(-8deg);
}

.streamtunes-menu-icon-masks::after {
    left: 62%;
    transform: translate(-50%, -48%) rotate(8deg);
}

.streamtunes-menu-icon-gauge::before {
    content: "\25D4";
}

.streamtunes-menu-icon-logout::before {
    content: "\21AA";
}

.streamtunes-menu-icon-login::before {
    content: "\21A9";
}

.streamtunes-menu-icon-add::before {
    content: "+";
    font-weight: 700;
}

.streamtunes-menu-icon-send::before {
    content: "\2708";
}

.streamtunes-menu-icon-delete::before {
    content: "\00D7";
    font-weight: 700;
}

.streamtunes-menu-icon-edit::before {
    content: "\270E";
}

.streamtunes-button-icon {
    display: inline-block;
    font-family: "Segoe UI Symbol", "Noto Sans Symbols", "Segoe UI Emoji", sans-serif;
    font-style: normal;
    font-weight: 400;
    height: 1em;
    line-height: 1;
    position: relative;
    text-align: center;
    width: 1em;
}

.streamtunes-button-icon::before {
    display: inline-block;
    line-height: 1;
}

.streamtunes-button-icon-song-list::before,
.streamtunes-button-icon-song-list::after {
    box-sizing: border-box;
    display: block;
    position: absolute;
}

.streamtunes-button-icon-upload-music::before {
    content: "\2B06";
    font-size: 1.12em;
    font-weight: 800;
}

.streamtunes-button-icon-creator::before {
    content: "\2736";
}

.streamtunes-button-icon-dashboard::before {
    content: "\25A6";
}

.streamtunes-button-icon-song-list::before {
    content: "\266A";
    font-size: 0.86em;
    font-weight: 700;
    left: 0.02em;
    line-height: 1;
    top: 50%;
    transform: translateY(-52%);
}

.streamtunes-button-icon-song-list::after {
    background:
        linear-gradient(currentColor, currentColor) 0 0 / 100% 1.5px no-repeat,
        linear-gradient(currentColor, currentColor) 0 50% / 100% 1.5px no-repeat,
        linear-gradient(currentColor, currentColor) 0 100% / 100% 1.5px no-repeat;
    content: "";
    height: 0.45em;
    left: 0.53em;
    top: 50%;
    transform: translateY(-50%);
    width: 0.42em;
}

.streamtunes-button-icon-earnings::before {
    content: "\25A5";
}

.streamtunes-button-icon-payout::before {
    content: "$";
    font-weight: 800;
}

.streamtunes-button-icon-save::before {
    content: "\2713";
    font-weight: 800;
}

.streamtunes-button-icon-tax-form::before {
    content: "\270E";
}

.streamtunes-button-icon-retry::before {
    content: "\21BA";
    font-weight: 800;
}

.streamtunes-button-icon-warning::before {
    content: "!";
    font-weight: 800;
}

.streamtunes-button-icon-cancel::before {
    content: "\00D7";
    font-weight: 800;
}

.loading-state,
.error-state {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 200px;
}

/* The grid's display/width live in the CARDS - structure block at the end of this file;
   its track size and gap are sizing, so they live in xl_app.css with the other breakpoints. */

/* Music Library Sticky Header */
.music-library-sticky-header {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 16px;
    padding: 12px 0;
}

.music-library-sticky-header h3 {
    margin: 0;
}

/* Filter Pill */
.filter-pill-container {
    position: relative;
}

.filter-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border-radius: 20px;
    border: 1px solid;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
    transition: background-color 0.2s, border-color 0.2s;
}

.filter-pill-clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: bold;
    cursor: pointer;
    line-height: 1;
}

.filter-pill-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    width: 20px;
    height: 20px;
    font-size: 11px;
    font-weight: bold;
}

.filter-pill-arrow {
    font-size: 10px;
}

.filter-pill-dropdown {
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 220px;
    max-height: 300px;
    overflow-y: auto;
    border-radius: 8px;
    border: 1px solid;
    margin-top: 4px;
    z-index: 20;
    padding: 8px 0;
}

.filter-pill-search-container {
    padding: 4px 12px 8px;
}

.filter-pill-search-input {
    width: 100%;
    padding: 6px 10px;
    border: 1px solid;
    border-radius: 6px;
    font-size: 14px;
    outline: none;
    box-sizing: border-box;
}

.filter-pill-dropdown-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 16px;
    cursor: pointer;
    font-size: 14px;
    transition: background-color 0.15s;
}

.filter-pill-dropdown-item input[type="checkbox"] {
    width: 16px;
    height: 16px;
    cursor: pointer;
}

.filter-pill-dropdown-compact {
    min-width: 180px;
}

.filter-pill-dropdown-option {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 10px 16px;
    border: 0;
    background: transparent;
    font-size: 14px;
    text-align: left;
    cursor: pointer;
    transition: background-color 0.15s;
}

.filter-pill-option-indicator {
    font-size: 13px;
    font-weight: 700;
}

.filter-pill-option-label {
    display: inline-flex;
    align-items: center;
}

.filter-pill-option-icon {
    flex-shrink: 0;
    pointer-events: none;
    user-select: none;
}

.player-art-shell {
    position: relative;
    display: inline-flex;
    border-radius: 8px;
    overflow: hidden;
}

.player-art-content {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    flex-shrink: 0;
}

.ai-content-badges {
    display: flex;
    align-items: center;
    justify-content: center;
}

.ai-content-badge-icon {
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.45));
    pointer-events: none;
    user-select: none;
}

/* Featured Music Header - flexbox layout for title and link */
.featured-music-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
}

.featured-music-header h2 {
    margin: 0;
}

.music-library-link {
    text-decoration: none;
    font-weight: 500;
    font-size: 14px;
    transition: color 0.2s ease;
}

.music-library-link:hover {
    text-decoration: underline;
}

/* Featured Music Carousel - horizontal scrolling container */
.featured-music-carousel {
    display: flex;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
}

/* Hide scrollbar on touch devices (mobile), show on hover-enabled devices (desktop) */
@media (hover: none) {
    .featured-music-carousel {
        scrollbar-width: none; /* Firefox */
        -ms-overflow-style: none; /* IE/Edge */
    }
    
    .featured-music-carousel::-webkit-scrollbar {
        display: none; /* Chrome/Safari */
    }
}

/* Desktop carousel inherits global scrollbar colors; just set a thinner height */
@media (hover: hover) {
    .featured-music-carousel::-webkit-scrollbar {
        height: 8px;
    }
}

.featured-music-carousel .music-card {
    flex: 0 0 auto;
    scroll-snap-align: start;
}

/* .music-card, .card-album-art and .card-album-art > img are defined once, in the
   "CARDS - structure" block at the end of this file. */

/* Lottie animation styles for songs without cover art */
.card-album-art-animation {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 4px;
    overflow: hidden;
    border: #06acfc 4px solid;
}

/* Static fallback art for songs with no cover art (Shared/SongArtPlaceholder.razor).
   Replaced the per-card <dotlottie-wc>, which ran one animation player - canvas, WASM
   renderer, rAF loop - for every art-less song on a page that renders the whole library
   at once. Sized as a percentage so this single rule serves all four surfaces that use
   it: the card grid, the playlist track rows and the two Syncfusion grid cells. Every
   one of those wrappers is flex-centred with a resolved size. The SVG paints with
   currentColor, so setting `color` here is what makes it follow the theme; the literal
   is a fallback for any context where the token is not in scope. */
.song-art-placeholder {
    width: 55%;
    height: 55%;
    color: var(--st-accent, #06acfc);
}

/* The JS-owned island the lazy card animation is mounted into. Empty until the card
   scrolls into view, so the glyph beneath is what a card shows the rest of the time, and
   .is-animating is toggled by MusicLibrary.razor.js rather than by a :has() rule so the
   swap does not depend on selector support.

   This must NOT be display:none while empty, however tempting. IntersectionObserver is
   watching this element, and an element with no layout box is never reported as
   intersecting - it could not become visible until it was filled, and could not be
   filled until it was seen as visible. It stays laid out and simply paints nothing while
   it has no children. pointer-events:none keeps it from shadowing .card-art-open, which
   overlays the same square. */
.card-lottie-host {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

/* Hidden rather than removed: the glyph keeps its space in the flex centring, so the
   animation lands in exactly the box the glyph occupied and nothing shifts. */
.card-album-art-animation.is-animating .song-art-placeholder {
    visibility: hidden;
}

.card-lottie-animation {
    width: 100%;
    height: 100%;
}

.album-art-animation,
.playlist-art-animation {
    width: 200px;
    height: 200px;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 8px;
    overflow: hidden;
}

.player-lottie-animation {
    width: 100%;
    height: 100%;
}

.card-actions {
    display: flex;
    justify-content: space-between !important;    
    align-items: center;
    margin-top: auto;
}

/* New layout for card actions with like/dislike buttons */
.card-actions-with-likes {
    display: flex;
    justify-content: space-between !important;
    gap: 6px;
    align-items: center;
    margin-top: 4px;
    overflow: hidden;
}

.action-column {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    flex-shrink: 0;
}

.action-button {
    /* Ensure consistent button sizing with like/dislike buttons */
    min-width: 40px;
    min-height: 40px;
}

.action-button .e-btn-icon {
    /* Ensure icon inside button is consistent size */
    font-size: 20px;
}

/* Vertical layout support for like/dislike buttons */
.like-dislike-container.vertical-layout {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
}

.like-dislike-actions.vertical {
    flex-direction: row;
    gap: 0.5rem;
}

.like-dislike-display.vertical {
    flex-direction: row;
    gap: 0.5rem;
}

.like-dislike-counts-vertical {
    display: flex;
    flex-direction: row;
    gap: 0.5rem;
    font-size: 0.75rem;
    color: inherit;
}

.card-mini-player {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-top: 4px;
}

/* A DEFAULT SIZE, because an inline SVG that is given none is not small - it falls back to the
   replaced-element default of 300x150 and draws the icon at that size. Every other rule for this
   class is scoped to a player container (.playlist-player-container, .song-player-container), so
   any other home for these controls - the lyric timing editor is one - had no size at all and drew
   enormous shapes. The scoped rules are more specific and still win where they apply, and the
   breakpoint files load after this one so their overrides do too. */
.control-icon {
    width: 18px;
    height: 18px;
}

.card-mini-controls-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.card-mini-controls {
    display: flex;
    align-items: center;
    gap: 4px;
}

.card-volume-controls {
    display: flex;
    align-items: center;
    gap: 2px;
}

.card-volume-button {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: transparent;
    border: none;
    cursor: pointer;
    display: flex;
    justify-content: center;
    align-items: center;
    transition: color 0.2s ease;
}

.card-volume-bar-container {
    width: 60px;
    height: 4px;
    border-radius: 2px;
    cursor: pointer;
    position: relative;
}

.card-volume-bar-container:hover {
    height: 6px;
}

.card-volume-bar {
    height: 100%;
    border-radius: 2px;
    position: relative;
    pointer-events: none;
}

.card-volume-thumb {
    position: absolute;
    right: -4px;
    top: 50%;
    transform: translateY(-50%);
    width: 8px;
    height: 8px;
    border-radius: 50%;
    opacity: 0;
    transition: opacity 0.2s ease;
}

.card-volume-bar-container:hover .card-volume-thumb {
    opacity: 1;
}

.card-progress-container {
    display: flex;
    align-items: center;
    gap: 6px;
}

.card-progress-bar-container {
    flex: 1;
    height: 4px;
    border-radius: 2px;
    cursor: pointer;
    position: relative;
}

.card-progress-bar-container:hover {
    height: 6px;
}

.card-progress-bar {
    height: 100%;
    border-radius: 2px;
    position: relative;
    pointer-events: none;
}

.card-progress-thumb {
    position: absolute;
    right: -4px;
    top: 50%;
    transform: translateY(-50%);
    width: 8px;
    height: 8px;
    border-radius: 50%;
    opacity: 0;
    transition: opacity 0.2s ease;
}

.card-progress-bar-container:hover .card-progress-thumb {
    opacity: 1;
}

.card-progress-bar-container.restricted {
    position: relative;
}

.card-progress-limit-marker {
    position: absolute;
    top: -2px;
    bottom: -2px;
    width: 3px;
    border-radius: 2px;
    z-index: 2;
}

.card-audio-hidden {
    display: none;
}

.music-filter-group {
    display: flex;
    gap: 16px;
    margin-bottom: 24px;
    flex-wrap: wrap;
}

.filter-option {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    transition: color 0.2s ease;
}

.filter-option input[type="radio"] {
    appearance: none;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    cursor: pointer;
    position: relative;
    transition: border-color 0.2s ease;
}

.filter-option input[type="radio"]:checked::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 10px;
    height: 10px;
    border-radius: 50%;
}



/* .card-song-title and .card-stream-count are defined once, in the "CARDS - structure" block
   at the end of this file. The negative margins that used to live here are gone with them - see
   the note there about Syncfusion's line-height. */

.card-stream-info {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
}

.card-stream-info span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.card-stream-actions {
    display: inline-flex;
    align-items: center;
    gap: 16px;
    margin-left: auto;
    flex-shrink: 0;
}

.card-stream-report {
    display: inline-flex;
    align-items: center;
}

.card-stream-actions .tip-heart-card,
.card-stream-actions .report-button {
    margin-left: 0;
}

/* .card-genre is defined once, in the "CARDS - structure" block at the end of this file. */

/* Playlists Page Layout */
.playlists-page-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.playlists-page-grid {
    display: grid;
}

.playlists-page-actions {
    position: absolute;
    top: 8px;
    right: 8px;
}

.playlists-page-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.playlists-page-card-header h5 {
    margin: 0;
}

.playlists-page-info p {
    margin: 5px 0;
}

.playlists-page-card-actions {
    display: flex;
}

.playlists-page-songs-section {
    border-radius: 4px;
}

.playlists-page-songs-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.playlists-page-songs-header > div {
    display: flex;
}

.playlists-page-songs-list {
    display: flex;
    flex-direction: column;
}

.playlists-page-song-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-radius: 4px;
}

.playlists-page-song-info {
    display: flex;
    flex-direction: column;
}

.playlists-page-song-title {
    font-weight: 500;
}

.playlists-page-add-song-list {
    overflow-y: auto;
    display: flex;
    flex-direction: column;
}

.playlists-page-add-song-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-radius: 4px;
}

.playlists-page-dialog-content {
    text-align: center;
}

.admin-messages-panel {
    border-radius: 12px;
    overflow: hidden;
}

.admin-messages-panel-header {
    padding: 1rem 1.25rem;
}

.admin-messages-panel-body {
    padding: 1.25rem;
}

.admin-messages-channel-section {
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    border-radius: 10px;
    padding: 0.9rem 1rem;
}

.admin-messages-channel-label {
    margin-bottom: 0.75rem;
}

.admin-messages-channel-options {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.admin-messages-grid-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* Recommended Playlist Section - Layout */
.recommended-section,
.recommended-playlist-section {
    margin-bottom: 2rem;
}

.recommended-section h3 {
    margin-bottom: 0.5rem;
}

.section-subtitle {
    margin-bottom: 1rem;
    font-size: 0.9rem;
    opacity: 0.8;
}

.playlist-card-link {
    text-decoration: none;
    color: inherit;
    cursor: pointer;
}

.playlist-description {
    font-size: 0.85rem;
    opacity: 0.7;
    margin-top: 0.5rem;
}

/* Like/Dislike Component - Layout and Structure */
.like-dislike-container {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.like-dislike-actions,
.like-dislike-display {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.like-display,
.dislike-display {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
    min-width: 40px;
    min-height: 40px;
    padding: 8px;
}

.like-button,
.dislike-button {
    border: none;
    transition: transform 0.2s ease, opacity 0.2s ease;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
    min-width: 40px;
    min-height: 40px;
    padding: 8px;
}

.like-button:hover,
.dislike-button:hover {
    transform: scale(1.05);
}

.like-button:disabled,
.dislike-button:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.action-icon,
.display-icon {
    width: 20px;
    height: 20px;
}

.count-text {
    font-size: 0.875rem;
    font-weight: 500;
}

/* Card row holding the AI disclosure badges (left) and the like/dislike buttons (right).
   Lives outside the is-playing conditional, so the buttons stay available during playback. */
.card-ai-actions-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* The -8px/+10px margins that used to be here were cancelling Syncfusion's line-height,
       which the card now neutralises at source. See "CARDS - structure". */
    min-height: 24px;
}

.card-ai-actions-row .like-dislike-container {
    /* Keeps the buttons right-aligned on songs with no AI badges to sit opposite */
    margin-left: auto;
    gap: 0.5rem;
}

.card-ai-actions-row .like-dislike-actions {
    gap: 0.25rem;
}

/* ~40% smaller than the default 40px / 20px like/dislike buttons so they sit
   comfortably alongside the 28px AI badges. Scoped to the card row only -
   the song/playlist players keep the full-size buttons. */
.card-ai-actions-row .like-button,
.card-ai-actions-row .dislike-button {
    min-width: 24px;
    min-height: 24px;
    padding: 4px;
    gap: 0.125rem;
}

.card-ai-actions-row .action-icon {
    width: 12px;
    height: 12px;
}

.card-ai-actions-row .count-text {
    font-size: 0.75rem;
}


/* Facebook Share Button Styles */
.fb-share-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background-color: #3b5998;
    color: white;
    text-decoration: none;
    border-radius: 4px;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-weight: 700;
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    border: none;
    transition: background-color 0.2s ease;
}

.fb-share-btn:hover {
    background-color: #2d4373;
    color: white;
    text-decoration: none;
}

.fb-share-btn:active {
    background-color: #263961;
}

.fb-share-btn .fb-logo-box {
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #334b82;
    border-radius: 3px;
    color: white;
    font-weight: 700;
    flex-shrink: 0;
}

.fb-share-btn .share-text {
    white-space: nowrap;
}

/* Default size - used in player controls */
.fb-share-btn {
    padding: 6px 12px;
    gap: 6px;
}

.fb-share-btn .fb-logo-box {
    width: 22px;
    height: 22px;
    font-size: 16px;
}

/* Small size for cards */
.fb-share-btn.share-button-card {
    padding: 4px 10px;
    gap: 5px;
    font-size: 13px;
}

.fb-share-btn.share-button-card .fb-logo-box {
    width: 18px;
    height: 18px;
    font-size: 13px;
}

/* Action controls variant */
.fb-share-btn.share-button-action {
    padding: 6px 12px;
    gap: 6px;
}

/* .card-bottom-row and its share button are defined once, in the "CARDS - structure" block at
   the end of this file. The cart button the old comment here mentioned was removed by migration
   20260108000000_RemoveCartAndOwnedSongsTables. */

/* Home Page - Hero Section Layout */
.hero-section {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: nowrap;
    gap: 2rem;
}

.hero-content {
    flex: 1 1 0;
    min-width: 0;
    max-width: 34rem;
}

.hero-title {
    font-weight: 900;
    line-height: 1.1;
    margin: 0;
}

.hero-subtitle {
    line-height: 1.6;
    max-width: 32rem;
}

.hero-cta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-start;
    gap: 1rem;
}

.hero-visual {
    flex: 0 0 auto;
    display: flex;
    justify-content: center;
    align-items: center;
    margin-left: auto;
}

.hero-animation {
    animation: float 3s ease-in-out infinite;
    display: block;
}

@keyframes float {
    0%, 100% { transform: translateY(0px); }
    50% { transform: translateY(-10px); }
}

/* CTA Buttons */
.cta-primary,
.cta-secondary,
.cta-button {
    border-radius: 500px;
    font-weight: 700;
    text-transform: none;
    cursor: pointer;
    transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
}

.cta-primary:hover,
.cta-secondary:hover,
.cta-button:hover {
    transform: scale(1.04);
}

/* Section Layout */
.section-title {
    font-weight: 700;
    margin: 0;
}

.section-subtitle {
    line-height: 1.6;
}

/* Features Section Layout */
.features-section {
    text-align: center;
}

.features-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

.feature-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    border-radius: 16px;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.feature-card:hover {
    transform: translateY(-5px);
}

.feature-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
}

.feature-card h3 {
    font-weight: 600;
    margin: 0;
}

.feature-card p {
    line-height: 1.6;
}

/* User Playlists Section */
.user-playlists-section {
    display: flex;
    flex-direction: column;
}

.loading-text {
    text-align: center;
}

/* CTA Split Section Layout */
.cta-split-section {
    position: relative;
    overflow: hidden;
}

.cta-split-container {
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: stretch;
}

.cta-card {
    flex: 1;
    border-radius: 24px;
    text-align: center;
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.cta-card:hover {
    transform: translateY(-4px);
}

.cta-icon {
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 50%;
}

.cta-title {
    font-weight: 700;
    margin: 0;
}

.cta-description {
    text-align: center;
}

.cta-benefits {
    list-style: none;
    padding: 0;
    margin: 0 auto;
    text-align: left;
}

.cta-benefits li {
    display: flex;
    align-items: flex-start;
    font-weight: 500;
}

.cta-benefits li svg {
    flex-shrink: 0;
}

.cta-price-highlight {
    text-align: center;
    font-weight: 500;
}

.cta-price-highlight .price {
    font-weight: 900;
}

.cta-auth-text {
    text-align: center;
    font-weight: 600;
}

.cta-button-group {
    display: flex;
    flex-direction: row;
    gap: 1rem;
    justify-content: center;
    align-items: center;
}

.cta-button {
    align-self: center;
}

/* Subscribe CTA Dialog */
.subscribe-cta-dialog .e-dlg-content {
    padding: 0 !important;
}

.subscribe-cta-dialog .e-dlg-header-content {
    position: absolute;
    right: 0;
    top: 0;
    z-index: 1;
    border-bottom: none !important;
}

.subscribe-cta-dialog-content {
    text-align: center;
    padding: 2.5rem 2rem;
    border-radius: 8px;
}

.subscribe-cta-dialog-content .cta-title {
    font-size: 1.5rem;
    font-weight: 700;
    margin-bottom: 0.75rem;
}

.subscribe-cta-dialog-content .cta-price-highlight {
    font-size: 1.1rem;
    font-weight: 500;
    margin-bottom: 1.25rem;
}

.subscribe-cta-dialog-content .cta-price-highlight .price {
    font-size: 1.75rem;
    font-weight: 900;
}

.subscribe-cta-dialog-content .cta-auth-text {
    font-weight: 600;
    margin-bottom: 1rem;
}

.subscribe-cta-dialog-content .cta-button-group {
    display: flex;
    gap: 1rem;
    justify-content: center;
}

.subscribe-cta-dialog-content .cta-button {
    min-width: 100px;
}

.test-server-banner {
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 0.25rem;
    flex-wrap: wrap;
    padding: 8px 12px;
    border-bottom: 1px solid;
    box-sizing: border-box;
    text-align: center;
}

.test-server-banner-label,
.test-server-banner-link {
    font-size: 0.85rem;
    font-weight: 600;
}

.test-server-banner-link {
    text-decoration: underline;
}

.test-server-banner-link:hover,
.test-server-banner-link:focus {
    text-decoration-thickness: 2px;
}

/* Planned Maintenance Notice */
.maintenance-banner {
    width: 100%;
    display: flex;
    justify-content: center;
    padding: 6px 12px;
    border-bottom: 1px solid;
    box-sizing: border-box;
}

.maintenance-warning-label {
    font-size: 0.85rem;
    font-weight: 600;
    text-align: center;
}

.maintenance-notice-dialog .e-dlg-content {
    padding: 1.5rem !important;
}

.maintenance-dialog-header {
    font-size: 1.1rem;
    font-weight: 700;
}

.maintenance-dialog-content {
    text-align: center;
}

.maintenance-dialog-content p {
    margin-bottom: 0.75rem;
}

.maintenance-dialog-times {
    font-size: 1.05rem;
    padding: 10px 16px;
    border-radius: 6px;
    border: 1px solid;
    display: inline-block;
    margin: 0.5rem 0;
}

.maintenance-notice-dialog .e-footer-content {
    display: flex;
    justify-content: center;
    padding: 1rem !important;
}

/* Liked Songs Playlist Card */
.liked-songs-playlist-card {
    border-left: 4px solid #e91e63;
}

/* Terms of Use Page Styles */
.terms-content {
    max-width: 900px;
    margin: 0 auto;
    line-height: 1.8;
}

.learn-more-content {
    max-width: none;
    margin: 0;
}

.terms-content h1 {
    margin-bottom: 1.5rem;
}

.terms-content h2 {
    margin-top: 2rem;
    margin-bottom: 1rem;
    font-weight: 600;
}

.terms-content h3 {
    margin-top: 1.5rem;
    margin-bottom: 0.75rem;
    font-weight: 600;
}

.terms-content p {
    margin-bottom: 1rem;
}

.terms-content ul {
    margin-bottom: 1rem;
    padding-left: 2rem;
}

.terms-content li {
    margin-bottom: 0.5rem;
}

.terms-content section {
    margin-bottom: 2rem;
}

/* Privacy Policy and Terms content styling (same as terms-content) */
.privacy-content p {
    margin-bottom: 1rem;
}

.privacy-content ul {
    margin-bottom: 1rem;
    padding-left: 2rem;
}

.privacy-content li {
    margin-bottom: 0.5rem;
}

.privacy-content section {
    margin-bottom: 2rem;
}

/* Legal dialog styling for Register page */
.legal-dialog .e-dlg-content {
    padding: 0;
}

.legal-dialog-content {
    max-height: calc(80vh - 120px);
    overflow-y: auto;
    padding: 20px;
}

/* Legal agreement checkboxes on Register page */
.legal-agreements {
    margin-top: 1rem;
    padding: 1rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 0.375rem;
}

.google-auth-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.google-auth-icon {
    flex: 0 0 auto;
}

.legal-text {
    font-size: 0.9rem;
    line-height: 1.5;
}

.legal-link {
    color: var(--bs-link-color);
    text-decoration: underline;
    cursor: pointer;
}

.legal-link:hover {
    color: var(--bs-link-hover-color);
}

/* Legal iframe for displaying Terms and Privacy Policy in dialogs */
.legal-iframe {
    width: 100%;
    height: calc(80vh - 120px);
    border: none;
}

/* Dashboard layout styles */
.dashboard-chart-section {
    display: flex;
    flex-direction: column;
}

.dashboard-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 1rem;
}

.dashboard-control-group {
    display: flex;
    flex-direction: column;
}

.dashboard-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.5rem;
}

.dashboard-timezone-note {
    margin-top: 0.25rem;
    margin-bottom: 0.5rem;
}

/* Tip Dialog Styles */
.tip-dialog .e-dlg-content {
    padding: 0;
}

.tip-dialog-content {
    padding: 24px;
    text-align: center;
}

.tip-title {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin-bottom: 20px;
}

.tip-heart-icon {
    width: 24px;
    height: 24px;
}

.tip-preset-buttons {
    display: flex;
    gap: 12px;
    justify-content: center;
    margin-bottom: 16px;
}

.tip-amount-btn {
    padding: 12px 24px;
    border: 2px solid #1b6ec2;
    border-radius: 8px;
    background: transparent;
    color: #1b6ec2;
    font-size: 18px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s;
    min-width: 70px;
}

.tip-amount-btn:hover {
    background: #1b6ec2;
    color: #fff;
}

.tip-custom-btn {
    display: block;
    width: 100%;
    padding: 10px;
    border: 1px solid #dee2e6;
    border-radius: 8px;
    background: transparent;
    cursor: pointer;
    transition: all 0.2s;
    margin-bottom: 16px;
}

.tip-custom-btn:hover {
    border-color: #1b6ec2;
    color: #1b6ec2;
}

.tip-custom-input {
    display: flex;
    gap: 8px;
    margin-bottom: 16px;
    align-items: stretch;
}

.tip-custom-input .input-group {
    flex: 1;
}

.tip-submit-btn {
    padding: 8px 20px;
    border: none;
    border-radius: 8px;
    background: #1b6ec2;
    color: #fff;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: opacity 0.2s;
}

.tip-submit-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.tip-submit-btn:hover:not(:disabled) {
    opacity: 0.9;
}

.tip-info {
    margin-top: 12px;
    font-size: 12px;
    opacity: 0.6;
}

.tip-error {
    text-align: left;
    margin-bottom: 16px;
}

.tip-success {
    padding: 20px 0;
}

.tip-success-icon {
    width: 48px;
    height: 48px;
    margin-bottom: 12px;
}

.tip-success h3 {
    margin-bottom: 8px;
}

.tip-processing {
    padding: 30px 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
}

.tip-button.control-button {
    position: relative;
}

/* Tip Heart Button - used next to like/dislike and in track lists */
.tip-heart-button {
    background: none;
    border: none;
    cursor: pointer;
    padding: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.2s ease;
}

.tip-heart-button:hover {
    transform: scale(1.2);
}

.tip-heart-button .tip-heart-icon {
    width: 20px;
    height: 20px;
}

/* Tip heart in song card */
.tip-heart-card {
    margin-left: 4px;
}

.tip-heart-card .tip-heart-icon {
    width: 24px;
    height: 24px;
}

/* Tip Dashboard Styles */
.tip-dashboard-section {
    margin-top: 1.5rem;
}

.tip-buckets {
    display: flex;
    gap: 16px;
    margin-bottom: 20px;
    flex-wrap: wrap;
}

.tip-bucket {
    flex: 1;
    min-width: 150px;
    padding: 16px;
    border-radius: 8px;
    text-align: center;
}

.tip-bucket h5 {
    margin-bottom: 4px;
    font-size: 14px;
    opacity: 0.7;
}

.tip-bucket .tip-bucket-amount {
    font-size: 24px;
    font-weight: 700;
}

.tip-bucket .tip-bucket-count {
    font-size: 12px;
    opacity: 0.6;
}

/* Persona display styles */
.persona-section {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    justify-content: center;
    text-align: center;
    flex-direction: column;
    align-items: center;
    margin-bottom: 16px;
}

.persona-image {
    width: 100px;
    height: 100px;
    object-fit: cover;
    border-radius: 12%;
}

.persona-image-sm {
    width: 60px;
    height: 60px;
}

.persona-details {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.persona-bio {
    font-size: 0.9rem;
    margin: 0;
    opacity: 0.8;
    max-width: 400px;
    max-height: 4.5em;
    overflow-y: auto;
    line-height: 1.5;
}

.persona-website {
    font-size: 0.85rem;
    opacity: 0.7;
}

.playlist-persona-section {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 8px 16px;
    margin-bottom: 8px;
}

.card-persona-image {
    width: 40px;
    height: 40px;
    border-radius: 12%;
    object-fit: cover;
    margin-right: 4px;
    vertical-align: middle;
}

/* Artist / genre link colours — applied at all breakpoints */
.card-artist-name a {
    text-decoration: none;
}

.card-artist-name a:hover {
    text-decoration: underline;
}

.card-genre a {
    text-decoration: none;
}

.card-genre a:hover {
    text-decoration: underline;
}

.artist-link {
    text-decoration: none;
}

.artist-link:hover {
    text-decoration: underline;
}

.genre-link {
    text-decoration: none;
}

.genre-link:hover {
    text-decoration: underline;
}

.non-square-warning-badge {
    position: absolute;
    top: -5px;
    right: -5px;
    font-size: 0.6rem;
}

@media print {
    html,
    body,
    .page,
    main,
    .content {
        height: auto !important;
        min-height: 0 !important;
        max-height: none !important;
        overflow: visible !important;
        position: static !important;
    }

    .page {
        display: block !important;
    }

    main {
        display: block !important;
        flex: none !important;
    }

    .content {
        padding: 0 !important;
    }

    .container {
        width: 100% !important;
        max-width: none !important;
    }

    sf-appbar,
    .e-appbar,
    .e-sidebar,
    .mobile-app-install-banner,
    .test-server-banner,
    .maintenance-banner,
    .e-toast-container,
    .e-dlg-overlay,
    .e-dialog,
    #blazor-error-ui {
        display: none !important;
    }

    button,
    .e-btn {
        display: none !important;
    }

}

/* ---------------------------------------------------------------------------
   Lyric timing dialog (Components/Shared/LyricsEditorDialog.razor)
   Structure and layout only. Colours live in light.css / dark.css and sizing in
   the breakpoint files, per AGENTS.md.
   --------------------------------------------------------------------------- */
.lyrics-dialog-header {
    display: flex;
    align-items: center;
}

.lyrics-dialog-field {
    display: flex;
    flex-direction: column;
}

/* The textarea is the point of the dialog, so it gets the room. Resizable
   vertically only - horizontal resize breaks out of the modal. */
.lyrics-dialog-textarea textarea {
    resize: vertical;
}

.lyrics-dialog-counter {
    display: flex;
    justify-content: flex-end;
}

.lyrics-dialog-progress {
    display: flex;
    flex-direction: column;
}

.lyrics-dialog-footer {
    display: flex;
    justify-content: flex-end;
    align-items: center;
}

.lyrics-dialog-errors {
    margin: 0;
    padding-left: 1.25rem;
}

/* ============================================================
   Scrolling lyrics
   ============================================================
   Layout and animation only; colours live in light.css / dark.css.

   The box is FIXED HEIGHT and CLIPPED, and the track inside it is moved with a transform. It is
   never scrolled: scrollIntoView would drag every scrollable ancestor along with it - the library
   grid, the player page - every time a song moved to the next line.

   The track's vertical padding is half the box, so the first and last lines can reach the middle.
   Without it the opening line sits at the top until the song is a few lines in. */

.lyrics-scroller {
    position: relative;
    overflow: hidden;
    width: 100%;
    height: 100%;
    border-radius: 4px;
}

.lyrics-scroller-track {
    padding-block: 50%;
    transform: translate3d(0, 0, 0);
    transition: transform 320ms ease-out;
    will-change: transform;
}

.lyrics-scroller-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    padding: 12px;
    text-align: center;
}

.lyrics-line {
    padding: 2px 10px;
    line-height: 1.35;
    transition: opacity 200ms ease, transform 200ms ease;
}

.lyrics-word {
    transition: color 120ms linear;
}

/* The sung word. Deliberately a colour change and nothing that reflows - a transform or a font-size
   change here would move every following word on the line, sixty times a second. */
.lyrics-word--active {
    font-weight: 600;
}

.lyrics-word--selected {
    border-radius: 3px;
    outline-offset: 1px;
}

/* Section markers and blank lines. Present because they are part of how the artist laid the song
   out, but never highlighted and never scrolled to - they have no time. */
.lyrics-line--untimed {
    font-style: italic;
    opacity: 0.55;
}

/* --- Card: smallest type, in place of a 210px cover --- */
.lyrics-scroller--card {
    font-size: 11px;
}

.lyrics-scroller--card .lyrics-line {
    padding: 1px 6px;
}

/* --- Player page: in place of the hero art --- */
.lyrics-scroller--player {
    font-size: 14px;
}

/* --- Editor: the creator is reading and clicking these, so they get the most room --- */
.lyrics-scroller--editor {
    font-size: 17px;

    /* Turns this box into a query container so the track below can ask for half its HEIGHT.
       See the track rule for why a percentage cannot do that. Scoped to the editor rather than
       applied to .lyrics-scroller, because container-type: size also makes the box ignore its
       contents when sizing itself - safe here, where .lyrics-editor-scroller gives it a definite
       420px, but not something to impose on the card and player without checking each one. */
    container-type: size;
}

/* HALF THE SCROLLER'S HEIGHT, so the first line can sit in the middle with nothing above it.
   The shared rule asks for `padding-block: 50%`, and percentage padding resolves against the
   container's INLINE size - its width - never its height. That is close enough to right while the
   scroller is roughly square, which it is on a card and on the player page where it stands in for
   cover art. The editor is the one home where it is not: 420px tall and as wide as the page, so
   50% became some 700px of padding inside a 420px clipped box and pushed every line out of sight.
   cqh is the unit that means what this always intended. */
.lyrics-scroller--editor .lyrics-scroller-track {
    padding-block: 50cqh;
}

.lyrics-scroller--editor .lyrics-line {
    padding: 4px 12px;
}

.lyrics-scroller--editor .lyrics-word {
    cursor: pointer;
    padding: 0 1px;
}

/* The line's span, down the left edge of the editor. Tabular figures and a fixed width so the
   numbers form a column the eye can scan for the odd one out, and rendered even when empty (an
   untimed marker has no span) so every line's words still start at the same x. Not selectable:
   dragging across the lyrics to read them should not pick up a column of timestamps. */
.lyrics-scroller--editor .lyrics-line-time {
    display: inline-block;
    width: 9.5em;
    margin-right: 8px;
    font-family: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.66em;
    font-variant-numeric: tabular-nums;
    text-align: right;
    opacity: 0.55;
    cursor: default;
    user-select: none;
}

/* Somebody who has asked for less motion should not be given a panel that glides every few seconds.
   The highlight still moves; only the easing stops. */
@media (prefers-reduced-motion: reduce) {
    .lyrics-scroller-track {
        transition-duration: 0s;
    }

    .lyrics-line {
        transition-duration: 0s;
    }
}

/* The scroller sitting where the cover art was. Absolute inside .player-art-shell, which is already
   position:relative and overflow:hidden, so the swap costs no layout change at all. */
.lyrics-scroller-overlay {
    position: absolute;
    inset: 0;
    z-index: 2;
}

/* Kept in the DOM rather than removed, so the animation loop and element cache survive a toggle.
   visibility:hidden rather than display:none for the same reason - offsetTop has to keep working,
   or the first line after a toggle would centre against a zero-height box. */
.is-hidden {
    visibility: hidden;
    pointer-events: none;
}

/*
 * THE CARD ARTWORK IS THE WAY INTO THE SONG PLAYER.
 *
 * A transparent button laid over the whole art square, replacing the eyeball "view" button that
 * used to sit beside Play. It is a SIBLING of the art faces rather than a wrapper around them:
 * .card-album-art > img is a direct-child selector, and a wrapper would strip the image of all
 * sizing. Being a sibling of the lyrics toggle rather than its ancestor is also what saves the
 * toggle from needing anything more than the stopPropagation it already had.
 *
 * The z-index ladder inside .card-album-art, which is position:relative with z-index:auto and so
 * creates no stacking context of its own:
 *
 *     -  img / Lottie                     (static)
 *     2  .lyrics-scroller-overlay
 *     3  .card-art-open                   <- this
 *     4  .card-album-art::after           (the scrim, pointer-events:none, phone only)
 *     4  .lyrics-toggle-button
 *     6  .card-ai-actions-row             (a SIBLING of .card-album-art, overlaid at <576px)
 *
 * It deliberately covers the lyrics panel too - tapping the lyrics opens the player, which is the
 * requested behaviour. The cost is that the card lyrics can no longer be scrolled by hand; they
 * auto-scroll with the audio clock, as they already did.
 */
.card-art-open {
    position: absolute;
    inset: 0;
    z-index: 3;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
}

/* Negative offset because .card-album-art is overflow:hidden - an outward ring would be clipped.
   Matches the toggle's ring below, for the same reason: it is drawn over a photograph. */
.card-art-open:focus-visible {
    outline: 2px solid #fff;
    outline-offset: -3px;
}

/*
 * The CARD's lyrics/art toggle.
 *
 * SCOPED TO .card-album-art on purpose. Three components render .lyrics-toggle-button - the two
 * players and MusicLibrary - and only the card's is a control pinned to the corner of artwork;
 * in the players it is a segment in a pill. It used to be unscoped, which is why the players
 * each carry a long explicit undo block. Scoping it here means it cannot reach them at all.
 * MusicLibrary.razor puts the button INSIDE .card-album-art, so this matches exactly the one
 * instance that wants it.
 *
 * It sits on top of a photograph, so it cannot use the card's own colours: at 28px with
 * border:none, no background and opacity .75 it was invisible against dark artwork. A fixed
 * dark scrim and a white icon read on any cover in either theme - hence literals, not tokens.
 */
.card-album-art .lyrics-toggle-button {
    position: absolute;
    right: 8px;
    bottom: 8px;
    z-index: 4;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, .55);
    border-radius: 50%;
    background: rgba(8, 12, 20, .62);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .45);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    opacity: 1;
    transition: background-color var(--st-dur) var(--st-ease), border-color var(--st-dur) var(--st-ease), transform var(--st-dur) var(--st-ease);
}

.card-album-art .lyrics-toggle-button:hover {
    background: rgba(8, 12, 20, .85);
    border-color: #fff;
    transform: scale(1.06);
}

.card-album-art .lyrics-toggle-button:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}

/* The icon is drawn with fill="currentColor", so the white comes from the button above. */
.card-album-art .lyrics-toggle-icon {
    width: 17px;
    height: 17px;
}

.lyrics-toggle-icon {
    width: 15px;
    height: 15px;
}

@media (prefers-reduced-motion: reduce) {
    .card-album-art .lyrics-toggle-button,
    .card-album-art .lyrics-toggle-button:hover {
        transition: none;
        transform: none;
    }
}

/* ============================================================
   Creator lyric timing editor
   ============================================================ */

.lyrics-editor-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 12px;
}

/* The one-paragraph orientation that replaced the long help block at the top. The two ways of
   actually fixing a timing now document themselves inside the panel that offers them. */
.lyrics-editor-intro {
    max-width: 80ch;
    margin-bottom: 16px;
}

/* Names each option, so a creator can see there are two of them without opening either. Tighter
   than a stock h5 because these sit at the top of a 320px column, not on a page of their own. */
.lyrics-editor-panel-title {
    font-size: 1rem;
    font-weight: 600;
    margin: 0 0 8px 0;
}

.lyrics-editor-help {
    margin-bottom: 16px;
    border-radius: 6px;
    padding: 10px 14px;
}

/* Inside an option panel the help is a nested disclosure rather than a block of its own, so it is
   indented less and does not repeat the padding the panel already has. */
.lyrics-editor-record .lyrics-editor-help,
.lyrics-editor-selection .lyrics-editor-help {
    margin-bottom: 12px;
    padding: 0;
}

.lyrics-editor-record .lyrics-editor-help summary,
.lyrics-editor-selection .lyrics-editor-help summary {
    font-size: 0.875rem;
    font-weight: 500;
    opacity: 0.85;
}

/* 70ch is meaningless in a 320px column - it just lets the text set its own width and overflow. */
.lyrics-editor-record .lyrics-editor-help-body,
.lyrics-editor-selection .lyrics-editor-help-body {
    max-width: none;
    font-size: 0.875rem;
}

.lyrics-editor-help summary {
    cursor: pointer;
    font-weight: 600;
}

.lyrics-editor-help-body {
    padding-top: 10px;
    max-width: 70ch;
}

.lyrics-editor-help-body ol {
    padding-left: 1.25rem;
}

.lyrics-editor-help-body li {
    margin-bottom: 8px;
}

/* Scroller on the left, controls on the right, stacking on narrow screens. The scroller gets a
   fixed height because that is what makes centring meaningful - it is the box the current line is
   centred within. */
.lyrics-editor-body {
    display: flex;
    gap: 20px;
    align-items: flex-start;
    flex-wrap: wrap;
}

.lyrics-editor-scroller {
    flex: 1 1 420px;
    height: 420px;
    min-width: 280px;
}

.lyrics-editor-side {
    flex: 0 1 320px;
    min-width: 260px;
}

/* The transport, full width and above the body. It lived in the 320px side column until the seek bar
   turned out to be the least precise control on a page built around seeking: at that width a
   four-minute song gets about 0.75 seconds per pixel. Sticky because tapping means scrolling the
   words while keeping play and seek within reach, and an OPAQUE background for the same reason -
   the lyrics scroll underneath it. */
.lyrics-editor-transport {
    position: sticky;
    top: 0;
    z-index: 5;
    width: 100%;
    margin-bottom: 16px;
    padding: 10px 12px;
    border-radius: 8px;
    background: var(--st-surface);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}

.lyrics-editor-rates,
.lyrics-editor-seek {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 10px;
}

/* The selected word's own span. Tabular figures because these two numbers are read against each
   other and against the line times in the panel to the left, and digits that change width make
   that comparison harder than it needs to be. */
/* Quieter than the button's own label, because it is naming the shortcut rather than a second
   thing the button does. */
.lyrics-editor-tap-alt {
    font-size: 0.75rem;
    font-weight: 400;
    opacity: 0.85;
}

.lyrics-editor-span {
    margin-top: 10px;
}

.lyrics-editor-span-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 6px;
    margin-bottom: 2px;
}

.lyrics-editor-span-label {
    font-size: 0.8rem;
    opacity: 0.75;
}

.lyrics-editor-span-value {
    font-family: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: 0.95rem;
}

/* Six buttons sharing the row equally, so the pair for a given step sits the same distance either
   side of centre and the row can be aimed at without reading it. */
.lyrics-editor-span-steps {
    display: flex;
    gap: 3px;
    margin-bottom: 8px;
}

.lyrics-editor-span-steps .btn {
    flex: 1 1 0;
    padding-inline: 0;
    font-variant-numeric: tabular-nums;
    font-size: 0.75rem;
}

.lyrics-editor-span-length {
    font-size: 0.8rem;
    opacity: 0.7;
    margin-top: 2px;
}

.lyrics-editor-selection {
    margin-top: 16px;
    padding: 12px;
    border-radius: 6px;
}

.lyrics-editor-selected-word {
    font-size: 15px;
}

.lyrics-editor-actions {
    margin-top: 16px;
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}

.lyrics-editor-record {
    margin-top: 16px;
    padding: 12px;
    border-radius: 6px;
    transition: box-shadow 200ms ease;
}

.lyrics-editor-next-line {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 15px;
}

/* =========================================================================
   SONG PLAYER - "Marquee" layout
   =========================================================================
   Structure, layout, animation and transition only. Colour lives in
   light.css / dark.css (identical in both - see the note there). Sizing and
   spacing live in xl/lg/md/sm/xs_app.css. This follows the property-routing
   rule in AGENTS.md; the token values themselves are in tokens.css.

   Everything is scoped under .song-player-container on purpose. The playlist
   player still ships its own .razor.css and shares class names such as
   .player-bar and .control-button with this page, so an unscoped rule here
   would redesign a page that has not been redesigned yet.

   Replaces Components/Players/SongPlayerInteractive.razor.css, now deleted:
   it hard-coded Spotify's palette, carried dead cart/ownership styles, and
   being scoped CSS it outranked the theme sheets, so the player could never
   be themed at all.
   ========================================================================= */

.song-player-container {
    display: flex;
    flex-direction: column;
    /* dvh, not vh: on mobile the browser chrome makes 100vh taller than the
       visible viewport, which used to push the transport off-screen and clip
       the page rather than scroll it. */
    min-height: calc(100dvh - 66px);
    position: relative;
    font-family: var(--st-font);
}

.song-player-container .loading-state,
.song-player-container .error-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    flex: 1;
}

.song-player-container .loading-pulse {
    border-radius: 50%;
    animation: songPlayerPulse 1.4s ease-in-out infinite;
}

@keyframes songPlayerPulse {
    0%, 100% { transform: scale(.82); opacity: .45; }
    50% { transform: scale(1); opacity: 1; }
}

/* ---- Hero band -------------------------------------------------------- */

.song-player-container .song-header {
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: flex-end;
}

.song-player-container .song-hero-backdrop {
    position: absolute;
    inset: -60px;
    z-index: 0;
    /* The artwork, blown up and blurred into an ambient wash. A deliberately
       tiny rendition is requested for it - see CoverArtSizes.PlayerBackdrop. */
    filter: blur(52px);
    transform: scale(1.25);
    pointer-events: none;
}

.song-player-container .song-hero-backdrop-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.song-player-container .song-hero-scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
}

.song-player-container .song-hero-inner {
    position: relative;
    z-index: 2;
    display: grid;
    grid-template-areas: "art text actions";
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: flex-end;
    width: 100%;
    box-sizing: border-box;
}

.song-player-container .album-art-container { grid-area: art; }
.song-player-container .song-text-details { grid-area: text; min-width: 0; }
.song-player-container .action-controls { grid-area: actions; }

/* .player-art-shell must stay relative + overflow:hidden - the art is clipped
   to its rounded corners by it. */
.song-player-container .player-art-shell {
    position: relative;
    overflow: hidden;
    flex: 0 0 auto;
}

.song-player-container .song-hero-art {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.song-player-container .song-tag-row,
.song-player-container .song-meta-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
}

.song-player-container .song-chip {
    display: inline-flex;
    align-items: center;
    border-radius: var(--st-radius-pill);
    text-transform: uppercase;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color var(--st-dur) var(--st-ease), border-color var(--st-dur) var(--st-ease);
}

.song-player-container .song-title {
    margin: 0;
    text-wrap: balance;
}

.song-player-container .stream-count-info,
.song-player-container .album-track-info,
.song-player-container .subscription-badge,
.song-player-container .preview-label,
.song-player-container .artist-name-info {
    display: inline-flex;
    align-items: center;
    margin: 0;
}

.song-player-container .action-controls {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
}

.song-player-container .song-action-cluster {
    display: flex;
    align-items: center;
}

.song-player-container .play-button-large {
    border-radius: 50%;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    transition: transform var(--st-dur-fast) var(--st-ease), box-shadow var(--st-dur) var(--st-ease);
}

.song-player-container .play-button-large:hover { transform: scale(1.05); }
.song-player-container .play-button-large:active { transform: scale(.96); }

/* ---- Stage: the lyrics/art panel, and the artist beside it ------------ */

.song-player-container .song-stage {
    display: grid;
    align-items: start;
    flex: 1;
    min-height: 0;
}

.song-player-container .song-panel {
    border-radius: var(--st-radius-lg);
    border: 1px solid var(--st-player-line);
    box-sizing: border-box;
    position: relative;
    overflow: hidden;
}

.song-player-container .song-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.song-player-container .song-panel-caption {
    text-transform: uppercase;
    display: block;
}

.song-player-container .song-panel-switch {
    display: flex;
    border-radius: var(--st-radius-pill);
    border: 1px solid var(--st-player-line);
}

.song-player-container .lyrics-toggle-button,
.song-player-container .art-toggle-button {
    /* This block used to be an explicit undo of an UNSCOPED .lyrics-toggle-button - a 28px
       circle pinned absolutely to the corner of the artwork - which otherwise rendered the
       Lyrics segment as a clipped bubble in the panel corner. That rule is now scoped to
       .card-album-art, so it can no longer reach here. These declarations are kept as the
       segment's own definition, not as a defence. */
    position: static;
    width: auto;
    height: auto;
    opacity: 1;
    z-index: auto;
    display: inline-flex;
    align-items: center;
    border: none;
    border-radius: var(--st-radius-pill);
    cursor: pointer;
    background: transparent;
    white-space: nowrap;
    transition: background-color var(--st-dur) var(--st-ease), color var(--st-dur) var(--st-ease);
}

.song-player-container .lyrics-toggle-button:hover,
.song-player-container .art-toggle-button:hover {
    transform: none;
}

/* The scroller fills the stage panel rather than sitting in a box of its own. */
.song-player-container .lyrics-scroller {
    border-radius: 0;
}

.song-player-container .song-panel-body {
    position: relative;
}

/* The scroller is absolutely positioned into this box by the shared
   .lyrics-scroller-overlay rule, so the box needs a height of its own. */
.song-player-container .song-panel-art {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
}

.song-player-container .song-stage-art {
    display: block;
    max-width: 100%;
    max-height: 100%;
    border-radius: var(--st-radius-md);
    object-fit: contain;
}

.song-player-container .song-artist-panel {
    display: flex;
    flex-direction: column;
}

.song-player-container .persona-identity {
    display: flex;
    align-items: center;
}

/*
 * A persona bio runs to 2,000 characters and a creator bio to 1,000 - several paragraphs.
 * The unscoped .persona-bio above caps it at 4.5em with its OWN scrollbar and dims it to
 * opacity .8; all three have to be undone explicitly, because this rule setting only
 * font-size and margin is exactly how those leaked in in the first place.
 *
 * Do not simply unset the height: .song-panel is overflow:hidden at a fixed height, so an
 * unclamped bio would be silently CLIPPED rather than scrolled. The clamp below is the
 * deliberate version - the line count lives in the breakpoint sheets - and the
 * .persona-more link sends anyone who wants the rest to /artist, which shows it in full.
 */
.song-player-container .persona-bio {
    max-height: none;
    max-width: none;
    opacity: 1;
    /* Razor encodes the string, so without pre-line every newline the creator typed
       collapses and a multi-paragraph bio renders as one run-on blob. */
    white-space: pre-line;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.song-player-container .persona-more {
    display: inline-flex;
    align-items: center;
    text-decoration: none;
    transition: color var(--st-dur) var(--st-ease);
}

.song-player-container .persona-more svg {
    flex: 0 0 auto;
}

.song-player-container .song-tip-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--st-radius-pill);
    cursor: pointer;
    margin-top: auto;
    transition: transform var(--st-dur-fast) var(--st-ease), background-color var(--st-dur) var(--st-ease);
}

.song-player-container .song-tip-cta:hover { transform: scale(1.02); }

/* ---- Transport: a floating pill ---------------------------------------
   .controls-wrapper and .player-controls-row collapse with display:contents
   so .player-controls, .progress-container and .volume-controls become direct
   flex children of .player-bar and can be ordered independently. Both classes
   are asserted by PlayerTransportRegressionTests, so they stay in the DOM -
   they simply stop generating boxes. */

.song-player-container .player-bar {
    position: sticky;
    bottom: 0;
    z-index: 30;
    display: flex;
    /* Both of these are load-bearing, not defaults. The unscoped .player-bar in
       xl_app.css sets flex-direction:column for the playlist player, and without
       restating it here that column wins: the seek row and the volume stack
       underneath the buttons and spill out below the pill's fixed height. */
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    border-radius: var(--st-radius-pill);
    border: 1px solid var(--st-player-line-strong);
    box-sizing: border-box;
}

.song-player-container .controls-wrapper,
.song-player-container .player-controls-row {
    display: contents;
}

.song-player-container .player-controls {
    order: 1;
    display: flex;
    align-items: center;
}

.song-player-container .progress-container {
    order: 2;
    display: flex;
    align-items: center;
    flex: 1;
    min-width: 0;
}

.song-player-container .volume-controls {
    order: 3;
    display: flex;
    align-items: center;
}

.song-player-container .control-button {
    border-radius: 50%;
    background: transparent;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    transition: color var(--st-dur) var(--st-ease), background-color var(--st-dur) var(--st-ease);
}

.song-player-container .control-button[disabled] {
    cursor: default;
    opacity: .38;
}

.song-player-container .play-button-small {
    border-radius: 50%;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    transition: transform var(--st-dur-fast) var(--st-ease);
}

.song-player-container .play-button-small:hover { transform: scale(1.06); }

.song-player-container .time-display {
    font-variant-numeric: tabular-nums;
    text-align: center;
    flex: 0 0 auto;
}

.song-player-container .progress-bar-container,
.song-player-container .volume-bar-container {
    border-radius: var(--st-radius-xs);
    cursor: pointer;
    position: relative;
    transition: height var(--st-dur-fast) var(--st-ease);
}

.song-player-container .progress-bar-container { flex: 1; min-width: 0; }

.song-player-container .progress-bar,
.song-player-container .volume-bar {
    height: 100%;
    border-radius: var(--st-radius-xs);
    position: relative;
    pointer-events: none;
}

.song-player-container .progress-thumb,
.song-player-container .volume-thumb {
    position: absolute;
    right: -6px;
    top: 50%;
    transform: translateY(-50%);
    border-radius: 50%;
    opacity: 0;
    transition: opacity var(--st-dur) var(--st-ease);
}

.song-player-container .progress-bar-container:hover .progress-thumb,
.song-player-container .volume-bar-container:hover .volume-thumb {
    opacity: 1;
}

.song-player-container .progress-bar-container.restricted { position: relative; }

.song-player-container .progress-limit-marker {
    position: absolute;
    top: -3px;
    bottom: -3px;
    border-radius: 2px;
    z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
    .song-player-container .loading-pulse { animation: none; }

    .song-player-container .play-button-large,
    .song-player-container .play-button-small,
    .song-player-container .song-tip-cta {
        transition: none;
    }

    .song-player-container .play-button-large:hover,
    .song-player-container .play-button-small:hover,
    .song-player-container .song-tip-cta:hover {
        transform: none;
    }
}

/* =========================================================================
   PLAYLIST PLAYER - "Marquee" layout
   =========================================================================
   The song player's sibling, on the same design language and the same
   tokens. Structure, layout, animation and transition only; colour is in
   light.css / dark.css (identical in both), sizing in the breakpoint sheets.

   Scoped under .playlist-player-container throughout, mirroring
   .song-player-container, so the two pages stay independent.

   Replaces Components/Players/PlaylistPlayerInteractive.razor.css, deleted -
   and with it 283 rules across the global sheets, including five identical
   copies of @keyframes soundBars, five of .track-thumbnail, and the
   cart/ownership styles left dead by migration
   20260108000000_RemoveCartAndOwnedSongsTables.

   ONE TRAP, THE SAME ONE THAT BIT THE SONG PLAYER: the bare .player-bar in
   xl_app.css is unwrapped and sets flex-direction: column. The scoped sheet
   used to outrank it; now that the scoped sheet is gone it is live, so every
   property it sets has to be restated here, not just the ones being changed.
   ========================================================================= */

.playlist-player-container {
    display: flex;
    flex-direction: column;
    /* dvh, not vh: 100vh is taller than the visible viewport on mobile, which
       is what pushed the transport off-screen and clipped the page. */
    min-height: calc(100dvh - 66px);
    position: relative;
    font-family: var(--st-font);
}

.playlist-player-container .loading-state,
.playlist-player-container .error-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    flex: 1;
}

.playlist-player-container .loading-pulse {
    border-radius: 50%;
    animation: songPlayerPulse 1.4s ease-in-out infinite;
}

/* ---- Hero -------------------------------------------------------------- */

.playlist-player-container .playlist-header {
    position: relative;
    overflow: hidden;
    display: flex;
    align-items: flex-end;
}

.playlist-player-container .playlist-hero-backdrop {
    position: absolute;
    inset: -60px;
    z-index: 0;
    filter: blur(52px);
    transform: scale(1.25);
    pointer-events: none;
}

.playlist-player-container .playlist-hero-backdrop-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.playlist-player-container .playlist-hero-scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
}

.playlist-player-container .playlist-hero-inner {
    position: relative;
    z-index: 2;
    display: grid;
    grid-template-areas: "art text actions";
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: flex-end;
    width: 100%;
    box-sizing: border-box;
}

.playlist-player-container .playlist-art-container { grid-area: art; }
.playlist-player-container .playlist-text-details { grid-area: text; min-width: 0; }
.playlist-player-container .action-controls { grid-area: actions; }

.playlist-player-container .player-art-shell {
    position: relative;
    overflow: hidden;
    flex: 0 0 auto;
}

/* Artist and creator routes put a persona portrait here instead of cover art. */
.playlist-player-container .player-art-shell.is-round { border-radius: 50%; }

.playlist-player-container .playlist-hero-art {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.playlist-player-container .playlist-tag-row,
.playlist-player-container .playlist-meta-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
}

.playlist-player-container .playlist-chip {
    display: inline-flex;
    align-items: center;
    border-radius: var(--st-radius-pill);
    text-transform: uppercase;
    white-space: nowrap;
}

.playlist-player-container .song-title {
    margin: 0;
    text-wrap: balance;
}

.playlist-player-container .now-playing-info,
.playlist-player-container .stream-count-info,
.playlist-player-container .playlist-track-info,
.playlist-player-container .subscription-badge,
.playlist-player-container .preview-label {
    display: inline-flex;
    align-items: center;
    margin: 0;
}

.playlist-player-container .action-controls {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
}

.playlist-player-container .play-button-large {
    border-radius: 50%;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    transition: transform var(--st-dur-fast) var(--st-ease), box-shadow var(--st-dur) var(--st-ease);
}

.playlist-player-container .play-button-large:hover { transform: scale(1.05); }
.playlist-player-container .play-button-large:active { transform: scale(.96); }

/* ---- Stage: track list leads, lyrics and artist beside it -------------- */

.playlist-player-container .playlist-stage {
    display: grid;
    align-items: start;
    flex: 1;
    min-height: 0;
}

.playlist-player-container .playlist-side {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.playlist-player-container .playlist-panel {
    border-radius: var(--st-radius-lg);
    border: 1px solid var(--st-player-line);
    box-sizing: border-box;
    position: relative;
    overflow: hidden;
}

.playlist-player-container .track-list-container {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* The one scrolling region on the page. */
.playlist-player-container .track-list-scroll {
    overflow-y: auto;
    min-height: 0;
    flex: 1;
}

.playlist-player-container .playlist-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex: 0 0 auto;
}

.playlist-player-container .playlist-panel-caption {
    text-transform: uppercase;
    display: block;
}

.playlist-player-container .playlist-panel-switch {
    display: flex;
    border-radius: var(--st-radius-pill);
    border: 1px solid var(--st-player-line);
}

/* The unscoped .lyrics-toggle-button is the OLD control - a 28px circle pinned
   absolutely to the bottom-right of the artwork. Every one of those has to be
   undone or the Lyrics segment renders as a clipped bubble in the corner. */
/* See the matching .song-player-container block above: these are the pill segment's own
   declarations. The corner-circle rule they used to fight is scoped to .card-album-art now. */
.playlist-player-container .lyrics-toggle-button,
.playlist-player-container .art-toggle-button {
    position: static;
    width: auto;
    height: auto;
    opacity: 1;
    z-index: auto;
    display: inline-flex;
    align-items: center;
    border: none;
    border-radius: var(--st-radius-pill);
    cursor: pointer;
    background: transparent;
    white-space: nowrap;
    transition: background-color var(--st-dur) var(--st-ease), color var(--st-dur) var(--st-ease);
}

.playlist-player-container .lyrics-toggle-button:hover,
.playlist-player-container .art-toggle-button:hover { transform: none; }

.playlist-player-container .playlist-panel-body { position: relative; }

.playlist-player-container .playlist-panel-art {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
}

.playlist-player-container .playlist-stage-art {
    display: block;
    max-width: 100%;
    max-height: 100%;
    border-radius: var(--st-radius-md);
    object-fit: contain;
}

.playlist-player-container .lyrics-scroller { border-radius: 0; }

/* ---- Track list -------------------------------------------------------- */

.playlist-player-container .track-list {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
}

.playlist-player-container .track-list th {
    text-align: left;
    text-transform: uppercase;
    position: sticky;
    top: 0;
    z-index: 1;
}

.playlist-player-container .track-list th.track-number { text-align: center; }
.playlist-player-container .track-list th.track-streams,
.playlist-player-container .track-list th.track-duration { text-align: right; }

.playlist-player-container .track-row {
    cursor: pointer;
    transition: background-color var(--st-dur) var(--st-ease);
}

.playlist-player-container .track-row td { vertical-align: middle; }
.playlist-player-container .track-row td.track-number { text-align: center; position: relative; }
.playlist-player-container .track-row td.track-streams,
.playlist-player-container .track-row td.track-duration { text-align: right; font-variant-numeric: tabular-nums; }

.playlist-player-container .track-index { display: inline; font-variant-numeric: tabular-nums; }

.playlist-player-container .track-play-btn {
    display: none;
    align-items: center;
    justify-content: center;
    margin: 0 auto;
    border: none;
    background: transparent;
    border-radius: 50%;
    cursor: pointer;
}

.playlist-player-container .track-row:hover .track-index { display: none; }
.playlist-player-container .track-row:hover .track-play-btn { display: inline-flex; }
.playlist-player-container .track-row.current-track .track-index { display: none; }

/*
 * THE FIX. .current-track .track-index is hidden unconditionally, but the play
 * button used to appear only on :hover - so a current track that was PAUSED
 * rendered a completely blank number cell until you moused over it. Show the
 * button for the current row whether or not it is hovered.
 */
.playlist-player-container .track-row.current-track .track-play-btn { display: inline-flex; }

.playlist-player-container .track-title-cell { text-align: left; }

.playlist-player-container .track-title-content {
    display: flex;
    flex-direction: row;
    align-items: center;
    min-width: 0;
}

.playlist-player-container .track-name {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.playlist-player-container .track-preview-badge {
    display: inline-flex;
    align-items: center;
    border-radius: var(--st-radius-xs);
    text-transform: uppercase;
    white-space: nowrap;
    flex: 0 0 auto;
}

/* Carries the artist/genre that the dedicated columns show on wide screens.
   Hidden here, switched on by the md/sm/xs sheets - which is how the table
   sheds columns instead of shrinking its type to 8px. */
.playlist-player-container .track-sub {
    display: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.playlist-player-container .track-genre-link { text-decoration: none; }

.playlist-player-container .track-thumbnail { display: block; border-radius: var(--st-radius-xs); object-fit: cover; }

.playlist-player-container .track-thumbnail-animation {
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--st-radius-xs);
    overflow: hidden;
}

.playlist-player-container .playing-indicator {
    display: flex;
    align-items: flex-end;
    justify-content: center;
}

.playlist-player-container .playing-indicator span {
    border-radius: 1px;
    animation: soundBars .5s ease infinite alternate;
}

.playlist-player-container .playing-indicator span:nth-child(1) { height: 60%; animation-delay: 0s; }
.playlist-player-container .playing-indicator span:nth-child(2) { height: 100%; animation-delay: .1s; }
.playlist-player-container .playing-indicator span:nth-child(3) { height: 40%; animation-delay: .2s; }

/* Was declared five times, once per breakpoint sheet, with identical bodies. */
@keyframes soundBars {
    0% { height: 20%; }
    100% { height: 100%; }
}

/* ---- Artist / About panel --------------------------------------------- */

.playlist-player-container .persona-identity {
    display: flex;
    align-items: center;
}

/*
 * A persona bio runs to 2,000 characters and a creator bio to 1,000. The
 * unscoped .persona-bio caps it at 4.5em with its own scrollbar and dims it to
 * opacity .8 - undo all three explicitly. In CATALOGUE mode it is clamped and
 * .persona-more points at /artist; in ARTIST mode .persona-bio-full lifts the
 * clamp, because that route is the artist's own page.
 */
.playlist-player-container .persona-bio {
    max-height: none;
    max-width: none;
    opacity: 1;
    /* Razor encodes the string, so without this a multi-paragraph bio collapses
       into one run-on line. */
    white-space: pre-line;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.playlist-player-container .persona-bio-full {
    display: block;
    -webkit-line-clamp: none;
    overflow: visible;
}

.playlist-player-container .playlist-about-panel {
    overflow-y: auto;
}

.playlist-player-container .persona-more {
    display: inline-flex;
    align-items: center;
    text-decoration: none;
    transition: color var(--st-dur) var(--st-ease);
}

.playlist-player-container .persona-more svg { flex: 0 0 auto; }

.playlist-player-container .playlist-tip-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--st-radius-pill);
    cursor: pointer;
    margin-top: auto;
    transition: transform var(--st-dur-fast) var(--st-ease), background-color var(--st-dur) var(--st-ease);
}

.playlist-player-container .playlist-tip-cta:hover { transform: scale(1.02); }

/* ---- Transport: a floating pill ---------------------------------------- */

.playlist-player-container .player-bar {
    position: sticky;
    bottom: 0;
    z-index: 30;
    display: flex;
    /* Both restated on purpose - see the trap note at the top of this block. */
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    border-radius: var(--st-radius-pill);
    border: 1px solid var(--st-player-line-strong);
    border-top: 1px solid var(--st-player-line-strong);
    box-sizing: border-box;
    overflow: visible;
}

.playlist-player-container .controls-wrapper,
.playlist-player-container .player-controls-row {
    display: contents;
}

.playlist-player-container .player-controls {
    order: 1;
    display: flex;
    align-items: center;
}

.playlist-player-container .progress-container {
    order: 2;
    display: flex;
    align-items: center;
    flex: 1;
    min-width: 0;
}

.playlist-player-container .volume-controls {
    order: 3;
    display: flex;
    align-items: center;
}

.playlist-player-container .control-button {
    border-radius: 50%;
    background: transparent;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    transition: color var(--st-dur) var(--st-ease), background-color var(--st-dur) var(--st-ease);
}

.playlist-player-container .play-button-small {
    border-radius: 50%;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    transition: transform var(--st-dur-fast) var(--st-ease);
}

.playlist-player-container .play-button-small:hover { transform: scale(1.06); }

.playlist-player-container .time-display {
    font-variant-numeric: tabular-nums;
    text-align: center;
    flex: 0 0 auto;
}

.playlist-player-container .progress-bar-container,
.playlist-player-container .volume-bar-container {
    border-radius: var(--st-radius-xs);
    cursor: pointer;
    position: relative;
    transition: height var(--st-dur-fast) var(--st-ease);
}

.playlist-player-container .progress-bar-container { flex: 1; min-width: 0; }

.playlist-player-container .progress-bar,
.playlist-player-container .volume-bar {
    height: 100%;
    border-radius: var(--st-radius-xs);
    position: relative;
    pointer-events: none;
}

.playlist-player-container .progress-thumb,
.playlist-player-container .volume-thumb {
    position: absolute;
    right: -6px;
    top: 50%;
    transform: translateY(-50%);
    border-radius: 50%;
    opacity: 0;
    transition: opacity var(--st-dur) var(--st-ease);
}

.playlist-player-container .progress-bar-container:hover .progress-thumb,
.playlist-player-container .volume-bar-container:hover .volume-thumb { opacity: 1; }

.playlist-player-container .progress-bar-container.restricted { position: relative; }

.playlist-player-container .progress-limit-marker {
    position: absolute;
    top: -3px;
    bottom: -3px;
    border-radius: 2px;
    z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
    .playlist-player-container .loading-pulse,
    .playlist-player-container .playing-indicator span { animation: none; }

    .playlist-player-container .play-button-large,
    .playlist-player-container .play-button-small,
    .playlist-player-container .playlist-tip-cta { transition: none; }

    .playlist-player-container .play-button-large:hover,
    .playlist-player-container .play-button-small:hover,
    .playlist-player-container .playlist-tip-cta:hover { transform: none; }
}

/* =========================================================================
   CARDS — structure
   =========================================================================
   Song cards (MusicLibrary.razor) and playlist cards (PlaylistCard.razor,
   MyPlaylists, Home) share .music-card as their surface. Colour is in
   light.css / dark.css via the themed --st-* tokens declared at the top of
   each; sizing is in the breakpoint sheets.

   NOT SCOPED, on purpose. Unlike the two players, these class names are
   already unique to cards, so an extra ancestor would only start a
   specificity fight. Two exceptions matter:

     .card-mini-*, .card-progress-*, .card-audio-hidden are ALSO used by
     Creator/LyricsTimingEditor.razor and asserted in its tests. Do not put a
     card ancestor in front of them — app.css already documents the identical
     trap for .control-icon.

     .card-actions belongs to the PLAYLIST cards, never the song card, which
     uses .card-actions-with-likes.

   Folded in here: the inline <style> that used to live at the bottom of
   MusicLibrary.razor. Being injected only when that component rendered, it
   made the same playlist card 8px-padded on / and 16px-padded on
   /my-playlists.
   ========================================================================= */

.music-card,
.e-card.music-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    position: relative;
    border-radius: 14px;
    border: 1px solid transparent;
    padding: 10px;
    overflow: hidden;
    box-sizing: border-box;
    /*
     * THE FOUR LINES BELOW UNDO SYNCFUSION'S .e-card BASE. Do not remove them.
     *
     * bootstrap5.css ships:
     *     .e-card { justify-content:center; line-height:36px; min-height:36px; font-size:15px }
     *
     * line-height:36px is the important one. Every text row in the card - title, artist, genre,
     * stream count - inherits it, so a 14px title sits in a 36px box. Six rows of that added
     * ~130px of dead space and made the card absurdly tall on a desktop.
     *
     * The pre-redesign CSS hid this with negative margins stacked against each other
     * (.card-song-title had margin-top:-18px/margin-bottom:-8px, .card-genre -8px/-14px,
     * .card-ai-actions-row -8px). Those read as sloppy spacing and were removed; they were
     * actually cancelling this. Fixing it at the source is what lets them stay gone.
     *
     * justify-content:center matters too: a grid row stretches every card to the tallest one,
     * and centred content floats in the middle of the slack instead of sitting under the art.
     */
    justify-content: flex-start;
    line-height: 1.35;
    font-size: 14px;
    min-height: 0;
    transition: background-color var(--st-dur) var(--st-ease),
                box-shadow var(--st-dur) var(--st-ease),
                transform var(--st-dur) var(--st-ease);
}

/* Lifts without moving anything around it, so a grid does not jitter under
   the cursor. */
.music-card:hover { transform: translateY(-2px); }

.card-album-art {
    width: 100%;
    aspect-ratio: 1;
    border-radius: 8px;
    position: relative;
    overflow: hidden;
    margin-bottom: 0;
}

/* Direct child, deliberately — see the comment in MusicLibrary.razor. A
   wrapper here stops this matching and collapses the animation beside it. */
.card-album-art > img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.card-album-art-animation {
    /* Containing block for .card-lottie-host, which is inset:0 over the glyph. Set in
       both declarations of this selector so neither one alone can drop it. */
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 8px;
    overflow: hidden;
    box-sizing: border-box;
}

/* The card body used to be spaced by negative margins stacked against each
   other (.card-song-title had margin-top:-18px, .card-genre -8px/-14px).
   The flex gap above does that job now, so they are zeroed. */
.card-song-title,
.card-artist-name,
.card-genre,
.card-stream-count {
    margin: 0;
}

.card-song-title {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.card-artist-name {
    display: flex;
    align-items: center;
    min-width: 0;
}

.card-artist-name a,
.card-genre a {
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.card-persona-image {
    border-radius: 50%;
    flex: 0 0 auto;
    object-fit: cover;
}

/*
 * A wrapper that exists only for the phone breakpoint, which merges artist / genre / streams onto
 * one line. display:contents makes it vanish from the box tree here, so its three children stay
 * direct flex items of .music-card and every layout at 576px and up is untouched by the wrapper
 * being introduced. Only xs_app.css gives it a display of its own.
 */
.card-meta { display: contents; }

.card-stream-count {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
}

.card-stream-info,
.card-stream-actions {
    display: flex;
    align-items: center;
}

.card-actions-with-likes {
    display: flex;
}

.action-column { display: flex; flex: 1; }

.action-button {
    width: 100%;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background-color var(--st-dur) var(--st-ease), border-color var(--st-dur) var(--st-ease);
}

/*
 * Share sits on its own row, LEFT-ALIGNED AND AUTO-WIDTH - not stretched.
 *
 * .card-bottom-row .fb-share-btn used to be flex:1, which made a full-bleed blue bar the
 * loudest element on the card while Play, the actual primary action, was a quiet outline.
 * The approved layout has it as a small pill under the two action buttons.
 */
.card-bottom-row {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    margin-top: auto;
}

.card-bottom-row .fb-share-btn {
    flex: 0 0 auto;
    width: auto;
    /* margin-right:auto, not just the row's justify-content. An auto margin on a flex item
       absorbs the free space itself, so this pins the button left even if something else wins
       the justify-content declaration - which is exactly what kept happening. */
    margin-right: auto;
    align-self: flex-start;
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* Play is wider than View, as approved. */
.action-column-play { flex: 1.3; }

/*
 * The mini player's transport buttons. Styled on the ROW, not with a modifier class, because
 * Creator/LyricsTimingEditor.razor renders the same .card-mini-controls with plain <button
 * class="e-btn"> children - a class would have fixed the card and left the editor on raw
 * Syncfusion grey. :first-child is play/pause in both, and is the row's primary action.
 */
.card-mini-controls .e-btn {
    min-width: 0;
    min-height: 0;
    width: 34px;
    height: 34px;
    padding: 0;
    border-radius: 8px;
    border: 1px solid transparent;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-shadow: none;
    transition: background-color var(--st-dur) var(--st-ease), border-color var(--st-dur) var(--st-ease);
}

.card-mini-controls .e-btn svg {
    width: 15px;
    height: 15px;
}

/* ---- Containers -------------------------------------------------------- */

.music-cards-grid { display: grid; width: 100%; }

.featured-music-carousel {
    display: flex;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
}

.featured-music-carousel .music-card { flex: 0 0 auto; scroll-snap-align: start; }

.music-library-sticky-header {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
}

/* ---- Filter pills ------------------------------------------------------ */

.filter-pill {
    display: inline-flex;
    align-items: center;
    border-radius: var(--st-radius-pill);
    border: 1px solid;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color var(--st-dur) var(--st-ease), border-color var(--st-dur) var(--st-ease), color var(--st-dur) var(--st-ease);
}

.filter-pill-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
}

@media (prefers-reduced-motion: reduce) {
    .music-card { transition: none; }
    .music-card:hover { transform: none; }
}

/* Replaces a 2.5em U+1F4E7 emoji used as an icon on /new-creator-signup: an emoji
   ignores currentColor and renders in whatever the platform font decides. */
.contact-line { display: flex; align-items: center; gap: 10px; }
.contact-icon { flex: 0 0 auto; }

/* =========================================================================
   SECTION HEADERS AND PAGE STATES — structure
   =========================================================================
   The header trio (eyebrow / title / action link) is the one pattern shared by home, the
   library and the creator funnel. It is the players' micro-caption header, extended.

   .empty-state already existed in MyPlaylists.razor with NO css anywhere, so it rendered as a
   bare paragraph. Styling it here fixes that page too.
   ========================================================================= */

.section-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
}

.section-eyebrow {
    font-weight: 700;
    text-transform: uppercase;
}

.section-link {
    display: inline-flex;
    align-items: center;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.section-link svg {
    flex: 0 0 auto;
}

/* The error state gains a retry button, so it stacks rather than centring one child. */
.loading-state,
.error-state,
.empty-state {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
}

.empty-state-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
}

.empty-state-title {
    font-weight: 700;
    margin: 0;
}

.empty-state-body {
    margin: 0;
    max-width: 42ch;
}

.state-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--st-radius-pill);
    border: 1px solid transparent;
    font-weight: 700;
    cursor: pointer;
    transition: background-color var(--st-dur) var(--st-ease);
}

.state-action svg {
    flex: 0 0 auto;
}

/* ---- Creator funnel ------------------------------------------------------
   Replaces the 8 inline style= attributes the two signup pages carried. Inline styles beat
   every stylesheet including the theme sheets, so they could not be themed or overridden. */

.process-steps {
    list-style: none;
    padding-left: 0;
}

/* Between the two buttons in a row above 768px, a caption below them under it. */
.cta-group-note {
    margin: 0;
    text-align: center;
}

.back-link {
    display: inline-flex;
    align-items: center;
}

/* ---- Hero and the two new button tiers -----------------------------------
   .cta-outline and .cta-quiet complete the set: .cta-primary / .cta-button fill with the accent,
   .cta-secondary / .hero-secondary-cta fill with the creator violet, and these two are the
   listener-side secondary and tertiary. Same pill geometry as the group above them. */

.cta-outline,
.cta-quiet {
    border-radius: 500px;
    font-weight: 700;
    text-transform: none;
    cursor: pointer;
    transition: transform .2s ease, background-color .2s ease, border-color .2s ease;
}

.cta-outline:hover,
.cta-quiet:hover { transform: scale(1.04); }

.section-head-centred {
    justify-content: center;
    text-align: center;
}

/* The single remaining CTA card: centre it instead of stretching it across a two-card row. */
.cta-split-single {
    justify-content: center;
}

@media (prefers-reduced-motion: reduce) {
    .cta-outline,
    .cta-quiet,
    .cta-outline:hover,
    .cta-quiet:hover { transition: none; transform: none; }
}

/*
 * The hero headline is TWO elements rendering as one sentence, so the gap belongs below the
 * pair, not between the lines. Every breakpoint sheet sets margin-bottom on .hero-title and
 * both lines now carry that class, so the first line needs its bottom margin zeroed.
 *
 * Element-qualified (h1./h2.) on purpose: that is specificity 0,1,1, which beats the plain
 * .hero-title rules in xl/lg/md/sm regardless of the fact that those sheets load later.
 */
h1.hero-title { margin-bottom: 0; }
h2.hero-title-line-2 { margin-top: 0; }

/* =========================================================================
   AUTH PAGES - structure
   =========================================================================
   Login and Register share this shell. The brand panel is a BRAND surface, so it keeps one
   palette in both site themes - the same decision the players made, and for the same reason:
   it is not page content. Its colour block therefore lives here rather than being duplicated
   across the theme sheets, because nothing about it changes with the toggle.

   The negative margins cancel the .content padding MainLayout applies (padding-top: 1rem,
   px-4). This page is deliberately full-bleed under the app bar; every other page is not.
   ========================================================================= */

.auth-page {
    display: flex;
    align-items: stretch;
    overflow: hidden;
}

.auth-panel {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    overflow: hidden;
    /* Base is --st-navy, the logo disc edge. NOT --st-brand-gradient: white on that
       gradient stop #0186fd is 3.6:1 and fails AA, and this panel carries body copy.
       The gradient blooms in from the corner where no text sits. */
    background-color: #102e55;
    background-image:
        radial-gradient(120% 95% at 92% 104%, rgba(1, 134, 253, .62) 0%, rgba(1, 102, 214, .30) 40%, rgba(16, 46, 85, 0) 72%),
        radial-gradient(72% 62% at 8% -6%, rgba(2, 184, 253, .20) 0%, rgba(16, 46, 85, 0) 62%);
}

/* The auth pages are the only full-bleed pages under the app bar, and getting their height
   from a fixed subtraction is wrong on any deployment that shows something else up there.
   NavMenu can render a test-server banner (every non-production deployment does) and
   MobileAppInstallBanner can render another, both OUTSIDE <main> - so calc(100dvh - 66px)
   overshoots by a banner and leaves <main> scrolling on exactly the servers used to test.

   Handing the height down the chain removes the guess: <main> is already flex:1 inside a
   100vh column, so 100% of it is correct whatever sits above. The breakpoint sheets keep the
   calc as the fallback for anything without :has(), where it degrades to today's behaviour. */
main:has(> .content > .auth-page) > .content {
    height: 100%;
    padding: 0;
}

main:has(> .content > .auth-page) > .content > .auth-page { margin: 0; }

/* align-self, not just flex, because .auth-panel is a COLUMN flex container: the
   default align-items: stretch sets the used WIDTH, which squashes a fixed-height
   image out to the panel width and ignores its intrinsic ratio. */
.auth-panel-logo { display: block; flex: 0 0 auto; align-self: flex-start; width: auto; }
.auth-panel-copy { flex: 0 0 auto; }
.auth-panel-eyebrow { font-weight: 700; text-transform: uppercase; margin: 0; }
.auth-panel-title { font-weight: 750; margin: 0; text-wrap: pretty; }
.auth-panel-body { margin: 0; text-wrap: pretty; }

.auth-panel-rule {
    flex: 0 0 auto;
    border-radius: var(--st-radius-pill);
    background: linear-gradient(90deg, var(--st-blue-bright), var(--st-blue-deep));
}

.auth-form-col {
    flex: 1 1 auto;
    display: flex;
    justify-content: center;
    /* flex-start, not center. Centring a flex child that overflows its scroll
       container puts the overflow above the scrollable area, where no scrollbar can
       reach it - the top of a long form would simply be gone. The auto margins on
       .auth-form centre it while there is room and collapse to 0 when there is not. */
    align-items: flex-start;
    min-width: 0;
    /* Only this column scrolls while the layout is side by side. Stacked, the whole page
       scrolls instead - see md_app.css. */
    overflow-y: auto;
}

.auth-form { width: 100%; margin: auto 0; }
.auth-title { font-weight: 700; margin: 0; }
.auth-subtitle { margin: 0; }

.auth-label-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
}

.auth-label { display: block; font-weight: 600; }
.auth-link { font-weight: 600; text-decoration: none; }
.auth-link:hover { text-decoration: underline; }
.auth-link-strong { font-weight: 700; }

.auth-alert {
    display: flex;
    align-items: flex-start;
    border-radius: var(--st-radius-sm);
}

.auth-alert-icon { flex: 0 0 auto; }

.auth-divider { display: flex; align-items: center; }

.auth-divider::before,
.auth-divider::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
}

.auth-divider span { font-weight: 700; text-transform: uppercase; }

.auth-providers { display: flex; flex-direction: column; }

/* Syncfusion draws its own button content box; these make the provider buttons behave like
   the rest of the pill family rather than like a stock e-btn. */
.auth-page .e-btn.auth-provider,
.auth-page .e-css.e-btn.auth-provider {
    display: flex;
    /* Not inherited from a flex parent: Register renders this one inside a plain form. */
    width: 100%;
    align-items: center;
    justify-content: center;
    text-transform: none;
}

.auth-provider-icon { flex: 0 0 auto; }

.auth-page .e-btn.auth-submit {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    text-transform: none;
}

.auth-footer { margin: 0; text-align: center; }
.auth-dev-hint { margin: 0; text-align: center; }

.auth-page .e-input-group.e-outline,
.auth-page .e-input-group.e-control-wrapper.e-outline {
    border-radius: var(--st-radius-sm);
}

/* ---- Register adds to the auth shell ------------------------------------
   The three fields stay InputText, so they are .form-control rather than a Syncfusion input
   group. .auth-input gives them the same box without touching Bootstrap globally. */

.auth-legal { border-style: solid; border-width: 1px; }
.auth-legal-row { display: flex; align-items: flex-start; }
.auth-legal-split { height: 1px; }

.auth-page .validation-message,
.auth-page .validation-errors { list-style: none; margin: 0; padding: 0; }

.auth-hint { margin: 0; }
.auth-verify-lead { margin: 0; }
.auth-countdown { margin: 0; text-align: center; font-variant-numeric: tabular-nums; }
.auth-rule { height: 1px; }
.auth-subhead { font-weight: 700; margin: 0; }
.auth-subhead-note { margin: 0; }

/* =========================================================================
   SETTINGS SURFACES - structure
   =========================================================================
   Manage Account and Creator Settings are the same kind of page: a single column of sections,
   each headed by the eyebrow/title trio, with a nav rail beside it on wide viewports. The
   vocabulary is settings-* rather than manage-* so the second page reuses it rather than
   growing a parallel set.
   ========================================================================= */

.settings-page {
    display: flex;
    align-items: flex-start;
    justify-content: center;
}

/* Dropped below 1200px rather than collapsed into a dropdown: a 212px rail plus its gutter
   needs about 1030px beside a readable column, and the six section heads are already legible
   while scrolling. */
.settings-nav {
    display: none;
    flex: 0 0 auto;
    flex-direction: column;
}

.settings-nav-heading { font-weight: 700; text-transform: uppercase; margin: 0; }

.settings-nav-link {
    display: block;
    text-decoration: none;
    font-weight: 600;
}

.settings-main { flex: 1 1 auto; min-width: 0; }
.settings-head-eyebrow { font-weight: 700; text-transform: uppercase; margin: 0; }
.settings-head-title { font-weight: 750; margin: 0; }
.settings-head-note { margin: 0; }

.settings-column { display: flex; flex-direction: column; }

.settings-card {
    border-style: solid;
    border-width: 1px;
    scroll-margin-top: 24px;
}

.settings-section-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
}

.settings-eyebrow { font-weight: 700; text-transform: uppercase; margin: 0; }
.settings-title { font-weight: 700; margin: 0; }
.settings-note { margin: 0; }
.settings-prose { margin: 0; }
.settings-tz { margin: 0; }
.settings-subhead { font-weight: 700; margin: 0; }
.settings-hint { margin: 0; }
.settings-working { margin: 0; font-weight: 600; }

/* Status is a pill with a dot, never a bare coloured word - the state has to survive being
   read in greyscale. */
.status-pill {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    border-radius: var(--st-radius-pill);
    border-style: solid;
    border-width: 1px;
    font-weight: 700;
    white-space: nowrap;
}

.status-pill-dot { border-radius: 50%; flex: 0 0 auto; }

/* A read-only fact. Not a disabled input: there is nothing to type. */
.fact-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
}

.fact-label { flex: 0 0 auto; font-weight: 600; }
.fact-value { text-align: right; min-width: 0; overflow-wrap: anywhere; font-weight: 600; }

.terms-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.terms-list li { position: relative; }

.settings-actions { display: flex; flex-wrap: wrap; align-items: center; }
.settings-inline-status { font-weight: 600; }


/* A link wearing the button classes. Syncfusion styles .e-btn for a <button>, which
   leaves an anchor with an underline and its label off-centre. */
a.e-btn.settings-btn { display: inline-flex; align-items: center; justify-content: center; text-decoration: none; }
:is(.settings-page, .settings-dialog) .e-btn.settings-btn {
    border-radius: var(--st-radius-pill);
    font-weight: 700;
    text-transform: none;
}

.passkey-rows { display: flex; flex-direction: column; }
.passkey-row { display: flex; align-items: center; }
.passkey-icon { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; border-radius: 9px; }
.passkey-detail { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.passkey-name { font-weight: 700; overflow-wrap: anywhere; }
.passkey-actions { flex: 0 0 auto; display: flex; }

.settings-empty { display: flex; flex-direction: column; align-items: center; text-align: center; }
.settings-empty-icon { display: flex; align-items: center; justify-content: center; border-radius: 50%; }
.settings-empty-title { font-weight: 700; margin: 0; }
.settings-empty-body { margin: 0; max-width: 44ch; }

.settings-guard, .settings-alert { display: flex; align-items: flex-start; border-radius: var(--st-radius-sm); }
.settings-guard-icon, .settings-alert-icon { flex: 0 0 auto; }

.settings-label { display: block; font-weight: 600; }
.settings-loading { display: flex; align-items: center; justify-content: center; }

.settings-spinner {
    border-radius: 50%;
    border-style: solid;
    animation: settings-spin 900ms linear infinite;
}

@keyframes settings-spin { to { transform: rotate(360deg); } }

:is(.settings-page, .settings-dialog) .e-input-group.e-outline,
:is(.settings-page, .settings-dialog) .e-input-group.e-control-wrapper.e-outline { border-radius: var(--st-radius-sm); }

/* -------------------------------------------------------------------------
   CREATOR SETTINGS - what Manage Account does not already have
   -------------------------------------------------------------------------
   Everything shared (page shell, nav rail, cards, section heads, buttons, fields,
   status pill) comes from the settings-* block above. Only the pieces unique to a
   creator live here.
   ------------------------------------------------------------------------- */

/* A consent row. Bordered rather than loose, because in both places it appears the
   checkbox is a gate: creator activation, and the PayPal ownership affirmation. */
.settings-consent {
    display: flex;
    align-items: flex-start;
    border-style: solid;
    border-width: 1px;
    border-radius: var(--st-radius-sm);
}

.settings-hero { display: flex; align-items: flex-start; }
.settings-hero-icon { flex: 0 0 auto; display: flex; align-items: center; justify-content: center; border-radius: 50%; }
.settings-hero-body { min-width: 0; }

.settings-label-row { display: flex; align-items: baseline; justify-content: space-between; }
.settings-count { flex: 0 0 auto; font-variant-numeric: tabular-nums; }
.settings-optional { font-weight: 400; }

/* Three tiles that read the same way at a glance. Each is a label plus a status pill,
   so what is complete and what is not survives being read in greyscale. */
.status-tiles { display: flex; flex-wrap: wrap; }
.status-tile { flex: 1 1 0; min-width: 0; border-style: solid; border-width: 1px; border-radius: var(--st-radius-sm); }
.status-tile-label { display: block; font-weight: 700; text-transform: uppercase; }

.steps { display: flex; flex-direction: column; }
.step { display: flex; align-items: flex-start; border-bottom-style: solid; border-bottom-width: 1px; }
.step:last-child { border-bottom: none; }

.step-badge {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border-style: solid;
    border-width: 1px;
    font-weight: 700;
}

.step-detail { min-width: 0; display: flex; flex-direction: column; }
.step-title { font-weight: 700; }

/* Numbered for reference, not for order: IsPayoutReady is an AND of the two, and either
   can be done first. */
.payout-sections { display: flex; flex-direction: column; }
.payout-section { border-style: solid; border-width: 1px; border-radius: var(--st-radius-sm); }
.payout-heading { display: flex; align-items: center; }

.payout-num {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border-style: solid;
    border-width: 1px;
    font-weight: 700;
}

.payout-heading-title { flex: 1 1 auto; min-width: 0; font-weight: 700; }

/* A window that has not opened yet, not a disabled button. A greyed control invites
   pressing it to find out why; a plain statement of when does not. */
.retry-window { display: inline-flex; align-items: center; border-radius: var(--st-radius-pill); border-style: solid; border-width: 1px; font-weight: 700; font-variant-numeric: tabular-nums; }

.info-strip { display: flex; align-items: flex-start; border-style: solid; border-width: 1px; border-radius: var(--st-radius-sm); }
.info-strip-icon { flex: 0 0 auto; }

/* One identity per row. The grid this replaces split image, name, bio, website, song
   count and status across six cells of the same person. */
.persona-rows { display: flex; flex-direction: column; }
.persona-row { display: flex; align-items: center; border-bottom-style: solid; border-bottom-width: 1px; }
.persona-row:last-child { border-bottom: none; }

.persona-avatar {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    border-style: solid;
    border-width: 1px;
    font-weight: 750;
}

.persona-detail { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.persona-name { font-weight: 700; overflow-wrap: anywhere; }
.persona-bio { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.persona-meta { overflow-wrap: anywhere; }

/* /creator/personas. A card per identity rather than a row per identity split across six
   grid columns - image, name, bio, website, song count and status are all one person. */
.settings-main-wide { max-width: none; }
.persona-toolbar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.persona-summary { font-weight: 600; }

.persona-cards { display: grid; }
.persona-card { border-style: solid; border-width: 1px; display: flex; flex-direction: column; }
.persona-card-head { display: flex; align-items: center; }
.persona-card-title { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.persona-card-bio { margin: 0; }
.persona-card-site { margin: 0; display: block; overflow-wrap: anywhere; }

/* Sized here rather than on .persona-avatar so the compact row on Creator Settings and the
   card here can differ without either one having to override the other. */
.persona-avatar-lg { overflow: hidden; }
.persona-avatar-lg img { width: 100%; height: 100%; object-fit: cover; display: block; }

.persona-image-picker { display: flex; align-items: flex-start; border-style: dashed; border-width: 1px; border-radius: var(--st-radius-sm); }
.persona-image-copy { flex: 1 1 auto; min-width: 0; }
.persona-image-title { margin: 0; font-weight: 600; }
.persona-image-input { display: block; width: 100%; }

.crop-tool { border-style: solid; border-width: 1px; border-radius: var(--st-radius-sm); }
.crop-canvas { display: block; margin-inline: auto; border-radius: var(--st-radius-sm); cursor: grab; touch-action: none; }
.crop-canvas:active { cursor: grabbing; }
.crop-zoom { display: flex; align-items: center; }
.crop-zoom .settings-label { margin-bottom: 0; flex: 0 0 auto; }
.crop-zoom input[type="range"] { flex: 1 1 auto; min-width: 0; }

/* =========================================================================
   UPLOAD - structure
   =========================================================================
   Moved out of a 178-line inline <style> block in UploadFiles.razor, which
   had no dark variant for anything and hard-coded twelve colours. Colour is
   in light.css / dark.css, sizing in the breakpoint sheets, per the routing
   rule at the top of AGENTS.md.
   ========================================================================= */

.upload-box {
    display: flex;
    justify-content: center;
    align-items: center;
    border-style: dashed;
    border-width: 2px;
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease;
}

.upload-box-disabled { pointer-events: none; cursor: not-allowed; }
.hidden-input { display: none; }
.upload-content { text-align: center; pointer-events: none; }

.upload-spinner {
    justify-content: center;
    align-items: center;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

@keyframes upload-spin { to { transform: rotate(360deg); } }
.upload-processing-animation { animation: upload-spin 2s linear infinite; }

.cover-art-pool { border-style: dashed; border-width: 2px; border-radius: var(--st-radius-sm); }
.cover-art-chips { display: flex; flex-wrap: wrap; }

.cover-art-chip {
    border-style: solid;
    border-width: 1px;
    border-radius: 6px;
    cursor: grab;
    text-align: center;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.cover-art-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.cover-art-thumb {
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: 4px;
}

.cover-art-thumb-row { cursor: grab; }

/* The landmark the instructions above the table point at. Named there by SHAPE
   rather than colour, so this can follow the theme. */
.cover-art-dropzone {
    display: flex;
    align-items: center;
    justify-content: center;
    border-style: dashed;
    border-width: 2px;
    border-radius: 6px;
    text-align: center;
    cursor: pointer;
}

/* Applied to every row while an image is held, including rows that already have
   art - re-pairing is allowed there too, and the target has to look like one. */
.cover-art-target { outline-style: dashed; outline-width: 2px; outline-offset: -4px; cursor: copy; }
.cover-art-target .cover-art-dropzone { font-weight: 600; }
.cover-art-remove { flex: 0 0 auto; white-space: nowrap; }

.cover-art-hint-swatch {
    border-style: dashed;
    border-width: 1px;
    border-radius: 3px;
    white-space: nowrap;
}

/* ---- the review step ------------------------------------------------- */

.apply-all {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    border-style: solid;
    border-width: 1px;
    border-radius: var(--st-radius-sm);
}

.apply-all-label { align-self: center; font-weight: 700; }
.apply-all-field .form-label { font-weight: 600; }
.review-select { width: 100%; }

.title-check { display: flex; flex-wrap: wrap; align-items: center; }
.title-source-warn { font-weight: 600; }
.ai-flags { display: flex; flex-direction: column; }
.inline-create-actions { display: flex; }

/* A link, not a button that looks like one: it opens a one-field row in place
   rather than doing anything on its own. */
.inline-create-link {
    display: inline-block;
    padding: 0;
    border: 0;
    background: none;
    font-weight: 700;
    cursor: pointer;
}

.inline-create-link:hover { text-decoration: underline; }
.review-outstanding { font-weight: 600; }

.upload-icon { display: block; margin-inline: auto; }
.inline-icon { vertical-align: -0.125em; }

.row-remove { display: block; }

/* The disclosure is one control, not three loose boxes, so it gets an edge that
   shows which answers belong together and can be marked invalid as a whole. */
.ai-declare { border-style: solid; border-width: 1px; border-radius: var(--st-radius-sm); }
.ai-declare-or { display: block; }
