/*
 * sylqi — listen (v2). The frozen design is
 * notes/sylqi/claude_design/2026_10_08_listener_v2.dc.html; the contract is
 * notes/sylqi/listener-v2.md.
 *
 * Light where the page is the listener's (home, search, places, explore,
 * the room); dark where the artist is present (the cover, the person, the
 * drawer). Tone is meaning, not the OS's scheme: main.js sets
 * body[data-tone] per route, and no prefers-color-scheme rule exists here
 * (ruled on the canvas, turn 4: "dark/light is meaning, no OS switch").
 *
 * This file holds the tokens, the base, the layout and the drawer's frame.
 * Each view appends its own section below under a banner
 * `/* view: <name> *\/` — append only, never edit another view's section.
 */

/* ---- tokens ---------------------------------------------------------- */

:root {
  /* the palette, as drawn */
  --paper: #f2efe8;
  --paper-alt: #f7f5ee;
  --card: #ffffff;
  --ink: #17150f;
  --body-ink: #45413a;
  --muted: #6a6558;
  --faint: #a39c8a;
  --rule: #ddd8cb;
  --rule-soft: #e4dfd2;
  --rule-strong: #c9c4b7;
  --teal: #0b5f78;
  --teal-tint: #e8f0f4;
  --heard-bg: #ebe7dd;
  --night: #100f0c;
  --night-ink: #f2efe6;
  --night-muted: #a39c8c;
  --night-teal: #6cc3d9;
  --night-rule: rgba(242, 239, 230, 0.14);
  --night-line: rgba(242, 239, 230, 0.3);
  --night-card: rgba(242, 239, 230, 0.07);
  --drawer-bg: #1a1915;
  --eyebrow-on-ink: #8fd3e6;
  --youtube-red: #cc0000;
  --on-red: #ffffff;
  --still-bg: #333333;
  --sky-field: #0a0c12;
  --sky-tie: #9fb3d9;
  --sky-place: #9fb3d9;
  /* a person not yet heard: a step back from the light (2026-10-26, Terran
     on a phone: "Rebalance the color here, too hard to read"). Each star is
     composited alone, so where they crowd the field fills to this colour
     itself: #8a93a8 made the middle a pale mass the light and the names
     drowned in; #4a546e caps it where a name still reads at 6:1 and the
     cream at 6.4:1 (v2-sky-balance.test.js) */
  --sky-not-yet: #4a546e;
  --sky-heard: #f6ecc8;
  --sky-saved: #86d3ea;
  --sky-fresh: #ffb86b;
  --sky-next: #c9a77e;
  --sky-now: #ffffff;
  --sky-name: #e8e4d8;
  /* a name's halo over the sky: the field's colour close round each glyph */
  --sky-halo:
    0 0 2px var(--sky-field), 0 0 3px var(--sky-field), 0 0 6px var(--sky-field);

  /* the tone in use: light by default, remapped under [data-tone="dark"] */
  --bg: var(--paper);
  --fg: var(--ink);
  --fg-muted: var(--muted);
  --fg-faint: var(--faint);
  --line: var(--rule);
  --line-strong: var(--rule-strong);
  --accent: var(--teal);
  --on-accent: var(--paper-alt);
  --surface: var(--card);

  /* type */
  --serif: Newsreader, "Iowan Old Style", "Palatino Linotype", Georgia, serif;
  --sans:
    Karla, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial,
    sans-serif;
  --t-name-xl: 40px; /* cover and person name */
  --t-title: 34px; /* room title */
  --t-hero: 26px;
  --t-head: 22px; /* wordmark, Places title */
  --t-field: 18px; /* search field, room row name */
  --t-row: 17px; /* rooms and people rows */
  --t-body: 14px;
  --t-small: 12.5px;
  --t-meta: 12px;
  --t-label: 11px; /* section labels, uppercase */
  --t-eyebrow: 10px;

  /* shape */
  --r-pill: 999px;
  --r-card: 14px;
  --r-tile: 12px;
  --r-thumb: 10px;
  --r-still: 8px;
  --r-drawer: 22px;

  /* space and reach */
  --gutter: 18px;
  --section: 22px;
  --tap: 44px; /* the floor for anything pressed */
  --tap-round: 44px; /* round icon buttons (⋯, ×, ↺): the tap floor; the canvas draws 40 */
  --row-min: 52px;
  --page-max: 720px; /* the desktop is the phone, wider */
  --drawer-mini: 72px; /* the mini row's height, for the page's foot */
  /* the player's width in the mini row: on a phone the name needs the room
     more than the player needs the width (112px left it about 88px at 390) */
  --stage-mini: 80px;
  --stage-open: 400px;
  /* the full player's top row (⌄ Back to the page, the places): a tap high
     with 8px either side */
  --drawer-exits: calc(var(--tap) + 16px);

  /* motion: one tempo, one easing, and nothing else (the press round,
     notes/sylqi/design/2026_10_24_press_motion/ROUND.md §4). Every motion on
     the page's presses is a @keyframes under these four; listen/press.js
     PRESS holds the same numbers for the decisions a render makes. */
  --press-flip: 180ms; /* a state flip: + to ✓, ❚❚ to ▶ */
  --press-settle: 420ms; /* a one-shot: a ring, a travel, a dip */
  --press-breath: 2.4s; /* the one continuous motion, while something sounds */
  --press-ease: cubic-bezier(0.2, 0, 0, 1); /* decelerating; nothing bounces */
}

