/* The TV lyrics screen. Apple Music's lyrics style, with Rahuletto/lirics
   as the visual reference, sized for a 50-inch TV seen from about 3 m.
   1rem is a 108th of the screen height: 10px at 1080p, 20px at 4K.
   Written for the TV's Chrome 84: no aspect-ratio, inset, or :has. */

/* Instrument Sans (SIL Open Font License, fonts/OFL.txt), from Google Fonts.
   One variable file per character set covers weights 400 to 700. */
@font-face {
  font-family: "Instrument Sans";
  font-weight: 400 700;
  font-display: swap;
  src: url(fonts/instrument-sans-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Instrument Sans";
  font-weight: 400 700;
  font-display: swap;
  src: url(fonts/instrument-sans-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
    U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Motion: lines move in a quick, smooth wave.
   --move and --move-ease: lines scrolling. --fade: everything that fades.
   --stagger: each line below the current one starts this much later
   (screen.js reads it, and sets each line's position and size itself). */
:root {
  --ink: #fff;
  --ink-soft: rgba(255, 255, 255, 0.62);
  --base: #0b0b0d;
  /* 0.7 s: at the TV's 30 frames a second a faster move jumps far enough
     each frame to look choppy (picked on the TV, card 3). */
  --move: 0.7s;
  /* A smooth curve with no spring: it starts gently, since the TV draws 30
     frames a second and a fast start shows as a few big jumps (picked on
     the TV as the least choppy, card 3). */
  --move-ease: cubic-bezier(0.45, 0, 0.25, 1);
  --fade: 0.25s;
  --stagger: 33ms; /* one frame at 30 a second, so each line starts on a frame */
  --line: 8.5rem; /* lyric lines, picked on the TV from the couch (card 3) */
  --edge: 7rem; /* the margin on the left, right, and bottom */
  --progress: 5.4rem; /* the time and bar together: 3.4 + 1.2 + 0.8 */
  font-size: calc(100vh / 108);
}

* {
  box-sizing: border-box;
  margin: 0;
}

html,
body {
  height: 100%;
  overflow: hidden;
  background: var(--base);
  color: var(--ink);
  font-family: "Instrument Sans", "Segoe UI", Roboto, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.app {
  position: relative;
  height: 100%;
}

.bg,
.bg-layer,
.dim,
.connecting {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

/* Everything that fades keeps a layer of its own for good. On the TV, a
   layer made when a fade starts and dropped when it ends cost a frame or
   several each time (card 3). */
.bg-layer,
.stage,
.pause,
.dim,
.connecting,
.banner {
  will-change: opacity;
}

.lines {
  will-change: opacity, transform;
}

/* Background: blurred cover colours (darkened in screen.js), slowly
   turning, drawn on one small canvas stretched over the screen. */

.bg-layer {
  opacity: 0;
  transition: opacity 1.2s ease; /* BG_FADE_MS in screen.js */
}

.bg-layer.is-shown {
  opacity: 1;
}

.bg-layer canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* Layout: cover and details on the left, lyrics on the right, the
   progress bar across the bottom. */

.stage {
  position: relative;
  display: flex;
  height: 100%;
  transition: opacity var(--fade) ease; /* screen.js waits this long */
}

.stage.is-fading {
  opacity: 0;
}

.now {
  flex: 0 0 40%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Centred in the space above the time. */
  padding: 0 0 calc(var(--edge) + var(--progress)) var(--edge);
}

.cover {
  position: relative;
  width: 58rem;
  height: 58rem;
  border-radius: 1.6rem;
  background: rgba(255, 255, 255, 0.08);
  box-shadow: 0 2.4rem 6rem rgba(0, 0, 0, 0.45);
}

.cover img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
}

.cover img:not([src]),
.cover img[src=""],
.connecting img:not([src]),
.connecting img[src=""] {
  visibility: hidden;
}

.meta {
  width: 58rem;
  margin-top: 4rem;
}

.title,
.artist {
  line-height: 1.2;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.title {
  font-size: 4.8rem;
  font-weight: 700;
}

.artist {
  margin-top: 0.6rem;
  font-size: 3.8rem;
  color: var(--ink-soft);
}

.artist:empty {
  display: none;
}

/* Progress: the time over a rounded bar along the bottom, the same
   distance from the left, right, and bottom edges. */

.progress {
  position: absolute;
  left: var(--edge);
  right: var(--edge);
  bottom: var(--edge);
}

.time {
  margin-bottom: 1.2rem;
  font-size: 2.8rem;
  line-height: 3.4rem;
  font-weight: 500;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.bar {
  height: 0.8rem;
  border-radius: 0.4rem;
  background: rgba(255, 255, 255, 0.2);
  overflow: hidden;
}

.bar-fill {
  height: 100%;
  background: rgba(255, 255, 255, 0.85);
  transform: scaleX(0);
  transform-origin: left center;
}

/* Lyrics. screen.js sets each line's transform (position, and shrunk
   unless current) and transition delay (the wave) directly. */

.lyrics {
  position: relative;
  flex: 1 1 auto;
  height: 100%;
  overflow: hidden;
  padding-right: var(--edge);
  /* Lines show in the band beside the cover (--band-top and --band-bottom,
     set in screen.js) and fade out just past its edges. */
  --band-top: 10%;
  --band-bottom: 64%;
  -webkit-mask-image: linear-gradient(
    to bottom,
    transparent calc(var(--band-top) - 5rem),
    #000 var(--band-top),
    #000 var(--band-bottom),
    transparent calc(var(--band-bottom) + 5rem)
  );
}

/* --line (in :root) was picked on the TV from the couch. --fit shrinks a line
   that would wrap past two rows (screen.js). */
.line {
  margin-bottom: 3.6rem;
  font-size: calc(var(--line) * var(--fit, 1));
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.01em;
  opacity: 0.22;
  transform-origin: left center;
  transition: opacity var(--fade) ease, transform var(--move) var(--move-ease);
  will-change: transform;
}

.line.is-current {
  opacity: 1;
}

.line.is-next {
  opacity: 0.4;
}

.line.is-prev {
  opacity: 0.16;
}

.line.is-gone {
  opacity: 0.1;
}

.no-anim,
.no-anim .line {
  transition: none;
}

/* A seek: the lyrics fade out drifting up, move while hidden, and fade back
   in from a little below (JUMP_FADE_MS in screen.js). */
.lines {
  transition: opacity 0.12s ease, transform 0.2s ease-out;
}

.lines.is-leaving {
  opacity: 0;
  transform: translate3d(0, -4rem, 0);
}

.lines.is-arriving {
  opacity: 0;
  transform: translate3d(0, 4rem, 0);
}

/* Word by word: sung words fill in from the left as they're sung. Only the
   word being sung wears the fill (screen.js); painting every word of the
   line that way dropped a frame at each line change on the TV. */

.line.is-current .word {
  color: rgba(255, 255, 255, 0.4);
}

.line.is-current .word.is-sung {
  color: #fff;
}

.line.is-current .word.is-singing {
  --p: 0;
  color: transparent;
  background-image: linear-gradient(
    90deg,
    #fff calc(var(--p) * 115% - 15%),
    rgba(255, 255, 255, 0.4) calc(var(--p) * 115%)
  );
  -webkit-background-clip: text;
  background-clip: text;
}

/* Instrumental gaps: three dots that light up one by one as the gap goes
   on (screen.js), breathing gently as one piece. */

.line.gap {
  display: flex;
  height: var(--line);
  align-items: center;
}

.line.gap:not(.is-current) {
  opacity: 0;
}

.dots {
  display: flex;
  transform-origin: left center;
  will-change: transform;
}

.dots i {
  width: 2.4rem;
  height: 2.4rem;
  margin-right: 2rem;
  border-radius: 50%;
  background: #fff;
  opacity: 0.3;
}

.line.gap.is-current .dots {
  animation: dots-breathe 2.4s ease-in-out infinite;
}

@keyframes dots-breathe {
  50% {
    transform: scale(1.12);
  }
}

/* Credits: smaller and softer, after the last line. In the song's outro
   they light up at the bottom of the band, the last lines faded above. */

.line.credits {
  margin-top: 2rem;
  font-size: 3.2rem;
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: 0;
  color: var(--ink-soft);
}

/* Lyrics without timing: all shown, scrolling slowly as one block (moved
   in screen.js), none lit. */

.is-unsynced .lines {
  transition: none;
}

.is-unsynced .line {
  opacity: 0.7;
  transition: none;
}

.line.spacer {
  height: 2rem;
}

/* No lyrics, and podcasts: the cover large and centred above the progress. */

.layout-centered .stage {
  justify-content: center;
}

.layout-centered .now {
  flex: 0 0 auto;
  align-items: center;
  padding: 0 0 calc(var(--edge) + var(--progress));
  text-align: center;
}

.layout-centered .cover {
  width: 64rem;
  height: 64rem;
}

.layout-centered .meta {
  width: 100rem;
}

.layout-centered .title {
  font-size: 5.6rem;
}

.layout-centered .artist {
  font-size: 4.2rem;
}

.layout-centered .lyrics {
  display: none;
}

/* Paused: a big pause symbol over the cover. After a while the whole screen
   dims, the symbol too, so nothing bright stays still for long (it could
   burn into an OLED screen). */

.pause {
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  width: 18rem;
  height: 18rem;
  margin: -9rem 0 0 -9rem;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: opacity var(--fade) ease;
}

.pause span {
  width: 2.6rem;
  height: 8rem;
  margin: 0 1.1rem;
  border-radius: 0.8rem;
  background: #fff;
}

.pause.is-on {
  opacity: 1;
}

/* Dimming is one black sheet fading in over everything: fading each part
   of the screen instead dropped frames on the TV. */
.dim {
  background: #000;
  opacity: 0;
  transition: opacity 2s ease;
  pointer-events: none;
}

.dim.is-on {
  opacity: 0.78;
}

/* Connecting: a plain dark screen with the cover. */

.connecting {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--base);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--fade) ease, visibility 0s linear var(--fade);
}

.is-connecting .connecting {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--fade) ease;
}

.connecting img {
  width: 44rem;
  height: 44rem;
  border-radius: 1.4rem;
  object-fit: cover;
  opacity: 0.85;
}

.connecting p {
  margin-top: 4rem;
  font-size: 4.2rem;
  font-weight: 600;
  color: var(--ink-soft);
}

/* Lost connection: a note at the top; the lyrics stay on screen. */

.banner {
  position: absolute;
  top: 5rem;
  left: 50%;
  padding: 1.6rem 3.6rem;
  border-radius: 4rem;
  background: rgba(24, 24, 28, 0.88);
  font-size: 3.4rem;
  font-weight: 600;
  white-space: nowrap;
  opacity: 0;
  transform: translate(-50%, 0);
  transition: opacity var(--fade) ease;
}

.is-lost .banner {
  opacity: 1;
}
