/* The room's "Now" screen: masthead, the "Now playing" panel with its
 * transport, the add-a-song field, the folded queue and its song sheet, the
 * invite in both its sizes, the speaker card, and the tab bar.
 *
 * Structure only, like every stylesheet outside skins/: no literal colour,
 * no [data-skin], no var() fallback. Where the design wants a token at partial
 * strength -- the accent ring on the add field, the tint behind the current
 * tab, the wash behind the speaker's warning glyph -- a pseudo-element paints
 * the token and carries the opacity, so the colour itself stays the skin's.
 */

/* ---------------------------------------------------------------- masthead */

.rk-roomhead { align-items: flex-start; }

.rk-roomhead__identity {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

/* One line, always. A chosen name takes an ellipsis; a code as the title is
   scaled to the line by the fitted-text controller instead, so all eight
   characters stay whatever face the skin brought. */
.rk-roomhead__name {
  display: block;
  min-width: 0;
  font-size: var(--text-2xl);
  line-height: 1.1;
  color: var(--ink-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.rk-roomhead__name-text {
  display: block;
  min-width: 0;
  font-size: calc(1em * var(--rk-fit-scale, 1));
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A fitted code is scaled to the line, to the hundredth; the pixel that
   rounding can leave over must not cost the last letter, so the fitted text
   never takes an ellipsis -- the title's own clip swallows the remainder. */
.rk-roomhead__name[data-controller] .rk-roomhead__name-text {
  overflow: visible;
  text-overflow: clip;
}

.rk-roomhead__meta {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* The people menu's trigger is a pill: who is here, how many, and the chevron
   that says it opens. */
.rk-people__trigger {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: var(--space-7);
  padding: 0 var(--space-3) 0 6px;
  background: var(--surface-panel);
  border: var(--edge-width) solid var(--edge-left);
  color: var(--ink-primary);
}

.rk-people__count {
  font-family: var(--control-font);
  font-size: var(--text-sm);
  white-space: nowrap;
}

.rk-people__chevron { flex: 0 0 auto; color: var(--ink-primary); }

/* Two letters have to fit a 32px face under every skin's label font: a step
   down from the smallest size, no tracking, and nothing spilling past the rim. */
.rk-avatar {
  min-width: 32px;
  padding: 0 2px;
  font-size: calc(var(--text-xs) - 1px);
  letter-spacing: 0;
  line-height: 1;
  overflow: hidden;
}

/* ---------------------------------------------------------- panel captions */

/* The right-hand side of a title bar: a count, a length, a status. Label type,
   so it reads as a caption to the title rather than a second title. */
.rk-panel__chrome-meta {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
  font-family: var(--font-label);
  font-size: var(--label-size);
  font-weight: var(--label-weight);
  letter-spacing: var(--label-tracking);
  text-transform: var(--label-transform);
  color: var(--ink-muted);
  white-space: nowrap;
}

/* A folded panel is its title bar and nothing else, so the bar has no body to
   cast its shadow onto. */
.rk-panel--collapsed .rk-panel__chrome { box-shadow: none; }

.rk-deck__chrome,
.rk-queue-panel .rk-panel__chrome,
.rk-invite__chrome { padding: 0 var(--space-3) 0 var(--space-4); }

/* ------------------------------------------------------------- now playing */

/* The panel's top is a Turbo target and its foot another, and neither may be a
   box of its own or the panel's column layout would see two blocks instead of
   the chrome, the stage and the tail. */
.rk-playback { display: contents; }

/* The volume popup opens over the readout, and a panel that clipped it would
   cut it off at the title bar. The chrome takes the panel's corner radius so
   nothing pokes past the rounded top with the clip gone. */
.rk-deck { overflow: visible; }

.rk-deck__chrome {
  border-radius: calc(var(--edge-radius) - var(--edge-width))
                 calc(var(--edge-radius) - var(--edge-width)) 0 0;
}

.rk-deck__speaker { color: var(--positive); }
.rk-deck__speaker--off { color: var(--ink-muted); }

/* The offer is a gate around the card, not a box of its own. */
.rk-speaker__offer { display: contents; }
.rk-deck__speaker-icon { flex: 0 0 auto; }

.rk-deck__stage {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-4) var(--action-lift);
}

/* The readout sits a little taller in the panel than the primitive's default. */
.rk-deck__stage .rk-readout { padding: 11.5px var(--space-3); }

.rk-deck__progress {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.rk-deck__times {
  display: flex;
  justify-content: space-between;
  font-family: var(--font-label);
  font-size: var(--label-size);
  letter-spacing: 0.12em;
  color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

.rk-deck__tail {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-1) var(--space-4) var(--space-4);
}

/* Nothing to say and nothing to press: a guest with no vote to cast and no
   notice to read gets a panel that ends at the progress line. */
.rk-deck__tail:not(:has(.rk-controls:not([hidden]) .rk-button:not([hidden] *),
                        .rk-listener,
                        .rk-notices__item,
                        .rk-playback__notice:not(:empty))) { display: none; }

/* The silent room: one thing to do, said once. */
.rk-deck__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-5) var(--space-4);
  text-align: center;
}

.rk-deck__viz {
  flex: 0 0 auto;
  width: 100%;
  max-width: 260px;
  min-height: 0;
}

.rk-deck__empty-copy {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.rk-deck__empty-copy .rk-text { max-width: 280px; }

.rk-deck__empty .rk-playback__paused,
.rk-deck__empty .rk-playback__disconnected {
  width: 100%;
  text-align: left;
}

/* --------------------------------------------------------------- transport */

/* Every button the row holds gets an equal share of it, however many there
   are: three for the host, two for a guest with queue control, the vote alone
   for everyone else. */
.rk-controls {
  position: relative;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 10px;
}

/* A host's row can hold nothing but a volume the speaker gate has hidden, so
   only a button that is showing keeps the row open. */
.rk-controls:not(:has(.rk-button:not([hidden] *))) { display: none; }

/* button_to and form_with wrap their buttons, and Clear queue has a stream
   target around its form; the button is the grid item. */
.rk-controls__form,
.rk-controls__clear { display: contents; }

.rk-controls__vote:not(:has(*)) { display: none; }

.rk-controls__button {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  width: 100%;
  min-width: 0;
  height: 58px;
  padding: 0;
  font-size: 12px;
  white-space: normal;
  overflow-wrap: anywhere;
  text-align: center;
  list-style: none;
  cursor: pointer;
}

.rk-controls__button::-webkit-details-marker { display: none; }

.rk-controls__icon { display: var(--action-icon-display); }

/* On a phone the host's actions wrap two to a row so translated labels keep
   their words intact, including skins with wide pixel lettering. Flex rather
   than a two-column grid: whichever buttons are showing, an odd one out takes
   the whole last row instead of leaving an empty half beside it. */
@media (max-width: 480px) {
  .rk-controls {
    display: flex;
    flex-wrap: wrap;
  }

  .rk-controls > *,
  .rk-controls__form > * { flex: 1 1 calc(50% - 5px); }

  .rk-controls__vote { flex-basis: 100%; }
}

/* The volume opens over the row, inside the panel. `static` so the popup is
   placed against the row, not against its own button. */
.rk-controls__volume { position: static; }

.rk-controls__volume[open] .rk-controls__volume-trigger {
  background-color: var(--control-face-active);
  border-top-color: var(--control-dark);
  border-left-color: var(--control-dark);
  border-right-color: var(--control-light);
  border-bottom-color: var(--control-light);
  border-image-source: var(--control-frame-image-active);
  box-shadow: var(--control-shadow-active);
}

.rk-controls__popup {
  position: absolute;
  z-index: 5;
  left: 0;
  right: 0;
  bottom: calc(100% + var(--space-2));
  padding: var(--space-3);
  background: var(--surface-panel);
  -webkit-backdrop-filter: var(--surface-blur);
  backdrop-filter: var(--surface-blur);
  border-style: solid;
  border-width: var(--edge-width);
  border-top-color: var(--edge-top);
  border-left-color: var(--edge-left);
  border-right-color: var(--edge-right);
  border-bottom-color: var(--edge-bottom);
  border-radius: var(--edge-radius);
  box-shadow: var(--edge-shadow-float);
}

/* ------------------------------------------------------------ speaker card */

/* The player's mount fills the player: the YouTube iframe lands inside it and
   has to be given the whole 16:9 box. */
.rk-speaker__mount,
.rk-speaker__mount iframe {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 200px;
  border: 0;
}

/* The slot and the section are Turbo targets and controller mounts, not boxes:
   the card, the player and the status line each stand in the room column on
   their own, so a hidden one leaves no gap behind. */
.rk-speaker-player-slot,
.rk-speaker { display: contents; }

.rk-speaker-card {
  gap: var(--space-3);
  padding: var(--space-4);
}

.rk-speaker-card__head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

/* The warning glyph on a wash of its own colour. */
.rk-speaker-card__icon {
  position: relative;
  isolation: isolate;
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--edge-radius-sm);
  color: var(--danger);
  overflow: hidden;
}

.rk-speaker-card__icon::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  background: var(--danger);
  opacity: 0.12;
}

.rk-speaker-card__copy {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.rk-speaker-card__title {
  font-size: var(--text-lg);
  line-height: 1.25;
  text-wrap: initial;
}

.rk-speaker-card__tv {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-height: var(--space-6);
  color: var(--accent);
  font-size: var(--text-sm);
  text-decoration: none;
}

/* The television is the other device; on the television itself the offer is
   circular. */
.rk-tv .rk-speaker-card__tv { display: none; }

.rk-speaker__status-row { justify-content: center; }
.rk-speaker__status-row:not(:has(p:not([hidden]):not(:empty))) { display: none; }

/* Listening along is one ghost row at the foot of the panel: hear it here, or
   stop. The player stays out of the way until it is switched on. */
.rk-listener {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

.rk-listener-player-slot { display: contents; }

.rk-listener__controls {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: 0;
  border: 0;
}

.rk-listener__toggle {
  height: var(--space-7);
  min-height: var(--space-7);
  padding: 0 var(--space-3);
  font-size: var(--text-sm);
}

.rk-listener__status:empty { display: none; }

/* The listener's own player, when it is on: the action row above it is where
   the transport ends and the player begins. */
.rk-listener__player {
  min-height: 0;
  padding-top: var(--action-divider-gap);
  border-top: var(--action-divider-width) solid var(--edge-hairline);
}

/* --------------------------------------------------------------- add field */

/* The field reads as the room's primary action: accent glyph, a real label in
   control type, and the hint of what else it takes tucked to the right. The
   accent ring is painted by a pseudo-element so the accent can sit at partial
   strength without the stylesheet ever mixing a colour. */
.rk-add-field {
  position: relative;
  cursor: text;
}

.rk-add-field::after {
  content: "";
  position: absolute;
  inset: calc(var(--edge-width) * -1);
  border: var(--edge-width) solid var(--accent);
  border-radius: inherit;
  opacity: 0.35;
  pointer-events: none;
}

.rk-add-field:focus-within::after { opacity: 0.7; }

.rk-add-field__icon { flex: 0 0 auto; color: var(--accent); }

.rk-add-field__input {
  font-family: var(--control-font);
  font-size: var(--control-size);
  color: var(--ink-primary);
}

.rk-add-field__input::placeholder {
  color: var(--ink-primary);
  opacity: 1;
}

.rk-add-field__hint {
  flex: 0 0 auto;
  color: var(--ink-muted);
  font-size: 12px;
  white-space: nowrap;
}

.rk-add-field:focus-within .rk-add-field__hint,
.rk-add-field__input:not(:placeholder-shown) ~ .rk-add-field__hint { display: none; }

/* ------------------------------------------------------------------- queue */

/* Phone rows: index, title over artist and who added it, length, and the
   chevron that says the row opens. */
/* No row gap: the index spans two grid rows so a badge line can sit under the
   text, and an empty second row must not open a gap of its own. */
.rk-screen .rk-queue__row,
.rk-screen .rk-queue__trigger {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr) auto;
  align-items: center;
  gap: 0 10px;
  min-height: var(--row-height);
  padding: 10.5px var(--space-3);
}

.rk-screen .rk-queue__trigger { grid-template-columns: 24px minmax(0, 1fr) auto 18px; }

.rk-screen .rk-queue__index { grid-column: 1; grid-row: 1 / span 2; }
.rk-screen .rk-queue__text { grid-column: 2; }
.rk-screen .rk-queue__duration { grid-column: 3; }
.rk-screen .rk-queue__chevron { grid-column: 4; grid-row: 1 / span 2; }
.rk-screen .rk-queue__badges { grid-column: 2 / -1; justify-content: flex-start; margin-top: var(--space-1); }

.rk-screen .rk-queue--dense .rk-queue__row {
  min-height: var(--row-height);
  font-family: var(--font-body);
  font-size: var(--text-md);
  border-top: var(--edge-width) solid var(--edge-hairline);
}

.rk-screen .rk-queue--dense .rk-queue__text {
  flex-direction: column;
  align-items: stretch;
  gap: 2px;
}

.rk-screen .rk-queue--dense .rk-queue__title {
  font-size: var(--text-md);
  font-weight: 600;
}

.rk-screen .rk-queue--dense .rk-queue__artist {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  letter-spacing: 0;
  text-transform: none;
}

.rk-queue__chevron {
  flex: 0 0 auto;
  color: var(--ink-on-row-dim);
  opacity: 0.7;
}

.rk-queue__trigger {
  width: 100%;
  color: inherit;
  text-align: left;
  border-radius: 0;
}

.rk-queue__trigger:hover { background: var(--surface-raised); }
.rk-queue__trigger:focus-visible { outline-offset: -2px; }
.rk-queue .rk-queue__row--interactive { display: block; padding: 0; }

/* The fold: one line naming what comes next, and the rest of the queue behind it. */
.rk-queue__fold,
.rk-queue__fold-details { display: block; }

.rk-queue__fold-summary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 40px;
  padding: 0 var(--space-3);
  border-top: var(--edge-width) solid var(--edge-hairline);
  color: var(--ink-muted);
  font-size: var(--text-sm);
  text-align: center;
  list-style: none;
  cursor: pointer;
}

.rk-queue__fold-summary::-webkit-details-marker { display: none; }

.rk-queue__folded {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* -------------------------------------------------------------- song sheet */

/* The song's actions rise from the foot of the screen, inset from its edges,
   over a dimmed room. The native popover puts it in the top layer; this is
   only where it lands. */
.rk-queue__popup {
  position: fixed;
  inset: auto var(--space-2) var(--space-2);
  width: auto;
  max-height: calc(100dvh - var(--space-4));
  margin: 0;
  padding: 0 var(--space-4) var(--space-5);
  overflow-y: auto;
  color: var(--ink-primary);
  background: var(--surface-panel);
  -webkit-backdrop-filter: var(--surface-blur);
  backdrop-filter: var(--surface-blur);
  border-style: solid;
  border-width: var(--edge-width);
  border-top-color: var(--edge-top);
  border-left-color: var(--edge-left);
  border-right-color: var(--edge-right);
  border-bottom-color: var(--edge-bottom);
  border-radius: var(--edge-radius);
  box-shadow: var(--edge-shadow-float);
  font-family: var(--font-body);
}

.rk-queue__popup:popover-open {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* The room behind the sheet: the page colour at part strength over a blur, so
   the scrim is translucent on every skin, however opaque its surfaces. */
.rk-queue__popup::backdrop {
  background: var(--bg-page);
  opacity: 0.62;
  -webkit-backdrop-filter: var(--surface-blur);
  backdrop-filter: var(--surface-blur);
}

.rk-sheet__handle {
  display: block;
  flex: 0 0 auto;
  width: 40px;
  height: 4px;
  margin: 10px auto 0;
  border-radius: var(--edge-radius-pill);
  background: var(--ink-muted);
  opacity: 0.5;
}

.rk-sheet__head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.rk-sheet__position {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--edge-radius-pill);
  background: var(--surface-raised);
  color: var(--ink-primary);
  font-family: var(--font-readout);
  font-size: var(--text-md);
  font-variant-numeric: tabular-nums;
}

.rk-sheet__copy {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.rk-sheet__title {
  font-size: var(--text-lg);
  line-height: 1.25;
  overflow-wrap: anywhere;
  text-wrap: initial;
}

.rk-sheet__meta { color: var(--ink-muted); font-size: var(--text-sm); }
.rk-sheet__eta { color: var(--accent); font-size: var(--text-sm); }

/* The close sits in a faint rounded square: the ghost's face, with a hairline
   rim, so the glyph reads as a button rather than a stray mark. */
.rk-queue__close {
  flex: 0 0 auto;
  width: 37px;
  min-width: 37px;
  height: 37px;
  min-height: 37px;
  border-radius: 10px;
  background-color: var(--surface-row);
  box-shadow: inset 0 0 0 var(--edge-width) var(--edge-hairline);
}

.rk-sheet__moves {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: 10px;
}

.rk-sheet__move {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  min-width: 0;
  height: 64px;
  padding: 0;
  font-size: 12px;
}

/* The vote is a row of the sheet: what it does, how far along it is, and the
   count as a badge. Cast, it stays as information. */
.rk-sheet__vote {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--edge-radius-sm);
  background: var(--surface-row);
  color: var(--ink-primary);
  text-decoration: none;
}

.rk-sheet__vote[aria-disabled] { color: var(--ink-secondary); }

.rk-sheet__vote-copy {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.rk-sheet__vote-title { font-size: 14px; }
.rk-sheet__vote-sub { font-size: 12px; color: var(--ink-muted); }

/* ------------------------------------------------------------------ invite */

/* Two sizes of the same card, chosen by whether the room is playing. The
   join-code component's boxes dissolve into the panel's grid so the QR, the
   code, the fold and the share button can each take their place in the row
   whatever order the component drew them in. */
.rk-invite__body {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto auto;
  grid-template-areas:
    "qr label fold share"
    "qr code fold share"
    "status status status status";
  align-items: center;
  column-gap: var(--space-3);
  row-gap: 1px;
  min-height: var(--row-height);
  padding: 10px var(--space-3) 10px 10px;
}

.rk-invite__chrome { display: none; }

.rk-invite__body .rk-joincode,
.rk-invite__body .rk-joincode__details,
.rk-invite__body [id] { display: contents; }

.rk-invite__body .rk-joincode__qr {
  grid-area: qr;
  width: 40px;
  padding: 2px;
  border-radius: 8px;
}

.rk-invite__body .rk-joincode__details > .rk-label { grid-area: label; }

.rk-invite__body .rk-joincode__code {
  grid-area: code;
  font-size: var(--text-lg);
  line-height: 1.2;
  color: var(--ink-primary);
}

/* The row carries the caption; the open card carries the instruction. */
.rk-invite__body .rk-joincode__address { display: none; }

.rk-invite__body .rk-joincode__details > .rk-button {
  grid-area: share;
  height: 40px;
  padding: 0 14px;
  border-radius: var(--edge-radius-sm);
  font-size: var(--text-sm);
  background-color: var(--control-face);
  color: var(--control-ink);
  border-top-color: var(--control-light);
  border-left-color: var(--control-light);
  border-right-color: var(--control-dark);
  border-bottom-color: var(--control-dark);
  box-shadow: var(--control-shadow);
  text-shadow: var(--control-content-shadow);
}

.rk-invite__body [data-share-invite-target="status"] { grid-area: status; }
.rk-invite__body [data-share-invite-target="status"]:empty { display: none; }

.rk-invite__row-fold {
  grid-area: fold;
  display: flex;
  align-items: center;
}

/* A silent room has one call to action, the button in the empty state, so the
   add field stands down until a song plays; and a queue with nothing in it --
   nobody's songs waiting, not even this person's -- has nothing to show under
   a panel that already says the room is silent. */
.rk-screen:has(.rk-playback--idle) .rk-room-search,
.rk-screen:has(.rk-playback--idle) .rk-panel-slot:has(.rk-queue__empty):not(:has(.rk-staged__row)) .rk-queue-panel {
  display: none;
}

/* Nothing playing: inviting is the other thing a host does in an empty room,
   so the card opens up -- title bar, a QR big enough to scan across a table,
   the code in display size. */
.rk-screen:has(.rk-playback--idle) .rk-invite__chrome { display: flex; }
.rk-screen:has(.rk-playback--idle) .rk-invite__row-fold { display: none; }

.rk-screen:has(.rk-playback--idle) .rk-invite__body {
  grid-template-columns: 96px minmax(0, 1fr);
  grid-template-areas:
    "qr code"
    "qr label"
    "qr share"
    "status status";
  column-gap: var(--space-4);
  row-gap: var(--space-1);
  padding: 14px var(--space-4);
}

.rk-screen:has(.rk-playback--idle) .rk-invite__body .rk-joincode__qr {
  width: 96px;
  padding: var(--space-1);
  border-radius: 10px;
}

.rk-screen:has(.rk-playback--idle) .rk-invite__body .rk-joincode__code {
  font-size: var(--text-xl);
}

.rk-screen:has(.rk-playback--idle) .rk-invite__body .rk-joincode__details > .rk-label { display: none; }

.rk-screen:has(.rk-playback--idle) .rk-invite__body .rk-joincode__address {
  grid-area: label;
  display: block;
  color: var(--ink-muted);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  letter-spacing: 0;
  text-transform: none;
}

.rk-screen:has(.rk-playback--idle) .rk-invite__body .rk-joincode__details > .rk-button {
  justify-self: start;
  margin-top: var(--space-1);
}