@media (min-width: 561px) {
  :root {
    --stage-mini: 160px;
    --drawer-mini: 96px;
  }
}

[data-tone="dark"] {
  --bg: var(--night);
  --fg: var(--night-ink);
  --fg-muted: var(--night-muted);
  --fg-faint: var(--night-muted);
  --line: var(--night-rule);
  --line-strong: var(--night-line);
  --accent: var(--night-teal);
  --on-accent: var(--night);
  --surface: var(--night-card);
}

/* ---- base ------------------------------------------------------------ */

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

/* Something the page hid stays hidden, whatever a display rule says about
   its class: the browser's own [hidden] is the bottom of the cascade. */
[hidden] {
  display: none !important;
}

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

body {
  margin: 0;
  min-height: 100vh;
  /* the small viewport: in iOS Safari and in-app webviews vh is the large
     one, under the browser's own bars */
  min-height: 100svh;
  background: var(--bg);
  color: var(--fg);
  font: 400 var(--t-body) / 1.5 var(--sans);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
p,
ul,
ol,
figure {
  margin: 0;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

img {
  display: block;
  max-width: 100%;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* A proper noun is a door (2026-10-20, listen/noun.js): a performer's name
   or a song's title is a link that keeps the type and colour of whatever
   holds it, and shows itself as a link on hover and focus. */
a.noun {
  color: inherit;
}

a.noun:focus-visible {
  text-decoration: underline;
}

/* A version word (piano · English) is a door to the covers whose title says
   it (listen/version.js, 2026-10-20). Unlike a name, a word does not read as
   a link, so it carries a quiet underline at every width. It stands above
   the row's own link, whose hit area reaches over the line (room.css
   .room-name); its own grows above the line's text without moving the line,
   and stops short of the line under it, which can hold a name of its own. */
a.version-word {
  position: relative;
  z-index: 1;
  padding-block: 8px 2px;
  text-decoration: underline;
  text-decoration-color: var(--line-strong);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

a.version-word:hover,
a.version-word:focus-visible {
  text-decoration-color: currentcolor;
}

/* A search result's original artist (views/search.js): the room's link
   covers the row (search.css .search-hit::after), so the name stands above
   it, as the row's named people do. */
.search-row .meta a.noun {
  position: relative;
  z-index: 1;
}

button,
input {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
  background: none;
  border: 0;
  padding: 0;
}

button:disabled {
  cursor: default;
  opacity: 0.45;
}

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

/* Off under reduced motion: every press motion with it (the press round,
   ROUND §4). What is left is each control's still state, the page as it was
   before the round; the said line's words are drawn by the model, not by a
   keyframe, so they stay. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
}

/* Read by a screen reader, not drawn. */
.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ---- shared parts every view uses ------------------------------------ */

/* Names and titles are Newsreader; everything else is Karla. */
.serif {
  font-family: var(--serif);
  font-weight: 400;
  letter-spacing: -0.01em;
}

/* The section label recipe: 11px / 700 / .1em / uppercase / muted. */
.label {
  font: 700 var(--t-label) / 1.3 var(--sans);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--fg-muted);
}

.eyebrow {
  font: 700 var(--t-eyebrow) / 1.3 var(--sans);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

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

/* A section heading row: the label, and a link or count to its right. */
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: var(--section) var(--gutter) 10px;
}

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

/* A pill button or link, at least a tap tall. */
.pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: var(--tap);
  padding: 0 16px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--fg);
  font: 700 13px / 1.2 var(--sans);
  text-decoration: none;
}

.pill:hover {
  text-decoration: none;
}

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

/* A row's toggle prints its verb beside the glyph (+ Queue, ◇ Keep): a bare
   + or ◇ read as either (Terran, 2026-10-08). Snug, on one line. */
.pill.verb {
  padding: 0 12px;
  white-space: nowrap;
}

/* One width across both states (Terran, 2026-10-24: "queue shouldn't
   trigger a reflow, button should be same size if we can"). The pill
   carries the other state's words as data-other (listen/press.js
   otherWords); they sit in the same grid cell as the words shown, unseen and
   out of the accessibility tree (the empty alt text), so the pill is as wide
   as the wider of the two and a press never reflows the row. */
.pill.verb[data-other] {
  display: inline-grid;
  place-items: center;
}

.pill.verb[data-other] > .pv,
.pill.verb[data-other]::after {
  grid-area: 1 / 1;
}

.pill.verb[data-other]::after {
  /* a browser without alt text for content keeps the first; hidden either way */
  content: attr(data-other);
  content: attr(data-other) / "";
  visibility: hidden;
}

/* Queue: the + turns into the ✓ (the press round, Q1's flip): the glyph
   turns in from a quarter-turn, thin, over --press-flip, from the queued
   entry's own time (--t, listen/press.js queuePress), so a redraw resumes
   it. The word and the fill change as they always did. */
.pill .press-glyph {
  display: inline-block;
}

@keyframes press-turn-in {
  from {
    opacity: 0;
    transform: rotate(-90deg) scaleX(0.5);
  }
}

.pill.verb[data-just="queue"] .press-glyph {
  animation: press-turn-in var(--press-flip) var(--press-ease) both;
  animation-delay: var(--t);
}

/* A round icon button (⋯, ×, ↺): a tap wide, labelled by aria-label. */
.round {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: var(--tap-round);
  height: var(--tap-round);
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--fg);
  font-size: 16px;
  line-height: 1;
}

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

