/* The listening page (listen.blade.php). */
.intro { color: var(--muted); font-size: 19px; }
/* The console, in page order: Find a Stream, the way in (signed out), Your Mix (while listening), then Refer a Broadcaster. One column on a phone,
   where the way in is drawn first (order) so a guest meets it before Live Now. While listening, a computer puts Find, with the referral under it, on the left
   and Your Mix, the full height, on the right. */
.console { display: grid; gap: var(--s3); align-items: start; }
.console > section, .console > #refer-panel { margin: 0; min-width: 0; }
#way-in { order: -1; }
@media (min-width: 900px) {
  .console:has(#deck:not([hidden])) { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); grid-template-rows: auto 1fr; }
  .console:has(#deck:not([hidden])) #find-panel { grid-column: 1; grid-row: 1; }
  .console:has(#deck:not([hidden])) #refer-panel { grid-column: 1; grid-row: 2; }
  .console:has(#deck:not([hidden])) #deck { grid-column: 2; grid-row: 1 / span 2; align-self: stretch; }
  .console:has(#deck:not([hidden])) .while-listening-hidden { display: none; }
}
.console:not(:has(#deck:not([hidden]))) > * { width: 100%; max-width: 640px; margin-inline: auto; }
/* From the mtm_frame cookie (js/first-paint.js), until listen.js has the site's own answer. */
html[data-roles~="member"]:not(.listen-answered) [data-signed-out-only] { display: none; }
/* Search is for members: the cookie's roles decide as served, listen.js adds listen-signed-out when the site says otherwise. */
html:not([data-roles~="member"]) [data-member-only], html.listen-signed-out [data-member-only] { display: none; }
/* One field, and the icon-only Find beside it. */
#find { margin: 0 0 var(--s1); }
.with-button { display: flex; gap: var(--s1); }
.search-field { position: relative; flex: 1; min-width: 0; }
.search-field input { width: 100%; }
.btn.icon-only { flex: none; width: 44px; padding: 0; }
#find .hint { margin: var(--s1) 0 0; }
/* Who a typed name matches, on any platform. */
#matches { margin: var(--s2) 0 0; }
#matches[hidden] { display: none; }
.rows .grow { flex: 1; min-width: 140px; }
.rows .row-title { font-family: var(--font-ui); font-weight: 500; overflow-wrap: anywhere; }
.platform-mark { flex: none; width: 22px; height: 22px; }
.platform-mark.is-twitch { color: var(--twitch); }
.platform-mark.is-youtube { color: var(--youtube); }
.platform-mark.is-kick { color: var(--kick); }
.tag.live { color: var(--white); background: var(--live); border-color: var(--live); }
/* The way in: the button, then the line under it. */
#way-in-guest { display: grid; gap: var(--s1); justify-items: start; }
#way-in-guest .hint { margin: 0; }
#way-in-waitlisted { margin: 0; }
/* One line is kept for it while empty, so what is under it does not move when it speaks. */
#message { color: var(--muted); margin: 12px 0; }
/* Kept in the page (a status region) but no room while empty. */
#message:empty { margin: 0; }
#message.error { color: var(--error); }
#message.waiting { color: var(--warning); }
#message.rule { color: var(--text); }
#message.error::before { content: "! " / ""; font-weight: 700; }
/* A failed join, said where the player was (#807): the look of #message for each tone. */
#join-failed { color: var(--muted); margin: auto 0; padding: var(--s3) 0; }
#join-failed.error { color: var(--error); }
#join-failed.waiting { color: var(--warning); }
#join-failed.rule { color: var(--text); }
#join-failed.error::before { content: "! " / ""; font-weight: 700; }
/* Where the listener pass is in use, with a Disconnect on each (#123, StemStream.passPlaces). */
#join-failed .ss-places { margin-top: var(--s2); color: var(--text); }
#join-failed .ss-places p { margin: var(--s1) 0; }
#join-failed .ss-places ul { list-style: none; margin: 0; padding: 0; }
#join-failed .ss-places li { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s2); padding: var(--s1) 0; }
#join-failed .ss-places li span { color: var(--muted); }
#join-failed .ss-places li button { margin-left: auto; }
/* The broadcaster found: a card with their name, who they are, and whether they are live. */
#found { margin: 12px 0; display: grid; gap: var(--s2); }
#found-banner { display: block; width: 100%; aspect-ratio: 3 / 1; object-fit: cover; border-radius: 8px; background: var(--raised); opacity: 0; }
#found-banner.loaded { opacity: 1; }
@media (prefers-reduced-motion: no-preference) { #found-banner { transition: opacity 0.2s; } }
/* On a phone the name and Listen come first and the banner follows, so a banner arriving never moves Listen. */
@media (max-width: 600px) { #found-banner { order: 1; } }
#found-banner[hidden] { display: none; }
.card-head { display: flex; align-items: center; gap: 12px; }
.avatar { flex: none; width: 64px; height: 64px; border-radius: 50%; overflow: hidden; display: grid; place-items: center; background: var(--raised); color: var(--text); font-family: var(--font-ui); font-size: 26px; font-weight: 600; }
.avatar.small { width: 32px; height: 32px; font-size: 15px; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.who { min-width: 0; flex: 1; }
.who h3 { margin: 0; font-size: 20px; overflow-wrap: anywhere; }
.who p { margin: 0; color: var(--muted); }
.who .state { display: flex; align-items: center; gap: 8px; }
.state.on::before { content: ""; width: 10px; height: 10px; border-radius: 50%; background: var(--copper); box-shadow: 0 0 0 3px var(--copper-tint); }
#listen { justify-self: start; }
#offline { color: var(--muted); margin: 0; }
/* Live on a platform (#1565): the channel comes before Listen. */
.channel-first { display: grid; gap: var(--s2); justify-items: start; padding: 12px; border: 1px solid var(--copper-line); border-radius: 8px; background: var(--copper-tint); }
.channel-first[hidden] { display: none; }
.channel-first h4, .channel-first p { margin: 0; }
/* What is playing: the light, the broadcaster's name and where they stream, and Stop Listening at the end of the line. */
#now { display: flex; align-items: center; gap: 12px; margin: 0 0 var(--s2); flex-wrap: wrap; }
#now .who { flex: 1 1 160px; }
#now b { display: block; font-family: var(--font-ui); font-size: 20px; overflow-wrap: anywhere; }
#now-meta { color: var(--muted); }
/* Amber while connected and waiting for the broadcaster's sources; crimson only once audio flows. */
#now-dot { background: var(--warning); box-shadow: none; }
#now-dot.live { background: var(--live); box-shadow: 0 0 0 4px var(--live-glow); }
#mixer:not(:empty) { margin-bottom: var(--s3); }
/* The deck is there while something plays or a join failed (listen.js hides it otherwise). */
#deck { display: flex; flex-direction: column; }
#deck[hidden] { display: none; }
#report-problem-line { margin: auto 0 0; padding-top: var(--s2); }
#live-now { color: var(--muted); padding: var(--s1) 0; }
#live-now h3 { margin: var(--s2) 0 var(--s1); color: var(--text); }
#live-list { list-style: none; margin: 0; padding: 0; }
#live-list li { display: flex; align-items: center; gap: 12px; padding: var(--s1) 0; border-bottom: 1px solid var(--line-soft); }
#live-list b { flex: 1; min-width: 0; color: var(--text); font-family: var(--font-ui); overflow-wrap: anywhere; }
#refer-panel .stack { max-width: none; }
/* The mixer's own sheet (viewer/core/ui.js) is injected after this one and sized for the extension's
   compact panel; here every target is 44px (docs/design.md, Tap targets) and the fader's 100% mark is left off. */
#mixer .ss-q { width: 44px; height: 44px; min-height: 44px; margin: -6px 0; }
#mixer .ss-q::before { inset: 12px; }
#mixer .ss-mute:not(.ss-all-mute) { width: 44px; min-height: 44px; }
#mixer .ss-all button.ss-all-mute, #mixer .ss-sleep-go, #mixer .ss-sleep select { min-height: 44px; }
#mixer .ss-tick, #mixer .ss-hundred { display: none; }
/* The broadcaster's links (listen-links.js). The symbols' sheet takes no room. */
.link-icons { position: absolute; width: 0; height: 0; overflow: hidden; }
#broadcaster-links { color: var(--muted); padding: var(--s1) 0; }
#broadcaster-links h3 { margin: var(--s2) 0 var(--s1); color: var(--text); }
.link-list { list-style: none; margin: 0; padding: 0; }
.link-list li { display: flex; align-items: center; gap: var(--s1); padding: var(--s1) 0; border-bottom: 1px solid var(--line-soft); }
.link-list li[hidden], .show-all[hidden] { display: none; }
.show-all { margin-top: var(--s1); }
.link-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 12px; min-height: 44px; }
.link-icon { flex: none; width: 24px; height: 24px; color: var(--copper); }
.link-text { display: grid; min-width: 0; }
.link-title { font-family: var(--font-ui); color: var(--text); overflow-wrap: anywhere; }
.link-domain { color: var(--muted); font-size: 15px; overflow-wrap: anywhere; }
.link-report { flex: none; display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--s1); color: var(--muted); font-size: 15px; }
.link-gate { margin: var(--s2) 0 var(--s1); }
.link-gate p { margin: 0 0 var(--s1); }
.link-gate[hidden] { display: none; }
#crash-preview { margin: var(--s1) 0 var(--s2); padding: var(--s1) 12px; max-height: 240px; overflow: auto; background: var(--bg); border: 1px solid var(--line); border-radius: 6px; font-family: monospace; font-size: 14px; white-space: pre-wrap; overflow-wrap: anywhere; }
/* Refer a Broadcaster is in the page for everyone: the roles (js/first-paint.js) decide who sees it and its form. */
#refer-panel { display: none; }
html[data-roles~="referrer"] #refer-panel { display: block; }
html:not([data-roles~="recommender"]) #refer-panel .refer-form, html[data-roles~="recommender"] #refer-panel .refer-hint { display: none; }
/* Reserved slots (docs/design.md, First paint): skeletons stand where fetched content goes, so it fills them without moving the page. */
.skeleton { background: var(--raised); border-radius: 6px; }
@media (prefers-reduced-motion: no-preference) { .skeleton { animation: skeleton-pulse 1.4s ease-in-out infinite; } }
@keyframes skeleton-pulse { 50% { opacity: 0.5; } }
#lookup-slot { display: grid; }
#lookup-slot > * { grid-area: 1 / 1; }
#found-skeleton { display: none; margin: 12px 0; height: 128px; }
html.finding #found-skeleton, html.lookup-missed #found-skeleton { display: block; }
#find-panel:has(#found:not([hidden])) #found-skeleton { display: none; }
/* Nothing found: the same box, now an empty state with the not-found words in it. */
html.lookup-missed #found-skeleton { animation: none; background: none; border: 1px dashed var(--line); }
html.lookup-missed #message { align-self: center; justify-self: center; text-align: center; margin: 12px 24px; z-index: 1; }
#live-list li.skeleton { height: 61px; padding: 0; border-bottom: 0; }
/* Until the first answer only: the heading and one row, so a list of one does not move what is under it. Then it is as tall as its list. */
#live-now[aria-busy="true"] { min-height: 133px; }
/* The only one live is the broadcaster shown above: no heading over nothing. */
#live-now.only-found { display: none; }
/* A broadcaster named in the address (html.finding while they are looked up, html.named until the list answers), or already found, may be the only one live: the list waits for them, so it neither reserves a row nor shows them twice and then collapses. */
html.finding #live-now, html.named #live-now[aria-busy="true"], #find-panel:has(#found:not([hidden])) #live-now[aria-busy="true"] { display: none; }
