/*
 * Party Playlist — host rail.
 *
 * The host rail is a backstage surface: sign in, make a party, read a code out
 * to a room. So it stays quiet, and spends its one loud moment on the join code
 * — the thing a host actually reads aloud or photographs.
 *
 * Slate-green neutrals against an oxidised signal orange. Iowan Old Style for
 * headings, the system sans for reading, SF Mono for anything a person has to
 * transcribe. No webfonts: a silent fallback would cost more than it buys.
 */

:root {
  --bg: #E9EDEA;
  --surface: #F4F6F3;
  --surface-2: #DEE4DF;
  --ink: #1B211E;
  --ink-2: #4A544E;
  --ink-3: #6E7A73;
  --rule: #C6CFC8;
  --rule-strong: #A9B5AD;

  --accent: #B4460E;
  --accent-ink: #8A350A;
  --accent-soft: #F3DED2;
  --accent-contrast: #fff;

  --ok: #2E6B3E;
  --ok-soft: #D6E8D9;
  --warn: #7E6008;
  --warn-soft: #F0E4C2;
  --danger: #9B2A26;
  --danger-soft: #F2D9D7;

  --serif: "Iowan Old Style", Charter, "Palatino Linotype", Palatino, Georgia, serif;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, "Cascadia Mono", Consolas, monospace;

  --gap: 16px;
  --radius: 5px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #141815;
    --surface: #1C221E;
    --surface-2: #262E28;
    --ink: #E4E9E5;
    --ink-2: #A6B1AA;
    --ink-3: #7C8880;
    --rule: #333D36;
    --rule-strong: #47534A;

    --accent: #FF7A3D;
    --accent-ink: #FF9463;
    --accent-soft: #3A2318;
    --accent-contrast: #fff;

    --ok: #74C98A;
    --ok-soft: #16301D;
    --warn: #D9AC3A;
    --warn-soft: #332912;
    --danger: #E8635E;
    --danger-soft: #351A19;
  }
}

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
  font-family: var(--serif);
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.15;
  text-wrap: balance;
  margin: 0 0 10px;
}
h1 { font-size: 27px; }
h2 { font-size: 19px; }

p { margin: 0 0 12px; }

a { color: var(--accent-ink); text-underline-offset: 2px; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

/* ---------- shell ---------- */

.appbar {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 12px 22px;
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
}

.appbar__mark {
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
}

.appbar__spacer { flex: 1; }

.appbar a { color: var(--ink-2); font-size: 13.5px; }

.page {
  max-width: 860px;
  margin: 0 auto;
  padding: 28px 22px 64px;
}

/* Narrow centred column for the signed-out screens. */
.panel {
  max-width: 380px;
  margin: 40px auto 0;
  padding: 30px 28px 34px;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.panel--plain {
  background: none;
  border: none;
  text-align: center;
}

.lede {
  color: var(--ink-2);
  font-size: 14px;
  margin: -4px 0 22px;
}

.mark {
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 600;
  margin: 0 0 24px;
}

/* ---------- flash ---------- */

.flash {
  max-width: 860px;
  margin: 14px auto 0;
  padding: 10px 16px;
  border-left: 3px solid var(--ink-3);
  background: var(--surface-2);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 14px;
}
.flash-notice { border-left-color: var(--ok); background: var(--ok-soft); }
.flash-alert { border-left-color: var(--danger); background: var(--danger-soft); }

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

form label {
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
  margin-bottom: 4px;
}

input[type="email"],
input[type="password"],
input[type="text"],
input[type="datetime-local"] {
  display: block;
  width: 100%;
  margin-bottom: var(--gap);
  padding: 9px 11px;
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--bg);
  border: 1px solid var(--rule-strong);
  border-radius: 4px;
}

input::placeholder { color: var(--ink-3); }

input[type="submit"],
button {
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  padding: 9px 16px;
  color: var(--accent-contrast);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 4px;
  cursor: pointer;
}

input[type="submit"] { width: 100%; }

/* button_to wraps its button in a form; keep those inline so action rows sit in a line. */
.button_to { display: inline-block; margin: 0 6px 6px 0; }

/* Secondary and destructive actions are quieter than the primary orange. */
.button_to button {
  color: var(--ink);
  background: var(--surface-2);
  border-color: var(--rule-strong);
}
.actions .button_to:first-child button {
  color: var(--accent-contrast);
  background: var(--accent);
  border-color: var(--accent);
}
.button_to--danger button {
  color: var(--danger);
  background: transparent;
  border-color: var(--danger);
}

.btn {
  display: inline-block;
  padding: 8px 15px;
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
  color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--rule-strong);
  border-radius: 4px;
}
.btn--primary { color: var(--accent-contrast); background: var(--accent); border-color: var(--accent); }