/* A still from YouTube, never re-hosted: a background on a button or link. */
.still {
  display: block;
  background: var(--still-bg) center / cover no-repeat;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-still);
}

/* The top line on every route: the mark (home), the three places, and
   where you are — a true location or nothing (cold review 2026-10-09
   "fix the breadcrumb"; main.js renderTop). Terran, 2026-10-08: "It's not
   clear how to go back or get to menus". A link, never a button: the
   browser's own back still works. Every link a full tap; a name wraps
   rather than truncating. The same column as the page and the drawer: on a
   wide screen a full-width line put the places in the far corner, away
   from everything else (Terran, 2026-10-14: "on web i can't see explore"). */
.top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 14px;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 4px var(--gutter) 0;
  font-size: 12px;
  color: var(--fg-muted);
}
.top-mark {
  font-family: var(--serif);
  font-size: 18px;
  color: var(--fg);
  margin-right: auto;
}
/* Home and the first visit print their own wordmark, so the line draws no
   mark there below 900px (main.js showsMark); the places keep the place
   they have on every other page. A part marked .top-wide (the mark there,
   home's search) is the wide screen's (the block below). */
.top .top-wide {
  display: none;
}
.top > .top-place:first-child,
.top > .top-mark.top-wide + .top-place {
  margin-left: auto;
}
.top a {
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
  font-weight: 600;
}
.top-place[aria-current="page"] {
  color: var(--fg);
  text-decoration: underline;
  text-underline-offset: 4px;
}
/* The search field, on every view that has none of its own (main.js
   OWN_SEARCH_VIEWS): its own row under the links. Terran, 2026-10-08: "Did
   we lose our search bar?" */
