/* Palette lifted from the cover art: cream paper, ink navy, and the record's
   vermillion, with yellow and teal as the supporting accents. */
:root {
  color-scheme: light;
  --paper: #f7f0e1;
  --card: #fffdf7;
  --ink: #1b2030;
  --ink-soft: #6b6f7d;
  --vermillion: #f04e23;
  --yellow: #f5c24a;
  --teal: #2e9d9b;
  --radius: 18px;
  --shadow: 0 4px 0 var(--ink);
}

* { box-sizing: border-box; }

/* The UA's `[hidden] { display: none }` loses to any author `display` rule, so
   `.btn-block` kept hidden buttons on screen. `!important` is what outranks it. */
[hidden] { display: none !important; }

body {
  margin: 0;
  padding: 28px 18px 10px;
  background: var(--paper);
  color: var(--ink);
  font: 16px/1.55 "Fredoka", system-ui, -apple-system, sans-serif;
  -webkit-text-size-adjust: 100%;
}

main { max-width: 460px; margin-inline: auto; }

/* Paid for out of the body's bottom padding rather than added to it: the round
   has to keep fitting one screen, which is why the masthead goes away. The
   footer and the padding under it together fit inside the 56px that used to be
   bottom padding alone. */
.colophon {
  margin: 8px 0 0;
  text-align: center;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
}

/* The wordmark is the way out of a game in progress. A footer-sized target is
   not a thumb-sized one, so the padding buys a real hit area and the matching
   negative margin gives the layout height straight back. */
.wordmark {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  background: none;
  border: 0;
  border-radius: 10px;
  padding: 13px 14px;
  margin: -13px -14px;
  cursor: pointer;
  color: var(--vermillion);
  font-weight: 600;
}

.colophon-date { display: block; margin-top: 2px; }

.wordmark:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }

/* On the setup screen the wordmark has nowhere to go, so it is disabled there
   and reads as the plain colophon it looks like. */
.wordmark:disabled { cursor: default; }



header { text-align: center; margin-bottom: 26px; }

h1 {
  font-size: clamp(34px, 12vw, 46px);
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: -0.02em;
  margin: 0;
}

.masthead-home {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  background: none;
  border: 0;
  padding: 0;
  border-radius: 10px;
  cursor: pointer;
}

.masthead-home:disabled { cursor: default; }

.masthead-home:focus-visible { outline: 3px solid var(--ink); outline-offset: 4px; }

.pop { color: var(--vermillion); }

.tagline { color: var(--ink-soft); margin: 8px 0 0; font-size: 15px; }

/* ---------- Cards, controls ---------- */

.card {
  background: var(--card);
  border: 3px solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 22px;
}

label, .field-label {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  margin: 20px 0 8px;
}

label:first-child { margin-top: 0; }
label b { color: var(--ink); font-weight: 600; text-transform: none; letter-spacing: 0; }

input[type="text"] {
  width: 100%;
  padding: 14px 16px;
  font: 600 17px/1.3 "Fredoka", system-ui, sans-serif;
  color: var(--ink);
  background: var(--paper);
  border: 3px solid var(--ink);
  border-radius: 12px;
}

input[type="text"]:focus {
  outline: none;
  border-color: var(--vermillion);
}

/* Label, then minus / value / plus. The value track is wide enough for the
   longest reading ("10.0s") so pressing a button never shifts the other one. */
.stepper {
  display: grid;
  grid-template-columns: 1fr auto minmax(4.5ch, auto) auto;
  align-items: center;
  gap: 12px;
  margin-top: 18px;
}

.stepper .field-label { margin: 0; }

.stepper-value {
  font: 600 19px/1 "Fredoka", system-ui, sans-serif;
  text-align: center;
}

