/* =====================================================================
     🎵 Music App
     ===================================================================== */

/* Both of these were dark for the Spotify embed that used to load into this
   window. The app is now the local player alone, which paints itself
   var(--panel), so the chrome underneath matches it rather than showing black
   anywhere the player does not reach. */
.music-player {
  background-color: var(--panel);
  padding: 0;
}

#music.window {
  background-color: var(--panel);
}

/* Lifted out of the inline style on the element so the minimize hint below can
   raise it. An inline padding cannot be overridden by a stylesheet at all, and
   reserving room for the bubble is the one thing that has to change it.

   #music carries no height, so this whole column is what sizes the window.
   Every figure below is measured against the two places it has to fit: a 320px
   window on the desktop, and a 320x568 phone where the window starts at y=50
   and the dock's top edge is at y=468. The shelf added about 13px to what the
   single-record player used to take, which is the slack that was there. */
#local-music-view {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 20px 22px 18px;
  background: var(--panel);
  color: #000;
  text-align: center;
  box-sizing: border-box;
  overflow-y: auto;
}

/* The turntable. The disc art is a circle on a transparent square, so the
   platter shows through its corners as it turns. */
.music-vinyl-wrap {
  margin: 0 0 14px;
  padding: 8px;
  line-height: 0;
  border-radius: 50%;
  border: 3px solid #000;
  background: #1a1a1a;
}

/* Nothing is clipped: the art carries its own alpha, and a 50% radius on a
   501x498 file is an ellipse that would shave the disc's own edge. The
   animation always runs and play-state is what starts and stops it, so Pause
   freezes the record mid-turn rather than snapping it back to the top. */
.music-vinyl {
  display: block;
  width: 128px;
  height: auto;
  user-select: none;
  -webkit-user-drag: none;
  animation: vinyl-spin 8s linear infinite;
  animation-play-state: paused;
}

.music-vinyl.spinning {
  animation-play-state: running;
}

@keyframes vinyl-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.music-title {
  margin: 0 0 2px;
  font-family: "Pixelify Sans", sans-serif;
  font-size: 22px;
  line-height: 1.1;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.music-subtitle {
  margin: 0 0 12px;
  font-family: "VT323", monospace;
  font-size: 17px;
  line-height: 1.1;
  color: #333;
}

.music-controls {
  display: flex;
  gap: 10px;
  margin: 0 0 14px;
}

.music-btn {
  display: inline-block;
  min-width: 88px;
  font-family: 'VT323', monospace;
  font-size: 19px;
  padding: 6px 22px;
  background: #e0e0e0;
  border: 3px solid #000;
  box-shadow: 5px 5px 0 rgba(0,0,0,0.9);
  cursor: pointer;
  transition: transform 0.1s ease, box-shadow 0.1s ease;
}

.music-btn:active {
  transform: translate(2px, 2px);
  box-shadow: 3px 3px 0 rgba(0,0,0,0.9);
}

/* A disc with no file behind it yet, and the whole player once the theme takes
   the speakers. Disabled rather than removed: a control that disappears reads
   as a window that failed to draw, and the one thing this state has to say is
   that the player stopped answering. The shadow shrinks and the press travel
   is taken away, so it is dead to the touch as well as to the eye. */
.music-btn:disabled,
.music-btn:disabled:active {
  transform: none;
  opacity: 0.55;
  box-shadow: 2px 2px 0 rgba(0,0,0,0.5);
  cursor: not-allowed;
}

/* =====================================================================
         💿 The Shelf
   ===================================================================== */

.music-shelf {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  width: 100%;
}

/* Reads "Discs" normally and carries the locked line under the theme, so the
   takeover explains itself without the window growing a row to say it in. */
.music-shelf-note {
  margin: 0;
  font-family: "VT323", monospace;
  font-size: 14px;
  line-height: 1.1;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #555;
}

/* One row, always. Seven 32px records and their gaps come to 260px, which is
   inside the 270 a 320px desktop window leaves and the 271 a 320px phone
   leaves — so this only ever scrolls on a viewport narrower than either, and
   the overflow is a safety net rather than the layout.

   width: fit-content with auto margins rather than justify-content: center,
   because a centred flex row that does overflow pushes its first items off the
   left edge where no amount of scrolling can reach them. */
.music-disc-strip {
  display: flex;
  flex-wrap: nowrap;
  gap: 4px;
  width: fit-content;
  max-width: 100%;
  margin: 0 auto;
  /* Room for the selected ring, which is an outline and so draws outside the
     chip's own box — without this the scroll container would clip it. */
  padding: 4px 6px;
  overflow-x: auto;
  scrollbar-width: none;
}

.music-disc-strip::-webkit-scrollbar {
  display: none;
}

.music-disc {
  display: block;
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  line-height: 0;
  cursor: pointer;
}

/* The seventh disc is hidden rather than absent until the theme lands, and the
   rule above outranks the user agent's [hidden] on its own. */
.music-disc[hidden] {
  display: none;
}

.music-disc img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
}

/* Selection is an outline in currentColor rather than a border or a fill.
   Both desktop themes repaint borders, backgrounds and shadows on anything
   button-shaped and leave outlines alone, and currentColor is already the
   right ink in all three themes because #local-music-view sets it. */