.errors {
  margin: 0 0 var(--gap);
  padding: 10px 14px 10px 30px;
  list-style: disc;
  font-size: 13.5px;
  color: var(--danger);
  background: var(--danger-soft);
  border-radius: 4px;
}

.formfoot { margin-top: 20px; font-size: 13.5px; color: var(--ink-2); }
.formfoot a { display: inline-block; }

/* Hint text under a field, and the secondary line in a list row. Used on both
   host and guest pages, as <p> and as <span> -- so size and colour only, and
   nothing that would move a block around. --ink-2 is one of the tokens the
   guest layout overrides per party, so this follows a host's theme rather than
   fighting it.

   This rule was missing entirely until 2026-08-31: eight views asked for
   .pmeta and got the full body size and weight, which is the opposite of what
   a hint is for. */
.pmeta { font-size: 13.5px; color: var(--ink-2); }

/* ---------- page furniture ---------- */

.pagehead {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}
.pagehead h1 { margin: 0; }
.pagehead .actions { margin-left: auto; }

.actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.card {
  padding: 18px 20px;
  margin-bottom: var(--gap);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.card h2 { margin-bottom: 6px; }
.card p:last-child { margin-bottom: 0; }
.card .note { font-size: 13.5px; color: var(--ink-2); }

.rsvpsum {
  font-family: var(--mono);
  font-size: 15px;
  color: var(--ink);
  margin-bottom: 8px;
}

.pill {
  display: inline-block;
  padding: 3px 10px;
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1px solid var(--rule-strong);
  border-radius: 100px;
}
.pill--live { color: var(--ok); background: var(--ok-soft); border-color: var(--ok); }
.pill--setup { color: var(--accent-ink); background: var(--accent-soft); border-color: var(--accent); }
.pill--pending { color: var(--warn); background: var(--warn-soft); border-color: var(--warn); }
.pill--ready { color: var(--ok); background: var(--ok-soft); border-color: var(--ok); }
.pill--failed { color: var(--danger); background: var(--danger-soft); border-color: var(--danger); }

/* ---------- party list ---------- */

.partylist {
  margin: 0;
  padding: 0;
  list-style: none;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
}

.partylist li {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  padding: 13px 18px;
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
}
.partylist li:last-child { border-bottom: none; }
.partylist li a { font-weight: 600; text-decoration: none; color: var(--ink); }
.partylist li a:hover { color: var(--accent-ink); }
/* Not .partylist .grow: the reorder rows in the party form carry the same class
   and sit in the same kind of flex row, and scoping it to one list left their
   buttons floating next to the label instead of at the end of the row. */
.grow { flex: 1; min-width: 140px; }

.thumb {
  flex: none;
  width: 48px;
  height: 48px;
  border-radius: 3px;
  object-fit: cover;
  background: var(--surface-2);
}

.thumb--empty {
  display: block;
  border: 1px solid var(--rule);
}

.empty {
  padding: 30px 22px;
  text-align: center;
  font-size: 14px;
  color: var(--ink-2);
  border: 1px dashed var(--rule-strong);
  border-radius: var(--radius);
}

/* ---------- the join code: the one loud thing on the rail ---------- */

.joinbox {
  padding: 22px 24px;
  margin-bottom: var(--gap);
  background: var(--surface);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
}

.joinbox__label {
  margin: 0 0 8px;
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.joincode {
  margin: 0;
  font-family: var(--mono);
  font-size: clamp(32px, 9vw, 46px);
  font-weight: 600;
  letter-spacing: 0.16em;
  line-height: 1;
  color: var(--ink);
}

.joinbox__url {
  margin: 12px 0 0;
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--ink-2);
  word-break: break-all;
}

/* A second link (the song queue) starts a new group, not a continuation of the notes above it. */
.joinbox__meta + .joinbox__label { margin-top: 18px; }

.joinlink__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 10px;
}

/* A preview, not the printable code: the downloads are what goes on an invitation. White
   behind it in both themes, because a QR code needs its light border to scan. */
.joinlink__qr {
  flex: none;
  padding: 4px;
  background: #fff;
  border: 1px solid var(--rule-strong);
  border-radius: 4px;
}

.joinlink__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.joinbox__meta {
  margin: 4px 0 0;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-3);
}

