/* view: drawer */
/*
 * The drawer's controls (views/drawer.js), drawn inside #drawer-ui. The
 * frame, the stage and the player are listen.css's: nothing here sizes,
 * moves or hides .drawer-stage — the player is placed by the frame's rules
 * on #drawer[data-state] alone. Colours are the drawer's own dark tokens
 * (listen.css sets them on .drawer whatever the page's tone).
 */

.dw {
  min-width: 0;
}

/* ---- before the first play: one slim line, nothing to press ---------- */

.dw-idle {
  padding: 2px 0;
  font: 600 var(--t-small) / 1.4 var(--sans);
  color: var(--fg-muted);
  text-align: center;
}

/* ---- the row: text, ‹ ›, and the state toggle ------------------------ */

.dw-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.dw[data-state="open"] .dw-bar {
  position: sticky;
  top: 0;
  z-index: 1;
  padding-bottom: 10px;
  background: var(--drawer-bg);
  border-bottom: 1px solid var(--line);
}

.dw-text {
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--tap);
  display: flex;
  flex-direction: column;
  justify-content: center;
  text-align: left;
  color: var(--fg);
}

button.dw-text {
  padding: 2px 0;
}

.dw-text > * {
  display: block;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The eyebrow is the state, then " · their video". The state is never cut
   (cold review 2026-10-09): it wraps before it truncates. On a phone's mini
   row the text column is about 110px, so "their video" is not drawn there
   at all; the open sheet and the full player have the room for both. */
.dw-eyebrow {
  display: flex;
  flex-wrap: wrap;
  color: var(--accent);
}

.dw-text > .dw-eyebrow {
  white-space: normal;
  overflow: visible;
}

.dw-state {
  min-width: 0;
  overflow-wrap: anywhere;
}

.dw-whose {
  white-space: pre;
}

.dw[data-state="mini"] .dw-whose {
  display: none;
}

/* On a phone the mini row keeps ▶/❚❚ › ⌃ and leaves ‹ to the open sheet:
   four rounds beside the player left the name about 70px. */
.dw[data-state="mini"] [data-k="back"] {
  display: none;
}

@media (min-width: 561px) {
  .dw[data-state="mini"] .dw-whose {
    display: inline;
  }

  .dw[data-state="mini"] [data-k="back"] {
    display: inline-flex;
  }
}

/* ◇ Save and ⤢ in the mini row are a wide screen's (the block at the end):
   a phone's row has no room beside ‹ ▶/❚❚ › ⌃. */
.dw[data-state="mini"] [data-k="keep-mini"],
.dw[data-state="mini"] [data-k="full"] {
  display: none;
}

/* Unsaved only: the accent ring would otherwise outrank .round's pressed
   fill and draw the ✓ in the accent on the accent — a blank blue disc
   (Terran's feedback, 2026-10-19). Saved takes the fill, like "Saved ✓". */
.dw [data-k="keep-mini"]:not([aria-pressed="true"]) {
  border-color: var(--accent);
  color: var(--accent);
}

.dw-title {
  font-size: 15px;
  line-height: 1.3;
  color: var(--fg);
}

.dw[data-state="open"] .dw-title {
  font-size: var(--t-row);
}

.dw-sub {
  font-size: var(--t-meta);
  color: var(--fg-muted);
}

.dw-count {
  font: 700 11px / 1.4 var(--sans);
  color: var(--fg-muted);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- the universe's strip (Star Map 8a, Desktop 2c) ------------------ */
/*
 * On the universe the mini row's words are doors, not a button: the name
 * (their page) over "a cover of “{song}” · the song →" (the song), the
 * state above them and what plays next below, plain. Each door is a tap
 * tall without growing its line: the name reaches up over the state, the
 * song down over what plays next, so the two never overlap. On a phone
 * each keeps the row's one line, as the button's words do (the dock keeps
 * its size); with room (561px and up) they wrap rather than cut. While the
 * name is held (8b) the song takes the name's line, quieter, and the
 * reason under it wraps whole.
 */
.dw-strip {
  /* button.dw-text's, so the row is the button's height */
  padding: 2px 0;
}

/* Each door at least a tap tall (min-height: a line's rounding never
   leaves it a hair short), its reach a tap less its own line. */
.dw-strip > .dw-door {
  min-height: var(--tap);
}

/* the reach over the line above: a tap less .dw-title's 1.3 line */
.dw-strip > [data-k="name"] {
  padding-top: calc(var(--tap) - 1.3em);
  margin-top: calc(1.3em - var(--tap));
}

/* the reach over the line below: a tap less its own 1.5 line */
.dw-strip > [data-k="song"] {
  line-height: 1.5;
  padding-bottom: calc(var(--tap) - 1.5em);
  margin-bottom: calc(1.5em - var(--tap));
}

.dw-strip[data-held] > .dw-title {
  font-style: italic;
  color: var(--fg-muted);
}

.dw-strip > .dw-note {
  white-space: normal;
  overflow-wrap: anywhere;
}

@media (min-width: 561px) {
  .dw-strip > .dw-door {
    white-space: normal;
    overflow-wrap: anywhere;
  }
}

.dw-gap {
  flex: 1 1 auto;
}

/* ---- the person: the name (their page), the song (the cover), and
   Subscribe on YouTube; in full, then Save and Open on YouTube ↗ ------- */

.dw-now {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 14px 4px 12px;
}

.dw-now > .dw-eyebrow {
  margin-bottom: 2px;
}

/* A name wraps rather than truncates, and each link is a tap tall. */
.dw-now-title {
  display: flex;
  align-items: center;
  min-height: var(--tap);
  max-width: 100%;
  overflow-wrap: anywhere;
}

.dw-now-title {
  font-family: var(--serif);
  font-size: var(--t-hero);
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--fg);
}

/* "a cover of {song} by {originator}": running words, the two names links
   padded to a tap tall without growing the line. */
.dw-now-sub {
  max-width: 100%;
  margin: 0;
  font-size: 13px;
  line-height: 1.3;
  color: var(--fg-muted);
  overflow-wrap: anywhere;
}

.dw-now-sub a {
  display: inline-block;
  padding: 13px 0;
  margin: -13px 0;
  color: var(--fg);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.dw-subscribe {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding: 0 16px;
  border-radius: var(--r-pill);
  background: var(--youtube-red);
  color: var(--on-red);
  font: 700 13px / 1.2 var(--sans);
  text-align: center;
}

.dw-subscribe:hover {
  text-decoration: none;
}

/* The open sheet: the same person, smaller, between the bar and the lists. */
.dw-now-open {
  padding: 6px 0 4px;
}

.dw-now-open .dw-now-title {
  font-size: var(--t-row);
}

/* The row of four, then This cover →, Subscribe, Open on YouTube ↗ two to
   a row on a phone, and ⋯ beside them; its panel under them all. */
.dw-now-acts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  align-self: stretch;
  gap: 8px;
  margin-top: 12px;
}

.dw-now-acts > .pill {
  flex: 1 1 calc(50% - 32px);
  min-width: 0;
}

/* The row of four (ledger 449): Save this cover, Save this person, Not
   this song, Not them — two to a row on a phone, each a toggle. The saves
   in the saves' colour; the set-asides plain, filled once pressed. */
.dw-taste {
  display: flex;
  flex-wrap: wrap;
  flex: 1 1 100%;
  gap: 8px;
  min-width: 0;
}

.dw-taste > .pill {
  flex: 1 1 calc(50% - 4px);
  min-width: 0;
}

.dw-keep,
.dw-keep-person {
  border-color: var(--accent);
}

.dw-aside-song[aria-pressed="true"],
.dw-aside-person[aria-pressed="true"] {
  background: var(--fg);
  border-color: var(--fg);
  color: var(--drawer-bg);
}

/* What a set-aside did, with its Undo: under the row of four, or a row. */
.dw-aside {
  flex: 1 1 100%;
  min-width: 0;
}

.dw-aside .aside-said {
  margin: 0;
}

.dw-aside-row {
  padding: 0 0 6px 2px;
}

/* ⋯ and its panel: "Report a mix-up", a claim a row, the note, Undo. */
.dw-mixup {
  flex: 1 1 100%;
  min-width: 0;
  padding: 10px 0 2px;
  border-top: 1px solid var(--line);
}

.dw-mixup-label {
  margin: 0 0 8px;
}

.dw-mixup-claims {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.dw-mixup-claim {
  min-height: var(--tap);
  padding: 6px 14px;
  border-radius: var(--r-pill);
  border: 1px solid currentColor;
  background: none;
  color: inherit;
  font: 600 var(--t-small) / 1.2 var(--sans);
  text-align: left;
  cursor: pointer;
}

.dw-mixup-claim[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}

.dw-mixup-note {
  margin: 6px 0 0;
  font-size: var(--t-small);
  color: var(--fg-muted);
}

.dw-mixup-undo {
  min-height: var(--tap);
  padding: 0 4px;
  border: 0;
  background: none;
  color: var(--accent);
  font: 700 var(--t-small) / 1 var(--sans);
  cursor: pointer;
}

/* The full player's top row, above the player: the frame leaves it the
   room (listen.css --drawer-exits), and it is placed against the frame,
   so the controls below scroll and it stays. */
.dw-exits {
  position: absolute;
  top: env(safe-area-inset-top, 0px);
  left: 14px;
  right: 14px;
  height: var(--drawer-exits);
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.dw-exit {
  flex: none;
}

/* The ⌄ of Back to the page, drawn as two strokes so it sits on the pill's
   middle: the typed ⌄ (U+2304) hangs at the baseline, below the words'
   centre (Terran, 2026-10-19: "arrow is off center"). A square's right and
   bottom edges turned 45°, lifted so the strokes, not the square, centre. */
.dw-exit-chevron {
  flex: none;
  width: 6px;
  height: 6px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}

.dw-exit-places {
  display: flex;
  gap: 14px;
  margin-left: auto;
  min-width: 0;
  overflow-x: auto;
  white-space: nowrap;
}

.dw-exit-places a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  color: var(--fg-muted);
  font: 600 var(--t-small) / 1.2 var(--sans);
  text-decoration: none;
}

.dw-exit-places a:hover {
  color: var(--fg);
}

/* ---- full: what is held, the others who sing it, up next, played ----- */
/*
 * Claude Design "Desktop" 2d (2026-10-19). Below 900px the parts follow
 * the player in reading order, inside the controls that scroll under it;
 * the wide block at the end puts up next and played in a column beside it.
 */
.dw-full-main {
  padding: 14px 4px 12px;
}

.dw-full-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

/* "a cover of {song} by {originator}", the song's title its large lead —
   inline, so the line stays one sentence — then "sung by {name} · …".
   Every link is a tap tall without growing its line. */
.dw-full-line,
.dw-full-sung {
  max-width: 100%;
  margin: 0;
  color: var(--fg-muted);
  overflow-wrap: anywhere;
}

.dw-full-line {
  font-size: 13px;
  line-height: 1.3;
}

.dw-full-sung {
  font-size: var(--t-body);
  line-height: 1.4;
}

.dw-full-line a,
.dw-full-sung a {
  display: inline-block;
  padding: 13px 0;
  margin: -13px 0;
  color: var(--fg);
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-underline-offset: 3px;
}

.dw-full-line .dw-full-song {
  padding: 8px 0;
  margin: -8px 0;
  font: 300 var(--t-hero) / 1.1 var(--serif);
  letter-spacing: -0.02em;
  text-decoration: none;
}

/* The full player's row of four (ledger 449): on a phone two to a row
   under the head; on a wide screen one row beside it (below). */
.dw-saves {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
}

/* On a wide screen the saves sit beside the head while the line keeps
   18rem, else under it, so ‹ ▶/❚❚ › and the ways onward stay one row and
   the others who sing it stay on a 1280 × 720 screen. */
@media (min-width: 900px) {
  .dw-full-top {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    column-gap: 16px;
  }

  .dw-full-top > .dw-full-head {
    flex: 1 1 18rem;
    min-width: 0;
  }

  /* the row of four in one row beside the head, its pills' sides 10px so
     the head keeps 18rem and more beside them at 1280: the row is the
     head's height, and the others who sing it stay on a 1280 × 720 screen */
  .dw-saves {
    flex: 0 1 auto;
    max-width: 100%;
    margin-top: 12px;
  }

  .dw-saves .dw-taste {
    flex-wrap: nowrap;
  }

  .dw-saves .dw-taste > .pill {
    flex: none;
    padding: 0 10px;
  }
}

/* ‹ ▶/❚❚ › on a row of their own on a phone, then the verbs two to a row. */
.dw-full-acts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
}

.dw-transport {
  display: flex;
  flex: 1 0 100%;
  gap: 8px;
}

.dw-full-acts > .pill {
  flex: 1 1 calc(50% - 4px);
  min-width: 0;
}

.dw-same {
  margin-top: 18px;
}

.dw-same-label {
  padding-bottom: 8px;
}

.dw-same-list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px 14px;
}

