/* =========================================================================
   Backdrop

   The SOLUM animation behind the opening screen. It is a closed section:
   the element sits inside .page and is bounded by it, so it scrolls away
   with the opening screen rather than following the page down. Everything
   below is plain black.

   The layers, back to front: black, a still frame of the scene by day,
   the same scene after dark cycled over it, the video, and the scrim that
   keeps the type legible over all of it.

   The still holds the screen until the video has a frame to show, and
   stands in permanently where the video is never fetched — see
   src/js/ui/backdrop.js for who gets it. It is standing in for everyone
   at the moment: HERO_VIDEO in config.js is off while the clip is re-cut,
   so what is behind the opening screen is the two renders and nothing
   else. The video layer stays in place, empty, for when it comes back —
   and when it does, note the still is now the day render, where the clip
   was cut to hand over from the night one.

   The still shows at every width, from two different renders: a portrait
   one below 64rem and the landscape one above. The day layer over it is
   cut the same way, from its own pair. They are the same scene
   composed for the shape it has to fill, which is what a crop of the wide
   render could not do — a phone's box is a third as wide as it is tall,
   and cover was throwing away four fifths of the picture to fill it.

   The video does not show at every width: below 64rem it is never
   fetched, and it is off everywhere at the moment anyway.
   ========================================================================= */

.backdrop {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;      /* the scene is clipped to the opening screen */
  background: var(--c-black);
  pointer-events: none;
}

/* The scene is the first thing to arrive. A glimpse of it is there from
   the first paint: a 48px copy of the still (1.2K, carried in this sheet,
   so it is here as soon as the page is), blurred, as if the scene were
   coming into focus. Once the still itself has arrived (reveal.js, at
   most 1.5s), it comes up over the glimpse in 1.2s, and the marks and copy
   follow it in. On a slow connection the page used to hold at black for up
   to 2.5s and fade up over 2s. (Chosen from a study, tag opening-study,
   docs/studies/opening/.) Without JavaScript the still is simply there. */
.js .backdrop::before {
  content: "";
  position: absolute;
  /* the still's own box (below), so the glimpse sits where it will */
  top: -20%;
  left: 0;
  width: 100%;
  height: 120%;
  background: url("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBMRXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAG6ADAAQAAAABAAAAMAAAAAD/wAARCAAwABsDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9sAQwAEBAQEBAQIBAQICwgICAsPCwsLCw8SDw8PDw8SFhISEhISEhYWFhYWFhYWGxsbGxsbHx8fHx8jIyMjIyMjIyMj/9sAQwEFBgYJCAkPCAgPJBkUGSQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQk/90ABAAC/9oADAMBAAIRAxEAPwDyXRWW6VbbUE2zRgbHOcOo44z/ACrpLTSbc6j/AGiww6xlBj3PJNSaHqVlrsCPFgODwp5IJ6fn7V2NrpkscgDqefaunDwpyp6u+t0c9VzU9jhfFVlZ6fpn9qEFpRwqZxuPYeuM155N4UvLmQ3F7PslfDOowACR2r6autIXUbJ0mh8woN0acDJHT0ry+78Ia7JcM7WyRlsHaIWbGR/ezyfWvLxlOXtPcdkehh2uX3j/0OK+H+kqkotowrJjcGIILAdCMj1/GvdbDT5ojgZVh/FXykdW8QacRPYCRZY4kiXap/gOCMYxyPzxT7Txj41bUoZ7uaeNeTtTCLn2CYFcFCrNUmjtqQg5pn2baWPmMFdgZCcc1tyeEJrhvOF3cxBsfInl7R9MoT+teK6D8Xbiyt44LtXmdQzMcAnA/X9K7E/tKeEbU/Z5bWV2UAE7gOfoelefUxVVy5WtPL/gHaqEUrx1/rzP/9Hwx9JuYh5KXztOw3BfMyMeufc1zN8NUt5EmeLcsikKS2/jvnqOfpWA9zC8ZdnZmc9VbACnsfXHNTSauxh+xXDExL1A4PHuK8WEJJ66noycWtNCwLuSKTzpJEtsjHU7vqOv9Kf9stH+YxiQnq23r78mqR1Sy1NfJnj2hOEb07jPtWTJrmqxuURkAHAGBW3I5dNSVJR6n//Z") 50% 62% / cover;
  filter: blur(18px);
  transform: scale(1.08);   /* the blur's soft edge kept off the frame */
}

