/* ===== MODALS ===== */
/* Compound selector so these values reliably win over the generic .modal rule in
   addEditModal.css — both are single-class selectors (equal specificity), and
   addEditModal.css loads after this file, so a bare .detail-modal loses that tie
   for every property they share (width, padding, max-width, max-height). */
.modal.detail-modal {
  position: relative;
  /* 20% larger across the whole modal (padding/width/max-height, and everything within it — icons,
     text, buttons) per explicit request — every dimensional value in this file's non-mobile rules
     is scaled by the same 1.2x, except border-width (divider lines stay 1px) and border-radius on
     already-fully-rounded pill/circle shapes (scaling those would just change nothing visually). */
  padding: 34px;
  width: 374px;
  max-width: 94vw;
  /* A cap, not a firm height — the modal shrink-wraps to its actual content, so the bottom
     padding sits immediately after the last row, matching the top padding above the image. A
     firm height was tried instead (forcing every category to the same fixed box) but left a dead
     gap below Add to Queue for any category whose collapsed content came in shorter than the cap. */
  max-height: min(90vh, 720px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* Overrides addEditModal.css's shared .modal-overlay display: none/flex instant toggle with an
   animated opacity/visibility version instead, scoped to this modal specifically (an ID selector
   always outranks .modal-overlay's class selector, regardless of file/rule order) — the Add/Edit
   and other modals keep their existing instant behavior. visibility (not display) is what actually
   keeps the closed overlay out of the click/accessibility path; the delayed visibility transition
   (0s duration, but delayed until the opacity fade finishes) is what lets opacity animate all the
   way to 0 before that happens, rather than the overlay vanishing mid-fade. */
#detail-modal-overlay {
  display: flex;
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.2s var(--m3-standard-accelerate), visibility 0s linear 0.2s;
}
#detail-modal-overlay.open {
  visibility: visible;
  opacity: 1;
  transition: opacity 0.3s var(--m3-standard-decelerate), visibility 0s linear 0s;
}

/* The "container transform" half of the same open/close motion — the modal itself grows into
   place (scale + slight upward drift) as the backdrop fades in, and shrinks back on close. Faster/
   accelerate on close, slower/emphasized-decelerate on open — same asymmetry as everywhere else
   in this file's M3 motion. */
#detail-modal-overlay .modal.detail-modal {
  transform: scale(0.94) translateY(8px);
  transition: transform 0.2s var(--m3-standard-accelerate);
}
#detail-modal-overlay.open .modal.detail-modal {
  transform: scale(1) translateY(0);
  transition: transform 0.3s var(--m3-emphasized-decelerate);
}

.modal-x-close {
  position: absolute;
  top: 8px;
  right: 8px;
  background: none;
  border: none;
  font-size: 14px;
  line-height: 1;
  color: var(--text-muted);
  cursor: pointer;
  padding: 6px;
  z-index: 10;
  border-radius: 4px;
  transition: color 0.15s;
}
.modal-x-close:hover { color: var(--text-primary); }

/* Desktop-hidden — the detail modal's own close button, mobile-only (see @media below). Backdrop
   click/Escape already cover desktop, where the modal is small enough to always leave backdrop to
   tap; on mobile the modal nearly fills the viewport instead, leaving almost none. */
.detail-modal-close {
  display: none;
}

/* This, .detail-bookmark-btn, and .detail-favorite-btn form the top-right icon column — all
   three share the same right: 4px, centering their 32px width in the gap between the (fixed
   232px, no longer modal-width-relative) featured image's right edge and the modal's own outer
   right edge, rather than a flat inset from the modal edge. That gap is 40px (the modal's 312px
   minus the image's 40px left offset minus its own 232px width), so a 4px inset centers a 32px
   icon within it. */
.detail-edit {
  position: absolute;
  top: 112px;
  right: 5px;
  background: none;
  border: none;
  border-radius: 50%;
  width: 38px;
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #fff;
  z-index: 20;
}

.detail-bookmark-btn {
  position: absolute;
  /* Swapped with .detail-favorite-btn's position (star now on top, bookmark in the middle) — was
     16px/top-most; see .detail-favorite-btn's own comment for the top-most-slot math this
     inherited. */
  top: 64px;
  right: 5px; /* see .detail-edit above */
  background: none;
  border: none;
  border-radius: 50%;
  width: 38px;
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #fff;
  z-index: 20;
  transition: color 0.15s;
}

.detail-bookmark-btn:hover,
.detail-bookmark-btn--saved {
  color: var(--primary);
}

.detail-favorite-btn {
  position: absolute;
  /* Swapped with .detail-bookmark-btn's position (star now top-most) — 4px above the CTA
     wrapper's own 17px top, same reasoning .detail-bookmark-btn's old comment had: the icon
     glyph inside is 24px tall centered in this 32px box (a 4px inset on every side), while the
     "Official Website" pill's border/glow sits right at its wrapper's top edge with no such
     inset, so matching the raw 17px here left the icon's visible top sitting 4px below the
     pill's. */
  top: 16px;
  right: 5px; /* see .detail-edit above */
  background: none;
  border: none;
  border-radius: 50%;
  width: 38px;
  height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  color: #fff;
  z-index: 20;
  transition: color 0.15s;
}

.detail-favorite-btn--active {
  color: var(--primary);
}

/* "Save to:" dropdown (detailModalHeader.js's _toggleSaveToMenu) — inserted as a direct sibling
   right after #detail-favorite-btn, so it shares that button's own containing block
   (.modal.detail-modal, position:relative) for absolute positioning. Sits just left of the
   edit/bookmark/favorite icon column (which ends at right:43px — 5px inset + 38px width) rather
   than below the star, where it would collide with the Edit button sitting close beneath it.
   Same solid-purple/rounded-corner treatment as .my-options-dropdown (base.css) for visual
   consistency with the app's one other dropdown menu. */
.detail-save-to-menu {
  position: absolute;
  top: 60px;
  right: 50px;
  min-width: 160px;
  background: var(--primary);
  border-radius: 14px;
  box-shadow: 0 4px 24px rgba(0,0,0,0.4);
  overflow: hidden;
  z-index: 30;
}

.detail-save-to-menu-title {
  padding: 10px 16px 4px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: rgba(255,255,255,0.7);
}

.detail-save-to-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 16px;
  background: none;
  border: none;
  color: #fff;
  font-size: 14px;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}

.detail-save-to-menu-item:hover {
  background: rgba(255,255,255,0.12);
}

.detail-save-to-menu-item-name {
  flex: 1;
}

/* Styled to look like a native radio button, but isn't one — a real <input type="radio"> can't be
   unchecked by clicking the already-selected one again, which selecting-to-deselect here needs. */
.detail-save-to-menu-radio {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.6);
  position: relative;
}

.detail-save-to-menu-radio--checked {
  border-color: #fff;
}

.detail-save-to-menu-radio--checked::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #fff;
  transform: translate(-50%, -50%);
}

/* Custom hover tooltip for the right-side icon column (edit/bookmark/favorite). Built as a
   ::after (generated content, not a real child) rather than a nested <span>, because the
   bookmark/favorite buttons have their innerHTML fully replaced on every state change
   (updateBookmarkIcon/updateFavoriteIcon) — a real child span would get wiped out with it. */
.detail-edit::after,
.detail-bookmark-btn::after,
.detail-favorite-btn::after {
  content: attr(data-tooltip);
  position: absolute;
  right: 100%;
  top: 50%;
  transform: translateY(-50%);
  margin-right: 6px;
  background: var(--surface);
  border: 1px solid var(--primary);
  border-radius: 7px;
  padding: 5px 11px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--text-primary);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s, visibility 0.15s;
  pointer-events: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
  z-index: 10;
}

.detail-edit:hover::after,
.detail-bookmark-btn:hover::after,
.detail-favorite-btn:hover::after {
  opacity: 1;
  visibility: visible;
}

.detail-artist-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
  padding: 22px 26px 19px;
  flex-shrink: 0;
}

.detail-artist-header--inline {
  position: absolute;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  padding: 0;
  z-index: 2;
}

/* Fixed pixel size, deliberately not a % of the modal's own width — so narrowing or widening the
   modal doesn't resize the featured image along with it. */