/* One door to that cover: its still (a cover is its still, wide.md rule 7)
   and who sings it, the name wrapping. */
.dw-same-card {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr);
  gap: 10px;
  align-items: center;
  min-height: var(--tap);
  color: var(--fg);
}

.dw-same-card:hover {
  text-decoration: none;
}

.dw-same-card:hover .dw-same-name {
  text-decoration: underline;
}

.dw-same-name {
  min-width: 0;
  font-size: 15px;
  line-height: 1.2;
  overflow-wrap: anywhere;
}

.dw-full-played {
  margin-top: 12px;
  border-top: 1px solid var(--line-strong);
}

/* The full player's rows: no number on a queued row; a played row is its
   names, Save and ↺ (the board's). */
.dw-full-side .dw-queued .dw-face {
  grid-template-columns: 56px minmax(0, 1fr) var(--tap-round);
}

.dw-full-side .dw-played {
  grid-template-columns: minmax(0, 1fr) var(--tap-round) var(--tap-round);
}

/* ---- open: played above the line, up next below ---------------------- */

.dw-lists {
  padding-bottom: 8px;
}

.dw-day-label {
  padding: 12px 0 4px;
}

.dw-row {
  display: grid;
  gap: 10px;
  align-items: center;
  min-height: var(--row-min);
}