.top .top-search {
  flex-basis: 100%;
  margin: 2px 0 0;
}
.top-where {
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 6px;
  margin: 0;
  overflow-wrap: anywhere;
}
.top-where a,
.top-where [aria-current="page"] {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
}
.top-where [aria-current="page"] {
  color: var(--fg);
}
.top-where .top-sep {
  color: var(--fg-faint);
}

/* A wide screen (≥ 900px): the top line is one row — the mark, the places,
   then the search as a 260px pill beside them — and where you are under it
   (Claude Design, "Badge One Cover" turn 2 and "Desktop" turn 1,
   2026-10-17: "Top bar 78px: wordmark 20px Newsreader; Explore · Places ·
   People 13px Karla 600; search 260×38"; the pill keeps the 44px tap
   floor). Every page the Desktop canvas draws runs to 1200px with its top
   line, 40px in from each side (each view's sheet sets its own 40px), so
   both keep one column (Explore since Desktop 2b, the universe since
   Desktop 2c, 2026-10-19). */
@media (min-width: 900px) {
  .top {
    gap: 0 28px;
    padding-top: 17px;
    font-size: 13px;
  }
  .top-mark {
    font-size: 20px;
  }
  .top .top-mark.top-wide {
    display: inline-flex;
  }
  .top .top-search.top-wide {
    display: flex;
  }
  .top > .top-mark.top-wide + .top-place {
    margin-left: 0;
  }
  .top .top-search {
    flex: 0 1 260px;
    min-height: var(--tap);
    margin: 0;
  }
  .top .top-search .field-matches {
    left: auto;
    width: max(100%, 360px);
  }
  /* `body #view`: the base `#view` rule (layout, below) sets the phone's
     column later in this file, so the wide column outranks it here */
  body #view,
  .top {
    max-width: 1200px;
  }
  .top {
    padding-inline: 40px;
  }
}

/* A still that is a BUTTON is a play, and it says so. The ▶ is drawn here,
   by the stylesheet, so no view can forget it — on 2026-10-08 the person
   page, home, places and the pocket all shipped bare stills beside a → (to
   the room) and a + (queue), and Terran could not tell what to press:
   "i don't think its clear what i can click, what i should click, to play".
   A still that is a link or a span (the drawer's lists, search results)
   draws nothing: it is not a play. */
button.still,
button.pocket-still,
button.cover-card-still {
  position: relative;
}

button.still::after,
button.pocket-still::after,
button.cover-card-still::after {
  content: "▶";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--paper-alt);
  opacity: 0.9;
  color: var(--ink);
  font-size: 11px;
  pointer-events: none;
}

button.still.is-playing::after,
button.pocket-still.is-playing::after,
button.cover-card-still.is-playing::after {
  content: "♪";
}

/* A horizontal rail: scrolls inside itself, never the page. */
.rail {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  /* the snap lands a card on the gutter, not flush with the screen edge */
  scroll-padding-inline: var(--gutter);
  /* room inside the scroller for a focus outline (2px at 2px): overflow-x
     clips the y axis too, and a rail's first stills sit at its top */
  padding: 4px var(--gutter);
  scrollbar-width: none;
}

.rail::-webkit-scrollbar {
  display: none;
}

.rail > * {
  flex: none;
  scroll-snap-align: start;
}

/* A line the page says back after an act (Sent., Copied, Kept in …). */
.said {
  font-size: var(--t-meta);
  color: var(--fg-muted);
}

.said.err {
  color: var(--youtube-red);
}

/*
 * What a listener set aside, said plainly with its Undo (ledger 449): the
 * cover page, the player, a queued row, a song's or a person's own page.
 * The tone is the page's own; the Undo is a tap target in the accent.
 */
.aside-said {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 6px;
  font-size: var(--t-meta);
  color: var(--fg-muted);
}

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

/* ---- layout ---------------------------------------------------------- */

/*
 * #view is the only thing a route replaces. The drawer sits outside it, so
 * the page's foot is padded by the drawer's mini height (and the safe area)
 * to keep the last row clear of it.
 */
#view {
  display: block;
  width: 100%;
  max-width: var(--page-max);
  margin: 0 auto;
  min-height: 100vh;
  min-height: 100svh;
  padding-bottom: calc(
    var(--drawer-mini) + 24px + env(safe-area-inset-bottom, 0px)
  );
  overflow-wrap: anywhere;
}