/* ---------- guest join screen ---------- */

.joined__label {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 6px;
}
.joined h1 { font-size: 26px; margin-bottom: 14px; }

@media (max-width: 560px) {
  .page { padding: 20px 16px 48px; }
  .panel { margin-top: 20px; padding: 24px 20px 28px; border-radius: 0; }
  .pagehead .actions { margin-left: 0; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ---------- party creation/edit form ---------- */

.landing-fields {
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 16px 18px;
  margin: 0 0 var(--gap);
}

.landing-fields legend {
  padding: 0 6px;
  font-weight: 600;
}

.landing {
  padding: 18px 20px;
  margin-bottom: var(--gap);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
}

.landing__when {
  margin: 0 0 4px;
  font-weight: 600;
}

.landing__where { margin: 0 0 10px; }

.landing__rsvpby {
  margin: 10px 0 0;
  font-size: 13.5px;
  color: var(--ink-2);
}

.landing__notes h2 {
  margin: 14px 0 4px;
  font-size: 15px;
}

.checkline {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}

/* ---------- the search spinner: no JavaScript involved ---------- */

/* Turbo sets aria-busy="true" on a frame for the life of its request, so the
   whole loading state is CSS. The spinner lives outside the frame — inside it,
   a results swap would delete it mid-spin. :has() reaches back up from the
   frame's state to the field; ApplicationController already enforces
   allow_browser versions: :modern, so it is safe to rely on. */

.searchfield {
  position: relative;
  display: block;
}

.searchspin {
  position: absolute;
  top: 19px;
  right: 12px;
  width: 14px;
  height: 14px;
  margin-top: -7px;
  border: 2px solid var(--rule-strong);
  border-top-color: var(--accent);
  border-radius: 50%;
  opacity: 0;
  transition: opacity 120ms ease-out;
  pointer-events: none;
}

.panel:has(turbo-frame[aria-busy="true"]) .searchspin {
  opacity: 1;
  animation: searchspin 700ms linear infinite;
}

@keyframes searchspin { to { transform: rotate(360deg); } }

/* Reduced motion still gets the signal, just not the spin. */
@media (prefers-reduced-motion: reduce) {
  .panel:has(turbo-frame[aria-busy="true"]) .searchspin {
    animation: none;
    border-top-color: var(--rule-strong);
    background: var(--accent);
  }
}

/* The flyer is atmosphere, never information: nothing in it needs to be read,
   so it is cropped and dimmed without apology. A fixed-position element rather
   than background-attachment: fixed, which is broken on iOS Safari. */
.partybg {
  position: fixed;
  inset: 0;
  z-index: -1;
  background-size: cover;
  background-position: center;
}

.partybg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--bg);
  opacity: 0.72;
}

/* A flyer means the panel has to actually be a panel. Party::Theme derives
   every ink and accent value against --surface and corrects a host's accent
   until it clears 4.5 there, so that guarantee is worth something only if
   --surface is the colour the text is genuinely drawn on. Left transparent,
   guest text composites onto 0.72 * --bg + 0.28 * whatever the host uploaded:
   .lede falls from 7.2 to 3.4 on the very pairing the form recommends, and a
   pale accent corrected to 4.94 lands at 2.32. No scrim opacity fixes that --
   the panel is what the spec meant by "a filled panel for anything that has to
   be read". :has() is the mechanism the search spinner already relies on, and
   ApplicationController enforces allow_browser versions: :modern. */
body:has(.partybg) .panel--plain {
  background: var(--surface);
  border: 1px solid var(--rule);
}

.panel--top { margin-top: 0; }
.panel--middle { margin-top: 8vh; }
.panel--bottom { margin-top: 22vh; }

/* Horizontal placement needs width to mean anything. On a phone the panel is
   full-width and only the vertical choice does any work. */
@media (min-width: 700px) {
  .panel--left { margin-right: auto; margin-left: 0; }
  .panel--centre { margin-right: auto; margin-left: auto; }
  .panel--right { margin-right: 0; margin-left: auto; }
}

.tones {
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 16px 18px;
  margin: 0 0 var(--gap);
}

.tones legend { padding: 0 6px; font-weight: 600; }
.tones label { display: block; margin-bottom: 8px; }

.reorder {
  list-style: none;
  margin: 0 0 var(--gap);
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.reorder li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-bottom: 1px solid var(--rule);
}

.reorder li:last-child { border-bottom: none; }
.reorder button { width: auto; padding: 2px 10px; }