.detail-image-wrap {
  position: relative;
  width: 278px;
  margin: 0 auto;
  aspect-ratio: 1/1;
  overflow: visible; /* the "Your Statement" badge (.vc-sponsored-tag--overlay) sits partly outside this box (bottom: -10.5px) and needs it — safe to override to hidden below since that badge is already display:none by the time focus mode (the only state that collapses this) is active */
  background: var(--border);
  flex-shrink: 0;
  max-height: 278px; /* matches the aspect-ratio-derived height exactly — a no-op cap normally, but gives .detail-modal--focus-mode below something to animate down to 0 */
  opacity: 1;
  /* Entering focus mode (collapsing) gets the M3 "emphasized decelerate" curve — same reasoning
     as the accordion sections' own open transition: a gentle settle reads better for the more
     prominent motion in the pair. */
  transition: max-height 0.3s var(--m3-emphasized-decelerate), opacity 0.25s var(--m3-emphasized-decelerate), margin 0.3s var(--m3-emphasized-decelerate);
}
/* Absolutely-positioned so it's out of flow — keeps the wrap's aspect-ratio from being
   overridden by the actual photo's natural size, while still clipping/cropping its content. */
.detail-image-crop { position: absolute; inset: 0; overflow: hidden; }
/* White background behind the image itself (not just the modal) — matches .card-image's own fix,
   same reason: some curated logo PNGs have transparent backgrounds and were nearly invisible
   against the modal's dark background showing through. */
.detail-image { width: 100%; height: 100%; object-fit: cover; object-position: center; display: block; background: #fff; }
.detail-image--faces { object-position: center 20%; }
.detail-image--clickable { cursor: pointer; transition: filter 0.15s ease; }
.detail-image--clickable:hover { filter: brightness(0.7); }
/* Extra zoom-crop for YouTube thumbnails specifically (isYoutubeThumbnailUrl, utils.js) — same
   reasoning/values as .card-image--zoom, cards.css. .detail-image-crop's own overflow: hidden
   clips the excess. */
/* 1.2 -> 1.25 -> 1.35, per direct follow-ups. */
.detail-image--zoom { transform: scale(1.35); }

.detail-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 64px;
  font-weight: 700;
  color: rgba(255,255,255,0.9);
}

.detail-body {
  /* flex: 1 is what actually makes this the modal's scroll region — without it, this sizes to
     fit its content's natural height (its own overflow-y:auto never engages), and the *whole
     modal* balloons toward its max-height: 90vh cap instead whenever an accordion opens. */
  flex: 1;
  /* No top padding here (moved into .detail-title-row / .detail-note-toolbar's own padding-top
     below) — a sticky child only ever locks flush with THIS element's own top edge, never past
     it, so container-level top padding leaves a permanent gap above the stuck position where
     scrolled-past content keeps rendering, uncropped, instead of being hidden by the sticky bar. */
  padding: 0 0 7px 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow-y: auto;
  min-height: 0;
  /* .detail-note-toolbar intentionally bleeds 28px past this container's own edges (negative
     margins, see its own comment) to reach the modal's true outer edge — overflow-x: hidden here
     would clip that bleed at THIS element's own boundary instead of the modal's, so it's left as
     the implicit 'auto' overflow-x computes to (a bit of horizontal scroll capacity, no visible
     scrollbar) rather than fixed explicitly. On mobile that's still safe from a touch-drag
     standpoint without needing its own touch-action here too — body's touch-action: pan-y
     (base.css) already blocks horizontal panning for this whole subtree. */
  /* The browser's native "keep the caret visible while typing" auto-scroll has no idea the sticky
     title/toolbar (see .detail-title-row / .detail-note-toolbar) will re-cover the very top of
     this scrollport regardless of scroll position — without this, growing a note tall enough
     (typing past the visible area) scrolls it to align flush with y=0, landing directly UNDER the
     sticky toolbar instead of below it. Reserves the toolbar's own height (~60px) as a no-scroll-
     into buffer, the same fix as scroll-margin-top under a fixed page header. (The real fix for
     this is the JS-computed _correctScrollUnderToolbar() in detailModalNotes.js — this is a
     best-effort supplement for any native scroll paths that one doesn't cover.)
  */
  scroll-padding-top: 72px;
  /* Scrolling stays fully functional (mouse wheel/trackpad/keyboard) — this only hides the
     visible scrollbar track/thumb. */
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* legacy Edge/IE */
}

.detail-body::-webkit-scrollbar {
  display: none; /* Chrome/Safari/Edge (Chromium) */
}

/* Without this, flex's default shrink behavior compresses these rows/sections smaller than
   their actual content when everything together doesn't fit .detail-body's available height —
   clipping them internally instead of letting .detail-body's own scroll handle the overflow. */
.detail-body > * {
  flex-shrink: 0;
}

/* Matches the ~12px breathing room the edit/bookmark icons sit inset from the top of the
   image (which itself is flush, 0 padding) — the default 6px bottom padding plus the normal
   gap after the collapsed Queue section otherwise reads as more space than the top has. */
.detail-body--tight-bottom {
  padding-bottom: 0;
  margin-bottom: -4px;
}

.detail-title-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  position: sticky;
  top: 0;
  z-index: 4; /* above ordinary accordion content, below the 20-tier edit/bookmark/favorite icons */
  background: var(--modal-bg);
  /* was margin-top: 9px — moved to padding so the sticky background actually covers this gap;
     margin sits outside the border box and wouldn't paint over scrolled content once stuck.
     24px = the original 9px plus the 15px #detail-body used to carry as its own top padding —
     moved here because a sticky child only ever locks flush with its scroll container's real
     top edge, never past it, so padding on the container itself left a permanent gap above the
     stuck position where scrolled-past content kept rendering instead of being cropped away.
     (29px = the original 24px × the modal's overall 1.2x scale-up.) */
  padding-top: 29px;
  padding-bottom: 7px;
  /* Title/toolbar swap (see .detail-body--editing-note below) reads as one continuous upward
     "conveyor" motion rather than a reverse-on-close bounce: whichever of the two is leaving
     always slides up and fades out (translateY 0 → -8px), and whichever is arriving always
     slides up into place from just below (translateY 8px → 0) — same direction both ways, only
     entering vs. exiting differs (hence the different M3 curves, not different translateY signs).
     `display` is included in the transition list (allow-discrete) so toggling display: none
     doesn't just instantly cut the animation short; @starting-style below supplies the "from"
     values for the display: none → flex direction, which a plain transition can't animate
     otherwise (an element can't transition FROM a state it wasn't rendered in). */
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.2s var(--m3-standard-accelerate), transform 0.2s var(--m3-standard-accelerate), display 0.2s allow-discrete;
}
@starting-style {
  .detail-title-row { opacity: 0; transform: translateY(8px); }
}

.detail-note-toolbar {
  display: none; /* shown via .detail-body--editing-note below, replacing the title in the same slot */
  position: sticky;
  top: 0;
  z-index: 4;
  align-items: center;
  justify-content: center; /* centers the button row so leftover space (this box is wider than 6 small icon buttons) splits evenly left/right, instead of all piling up on the right the way a default left-aligned row would */
  gap: 7px;
  padding: 9px 12px; /* equal top/bottom (was 11px/7px) so align-items:center actually centers the
  buttons within the purple bar's full height, not just within an already-off-center content box.
  Equal 12px left/right shifts the button row off the modal's true edge on both sides evenly (the
  toolbar's own box no longer bleeds — see the ::before pseudo below). */
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s var(--m3-standard-accelerate), transform 0.2s var(--m3-standard-accelerate), display 0.2s allow-discrete;
}
/* The purple background lives on this pseudo-element instead of directly on .detail-note-toolbar,
   bled out to the modal's true edges via absolute positioning (left/right: -34px, matching
   .modal.detail-modal's own 34px side padding) — negative margin on the toolbar itself was tried
   first, but margin on a sticky flex item inside a `gap`-using container didn't reliably bleed
   (particularly margin-top, needed for the focus-mode case below, which had no visible effect at
   all despite computing correctly). Absolute positioning against .detail-note-toolbar's own
   sticky containing block is unaffected by either of those and bleeds reliably in both axes.
   z-index: -1 keeps it behind the toolbar's own button content. */
.detail-note-toolbar::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: -34px;
  right: -34px;
  background: var(--primary);
  z-index: -1;
}
/* With the image hidden, the toolbar is the modal's first visible content, so its background
   needs to additionally bleed upward to cover .modal.detail-modal's own 28px top padding (which
   the image normally sits in front of) — and round to match the modal's own 16px corners, since
   the toolbar now touches them directly instead of a sharp corner poking past the curve. */