#view:focus {
  outline: none;
}

/* A view not built yet, or no export: say so in the page's own type. */
.placeholder {
  padding: 48px var(--gutter);
}

.placeholder h1 {
  font: 400 var(--t-head) / 1.25 var(--serif);
}

.placeholder p {
  margin-top: 8px;
  color: var(--fg-muted);
}

/* ---- the drawer's frame ---------------------------------------------- */

/*
 * One element, mounted once by main.js, never inside #view. It holds the
 * page's only player (#player-host, which the IFrame API replaces with its
 * iframe) and the drawer's controls (#drawer-ui, drawn by views/drawer.js).
 * player.js writes data-state (mini | open | full) and data-has-player
 * (after the first press). The player is resized by state, never moved or
 * rebuilt: moving an iframe reloads it.
 *
 * mini — one row: the player (or nothing before a press) and the controls.
 * open — the player wider on top, the controls (played, up next) below.
 * full — the drawer covers the page: the player full width, then controls.
 */
.drawer {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto;
  align-items: center;
  gap: 12px;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 12px 14px calc(12px + env(safe-area-inset-bottom, 0px));
  background: var(--drawer-bg);
  color: var(--night-ink);
  border-top: 1px solid var(--night-rule);
  border-radius: var(--r-drawer) var(--r-drawer) 0 0;
  box-shadow: 0 -20px 40px -20px rgba(0, 0, 0, 0.6);
  /* the drawer is the artist's: dark tokens whatever the page's tone */
  --bg: var(--drawer-bg);
  --fg: var(--night-ink);
  --fg-muted: var(--night-muted);
  --fg-faint: var(--night-muted);
  --line: var(--night-rule);
  --line-strong: var(--night-line);
  --accent: var(--night-teal);
  --on-accent: var(--night);
  --surface: transparent;
}

.drawer-stage {
  position: relative;
  width: var(--stage-mini);
  aspect-ratio: 16 / 9;
  border-radius: 6px;
  overflow: hidden;
  background: #000;
}

/* Before the first press there is no player to show, and nothing was
   asked of YouTube: the stage takes no room. */
.drawer:not([data-has-player]) .drawer-stage {
  display: none;
}

.drawer:not([data-has-player]) {
  grid-template-columns: minmax(0, 1fr);
}

.drawer-stage > #player-host,
.drawer-stage > iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/*
 * The stage's note (views/stage.js), over the player: while a pressed video
 * has not sounded, its still and a pulse fill the black; when a tap is
 * asked for or YouTube refused it, one line along the foot, the player in
 * view. It never takes a tap: a tap on the stage is YouTube's.
 */
.stage-note {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  display: flex;
  align-items: flex-end;
}

.stage-note:not([data-tone]) {
  display: none;
}

.stage-still {
  position: absolute;
  inset: 0;
  background: #000 center / cover no-repeat;
  opacity: 0.55;
}

.stage-line {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 12px;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.75));
  color: var(--night-ink);
  font-size: 0.875rem;
  line-height: 1.3;
}

.stage-note[data-tone="wait"],
.stage-note[data-tone="slow"] {
  align-items: center;
  justify-content: center;
}

.stage-note[data-tone="wait"] .stage-line,
.stage-note[data-tone="slow"] .stage-line {
  width: auto;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.6);
}

.stage-pulse {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--night-teal);
  animation: stage-pulse 1.2s ease-in-out infinite;
}

.stage-note[data-tone="slow"] .stage-pulse {
  animation-duration: 2.4s;
}

.stage-note[data-tone="trouble"] .stage-line {
  background: rgba(0, 0, 0, 0.85);
}

@keyframes stage-pulse {
  0%,
  100% {
    opacity: 0.3;
    transform: scale(0.8);
  }
  50% {
    opacity: 1;
    transform: scale(1.2);
  }
}

/* The mini stage is a thumbnail: the pulse and the still say it there, the
   eyebrow beside it carries any words, and the note's words are read, not
   drawn. */
.drawer[data-state="mini"] .stage-line {
  padding: 4px;
  background: rgba(0, 0, 0, 0.6);
}