.dw-played {
  grid-template-columns: 56px minmax(0, 1fr) var(--tap-round) var(--tap-round);
}

/* A queued row is its face (the row's grid) over what a swipe reveals,
   then, while its song or person is set aside, what that did, with Undo. */
.dw-queued {
  display: block;
}

.dw-queued .dw-face {
  display: grid;
  gap: 10px;
  align-items: center;
  min-height: var(--row-min);
  grid-template-columns: 18px 56px minmax(0, 1fr) var(--tap-round);
}

/* ---- a queued row's swipe (phones; listen/swipe.js) ------------------- */

/* The list scrolls up and down under a finger; only a sideways move is the
   row's. The face slides over what it hides, drawn on the drawer's own
   ground so nothing shows through it. --swipe-x is the face's offset: the
   open side's while the row stands aside, the finger's (set on the row by
   swipe.js) while it moves. The ease is off under reduced motion
   (listen.css). */
.dw-queued {
  --swipe-reveal: 176px;
  touch-action: pan-y;
}

.dw-queued[data-open="left"] {
  --swipe-x: calc(-1 * var(--swipe-reveal));
}

.dw-queued[data-open="right"] {
  --swipe-x: var(--swipe-reveal);
}

.dw-swipe {
  position: relative;
  overflow-x: clip;
}

