/* ocean.css — the background layers behind the page.
 *
 * History worth keeping: this file first built an animated underwater scene in
 * CSS, then positioned a 2D canvas that painted one per frame. Both were removed
 * because they animated on the main thread and scrolling fell to 12fps; a still
 * photograph replaced them.
 *
 * The scene is back, but on the GPU this time (assets/js/ocean3d.js). The
 * photograph did not leave — it is now the poster shown while WebGL starts up,
 * and the permanent fallback for anyone whose browser cannot give us a WebGL2
 * context. Deleting it would mean a blank background for those visitors, so it
 * stays.
 *
 * Stacking, deepest first:
 *   -5  .bgmesh       the site's own colour wash
 *   -4  .ocean-photo  poster / fallback
 *   -3  #ocean3d      the live scene
 * Page content is in normal flow and paints above every negative layer without
 * needing a z-index of its own.
 */

#ocean3d,
.ocean-photo {
  position: fixed;
  inset: 0;
  /* The document is dir="rtl". An absolutely-positioned element with no `left`
     resolves its position from the *right* edge, so on an RTL page a full-bleed
     layer that only sets `inset` in a browser that does not honour the shorthand
     drifts off-screen. Saying it twice costs nothing and has bitten this file
     before. */
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

#ocean3d {
  z-index: -3;
  /* Faded in once the first frame is on screen, so the scene never appears as a
     black rectangle over the poster while shaders compile. */
  opacity: 0;
  transition: opacity 1.1s ease;
}

:root.ocean3d-ready #ocean3d { opacity: 1; }

/* Once the live scene is up, the photograph underneath it is completely
   occluded. Taking it out of the paint tree entirely means the compositor is not
   blending a full-viewport image behind an opaque canvas every frame. It comes
   straight back if the WebGL context is lost, because that removes the class. */
:root.ocean3d-ready .ocean-photo { display: none; }

/* ── The poster / fallback ─────────────────────────────────────────────── */

.ocean-photo {
  z-index: -4;
  background-image: url('../img/whale-bg.jpg');
  background-size: cover;
  background-position: center 38%;
  /* `background-attachment: fixed` would be the obvious way to pin this, but on
     iOS Safari it is either ignored or forces a repaint of the whole viewport on
     every scroll frame. A position:fixed element looks the same and costs
     nothing. */
  background-repeat: no-repeat;
  opacity: .42;
}

/* A vertical scrim: darkest where the copy sits, clear at the edges so the
   photograph still reads as a photograph. */
.ocean-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  left: 0;
  background: linear-gradient(180deg,
    rgba(1, 12, 22, .55) 0%,
    rgba(1, 12, 22, .30) 35%,
    rgba(1, 12, 22, .55) 100%);
}

/* Phones get the smaller file. Two thirds fewer bytes on the connection least
   able to spare them, and it is never displayed large enough to tell. */
@media (max-width: 900px) {
  .ocean-photo {
    background-image: url('../img/whale-bg-sm.jpg');
    /* Portrait screens crop hard horizontally; biasing toward the head keeps the
       animal recognisable instead of showing a slab of flank. */
    background-position: 62% 42%;
    opacity: .34;
  }
}

/* Light theme: the photograph is a dark underwater scene and cannot be
   recoloured into a light one, so it recedes further and a pale wash sits over
   it to keep dark text readable. */
.light-theme .ocean-photo { opacity: .16; }
.light-theme .ocean-photo::after {
  background: linear-gradient(180deg, rgba(234, 246, 253, .55), rgba(220, 238, 249, .70));
}

/* The live scene is a dark underwater column for the same reason, so the light
   theme veils it rather than trying to recolour it. The theme class is toggled
   on documentElement, which is the same element as :root — so this is one
   compound selector, not a descendant one. */
:root.ocean3d-ready.light-theme #ocean3d { opacity: .30; }

/* The site's own colour wash stays behind everything. */
.bgmesh { z-index: -5; }

/* ── The one blur that survived ───────────────────────────────────────────
 *
 * perf.css switches backdrop-filter off everywhere except the header, after it
 * was measured at 24fps with the panels blurring versus 60 without. The header
 * was kept because there is exactly one of it and it never scrolls.
 *
 * That reasoning assumed a *static* backdrop. It no longer holds: the scene now
 * moves every frame, so a blurred header has to re-sample a changing backdrop
 * 60 times a second, and it is pinned over the busiest part of the water. The
 * header keeps its translucent background — it still reads as glass — and loses
 * only the refraction.
 *
 * This lives here rather than in perf.css because it is a consequence of the
 * animated scene: if the scene is ever removed, this rule stops applying on its
 * own, instead of silently outliving its reason.
 */
:root.ocean3d-ready header {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

.ocean-photo, #ocean3d { pointer-events: none; }

/* Page content sits above both.
 *
 * This used to be `body > *:not(.ocean-photo):not(.bgmesh) { position: relative;
 * z-index: 1 }` and it was a trap. That selector scores higher than a bare
 * `header { position: fixed; z-index: 50 }`, so it silently reassigned the site
 * header to `position: relative; z-index: 1` — the header stopped being fixed
 * and lost every stacking fight on the page, which is what put the hamburger
 * button behind other content.
 *
 * The rule was never needed: the backgrounds sit at negative z-index, and
 * normal-flow content already paints above negative layers. Anything that
 * genuinely needs lifting should say so itself rather than have a background
 * stylesheet reach out and restyle every element on the page.
 */
