/* Product-screen scaffolding for the navigable prototype.
 *
 * Structure only, like every stylesheet outside skins/: no literal colour,
 * no [data-skin], no var() fallback. Everything visual is a contract token.
 */

/* -------------------------------------------------------- the phone column */

.rk-screen {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  width: 100%;
  max-width: 430px;
  min-height: 100dvh;
  margin-inline: auto;
  padding: var(--space-4) var(--space-4) var(--space-7);
}

/* ------------------------------------------------------- the page's light */

/* One light source behind everything, drifting. It is a sibling of the content
   at z-index -1 rather than a background on .rk-app because a background cannot
   move without repainting; a transformed layer can, on the compositor.
   The layer is not required to cover the viewport at every moment of the drift,
   and sizing it so would cost real compositor memory on a wide screen. It is
   required to *contain* its lights: --bg-page-lights keeps every ellipse inside
   the layer (the contract says so), so the layer's own edge is always at alpha
   zero and a corner of it swinging into view shows nothing at all. What the size
   buys is scale -- the lights and their travel are percentages of this box, so
   170% is how large a light is and how far it goes. `pointer-events: none`
   because a layer over the whole screen must still catch nothing.

   The skin decides whether there is a light at all: with --bg-page-lights at
   `none` the layer paints nothing, and with --bg-lights-drift at `none` there is
   no animation for the compositor to promote a layer for, so a flatly-lit skin
   pays neither the pixels nor the frame. */
.rk-lights {
  position: fixed;
  z-index: -1;
  top: 50%;
  left: 50%;
  width: 170vw;
  height: 170vh;
  translate: -50% -50%;
  pointer-events: none;
  background-image: var(--bg-page-lights);
  background-repeat: no-repeat;
  animation: var(--bg-lights-drift);
}

.rk-room-shell,
.rk-room-shell__now { min-height: 100dvh; }

.rk-room-shell {
  position: relative;
  isolation: isolate;
}

.rk-room-shell--overlay .rk-room-shell__now {
  height: 100dvh;
  overflow: hidden;
  opacity: 0.55;
  filter: blur(2px);
  transform: scale(0.985);
  transform-origin: center;
}

.rk-room-overlay {
  position: fixed;
  z-index: 20;
  inset: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.rk-room-overlay > .rk-screen {
  background-color: var(--bg-page);
  background-image: var(--bg-page-texture);
  box-shadow: var(--edge-shadow-float);
}

.rk-room-overlay--tv {
  background-color: var(--bg-page);
  background-image: var(--bg-page-texture);
}

.rk-room-shell--tv .rk-room-shell__now { visibility: hidden; }

.rk-screen__nav {
  position: sticky;
  bottom: 0;
  margin-top: auto;
}

.rk-screen__bottom {
  position: sticky;
  z-index: 10;
  bottom: 0;
  margin-top: auto;
}

.rk-screen__bottom .rk-screen__nav {
  position: static;
  margin-top: 0;
}

.rk-room-toolbar {
  position: relative;
  display: flex;
  justify-content: center;
  padding: var(--space-1) var(--space-2);
  background: var(--surface-panel);
  border-top: var(--edge-width) solid var(--edge-hairline);
}

/* The frost goes on a pseudo-element rather than on .rk-room-toolbar itself,
   because the volume popup is a DOM descendant of the toolbar: an element
   carrying a backdrop-filter becomes its descendants' backdrop root, so
   frosting the strip directly would leave the popup above it with nothing to
   blur. A pseudo-element has no descendants to root, so both frost. It sits in
   the negative layer, behind the toolbar's own translucent face, which is what
   lets that face tint the blurred page rather than hide it. */
.rk-room-toolbar::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  -webkit-backdrop-filter: var(--surface-blur);
  backdrop-filter: var(--surface-blur);
}

.rk-room-toolbar__buttons {
  display: flex;
  justify-content: center;
  gap: var(--space-1);
}

.rk-room-tool { position: static; }

.rk-room-tool__trigger {
  list-style: none;
  width: var(--control-height);
}

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

.rk-room-tool[open] .rk-room-tool__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-room-tool__icon { flex: 0 0 auto; }

/* A slot is a Turbo target, not a box. The room column is a flex stack with a
   gap, so a slot whose panel is minimised has to leave no trace at all. */
.rk-panel-slot { display: contents; }

.rk-room-tool__panel {
  position: absolute;
  z-index: 1;
  left: 50%;
  bottom: calc(100% - var(--edge-width));
  width: min(360px, calc(100vw - var(--space-6)));
  padding: var(--space-3);
  transform: translateX(-50%);
  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);
}

/* A minimised panel arrives with its own surface -- title bar, edges, frost --
   so the popup around it drops back to being only a position. */