.detail-body--editing-note .detail-title-row {
  display: none;
  opacity: 0;
  transform: translateY(-8px); /* exiting — continues the upward motion up and off, rather than reversing back down */
  transition: opacity 0.2s var(--m3-standard-accelerate), transform 0.2s var(--m3-standard-accelerate), display 0.2s allow-discrete;
}
.detail-body--editing-note .detail-note-toolbar {
  display: flex;
  opacity: 1;
  transform: translateY(0);
  /* Entering gets the more pronounced "emphasized" curve (vs. the title's exit above, and the
     toolbar's own exit below, both "standard") — M3 reserves emphasized easing for the more
     prominent motion in a transition pair, and arriving/taking-over reads as the prominent half
     here. Slightly longer duration too, so the settle-into-place is perceptible rather than instant.
     Delayed to start after the MY NOTES/SONG LIST section (see .detail-accordion-collapsible.open
     in this file) has finished sliding open, rather than both animating at once — the toolbar
     fading in as the last step of one sequence, not two motions competing for attention. */
  transition: opacity 0.25s var(--m3-emphasized-decelerate) 0.3s, transform 0.25s var(--m3-emphasized-decelerate) 0.3s, display 0.25s allow-discrete 0.3s;
}
@starting-style {
  .detail-body--editing-note .detail-note-toolbar { opacity: 0; transform: translateY(8px); }
}

/* Filled the same solid purple as the toolbar's own container background (.detail-note-toolbar's
   ::before) — the button only reads as a distinct button (not just part of the bar) because its
   icon and outline are var(--modal-bg) instead: dark against the purple fill, and matching the
   modal's own surface color so a button reads as "a piece of the modal poking through the purple
   bar" rather than a separately-colored UI chrome element. */
.detail-note-toolbar-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 7px;
  border: 2px solid var(--modal-bg);
  background: var(--primary);
  color: var(--modal-bg);
  cursor: pointer;
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.detail-note-toolbar-btn svg {
  /* Overrides each icon's own inline width="18"/height="18" attributes (index.html) — 18px × the
     modal's overall 1.2x scale-up. Same value the mobile media query below already used. */
  width: 22px;
  height: 22px;
}
/* Hover inverts the fill/icon (same scheme --active below uses) — modal-bg fill, purple icon —
   so hovering previews exactly what the toggled-on look is, rather than just brightening the
   same purple. */
.detail-note-toolbar-btn:hover { background: var(--modal-bg); color: var(--primary); }
/* Active (toggled-on, e.g. Focus mode engaged) inverts the resting scheme — var(--modal-bg) fill,
   purple icon/border — so a toggled button reads clearly differently from every plain one sitting
   next to it, not just a slightly different shade of the same purple. */
.detail-note-toolbar-btn--active {
  color: var(--primary);
  background: var(--modal-bg);
  border-color: var(--primary);
}
.detail-note-toolbar-btn[disabled] { opacity: 0.4; pointer-events: none; cursor: default; }
/* No extra margin on Expand or Close — every button (formatting or action) sits at the same 7px
   gap the toolbar's own flex `gap` already provides, and the row is centered as a whole (see
   .detail-note-toolbar's justify-content) rather than any one button carrying an edge inset.
   Two earlier attempts at visually separating Expand/Close into their own "actions" cluster
   (margin-left: auto, then a fixed 20px) both still read as "there's a gap here" once the row
   had 4 formatting buttons instead of 3; a uniform gap is the one that doesn't. */

/* ===== VOICE NOTE POPUP (see voiceNotes.js) ===== */
.voice-note-modal { width: 380px; }
.voice-note-modal .modal-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 12px 0 20px;
}
.voice-note-record-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  border: none;
  background: var(--primary);
  color: #fff;
  cursor: pointer;
  transition: background 0.15s, transform 0.1s;
}
.voice-note-record-btn:hover { background: var(--primary-hover); }
.voice-note-record-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.voice-note-stop-btn {
  padding: 9px 28px;
  border: none;
  border-radius: 24px;
  background: #E24444;
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.voice-note-recording-indicator {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
}
.voice-note-rec-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #E24444;
  animation: voiceNoteRecPulse 1.1s ease-in-out infinite;
}
@keyframes voiceNoteRecPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}
#voice-note-audio-preview { width: 100%; }

.detail-modal--focus-mode #detail-edit,
.detail-modal--focus-mode #detail-bookmark-btn,
.detail-modal--focus-mode #detail-favorite-btn,
.detail-modal--focus-mode #detail-artist-header {
  display: none;
}

/* Albums/Web Links/Add to Queue — the other accordion rows in the same stack as My Notes/Song
   List — also hide in focus mode, so the note itself is the only thing left to focus on. Unlike
   the 4 buttons above, these three carry their own inline style.display (toggled per-category in
   detailModalSummary.js/detailModalQueue.js), which always wins over a plain class rule — hence
   !important here, the only way to reliably override an inline style from CSS. */
.detail-modal--focus-mode #detail-albums-accordion-header,
.detail-modal--focus-mode #detail-albums-list,
.detail-modal--focus-mode #detail-streaming,
.detail-modal--focus-mode #btn-standalone-queue,
.detail-modal--focus-mode #detail-queue {
  display: none !important;
}

/* Animated instead of the instant display: none the 4 buttons above use — those are position:
   absolute (don't occupy layout space either way), but this is a normal-flow sibling before
   #detail-body, so collapsing it smoothly is what actually makes the toolbar/content section
   visibly slide up into the freed space, rather than snapping. overflow: hidden here (vs. the
   base rule's visible, needed for the "Your Statement" badge — see its own comment) is safe: that
   badge is already display: none by the time focus mode is active (.detail-modal--editing-note
   .vc-sponsored-tag--overlay), so there's nothing left needing to overflow this box when it
   matters. */
.detail-modal--focus-mode #detail-image-wrap {
  overflow: hidden;
  max-height: 0;
  opacity: 0;
  margin: 0;
  /* Exiting focus mode (re-expanding) gets the quicker "standard accelerate" curve — same
     asymmetry as everywhere else in this file, snappier than the entrance. */
  transition: max-height 0.2s var(--m3-standard-accelerate), opacity 0.15s var(--m3-standard-accelerate), margin 0.2s var(--m3-standard-accelerate);
}

/* The "Your Statement" sponsored badge sits inside #detail-image-wrap, which only hides in full
   focus mode above — but it's distracting/overlapping even just while typing a note (toolbar
   shown, image still visible), so it hides on that broader "editing" condition instead. */
.detail-modal--editing-note .vc-sponsored-tag--overlay {
  display: none;
}

.detail-modal--focus-mode {
  /* Hiding the image/icon column above would otherwise let the modal (which normally
     shrink-wraps to its content — see the comment on .modal.detail-modal) auto-shrink shorter.
     Pin it to the same max-height cap it already uses instead, and let .detail-body's flex: 1
     absorb the freed vertical space — giving the note more room is the whole point of focus mode,
     not a smaller modal. */
  height: min(90vh, 720px);
}

.detail-title {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 10px;
  font-size: 21px;
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.3;
}

.detail-title-text {
  flex: 1;
}

.detail-title-sep {
  opacity: 0.35;
  margin: 0 3px;
}

.btn-detail-website {
  position: relative;
  flex-shrink: 0;
  /* No fixed width — this used to be sized to fit "OFFICIAL WEBSITE"; now that the label is just
     "WEBSITE" (much shorter), a fixed width left a lot of dead space on either side of the text.
     Shrink-wraps to the text via padding instead, same as this file's other pill buttons. Still
     centers correctly under the image (.detail-artist-header--inline uses translateX(-50%), which
     re-centers on whatever width this ends up rendering at). */
  padding: 5px 18px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--primary) 25%, white);
  background: rgba(10, 8, 20, 0.8);
  border: 2px solid var(--primary);
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  text-shadow: 0 0 4px color-mix(in srgb, var(--primary) 60%, transparent);
  box-shadow:
    0 0 3px rgba(255, 255, 255, 0.5),
    0 0 8px 1px color-mix(in srgb, var(--primary) 40%, transparent),
    0 0 14px 2px color-mix(in srgb, var(--primary) 35%, transparent),
    inset 0 0 4px color-mix(in srgb, var(--primary) 20%, transparent);
  transition: box-shadow 0.15s, color 0.15s, background 0.15s, transform 0.1s;
  margin-top: 1px;
}