@media (min-width: 64rem) {
  .js .backdrop::before {
    background-image: url("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBMRXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAMKADAAQAAAABAAAAGwAAAAD/wAARCAAbADADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9sAQwAEBAQEBAQIBAQICwgICAsPCwsLCw8SDw8PDw8SFhISEhISEhYWFhYWFhYWGxsbGxsbHx8fHx8jIyMjIyMjIyMj/9sAQwEFBgYJCAkPCAgPJBkUGSQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQk/90ABAAD/9oADAMBAAIRAxEAPwDzDwvr0mok292qRJwcOcEkgZxnPG4dO2a9Kj0xZYkkUBMj7oxx+XFfPlpHFDpc11aNvkt3JbPOcEDj2zXXab4406wgisNXSRWjjz5sfPXoMZ6+tY0MZJaM1qYaL1PY4NHy+QOemfaumtPDrnB3KPX2r59ufi5LaRhbCN3DLlWdgvGSOgB9K7jQ/iDqF3NHDKSpYgZyCOfbArapiZJcy0Jp4aLdtz0bxZ4XuJ9AMNlHHJIrqxbCkhV+ZsZ55xXDaV8NU13wCLaYSJe3F79pl3rsDDBXarHC9+K9osdStoZPst1eQuw6/L1Hp1rovEd/o1v4akvrjUVtUXqVA3MAPugH1rwamNTlq2erHCWWx//Q+arc20gZLGT5dmHUJgHBGCfn+n40y58P25kWKaXHy5AHGQRnqW7etWPCjFp7mJuVMJJB9dmf51YAD2FxcNy6xYVu4xsxivJdRwlZHoKClG7ObvbDSbeKMGSQy527AQRjOck4yM59K6Jb61sG8zdNkEYwoII9QQf6ViasqwzARDbhF6deRk81zy3Nxn77cnHWt1eavcyb5HZHqFn4zSa6kaeWaNOi5kGOnJIx+Qrm9f8AFmoXSrZyyvPEoALf3j1rlkGeWJO4ZOSap3EskFs4iYjj196XsYuV2Uq80rH/2Q==");
  }
}

.js .backdrop:not(.is-lit) .backdrop__still {
  opacity: 0;
}

.js .backdrop:not(.is-lit) .backdrop__night {
  visibility: hidden;      /* its own cycle animates its opacity */
}

/* coming up over the glimpse, at once rather than after the still's own
   hold for the video (below) */
.js .backdrop.is-lit:not(.is-playing) .backdrop__still {
  transition: opacity 1.2s cubic-bezier(0.25, 0.1, 0.25, 1);
}

/* The scene sits higher than the opening screen: the foot of the frame was
   being lost under the scrim. Rather than shifting it — which drags its
   bottom edge up off the screen and leaves a band of black — the box is
   grown 20% taller and hung above the top. It still reaches the bottom, so
   there is nothing to expose. The scrim does not move with it. */
.backdrop__still,
.backdrop__night,
.backdrop__video {
  top: -20%;
  left: 0;
  width: 100%;
  height: 120%;
}

/* Painted as a background rather than an <img>, which is what lets the
   width below decide which file is fetched, and only that one.
   reveal.js waits for whichever file was actually fetched.

   (AVIF versions were offered here for a day and taken back out: the
   AVIFs macOS's sips writes, tiled and carrying HDR light levels, drew
   black in Chromium although they decoded. JPEG until an encoder that
   writes plain AVIF is to hand and they are checked in Safari and
   Chrome.) */
.backdrop__still {
  position: absolute;
  background-image: image-set(
    url("/assets/img/solum-hero-day-portrait-800.jpg") 1x,
    url("/assets/img/solum-hero-day-portrait-1200.jpg") 2x);
  background-size: cover;
  background-position: 50% 62%;
  /* held a moment while the video rises under it, so the handover has no
     dip: the two images are all but identical anyway */
  transition: opacity 1.2s linear 0.4s;
}

/* Once the video is running it is the only thing we want behind the type.
   The still has to go, or the end-of-loop fade would reveal it instead of
   fading to black. */
.backdrop.is-playing .backdrop__still {
  opacity: 0;
}