.btn.step {
  min-width: 52px;
  padding: 0;
  font-size: 24px;
  font-weight: 600;
  /* A held button must not scroll the page, select the glyph, or raise iOS's
     long-press callout -- all three would interrupt the repeat. */
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

.split { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.split.gap { gap: 10px; margin-top: 14px; }

/* ---------- Buttons ---------- */

.btn {
  font: 600 17px/1 "Fredoka", system-ui, sans-serif;
  min-height: 54px; /* comfortable thumb target */
  padding: 14px 20px;
  color: var(--ink);
  background: var(--card);
  border: 3px solid var(--ink);
  border-radius: 14px;
  box-shadow: var(--shadow);
  cursor: pointer;
  transition: transform 0.06s ease, box-shadow 0.06s ease;
}

.btn:active:not(:disabled) {
  transform: translateY(4px);
  box-shadow: 0 0 0 var(--ink);
}

.btn-primary { background: var(--vermillion); color: #fff; }
.btn-block { display: block; width: 100%; margin-top: 22px; }
.btn:disabled { opacity: 0.4; cursor: default; box-shadow: var(--shadow); }

.segmented { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }

.segmented button {
  font: 600 16px/1 "Fredoka", system-ui, sans-serif;
  min-height: 50px;
  color: var(--ink);
  background: var(--card);
  border: 3px solid var(--ink);
  border-radius: 12px;
  cursor: pointer;
}

.segmented button[aria-pressed="true"] { background: var(--yellow); box-shadow: inset 0 -4px 0 rgb(0 0 0 / 0.12); }

/* ---------- Scoreboard ---------- */

.scoreboard {
  /* Equal side tracks, so the middle track is centred on the container -- and
     therefore on the record below it. space-between only centres the artist
     within the leftover gap, which is not the same thing. 1fr alone is not
     enough: once the row is full the sides fall back to their own content
     widths, and an off-centre chip is what a wider score pill buys. --side is
     the wider of the two -- the pill, at any score up to 99 -- so the tracks
     stay equal at every width and the artist name ellipsises instead. */
  --side: 93px;
  display: grid;
  grid-template-columns: minmax(var(--side), 1fr) minmax(0, auto) minmax(var(--side), 1fr);
  align-items: center;
  gap: 10px;
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-soft);
  margin-bottom: 22px;
}

.chip {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
  color: var(--ink);
  font-weight: 600;
}

#progress {
  justify-self: start;
  white-space: nowrap;
}

.score-pill {
  /* Grid items stretch by default; the pill must keep its own width. */
  justify-self: end;
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 12px;
  white-space: nowrap;
  color: #fff;
  background: var(--teal);
  border: 3px solid var(--ink);
  border-radius: 999px;
  font-weight: 600;
}

/* Smaller than the count it labels, so the row's line box still sits on the
   14px number and the scoreboard keeps its height. */
.score-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

/* Two digits' worth, so the pill is one width for every score. */
#score { min-width: 18px; text-align: center; }

/* ---------- The record ---------- */

.deck { display: flex; justify-content: center; padding: 6px 0 18px; }

.record {
  position: relative;
  width: min(250px, 68vw);
  aspect-ratio: 1;
  padding: 0;
  border: 4px solid var(--ink);
  border-radius: 50%;
  background: none;
  cursor: pointer;
  box-shadow: 0 6px 0 var(--ink);
  transition: transform 0.06s ease, box-shadow 0.06s ease;
}

.record:active:not(:disabled) { transform: translateY(5px); box-shadow: 0 1px 0 var(--ink); }
.record:disabled { opacity: 0.55; cursor: default; }

/* Grooves live on their own layer so the disc can spin while the hub label
   stays upright and readable. */
.disc {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  /* The sheen is what makes the spin visible at all: the grooves are a
     rotationally symmetric gradient, so turning them maps them onto
     themselves. */
  background:
    conic-gradient(from 210deg,
      rgba(255, 255, 255, 0) 0deg,
      rgba(255, 255, 255, 0.22) 60deg,
      rgba(255, 255, 255, 0) 130deg),
    repeating-radial-gradient(circle at 50% 50%,
      #14161d 0 2px, #23262f 2px 5px);
  /* Paused rather than absent, so stopping leaves the disc where the needle
     lifted instead of snapping back to 0deg. */
  animation: spin 1.8s linear infinite;
  animation-play-state: paused;
}

/* 1.8s is a real LP's 33 1/3 rpm. A 0.5s snippet turns ~100deg either way, so
   legibility costs nothing here and realism wins. */
.record.playing .disc { animation-play-state: running; }

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

/* A replay is the same few seconds, so the needle goes back to the same spot:
   the disc pauses, then winds back to where it started. The animation has to
   be off for the transform transition to have anything to move. */
.disc.rewind {
  animation: none;
  transition: transform 0.5s ease-in-out 0.35s;
}

/* The centre label, always on show. Being a child of .disc it turns with the
   grooves rather than running an animation of its own -- nothing to keep in
   sync, and the rewind carries it back too. */
.label {
  position: absolute;
  inset: 28%;
  border-radius: 50%;
  background: var(--vermillion);
  border: 3px solid var(--ink);
}

/* The spindle hole. */
.label::after {
  content: "";
  position: absolute;
  inset: 44%;
  border-radius: 50%;
  background: var(--paper);
  border: 2px solid var(--ink);
}

/* A word in the centre needs the spindle out of the way, or it reads as a typo.
   The record is disabled in exactly the two states that show one: loading, and
   out of replays. */
.record:disabled .label::after { display: none; }

/* The label is always on show, so the hub is no longer a disc of its own --
   just what sits on top of it, centred on the spindle. */
.hub {
  position: absolute;
  inset: 28%;
  display: grid;
  place-items: center;
  color: #fff;
  font-weight: 600;
  font-size: 19px;
  text-align: center;
  line-height: 1.15;
  padding: 6px;
}

.hub > * { grid-area: 1 / 1; }

/* A span around the svg, because `hidden` is an HTML property -- setting it on
   an <svg> element silently does nothing. */
.play-icon { width: 44%; }

.play-icon svg {
  display: block;
  width: 100%;
  fill: var(--ink);
  /* Rounded corners, to sit with Fredoka rather than cut across it. */
  stroke: var(--ink);
  stroke-width: 9;
  stroke-linejoin: round;
}

/* Plays left in the round, as a row of pips that empty as they are spent.
   Fixed height so ticking one off cannot move the guess row. */
.pips {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 9px;
  height: 18px;
  margin: 0;
}

.pip {
  width: 14px;
  height: 14px;
  border: 3px solid var(--ink);
  border-radius: 50%;
  background: var(--vermillion);
}

.pip.spent { background: var(--paper); opacity: 0.5; }

/* ---------- Guess + reveal ---------- */

/* The reveal is the taller of the two states, so the panel reserves its height
   and swapping one for the other moves nothing above it.

   The gap above the panel belongs to the panel, not to whichever child is
   showing. `#panel` has no padding and no border, so a child's `margin-top`
   collapses straight out through it and moves the panel's own top box: while
   `#guess-row` and `.reveal` carried 26px and 24px of their own, revealing sat
   2px higher than guessing and the page came out 2px shorter (T19). Keeping the
   spacing here is what makes that structural rather than a coincidence between
   two numbers in different rules. */
#panel { min-height: 258px; margin-top: 26px; }

#guess-row { margin-top: 0; }

.reveal { margin-top: 0; animation: rise 0.22s ease-out; }

@keyframes rise { from { opacity: 0; transform: translateY(8px); } }

/* Artwork beside the title rather than above it: stacked, the card was tall
   enough to push Next song off the bottom of a 800px-tall window. */
.answer { display: flex; align-items: center; gap: 14px; margin-top: 12px; }

.answer-text { min-width: 0; } /* lets the ellipsis rules below bite */

#art {
  flex: none;
  width: 72px;
  height: 72px;
  border-radius: 12px;
  border: 3px solid var(--ink);
  background: var(--paper);
}

.verdict {
  margin: 0;
  text-align: center;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.verdict.right { color: var(--teal); }
.verdict.wrong { color: var(--vermillion); }

.title {
  font-size: 21px;
  font-weight: 600;
  margin: 0;
  line-height: 1.2;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.meta {
  color: var(--ink-soft);
  font-size: 15px;
  margin: 6px 0 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

#end .meta { white-space: normal; }

#end { text-align: center; }
.final { font-size: 56px; font-weight: 700; margin: 6px 0 0; color: var(--vermillion); }

/* The clip-trouble message. `.meta` is a single ellipsised line, which a
   sentence needs not to be. */
.note { margin: 10px 0 0; font-size: 15px; line-height: 1.35; color: var(--ink-soft); text-align: center; }

#status { min-height: 22px; margin: 14px 0 0; font-size: 14px; color: var(--ink-soft); text-align: center; }
#status.error { color: var(--vermillion); font-weight: 500; }

/* The head-to-head from a challenge link. Yellow-lined rather than another
   grey `.meta`: it is the one line on the screen that is about someone else. */
.versus {
  margin: 12px 0 0;
  padding: 8px 12px;
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
  background: var(--yellow);
  border: 3px solid var(--ink);
  border-radius: 12px;
}

/* The end screen is centred, but a labelled field is not: the label belongs
   over the left edge of the box it names. */
.name-row { margin-top: 20px; text-align: left; }

/* The copy fallback. Only ever on screen when the clipboard refused, so it can
   be plain -- its whole job is to be long-pressable. */
.share-text {
  display: block;
  width: 100%;
  margin-top: 10px;
  padding: 10px;
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: var(--paper);
  border: 3px solid var(--ink);
  border-radius: 12px;
  resize: none;
  overflow: hidden; /* height is set to fit the text, so there is nothing to scroll */
}

/* ---------- End-screen scoreboard ---------- */

/* Only ever on screen when there are rows or a request is still out. A board
   that could not be reached draws nothing at all, so there is no empty state
   to style here. */
.board {
  margin-top: 22px;
  padding: 10px 12px 12px;
  text-align: left;
  background: var(--paper);
  border: 3px solid var(--ink);
  border-radius: 12px;
}

.board-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* The code gets read aloud as often as it gets tapped, so it is the half of the
   heading set in the ink colour. */
.board-code { color: var(--ink); letter-spacing: 0.14em; }

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

/* The waiting line wears this too, and nothing but this, so the block is
   exactly as tall waiting for one row as it is showing it. */
.board-line {
  margin: 0;
  padding: 8px 0;
  border-top: 2px solid var(--card);
  font-size: 15px;
  line-height: 1.4;
}

.board-waiting { color: var(--ink-soft); }

/* Fixed side tracks, as `.scoreboard` does it: only the name flexes, so a long
   one ellipsises instead of widening the block or moving the times out of
   line. */
.board-row {
  display: grid;
  grid-template-columns: 1.2em minmax(0, 1fr) 2.6em 3.2em 2em;
  align-items: center;
  gap: 6px;
}

.board-pos { font-size: 13px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }

.board-name {
  min-width: 0;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.board-score { font-weight: 600; text-align: right; font-variant-numeric: tabular-nums; }

.board-time {
  font-size: 13px;
  color: var(--ink-soft);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.board-you {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--vermillion);
}

/* Your row is the one the player is looking for. The negative side margin is
   paid straight back as padding, so the highlight reaches the block's edges
   without moving a single column. */
.board-row.is-you {
  margin-inline: -12px;
  padding-inline: 12px;
  background: var(--yellow);
  border-top-color: var(--yellow);
}

.board-row.is-you .board-name, .board-row.is-you .board-pos, .board-row.is-you .board-time {
  color: var(--ink);
}

.board-row.is-you .board-name { font-weight: 700; }

/* ---------- End-screen recap ---------- */

#recap {
  list-style: none;
  margin: 22px 0 4px;
  padding: 0;
  text-align: left;
}

/* Paper on card: enough of a hairline to separate twenty rows without ruling
   the whole screen. */
.recap-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-top: 2px solid var(--paper);
}

.recap-art {
  flex: none;
  width: 56px;
  height: 56px;
  border-radius: 10px;
  border: 3px solid var(--ink);
  background: var(--paper);
}

.recap-text { min-width: 0; flex: 1; }

.recap-text .title { font-size: 17px; -webkit-line-clamp: 1; }
.recap-text .meta { font-size: 13px; margin-top: 2px; }

.said {
  margin: 2px 0 0;
  font-size: 13px;
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.said b { font-weight: 600; color: var(--ink); }

.recap-score { flex: none; display: flex; flex-direction: column; align-items: center; gap: 4px; }

.recap-time {
  font-size: 12px;
  font-weight: 500;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* Drawn rather than an emoji: a check glyph is a different shape, weight and
   colour in every platform's font, and none of them match the ink border. */
.mark {
  position: relative;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 3px solid var(--ink);
}

.mark::before, .mark::after {
  content: "";
  position: absolute;
  background: var(--ink);
}

.mark.right { background: var(--teal); }
.mark.wrong { background: var(--vermillion); }
.mark.gaveup { background: var(--yellow); }

/* The tick is two borders on an empty box, turned on its corner. */
.mark.right::before {
  width: 7px;
  height: 11px;
  left: 6px;
  top: 2px;
  background: none;
  border-right: 3px solid var(--card);
  border-bottom: 3px solid var(--card);
  transform: rotate(40deg);
}

.mark.wrong::before, .mark.wrong::after {
  inset: 9px 4px;
  background: var(--card);
  border-radius: 2px;
}

.mark.wrong::before { transform: rotate(45deg); }
.mark.wrong::after { transform: rotate(-45deg); }

/* A bar, not a cross: giving up is not a wrong answer. */
.mark.gaveup::before { inset: 9px 4px; background: var(--ink); border-radius: 2px; }

@media (prefers-reduced-motion: reduce) {
  .disc { animation: none; }
  .disc.rewind { transition: none; }
  .reveal { animation: none; }
}