.btn-detail-website::before,
.btn-detail-website::after {
  content: '';
  position: absolute;
  width: 7px;
  height: 4px;
  border-radius: 999px;
  background: #fff;
  pointer-events: none;
  box-shadow:
    0 0 4px 2px rgba(255, 255, 255, 0.9),
    0 0 9px 4px color-mix(in srgb, var(--primary) 80%, transparent),
    0 0 15px 6px color-mix(in srgb, var(--primary) 45%, transparent);
}

.btn-detail-website::before {
  top: -4px;
  right: 6%;
}

.btn-detail-website::after {
  bottom: -4px;
  left: 6%;
}

.btn-detail-website:hover {
  color: #fff;
  background: var(--primary);
  box-shadow:
    0 0 6px color-mix(in srgb, var(--primary) 60%, transparent),
    inset 0 0 6px color-mix(in srgb, var(--primary) 30%, transparent);
}

.btn-detail-website:active {
  transform: translateY(1px);
}

.detail-notes {
  font-size: 15px;
  color: var(--text-secondary);
  line-height: 1.5;
}

.detail-summary-wrap {
  position: relative;
  margin-top: -6px;
}

.detail-summary-text {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  width: 100%;
  padding-right: 19px;
  line-height: 22px;
}

.detail-summary-text--expanded {
  -webkit-line-clamp: unset;
  overflow: visible;
}

.detail-summary-toggle {
  position: absolute;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  padding: 2px;
  transition: color 0.15s;
}

.detail-summary-toggle svg {
  transition: transform 0.2s ease;
}

.detail-summary-toggle:hover {
  color: var(--text-primary);
}

.detail-summary-toggle--open svg {
  transform: rotate(90deg);
}

/* Musician modal — accordion rows (My Notes / Albums), mirrors the .streaming-header/
   .streaming-links-wrap collapse mechanics used by Web Links/Queue, with its own classes
   since the visual treatment (full-width row, not a pill) is different. */
.detail-accordion-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 2px;
  border-bottom: 1px solid #5B5BEF;
  cursor: pointer;
  user-select: none;
  color: var(--text-primary);
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.detail-accordion-header--no-divider {
  border-bottom: none;
}

.detail-accordion-header--book-summary {
  margin-top: 0;
}

/* Web Links (.detail-streaming) is a single self-contained element, not split into a header +
   a separate .detail-accordion-collapsible sibling like the other rows — so unlike them, there's
   nothing here to cancel the .detail-body flex gap above this row. Match it manually so the gap
   above Add to Queue looks the same as the (cancelled-down-to-flush) gaps between the others. */
#btn-standalone-queue {
  margin-top: -10px;
  /* Deliberately no margin-bottom compensation to match: pulling this row's trailing space flush
     with the modal's own bottom padding only looks right when the modal is short enough to
     shrink-wrap without scrolling. Once .detail-body actually scrolls (a normal case — content
     length varies by item, not just category), the same trick leaves almost no breathing room
     below the last row, reading as clipped right at the bottom edge. */
}

/* Pulls My Notes up closer to the summary above it — the first accordion row doesn't need
   the full .detail-body gap that separates the other (already-flush) accordion rows. */
#detail-notes-accordion-header {
  margin-top: -1px;
}

.detail-accordion-header span {
  flex: 1;
  line-height: 1;
}

.detail-accordion-icon {
  display: block;
  flex-shrink: 0;
  color: #5B5BEF;
  width: 25px;
  height: 25px;
}

/* This icon's artwork sits visually high within its own box relative to the other two
   accordion icons — nudged down to match them. */
#detail-albums-accordion-header .detail-accordion-icon {
  margin-top: 2px;
  width: 23px;
  height: 23px;
}

#detail-notes-accordion-header .detail-accordion-icon {
  width: 23px;
  height: 23px;
}

/* ID-scoped to unambiguously force the same size/color as the other three accordion icons —
   this one is a directly-set fill="currentColor" SVG rather than a nested icon, so pin it down
   explicitly rather than leaning on the shared .detail-accordion-icon class alone. */
#standalone-queue-bookmark {
  width: 23px;
  height: 23px;
  color: #5B5BEF;
}

.detail-accordion-chevron {
  display: block;
  flex-shrink: 0;
  color: var(--primary);
  transition: transform 0.2s ease;
  /* 12px × 1.1, × the modal's overall 1.2x scale-up — overrides the width="12" height="12"
     attributes set on every instance in index.html (a CSS property always wins over the
     equivalent presentational HTML attribute), so this one rule covers all of them instead of
     editing each SVG tag individually. */
  width: 16px;
  height: 16px;
}

.detail-accordion-header.open .detail-accordion-chevron {
  transform: rotate(90deg);
}

/* When open, the divider moves from between the header and its content to underneath the
   revealed content instead — the header itself no longer draws its own bottom border. The
   title turns purple too (icon/chevron have their own explicit colors, so this only affects
   the label span, which has no color override of its own to inherit over). */
.detail-accordion-header.open {
  border-bottom: none;
  color: var(--primary);
}

/* Same purple as the open state, just on hover — icon/chevron keep their own explicit colors
   (unaffected, same reasoning as .open above), so this only affects the label span. */
.detail-accordion-header:hover {
  color: var(--primary);
}

.detail-accordion-collapsible.open {
  border-bottom: 1px solid #5B5BEF;
  padding-bottom: 10px;
}

/* Albums/Web Links/Add to Queue (whatever this divider would normally separate My Notes/Song
   List from) are hidden entirely in focus mode — nothing left below to divide from, so the line
   just reads as a stray leftover. */
.detail-modal--focus-mode #detail-notes-list.open,
.detail-modal--focus-mode #detail-tracklist.open {
  border-bottom: none;
}

/* Web Links (Musician mode): #detail-streaming (not the header div itself) receives the
   .open class, so the header/content divider swap needs its own selector here. */
.detail-streaming--tight.open .detail-accordion-header {
  border-bottom: none;
}

.detail-streaming--tight.open .streaming-links-wrap {
  border-bottom: 1px solid #5B5BEF;
  padding-bottom: 10px;
}

.detail-accordion-collapsible {
  max-height: 0;
  overflow: hidden;
  /* Slides the section's own content up into place as it opens (and lets it sink back down as it
     closes) — max-height alone reads as a growing reveal, not a slide; this transform is what
     actually makes it feel like the enter/exit motion, same idea as the title/toolbar swap above.
     No opacity fade — some of this class's max-height caps (2000px for My Notes/chapters, Summary
     prose, etc.) are far larger than any real content, so a fade tied to the same duration as that
     climb reads as "fades in, *then* the box finishes growing" rather than one clean motion; the
     real content reaching its true size well before max-height stops climbing is harmless on its
     own (that headroom is just invisible), it only became visible as an issue once opacity was
     layered on top of it. This (the closed state) is what's transitioned TO on close, so it uses
     M3's "accelerate" curve — quick start, fast finish, reads as snappier/getting-out-of-the-way. */
  transform: translateY(10px);
  transition: max-height 0.2s var(--m3-standard-accelerate), margin 0.2s var(--m3-standard-accelerate), transform 0.2s var(--m3-standard-accelerate);
  /* Cancels out the .detail-body flex gap above and below this (currently zero-height)
     element, so consecutive accordion rows sit flush against each other when collapsed. */
  margin: -10px 0;
}

.detail-accordion-collapsible.open {
  max-height: 384px;
  margin: 10px 0 0;
  overflow-y: auto;
  /* Scrolling stays fully functional — this only hides the visible scrollbar track/thumb, same
     as #detail-body above. Sections whose max-height is now JS-measured to their own exact
     scrollHeight (see _fitAccordionSection in detailModalNotes.js) can end up a sub-pixel off
     from the real rendered height (scrollHeight rounds to a whole pixel; layout doesn't), which
     is just enough overflow for the browser to draw a scrollbar for content that isn't actually
     meant to scroll within this element at all. */
  scrollbar-width: none;
  transform: translateY(0);
  /* Transitioned TO on open — "emphasized" decelerate (vs. plain "standard" elsewhere in this
     file) settles more gently into its final size at the tail end, reading as a softer finish
     rather than an abrupt stop. */
  transition: max-height 0.3s var(--m3-emphasized-decelerate), margin 0.3s var(--m3-emphasized-decelerate), transform 0.3s var(--m3-emphasized-decelerate);
}