.rk-room-tool__panel--bare {
  padding: 0;
  background: none;
  border-width: 0;
  border-radius: 0;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* -------------------------------------------------------------- playback */

.rk-playback,
.rk-speaker,
.rk-listener {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

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

.rk-volume__heading { justify-content: space-between; }
.rk-volume__heading output { font-variant-numeric: tabular-nums; }
.rk-volume__control .rk-meter { flex: 1 1 auto; }
.rk-volume__control .rk-button { flex: 0 0 auto; min-width: var(--control-height); }

.rk-volume__meter { height: var(--control-height); }

.rk-volume__slider {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.rk-volume__slider:disabled { cursor: not-allowed; }

.rk-volume__meter:focus-within {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.rk-volume__icon {
  display: block;
  width: 16px;
  height: 16px;
  background-color: currentColor;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: 16px 16px;
  mask-size: 16px 16px;
}

.rk-volume__icon--down {
  -webkit-mask-image: var(--control-decrement-icon);
  mask-image: var(--control-decrement-icon);
}

.rk-volume__icon--up {
  -webkit-mask-image: var(--control-increment-icon);
  mask-image: var(--control-increment-icon);
}

.rk-tv .rk-volume { display: none; }

.rk-speaker-player-slot > .rk-tv__standby { display: none; }

.rk-tv .rk-speaker-player-slot > .rk-tv__standby { display: flex; }

.rk-playback__clock {
  color: var(--accent);
  font-family: var(--font-readout);
  font-variant-numeric: tabular-nums;
}

.rk-playback__paused,
.rk-playback__disconnected,
.rk-playback__notice:not(:empty) {
  padding: var(--space-2) var(--space-3);
  border: var(--edge-width) solid var(--danger);
  color: var(--danger);
  font-family: var(--font-label);
  font-size: var(--label-size);
}

.rk-playback__paused {
  border-color: var(--accent-secondary);
  color: var(--accent-secondary);
}

.rk-disclosure summary {
  color: var(--ink-muted);
  cursor: pointer;
  font-family: var(--font-label);
  font-size: var(--label-size);
}

.rk-disclosure p { margin-top: var(--space-2); }

.rk-settings-advanced__body { margin-top: var(--space-3); }
.rk-settings-advanced__body p { margin-top: 0; }

.rk-danger-actions {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
  min-width: 0;
}

.rk-danger-actions__form { min-width: 0; }

.rk-danger-actions .rk-button {
  min-width: 0;
  height: auto;
  min-height: var(--control-height);
  padding-block: var(--space-2);
  white-space: normal;
}

@media (max-width: 360px) {
  .rk-danger-actions { grid-template-columns: minmax(0, 1fr); }
}

.rk-people-names { overflow-wrap: anywhere; }

.rk-member + .rk-member {
  padding-top: var(--space-3);
  border-top: var(--edge-width) solid var(--edge-hairline);
}

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

.rk-speaker__player {
  width: 100%;
  min-width: 200px;
  min-height: 200px;
  aspect-ratio: 16 / 9;
  background: var(--surface-sunken);
}

.rk-speaker__standby { display: none; }

.rk-speaker__player iframe {
  display: block;
  width: 100%;
  height: 100%;
  min-width: 200px;
  min-height: 200px;
  border: 0;
}

.rk-listener__player {
  width: 100%;
  min-width: 200px;
  min-height: 200px;
  aspect-ratio: 16 / 9;
  background: var(--surface-sunken);
}

.rk-listener__mount,
.rk-listener__mount iframe {
  display: block;
  width: 100%;
  height: 100%;
  min-width: 200px;
  min-height: 200px;
  border: 0;
}

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

.rk-listener__controls p { flex: 1 1 260px; }

.rk-listener__status {
  font-family: var(--font-label);
  font-size: var(--label-size);
  text-align: center;
}

.rk-speaker__controls {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.rk-speaker__status-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: baseline;
}

.rk-speaker__wake-status {
  color: var(--accent-secondary);
  font-family: var(--font-label);
  font-size: var(--label-size);
}

/* ---------------------------------------------------------- room actions */

/* Pause, resume and skip act on the song on screen, so they read as part of the
   player rather than as a free-floating pair. --action-lift takes some of the
   column's rhythm back off the top to say so, and the hairline underneath is
   where the player ends and the rest of the room begins.

   .rk-listener-player-slot is one of the two Turbo targets that sit between the
   player and this row, and it is empty for whichever of the speaker and the
   listener this device is not. An empty flex child still claims a gap, which is
   what put a second 16px of nothing above these buttons. :has rather than :empty
   because both slots hold the template's whitespace. */
.rk-listener-player-slot:not(:has(*)) { display: none; }

.rk-room-actions {
  margin-top: calc(var(--action-lift) * -1);
  padding-bottom: var(--action-divider-gap);
  border-bottom: var(--action-divider-width) solid var(--edge-hairline);
}

/* A row with no actions in it draws no rule either: the guests who cannot skip
   and the host with nothing playing both get one target that is simply absent,
   rather than an empty band with a line under it. */
.rk-room-actions:not(:has(.rk-button)) { display: none; }

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

/* ------------------------------------------------------------ landing hero */

.rk-hero {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  align-items: center;
  text-align: center;
  padding-top: var(--space-5);
}

.rk-hero__brand {
  color: var(--ink-primary);
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  font-weight: var(--title-weight);
  letter-spacing: var(--title-tracking);
  text-transform: var(--title-transform);
}


/* ---------------------------------------------------------- rooms you're in */

.rk-my-rooms {
  display: grid;
  gap: var(--space-1);
  width: 100%;
  min-width: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The landing page's list stops at three rows and scrolls past them, so a
   regular does not have to scroll the whole page to reach "Start a party".
   The row height is rebuilt out of the very tokens the row is made of -- its
   padding, its two line boxes, the gap between them -- so a skin that changes
   its type scale moves the cut with it instead of clipping. `auto` is what
   keeps the scrollbar away until there is a fourth room. */
.rk-my-rooms--capped {
  --rk-room-rows: 3;
  --rk-room-row: calc(2 * var(--space-2) + var(--space-1)
                      + (var(--text-md) + 1em) * var(--body-line));
  max-height: calc(var(--rk-room-rows) * var(--rk-room-row)
                   + (var(--rk-room-rows) - 1) * var(--space-1));
  overflow-y: auto;
  overscroll-behavior: contain;
}

.rk-my-room { min-width: 0; }

.rk-my-room__link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
  padding: var(--space-2);
  border-radius: var(--control-radius);
  color: var(--ink-secondary);
  text-decoration: none;
}

.rk-my-room__link:hover {
  background: var(--surface-raised);
  color: var(--ink-primary);
}

.rk-my-room__copy {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.rk-my-room__name {
  color: var(--ink-primary);
  font-family: var(--font-display);
  font-size: var(--text-md);
  letter-spacing: var(--title-tracking);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rk-my-room__meta {
  color: var(--ink-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rk-my-room__side {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: var(--space-2);
}

.rk-my-room__listeners {
  color: var(--ink-muted);
  font-family: var(--font-label);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--label-tracking);
  text-transform: var(--label-transform);
}
/* -------------------------------------------------------------- back links */

.rk-backlink {
  align-self: flex-start;
  color: var(--ink-muted);
  font-family: var(--font-label);
  font-size: var(--label-size);
  letter-spacing: var(--label-tracking);
  text-transform: var(--label-transform);
  text-decoration: none;
}

.rk-backlink:hover { color: var(--ink-primary); }

.rk-link { color: var(--accent); }

/* ------------------------------------------------------------------- forms */

.rk-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.rk-input {
  width: 100%;
  height: var(--control-height);
  padding: 0 var(--space-3);
  background: var(--surface-sunken);
  border-style: solid;
  border-width: var(--edge-width);
  border-top-color: var(--edge-inset-top);
  border-left-color: var(--edge-inset-left);
  border-right-color: var(--edge-inset-right);
  border-bottom-color: var(--edge-inset-bottom);
  border-radius: var(--control-radius);
  color: var(--ink-primary);
  font-family: var(--font-body);
  font-size: var(--text-md);
}

.rk-input::placeholder { color: var(--ink-muted); }

.rk-input:disabled { opacity: 0.7; cursor: not-allowed; }

.rk-input:is(input, textarea):focus-visible {
  outline: none;
  box-shadow: var(--focus-shadow);
  transition: box-shadow var(--motion-fast) var(--ease);
}

.rk-setting-check {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  cursor: pointer;
}

.rk-setting-check > input {
  flex: 0 0 auto;
  width: 1.1rem;
  height: 1.1rem;
  margin-top: var(--space-1);
  accent-color: var(--accent);
}

.rk-setting-check > span { display: flex; flex-direction: column; gap: var(--space-1); }
.rk-setting-check:has(input:disabled) { opacity: 0.7; cursor: not-allowed; }

.rk-skin-picker {
  min-width: 0;
  color: var(--ink-secondary);
}

.rk-skin-picker__summary,
.rk-skin-picker__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
  padding: var(--space-3);
  background: var(--surface-panel);
  color: var(--ink-secondary);
  text-decoration: none;
  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);
}

/* The closed summary is the part that meets the page, so it frosts on its own;
   the items do not, because --menu carries the filter for all of them. */
.rk-skin-picker__summary {
  list-style: none;
  border-radius: var(--control-radius);
  cursor: pointer;
  -webkit-backdrop-filter: var(--surface-blur);
  backdrop-filter: var(--surface-blur);
}

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

.rk-skin-picker__copy {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.rk-skin-picker__name {
  color: var(--ink-primary);
  font-family: var(--font-display);
  font-size: var(--text-md);
  letter-spacing: var(--title-tracking);
}

.rk-skin-picker__tagline {
  color: var(--ink-muted);
  font-family: var(--font-label);
  font-size: var(--label-size);
  letter-spacing: var(--label-tracking);
  text-transform: var(--label-transform);
}

.rk-skin-picker__swatches {
  display: flex;
  flex: 0 0 auto;
  gap: var(--space-1);
}

.rk-skin-picker__swatch {
  width: 18px;
  height: 18px;
  border: var(--edge-width) solid var(--edge-hairline);
  border-radius: var(--edge-radius-sm);
  background: var(--rk-swatch);
}

.rk-skin-picker__chevron {
  flex: 0 0 auto;
  transition: transform var(--motion-base) var(--ease);
}

.rk-skin-picker[open] .rk-skin-picker__chevron { transform: rotate(180deg); }

.rk-skin-picker__menu {
  display: grid;
  gap: var(--space-1);
  margin-top: var(--space-1);
  padding: var(--space-1);
  background: var(--surface-sunken);
  -webkit-backdrop-filter: var(--surface-blur);
  backdrop-filter: var(--surface-blur);
  border: var(--edge-width) solid var(--edge-hairline);
  border-radius: var(--control-radius);
  box-shadow: var(--edge-shadow-float);
}

.rk-skin-picker__item {
  cursor: pointer;
  border-radius: var(--control-radius);
}

.rk-skin-picker__item > input {
  flex: 0 0 auto;
  accent-color: var(--accent);
}

.rk-skin-picker__item--selected,
.rk-skin-picker__item:has(input:checked) {
  background: var(--surface-raised);
  color: var(--ink-primary);
}

.rk-skin-picker__item--selected .rk-skin-picker__name,
.rk-skin-picker__item:has(input:checked) .rk-skin-picker__name { color: var(--accent); }

.rk-skin-picker__item:has(input:disabled) { opacity: 0.7; cursor: not-allowed; }

/* The same disclosure shrunk to footer scale: the summary takes on label
   typography and drops its chrome, while the menu keeps its full shape and
   opens upward, because the footer sits at the bottom of a screen. */

.rk-skin-picker--compact { position: relative; }

.rk-skin-picker--compact .rk-skin-picker__summary {
  gap: var(--space-2);
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  color: var(--ink-muted);
  font-family: var(--font-label);
  font-size: var(--text-xs);
  letter-spacing: var(--label-tracking);
  text-transform: var(--label-transform);
}

.rk-skin-picker--compact .rk-skin-picker__summary:hover { color: var(--ink-primary); }

.rk-skin-picker--compact .rk-skin-picker__summary .rk-skin-picker__copy { flex: 0 1 auto; }

.rk-skin-picker--compact .rk-skin-picker__summary .rk-skin-picker__name {
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rk-skin-picker--compact .rk-skin-picker__summary .rk-skin-picker__tagline { display: none; }

.rk-skin-picker--compact .rk-skin-picker__summary .rk-skin-picker__swatch {
  width: 12px;
  height: 12px;
}

.rk-skin-picker--compact .rk-skin-picker__chevron { width: 12px; height: 12px; }

.rk-skin-picker--compact .rk-skin-picker__menu {
  position: absolute;
  bottom: calc(100% + var(--space-2));
  left: 50%;
  z-index: 30;
  width: max-content;
  max-width: min(320px, calc(100vw - var(--space-5)));
  margin: 0;
  transform: translateX(-50%);
  text-align: start;
}


/* ------------------------------------------------------------ result lists */

.rk-results {
  list-style: none;
  margin: 0;
  padding: 0;
}

.rk-result {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-bottom: var(--edge-width) solid var(--edge-hairline);
}

.rk-result:last-child { border-bottom: none; }

.rk-result__text { min-width: 0; }

/* The fallback playlist is a list to manage, not a list to read: it is only
   ever the host, deciding what stays. So each song is one line with its own
   remove control on it, rather than a card with the control parked underneath,
   and the list stops at four rows and scrolls past them -- the way the landing
   page's rooms stop at three -- so the import form is still on a 390x720 phone's
   first screen, with the add button under it, however long the playlist grows.

   The row height is rebuilt out of the tokens the row is made of: its padding,
   its two line boxes, the gap between them, its hairline -- against the tap
   target, because on a skin with a small type scale the button is the taller
   of the two. A skin that changes its type scale moves the cut with it instead
   of clipping a row in half. */
.rk-fallback-list {
  --rk-fallback-rows: 4;
  --rk-fallback-line-gap: var(--space-1);
  --rk-fallback-row: calc(2 * var(--space-2) + var(--edge-width)
                          + max(var(--space-7),
                                var(--rk-fallback-line-gap)
                                + (var(--text-md) + var(--text-sm)) * var(--body-line)));
  max-height: calc(var(--rk-fallback-rows) * var(--rk-fallback-row));
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* The row carries the hover, not the control, because the control is a glyph on
   a transparent square: without it, aiming at the trash lights nothing until
   the pointer is already on it. */
.rk-fallback-item { transition: background var(--motion-fast) var(--ease); }

.rk-fallback-item:hover { background: var(--surface-raised); }

/* Two lines of one song, closer than the stack's own rhythm -- and the same gap
   the height formula above counts, so the two cannot drift apart and cut a row
   in half. */
.rk-fallback-item__text { gap: var(--rk-fallback-line-gap); }

.rk-fallback-item__form { flex: 0 0 auto; }

/* The glyph and the target are two boxes: the trash keeps its drawn size while
   the square around it stays a thumb's, out of a structural token so no skin
   can shrink it. It rests in the row's muted ink -- twelve of these should not
   read as twelve warnings -- and answers in --danger under a pointer or a focus
   ring. */
.rk-fallback-item__remove {
  width: var(--space-7);
  height: var(--space-7);
  color: var(--ink-muted);
}

.rk-fallback-item__remove:hover:not(:active),
.rk-fallback-item__remove:focus-visible { color: var(--danger); }

.rk-result__source,
.rk-result-group__label {
  color: var(--accent);
  font-family: var(--font-label);
  font-size: var(--text-xs);
  letter-spacing: var(--label-tracking);
  text-transform: var(--label-transform);
}

.rk-result__thumbnail {
  flex: 0 0 auto;
  width: 78px;
  height: 44px;
  object-fit: cover;
  border-radius: var(--control-radius);
}

.rk-search-empty { padding: var(--space-3); }

/* The room's search results live in a frame that is empty until someone types.
   A flex column still spaces an empty child, so an untouched field would sit
   above a gap the size of a panel. */
.rk-room-search { display: contents; }
.rk-room-search turbo-frame:empty { display: none; }

.rk-result-group + .rk-result-group {
  border-top: var(--edge-width) solid var(--edge-hairline);
}

.rk-result-group[data-version-count]:not([data-version-count="1"]) {
  margin: var(--space-2);
  border: var(--edge-width) solid var(--edge-hairline);
  border-radius: var(--control-radius);
  background: var(--surface-sunken);
}

.rk-result-group__label { padding: var(--space-2) var(--space-3) 0; }

.rk-search-provider-action {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3);
  border-top: var(--edge-width) solid var(--edge-hairline);
}

/* The pasted link, proving itself. Sunken like every other well so the video
   reads as something Rocola is holding rather than something it has queued. */
.rk-embed {
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--surface-sunken);
  border-style: solid;
  border-width: var(--edge-width);
  border-top-color: var(--edge-inset-top);
  border-left-color: var(--edge-inset-left);
  border-right-color: var(--edge-inset-right);
  border-bottom-color: var(--edge-inset-bottom);
  border-radius: var(--control-radius);
}

.rk-embed iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* -------------------------------------------------------- public discovery */

.rk-public-rooms {
  display: grid;
  gap: var(--space-3);
  width: 100%;
  min-width: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.rk-public-room {
  width: 100%;
  min-width: 0;
}

.rk-public-room__details {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-3);
  min-width: 0;
}

.rk-public-room__now-playing { min-width: 0; }

.rk-public-room .rk-panel__body,
.rk-public-room .rk-panel__chrome,
.rk-public-room .rk-panel__chrome-title { min-width: 0; }

.rk-public-room .rk-panel__chrome {
  height: auto;
  min-height: var(--chrome-height);
  white-space: normal;
}

.rk-public-room .rk-panel__chrome-title-text {
  min-width: 0;
  overflow-wrap: anywhere;
}

.rk-public-room__skin {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.rk-public-room__swatches { display: flex; gap: var(--space-1); }

.rk-public-room__swatch {
  width: 1.25rem;
  aspect-ratio: 1;
  border: var(--edge-width) solid var(--edge-hairline);
  border-radius: var(--control-radius);
  background: var(--rk-swatch);
}

.rk-public-room__track {
  min-width: 0;
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rk-public-room__listeners { font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------- admin */

.rk-admin {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  width: min(1100px, 100%);
  min-height: 100dvh;
  margin-inline: auto;
  padding: var(--space-5) var(--space-4);
}

.rk-admin-nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
}

.rk-admin-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-3);
}

.rk-admin-stat {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-4);
  color: inherit;
  text-decoration: none;
}

.rk-admin-stat strong {
  color: var(--accent);
  font-family: var(--font-readout);
  font-size: var(--text-3xl);
}

.rk-admin-filter {
  display: flex;
  gap: var(--space-2);
  padding: var(--space-3);
}

.rk-admin-table-wrap { overflow-x: auto; }

.rk-admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.rk-admin-table th,
.rk-admin-table td {
  padding: var(--space-2) var(--space-3);
  border-bottom: var(--edge-width) solid var(--edge-hairline);
  text-align: start;
  vertical-align: middle;
}

.rk-admin-table th {
  color: var(--ink-muted);
  font-family: var(--font-label);
  letter-spacing: var(--label-tracking);
  text-transform: var(--label-transform);
}

.rk-admin-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.rk-admin-moderation {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
}

.rk-admin-moderation .rk-input {
  width: min(16rem, 100%);
}

.rk-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ----------------------------------------------------------------- TV mode */

.rk-tv {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  gap: var(--space-4);
  width: 100%;
  max-width: none;
  height: 100vh;
  height: 100dvh;
  min-height: 100dvh;
  margin-inline: auto;
  padding: var(--space-4);
  /* Overscan crops the edge of the picture on a lot of sets, and the room code
     and the marquee were sitting in the part that gets cropped. */
  padding: max(var(--space-4), 2.2vh) max(var(--space-4), 1.8vw);
  overflow: hidden;
}

.rk-tv__title {
  color: var(--ink-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: var(--font-display);
  font-size: 56px;
  font-weight: var(--title-weight);
  letter-spacing: var(--title-tracking);
  text-transform: var(--title-transform);
  line-height: 1.2;
  white-space: nowrap;
}

.rk-tv__masthead {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: minmax(420px, 0.7fr) minmax(0, 2fr);
  align-items: center;
  gap: var(--space-4);
  min-width: 0;
}

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

/* Legible from a sofa, but never at the cost of the code beside it. */
.rk-tv__backlink { font-size: 24px; }

.rk-tv__marquee { min-width: 0; }

.rk-tv__marquee .rk-marquee {
  min-height: 52px;
  /* A hard clip cuts the scrolling text mid-glyph right against the room name,
     which reads as a broken screen from the far end of a room. A mask takes
     only the alpha channel, so currentColor stands in for an opaque stop and
     the fade owes nothing to a colour of its own. Sets too old for masks keep
     the cut they already had. */
  -webkit-mask-image: linear-gradient(to right, transparent, currentColor var(--space-5),
                                      currentColor calc(100% - var(--space-5)), transparent);
  mask-image: linear-gradient(to right, transparent, currentColor var(--space-5),
                              currentColor calc(100% - var(--space-5)), transparent);
}

.rk-tv__marquee .rk-marquee__item { font-size: 30px; }

.rk-tv__body {
  display: flex;
  flex: 1 1 auto;
  gap: var(--space-4);
  min-width: 0;
  min-height: 0;
}

.rk-tv__main {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}

.rk-tv__sidebar {
  flex: 0 1 38%;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  min-width: 720px;
  min-height: 0;
}

@supports (display: grid) {
  .rk-tv__body {
    display: grid;
    grid-template-columns: minmax(720px, 1fr) minmax(720px, 38vw);
  }
}

.rk-tv__player-panel { height: 100%; }

.rk-tv__player-panel > .rk-panel__body {
  min-height: 0;
  overflow: hidden;
}

.rk-tv__stage {
  display: flex;
  flex: 1 1 auto;
  min-height: 200px;
  min-width: 0;
}

.rk-tv__media-stage {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 200px;
}

.rk-tv__media-stage > .rk-speaker-player-slot {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}

.rk-tv__media-stage > .rk-speaker-player-slot > .rk-speaker {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;
}

.rk-tv__media-stage > [id^="listener_player_slot"] { display: none; }

.rk-tv__stage--standby { flex: 1 1 auto; min-height: 0; }
.rk-tv__stage--standby .rk-tv__standby { min-height: 200px; }

.rk-tv__stage > .rk-speaker { flex: 1 1 auto; min-width: 0; min-height: 0; }

.rk-tv .rk-speaker__standby {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  justify-content: center;
  min-height: 200px;
  padding: var(--space-5);
  background: var(--surface-sunken);
  text-align: center;
}

.rk-tv .rk-speaker__standby p { max-width: 48ch; font-size: 24px; }

.rk-tv__standby {
  flex: 1 1 auto;
  align-items: center;
  justify-content: center;
  min-height: 360px;
  padding: var(--space-6);
  background: var(--surface-sunken);
  border-style: solid;
  border-width: var(--edge-width);
  border-top-color: var(--edge-inset-top);
  border-left-color: var(--edge-inset-left);
  border-right-color: var(--edge-inset-right);
  border-bottom-color: var(--edge-inset-bottom);
  border-radius: var(--edge-radius-sm);
  text-align: center;
}

.rk-tv__standby h2 {
  font-family: var(--font-display);
  font-size: 38px;
  letter-spacing: var(--title-tracking);
  text-transform: var(--title-transform);
}

.rk-tv__standby p { font-size: 24px; }

/* The height of this box is the whole point of TV mode, and on the browsers
   this runtime exists for it had no source at all. `aspect-ratio` shipped four
   years after the grid the `@supports` fallbacks above are written for, so on
   those sets it was ignored -- leaving a box whose only child is an iframe
   sized `height: 100%` against a parent height of auto. It collapsed onto the
   200px floor and played the room's video in a slot a few inches tall. A
   percentage padding ratio is arithmetic every browser has always done, so the
   TV path derives its height from that alone and turns `aspect-ratio` off
   rather than keep a second source of truth that only some sets can read. */
.rk-tv .rk-speaker__player {
  --rk-tv-player-width: 94%;
  position: relative;
  flex: 0 0 auto;
  align-self: center;
  width: var(--rk-tv-player-width);
  height: 0;
  min-height: 0;
  /* Percentage padding resolves against the containing block's width, not this
     element's, so the ratio is taken over the width set above. */
  padding-top: calc(var(--rk-tv-player-width) * 0.5625);
  aspect-ratio: auto;
}

/* The YouTube API replaces the mount with an iframe of its own making, which
   keeps the id but not the class -- so what fills the box cannot be named. */
.rk-tv .rk-speaker__player > * {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
}

/* The stage's slack belongs above this line, not between it and the video:
   Stimulus needs the wake warning inside the controller's element, so it
   cannot move to the readout, but it can sit at the bottom edge next to it. */
.rk-tv .rk-speaker__status-row {
  margin-top: auto;
  justify-content: center;
  text-align: center;
}

.rk-speaker__mount,
.rk-speaker__mount iframe {
  display: block;
  width: 100%;
  height: 100%;
  min-width: 200px;
  min-height: 200px;
}

.rk-tv .rk-speaker__controls {
  flex-direction: row;
  align-items: center;
  gap: var(--space-3);
}

.rk-tv .rk-speaker__help { display: none; }

.rk-tv .rk-speaker__controls .rk-button {
  min-height: 52px;
  font-size: 24px;
}

.rk-tv .rk-speaker__controls .rk-text--dim,
.rk-tv .rk-speaker__wake-status { font-size: 24px; }

.rk-tv .rk-playback__notice,
.rk-tv .rk-playback__paused,
.rk-tv .rk-playback__disconnected,
.rk-tv [data-speaker-player-target~="status"] { font-size: 28px; }

.rk-tv .rk-readout__line { font-size: 32px; }
.rk-tv .rk-readout__meta { font-size: 28px; }
.rk-tv .rk-playback__clock { font-size: 28px; }

.rk-tv .rk-readout {
  padding-block: var(--space-1);
  gap: var(--space-5);
}

/* Every TV meta line ends in the volume, and at TV type it always ran past the
   panel -- so the readout ellipsised the one item nothing else on this screen
   shows (the volume remote is hidden here). Wrapping costs a line and loses
   nothing. */
.rk-tv .rk-readout__meta {
  white-space: normal;
  overflow: visible;
}

.rk-tv .rk-readout__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto auto;
}

.rk-tv .rk-readout__line { grid-column: 1 / -1; }
.rk-tv .rk-readout__meta { grid-column: 1; }
.rk-tv .rk-playback__clock { grid-column: 2; grid-row: 2; }

/* The server seats two complete rows as a no-script floor. The fitted queue
   grows into the rest, then measures that real space and reveals every whole
   row it can without giving up the final "+ X more" line. */
.rk-tv__queue-panel {
  flex: 1 1 0;
  min-height: 250px;
}

.rk-tv__queue-panel > .rk-panel__body {
  min-height: 0;
  overflow: hidden;
}

.rk-tv .rk-queue--fitted {
  flex: 1 1 auto;
  height: 100%;
  min-height: 0;
  overflow: hidden;
}

.rk-tv .rk-queue__row {
  min-height: 81px;
  gap: var(--space-2);
}

.rk-tv .rk-queue__title { font-size: 30px; }


.rk-tv .rk-queue__empty {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: var(--space-5);
  font-size: 28px;
}
.rk-tv .rk-queue__artist,
.rk-tv .rk-queue__duration { font-size: 28px; }

.rk-tv .rk-queue__more {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  color: var(--ink-on-row-dim);
  font-family: var(--font-label);
  font-size: 28px;
  letter-spacing: var(--label-tracking);
  text-transform: var(--label-transform);
}

.rk-tv__join-panel { flex: 0 0 auto; }
.rk-tv__join-panel > .rk-panel__body { gap: var(--space-2); }
.rk-tv .rk-joincode { flex-wrap: wrap; }
.rk-tv .rk-joincode__details { flex-basis: 440px; }
.rk-tv .rk-joincode--tv .rk-joincode__qr { width: clamp(366px, 19vw, 410px); }
.rk-tv .rk-joincode__code { font-size: 52px; }
.rk-tv .rk-joincode__address { font-size: 24px; }
.rk-tv .rk-joincode .rk-label { font-size: 28px; }
.rk-tv .rk-panel__chrome {
  min-height: 38px;
  height: auto;
  font-size: 28px;
}

@media (max-width: 1800px) and (min-width: 1101px) {
  .rk-tv__sidebar { min-width: 560px; }

  @supports (display: grid) {
    .rk-tv__body { grid-template-columns: minmax(600px, 1fr) minmax(560px, 38vw); }
  }

  .rk-tv .rk-joincode--tv .rk-joincode__qr { width: clamp(280px, 18vw, 330px); }
  .rk-tv .rk-joincode__details { flex-basis: 300px; }
}

@media (prefers-reduced-motion: reduce) {
  .rk-tv .rk-marquee__track {
    animation: none;
    min-width: 0;
    max-width: 100%;
  }

  .rk-tv .rk-marquee__item:first-child {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .rk-tv .rk-marquee__item[aria-hidden="true"] { display: none; }
}

@media (max-width: 1100px) {
  .rk-tv {
    height: auto;
    min-height: 100vh;
    overflow: visible;
  }

  .rk-tv__masthead { grid-template-columns: minmax(0, 1fr); }
  .rk-tv__title { white-space: normal; }
  .rk-tv__body { display: flex; flex-direction: column; }
  .rk-tv__main,
  .rk-tv__sidebar { width: 100%; height: auto; min-width: 0; }
  .rk-tv__sidebar { overflow: visible; }
  .rk-tv .rk-queue--fitted { height: auto; overflow: visible; }
  .rk-tv__player-panel { height: auto; }
}

/* Under 1080 lines the sidebar cannot seat full-size TV type and a full-size
   invite at once, so both come down together. Sizing only one of them just
   moves the overflow onto the other. */
@media (max-height: 1000px) and (min-width: 1101px) {
  .rk-tv { padding: var(--space-2); gap: var(--space-2); }
  .rk-tv { padding: max(var(--space-2), 1.6vh) max(var(--space-2), 1.4vw); }
  /* The video is width-driven, and on these screens the stage that has to hold
     it is shorter than the width would ask for. */
  .rk-tv .rk-speaker__player { --rk-tv-player-width: 74%; }
  .rk-tv .rk-queue__empty { font-size: 22px; padding: var(--space-3); }
  .rk-tv__marquee .rk-marquee { min-height: 40px; }
  .rk-tv__title { font-size: 44px; }
  .rk-tv__backlink { font-size: 20px; }
  .rk-tv .rk-queue__row { min-height: 56px; }
  .rk-tv .rk-queue__title { font-size: 24px; }
  .rk-tv .rk-queue__artist,
  .rk-tv .rk-queue__duration { font-size: 22px; }
  .rk-tv .rk-queue__more { min-height: 36px; font-size: 22px; }
  .rk-tv .rk-joincode--tv .rk-joincode__qr { width: min(300px, 28vh); }
  .rk-tv .rk-joincode__code { font-size: 40px; }
  .rk-tv .rk-joincode__address { font-size: 18px; }
  .rk-tv .rk-joincode .rk-label { font-size: 22px; }
  .rk-tv .rk-panel__chrome { font-size: 22px; }
  .rk-tv__queue-panel { min-height: 224px; }
  .rk-tv__player-panel > .rk-panel__body { gap: var(--space-2); }
  .rk-tv .rk-speaker__standby { min-height: 120px; padding: var(--space-2); }
  .rk-tv .rk-speaker__standby .rk-viz { height: 64px; }
  .rk-tv .rk-speaker__standby p { font-size: 18px; }
  .rk-tv .rk-playback__notice,
  .rk-tv .rk-playback__paused,
  .rk-tv .rk-playback__disconnected { padding: var(--space-1) var(--space-2); font-size: 20px; }
}

/* -------------------------------------------------------------- user tools */

.rk-user-tools {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--space-3);
  max-width: 100%;
  padding: var(--space-2);
  font-family: var(--font-label);
  font-size: var(--text-xs);
  letter-spacing: var(--label-tracking);
  text-transform: var(--label-transform);
}

.rk-user-tools__item {
  color: var(--ink-muted);
  text-decoration: none;
}

.rk-user-tools__item:hover { color: var(--ink-primary); }
.rk-user-tools__item[aria-current] { color: var(--accent); }

.rk-locale-switcher {
  display: inline-flex;
  gap: var(--space-2);
}

/* The theme picker takes a centred row of its own so its upward menu has room
   on both sides of it, whatever else the footer is carrying. */
.rk-user-tools__theme {
  display: flex;
  flex: 1 0 100%;
  justify-content: center;
  min-width: 0;
}
