/* ============================================================================
   Playlist card — the stepped stack.

   Built to docs/playlist-card-mockup.html (signed off 2026-10-03) and spec §7.
   Every offset here is one of that mockup's numbers; do not tune them without
   going back to it.

   Why a real stylesheet and not a <style> block: `card.html` carries its CSS
   inline, so its 227 lines are emitted once PER CARD — ~2,700 duplicated lines
   on a twelve-card grid. That is a known, accepted wart on `card.html`; it is
   not a pattern to copy. Nothing in here is emitted more than once.

   Loaded from base.html after theme.css, so an unprefixed rule here beats an
   unprefixed rule there. theme.css does use `!important` on `.card`, which is
   why the two rules that have to overrule it say so.
   ========================================================================= */

/* ---------------------------------------------------------------- the stack */

/* The step bleeds 15px right and 15px down — one direction only, which is the
   whole reason this treatment was chosen over the rotated one.

   Was 21px/21px (the signed-off mockup's number) until Alex asked to see a
   tighter step on 2026-10-08 and kept this one. Every number in this file
   that depends on the step size moved with it: the sleeve's own `bottom`
   offset, `.playlist-stack`'s `padding-bottom` (both below), the hover
   transforms, and the reduced-motion pin further down.

   Horizontally the bleed is paid for by the grid's own existing gutter, not
   by extra margin: the column keeps its 14.5rem flex basis with NO added
   right margin, so a row with a playlist in it wraps at exactly the same
   card count as a row of plain tracks — confirmed 2026-10-08 after a margin
   here was found to be quietly stealing a card per row (3 instead of 4) on
   the exact row a playlist landed in. Every template that renders a playlist
   card sits inside `.catalog-page-shell`, where `filter_panel.css` widens
   `--catalog-grid-gap` from the 1.5rem (24px) default to ~1.875rem (30px,
   exact value varies slightly with viewport) by giving back a third of the
   edge padding it reclaims for its own, unrelated reason — see that file's
   comment for the zero-sum derivation. 30px clears the 15px resting bleed
   with room to spare; the 20px hover peek fits inside it too, painted (not
   laid out — `pointer-events: none`, so it changes nothing about row math)
   with no overlap at the reference 1470px width this was tuned against. A
   page that ever rendered a playlist card outside `.catalog-page-shell`
   would fall back to the bare 24px gap, which now clears both the 15px
   resting bleed and the 20px hover peek comfortably too — the smaller step
   made the old fallback-overrun case (hover ran 4px past a bare 24px gap
   under the original 21px step) moot.

   Vertically it is neither margin nor a height subtraction, and both of those
   were tried (illustrated below with the 21px step that was current when
   this was written; the mechanism is unchanged, only the number is):

   * `margin-bottom: 21px` makes the flex line 21px taller — a line's cross
     size is the largest *margin box* — so every track card beside a playlist
     stretched 21px past the height track cards have anywhere else.
   * `height: calc(100% - 21px)` is right only while something else in the row
     is taller. When the playlist is the tallest item (any row of only
     playlists, and every row on `myuploads`, where the Edit/Delete foot makes
     the playlist the tallest thing there), the percentage resolves against a
     height the playlist's own content set, so the card is sized 21px smaller
     than its content and `overflow-hidden` ate the foot.

   Padding is inside the column's own hypothetical cross size, so the line
   grows to fit it instead of the card shrinking below its content, and both
   cases come out right. It is a flat 15px at every depth, not the depth's
   actual bleed, because the locked requirement is that no playlist card is
   ever a different height from its neighbour — a 1-track card and a 12-track
   card have to agree. */

.playlist-stack {
  position: relative;
  width: 100%;
  height: 100%;
  padding-bottom: 15px;
}