.detail-accordion-collapsible.open::-webkit-scrollbar {
  display: none; /* Chrome/Safari/Edge (Chromium) */
}

/* MY NOTES/SONG LIST specifically (not the shared base rule above, which Albums/Web Links/Queue
   also use and have no associated toolbar to wait for): closing should let the note toolbar (see
   .detail-body--editing-note .detail-note-toolbar) finish fading out FIRST, then slide shut —
   mirroring the open sequence (section slides open, then the toolbar fades in) in reverse, rather
   than both animating at once. :not(.open) scopes this to only the closing direction — opening
   stays immediate, unaffected. */
#detail-notes-list.detail-accordion-collapsible:not(.open),
#detail-tracklist.detail-accordion-collapsible:not(.open) {
  transition-delay: 0.2s;
}

.detail-albums-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Each row is ~44px (32px thumb + 6px top/bottom padding) plus a 2px row gap — sized to show
   about 3 rows before the list's own scrollbar kicks in, rather than the much taller generic
   accordion cap. Extra classes for specificity over .detail-accordion-collapsible.open. */
.detail-albums-list.detail-accordion-collapsible.open {
  max-height: 168px;
}

/* Musician, zero known albums yet — just the single "Fetch Albums" row (detailModalSummary.js),
   not up to 3 real album rows — reported live: the shared 168px cap left a large empty gap below
   the button before Web Links, since max-height ends up read as reserved space during this
   section's open transition rather than shrinking to the actual (much shorter) content. Four
   classes (vs. the rule above's three) so this reliably wins regardless of source order. */
.detail-albums-list.detail-albums-list--empty-fetch.detail-accordion-collapsible.open {
  max-height: 56px;
}

/* Summary content (Book/Show/Movie/Game) reuses this same element/id, but is prose rather than
   fixed-height rows. Generously large rather than a tight cap — like the Chapters fix, this
   defers to .detail-body's own scroll (flex: 1 + flex-shrink: 0 children) instead of showing its
   own secondary scrollbar (inherited from .detail-accordion-collapsible.open's overflow-y:auto). */
.detail-albums-list--summary.detail-accordion-collapsible.open {
  max-height: 2400px;
  margin-top: -11px;
  margin-bottom: -10px;
}

.detail-album-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px 2px;
  background: none;
  border: none;
  border-radius: 10px;
  cursor: pointer;
  text-align: left;
  color: var(--text-primary);
  font-size: 13px;
  width: 100%;
  transition: background 0.15s;
}

.detail-album-row:hover {
  background: var(--hover-bg);
}

.detail-album-row-thumb {
  width: 38px;
  height: 38px;
  border-radius: 5px;
  object-fit: cover;
  flex-shrink: 0;
  background: var(--border);
}

.detail-album-row-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.detail-album-row--see-all {
  color: var(--primary);
  font-weight: 600;
  justify-content: center;
}

.detail-album-row--fetch {
  background: var(--hover-bg);
  color: var(--text-primary);
  font-weight: 600;
  justify-content: center;
  padding: 10px 2px;
}
.detail-album-row--fetch:hover {
  background: var(--primary);
  color: #fff;
}
.detail-album-row--fetch:disabled {
  color: var(--text-muted);
  cursor: default;
}

.detail-tracklist {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.detail-tracklist.detail-accordion-collapsible.open {
  /* No nested scrollbar here and no growing the modal — max-height is just generously large
     enough that it never actually constrains this (open-ended, chapters can keep being added)
     list, so it renders at its natural height and any overflow scrolls via the existing outer
     .detail-body { overflow-y: auto; }, not a separate inner scroll region. */
  max-height: 2400px;
  margin-top: -11px;
  margin-bottom: -10px;
}

.detail-tracklist-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px 2px;
  color: var(--text-primary);
  font-size: 13px;
  cursor: pointer;
}

.detail-tracklist-number {
  flex-shrink: 0;
  width: 22px;
  text-align: center;
  color: var(--text-muted);
  font-size: 13px;
}

.detail-tracklist-number--has-note {
  color: var(--primary);
  font-weight: 700;
}

.detail-tracklist-title--has-note {
  color: var(--primary);
  font-weight: 700;
}

.detail-tracklist-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* While actively being renamed (see _startRenamingTitle, detailModalNotes.js) — the ellipsis
   clipping above would otherwise hide whatever's being typed past the row's normal width. Color
   forced back to the plain text color (and !important'd) regardless of --has-note or the row's
   own :hover rule below, both of which would otherwise still tint it purple while typing. */
.detail-tracklist-title--editing {
  overflow: visible;
  white-space: normal;
  outline: none;
  cursor: text;
  color: var(--text-primary) !important;
  caret-color: var(--text-primary);
  padding: 2px 7px;
  /* Left/top/bottom cancel the padding's own footprint so the row doesn't visibly shift/reflow the
     moment editing starts (same trick as the accordion rows' own margin/gap cancellation
     elsewhere). Right is negative too, but less so — the row's own flex `gap: 12px` already
     provides 12px before the plus/circle icon next to it; pulling in 6px of that nets a 6px true
     gap instead, rather than stacking on top of it for 18px. */
  margin: -2px -6px -2px -7px;
  border-radius: 5px;
  background: var(--search-bg); /* same light-gray input-field treatment as the search box, theme-aware */
}

/* The field is cleared to empty on entry (see _startRenamingTitle, detailModalNotes.js) — this is
   its placeholder, same convention as the note body's own data-placeholder ghost text just below,
   except hardcoded (every row's rename field shows the same "Rename…" prompt) rather than driven
   by a per-row data attribute. Disappears the moment the user types the first character, same as
   any other placeholder. */
.detail-tracklist-title--editing:empty::before {
  content: 'Rename…';
  color: var(--text-muted);
  pointer-events: none;
}

/* Belt-and-suspenders: if the user manually selects text instead of just typing over the
   collapsed caret above, the browser's default selection highlight would otherwise be a jarring
   blue box on top of the gray editing background. Blends it away instead: same text color, so
   selected title text still just reads as plain white text. */
.detail-tracklist-title--editing::selection {
  background: var(--search-bg);
  color: var(--text-primary);
}

.detail-tracklist-rename {
  display: none;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 0;
  margin-left: -60px;
  margin-right: 12px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  opacity: 0.6;
  transition: color 0.15s, opacity 0.15s;
}

/* Rename only shows once the user has tapped the plus and expanded that specific row's note
   (Chrome-only extension, so :has() is safe to rely on) — not a permanent fixture of every row. */
.detail-tracklist-item:has(.detail-tracklist-notes-input.open) .detail-tracklist-rename {
  display: flex;
}

/* ...but hides again the moment renaming actually starts — no reason to keep showing the pencil
   that got clicked to get here while its own title is already being edited. !important because
   :has()'s specificity comes from its argument, and .detail-tracklist-notes-input.open (2 classes)
   above outweighs .detail-tracklist-title--editing (1 class) here despite coming later in the
   file — without it, the "show while open" rule above would keep winning even while editing. */
.detail-tracklist-row:has(.detail-tracklist-title--editing) .detail-tracklist-rename {
  display: none !important;
}

.detail-tracklist-rename svg {
  display: block;
  /* Overrides RENAME_ICON's own inline width="16"/height="16" attributes (detailModalNotes.js) —
     16px × the modal's overall 1.2x scale-up. */
  width: 19px;
  height: 19px;
}

.detail-tracklist-row:hover .detail-tracklist-rename,
.detail-tracklist-rename:hover {
  opacity: 1;
}

.detail-tracklist-rename:hover {
  color: var(--primary);
}

.detail-tracklist-item {
  display: flex;
  flex-direction: column;
  /* Same fix as .dash-card/the Embed Builder's grid panels elsewhere in this codebase: a flex
     item defaults to min-width: auto, not 0, so it can refuse to shrink below its content's
     natural width and overflow its container even with width: 100% set on that content —
     letting a touch drag pan the whole accordion horizontally (reported live on an iPhone,
     touch-action alone on .detail-body didn't stop it since the actual overflow lived here,
     deeper than that ancestor). */
  min-width: 0;
  /* REGRESSION, tried and reverted: an overflow-x: hidden "hard backstop" was added here
     alongside min-width above, and broke something worse — setting only one overflow axis to a
     non-visible value forces the CSS spec to compute the other axis as 'auto' too, and a flex
     item with any non-'visible' overflow gets its "automatic minimum size" reset from its
     content's real min-content size down to a hard 0. That let this row collapse to zero height
     under any parent flex-shrink pressure, and detailModalNotes.js's _fitAccordionSection() (which
     measures scrollHeight via requestAnimationFrame to set the section's max-height) captured
     that already-collapsed state and locked the whole My Notes/Chapters section at it — reported
     live as "all the chapters are gone". min-width: 0 alone (no overflow-x here) is enough for
     the original bleed fix; don't re-add overflow-x/-y on this element without re-checking this. */
}

/* Song List has no trailing "+Add" button (unlike My Notes/Chapters) to naturally carry space
   before the divider below it — add it directly to the last row instead. */
#detail-tracklist .detail-tracklist-item:last-child {
  margin-bottom: 24px;
}

