/* Derek's Jukebox
   A modern take on a jukebox: warm amber on deep charcoal, chrome-ish edges,
   no chintz. Mobile-first; every interactive target is at least 44px.
   Contrast ratios are checked against WCAG AA on the charcoal background. */

:root {
  --bg: #12100f;
  --bg-raised: #1c1918;
  --bg-sunken: #0c0a0a;
  --line: #332e2b;
  --line-bright: #4a423d;

  --ink: #f5efe9;
  --ink-muted: #b3a79d;   /* 7.0:1 on --bg */
  --ink-faint: #8a7f76;   /* 4.6:1 on --bg */

  --amber: #ffb340;
  --amber-deep: #e08c15;
  --amber-ink: #2a1c05;

  --teal: #4fd1c5;
  --rose: #ff7a7a;
  --lime: #a3e635;

  --radius: 14px;
  --radius-sm: 9px;
  --shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  --tap: 44px;

  --wrap: 40rem;
  --step: clamp(0.75rem, 2vw, 1.25rem);
}

*, *::before, *::after { box-sizing: border-box; }

html {
  background: var(--bg);
  color-scheme: dark;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background:
    radial-gradient(120% 60% at 50% -10%, #241f1c 0%, transparent 60%),
    var(--bg);
  color: var(--ink);
  font: 400 1rem/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-synthesis-weight: none;
}

.wrap {
  width: min(100% - 2rem, var(--wrap));
  margin-inline: auto;
}

/* ---------------------------------------------------------------- a11y */

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--amber);
  color: var(--amber-ink);
  padding: 0.75rem 1rem;
  z-index: 100;
  border-radius: 0 0 var(--radius-sm) 0;
  font-weight: 600;
}
.skip-link:focus { left: 0; }

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
  border-radius: 4px;
}

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

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* -------------------------------------------------------------- chrome */

.site-header {
  border-bottom: 1px solid var(--line);
  background: linear-gradient(180deg, #211d1b, #171413);
  position: sticky;
  top: 0;
  z-index: 20;
  padding-block: 0.6rem;
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--ink);
  text-decoration: none;
  font-weight: 650;
  letter-spacing: -0.01em;
  min-height: var(--tap);
}

.brand__mark {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%, var(--bg-sunken) 0 18%, transparent 19%),
    conic-gradient(from 0deg, #4a423d, #8a7f76, #4a423d, #8a7f76, #4a423d);
  box-shadow: 0 0 0 1px var(--line-bright), 0 0 14px rgba(255, 179, 64, 0.28);
  flex: none;
}

.brand__text {
  font-size: 1.05rem;
  background: linear-gradient(180deg, #fff3e0, var(--amber));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.site-footer {
  margin-top: auto;
  padding-block: 1.5rem;
  color: var(--ink-faint);
  font-size: 0.85rem;
  border-top: 1px solid var(--line);
}

main { padding-block: var(--step) 2.5rem; flex: 1; }

/* --------------------------------------------------------- typography */

h1, h2, h3 {
  line-height: 1.2;
  letter-spacing: -0.02em;
  margin: 0 0 0.5em;
  font-weight: 650;
}
h1 { font-size: clamp(1.5rem, 5vw, 2rem); }
h2 { font-size: 1.15rem; margin-top: 1.75rem; }
h3 { font-size: 1rem; }

p { margin: 0 0 1rem; }
.lede { color: var(--ink-muted); }
.muted { color: var(--ink-faint); font-size: 0.9rem; }
.muted a { color: var(--ink-muted); }

a { color: var(--amber); }
a:hover { color: #ffc971; }

code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 0.1em 0.4em;
  overflow-wrap: anywhere;
}

.code-block {
  display: block;
  padding: 0.75rem;
  border-radius: var(--radius-sm);
  overflow-x: auto;
}

/* -------------------------------------------------------------- panels */

.panel {
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(1rem, 4vw, 1.75rem);
  box-shadow: var(--shadow);
}
.panel--narrow { max-width: 26rem; margin-inline: auto; }
.panel + .panel { margin-top: 1rem; }

.stack > * + * { margin-top: 1rem; }
.row { display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; }

/* ------------------------------------------------------------- controls */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: var(--tap);
  padding: 0.7rem 1.15rem;
  border-radius: 999px;
  border: 1px solid transparent;
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.12s ease, background-color 0.12s ease;
}
.button:active { transform: translateY(1px); }
.button[disabled], .button[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
}