.drawer[data-state="mini"] .stage-words {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.drawer[data-state="mini"] .stage-note[data-tone="tap"] .stage-line,
.drawer[data-state="mini"] .stage-note[data-tone="trouble"] .stage-line {
  background: none;
}

.drawer-ui {
  min-width: 0;
}

.drawer[data-state="open"],
.drawer[data-state="full"] {
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}

.drawer[data-state="open"] {
  max-height: min(560px, 85vh);
  /* the dynamic viewport, so the sheet's bar never sits under the
     browser's own chrome */
  max-height: min(560px, 85dvh);
  grid-template-rows: auto minmax(0, 1fr);
}

.drawer[data-state="open"] .drawer-stage {
  width: min(var(--stage-open), 100%);
  justify-self: center;
}

.drawer[data-state="full"] {
  top: 0;
  border-radius: 0;
  grid-template-rows: auto minmax(0, 1fr);
  /* room above the player for the controls' top row (views/drawer.css
     .dw-exits): the way back to the page and the places */
  padding-top: calc(var(--drawer-exits) + env(safe-area-inset-top, 0px));
}

.drawer[data-state="full"] .drawer-stage {
  width: 100%;
  border-radius: var(--r-drawer) var(--r-drawer) 0 0;
}

.drawer[data-state="open"] .drawer-ui,
.drawer[data-state="full"] .drawer-ui {
  overflow-y: auto;
  overscroll-behavior: contain;
  max-height: 100%;
}

/* The page under a full drawer does not scroll behind it. */
body:has(.drawer[data-state="full"]) {
  overflow: hidden;
}

/*
 * A wide screen (≥ 900px; Claude Design "Desktop", 2026-10-17: "Player strip
 * 72px, dark on every tone"; "Open (here): three panes — what's held with
 * its verbs, played by day …, up next"; "the open state never covers the
 * page's head"). The drawer runs the window's width. Mini is a 72px strip
 * with a 96px stage. Open is three panes along the foot: the stage, the bar
 * and what is held in a 420px pane, then the lists (views/drawer.css lays
 * out the controls; #drawer-ui steps out of the way so its parts sit in this
 * grid). Full ("Desktop" 2d, 2026-10-19: "video left, the three panes
 * right · fits a laptop without a scroll") puts the player at the head of
 * the first column and a 420px column beside it for up next and played;
 * #drawer-ui steps aside again, and its parts take the rest. The player is
 * 16:9, at most 782px wide, and never taller than leaves what goes under it
 * on screen: 782 × 440 on a 1280 × 760 window, less on a shorter one (at
 * the window's width the full player had covered its own Save).
 */
@media (min-width: 900px) {
  :root {
    --stage-mini: 96px;
    --drawer-mini: 72px;
    /* the full player's widest, and the height kept under it: the song,
       its verbs and the others who sing it (views/drawer.css .dw-full-main,
       248px at 1280 with the title on one line) and the frame's 1px rule */
    --player-full: 782px;
    --player-under: 249px;
  }
  .drawer {
    max-width: none;
    padding: 9px 24px;
    border-radius: 0;
  }
  .drawer[data-state="open"] {
    grid-template-columns: 420px minmax(0, 1fr);
    grid-template-rows: auto auto minmax(0, 1fr);
    gap: 0;
    height: min(500px, 80dvh);
    padding: 0;
  }
  .drawer[data-state="open"] .drawer-stage {
    grid-column: 1;
    grid-row: 1;
    justify-self: stretch;
    width: auto;
    margin: 20px 24px 0;
    border-radius: 8px;
  }
  .drawer[data-state="open"] .drawer-ui {
    display: contents;
  }
  .drawer[data-state="full"] {
    grid-template-columns: minmax(0, 1fr) 420px;
    grid-template-rows: auto minmax(0, 1fr);
    gap: 0;
    padding: calc(var(--drawer-exits) + env(safe-area-inset-top, 0px)) 0 0;
  }
  .drawer[data-state="full"] .drawer-stage {
    grid-column: 1;
    grid-row: 1;
    justify-self: center;
    width: min(
      100% - 48px,
      var(--player-full),
      (100dvh - var(--drawer-exits) - var(--player-under)) * 16 / 9
    );
    border-radius: 12px;
  }
  .drawer[data-state="full"] .drawer-ui {
    display: contents;
  }
}

/* ---- views append below this line ------------------------------------ */