.detail-tracklist-favorite {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  cursor: pointer;
  color: var(--text-muted);
  transition: color 0.15s;
}

.detail-tracklist-favorite svg {
  display: block;
  width: 18px;
  height: 18px;
}

/* My Notes/Chapters' own "open a note" icon (the plus, NOTE_OPEN_ICON) only — Song List's per-
   track pencil (SONG_NOTE_ICON) keeps the plain icon-only treatment, no circle. border uses
   currentColor so it automatically follows the same muted/active/hover color transitions the
   icon itself already gets, no separate state rules needed. 15% smaller than the rest of the
   1.2x-scaled desktop icons, per explicit feedback — scoped to this modifier specifically (not
   the shared .detail-tracklist-favorite svg rule above) so Song List's plain pencil is unaffected. */
.detail-tracklist-favorite--circle {
  width: 15px;
  height: 15px;
  border: 1.5px solid currentColor;
  border-radius: 50%;
}
.detail-tracklist-favorite--circle svg {
  width: 12px;
  height: 12px;
}

.detail-tracklist-favorite--active {
  color: var(--primary);
}

/* Inline voice-note marker — an <img data-audio-id> embedded directly in a note's rich-text
   content (see voiceNotes.js), same mechanism as a pasted image, just always this one fixed icon.
   Sized/aligned to read as a glyph sitting in the text flow, not an oversized inline image. */
.detail-tracklist-notes-input img[data-audio-id] {
  width: 18px;
  height: 18px;
  vertical-align: middle;
  margin: 0 2px;
  cursor: pointer;
  border-radius: 4px;
  background: var(--primary-bg);
  padding: 2px;
}
.detail-tracklist-notes-input img[data-audio-id]:hover { opacity: 0.75; }

/* Hovering the title or the pencil turns both purple together, as a preview of the row's
   tap action — separate from --active/--has-note, which is the persistent "has content" state. */
.detail-tracklist-row:hover .detail-tracklist-title,
.detail-tracklist-row:hover .detail-tracklist-favorite {
  color: var(--primary);
}

/* When the row is already purple (has a note), hovering can't turn it "more purple" — dim it
   instead, so hover still gives feedback. More specific than the plain --has-note/--active
   rules above, and than the two-class hover rule just above, so it wins on top of both. */
.detail-tracklist-row:hover .detail-tracklist-number--has-note,
.detail-tracklist-row:hover .detail-tracklist-title--has-note,
.detail-tracklist-row:hover .detail-tracklist-favorite--active {
  opacity: 0.65;
}

.detail-tracklist-notes-input {
  max-height: 0;
  overflow: hidden;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  margin: 0;
  /* Left inset matches .detail-tracklist-row's own 2px left padding — same left edge the row's
     number badge starts at, not the 34px that used to skip past the number+gap to align under
     the title instead. Requested for both desktop and mobile, so no separate mobile override. */
  padding: 0 2px;
  border: none;
  outline: none;
  resize: none;
  background: none;
  color: var(--text-secondary);
  font-size: 13px;
  font-family: inherit;
  line-height: 1.4;
  white-space: pre-wrap;     /* a bare contenteditable div doesn't wrap/preserve whitespace like a textarea natively does */
  overflow-wrap: break-word; /* prevent long unbroken tokens from overflowing the row width */
  transition: max-height 0.25s var(--m3-standard-accelerate), padding 0.25s var(--m3-standard-accelerate);
}

.detail-tracklist-notes-input.open {
  /* max-height: none removes the base rule's max-height: 0 cap — detailModal.js's
     fitTracklistNote() sets an explicit height (from scrollHeight) instead, so the box expands
     to exactly fit each note's content. .detail-body (flex: 1 + flex-shrink: 0 children) is the
     real scroll region, so a note taller than the modal's available space scrolls the body
     rather than clipping. */
  max-height: none;
  overflow: hidden;
  padding: 5px 2px 18px 2px;
}

/* Images inserted via the My Notes toolbar's image button (noteSanitizer.js allows a single
   <img src> through) — constrained to the note row's own width and given the app's standard
   rounded-corner treatment so a large/wide source image never overflows the note row or breaks
   its layout. display:block + margin-top clears any inline gap below the image and separates it
   from surrounding note text. */
.detail-tracklist-notes-input img {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: 8px;
  margin-top: 6px;
}


.detail-tracklist-notes-input:empty::before {
  /* contenteditable has no native placeholder attribute */
  content: attr(data-placeholder);
  color: var(--text-muted);
  pointer-events: none;
}

/* var(--text-primary) rather than inherit — noticeably brighter than the note's own base
   var(--text-secondary) color (in dark mode: #E8EAED vs #9AA0A6), so bold text actually reads as
   emphasized instead of just heavier-weight same-gray. */
.detail-tracklist-notes-input b { font-weight: 700; color: var(--text-primary); }
.detail-tracklist-notes-input mark {
  background: #FDE047; /* plain yellow highlighter, not themed — requested explicitly over the app's purple accent */
  color: #1a1a1a; /* fixed dark text — var(--text-secondary)/--text-primary would be near-white in dark mode, illegible on yellow */
  border-radius: 2px;
  padding: 0 2px;
}
.detail-tracklist-notes-input ul { margin: 5px 0; padding-left: 24px; }
.detail-tracklist-notes-input li { margin: 2px 0; }
/* Standard web-link styling (pasted links, and plain-text URLs noteSanitizer.js auto-linkifies) —
   deliberately the familiar blue+underline every browser/webpage defaults to, not the app's own
   purple accent, so a link reads as a link at a glance. Same blue in both themes; it has enough
   contrast against both the light and dark note background to stay legible either way. */
