.tip-banner {
    background: var(--bg-soft);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 0.7rem 1.1rem;
}
.tip-banner__copy { font-size: 0.92rem; line-height: 1.4; }
@media (max-width: 540px) {
    .tip-banner { justify-content: center !important; text-align: center; }
}

.show-header {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: flex-start;
    margin-bottom: 1.5rem;
}
.show-header__title { flex: 1 1 280px; min-width: 0; }
.show-header__title h1 {
    font-size: clamp(1.35rem, 2.5vw, 1.9rem);
    font-weight: 800;
    letter-spacing: -0.02em;
    margin: 0 0 0.5rem;
    line-height: 1.2;
    word-wrap: break-word;
}
.show-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 0.9rem;
    font-size: 0.85rem;
    color: var(--text-muted);
    margin-bottom: 0.4rem;
}
.show-meta .llm-chip {
    display: inline-block;
    padding: 0.18rem 0.6rem;
    background: var(--bg-soft);
    color: var(--text);
    border-radius: 999px;
    font-size: 0.78rem;
    font-weight: 600;
}
.show-subline { font-size: 0.82rem; color: var(--text-muted); margin: 0; line-height: 1.4; }
.show-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; flex: 0 0 auto; align-items: stretch; }
.show-actions .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.3rem;
    line-height: 1.2;
    white-space: nowrap;
}
.show-reorder-confirm { flex: 0 0 100%; }

/* BPM line under the title */
.tk-title .tk-title-text { display: block; }
.tk-title .tk-bpm-line {
    display: block;
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--text-muted);
    margin-top: 0.15rem;
}
.tk-title .tk-bpm-line .bpm-conflict {
    margin-left: 0.35rem;
    color: #b91c1c;
    font-weight: 600;
}

/* Tracks table → compact 2-row cards below 720px */
@media (max-width: 720px) {
    /* Drop the outer Bootstrap card wrapper so its border doesn't double up with each row-card's border */
    .tracks-card {
        border: none !important;
        background: transparent !important;
        box-shadow: none !important;
    }
    .tracks-card > .table-responsive { overflow: visible; }
    #tracks-table { background: transparent; }

    #tracks-table thead { display: none; }
    #tracks-table,
    #tracks-table tbody,
    #tracks-table tr { display: block; width: 100%; }
    #tracks-table tr:not(.spotify-embed-row) {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 0.3rem 0.5rem;
        background: #fff;
        border: 1px solid var(--border);
        border-radius: 10px;
        padding: 0.55rem 0.8rem;
        margin-bottom: 0.45rem;
        box-shadow: var(--shadow-sm);
    }
    #tracks-table td { display: block; border: none; padding: 0; }

    /* Line 1: idx | title (grow) | bpm | actions */
    #tracks-table td.tk-idx {
        order: 1;
        flex: 0 0 auto;
        font-size: 0.72rem;
        font-weight: 700;
        color: var(--text-muted);
        background: var(--bg-soft);
        padding: 0.12rem 0.42rem;
        border-radius: 5px;
        line-height: 1.1;
    }
    #tracks-table td.tk-title {
        order: 2;
        flex: 1 1 0;
        min-width: 0;
        font-weight: 600;
        font-size: 0.92rem;
        color: var(--text);
    }
    #tracks-table td.tk-title .tk-title-text {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    #tracks-table td.tk-title .tk-bpm-line { font-size: 0.72rem; }
    #tracks-table td.tk-speed { order: 3; flex: 0 0 auto; }
    #tracks-table td.tk-actions {
        order: 4;
        flex: 0 0 auto;
        display: flex;
        gap: 0.25rem;
    }
    #tracks-table td.tk-actions .btn { padding: 0.15rem 0.4rem !important; font-size: 0.72rem; }

    /* Line 2: artist only (speed is the pill, BPM is under the title) */
    #tracks-table td.tk-artist {
        order: 5;
        flex: 0 0 100%;
        font-size: 0.78rem;
        color: var(--text-muted);
        line-height: 1.2;
    }

    #tracks-table tr.spotify-embed-row {
        padding: 0 !important;
        background: transparent;
        border: none;
        box-shadow: none;
        margin-bottom: 0.5rem;
    }
    #tracks-table tr.spotify-embed-row td { padding: 0; }
}

/* Live song requests panel */
.requests-panel {
    margin-bottom: 1.4rem;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg-soft);
    overflow: hidden;
}
/* Bootstrap's .btn-outline-secondary text (#6c757d) lands at 4.4:1 on this
   panel's --bg-soft and misses WCAG AA by a hair. The site's own muted token
   is darker and clears it at about 7:1, so reuse that instead of inventing a
   colour. Only the resting state: hover and focus paint a dark fill and need
   to keep Bootstrap's light text. */
.requests-panel .btn-outline-secondary:not(:hover):not(:focus) {
    color: var(--text-muted);
}
.requests-panel summary {
    cursor: pointer;
    padding: 0.7rem 1rem;
    font-weight: 700;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}
