
:host {
   display: block;
   
   /*INPUTS*/
   
   /* Guide the aspect ratio of the video player. */
   --aspect-ratio: auto;
   
   --min-video-width:  0;
   --min-video-height: 0;
   --max-video-width:  100%;
   --max-video-height: none;
   
   /* Scale the player UI. Note that very large values may clip the player. */
   --scale: 1;
   
   /* Control the sizing of the current timestamp. */
   --timestamp-force-font-size: unset;          /* force an exact font size */
   --timestamp-base-font-size:  12px;           /* font size when not scaled */
   --timestamp-scale-font-size: var(--scale,1); /* adjust the font size scaling*/
   --timestamp-min-font-size:   unset;
   --timestamp-max-font-size:   unset;
   
   /* Control baseline subtitle styles. */
   /*
      We need to offer CSS variables for this because there's no way to expose a 
      native pseudo-element i.e. ::cue to styles outside of the shadow DOM, whether 
      as a ::part or otherwise. We'd have to expose the whole `video` element, which 
      would allow outside styles to break it.
      
      Moreover, as of 1/25/2025, Firefox mishandles style properties on ::cue: if a 
      property computes to an invalid value, Firefox resets it to the document-level 
      default (e.g. black Times New Roman) rather than leaving it unchanged. On top 
      of that, `initial`, `revert`, and friends all appear to be broken on ::cue as 
      well.
   */
   --subtitle-background:      unset;
   --subtitle-color:           #FFF;
   --subtitle-font-family:     "Segoe UI", Arial, sans-serif;
   --subtitle-font-size:       1em;
   --subtitle-font-style:      unset;
   --subtitle-font-variant:    unset;
   --subtitle-font-weight:     unset;
   --subtitle-opacity:         unset;
   --subtitle-outline:         1px solid #000;
   --subtitle-text-decoration: unset;
   --subtitle-text-shadow:     unset;
}
:host(:fullscreen) {
   .main {
      height: 100%;
   }
}

.main {
   width:  100%;
   height: 100%;
   
   /* Internal variables */
   --button-glyph-scale: var(--scale, 1);
}

/*
   LAYOUT
*/
.main {
   display: grid;
   grid-template:
      "content     content  content     " 1fr
      [lower-start]
      "seek        seek     seek        "
      ".           .        ." var(--gap-between-seek-and-controls, 0px)
      "left-gutter controls right-gutter"
      [lower-end]
      ".           .        ." var(--gap-below-controls, min-content)
      [lower-background-end]
      /
      1fr var(--controls-flex, auto) 1fr
   ;
   
   /*
      The way we set up the dark theme's bottom background, portions of it can poke 
      out if the player is too narrow / the scale is too large.
   */
   overflow: hidden;
   
   /*
      If the seek slider is moved into the controls tray, we want the tray to expand 
      as much as possible.
   */
   &:has(.controls .seek) {
      --controls-flex: 10fr;
   }
}
.gutter-left {
   grid-area: left-gutter;
   
   display:       grid;
   grid-template: ". time" / 1fr auto;
   align-items:   center;
}
.gutter-right {
   grid-area: right-gutter;
   
   display: grid;
   grid-template: ". rearrangeables" / 1fr auto;
   align-items:   center;
   
   padding: 0 calc(20px * var(--scale,1));
   
   .rearrangeables {
      grid-area: rearrangeables;
      
      display: flex;
      flex-flow: row nowrap;
      align-items: center;
      gap: calc(var(--base-widget-gap, 6px) * var(--scale,1));
   }
}

.content {
   grid-area:  content;
   max-width:  100%;
   min-height: 0px; /* allow this grid region to shrink if the player container shrinks */
   
   background: #000;
   
   display: grid;
   grid-template: "video";
   justify-items: center;
   align-items: center;
}
video {
   min-width:  var(--min-video-width, 0);
   min-height: var(--min-video-height, 0);
   max-width:  var(--max-video-width, 100%);
   max-height: var(--max-video-height, none);
   
   aspect-ratio: var(--aspect-ratio, auto);
   object-fit:   contain;
   
   /* ensure we shrink to fit the player when necessary */
   width:  100%;
   height: 100%;
}
video::cue {
   background:      var(--subtitle-background);
   color:           var(--subtitle-color);
   font-family:     var(--subtitle-font-family);
   font-size:       var(--subtitle-font-size);
   font-style:      var(--subtitle-font-style);
   font-variant:    var(--subtitle-font-variant);
   font-weight:     var(--subtitle-font-weight);
   opacity:         var(--subtitle-opacity);
   outline:         var(--subtitle-outline);
   text-decoration: var(--subtitle-text-decoration);
   text-shadow:     var(--subtitle-text-shadow);
}