.detail-tracklist-notes-input a {
  color: #3B82F6;
  text-decoration: underline;
  cursor: pointer;
}
.detail-tracklist-notes-input a:hover { color: #2563EB; }

.detail-tracklist-row--status {
  color: var(--text-muted);
  font-style: italic;
}

.detail-accordion-summary-text {
  padding: 7px 2px;
  font-size: 13px;
  color: var(--text-secondary);
  line-height: 1.5;
}

.detail-tracklist-add-chapter {
  align-self: flex-start;
  margin-top: 5px;
  margin-bottom: 24px;
  padding: 5px 2px;
  background: none;
  border: none;
  color: var(--primary);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.detail-tracklist-add-chapter:hover {
  text-decoration: underline;
}



.detail-streaming {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  margin-top: auto;
  padding-top: 18px;
  width: 100%;
}

.detail-streaming--tight {
  /* The preceding collapsed accordion body (My Notes/Albums) already cancels the .detail-body
     gap above it via its own negative margin — don't cancel it a second time here. */
  margin-top: 0;
  padding-top: 0;
}

.streaming-header {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 24px;
  width: 100%;
}

.streaming-label {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #fff;
  background: rgba(255,255,255,0.12);
  border-radius: 24px;
  padding: 5px 12px 5px 14px;
  width: fit-content;
  cursor: pointer;
  user-select: none;
  transition: background 0.15s;
}

.streaming-label:hover { background: rgba(255,255,255,0.2); }

.streaming-label svg {
  transition: transform 0.2s ease;
}

.detail-streaming.open .streaming-label svg {
  transform: rotate(90deg);
}

.detail-streaming.open .detail-accordion-chevron {
  transform: rotate(90deg);
}

.detail-streaming .detail-accordion-header {
  width: 100%;
}

.streaming-links-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.25s ease, margin-top 0.25s ease;
  margin-top: 0;
}

.detail-streaming.open .streaming-links-wrap {
  /* Generously large rather than a tight cap — same as Book Summary/Chapters, defers to
     .detail-body's own scroll instead of hard-clipping platform pills that wrap to 3+ rows. */
  max-height: 2400px;
  margin-top: 10px;
}

.streaming-link-btn {
  /* Trimmed from 6px/14px horizontal padding (and the wrap's own gap, above, from 7px) per direct
     request — Wikipedia (the last pill, always appended after the category's own platform list)
     was wrapping to its own row below YouTube Music/YouTube instead of sharing their row; flex-wrap
     packs pills by whatever actually fits the container width, so shaving a little width off every
     pill was the lever available to make three fit together instead of two. */
  padding: 6px 11px;
  border-radius: 24px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-primary);
  font-size: 13px;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

.streaming-link-btn:hover {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}

.vc-why-title {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #ffffff;
  margin-bottom: 5px;
}

.vc-why-tooltip-text {
  display: block;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  color: rgba(255, 255, 255, 0.85);
  line-height: 1.5;
}

/* Callout attached to the sponsored-statement badge itself — shown on hover instead of a
   permanently-visible "Why VoteCraft Recommends" section at the bottom of the modal. Opens
   downward (below the badge, which itself sits at the bottom edge of the featured image) rather
   than upward, so it never overlaps the artwork. */
.vc-sponsored-tooltip {
  position: absolute;
  top: 100%;
  left: 50%;
  margin-top: 10px;
  width: 300px;
  max-width: 80vw;
  box-sizing: border-box;
  background: var(--primary);
  border: 1px solid var(--primary);
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 15px;
  white-space: normal; /* .vc-sponsored-tag (the parent) sets nowrap for its own label — reset it here so this text wraps instead of overflowing */
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -4px);
  transition: opacity 0.15s, transform 0.15s, visibility 0.15s;
  z-index: 5;
}

.vc-sponsored-tag--overlay:hover .vc-sponsored-tooltip {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

/* Pointer tail — two stacked triangles (border color behind, surface color on top, inset 1px)
   so the tail reads as a continuation of the box's own border+fill, not a separate shape.
   Centered above the box: a border-drawn triangle centered on "left" needs margin-left equal
   to -border-width (its own half-width), which — unlike anchoring both from the same "right"
   offset — naturally keeps both triangles' apexes aligned even though they have different
   border-widths (12px vs 11px). Tail sits at the TOP of the box now (pointing up at the badge
   above it), the mirror image of the upward-opening version this replaced. */
.vc-sponsored-tooltip::before,
.vc-sponsored-tooltip::after {
  content: '';
  position: absolute;
  bottom: 100%;
  left: 50%;
  border-style: solid;
  border-color: transparent;
}

.vc-sponsored-tooltip::before {
  border-width: 14px;
  margin-left: -14px;
  border-bottom-color: var(--primary);
}

.vc-sponsored-tooltip::after {
  border-width: 13px;
  margin-left: -13px;
  margin-bottom: -1px;
  border-bottom-color: var(--primary);
}

.vc-sponsored-tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #fff;
  background: var(--primary);
  border: none;
  border-radius: 24px;
  padding: 5px 12px 7px;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s, transform 0.1s;
}

.vc-sponsored-tag:hover,
.vc-sponsored-tag:active {
  transform: translateY(-1px);
  background: #fff;
  color: var(--primary);
}

.vc-sponsored-tag--overlay {
  position: absolute;
  left: 0;
  right: 0;
  width: fit-content;
  margin: 0 auto;
  bottom: -13px;
  /* Was 2 — raised above the sticky title row/note toolbar's z-index: 4 (added for the sticky-
     title feature). A child's own z-index (the tooltip's 5) can't out-rank a sibling outside its
     own stacking context; only this element's z-index (its context's "entry" value) does, so the
     callout was rendering underneath the title/toolbar despite its own higher z-index. */
  z-index: 6;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
}

.image-lightbox-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.9);
  z-index: 600;
  align-items: center;
  justify-content: center;
  cursor: zoom-out;
}

.image-lightbox-overlay.open {
  display: flex;
}

.image-lightbox-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

#image-lightbox-img {
  max-width: 90vw;
  max-height: 80vh;
  object-fit: contain;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}

.detail-load-art-btn {
  position: absolute;
  bottom: -41px;
  right: 0;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-secondary);
  font-size: 12px;
  font-weight: 600;
  border-radius: 14px;
  padding: 5px 12px;
  cursor: pointer;
}
.detail-load-art-btn:hover { color: var(--primary); border-color: var(--primary); }
.detail-load-art-btn:disabled { opacity: 0.6; cursor: default; }

/* Overrides for the same button reused inside the lightbox, directly below the full-size image
   (see .image-lightbox-content) — needs normal document flow instead of the header thumbnail's
   absolute corner positioning, and reads better a bit larger. The compound selector (two classes)
   beats .detail-load-art-btn's own rules above regardless of source order. */
.detail-load-art-btn.image-lightbox-load-art-btn {
  position: static;
  font-size: 13px;
  padding: 7px 16px;
  border-radius: 16px;
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
  font-weight: 700;
}
.detail-load-art-btn.image-lightbox-load-art-btn:hover,
.detail-load-art-btn.image-lightbox-load-art-btn:active {
  background: #fff;
  border-color: #fff;
  color: var(--primary);
  filter: none;
}

.image-lightbox-arrow {
  display: none;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  border: none;
  background: none;
  color: rgba(255, 255, 255, 0.85);
  font-size: 40px;
  line-height: 1;
  cursor: pointer;
  z-index: 2;
  padding: 8px;
}
.image-lightbox-prev { left: 16px; }
.image-lightbox-next { right: 16px; }
.image-lightbox-arrow:hover { color: #fff; }

.image-lightbox-thumbs {
  display: none;
  position: absolute;
  bottom: 24px;
  left: 0;
  right: 0;
  gap: 8px;
  overflow-x: auto;
  padding: 4px 24px;
  justify-content: center;
  scrollbar-width: none;
}
.image-lightbox-thumbs::-webkit-scrollbar { display: none; }

.image-lightbox-thumb {
  flex-shrink: 0;
  width: 56px;
  height: 56px;
  border-radius: 6px;
  overflow: hidden;
  border: 2px solid transparent;
  padding: 0;
  cursor: pointer;
  opacity: 0.6;
}
.image-lightbox-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.image-lightbox-thumb.active { border-color: var(--primary); opacity: 1; }
.image-lightbox-thumb:hover { opacity: 1; }

.image-lightbox-overlay--gallery .image-lightbox-arrow,
.image-lightbox-overlay--gallery .image-lightbox-thumbs {
  display: flex;
}

.video-lightbox-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.9);
  z-index: 600;
  align-items: center;
  justify-content: center;
}
.video-lightbox-overlay.open {
  display: flex;
}
.video-lightbox-content {
  width: min(90vw, 960px);
  aspect-ratio: 16 / 9;
}
#video-lightbox-iframe {
  width: 100%;
  height: 100%;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}
.video-lightbox-close {
  position: absolute;
  top: 20px;
  right: 20px;
}

/* ===== MOBILE ===== */
/* Same breakpoint as the rest of the app (misc.css/cards.css). Desktop's mouse-precision sizing
   (thin accordion rows, small icon buttons) becomes hard to tap accurately on a touchscreen —
   these widen the actual hit areas to something closer to the ~44px minimum recommended touch
   target, without changing anything about the desktop layout below 768px wide. */