/* Sleeves are solid shapes in card surface colour, not copies of the cover:
   cheap, and it reads as a stack of sleeves rather than a broken image.

   `bottom: 15px`, not `inset: 0`. The two look equivalent but are not: a
   percentage/auto height on an absolutely positioned box resolves against
   the *padding* box of `.playlist-stack`, which is 15px taller than
   `.playlist-top` on purpose (that's the grid's reserved bleed room, above).
   `inset: 0` would make every sleeve 15px TALLER than the top card, and
   translateY would then add the step on top of that instead of just
   shifting it — at the original 21px step this is exactly how the bug read:
   a 7px sleeve bled 28px below the card, 21px peeked 42px. Pinning `bottom`
   to the same value as the padding-bottom above makes the sleeve's own box
   exactly the top card's size, so the translate step is the whole story
   again: a clean peek that matches on bottom and right. Fixed 2026-10-08;
   step size changed the same day, see the stack comment above. */
.playlist-layer {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 15px;
  border-radius: var(--r-lg, 14px);
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: 2px 3px 9px rgba(0, 0, 0, 0.6);
  transition: transform 0.28s cubic-bezier(0.2, 0.8, 0.2, 1);
  pointer-events: none;
}

.playlist-layer.l3 { z-index: 1; background: #1a1a1a; transform: translate(15px, 15px); }
.playlist-layer.l2 { z-index: 2; background: #1f1f1f; transform: translate(10px, 10px); }
.playlist-layer.l1 { z-index: 3; background: #242424; transform: translate(5px, 5px); }

.playlist-top {
  position: relative;
  z-index: 4;
  height: 100%;
  /* theme.css forces `box-shadow: none !important` on every .card, and the
     stack needs its top sleeve to sit above the others. */
  box-shadow: 2px 4px 14px rgba(0, 0, 0, 0.7) !important;
  transition: transform 0.28s cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* All three sleeves always show, on every playlist, regardless of how many
   tracks it holds — the stack is a symbolic "this is a group of tracks"
   marker, not a literal count. The exact number lives on the pill instead
   (§7c). Decided 2026-10-08, replacing the earlier count-bucketed thickness:
   simpler, and a 1-track playlist reads as a playlist just as clearly as an
   11-track one. */

/* `:focus-within` mirrors `:hover` so tabbing into the card spreads the stack
   the same way the mouse does. */
.playlist-stack:hover .l3,
.playlist-stack:focus-within .l3 { transform: translate(20px, 20px); }
.playlist-stack:hover .l2,
.playlist-stack:focus-within .l2 { transform: translate(14px, 14px); }
.playlist-stack:hover .l1,
.playlist-stack:focus-within .l1 { transform: translate(7px, 7px); }
.playlist-stack:hover .playlist-top,
.playlist-stack:focus-within .playlist-top { transform: translate(-2px, -2px); }

/* ------------------------------------------------------------- the artwork */

.playlist-artwork {
  position: relative;
  display: block;
  color: inherit;
  text-decoration: none;
}

.playlist-artwork .card-img-top {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

/* §7c: top-left and bottom-right belong to card.html's type and genre badges.
   A playlist spans both, so it draws neither and takes the one free corner.
   Gold because it is the only thing on the artwork, and the count is the fact
   that distinguishes a playlist from a track. */
.playlist-count-pill {
  position: absolute;
  bottom: 8px;
  left: 8px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  z-index: 2;
  background: rgba(0, 0, 0, 0.78);
  color: var(--gold, #d4a843);
  border: 1px solid rgba(212, 168, 67, 0.45);
}

/* Top-right stays card.html's corner, drawn to match. */
.playlist-private-badge {
  position: absolute;
  top: 8px;
  right: 8px;
  background: rgba(0, 0, 0, 0.7);
  padding: 3px 6px;
  border-radius: 4px;
  font-size: 11px;
  z-index: 2;
}

/* Borrowed from `card.html` so the title matches a track card's. It has to be
   restated here because card.html declares it inside its per-card <style>
   block, which a `myuploads` grid holding playlists and no tracks never emits
   — exactly the page a producer sees before their first upload. (The two-line
   clamp itself comes from master.css, which always loads.) */
.playlist-top .card-title-mini {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--text, #f0f0f0);
}

/* The label slot on a playlist card takes the place of the old "Owner · year"
   line, so it keeps that line's box (12px type at the body's 1.65
   line-height = 19.8px) rather than card.html's 28.15px: the playlist card
   then stays exactly as tall as before and no track card in its row grows.
   The pill and name are unchanged. Two classes to outrank card_label.css. */
.playlist-top .card-label-slot { height: calc(12px * 1.65); padding: 0 4px; }

/* ------------------------------------- the bundle chip on a track's artwork */

/* Wraps the cover link so the chip can sit over the artwork without being
   nested inside the link. Positioning only: it adds no box of its own. */
.track-cover-wrap { position: relative; }

/* Bottom-left of the artwork, the corner a track card leaves free. */
.track-bundle-chip {
  position: absolute;
  bottom: 8px;
  left: 8px;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: calc(100% - 16px - 6.75rem); /* clears the widest genre badge ("Progressive House", ~104px) */
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  background: rgba(0, 0, 0, 0.78);
  color: var(--gold, #d4a843);
  border: 1px solid rgba(212, 168, 67, 0.45);
  text-decoration: none;
}

.track-bundle-chip span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.track-bundle-chip:hover,
.track-bundle-chip:focus-visible {
  background: var(--gold, #d4a843);
  color: var(--night, #0c0c0c);
}

/* ---------------------------------------------------- the buy control row */

/* One occupant, always, so the row's height never depends on which state the
   card is in. */
.playlist-action-row .btn-xs { white-space: nowrap; flex-shrink: 0; }

.playlist-action-row {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  min-height: 26px;
  margin-bottom: 8px;
}

/* Deliberately a separate class from card.html's `.card-collect-btn`, which is
   defined inside that template's per-card <style>: a playlist card can be the
   only card on the page (a myuploads grid with no tracks), and then that block
   is never emitted at all. Same shape, standing on its own. */
.playlist-collect-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  flex-shrink: 0;
  padding: 2px 9px;
  min-height: 26px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.5;
  border-radius: 4px;
  border: 1px solid var(--gold, #d4a843);
  background: var(--gold, #d4a843);
  color: var(--night, #0c0c0c);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  box-shadow: 0 0 0 3px rgba(212, 168, 67, 0.14);
  transition: background 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
}

.playlist-collect-btn:hover,
.playlist-collect-btn:focus-visible {
  background: var(--gold-light, #f0c86a);
  color: var(--night, #0c0c0c);
  box-shadow: 0 0 0 4px rgba(212, 168, 67, 0.22);
}

.playlist-collect-btn:focus-visible {
  outline: 2px solid var(--gold-light, #f0c86a);
  outline-offset: 2px;
}

/* Short of dBs: same shape, drained of fill, dashed to say "not yet". Links to
   the shop rather than a confirm step `collect_playlist` would refuse. */
.playlist-collect-btn.is-short {
  background: rgba(212, 168, 67, 0.08);
  border-style: dashed;
  color: var(--gold, #d4a843);
  font-weight: 600;
  box-shadow: none;
}

.playlist-collect-btn.is-short:hover,
.playlist-collect-btn.is-short:focus-visible {
  background: rgba(212, 168, 67, 0.16);
  color: var(--gold-light, #f0c86a);
  box-shadow: none;
}

.playlist-collect-owned,
.playlist-no-bundle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  min-height: 26px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.5;
  border-radius: 4px;
  border: 1px solid var(--border-strong, rgba(255, 255, 255, 0.12));
  background: transparent;
  color: var(--muted, #888888);
  white-space: nowrap;
}

.playlist-no-bundle {
  border-style: dashed;
  font-weight: 400;
}

.playlist-collect-form {
  width: 100%;
}

/* ------------------------------------ slot 2 of 3: the expand row */

/* §7d. The toggle (30px), or nothing on a one-track playlist: the floor keeps
   the slot 30px either way so the card height never changes. */
.playlist-slot {
  min-height: 30px;
  display: flex;
  align-items: stretch;
  margin-bottom: 8px;
}

.playlist-slot > * {
  width: 100%;
}

.playlist-expand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  min-height: 30px;
  padding: 4px 8px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  border-radius: 4px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(255, 255, 255, 0.03);
  color: #cfcfcf;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}

.playlist-expand:hover,
.playlist-expand:focus-visible {
  background: rgba(212, 168, 67, 0.1);
  color: var(--gold-light, #f0c86a);
  border-color: rgba(212, 168, 67, 0.35);
}

.playlist-chevron {
  display: inline-flex;
  transition: transform 0.2s ease;
}

.playlist-expand[aria-expanded="true"] .playlist-chevron {
  transform: rotate(180deg);
}

/* ----------------------------------- slot 3 of 3: the ownership meter */

/* Always in the layout, empty or not, so a card you own two tracks of is
   exactly as tall as one you own none of. §2 locks "no pro-rata discount", so
   a partly-owned bundle charges the full price — the meter is what keeps that
   from reading as an overcharge. */
.playlist-own-slot {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 13px;
  /* The line-height has to match the reserved height exactly. At `normal`,
     9px text makes a 14.8px line box, so a filled meter came out 2px taller
     than an empty one — and a 2px difference in one card stretches its whole
     grid row. */
  line-height: 13px;
  margin: 0 0 7px;
  padding: 0 2px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--gold, #d4a843);
  font-variant-numeric: tabular-nums;
}

.playlist-own-bar {
  position: relative;
  flex: 1 1 auto;
  height: 3px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.12);
  overflow: hidden;
}

.playlist-own-bar i {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--gold, #d4a843);
  border-radius: 999px;
}

/* Two lines, reserved whether the sentence is there or not. The clamp is not
   cosmetic: a three-digit count ("119 of 200") would otherwise wrap to a third
   line and put the card back out of step with its row. */
.playlist-partial-note {
  min-height: 27px;
  font-size: 10px;
  line-height: 1.35;
  color: var(--gold, #d4a843);
  margin: 0 0 6px;
  text-align: center;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

/* ------------------------------------------------- the owner control foot */

/* Only rendered on `myuploads`, where every playlist card has one, so it can
   never make two cards in the same grid different heights. */
.playlist-card-foot {
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  padding-top: 8px;
  display: flex;
  gap: 6px;
  min-height: 36px;
  align-items: center;
}

.playlist-card-foot > * {
  flex: 1 1 0;
}

/* ------------------------------------------------------------- the drawer */

/* A full-width sibling row inside the same flex grid: `flex-basis: 100%`
   forces the line break, so opening it pushes the following cards down instead
   of reflowing or overlapping anything. It is the one layout approach that
   cannot break at another breakpoint. */
.playlist-drawer {
  flex: 0 0 100%;
  width: 100%;
  background: var(--elevated, #161616);
  border: 1px solid var(--border-strong, rgba(255, 255, 255, 0.12));
  border-top: 2px solid var(--gold, #d4a843);
  border-radius: var(--r-lg, 14px);
  padding: 14px 16px 16px;
  text-align: left;
}

.playlist-drawer-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}

.playlist-drawer-title {
  font-size: 15px;
  font-weight: 700;
  margin: 0;
}

.playlist-drawer-meta {
  font-size: 12px;
  color: var(--muted, #888888);
}

.playlist-drawer-scroll {
  display: flex;
  gap: var(--catalog-grid-gap, 1.5rem);
  overflow-x: auto;
  padding-bottom: 8px;
  align-items: flex-start;
}

.playlist-drawer-item {
  flex: 0 0 var(--catalog-card-width, 14.5rem);
  max-width: var(--catalog-card-width, 14.5rem);
  animation: playlist-drawer-in 0.26s ease both;
  animation-delay: calc(var(--i, 0) * 40ms);
}

/* The member cards come straight from card.html, which sizes itself off its
   own column class; inside the drawer the item is the column. */
.playlist-drawer-item .catalog-card-col {
  width: 100%;
  max-width: 100%;
  flex: 1 1 auto;
}

.playlist-drawer-remove {
  margin-top: 6px;
}

.playlist-drawer-withheld {
  font-size: 12px;
  color: var(--gold, #d4a843);
  margin: 0 0 10px;
}

.playlist-drawer-empty {
  font-size: 12px;
  color: var(--muted, #888888);
  margin: 0;
}

@keyframes playlist-drawer-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------ reduced motion */

/* A stack that leaps on hover is exactly the motion this is for. Every sleeve
   is pinned at its resting transform and the stagger is dropped; the collapse
   itself still works, because that is navigation, not decoration. */
@media (prefers-reduced-motion: reduce) {
  .playlist-layer,
  .playlist-top,
  .playlist-expand,
  .playlist-chevron,
  .playlist-collect-btn {
    transition: none;
  }

  .playlist-drawer-item {
    animation: none;
  }

  .playlist-stack:hover .l3,
  .playlist-stack:focus-within .l3 { transform: translate(15px, 15px); }
  .playlist-stack:hover .l2,
  .playlist-stack:focus-within .l2 { transform: translate(10px, 10px); }
  .playlist-stack:hover .l1,
  .playlist-stack:focus-within .l1 { transform: translate(5px, 5px); }
  .playlist-stack:hover .playlist-top,
  .playlist-stack:focus-within .playlist-top { transform: none; }
}

/* ------------------------------------------------------------- narrow screens */

/* Below the card width the column goes full-bleed, and even a 15px step on a
   320px screen is just a squashed card. Desktop keeps the stack; phones are
   served by /m/ anyway, which has no playlist surface yet (A6). */
@media (max-width: 575.98px) {
  /* No sleeves, so nothing to reserve for. */
  .playlist-stack { padding-bottom: 0; }

  .playlist-layer { display: none !important; }
}

/* ============================================================================
   Playlist detail page — templates/sounds/playlist_detail.html

   Built to docs/playlist-card-mockup.html §05: aside (cover stack, Bundle
   panel, facts) beside the title and tracklist. `.track-detail-shell` /
   `.track-detail-hero` / `.track-detail-related-frame` still come from
   master.css. Everything here is prefixed `pd-` or `playlist-detail-`.
   ========================================================================= */

.pd-layout {
  display: flex;
  flex-wrap: wrap;
  gap: 40px;
  align-items: flex-start;
}

.pd-aside { flex: 0 1 330px; min-width: 260px; }
.pd-main { flex: 999 1 540px; min-width: 0; }

/* The cover is the stack's top card; the sleeves and the bottom reservation
   come from `.playlist-stack` / `.playlist-layer` above, so card and page agree. */
.pd-cover-stack { margin-bottom: 14px; }

.pd-cover { overflow: hidden; border-radius: var(--r-lg, 14px); border: 1px solid var(--border, rgba(255, 255, 255, 0.09)); background: var(--card, #1c1c1c); }

.pd-cover .track-detail-cover {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 0;
}

.pd-panel {
  background: var(--card, #1c1c1c);
  border: 1px solid var(--border, rgba(255, 255, 255, 0.09));
  border-radius: var(--r-lg, 14px);
  padding: 18px;
}

.pd-panel + .pd-panel { margin-top: 16px; }

.pd-panel-label {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted, #888888);
  margin: 0 0 10px;
}

.pd-price {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 30px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--gold, #d4a843);
  margin: 0 0 6px;
  font-variant-numeric: tabular-nums;
}

.pd-price-sub {
  font-size: 12px;
  line-height: 1.55;
  color: var(--muted, #888888);
  margin: 0 0 16px;
}

.pd-own {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 12px;
  font-weight: 600;
  color: var(--gold, #d4a843);
  margin: 0 0 14px;
}

.pd-own .playlist-own-bar { height: 4px; }

/* The large collect button: same control as the card's pill, scaled up. */
.playlist-detail-actions .playlist-collect-btn,
.playlist-detail-actions .playlist-collect-owned,
.playlist-detail-actions .playlist-no-bundle {
  display: flex;
  width: 100%;
  justify-content: center;
  min-height: 44px;
  font-size: 14px;
  border-radius: 8px;
}

.playlist-detail-actions .playlist-collect-btn { font-weight: 700; }

.playlist-detail-partial-note,
.playlist-detail-withheld {
  margin: 12px 0 0;
  font-size: 11px;
  line-height: 1.55;
  color: var(--dim, #6f6f6f);
}

.playlist-detail-withheld { color: #c9b887; font-size: 12px; margin: 0 0 10px; }

.pd-facts { font-size: 13px; line-height: 2; color: var(--muted, #888888); margin: 0; }
.pd-facts b { color: #dcdcdc; font-weight: 600; }
.pd-facts a { color: inherit; text-decoration: none; }
.pd-facts a:hover { color: var(--gold-light, #f0c86a); }

.pd-eyebrow {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold, #d4a843);
  margin: 0 0 10px;
}

.pd-eyebrow-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 10px; }
.pd-eyebrow-row .pd-eyebrow { margin: 0; }
.pd-edit-btn {
  font-size: 12px;
  font-weight: 600;
  color: #dcdcdc;
  text-decoration: none;
  padding: 5px 12px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: 999px;
  white-space: nowrap;
}
.pd-edit-btn:hover,
.pd-edit-btn:focus-visible { color: var(--gold-light, #f0c86a); border-color: var(--gold, #d4a843); }

.playlist-detail-title {
  font-size: clamp(26px, 3.4vw, 38px);
  font-weight: 800;
  letter-spacing: -0.025em;
  line-height: 1.05;
  margin: 0 0 10px;
  text-wrap: balance;
}

.pd-artist { font-size: 16px; font-weight: 500; margin: 0 0 6px; }

.playlist-detail-artist-link { color: #cfcfcf; text-decoration: none; }
.playlist-detail-artist-link:hover,
.playlist-detail-artist-link:focus-visible { color: var(--gold-light, #f0c86a); }

/* A label's playlist: the label line under the artist, left-aligned with
   the header and a touch larger than on the cards. Two classes, because
   card_label.css loads after this file and would win a tie. */
.card-label-slot.pd-label-line { justify-content: flex-start; height: auto; padding: 0; margin: -2px 0 8px; font-size: 12px; }
.card-label-slot.pd-label-line .card-label-pill { font-size: 10px; }

.pd-meta { font-size: 13px; color: var(--muted, #888888); margin: 0; }

.playlist-detail-description {
  margin: 14px 0 0;
  font-size: 14px;
  line-height: 1.6;
  color: #c6ced8;
  white-space: pre-line;
}

/* ------------------------------------------------------------- the tracklist */

.pd-list-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin: 34px 0 14px;
}

.pd-list-head h2 { font-size: 18px; font-weight: 700; margin: 0; }
.pd-list-hint { font-size: 12px; color: var(--muted, #888888); }

.playlist-detail-rows { list-style: none; margin: 0; padding: 0; }

.pd-trow {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--border, rgba(255, 255, 255, 0.09));
  border-radius: 10px;
  background: var(--card, #1c1c1c);
}

.pd-trow + .pd-trow { margin-top: 8px; }
.pd-trow:hover { border-color: var(--border-strong, rgba(255, 255, 255, 0.12)); }

.pd-tnum {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 12px;
  color: var(--muted, #888888);
  flex: 0 0 22px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* `.play-pause-btn` is card.html's hook for card_player.js, and its look lives
   in that template's per-card <style>, which this page does not emit. */
.pd-tplay {
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--gold, #d4a843);
  color: var(--gold, #d4a843);
  background: transparent;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease;
}

.pd-tplay:hover,
.pd-tplay.is-playing,
.pd-tplay.is-current-track { background: var(--gold, #d4a843); color: var(--night, #0c0c0c); }

.pd-tmain { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }

.pd-ttitle {
  font-size: 14px;
  font-weight: 600;
  color: inherit;
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pd-ttitle:hover,
.pd-ttitle:focus-visible { color: var(--gold-light, #f0c86a); }

.pd-tsub {
  font-size: 11px;
  color: var(--muted, #888888);
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
}

.pd-tbadge {
  padding: 1px 6px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  background: rgba(255, 255, 255, 0.08);
  color: #ddd;
}

.pd-tbadge.is-edit { background: var(--gold, #d4a843); color: var(--night, #0c0c0c); }
.pd-tgenre { color: var(--gold, #d4a843); font-weight: 600; }

.pd-tright { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
/* Wishlist count always last (right edge); the price/collect pill sits to its left. */
.pd-tright > .pd-likes { order: 2; min-width: 3.2rem; justify-content: flex-end; }
.pd-tright > :not(.pd-likes) { order: 1; }

.pd-heart {
  background: transparent;
  border: 0;
  padding: 0;
  color: #e0e1dd;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
}

.pd-heart:hover { color: #f4c430; }
.pd-heart .heart-filled { display: none; }
.pd-heart.is-wishlisted .heart-outline { display: none; }
.pd-heart.is-wishlisted .heart-filled { display: inline-flex; color: #f4c430; }

/* Same shape as card.html's `.card-collect-btn`, standing on its own. */
.pd-tcollect {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 9px;
  min-height: 26px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.5;
  border-radius: 4px;
  border: 1px solid var(--gold, #d4a843);
  background: var(--gold, #d4a843);
  color: var(--night, #0c0c0c);
  white-space: nowrap;
  cursor: pointer;
  text-decoration: none;
  box-shadow: 0 0 0 3px rgba(212, 168, 67, 0.14);
}

.pd-tcollect:hover,
.pd-tcollect:focus-visible { background: var(--gold-light, #f0c86a); color: var(--night, #0c0c0c); }

.pd-tcollect.is-short {
  background: rgba(212, 168, 67, 0.08);
  border-style: dashed;
  color: var(--gold, #d4a843);
  font-weight: 600;
  box-shadow: none;
}

.pd-towned {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 8px;
  min-height: 26px;
  font-size: 11px;
  font-weight: 600;
  border-radius: 4px;
  border: 1px solid var(--border-strong, rgba(255, 255, 255, 0.12));
  color: var(--muted, #888888);
  white-space: nowrap;
}

.pd-likes {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted, #888888);
}

.pd-likes-count { min-width: 1ch; font-variant-numeric: tabular-nums; }

.pd-heart.is-static { cursor: default; color: var(--muted, #888888); }
.pd-heart.is-static:hover { color: var(--muted, #888888); }

/* A price with nothing to press: the viewer's own upload. */
.pd-tprice {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  min-height: 26px;
  font-size: 11px;
  font-weight: 700;
  border-radius: 4px;
  border: 1px solid rgba(212, 168, 67, 0.4);
  color: var(--gold, #d4a843);
  white-space: nowrap;
}

.playlist-detail-row-empty:hover { border-color: var(--border, rgba(255, 255, 255, 0.09)); }

@media (max-width: 575.98px) {
  .pd-trow { gap: 8px; padding: 8px; flex-wrap: wrap; }
  .pd-tmain { flex-basis: 40%; }
  .pd-tright { margin-left: auto; }
}