.requests-panel summary::-webkit-details-marker { display: none; }
.requests-panel summary::after {
    content: '▾';
    margin-left: auto;
    transition: transform 0.2s;
    opacity: 0.6;
}
.requests-panel[open] summary::after { transform: rotate(180deg); }
.requests-panel__count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.4rem;
    height: 1.4rem;
    padding: 0 0.4rem;
    border-radius: 999px;
    background: #b91c1c;
    color: #fff;
    font-size: 0.78rem;
    font-weight: 700;
}
.requests-panel__count.is-zero { background: var(--border); color: var(--text-muted); }
.requests-panel__body {
    padding: 0 1rem 1rem;
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
}
/* Grid children default to min-width: auto, so one item with unbreakable
   content (the event picker, whose option labels run to a venue name) widens
   the whole column past the viewport and the panel scrolls sideways on a
   phone. Nothing here needs to be wider than its column. */
.requests-panel__body > * { min-width: 0; }
@media (min-width: 720px) {
    .requests-panel__body { grid-template-columns: 220px 1fr; }
    /* Setup state has no QR, so it gets the full width. */
    .requests-panel__body--setup { grid-template-columns: 1fr; }
}
/* In setup the box is a full-width row: left-aligned, and its two groups
   (start the party / ask for an event link) sit side by side when there
   is room instead of stacking in a narrow column. */
.requests-panel__body--setup .requests-share {
    text-align: left;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 1rem 2rem;
}
.requests-panel__body--setup .requests-share > * { margin: 0; }
/* The divider belongs to whichever group comes second, and the event picker is
   now first: the event has to be chosen before the party starts, because
   starting it captures whatever is linked at that instant. Left on the event
   box, the line drew above (or beside) the very first control and separated
   nothing. */
.requests-panel__body--setup .event-link-box {
    flex: 1 1 300px;
    min-width: 0;
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}
.requests-panel__body--setup .party-start-group {
    flex: 1 1 260px;
    min-width: 0;
    border-left: 1px solid var(--border);
    padding-left: 1.5rem;
}
@media (max-width: 719px) {
    /* Stacked on a phone, so the divider goes back on top of the second group. */
    .requests-panel__body--setup .party-start-group {
        border-left: 0;
        padding-left: 0;
        border-top: 1px solid var(--border);
        padding-top: 0.8rem;
    }
}
.requests-share {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.8rem;
    text-align: center;
}
.requests-share img { width: 100%; height: auto; max-width: 220px; }
.requests-share__url {
    margin-top: 0.5rem;
    font-size: 0.78rem;
    word-break: break-all;
    color: var(--text-muted);
}
.requests-share__copy {
    margin-top: 0.4rem;
    font-size: 0.78rem;
    padding: 0.3rem 0.7rem;
}
.requests-list { display: flex; flex-direction: column; gap: 0.4rem; }
/* The caret lives on the wrapper rather than the control, because the select
   drops its native arrow with appearance:none and a background-image would
   need a data URI. pointer-events:none keeps clicks going to the select. */
.party-event-picker { position: relative; }
.party-event-picker::after {
    content: '';
    position: absolute;
    right: 0.7rem;
    bottom: 0.85rem;
    width: 0.42rem;
    height: 0.42rem;
    border-right: 2px solid var(--text-muted);
    border-bottom: 2px solid var(--text-muted);
    transform: rotate(45deg);
    pointer-events: none;
}
.party-event-picker select {
    width: 100%;
    margin-top: 0.25rem;
    /* Room on the right for the caret: with the native arrow the truncated
       option name ran straight into it, and the control read as a text field
       rather than something you could open. */
    padding: 0.4rem 2rem 0.4rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    font-size: 0.85rem;
    background: #fff;
    appearance: none;
    -webkit-appearance: none;
    /* The panel column is narrow; without these a long option name stretches
       the control, and then its own grid column, past the viewport. */
    max-width: 100%;
    min-width: 0;
}
.event-link-box, .party-event-picker { min-width: 0; }
.event-link-box {
    margin-top: 0.8rem;
    padding-top: 0.8rem;
    border-top: 1px solid var(--border);
    text-align: left;
}
.event-link-state { font-size: 0.8rem; color: var(--text-muted); margin: 0; }
.event-link-state.is-ok { color: #0a3622; font-weight: 600; }
.event-link-state + .event-link-state { margin-top: 0.3rem; }
/* Once a party is running the event line spans both columns: it describes
   the whole party, and the 220px QR column is far too narrow for the
   event picker. */
/* Centred on a phone like everything else in this column, left-aligned once
   there is room. Its own box forces text-align: left, hence the override. */
.event-link-row { grid-column: 1 / -1; text-align: center; }
.event-link-row .event-link-box { margin-top: 0; padding-top: 0; border-top: 0; text-align: inherit; }
@media (min-width: 560px) {
    .event-link-row { text-align: left; }
}
/* A bare <summary> reads as a paragraph, not as something you can press, so
   it borrows the look of the panel's other secondary controls. The native
   disclosure triangle is replaced by a caret inside the control. */
.event-link-change > summary {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    list-style: none;
    cursor: pointer;
    padding: 0.25rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: #fff;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--text);
}
.event-link-change > summary::-webkit-details-marker { display: none; }
.event-link-change > summary::marker { content: ''; }
.event-link-change > summary::after { content: '▾'; font-size: 0.7rem; line-height: 1; }
.event-link-change[open] > summary::after { content: '▴'; }
.event-link-change > summary:hover { border-color: var(--text); }
.event-link-change[open] > summary { margin-bottom: 0.5rem; }
/* The event link sat in default browser blue next to a teal palette. */
.event-link-state a { color: var(--teal); }
.event-link-hint { font-size: 0.75rem; color: var(--text-muted); margin: 0.35rem 0 0; font-weight: 400; }
/* Not an alert: for a non-admin host this is the normal way to run a party,
   not a problem to fix. */
.requests-manual-note {
    margin-bottom: 0.6rem;
    font-size: 0.8rem;
    color: var(--text-muted);
}
.requests-paused-note {
    margin-top: 0.4rem;
    font-size: 0.75rem;
    font-weight: 600;
    color: #7a5c00;
}
.spotify-connected-line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8rem;
    color: var(--text-muted);
    margin-bottom: 0.5rem;
}
.requests-history { margin-top: 0.8rem; }
.requests-history summary {
    font-size: 0.8rem;
    color: var(--text-muted);
    cursor: pointer;
}
.requests-history__list { margin-top: 0.4rem; }
/* Phone first: the song is the only thing worth reading here, so it gets the
   whole width and wraps in full, and the verdict follows underneath next to
   the actions. Sharing one line with a fixed verdict column left the title
   190px wide and every one of them ended in an ellipsis. */