.music-disc.selected {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

.music-disc:focus-visible {
  outline: 2px dashed currentColor;
  outline-offset: 2px;
}

/* The shelf under the takeover. Still drawn, so the visitor can see the six
   records they are no longer allowed to reach. */
.music-disc.is-locked {
  opacity: 0.3;
  cursor: not-allowed;
}

/* =====================================================================
         📱 Phone
   =====================================================================
   Here rather than in css/responsive.css for the same reason the guardian's
   reflow lives in css/trash.css: this is the app's own content tuning, and
   only the window's placement is shared. css/responsive.css still owns
   #music's left, top and width, and still gives it no height.

   At 320x568 the window runs 50 -> 407 with these figures and the dock's top
   edge is at 468, so there is about 60px of clearance. Anything added to this
   column has to be re-measured against that. */
@media (max-width: 600px) {
  #local-music-view {
    padding: 16px 12px 14px;
  }

  .music-vinyl-wrap {
    margin-bottom: 12px;
    padding: 6px;
  }

  .music-vinyl {
    width: 104px;
  }

  .music-title {
    font-size: 20px;
  }

  .music-subtitle {
    font-size: 15px;
    margin-bottom: 10px;
  }

  .music-controls {
    margin-bottom: 12px;
  }

  .music-btn {
    min-width: 78px;
    font-size: 17px;
    padding: 6px 18px;
  }

  .music-shelf-note {
    font-size: 13px;
  }

  /* Seven 30px records and their gaps come to 246px against the 271 a 320px
     phone leaves, which is the slack the desktop row does not have. */
  .music-disc {
    width: 30px;
    height: 30px;
  }
}

/* =====================================================================
         💬 Background Music Prompt
   ===================================================================== */

/* Same bubble idiom as Morphy's speech bubble in css/trash.css, but anchored
   above the dock icon and clickable. */
.music-prompt {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 5050;
  max-width: 260px;
  padding: 8px 12px;
  border: 3px solid #000;
  background: #fffdf2;
  color: #000;
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.9);
  font-family: "VT323", monospace;
  font-size: 20px;
  line-height: 1.1;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  /* On the way out visibility is held back until the fade finishes, so the
     bubble is actually seen leaving. Same idiom as .app-drawer in
     css/responsive.css. */
  transition: opacity 0.4s ease, visibility 0s 0.4s;
}

.music-prompt.visible {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.4s ease, visibility 0s 0s;
}

.music-prompt::after {
  content: "";
  position: absolute;
  left: var(--dock-bubble-tail-x, 22px);
  bottom: -13px;
  width: 18px;
  height: 18px;
  border-right: 3px solid #000;
  border-bottom: 3px solid #000;
  background: #fffdf2;
  transform: rotate(45deg);
}

/* The bubble points at the dock, so it leaves with the dock. */
body.has-fullscreen-window .music-prompt {
  display: none !important;
}

/* =====================================================================
         💬 Minimize Hint
   ===================================================================== */

/* The same bubble again, but this one points at the Music window's own
   minimize control rather than at the dock icon. It is a sibling of #music,
   not a child, because .window is overflow:hidden and anything reaching past
   the title bar would be clipped — so js/apps/music.js places it, the way the
   Guestbook prompt places itself beside its window.

   It takes no part in claimDockBubble(): it is not over the dock, and by the
   time it appears the dock prompt has stopped for good anyway, since pressing
   Play is what ends that cycle. */
.music-minimize-hint {
  position: fixed;
  left: 0;
  top: 0;
  /* Above a maximized window's 6001, because the minimize control is still
     there to point at when the Music window fills the screen. The dock bubbles
     can sit lower than this only because they hide with the dock. */
  z-index: 6050;
  /* Sized for a 320px window rather than the dock bubbles' 260px, so the
     window is still legible either side of it. */
  max-width: 232px;
  padding: 8px 12px;
  border: 3px solid #000;
  background: #fffdf2;
  color: #000;
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.9);
  font-family: "VT323", monospace;
  font-size: 20px;
  line-height: 1.1;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  /* On the way out visibility is held back until the fade finishes, the same
     idiom as the dock prompt above. */
  transition: opacity 0.4s ease, visibility 0s 0.4s;
}

.music-minimize-hint.visible {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.4s ease, visibility 0s 0s;
}

/* Sitting above the control, so right + bottom is the pair that points the
   rotated square down — the same tail the dock prompt draws, offset by the
   same --dock-bubble-tail-x that positionDockBubble() writes. */
.music-minimize-hint::after {
  content: "";
  position: absolute;
  left: var(--dock-bubble-tail-x, 22px);
  bottom: -13px;
  width: 18px;
  height: 18px;
  border-right: 3px solid #000;
  border-bottom: 3px solid #000;
  background: #fffdf2;
  transform: rotate(45deg);
}

/* Dropped beside the control when there is no headroom above the window, which
   is the normal outcome on a phone and whenever the window is maximized. Now
   the bubble is to the control's left, so the tail turns to point right on
   top + right, exactly as the Guestbook prompt's does, and reads
   --side-bubble-tail-y from positionBubbleLeftOf(). Both offsets from the rule
   above have to be unset rather than merely overridden. */
.music-minimize-hint.beside::after {
  left: auto;
  bottom: auto;
  top: var(--side-bubble-tail-y, 22px);
  right: -13px;
  border-bottom: none;
  border-top: 3px solid #000;
}

/* Deliberately no body.has-fullscreen-window rule. The three dock bubbles hide
   with the dock because that is what they point at; this one points at a
   control a maximized window still shows. */

/* =====================================================================
         🎶 Musical Notes Animation
   ===================================================================== */

.musical-note {
  position: fixed;
  font-size: 28px;
  color: #000;
  pointer-events: none;
  user-select: none;
  z-index: 4999;
  opacity: 1;
  animation: float-up ease-out forwards;
}

@keyframes float-up {
  from {
    transform: translateY(0) scale(1);
    opacity: 1;
  }
  to {
    transform: translateY(-90px) translateX(var(--random-x, 0px))
      scale(var(--random-scale, 0.5));
    opacity: 0;
  }
}