.button--primary {
  background: linear-gradient(180deg, var(--amber), var(--amber-deep));
  color: var(--amber-ink);
}
.button--primary:hover { background: linear-gradient(180deg, #ffc264, var(--amber)); }

.button--ghost {
  background: transparent;
  border-color: var(--line-bright);
  color: var(--ink);
}
.button--ghost:hover { background: #262120; color: var(--ink); }

.button--danger { background: #4a1f1f; color: #ffd7d7; border-color: #6b2b2b; }
.button--danger:hover { background: #5d2626; }

.button--small { min-height: 36px; padding: 0.35rem 0.75rem; font-size: 0.85rem; }

.field { display: flex; flex-direction: column; gap: 0.35rem; }
.field label { font-weight: 600; font-size: 0.9rem; }
.field .hint { color: var(--ink-faint); font-size: 0.82rem; }

input[type="text"],
input[type="password"],
input[type="search"],
input[type="number"],
select,
textarea {
  min-height: var(--tap);
  width: 100%;
  padding: 0.65rem 0.85rem;
  background: var(--bg-sunken);
  border: 1px solid var(--line-bright);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font: inherit;
}
input::placeholder { color: var(--ink-faint); }
input:focus, select:focus, textarea:focus { border-color: var(--amber); }

.checkbox {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: var(--tap);
  cursor: pointer;
}
.checkbox input { width: 1.25rem; height: 1.25rem; accent-color: var(--amber); }

/* -------------------------------------------------------------- alerts */

.alert {
  border-radius: var(--radius-sm);
  padding: 0.8rem 1rem;
  border: 1px solid;
  margin-bottom: 1rem;
}
.alert--success { background: #14261b; border-color: #2f5c3f; color: #cdeeda; }
.alert--warning { background: #2b2213; border-color: #6b5320; color: #f6e2b8; }
.alert--error   { background: #2b1616; border-color: #6b2b2b; color: #ffd7d7; }
.alert--info    { background: #14222b; border-color: #2b5163; color: #cfe8f6; }

/* Quieter than an alert: nothing is wrong, you are just not who this page is
   normally for. Sits above the content it explains. */
.viewer-note {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  justify-content: space-between;
  border-radius: var(--radius-sm);
  padding: 0.6rem 0.9rem;
  margin-bottom: 1rem;
  border: 1px dashed var(--line-bright);
  color: var(--ink-faint);
  font-size: 0.9rem;
}

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

.scope-list { margin: 0 0 1rem; }
.scope-list dt { font-weight: 600; margin-top: 0.75rem; }
.scope-list dd { margin: 0.15rem 0 0; color: var(--ink-muted); }

/* ------------------------------------------------------------ guest page */

.section-heading {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-faint);
  margin: 0 0 0.9rem;
  font-weight: 650;
}

.count {
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.05rem 0.5rem;
  font-size: 0.75rem;
  color: var(--ink-muted);
  letter-spacing: 0;
}

.eyebrow {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--amber);
  margin: 0 0 0.3rem;
  font-weight: 650;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.empty-state { padding: 1.25rem 0; text-align: center; }

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

.now-playing { margin-bottom: 1rem; }

.now-playing__inner,
.now-playing__empty {
  display: flex;
  gap: 1rem;
  align-items: center;
}

.now-playing__text { min-width: 0; }

.now-playing__title {
  font-size: 1.15rem;
  font-weight: 650;
  margin: 0 0 0.15rem;
  line-height: 1.25;
  overflow-wrap: anywhere;
}

.now-playing__artist { color: var(--ink-muted); margin: 0; overflow-wrap: anywhere; }

.art {
  width: 96px;
  height: 96px;
  border-radius: 10px;
  object-fit: cover;
  flex: none;
  background: var(--bg-sunken);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5);
}

.art--placeholder {
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
}
.art__note { font-size: 2rem; color: var(--ink-faint); }

.progress {
  margin-top: 1rem;
  height: 6px;
  border-radius: 999px;
  background: var(--bg-sunken);
  overflow: hidden;
  border: 1px solid var(--line);
}
.progress__bar {
  height: 100%;
  background: linear-gradient(90deg, var(--amber-deep), var(--amber));
  transition: width 1s linear;
}
.progress__times {
  display: flex;
  justify-content: space-between;
  font-size: 0.78rem;
  color: var(--ink-faint);
  margin: 0.35rem 0 0;
  font-variant-numeric: tabular-nums;
}

.dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  display: inline-block;
}
.dot--warn { background: #e0a415; }

/* --- search results --- */

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

.result {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--line);
}
.result:last-child { border-bottom: 0; }

.result__art, .queue__art {
  border-radius: 7px;
  object-fit: cover;
  flex: none;
  background: var(--bg-sunken);
}
.result__art { width: 56px; height: 56px; }
.queue__art { width: 44px; height: 44px; }

.result__art--placeholder, .queue__art--placeholder {
  display: grid;
  place-items: center;
  color: var(--ink-faint);
  border: 1px solid var(--line);
}

.result__text, .queue__text { flex: 1; min-width: 0; }

.result__title, .queue__title {
  margin: 0;
  font-weight: 600;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
.result__artist, .queue__artist {
  margin: 0.1rem 0 0;
  color: var(--ink-muted);
  font-size: 0.88rem;
  overflow-wrap: anywhere;
}
.result__meta { margin: 0.1rem 0 0; font-size: 0.8rem; }

.result__request { flex: none; }

.badge {
  display: inline-block;
  font-size: 0.62rem;
  font-weight: 700;
  padding: 0.05rem 0.3rem;
  border-radius: 3px;
  vertical-align: middle;
  margin-left: 0.25rem;
}
.badge--explicit { background: var(--line-bright); color: var(--ink); }

.load-more { width: 100%; margin-top: 0.75rem; }

/* --- queue --- */

.queue__item {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--line);
}
.queue__item:last-child { border-bottom: 0; }

.queue__item--mine {
  background: linear-gradient(90deg, rgba(255, 179, 64, 0.09), transparent 60%);
  margin-inline: -0.5rem;
  padding-inline: 0.5rem;
  border-radius: 8px;
}

.queue__position {
  width: 1.5rem;
  text-align: right;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
  flex: none;
}

.queue__meta {
  text-align: right;
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}
.queue__wait { font-size: 0.8rem; color: var(--ink-muted); white-space: nowrap; }
.queue__who { font-size: 0.75rem; }

/* --- feedback --- */

.spinner {
  width: 1rem;
  height: 1rem;
  border: 2px solid var(--line-bright);
  border-top-color: var(--amber);
  border-radius: 50%;
  display: inline-block;
  animation: spin 0.7s linear infinite;
  vertical-align: -0.15em;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* htmx toggles this class; without JS the indicator never shows, which is
   correct, because without JS there is nothing asynchronous to wait for. */
.htmx-indicator { display: none; padding: 0.5rem 0; color: var(--ink-muted); }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { display: block; }

.connection-warning {
  position: fixed;
  left: 50%;
  bottom: 1rem;
  transform: translateX(-50%);
  background: #2b2213;
  border: 1px solid #6b5320;
  color: #f6e2b8;
  padding: 0.5rem 1rem;
  border-radius: 999px;
  font-size: 0.85rem;
  z-index: 50;
  box-shadow: var(--shadow);
}

/* --- name popover --- */

.popover {
  border: 1px solid var(--line-bright);
  background: var(--bg-raised);
  color: var(--ink);
  border-radius: var(--radius);
  padding: 1.25rem;
  width: min(92vw, 22rem);
  box-shadow: var(--shadow);
}
.popover::backdrop { background: rgba(0, 0, 0, 0.55); }

/* Browsers without the popover API -- Safari before 17, Firefox before 125 --
   ignore the attribute rather than approximating it. There is then no UA rule
   hiding a closed popover, so the panel would sit open in the middle of the
   page with the chip above it doing nothing. Guests bring whatever phone they
   own, so this is not a hypothetical.

   All of it is inside @supports not, which browsers with the real thing skip
   entirely -- no undoing of author styles, no chance of fighting the top layer.
   The huge spread on the box-shadow stands in for ::backdrop. */
@supports not selector(:popover-open) {
  .popover { display: none; }
  .popover.is-open {
    display: block;
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 50;
    box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.55);
  }
}

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

.wrap--wide { width: min(100% - 2rem, 56rem); }

.control-row { margin-top: 1rem; gap: 0.5rem; }

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

.admin-queue { list-style: none; margin: 0; padding: 0; }
.admin-queue__item {
  display: flex;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.admin-queue__item:last-child { border-bottom: 0; }
.admin-queue__text { flex: 1; min-width: 12rem; }
.admin-queue__actions { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; }

.status {
  display: inline-block;
  padding: 0.05rem 0.4rem;
  border-radius: 4px;
  font-size: 0.72rem;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
}
.status--pending { color: var(--ink-muted); }
.status--submitted_to_spotify { color: var(--teal); border-color: #2b5163; }
.status--playing { color: var(--amber); border-color: #6b5320; }
.status--failed { color: var(--rose); border-color: #6b2b2b; }

.locked { font-size: 0.78rem; font-style: italic; }
.warn-text { color: #e0a415; }

.event-log { font-size: 0.82rem; }
.event {
  display: grid;
  grid-template-columns: 3.2rem 5.5rem 1fr;
  gap: 0.5rem;
  padding: 0.2rem 0;
  border-bottom: 1px solid var(--line);
  align-items: baseline;
}
.event:last-child { border-bottom: 0; }
.event__time { color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.event__category { color: var(--ink-faint); text-transform: uppercase; font-size: 0.7rem; }
.event__message { overflow-wrap: anywhere; }
.event--warning .event__message { color: #f6e2b8; }
.event--error .event__message { color: #ffd7d7; }

@media (max-width: 30rem) {
  .event { grid-template-columns: 3.2rem 1fr; }
  .event__category { display: none; }
}

/* ------------------------------------------------------- guest extras */

.name-prompt { margin-bottom: 1rem; border-color: var(--line-bright); }
.name-prompt__form { display: flex; gap: 0.75rem; align-items: flex-end; flex-wrap: wrap; }
.name-prompt__form .field { flex: 1; min-width: 12rem; }

.link-button {
  background: none;
  border: 0;
  color: var(--ink-faint);
  font: inherit;
  font-size: 0.75rem;
  text-decoration: underline;
  cursor: pointer;
  padding: 0.35rem 0;
  min-height: 32px;
}
.link-button:hover { color: var(--rose); }

.queue__locked { font-size: 0.72rem; font-style: italic; }

.join-gate {
  text-align: center;
  padding: 1.5rem 1rem;
  border: 1px dashed var(--line-bright);
  border-radius: var(--radius-sm);
}
.join-gate__icon { font-size: 2rem; color: var(--amber); margin: 0 0 0.5rem; }
.join-gate__lede { font-size: 1.05rem; font-weight: 600; margin: 0 0 0.4rem; }

.admin-qr {
  border-radius: 8px;
  background: #fff;
  padding: 0.4rem;
  flex: none;
}

/* --- readiness checklist --- */

.checklist { list-style: none; margin: 0; padding: 0; }
.checklist__item {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--line);
}
.checklist__item:last-child { border-bottom: 0; }
.checklist__item > div { flex: 1; min-width: 0; }

.checklist__mark {
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 0.8rem;
  flex: none;
  margin-top: 0.1rem;
}
.is-done .checklist__mark { background: #14261b; color: #7fd39b; border: 1px solid #2f5c3f; }
.is-todo .checklist__mark { background: #2b2213; color: #e0a415; border: 1px solid #6b5320; }
/* Configured but contradicted by Spotify right now -- louder than todo, because
   it looks set up and so nobody goes looking. */
.is-problem .checklist__mark { background: #331717; color: #f3a2a2; border: 1px solid #703434; }
.is-unknown .checklist__mark { background: var(--bg-sunken); color: var(--ink-muted); border: 1px solid var(--line-bright); }

.checklist__label { margin: 0; font-weight: 600; }
.is-todo .checklist__label { color: #f6e2b8; }
.is-problem .checklist__label { color: #f6c9c9; }
.checklist__detail { margin: 0.1rem 0 0; font-size: 0.85rem; }

.ready-badge {
  padding: 0.05rem 0.5rem;
  border-radius: 999px;
  font-size: 0.7rem;
  letter-spacing: 0;
  text-transform: none;
}
.ready-badge--yes { background: #14261b; border: 1px solid #2f5c3f; color: #cdeeda; }

.display__watching { color: var(--ink-faint); font-size: 0.85rem; margin: 0.3rem 0 0; }

/* --- get started --- */

.steps { list-style: none; margin: 0; padding: 0; counter-reset: step; }

.step {
  display: flex;
  gap: 0.9rem;
  align-items: flex-start;
  padding: 1rem 0;
  border-bottom: 1px solid var(--line);
}
.step:last-child { border-bottom: 0; }
.step__body { flex: 1; min-width: 0; }
.step__body p:last-child { margin-bottom: 0; }

.step__number {
  width: 2rem;
  height: 2rem;
  flex: none;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-weight: 650;
  font-size: 0.9rem;
  font-variant-numeric: tabular-nums;
  border: 1px solid var(--line-bright);
  background: var(--bg-sunken);
  color: var(--ink-muted);
}
.step--done .step__number { background: #14261b; border-color: #2f5c3f; color: #7fd39b; }
.step--todo .step__number { background: #2b2213; border-color: #6b5320; color: #e0a415; }
.step--problem .step__number { background: #331717; border-color: #703434; color: #f3a2a2; }
/* Dotted: the app has no opinion, because only a person can confirm it. */
.step--manual .step__number { border-style: dashed; color: var(--ink-faint); }

.step__title {
  margin: 0 0 0.35rem;
  font-weight: 650;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}
.step--done .step__title { color: var(--ink-muted); font-weight: 600; }
.step--problem .step__title { color: #f6c9c9; }

/* Each group names the device being set up. Only the steps you do somewhere
   else carry a tag, so it marks the exception rather than labelling every row
   the way the old per-step device tags did. */
.panel > .section-heading + .muted { margin-top: -0.25rem; }

.step-where {
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  padding: 0.15rem 0.45rem;
  border-radius: 4px;
  font-weight: 700;
  white-space: nowrap;
  background: #1d1a2b;
  border: 1px solid #443c63;
  color: #c3b8ea;
}

.start-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

/* The master switch over Spotify. Off is the resting state and looks like any
   other panel; on gets the amber edge, because "the jukebox currently owns
   Derek's Spotify account" is the one thing on this page worth spotting from
   across a room. */
.party-switch { border-left: 3px solid var(--line-bright); }
.party-switch--on { border-left-color: var(--amber); }

/* ------------------------------------------------------------- display */

/* Sizing here is in `dvh`, never `vh`.

   The container is `100dvh` -- the viewport actually visible. `vh` is the
   *large* viewport, the one you get with the browser chrome hidden, so on an
   iPad showing Safari's address bar every vh-sized thing on this screen was
   scaled for a viewport around 8% taller than the space it had to fit in. The
   artwork, the QR code, every font and the page padding, all overshooting
   together, which is why it got worse the more there was to fit and why it
   cut the walk-up button in half. */

.display {
  /* The tablet is a fixed viewport: never scroll, never bounce.

     Pinned rather than merely clipped. `overflow: hidden` stops the body
     scrolling but leaves the document itself free to move, which on iOS is
     enough for rubber-banding and for the address bar resizing the visual
     viewport out from under a 100dvh layout -- a party screen that drifts a
     few pixels when someone brushes it. */
  position: fixed;
  inset: 0;
  overflow: hidden;
  overscroll-behavior: none;
  height: 100dvh;
}

/* 100% of the pinned body, not another 100dvh inside it: two full-viewport
   boxes nested one inside the other is where the stray scroll came from. */
/* Every box between the pinned body and the queue is a grid row rather than a
   percentage of its parent. `height: 100%` needs a parent with a *definite*
   height, and main's height comes from flexing, which Safari treats as
   indefinite -- so the percentage collapsed to auto, the column sized to its
   content, and the bottom of the join panel was clipped by the fixed body.
   Two queued songs was enough to cut the walk-up button in half.

   A grid row of `minmax(0, 1fr)` stretches its child to the space available
   without either of them having to name a height. */
.display__main {
  padding: clamp(1rem, 3dvh, 2rem);
  height: 100%;
  min-height: 0;
  display: grid;
  grid-template-rows: minmax(0, 1fr);
}

/* The htmx wrapper is a bare div between the fixed-height main and the grid.
   With no height of its own, the grid's `height: 100%` had nothing to resolve
   against and fell back to content height -- 761px of content reporting itself
   as the full height of a 722px box. That, not too much content, is where the
   stray scroll on the party screen came from. */
#display-body {
  min-height: 0;
  display: grid;
  grid-template-rows: minmax(0, 1fr);
}

.display__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(18rem, 0.85fr);
  gap: clamp(1rem, 3vw, 2.5rem);
  min-height: 0;
  /* Stretch, so the side column gets a real height from the row rather than a
     percentage of a parent whose own height is resolved differently by
     different engines. The artwork keeps its optical centring below. */
  align-items: stretch;
}
.display__now { align-self: center; min-height: 0; }

.display__now { min-width: 0; text-align: center; }

.display__art {
  width: min(38dvh, 100%);
  aspect-ratio: 1;
  height: auto;
  border-radius: 18px;
  object-fit: cover;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
  margin: 0 auto 1.25rem;
  display: block;
  background: var(--bg-sunken);
}
.display__art--placeholder {
  display: grid;
  place-items: center;
  font-size: 5rem;
  color: var(--ink-faint);
  border: 1px solid var(--line);
}

.display__eyebrow {
  font-size: clamp(0.7rem, 1.4dvh, 0.9rem);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--amber);
  margin: 0 0 0.4rem;
  font-weight: 650;
}

.display__title {
  font-size: clamp(1.5rem, 4.2dvh, 3rem);
  line-height: 1.1;
  margin: 0 0 0.3rem;
  overflow-wrap: anywhere;
}

.display__artist {
  font-size: clamp(1rem, 2.4dvh, 1.5rem);
  color: var(--ink-muted);
  margin: 0;
  overflow-wrap: anywhere;
}

.display__requester {
  color: var(--ink-faint);
  margin: 0.4rem 0 0;
  font-size: clamp(0.8rem, 1.6dvh, 1rem);
}

.display__progress {
  margin: 1.25rem auto 0;
  max-width: 32rem;
  height: 8px;
  border-radius: 999px;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  overflow: hidden;
}
.display__progress-bar {
  height: 100%;
  background: linear-gradient(90deg, var(--amber-deep), var(--amber));
  transition: width 1s linear;
}
.display__times {
  display: flex;
  justify-content: space-between;
  max-width: 32rem;
  margin: 0.4rem auto 0;
  color: var(--ink-faint);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

/* Grid rows rather than flex-shrink. Safari 15 -- the party screen is an iPad
   Air 2 that goes no higher -- did not shrink the queue the way `flex: 1 1
   auto` implies, so a queued song rendered outside its box and the join panel,
   which has a background and comes later, painted straight over it: Derek
   queued a song and could not see it at all. `minmax(0, 1fr)` is a hard
   instruction to take the space left and no more. */
.display__side {
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  gap: 1.25rem;
  min-height: 0;
}

.display__heading {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-faint);
  margin: 0 0 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* The queue takes whatever is left and scrolls inside itself if the night is
   busy; the join panel below it never moves. Clipping instead would hide songs
   with no sign they existed, and pushing the QR off the bottom of a screen
   nobody can scroll is worse than either. */
.display__next {
  min-height: 0;
  /* Belt to the grid's braces: whatever happens above, nothing here is allowed
     to paint outside this box and end up under the join panel. */
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.display__queue {
  list-style: none;
  margin: 0;
  padding: 0;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.display__queue-item {
  display: flex;
  gap: 0.7rem;
  align-items: center;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--line);
}
.display__queue-item:last-child { border-bottom: 0; }
.display__queue-item img { border-radius: 6px; object-fit: cover; flex: none; }
.display__queue-title {
  margin: 0;
  font-weight: 600;
  font-size: clamp(0.85rem, 1.7dvh, 1.05rem);
  line-height: 1.25;
}
.display__queue-artist {
  margin: 0.05rem 0 0;
  color: var(--ink-muted);
  font-size: clamp(0.72rem, 1.4dvh, 0.9rem);
}
.display__queue-who { color: var(--ink-faint); }
.display__empty { color: var(--ink-faint); line-height: 1.6; }

.display__join {
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.1rem;
  text-align: center;
}
.display__qr {
  width: clamp(8rem, 16dvh, 13rem);
  height: auto;
  border-radius: 10px;
  /* The code must stay high-contrast to scan; it is generated black on white. */
  background: #fff;
  padding: 0.5rem;
  display: block;
  margin: 0 auto 0.75rem;
}
.display__scan { margin: 0; font-weight: 650; font-size: clamp(0.95rem, 2dvh, 1.2rem); }
.display__url {
  margin: 0.25rem 0 0;
  color: var(--ink-faint);
  font-size: 0.8rem;
  overflow-wrap: anywhere;
}
.display__pin {
  margin: 0.5rem 0 0;
  font-size: clamp(0.9rem, 1.8dvh, 1.1rem);
  letter-spacing: 0.05em;
}
.display__pin strong { color: var(--amber); letter-spacing: 0.2em; }
.display__walkup { margin-top: 0.9rem; width: 100%; }

.display__warning {
  position: fixed;
  bottom: 0.75rem;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  background: #2b2213;
  border: 1px solid #6b5320;
  color: #f6e2b8;
  padding: 0.3rem 0.9rem;
  border-radius: 999px;
  font-size: 0.78rem;
}

/* Portrait tablets and anything narrow: stack instead of side-by-side. */
/* An iPad Air 2 in landscape is 768px tall, and six queued songs came to about
   one row more than fitted. Tightening the rows is enough to land it, so the
   list holds a full queue without scrolling on the screen this is actually
   run on. Taller screens keep the roomier spacing. */
@media (max-height: 800px) {
  .display__queue-item { padding: 0.25rem 0; }
  .display__queue-item img { width: 48px; height: 48px; }
}

@media (max-aspect-ratio: 1/1), (max-width: 48rem) {
  .display { overflow: auto; height: auto; }
  .display__main { height: auto; display: block; }
  #display-body { display: block; }
  .display__grid { grid-template-columns: 1fr; align-items: start; }
  .display__art { width: min(46vw, 20rem); }
}

/* --------------------------------------------------------------- kiosk */

.kiosk__panel { max-width: 44rem; margin-inline: auto; }
/* Bigger targets: this is used standing up, often one-handed, often tipsy. */
.kiosk .result__request, .kiosk .button { min-height: 52px; }
.kiosk__name, .kiosk__search { font-size: 1.1rem; min-height: 54px; }
.results--kiosk .result { padding: 0.85rem 0; }

/* The kiosk enrolment address, shown so the display can be enrolled without
   ever signing in as host on it. Long and unbreakable, so it must wrap. */
.enrol-link {
  display: block;
  font-size: 0.78rem;
  padding: 0.5rem 0.65rem;
  border-radius: 6px;
  background: var(--bg-sunken);
  border: 1px solid var(--line-bright);
  color: var(--ink-muted);
  word-break: break-all;
  user-select: all;
}

/* Quiet enough to ignore, present enough to answer "which version is this?"
   without opening /healthz. */
.site-footer__version {
  color: var(--ink-faint);
  opacity: 0.65;
  font-size: 0.8em;
  font-variant-numeric: tabular-nums;
  margin-left: 0.35rem;
}

/* The enrolment QR. White plate because a scanner wants the quiet zone light,
   and this page is dark. */
.enrol-qr { margin-bottom: 1rem; }
.enrol-qr img {
  display: block;
  background: #fff;
  padding: 0.5rem;
  border-radius: var(--radius-sm);
  width: 200px;
  height: 200px;
  max-width: 100%;
}
.enrol-qr summary { cursor: pointer; margin-top: 0.6rem; min-height: var(--tap); }

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

   Three admin pages that are genuinely different jobs, so the way between them
   is always visible rather than being a link at the bottom of whichever one
   you are on. */
.admin-nav {
  display: flex;
  gap: 0.35rem;
  margin: 0 0 1rem;
  padding: 0.25rem;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.admin-nav__link {
  flex: 1;
  min-height: var(--tap);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
  color: var(--ink-muted);
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 600;
}
.admin-nav__link:hover { color: var(--ink); background: #211d1b; }
.admin-nav__link.is-here {
  background: var(--bg-raised);
  color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--line-bright);
}

/* -------------------------------------------------------------------- decks

   A phone page held one-handed in a dark room. Targets are deliberately larger
   than the 44px floor used elsewhere: the person tapping them is standing up,
   talking to somebody, and not looking very hard. */
.decks .wrap { max-width: 34rem; }

.deck-now__inner,
.deck-now__empty {
  display: flex;
  gap: 0.9rem;
  align-items: center;
}
.art--deck {
  width: 84px;
  height: 84px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  flex: none;
}
.deck-now__text { min-width: 0; }
.deck-now__title {
  margin: 0.15rem 0 0;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.2;
}
.deck-now__artist { margin: 0.1rem 0 0; color: var(--ink-muted); }
.deck-now__meta { margin: 0.25rem 0 0; font-size: 0.82rem; }

/* Tap-to-seek. Taller than the read-only bar elsewhere, and padded, because it
   is a control rather than a readout -- a 6px target is not a target. */
.progress--seek {
  height: 10px;
  cursor: pointer;
  touch-action: manipulation;
}
.progress--seek::after {
  /* Extends the tappable area above and below without moving the bar. */
  content: "";
  display: block;
  height: 34px;
  margin-top: -22px;
}

.deck-transport { position: sticky; bottom: 0.5rem; z-index: 2; }
.deck-transport__row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
}
.deck-transport__row--minor { margin-top: 0.75rem; gap: 0.5rem; }
.button--transport {
  min-width: 64px;
  min-height: 56px;
  font-size: 1.35rem;
  padding: 0.25rem 1rem;
}
.button--transport-main { min-width: 96px; min-height: 64px; font-size: 1.6rem; }
.button.is-on { border-color: var(--amber); color: var(--amber); }

.deck-volume {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 0.9rem;
}
.deck-volume input[type="range"] { flex: 1; min-height: var(--tap); accent-color: var(--amber); }
.deck-volume__icon { font-size: 0.95rem; opacity: 0.8; }

.deck-queue { list-style: none; margin: 0.5rem 0 0; padding: 0; }
.deck-queue__item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--line);
}
.deck-queue__item:last-child { border-bottom: 0; }
.deck-queue__pos {
  flex: none;
  width: 1.4rem;
  text-align: right;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
  font-size: 0.85rem;
}
.deck-queue__art { width: 40px; height: 40px; border-radius: 6px; object-fit: cover; flex: none; }
.deck-queue__text { flex: 1; min-width: 0; }
.deck-queue__title {
  margin: 0;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.deck-queue__sub {
  margin: 0.1rem 0 0;
  font-size: 0.8rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.deck-queue__actions { display: flex; gap: 0.25rem; flex: none; }
.button--icon {
  min-width: var(--tap);
  min-height: var(--tap);
  padding: 0.25rem;
  font-size: 1rem;
}

.deck-guests {
  display: block;
  font-size: 0.78rem;
  font-weight: 400;
  margin-top: 0.15rem;
}
.deck-history summary { cursor: pointer; min-height: var(--tap); display: flex; align-items: center; }
.deck-history__item { padding: 0.2rem 0; font-size: 0.85rem; }
.deck-history__who { color: var(--ink-faint); }
.deck-search { font-size: 1rem; }
.results--deck { list-style: none; margin: 0.5rem 0 0; padding: 0; }
.deck-attention { border-color: var(--amber-deep); }
.deck-join summary { cursor: pointer; min-height: var(--tap); display: flex; align-items: center; }
.deck-levers { margin-bottom: 2rem; }