@media (max-width: 768px) {
  /* Desktop pins this to a narrow 374px card with a 720px height cap — comfortable next to a
     mouse-driven grid, but it reads as a small floating box with dead space around it on a phone.
     Nearly fills the viewport instead, same spirit as the sidebar becoming a full-height drawer
     in its own mobile CSS. Padding trimmed too, so the extra width actually reaches the content
     rather than just widening the existing side margins. Can afford to go this close to the true
     edges (leaving barely any backdrop to tap) now that .detail-modal-close below gives mobile a
     real, dedicated close affordance instead of relying on tapping outside the modal. */
  .modal.detail-modal {
    /* % instead of vw: vw is a longstanding, well-documented mobile Safari inconsistency — it's
       calculated slightly differently from the real visible viewport (more room than actually
       shows), so 98vw can render a hair wider than 98% of the true screen, just enough for a
       touch to drag the whole modal a few px left/right (reported live, still present after the
       inner accordion/toolbar overflow was already fixed). % is calculated against
       #detail-modal-overlay's own box instead (position: fixed; inset: 0 — genuinely matches the
       real viewport, no vw-style discrepancy), which sidesteps the quirk entirely. */
    width: 98%;
    max-width: 98%;
    /* dvh, not vh: vh is fixed against the full screen and ignores the on-screen keyboard, so the
       modal stayed the same (tall) height once the keyboard opened — bringing the typing caret
       into view then had to scroll past the sticky note toolbar, pushing it above the visible
       area entirely (reported live: "tools get hidden above the screen" while typing a note).
       dvh tracks the actual visible viewport, shrinking with the keyboard, so there's no longer a
       tall hidden-above region for that scroll to push the toolbar into. */
    /* A firm height, not a max-height — desktop shrink-wraps to its content (see the base rule's
       own comment), but on mobile that meant the whole modal visibly grew/shrank as accordion
       sections opened and closed (reported live). Pinning a firm height keeps the modal itself
       static; .detail-body's flex: 1 + overflow-y: auto (below) absorbs any content taller than
       what's left, scrolling internally instead of resizing the modal around it. */
    height: 98dvh;
    padding: 40px 26px;
  }

  /* .detail-note-toolbar::before bleeds the toolbar's purple background past its own box via
     position: absolute + negative left/right offsets, out to the modal's true edge (a pure
     desktop visual-polish touch — see its own comment above in this file). Re-matching that
     negative offset to this file's mobile padding (first tried here) still left the drag/
     "modal too wide" bug reported live only partially fixed — this exact "absolutely positioned
     background bled out via a hardcoded pixel offset" mechanism has now caused two rounds of the
     same class of bug on mobile, so rather than keep re-deriving the exact right offset,
     mobile just doesn't bleed at all: the pseudo-element is turned off and the toolbar paints
     its own background directly, contained within its own row. Costs the edge-to-edge/rounded-
     corner touch on mobile only; removes the whole mechanism as a possible overflow source. */
  .detail-note-toolbar::before {
    content: none;
  }
  .detail-note-toolbar {
    background: var(--primary);
    /* Tighter than the base rule's 9px 12px — that plus the buttons' own height (below) made the
       whole bar read as too tall on a phone (reported live). */
    padding: 5px 12px;
  }

  /* Desktop deliberately keeps this a fixed 278px regardless of the modal's own width (see the
     base rule's comment) — but the modal itself is now nearly full-screen on mobile, so the same
     small fixed image read as tiny relative to all that space. Ties it back to the modal's width
     here instead (55% of it, not the full width — 100% read as too large), same as a typical
     mobile "detail view" hero image. Smaller than an even split with the content below on purpose:
     the accordion section titles (My Notes/Summary/Web Links/Add to Queue) need to all be visible
     the moment the modal opens, without the image eating the height budget they need (reported
     live — see .detail-body's own scroll region, which still exists for content taller than this).
     max-height mirrors that same 55%-of-content-width formula as an explicit calc (not none/
     percentage) rather than an unbounded cap: percentage max-height can't resolve against this
     element's auto-height container, and focus mode's collapse-to-0 transition needs a real
     numeric "from" value to animate smoothly from either way. */
  .detail-image-wrap {
    width: 55%;
    max-height: calc((98vw - 52px) * 0.55);
  }

  /* No focus-mode height override needed here (unlike the desktop rule above it in this file) —
     the base .modal.detail-modal rule above already pins a firm 98dvh on mobile at all times, so
     there's nothing for focus mode to additionally override. */

  /* Hidden on desktop (closing via backdrop click/Escape is enough there, and the modal is small
     enough to always have backdrop to spare) — shown here since the mobile modal above now leaves
     almost none. Left corner, not the existing .modal-x-close's default right — the top-right
     corner is already the edit/bookmark/favorite icon column. */
  .detail-modal-close {
    display: flex;
    align-items: center;
    justify-content: center;
    top: 10px;
    left: 10px;
    right: auto;
    width: 32px;
    height: 32px;
    color: #fff;
    background: rgba(0, 0, 0, 0.4);
    border-radius: 50%;
    font-size: 16px;
  }

  /* Sidebar's own mobile CSS already does the same padding-for-height trick for its rows
     (.sidebar-items-scroll .sidebar-item, misc.css) — mirrored here so every accordion row (My
     Notes, Albums/Song List, Web Links, Add to Queue) grows from a ~23-26px tap height to ~44px. */
  .detail-accordion-header {
    padding: 10px 2px;
  }

  /* 40px (up from desktop's own 36px) was meant to grow the tap target closer to the ~44px
     recommended minimum, but read as too tall/bulky for this specific row (reported live) —
     dropped below even desktop's 36px here, prioritizing a tighter bar over the extra tap-target
     margin for this particular toolbar (every other mobile touch-target bump elsewhere in this
     file is untouched). */
  .detail-note-toolbar-btn {
    width: 34px;
    height: 34px;
  }

  /* The circular "open this note" button (My Notes/Chapters rows) — visibly bigger, not just a
     padded invisible hit area, since it's the primary tap target on every row. The smaller rename
     pencil next to it is left alone; its size/position were tuned precisely enough this session
     that padding it out on mobile risks reopening that whole round of feedback. */
  .detail-tracklist-favorite--circle {
    width: 30px;
    height: 30px;
  }
  .detail-tracklist-favorite--circle svg {
    width: 17px;
    height: 17px;
  }

  /* ----- Text sizes: everything below reads noticeably smaller on a phone than the same pixel
     count does on a desktop monitor viewed from further away — bumped up across the board rather
     than case-by-case (~10% on top of the first mobile pass, per follow-up feedback). Values
     chosen to still fit their existing layout (checked against the tightest case,
     .btn-detail-website's fixed-width pill) rather than a single flat ratio. ----- */
  .detail-title { font-size: 19px; }
  .detail-notes { font-size: 15px; }
  .detail-accordion-header { font-size: 13px; }
  .detail-album-row { font-size: 15px; }
  .detail-tracklist-row { font-size: 15px; }
  .detail-tracklist-number { font-size: 14px; }
  .detail-tracklist-notes-input { font-size: 15px; }
  .detail-accordion-summary-text { font-size: 15px; }
  .detail-tracklist-add-chapter { font-size: 14px; }
  .streaming-label { font-size: 13px; }
  .streaming-link-btn { font-size: 14px; }
  .btn-detail-website {
    font-size: 14px;
  }

  /* ----- Icons/chevrons: same reasoning as the text sizes above — small enough to read as fine
     detail on a desktop monitor becomes hard to make out at arm's length on a phone. ----- */
  .detail-accordion-icon,
  #detail-albums-accordion-header .detail-accordion-icon,
  #detail-notes-accordion-header .detail-accordion-icon,
  #standalone-queue-bookmark {
    width: 24px;
    height: 24px;
  }
  .detail-accordion-chevron {
    width: 19px;
    height: 19px;
  }
  .detail-tracklist-favorite svg {
    width: 18px;
    height: 18px;
  }
  .detail-note-toolbar-btn svg {
    width: 22px;
    height: 22px;
  }

  /* The "⚡ Your Statement" sponsored badge, ~10% larger as a whole (font-size, padding, and the
     gap to its icon all scaled together, rather than just the text alone). */
  .vc-sponsored-tag {
    gap: 6px;
    font-size: 12px;
    /* 10px more on each side than the desktop-equivalent 11px, per direct request (+20px overall). */
    padding: 4px 21px 7px;
  }

  /* Desktop centers this via left/right: 0 + width: fit-content + margin: 0 auto, but that combo
     is a known WebKit quirk on iOS — Safari can resolve the fit-content width against the full
     0-to-100% span left/right:0 defines rather than the badge's own intrinsic content size, so
     margin:auto ends up centering a box that's already stretched full-width instead of the actual
     pill (reported live: the badge visibly off-center under the photo). left: 50% +
     translateX(-50%) sidesteps that — it centers on the badge's real rendered width, computed
     after layout, with no fit-content sizing step for Safari to get wrong. */
  .vc-sponsored-tag--overlay {
    left: 50%;
    right: auto;
    width: max-content;
    margin: 0;
    transform: translateX(-50%);
  }
}