.dw-face {
  position: relative;
  z-index: 1;
  background: var(--drawer-bg);
  transform: translateX(var(--swipe-x, 0px));
  transition: transform 200ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.dw-queued[data-drag] .dw-face {
  transition: none;
}

/* What a left swipe reveals sits at the row's right edge; a right swipe's,
   at its left. Out of sight and out of reach until its side shows. */
.dw-under {
  position: absolute;
  top: 0;
  bottom: 0;
  display: flex;
  width: var(--swipe-reveal);
  visibility: hidden;
}

.dw-under-left {
  right: 0;
}

.dw-under-right {
  left: 0;
}

.dw-queued[data-open="left"] .dw-under-left,
.dw-queued[data-drag="left"] .dw-under-left,
.dw-queued[data-open="right"] .dw-under-right,
.dw-queued[data-drag="right"] .dw-under-right {
  visibility: visible;
}

.dw-swipe-act {
  flex: 1 1 0;
  min-width: 0;
  min-height: var(--tap);
  padding: 4px 8px;
  border: 0;
  font: 600 var(--t-small) / 1.2 var(--sans);
  overflow-wrap: anywhere;
  color: var(--fg);
  background: var(--line-strong);
}

.dw-swipe-act + .dw-swipe-act {
  border-left: 1px solid var(--drawer-bg);
}

/* Remove, the one that changes what plays: the strongest of the two. */
.dw-swipe-remove {
  background: var(--fg);
  color: var(--drawer-bg);
}

/* The saves in the saves' colour; a saved one says so in its words. */
.dw-swipe-keep,
.dw-swipe-keep-person {
  background: var(--accent);
  color: var(--on-accent);
}

.dw-still {
  width: 56px;
  border-radius: 4px;
}

/* A still that is a press (play this one now) reaches the tap floor; a 56px
   16:9 still is 31.5px tall. */
button.dw-still {
  min-height: var(--tap);
}

.dw-names {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.dw-names > * {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A name wraps; only the line under it may be cut. */
.dw-names > .dw-name {
  white-space: normal;
  overflow-wrap: anywhere;
}

.dw-name {
  font-size: var(--t-body);
  color: var(--fg);
  /* a played row's still is decorative, so the name is its one way to the
     cover: a tap tall without moving the row — the reach covers the line
     under it, which is not pressed */
  position: relative;
  padding-block: 12px;
  margin-block: -12px;
}

.dw-names .dw-sub {
  font-size: 11px;
}

.dw-n {
  font-size: 11px;
  color: var(--fg-muted);
}

.dw-more {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  font: 600 var(--t-meta) / 1.3 var(--sans);
}

.dw-line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 12px;
  border-top: 1px solid var(--line-strong);
}

.dw-next-label {
  color: var(--accent);
  padding: 12px 0 4px;
}

/* Clear is drawn as a button, the rounds' outline at the tap floor (Terran
   2026-10-26 asked for "a button to clear the queue": the muted word beside
   Move to YouTube did not read as one). */
.dw-clear {
  min-height: var(--tap);
  padding: 0 16px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--surface);
  font: 600 var(--t-meta) / 1 var(--sans);
  color: var(--fg);
}

/* Up next's verbs: Move to YouTube ↗, then Clear. */
.dw-next-acts {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.dw-move-youtube {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  padding: 0 4px;
  font: 600 var(--t-meta) / 1 var(--sans);
  color: var(--accent);
}

.dw-empty {
  padding: 8px 0;
  font-size: var(--t-small);
  color: var(--fg-muted);
}

/* What Clear took, in the list's place, and its Undo beside it. */
.dw-cleared {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 8px;
  padding: 0;
}

.dw-undo {
  min-height: var(--tap);
  padding: 0 4px;
  font: 600 var(--t-meta) / 1 var(--sans);
  color: var(--accent);
}

/* ---- a wide screen (≥ 900px): the strip and the three panes ----------- */
/*
 * Claude Design "Desktop" (2026-10-17). Mini: one 72px strip — the name and
 * the song on the first line with the state beside the name, the song line
 * and the count under it, then ‹ ▶/❚❚ ›, ◇ Save, ⤢ and ⌃. Open: listen.css
 * puts the stage in a 420px pane and lets #drawer-ui's parts into its grid;
 * here the bar and what is held join the stage in that pane, and the lists
 * take the rest — played by day, then up next beside it. Full ("Desktop"
 * 2d, 2026-10-19): listen.css puts the player at the head of the first
 * column and a 420px column beside it; here what is held and the others
 * who sing it go under the player, at its width, and up next over played
 * fill the column, each half scrolling on its own.
 */
@media (min-width: 900px) {
  .dw[data-state="mini"] [data-k="keep-mini"],
  .dw[data-state="mini"] [data-k="full"] {
    display: inline-flex;
  }

  .dw[data-state="mini"] .dw-bar {
    gap: 8px;
  }

  .dw[data-state="mini"] .dw-text {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "title eyebrow"
      "sub count";
    column-gap: 10px;
    align-content: center;
  }

  .dw[data-state="mini"] .dw-title {
    grid-area: title;
    font-size: 16px;
  }

  .dw[data-state="mini"] .dw-eyebrow {
    grid-area: eyebrow;
    align-self: center;
  }

  .dw[data-state="mini"] .dw-sub {
    grid-area: sub;
  }

  .dw[data-state="mini"] .dw-count {
    grid-area: count;
    align-self: center;
  }

  .dw[data-state="open"] {
    display: contents;
  }

  .dw[data-state="open"] > .dw-bar {
    grid-column: 1;
    grid-row: 2;
    position: static;
    padding: 12px 24px 0;
    border-bottom: 0;
  }

  .dw[data-state="open"] > .dw-now-open {
    grid-column: 1;
    grid-row: 3;
    align-self: stretch;
    min-height: 0;
    overflow-y: auto;
    padding: 8px 24px 20px;
  }

  .dw[data-state="open"] > .said {
    grid-column: 1;
    padding: 0 24px 12px;
  }

  /* Played by day in the first column, up next in the second; up next
     spans the day rows (at most DRAWER_DAYS days and the way to Places). */
  .dw[data-state="open"] > .dw-lists {
    grid-column: 2;
    grid-row: 1 / -1;
    align-self: stretch;
    min-height: 0;
    overflow-y: auto;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-content: start;
    border-left: 1px solid var(--line);
  }

  .dw-lists > .dw-day,
  .dw-lists > .dw-more {
    grid-column: 1;
    margin: 0;
    padding: 16px 24px 0;
    border-right: 1px solid var(--line);
  }

  .dw-lists > .dw-next {
    grid-column: 2;
    grid-row: 1 / span 4;
    margin: 0;
    padding: 16px 24px;
  }

  /* Beside the played column, up next needs no line above it; with nothing
     played yet it takes both columns rather than leave one empty. */
  .dw-lists > .dw-next > .dw-line {
    margin-top: 0;
    border-top: 0;
  }

  .dw-lists:not(:has(> .dw-day)) > .dw-next {
    grid-column: 1 / -1;
  }

  .dw[data-state="full"] {
    display: contents;
  }

  .dw[data-state="full"] > .dw-exits {
    left: 24px;
    right: 24px;
  }

  /* Under the player, at its widest; listen.css sizes the player so this
     fits under it (--player-under). */
  .dw[data-state="full"] > .dw-full-main {
    grid-column: 1;
    grid-row: 2;
    align-self: stretch;
    justify-self: center;
    width: min(100%, calc(var(--player-full) + 48px));
    min-height: 0;
    overflow-y: auto;
    padding: 16px 24px 20px;
  }

  .dw-full-line .dw-full-song {
    padding: 6px 0;
    margin: -6px 0;
    font-size: 30px;
  }

  /* ‹ ▶/❚❚ › and the verbs on one row. */
  .dw-full-acts {
    margin-top: 12px;
  }

  .dw-transport,
  .dw-full-acts > .pill {
    flex: none;
  }

  .dw-same {
    margin-top: 16px;
  }

  .dw-same-list {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  /* Up next over played, in equal halves; up next alone takes the column. */
  .dw[data-state="full"] > .dw-full-side {
    grid-column: 2;
    grid-row: 1 / -1;
    align-self: stretch;
    display: flex;
    flex-direction: column;
    min-height: 0;
    border-left: 1px solid var(--line);
  }

  .dw-full-side > * {
    flex: 1 1 0;
    min-height: 0;
    overflow-y: auto;
    margin: 0;
    padding: 8px 24px 16px;
  }

  .dw-full-side > .dw-full-played {
    border-top: 1px solid var(--line);
  }

  .dw-full-side .dw-line {
    margin-top: 0;
    border-top: 0;
  }
}

/* ==== the presses (notes/sylqi/design/2026_10_24_press_motion/ROUND.md) == */
/*
 * Every motion here is a @keyframes under listen.css's four tokens
 * (--press-flip, --press-settle, --press-breath, --press-ease) and nothing
 * else. views/drawer.js writes each press as data: data-just / data-ring /
 * data-sounding / data-land on the control, and how long ago as a negative
 * delay (--t the press, --b the breath's phase, --r the ring's last change),
 * so a redraw mid-motion resumes it. Each one-shot ends on its control's
 * still state, so a node drawn after the motion, or under reduced motion
 * (listen.css's kill switch), is the control as it was before the round.
 */

/* ---- ▶ / ❚❚: the shapes, and the fold between them ------------------- */
/*
 * Two shapes, each a quadrilateral clip of a box the colour of the glyph.
 * Their size and weight are today's text glyphs', measured at 16px in the
 * page's font in Playwright's Chromium (2026-10-24): ❚❚ 12.25 × 11.125px,
 * each bar 5.625px with 1px between, ink 125px²; ▶ 10.125 × 9.875px with
 * its corners rounded off a sharp triangle of 10.75 × 10.75px, ink 60px² —
 * drawn here as that sharp triangle (58px²), centred in the same box, so
 * its weight is the glyph's. ❚❚ is two bars; ▶ is cut at its middle into a band and a tip, so the
 * left bar folds into the band and the right bar into the tip, corner to
 * corner. Every frame between is two convex quadrilaterals that do not
 * overlap: no ragged in-between frame (the cold review's defect in the
 * strip's sheared fold).
 */

.dw [data-k="pause"] {
  position: relative;
  --g-pause-1: polygon(0 0, 5.625px 0, 5.625px 11.125px, 0 11.125px);
  --g-pause-2: polygon(
    6.625px 0,
    12.25px 0,
    12.25px 11.125px,
    6.625px 11.125px
  );
  --g-play-1: polygon(
    0.75px 0.1875px,
    6.125px 2.875px,
    6.125px 8.25px,
    0.75px 10.9375px
  );
  --g-play-2: polygon(
    6.125px 2.875px,
    11.5px 5.5625px,
    11.5px 5.5625px,
    6.125px 8.25px
  );
}

.dw-glyph {
  display: none;
}

@supports (clip-path: polygon(0 0, 1px 0, 0 1px)) {
  /* the text glyph stays for the words (and a browser without clip-path) */
  .dw [data-k="pause"] .dw-gt {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .dw-glyph {
    position: relative;
    display: block;
    width: 12.25px;
    height: 11.125px;
  }
}

.dw-glyph > i {
  position: absolute;
  inset: 0;
  background: currentColor;
}

.dw-glyph[data-glyph="pause"] > .dw-g1 {
  clip-path: var(--g-pause-1);
}

.dw-glyph[data-glyph="pause"] > .dw-g2 {
  clip-path: var(--g-pause-2);
}

.dw-glyph[data-glyph="play"] > .dw-g1 {
  clip-path: var(--g-play-1);
}

.dw-glyph[data-glyph="play"] > .dw-g2 {
  clip-path: var(--g-play-2);
}

@keyframes press-fold-1-from-pause {
  from {
    clip-path: var(--g-pause-1);
  }
}

@keyframes press-fold-2-from-pause {
  from {
    clip-path: var(--g-pause-2);
  }
}

@keyframes press-fold-1-from-play {
  from {
    clip-path: var(--g-play-1);
  }
}

@keyframes press-fold-2-from-play {
  from {
    clip-path: var(--g-play-2);
  }
}

/* a press folds the shapes into the new state's, from the press's time */
[data-k="pause"][data-just="pause"] .dw-glyph > i {
  animation-duration: var(--press-flip);
  animation-timing-function: var(--press-ease);
  animation-fill-mode: both;
  animation-delay: var(--t);
}

[data-k="pause"][data-just="pause"] .dw-glyph[data-glyph="play"] > .dw-g1 {
  animation-name: press-fold-1-from-pause;
}

[data-k="pause"][data-just="pause"] .dw-glyph[data-glyph="play"] > .dw-g2 {
  animation-name: press-fold-2-from-pause;
}

[data-k="pause"][data-just="pause"] .dw-glyph[data-glyph="pause"] > .dw-g1 {
  animation-name: press-fold-1-from-play;
}

[data-k="pause"][data-just="pause"] .dw-glyph[data-glyph="pause"] > .dw-g2 {
  animation-name: press-fold-2-from-play;
}

/* the disc dips on a press and settles; nothing overshoots */
@keyframes press-dip {
  30% {
    transform: scale(0.94);
  }
}

[data-k="pause"][data-just="pause"] {
  animation: press-dip var(--press-settle) var(--press-ease) both;
  animation-delay: var(--t);
}

/* ---- ▶ / ❚❚: the breath ---------------------------------------------- */
/*
 * The ring's spread is a product: the breath (--ring-spread, 0 → 6px, phase
 * --b from the play's own start) times whether it sounds (--ring-on). A
 * pause holds the breath on its frame and takes --ring-on 1 → 0 over
 * --press-settle, so the ring settles to nothing from wherever the breath
 * was; a resume brings it back while the breath runs on. Paused, ended or
 * idle at rest, --ring-on is 0 and the ring draws nothing: today's frame.
 */

@property --ring-spread {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}

@property --ring-on {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}

.dw-ring {
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0.25;
  --ring-on: 0;
  box-shadow: 0 0 0 calc(var(--ring-spread) * var(--ring-on)) var(--accent);
}

@keyframes press-breath-spread {
  0%,
  100% {
    --ring-spread: 0px;
  }

  50% {
    --ring-spread: 6px;
  }
}

/* to the still value: 1 while it sounds, 0 while it does not */
@keyframes press-ring-on {
  from {
    --ring-on: 0;
  }
}

@keyframes press-ring-off {
  from {
    --ring-on: 1;
  }
}

[data-k="pause"][data-sounding] .dw-ring {
  --ring-on: 1;
  animation: press-breath-spread var(--press-breath) var(--press-ease) infinite;
  animation-delay: var(--b);
}

[data-k="pause"][data-sounding][data-ring="on"] .dw-ring {
  animation:
    press-breath-spread var(--press-breath) var(--press-ease) infinite,
    press-ring-on var(--press-settle) var(--press-ease) both;
  animation-delay: var(--b), var(--r);
}

[data-k="pause"]:not([data-sounding])[data-ring="off"] .dw-ring {
  animation:
    press-breath-spread var(--press-breath) var(--press-ease) infinite paused,
    press-ring-off var(--press-settle) var(--press-ease) both;
  animation-delay: var(--b), var(--r);
}

/* ---- the said line: a first Save, a Queue ---------------------------- */
/*
 * views/drawer.js draws the line while the press is news (now − at under a
 * breath) and redraws once when it is not, so the words are there under
 * reduced motion too; the keyframe only eases them in and out. In the mini
 * strip the line sits over the strip's top edge, where the eye already is.
 */

.dw[data-state="mini"] > .dw-said {
  position: absolute;
  left: 14px;
  bottom: calc(100% + 8px);
  max-width: calc(100% - 28px);
  padding: 4px 12px;
  border-radius: var(--r-pill);
  background: var(--drawer-bg);
  color: var(--fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

@keyframes press-said-ease {
  0% {
    opacity: 0;
    transform: translateY(4px);
  }

  8%,
  85% {
    opacity: 1;
    transform: none;
  }

  100% {
    opacity: 0;
    transform: scaleY(0);
  }
}

/* In the open sheet and the full player the line is a press's for a breath,
   and the sheet's own content must not move under it (the full player fits a
   laptop with no scroll): it sits over the foot of the screen, not in the
   flow. The second press's line, which stays, keeps its place in the flow. */
.dw:not([data-state="mini"]) .dw-said-once {
  position: fixed;
  left: 16px;
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  z-index: 30;
  max-width: calc(100vw - 32px);
  margin: 0;
  padding: 4px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--drawer-bg);
  color: var(--fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

.dw-said-once {
  transform-origin: bottom;
  animation: press-said-ease var(--press-breath) var(--press-ease) both;
  animation-delay: var(--t);
}

/* ---- Queue: the still's travel into the strip (kept apart) ----------- */
/*
 * A 24px still of the cover just queued leaves the row's Queue pill and
 * lands on the mini strip's ⌃, over --press-settle: across and down to a
 * still's height above the strip (a shallow lift makes it an arc), then
 * straight down, shrinking to 0.6 under the fold; the strip dips 0.98 as it
 * lands. Fixed to the viewport: views/drawer.js measured the pill and the ⌃
 * there, once per press. At rest, and under reduced motion, it draws
 * nothing (opacity 0): nothing travels, and the said line still says where
 * the cover stands.
 */

.dw-travel {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 30;
  pointer-events: none;
  opacity: 0;
}

.dw-lift {
  display: block;
}

.dw-travel-still {
  height: 24px;
  border-radius: 4px;
}

@keyframes press-travel {
  from {
    opacity: 1;
    transform: translate(var(--x0), var(--y0));
  }

  70% {
    opacity: 1;
    transform: translate(
      calc(var(--x0) + var(--dx)),
      calc(var(--y0) + var(--up))
    );
  }

  to {
    opacity: 0;
    transform: translate(
      calc(var(--x0) + var(--dx)),
      calc(var(--y0) + var(--dy))
    );
  }
}

@keyframes press-lift {
  35% {
    transform: translateY(calc(var(--lift) * -1));
  }

  70%,
  100% {
    transform: none;
  }
}

@keyframes press-under {
  70% {
    transform: none;
  }

  to {
    transform: scale(0.6);
  }
}

@keyframes press-land {
  50% {
    transform: scale(0.98);
  }
}

.dw-travel {
  animation: press-travel var(--press-settle) var(--press-ease) both;
  animation-delay: var(--t);
}

.dw-lift {
  animation: press-lift var(--press-settle) var(--press-ease) both;
  animation-delay: var(--t);
}

.dw-travel-still {
  animation: press-under var(--press-settle) var(--press-ease) both;
  animation-delay: var(--t);
}

.dw-bar[data-land] {
  animation: press-land var(--press-flip) var(--press-ease) both;
  animation-delay: calc(var(--t) + var(--press-settle));
}
