/* ── The dream ─────────────────────────────────────────────────────────────
   The photograph starts remembering things that are not in it.

   This is the early-DeepDream failure mode, not a decoration laid on top: a
   network run against its own output until it finds, everywhere, the thing it
   was trained hardest to find. Eyes, mostly. The picture does not acquire eyes —
   it CORRUPTS INTO them, out of its own grain, wherever its own structure gives
   them somewhere to take hold.

   An earlier version drew fifteen eyes on an overlay and floated them over the
   page. That was the wrong thing: eyes ON an image rather than an image
   becoming eyes. Nothing here is placed at a coordinate of its own choosing —
   every ocellus is masked by the backdrop's own luminance, so it can only exist
   where the photograph already had something for it to grow out of.

   THE BACKDROP ITSELF IS LEFT ALONE. There were two more layers here: displaced,
   contrast-amplified copies of the photograph laid over the real one, so that
   the lake stopped holding still. They are removed. To displace a fog you must
   first amplify its grain to have anything to displace, and that amplifies its
   noise with it — over the backdrop's own four-layer blend the result was a
   heavier and grainier picture than the one it was built on. The four layers in
   base.css are the backdrop again, untouched, and the eyes carry the idea on
   their own.

   (It is four commits of work and it is in the history, not lost. `git log --
   site/css/dream.css` finds it if the amplified version is ever wanted back.)

   Restraint is still the whole design. Nothing here should be the moment you
   noticed it. It is `pointer-events: none` and `aria-hidden` throughout: it
   cannot take a click, cannot take focus, is not announced, and the page works
   identically with it removed.
   ------------------------------------------------------------------------ */

.dream__ocelli {
  position: fixed; inset: 0;
  pointer-events: none;
  overflow: hidden;
}

/* The eyes surface over the whole page, cards and text included: the dream does
   not respect what is in front of what. */
.dream__ocelli { z-index: 70; opacity: 0; }   /* dream.js fades it up over a minute */

/* ── the ocelli ────────────────────────────────────────────────────────────
   MASKED BY THE PICTURE. `mask-mode: luminance` means an eye is only as present
   as the photograph is bright underneath it — they take hold in the sky and the
   water and fade out in the dark of the treeline, which is what makes them read
   as having grown out of the image rather than as having been put on it.

   Where luminance masking is unsupported the mask falls back to alpha and the
   JPEG is opaque, so the eyes simply show everywhere. That is a worse effect,
   not a broken one.
   ------------------------------------------------------------------------ */
.dream__ocelli {
  -webkit-mask-image: url("../assets/img/backdrop/frame-17.jpg");
  mask-image: url("../assets/img/backdrop/frame-17.jpg");
  mask-mode: luminance;
  mask-size: cover;
  mask-position: 50% 66%;
}

.ocellus {
  position: absolute;
  left: var(--x); top: var(--y);
  width: var(--w);
  height: var(--w);                    /* round: the eye is a sphere seen head on */
  margin: calc(var(--w) * -.5) 0 0 calc(var(--w) * -.5);
  opacity: 0;
  transform: scale(.2) rotate(var(--tilt, 0deg));
  overflow: visible;

  /* The turbulence is applied PER EYE, not to the overlay.
     A filter on the container would mean every arriving eye invalidates the
     whole screen and Chrome re-runs a full-viewport feTurbulence displacement
     on that frame — forty of those overlapping is a lot of work for something
     nobody is supposed to notice. Filtering each one separately costs almost
     nothing, and the displacement field being per-element rather than
     continuous with the backdrop's is not something that can be seen.

     No `will-change`: these animate once and then never again, and promoting
     forty layers permanently to save one transition is the wrong trade. */
  filter: url("#dream-warp-eye");
}

/* ── the eye ───────────────────────────────────────────────────────────────
   Concentric circles and nothing else — no lid, no line work. The globe is
   pale and translucent so the photograph is still visible through it; the iris
   and the pupil are the only solid things on the page.
   ------------------------------------------------------------------------ */
.ocellus__globe { fill: #efe9dd; opacity: .46; }
.ocellus__band  { fill: none; stroke: #e8dfd0; stroke-width: 2.4; opacity: .3;
                  vector-effect: non-scaling-stroke; }
.ocellus__iris  { fill: #241b14; opacity: .66; }
.ocellus__ring  { fill: none; stroke: #ded3c2; stroke-width: 1.6; opacity: .42;
                  vector-effect: non-scaling-stroke; }
.ocellus__pupil { fill: #000; opacity: .9; }

/* ── the growth ────────────────────────────────────────────────────────────
   Shaded, not coloured: the fill is a radial gradient running from white at
   the upper left through nothing to black at the rim. Over a photograph that
   reads as something swelling up underneath it rather than as a grey shape
   lying on top — which is the difference between a lump in the picture and a
   sticker on it.
   ------------------------------------------------------------------------ */
.growth__mass,
.growth__bud {
  /* a dark edge as well as the shading: a lump has a rim where it turns away
     from the light, and without it these read as bokeh rather than as tissue */
  stroke: #16110c;
  stroke-width: 1.1;
  stroke-opacity: .34;
  vector-effect: non-scaling-stroke;
}

@media (prefers-reduced-motion: reduce) {
  /* The arrival was the whole effect and it is motion. Left at a trace of
     itself, held still. */
  .ocellus {
    opacity: .3 !important;
    transform: scale(1) rotate(var(--tilt, 0deg)) !important;
    animation: none !important;
  }
}