/* ---- Day into night, and back ----

   The same scene lit twice, the night one laid over the day one and its
   opacity cycled. Nothing else moves: the two renders are the same camera,
   so what reads is the light changing rather than the picture being
   swapped.

   Day is underneath and night is the layer that moves, rather than the
   other way about, because the moving layer is the one that starts at
   zero and rises slowly. A night render that has not arrived yet costs
   nothing — the day simply holds until it does, and the cycle catches up.
   Nothing pops. With the order reversed the top layer would have to start
   fully up, and would jump in whenever its image landed.

   Every width. The two layers change render at the same breakpoint, so
   whichever pair is in use, both are the same shape. */
.backdrop__night {
  position: absolute;
  background-image: image-set(
    url("/assets/img/solum-hero-night-portrait-800.jpg") 1x,
    url("/assets/img/solum-hero-night-portrait-1200.jpg") 2x);
  background-size: cover;
  background-position: 50% 62%;
  opacity: 0;
  animation: hero-nightfall 50s ease-in-out infinite;
}

/* The wide render takes over where the still's does, so the two layers are
   always the same shape as one another. */
@media (min-width: 64rem) {
  .backdrop__night {
    background-image: image-set(
      url("/assets/img/solum-hero-night-1600.jpg") 1x,
      url("/assets/img/solum-hero-night-2400.jpg") 1.5x);
  }
}

/* Day is the long hold: fifteen seconds of it as the page opens, then
   eight for the evening to come in, ten of night, eight for the morning,
   and nine more of day before the loop — so once it is running, day holds
   for twenty-four seconds at a stretch against night's ten. Slow enough
   that it reads as the hour changing rather than as a slideshow. */
@keyframes hero-nightfall {
  0%, 30%    { opacity: 0; }
  46%, 66%   { opacity: 1; }
  82%, 100%  { opacity: 0; }
}

/* Handed to the day-and-night slider (src/js/ui/daylight.js), which picks
   the cycle up where this left it and runs it so that it can be dragged:
   the keyframes stand down and the layer takes the level it is given. */
.backdrop.is-driven .backdrop__night {
  animation: none;
  opacity: var(--night, 0);
}

/* Asked for less motion: the day render stands. */
@media (prefers-reduced-motion: reduce) {
  .backdrop__night {
    animation: none;
  }
}

.backdrop__video {
  position: absolute;
  object-fit: cover;
  object-position: 50% 62%;
  opacity: 0;
  /* linear, and the same in both directions, so the end-of-loop fade down
     to black and the fade back up read as one even breath */
  transition: opacity 1.2s linear;
}

.backdrop.is-playing .backdrop__video {
  opacity: 1;
}

/* The last moment of the clip. src/js/ui/backdrop.js adds this 1.6s from
   the end; looping back to zero takes it off again, and the picture rises
   out of black. */
.backdrop.is-playing .backdrop__video.is-fading {
  opacity: 0;
}

/* Holds the type legible over the lit path and the city: dark at the foot
   and along the left, where the announcement and the mark sit. */
.backdrop__scrim {
  position: absolute;
  inset: 0;
  background:
    /* solid at the very foot, so the opening screen's bottom edge meets the
       black of the page below it without a seam */
    linear-gradient(0deg, rgb(0 0 0 / 1) 0%, rgb(0 0 0 / 0.95) 4%, rgb(0 0 0 / 0.72) 12%, rgb(0 0 0 / 0.45) 22%, rgb(0 0 0 / 0.18) 36%, transparent 56%),
    linear-gradient(90deg, rgb(0 0 0 / 0.7) 0%, rgb(0 0 0 / 0.25) 34%, transparent 62%),
    linear-gradient(180deg, rgb(0 0 0 / 0.75) 0%, transparent 22%);
}

/* The page's own content rides above the backdrop. Without this they share
   the same stacking level and the scene paints over them. */
.page > .site-header,
.page > .main {
  position: relative;
  z-index: 1;
}

/* The wide render takes over where there is width to fill it. Both sets
   are quoted at the sizes the shape actually needs rather than at one
   standard: a night sky carries grain and compresses badly, so the phone
   pair is encoded harder — the scrim and the overscale hide it. */
@media (min-width: 64rem) {
  .backdrop__still {
    background-image: image-set(
      url("/assets/img/solum-hero-day-1600.jpg") 1x,
      url("/assets/img/solum-hero-day-2400.jpg") 1.5x);
  }
}
