/* --------------------------------------------------------------------------
the room: the site's weather, behind every page. The
paper's fibres, daylight from a window crossing the desk with a tree
moving in it. Nothing here is an object. The markup is drawn by
scripts/make_room.py; every dial is here.
-------------------------------------------------------------------------- */

/* The dials. By day the paper is already near white, so the window
   cannot be made much brighter; the contrast comes from the other
   side instead -- the unlit sheet sits a shade under the paper, as it
   would out of the sun. That shade is the dial. After dark the window
   lets in the moon instead of the sun. */
:root {
  /* a touch warmer than the paper's own hue, so that beside the
     yellow of the daylight it does not read as mauve */
  --unlit: oklch(0.905 0.013 88);
  --daylight: oklch(0.995 0.017 90);
  --shade: oklch(0.374 0.0087 67.6 / 0.11);
  --fibre: 0.14;
  --grain: 0.1;
}

[data-theme="dark"] {
  --unlit: var(--color-bg);
  --daylight: oklch(0.82 0.035 250 / 0.12);
  --shade: oklch(0.1 0.005 85 / 0.3);
  --fibre: 0.1;
  --grain: 0.16;
}

/* The body isolates the room, so it sits behind the page and in front
   of the paper */
body:has(> .room) {
  isolation: isolate;
}

/* The room is on every page and never swapped (navigation replaces
   #page over it), so the weather keeps its time across the visit. It
   breathes in once on arrival. A page may set data-room on the
   wrapper: "off" fades the room out (and back in on the way out),
   "still" sends the sun behind a cloud and lets the wind die (room.js). The fill is backwards only, so once the entrance
   has played the opacity is the cascade's to transition. */
.room {
  animation: room-in 1.8s ease backwards 0.1s;
  background: var(--unlit);
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  position: fixed;
  transition: opacity 1.8s ease;
  z-index: -1;
}

/* data-room="off": the room clears quickly and returns on the long
   breath */
body:has(.site-wrapper[data-room~="off"]) .room {
  animation: none;
  opacity: 0;
  transition-duration: 0.5s;
}

/* data-room="still": the sun goes behind a cloud. The window light,
   the leaves in it and the room's shading fade to nothing over a few
   seconds, leaving the plain sheet; room.js lets the wind die on the
   same breath. Both come back on the way out. */
.window,
.shade {
  transition: opacity 4s ease;
}

body:has(.site-wrapper[data-room~="still"]) :is(.window, .shade) {
  opacity: 0;
  transition-duration: 5s;
}

.room > * {
  inset: 0;
  position: absolute;
}

@keyframes room-in {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

/* The field: tone-on-tone, drawn as masks so the ink follows the
   theme */
.fibres {
  background: var(--color-text);
  opacity: var(--fibre);
}

.grain {
  background: var(--color-text);
  opacity: var(--grain);
}

/* The window: two panes by three, raked across the sheet. The sun
   moves it a little over a few minutes; the clouds dim it and let it
   back, never so far that the leaves are lost. Only transforms and
   opacity move, so everything is drawn once and the compositor does
   the rest. */
.window {
  animation: sun 240s ease-in-out infinite alternate;
  height: 60vmax;
  inset: auto;
  left: 50%;
  margin: -27vmax 0 0 -6vmax;
  top: 50%;
  transform: rotate(13deg) skewX(-31deg);
  width: 40vmax;
}

/* The next window along the same wall, a pier's width to the left:
   the same sun, so the same rake, shifted along the window's own
   sill line (69vmax at 13deg). Only its near corner reaches the
   sheet, in the upper left. */
.window-next {
  margin: -42.5vmax 0 0 -73.25vmax;
}

.panes {
  animation: clouds 53s ease-in-out infinite;
  display: grid;
  filter: blur(0.5vmax);
  gap: 1.7vmax;
  grid-template: repeat(3, 1fr) / repeat(2, 1fr);
  inset: 0;
  position: absolute;
}

.panes i {
  background: var(--daylight);
}

/* Leaf shadows are the unlit sheet's own colour: they take the light
   away and darken nothing, so outside the panes they are nothing at
   all. A bough sways as a whole, in gusts; its leaves are dealt
   into sprays that flutter on clocks of their own (set inline, by the
   script). The blur sits on the still layers inside, never on a
   parent of something moving, and is kept light: a leaf has to hold
   its edge for the eye to catch it moving. */
.bough {
  animation: sway 7s ease-in-out infinite;
  aspect-ratio: 1;
  color: var(--unlit);
  position: absolute;
  transform-origin: 100% 15%;
  will-change: transform;
}

.bough > * {
  filter: blur(0.4vmax);
  inset: 0;
  position: absolute;
}

.bough .spray {
  animation: flutter 2.4s ease-in-out infinite alternate;
  will-change: transform;
}

.bough svg {
  display: block;
  width: 100%;
}

.bough-1 {
  right: -10%;
  top: -8%;
  width: 96%;
}

.bough-2 {
  animation-delay: -4s;
  animation-duration: 9s;
  right: -18%;
  top: 20%;
  width: 86%;
}

/* The far bough: softer, and a beat behind */
.bough-3 {
  animation-delay: -7s;
  animation-duration: 8s;
  right: -8%;
  top: 46%;
  width: 76%;
}

.bough-3 > * {
  filter: blur(0.65vmax);
}

/* The next window's one bough, hung where its corner shows */
.bough-4 {
  animation-delay: -2s;
  animation-duration: 10s;
  right: -10%;
  top: 12%;
  width: 80%;
}

/* The room dims away from the window */
.shade {
  background: radial-gradient(
    ellipse 85vmax 70vmax at 62% 44%,
    transparent 22%,
    var(--shade)
  );
}

@keyframes sun {
  from {
    transform: translate3d(-3vmax, -1vmax, 0) rotate(13deg) skewX(-31deg);
  }

  to {
    transform: translate3d(3vmax, 1.5vmax, 0) rotate(11deg) skewX(-34deg);
  }
}

@keyframes clouds {
  0%,
  100% {
    opacity: 1;
  }

  18% {
    opacity: 0.75;
  }

  31% {
    opacity: 0.95;
  }

  58% {
    opacity: 0.55;
  }

  72% {
    opacity: 0.6;
  }

  86% {
    opacity: 0.9;
  }
}

/* A breeze, not a pendulum: the bough is pushed over, eases most of
   the way back, is pushed further, and settles */
@keyframes sway {
  0%,
  100% {
    transform: rotate(-2.5deg);
  }

  18% {
    transform: rotate(3.5deg) translate3d(0.4vmax, 0.8vmax, 0);
  }

  34% {
    transform: rotate(0.5deg) translate3d(0.1vmax, 0.2vmax, 0);
  }

  55% {
    transform: rotate(5deg) translate3d(0.6vmax, 1.2vmax, 0);
  }

  72% {
    transform: rotate(1deg) translate3d(0.2vmax, 0.3vmax, 0);
  }

  88% {
    transform: rotate(-1.5deg);
  }
}

@keyframes flutter {
  from {
    transform: rotate(-5deg);
  }

  to {
    transform: rotate(5deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .room,
  .window,
  .panes,
  .bough,
  .bough .spray {
    animation: none;
  }
}