.seek {
   grid-area: seek;
   
   .main > & {
      grid-area: seek;
   }
   .controls & {
      flex: 1 0 auto;
   }
   .rearrangeables & {
      min-width: 100px;
      flex: 1 0 100px;
   }
}
:host(:state(buffering)) .seek {
   --fill-gradient: linear-gradient(to bottom, #a7c2d3, #3c587f, #90a7c5);
}
:host(:state(stalled)) .seek {
   --fill-gradient: linear-gradient(to bottom, #e2adad, #933434, #d28080);
}

.current-time {
   --side-distance: calc(6px * var(--scale, 1));
   
   grid-area:  time;
   align-self: center;
   padding: 0 var(--side-distance);
   
   text-align: right;
   
   font-family: "Segoe UI", Arial, sans-serif;
   color: #000;
   -webkit-text-stroke: 4px #FFF; /* TODO: this is for a light background only */
   paint-order: stroke fill;
   
   user-select: none;
   
   font-size: var(--timestamp-force-font-size, 
      clamp(
         var(--timestamp-min-font-size, 0.001px), /* minifiers may strip 0px to 0, breaking this */
         calc(var(--timestamp-base-font-size,12px) * var(--timestamp-scale-font-size,1)),
         var(--timestamp-max-font-size, 999999px)
      )
   );
   
   /* Force a width, so that the controls layout doesn't shift when the timestamp is updated. */
   width: 5ch;
   &[datetime]:not([datetime^="P0H"]) { /* if a non-zero amount of hours */
      width: 8ch;
   }
}

.controls {
   grid-area: controls;
   
   --height:          calc(23px * var(--scale,1));
   --base-widget-gap: calc( 6px * var(--scale,1));
   
   /* computed below, in the spritesheet code */
   --play-pause-flex-area-width:  unset;
   --play-pause-flex-area-height: unset;
   
   --play-pause-extra-thickness: calc((var(--play-pause-flex-area-height) - var(--height)) / 2);
   
   --extra-diff: calc((var(--tray-bulge-sprite-h) - var(--tray-middle-sprite-h)) * var(--tray-spritesheet-effective-scale) / 2);
   
   --play-pause-flex-area-width: var(--play-pause-button-hitbox);
   
   display: grid;
   grid-template:
      "left play right" var(--height)
      /
      var(--tray-left-flex, min-content) var(--play-pause-flex-area-width) var(--tray-right-flex, min-content)
   ;
   justify-content: center;
   align-items:     center;
   
   height:        var(--height, auto);
   margin-top:    calc(var(--play-pause-extra-thickness,0px) + var(--margin-above,0px));
   margin-bottom: calc(var(--play-pause-extra-thickness,0px) + var(--margin-below,0px));
   
   .left {
      grid-area: left;
   }
   .right {
      grid-area: right;
   }
   .play-pause {
      grid-area: play;
   }
   
   .left,
   .right {
      display:         flex;
      flex-flow:       row nowrap;
      justify-content: center;
      align-items:     center;
      gap:             var(--base-widget-gap, calc(6px*var(--scale,1)));
      
      height: var(--height);
   }
   
   /*
      If the seek slider is moved into either side of the controls tray, we 
      want that side of the tray to expand as much as possible.
   */
   &:has(.left .seek) {
      --tray-left-flex: 1fr;
   }
   &:has(.right .seek) {
      --tray-right-flex: 1fr;
   }
}

.controls {
   /*
      The spritesheet being configured here is for when controls are overlaid 
      (i.e. WMP's "Now Playing" view). These variables are redefined as needed 
      for the other player themes.
   */
   --height:  calc(62px * var(--scale,1) / (2 * 0.92));
   --padding: calc(4px * var(--scale,1));
   
   --tray-spritesheet:            url(spritesheet-tray-background-overlay.svg);
   --tray-spritesheet-w-unitless: 96;
   --tray-spritesheet-w:          calc(1px * var(--tray-spritesheet-w-unitless));
   --tray-spritesheet-h:          416px;
   --tray-spritesheet-scale:      calc(2 * 0.96);
   
   --tray-endcap-sprite-w: 32px;
   --tray-endcap-sprite-h: 63px;
   
   --tray-middle-sprite-y: 64px;
   --tray-middle-sprite-h: 63px;
   
   --tray-bulge-sprite-y: 128.5px; /* TODO: width doesn't include the whole stroke? this should be 196px. */
   --tray-bulge-sprite-w: 69px;
   --tray-bulge-sprite-h: 96px;
   
   --tray-spritesheet-effective-scale: calc(var(--scale,1) / var(--tray-spritesheet-scale));
   --tray-spritesheet-effective-size:
      calc(var(--tray-spritesheet-w) * var(--tray-spritesheet-effective-scale))
      calc(var(--tray-spritesheet-h) * var(--tray-spritesheet-effective-scale))
   ;
   --tray-bulge-effective-w: calc(var(--tray-bulge-sprite-w) * var(--tray-spritesheet-effective-scale));
   --tray-bulge-effective-h: calc(var(--tray-bulge-sprite-h) * var(--tray-spritesheet-effective-scale));
   
   /* set these to assist with how the controls tray is laid out, in the styles further above */
   --play-pause-flex-area-width:  var(--tray-bulge-effective-w);
   --play-pause-flex-area-height: var(--tray-bulge-effective-h);
   
   .left,
   .right {
      --tray-endcap-effective-w: calc(var(--tray-endcap-sprite-w) * var(--tray-spritesheet-effective-scale));
      --tray-endcap-effective-h: calc(var(--tray-endcap-sprite-h) * var(--tray-spritesheet-effective-scale));
      
      /*
         Ensure the backgrounds on these elements (the right half in particular) 
         don't partially overlap and cover the play/pause button.
      */
      order: -1;
      
      box-sizing: border-box;
      background:
         var(--tray-spritesheet)
         left calc(-1 * var(--tray-middle-sprite-y) * var(--tray-spritesheet-effective-scale))
         /
         var(--tray-spritesheet-effective-size)
         repeat-x
         padding-box padding-box
      ;
      
      /*
         The original plan was to use two background images: one for the endcap and 
         one for the middle portion. Unfortunately, in both Firefox and Chrome, that 
         produces sub-pixel rounding errors and visible seams.
         
         If we position the endcaps via the box model, i.e. using a pseudo-element, 
         then things seem to render just fine... mostly. Firefox gets sub-pixel gaps 
         at some zoom levels if the background on the pseudo-element is *just* the 
         endcap; we fix this by just having extra width in that background.
      */
      position: relative;
      &::before {
         content:  " ";
         display:  block;
         width:    var(--tray-endcap-effective-w);
         position: absolute;
         top:      0;
         bottom:   0;
         right:    100%;
         background:
            var(--tray-spritesheet)
            var(--endcap-position-x) 0
            /
            var(--tray-spritesheet-effective-size)
            no-repeat
            border-box border-box
         ;
      }
      &.right::before {
         right: auto;
         left:  100%;
      }
   }
   .left {
      --endcap-position-x: 0;
      border-left:   var(--tray-endcap-effective-w) solid transparent;
      padding-right: var(--padding); /* spacing between prev/next and play/pause */
   }
   .right {
      --endcap-position-x: right;
      border-right: var(--tray-endcap-effective-w) solid transparent;
      padding-left: var(--padding); /* spacing between prev/next and play/pause */
   }
   
   &::before {
      grid-area: play;
      justify-self: center;
      align-self: center;
      
      content: " ";
      width:   var(--tray-bulge-effective-w);
      height:  var(--tray-bulge-effective-h);
      background:
         var(--tray-spritesheet)
         left calc(-1 * var(--tray-bulge-sprite-y) * var(--tray-spritesheet-effective-scale))
         /
         var(--tray-spritesheet-effective-size)
         no-repeat
      ;
   }
   
   /*
      Account for changes to the control layouts which leave either side 
      of the tray empty.
   */
   .left:empty,
   .right:empty {
      display: none;
   }
   &:has(.left:empty):has(.right:not(:empty)),
   &:has(.right:empty):has(.left:not(:empty)) {
      --tray-bulge-sprite-y: 224px;
      --tray-bulge-sprite-w: 82.595px;
      --tray-bulge-sprite-h: 96px;
      
      --tray-bulge-button-displacement: calc(5px*var(--scale,1));
   }
   
   &:has(.left:empty ~ .right:not(:empty)) {
      .play-pause {
         margin-left: var(--tray-bulge-button-displacement);
      }
   }
   &:has(.left:not(:empty) ~ .right:empty) {
      .play-pause {
         margin-right: var(--tray-bulge-button-displacement);
      }
      &::before {
         transform: scale(-100%, 100%);
      }
   }
   &:has(.left:empty):has(.right:empty) {
      --tray-bulge-sprite-y: 320px;
      --tray-bulge-sprite-w: 96px;
      --tray-bulge-sprite-h: 96px;
   }
}

.controls hr {
   width:  1.5px;
   height: calc(19px * var(--scale,1));
   background: #CECDCF;
   border: 0;
}

.play-pause {
   grid-area: play;
   justify-self: center;
}

.volume {
   width: calc(67px * var(--scale,1));
   height: calc(30px * var(--scale,1));
   margin-left: calc(4px * var(--scale,1));
}

/*
   As it turns out, the spacing between buttons in the WMP player UI 
   is... all over the place. Including button glass:
   
    - ~6px between the lefthand endcap and Shuffle
    -  6px between Shuffle and Loop
    - ~9px between Loop and the separator
    - ~8px between the separator and Stop
    -  9px between Stop and Prev
    - 12px between Next and Mute
    - 10px between Mute and the volume slider track
    
   Notably, if you've got the volume at 50%, then the Mute glyph (not 
   the glass) is roughly 16px away from both the Prev button glass 
   and the volume slider track. Similarly, the Stop glyph (not the 
   glass) is roughly 14px from the separator and the Prev button. So 
   Microsoft designed the controls so that the spacing would appear 
   balanced when the UI is in a non-interacted-with state, i.e. when 
   no hover-only button glass is visible. Unfortunately, we lay out 
   the controls based on their hitboxes, which to some extent reflect 
   the glass sizes, so we're... gonna have to get creative.
   
   We've set the baseline spacing between buttons to 6px, so we'll go 
   from there. We use `--button-glyph-scale` to ensure relatively 
   consistent spacing when controls are overlaid, but it looks like 
   WMP uses fairly different spacing in that case, so we're not 100% 
   accurate there.
   
   As of this writing, I want to implement rearranging the control 
   layout, but haven't yet. These styles, then, shouldn't target any 
   specific button, but rather should target the various button styles 
   based on their locations relative to one another.
*/
:is(.controls, .rearrangeables) {
   --base-widget-gap: calc(6px * var(--button-glyph-scale,1));
   hr {
      margin: 0 calc(3px*var(--button-glyph-scale,1));
   }
   .basic-button + .prev-rw {
      margin-left: calc(3px*var(--button-glyph-scale,1));
   }
   .next-ff + .basic-button {
      margin-left: calc(6px*var(--button-glyph-scale,1));
   }
   .basic-button + .basic-button {
      margin-left: calc(2px*var(--button-glyph-scale,1));
   }
   .basic-button + .volume {
      margin-left: calc(4px*var(--button-glyph-scale,1));
   }
   
   /* widget distances from the tray endcaps */
   .left .basic-button:first-child {
      margin-left: calc(4px*var(--scale,1));
   }
   .right .basic-button:last-child {
      margin-right: calc(4px*var(--scale,1));
   }
   
   /* account for widgets being placed directly next to the play/pause button */
   .left .seek:last-child {
      margin-right: calc(4px*var(--scale,1));
   }
   .right .seek:first-child {
      margin-left: calc(4px*var(--scale,1));
   }
   .left .basic-button:last-child {
      margin-right: calc(10px*var(--scale,1));
   }
   .right .basic-button:first-child {
      margin-left: calc(10px*var(--scale,1));
   }
}


/*
   THEMES
*/

:host(:not([data-overlay-controls])) .controls,
:host([data-overlay-controls="video-only" i]) .main:not(.video) .controls {
   --height:  calc(31px * var(--scale,1));
   --padding: 4px;
   
   --tray-spritesheet:            url(spritesheet-tray-background-non-overlay.svg#lite);
   --tray-spritesheet-w-unitless: 52;
   --tray-spritesheet-w:          calc(1px * var(--tray-spritesheet-w-unitless));
   --tray-spritesheet-h:          219px;
   --tray-spritesheet-scale:      1;
   
   --tray-endcap-sprite-w: 15.5px;
   --tray-endcap-sprite-h: 31px;
   
   --tray-middle-sprite-y: 32px;
   --tray-middle-sprite-h: 31px;
   
   --tray-bulge-sprite-y: 63px;
   --tray-bulge-sprite-w: 40.497px;
   --tray-bulge-sprite-h: 51px;
   
   --extra-diff: calc((var(--tray-bulge-sprite-h) - var(--tray-middle-sprite-h)) * var(--tray-spritesheet-effective-scale) / 2);
   
   /* fake a 1.5px width using a mask, since browsers round everything */
   hr {
      width: 2px;
      mask: linear-gradient(to right, #FFF, #FFF 50%, #000) 0 0/2px 100%;
   }
   
   /*
      Account for changes to the control layouts which leave either side 
      of the tray empty.
   */
   &:has(.left:empty):has(.right:not(:empty)),
   &:has(.right:empty):has(.left:not(:empty)) {
      --tray-bulge-sprite-y: 115px;
      --tray-bulge-sprite-w: 45.747px;
      --tray-bulge-sprite-h: 51px;
   }
   &:has(.left:empty):has(.right:empty) {
      --tray-bulge-sprite-y: 167px;
      --tray-bulge-sprite-w: 51px;
      --tray-bulge-sprite-h: 51px;
   }
}
:host([data-overlay-controls]:not([data-overlay-controls="video-only" i])) .main,
:host([data-overlay-controls="video-only" i]) .main.video {
   --requested-scale: var(--scale);
   .controls {
      /*
         In WMP's Now Playing view, player controls are overlaid 
         on the playing media. The controls are rendered at 92% 
         scale, but the icon glyphs that serve as button labels 
         remain at 100% scale.
         
         Notably, elements like the seek slider and the current 
         timestamp are also not downscaled.
      */
      --scale: calc(var(--requested-scale,1) * 0.92);
      --button-glyph-scale: var(--requested-scale,1);
   }
   
   --gap-between-seek-and-controls: calc(12px * var(--scale,1));
   --gap-below-controls: calc( 3px * var(--scale,1));
   
   .content {
      grid-row: 1 / span 100;
      
      video::cue {
         /*
            TODO: We need to move cues out from below the video 
            controls. The problem is that CSS... just doesn't 
            give us a way to do that. So whenever CSS control 
            over subtitles is, uh, no longer terrible, we'll 
            need to go and update this.
         */
      }
   }
   .seek:not(.controls *) {
      margin-left: calc(12px * var(--scale,1));
      margin-right: calc(12px * var(--scale,1));
   }
   .current-time {
      color: #FFF;
      -webkit-text-stroke: 4px #000A;
      --side-distance: calc(10px * var(--scale,1));
   }
   .controls {
      hr {
         background:      #00000040;
         background-clip: content-box;
         border:          calc(1px*var(--scale,1)) solid #FFFFFF20;
         width:           calc(1px*var(--scale,1));
         border-radius:   calc(2px*var(--scale,1));
      }
   }
}
:host([data-theme="dark" i]:not([data-overlay-controls])) .main,
:host([data-theme="dark" i][data-overlay-controls="video-only"]) .main:not(.video) {
   /*
      Styles for the dark theme used by WMP 11 on Windows XP.
   */
   
   /* background */
   --tray-background-height: calc(69px*var(--scale,1));
   &::before {
      content: " ";
      grid-row:    lower-start / lower-background-end;
      grid-column: 1 / span 3;
      order: -2;
      z-index: -2;
      
      background:
         url(spritesheet-tray-background-dark.svg#middle) left top/1px var(--tray-background-height) repeat-x
      ;
   }
   &::after {
      --endcap-outset: calc(-78px * var(--scale,1));
      --endcap-border-width: calc(110px * var(--scale,1));
      
      content: " ";
      
      grid-row:    lower-start / lower-background-end;
      grid-column: controls-start / controls-end;
      z-index:    -1;
      
      margin:     0 var(--endcap-outset);
      min-height: var(--tray-background-height);
      border-image:
         url(spritesheet-tray-background-dark.svg)
         69 110 0 110 / var(--tray-background-height) var(--endcap-border-width) 0 var(--endcap-border-width)
         stretch space
      ;
      min-width: calc(var(--endcap-border-width) * 2);
   }
   
   .seek:not(.controls *) {
      padding-top: calc(5px*var(--scale,1));
      margin:      calc(2px*var(--scale,1)) calc(5px*var(--scale,1));
      
      --track-border-color-upper: #444b5e;
      --track-border-color-lower: #656e86;
   }
   .current-time {
      color: #FFF;
      -webkit-text-stroke: 0px;
      
      /*
         Displace the timestamp out of the "curve" around the tray.
      */
      padding-right: calc(51px * var(--scale,1));
   }
   
   .controls {
      --tray-spritesheet: url(spritesheet-tray-background-non-overlay.svg#dark);
      
      margin:  calc(var(--extra-diff) - 3px * var(--scale,1)) 0 var(--extra-diff) 0;
      
      hr {
         background: linear-gradient(to bottom, #2a2d35, #2a2d35, #131415, #2a2e37);
      }
   }
}