.request-done {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.6rem;
    font-size: 0.82rem;
    padding: 0.5rem 0.1rem;
    border-bottom: 1px solid var(--border);
}
.request-done:last-child { border-bottom: 0; }
.request-done .btn { order: 3; flex: 0 0 auto; padding: 0.2rem 0.5rem; font-size: 0.75rem; }
.request-done__text { order: 1; flex: 1 1 100%; min-width: 0; }
.request-done__song { font-weight: 600; }
.request-done__by { color: var(--text-muted); }
.request-done__status { order: 2; flex: 0 0 auto; font-weight: 600; font-size: 0.75rem; }
.request-done__status.is-approved { color: #0a3622; }
.request-done__status.is-rejected { color: var(--text-muted); }
/* The first action absorbs the free space so the pair sits right, opposite
   the verdict. */
.request-done .btn:not(.btn ~ .btn) { margin-left: auto; }

@media (min-width: 560px) {
    /* Room for one line: verdict in a fixed leading column so the titles all
       start on the same edge, then the song, then the actions. The song
       truncates here rather than wrapping, to keep the rows scannable. */
    .request-done { flex-wrap: nowrap; gap: 0.6rem; }
    .request-done__status { order: 1; flex: 0 0 5.5rem; }
    .request-done__text {
        order: 2;
        flex: 1 1 auto;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    /* Free space belongs to the title now, not to a margin: with an auto
       margin the title truncated while half the row sat empty. */
    .request-done .btn:not(.btn ~ .btn) { margin-left: 0; }
}
.requests-status {
    min-height: 1.1rem;
    font-size: 0.85rem;
    margin-bottom: 0.4rem;
    color: var(--text-muted);
}
.requests-status.is-ok { color: #0a3622; font-weight: 600; }
.requests-status.is-error { color: #b91c1c; font-weight: 600; }
.requests-list__empty {
    text-align: center;
    padding: 1rem 0.5rem;
    color: var(--text-muted);
    font-size: 0.9rem;
}
.request-item {
    display: flex;
    align-items: center;
    /* Wraps so the three actions can carry real words instead of icons that
       all read as "no". On a narrow panel they drop to their own line rather
       than squeezing the song title out. */
    flex-wrap: wrap;
    gap: 0.6rem;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 0.5rem 0.7rem;
}
.request-item img {
    width: 44px;
    height: 44px;
    border-radius: 6px;
    object-fit: cover;
    background: var(--bg-soft);
    flex-shrink: 0;
}
.request-item__body { flex: 1 1 55%; min-width: 0; }
.request-item__title {
    display: block;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Two parts with different priorities: the artist may be cut, the guest name
   may not. The host moderates by who asked, so truncating "asked by Ada" to
   "asked by A..." removes the one thing they were reading. */
.request-item__meta {
    display: flex;
    min-width: 0;
    font-size: 0.8rem;
    color: var(--text-muted);
}
.request-item__artist {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.request-item__guest { flex: 0 0 auto; white-space: nowrap; }
/* Phone first: the three labelled actions take their own full-width line and
   wrap among themselves. With flex-shrink: 0 the cluster refused to shrink,
   so instead of wrapping it ran straight off the edge of the card and
   "Block guest" was cut in half. */
.request-item__actions { display: flex; flex-wrap: wrap; gap: 0.3rem; flex: 1 1 100%; }
.request-item__actions .btn { padding: 0.25rem 0.55rem; font-size: 0.8rem; }
@media (min-width: 560px) {
    /* Room for one line: actions back on the right of the song. */
    .request-item__actions { flex: 0 0 auto; margin-left: auto; }
}
