/*
 * Russell Labs — Grid Ocean
 * -------------------------
 * A pure CSS animated background: a connected blue node grid laid out as a
 * body of water in perspective, with a slow swell rolling in toward the viewer.
 *
 * Structure (see index.html):
 *   .rl-ocean                 full-bleed container (position it where you need it)
 *     .rl-ocean__sky          deep gradient + horizon glow
 *     .rl-ocean__scene        perspective camera
 *       .rl-ocean__plane      the water surface, tilted, preserve-3d
 *         .rl-ocean__row x N  one grid row each, lifted on the surface normal
 *     .rl-ocean__fog          distance haze toward the horizon
 *     .rl-ocean__shore        bottom vignette so content sits on the water
 *     .rl-ocean__vignette     side and corner darkening
 *     .rl-ocean__grain        fine film grain
 *
 * Every row carries two overlapping swells (a primary one on the row, a
 * longer, slower one on its ::before) so the motion never reads as a loop.
 * Only transform and opacity animate. Honors prefers-reduced-motion.
 *
 * Tune the --rl-ocean-* custom properties; nothing else needs touching.
 */

.rl-ocean {
  /* Brand palette */
  --rl-ocean-ink: #020617;
  --rl-ocean-navy: #0f172a;
  --rl-ocean-blue: #2563eb;
  --rl-ocean-cyan: #00d4ff;
  --rl-ocean-violet: #7c3aed;

  /* Grid geometry */
  --rl-ocean-rows: 36;              /* must match the number of .rl-ocean__row elements */
  --rl-ocean-cell: 56px;            /* grid cell size on the plane, before perspective */
  --rl-ocean-plane-width: 4400px;   /* wider than any viewport so edges never show */
  --rl-ocean-tilt: 74deg;           /* camera angle; 90 = looking straight along the surface */
  --rl-ocean-horizon: 38%;          /* where the far edge of the water sits in the container */
  --rl-ocean-perspective: 820px;

  /* Line and node intensity */
  --rl-ocean-line: rgb(37 99 235 / 0.42);
  --rl-ocean-line-weight: 1px;
  --rl-ocean-node: rgb(0 212 255 / 0.95);
  --rl-ocean-node-size: 1.6px;

  /* Swell */
  --rl-ocean-amp: 64px;             /* primary swell height */
  --rl-ocean-period: 10s;           /* time for one primary crest to pass a row */
  --rl-ocean-phase: 0.72s;          /* delay between neighbouring rows: smaller = longer wavelength */
  --rl-ocean-amp-2: 22px;           /* secondary swell height */
  --rl-ocean-period-2: 17s;
  --rl-ocean-phase-2: 0.5s;
  --rl-ocean-crest: 0.45;           /* peak opacity of the cyan crest highlight */
  /* Row lean that matches the wave slope so neighbouring rows meet.
     slope = atan( pi * amp * phase / (period * cell) ). For the defaults: ~14deg. */
  --rl-ocean-slope: 14deg;

  --rl-ocean-ease: cubic-bezier(0.45, 0, 0.55, 1); /* sine-like in/out */

  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--rl-ocean-ink);
  isolation: isolate;
  pointer-events: none;
  contain: strict;
}

.rl-ocean > * {
  position: absolute;
  inset: 0;
}

/* Sky: near-black ink overhead, a restrained blue bloom at the horizon, a trace of violet high up. */
.rl-ocean__sky {
  background:
    radial-gradient(ellipse 58% 30% at 50% var(--rl-ocean-horizon),
      rgb(0 212 255 / 0.14), rgb(37 99 235 / 0.22) 24%, rgb(37 99 235 / 0.07) 50%, transparent 72%),
    radial-gradient(ellipse 55% 45% at 84% 0%,
      rgb(124 58 237 / 0.09), transparent 60%),
    linear-gradient(to bottom,
      #01030c 0%,
      #030818 34%,
      #061028 var(--rl-ocean-horizon),
      #050e26 68%,
      var(--rl-ocean-ink) 100%);
}

/* Horizon line: the far edge of the water, lit by the bloom behind it. */
.rl-ocean__sky::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: var(--rl-ocean-horizon);
  height: 1px;
  background: linear-gradient(90deg,
    transparent 10%, rgb(0 212 255 / 0.32) 40%, rgb(0 212 255 / 0.5) 50%,
    rgb(0 212 255 / 0.32) 60%, transparent 90%);
}

/* Canvas renderer (grid-ocean.js). Sits between the sky and the fog. */
.rl-ocean__canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* Camera (CSS-only renderer, see css-only.html) */
.rl-ocean__scene {
  perspective: var(--rl-ocean-perspective);
  perspective-origin: 50% var(--rl-ocean-horizon);
}

/* Water surface. Top edge pinned at the horizon, then tilted away from the viewer. */
.rl-ocean__plane {
  position: absolute;
  top: var(--rl-ocean-horizon);
  left: 50%;
  width: var(--rl-ocean-plane-width);
  height: calc(var(--rl-ocean-rows) * var(--rl-ocean-cell));
  transform-origin: 50% 0;
  transform: translateX(-50%) rotateX(var(--rl-ocean-tilt));
  transform-style: preserve-3d;
}

/*
 * One grid row. Carries its own horizontal line and the nodes sitting on it,
 * plus the vertical lines that run through the cell. Because the line and
 * nodes are centred in the row, every row is a complete mesh segment and can
 * rise independently without clipping.
 */
.rl-ocean__row {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--i) * var(--rl-ocean-cell));
  height: var(--rl-ocean-cell);
  transform-style: preserve-3d;
  will-change: transform;
  animation: rl-ocean-swell var(--rl-ocean-period) var(--rl-ocean-ease) infinite;
  animation-delay: calc((var(--rl-ocean-rows) - var(--i)) * var(--rl-ocean-phase) * -1);
}

/* The mesh itself, riding the secondary swell. */
.rl-ocean__row::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    radial-gradient(circle,
      var(--rl-ocean-node) 0 var(--rl-ocean-node-size),
      rgb(0 212 255 / 0.35) calc(var(--rl-ocean-node-size) + 0.6px),
      transparent calc(var(--rl-ocean-node-size) + 2.4px)),
    linear-gradient(90deg, var(--rl-ocean-line) 0 var(--rl-ocean-line-weight), transparent 0),
    linear-gradient(to bottom,
      transparent calc(50% - var(--rl-ocean-line-weight) / 2),
      var(--rl-ocean-line) calc(50% - var(--rl-ocean-line-weight) / 2),
      var(--rl-ocean-line) calc(50% + var(--rl-ocean-line-weight) / 2),
      transparent 0);
  background-size:
    var(--rl-ocean-cell) var(--rl-ocean-cell),
    var(--rl-ocean-cell) 100%,
    100% 100%;
  background-position:
    calc(var(--rl-ocean-cell) / 2 * -1) 0,
    0 0,
    0 0;
  background-repeat: repeat, repeat, no-repeat;
  will-change: transform;
  animation: rl-ocean-swell-2 var(--rl-ocean-period-2) var(--rl-ocean-ease) infinite;
  animation-delay: calc((var(--rl-ocean-rows) - var(--i)) * var(--rl-ocean-phase-2) * -1);
}

/* Crest light: a cyan wash that brightens as the row lifts, like light catching the face of a wave. */
.rl-ocean__row::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom,
      transparent calc(50% - 1px),
      rgb(0 212 255 / 0.9) calc(50% - 1px),
      rgb(0 212 255 / 0.9) calc(50% + 1px),
      transparent 0),
    radial-gradient(ellipse 50% 200% at 50% 50%, rgb(0 212 255 / 0.10), transparent 70%);
  opacity: 0;
  transform: translateZ(1px);
  animation: rl-ocean-crest var(--rl-ocean-period) var(--rl-ocean-ease) infinite;
  animation-delay: calc((var(--rl-ocean-rows) - var(--i)) * var(--rl-ocean-phase) * -1);
}

/* Distance haze: the far rows dissolve into the horizon bloom. Only covers the water. */
.rl-ocean__fog {
  background: linear-gradient(to bottom,
    rgb(7 17 48 / 0.9) var(--rl-ocean-horizon),
    rgb(7 17 48 / 0.45) calc(var(--rl-ocean-horizon) + 4%),
    rgb(7 17 48 / 0.12) calc(var(--rl-ocean-horizon) + 12%),
    transparent calc(var(--rl-ocean-horizon) + 26%));
  -webkit-mask-image: linear-gradient(to bottom, transparent calc(var(--rl-ocean-horizon) - 1%), black var(--rl-ocean-horizon));
  mask-image: linear-gradient(to bottom, transparent calc(var(--rl-ocean-horizon) - 1%), black var(--rl-ocean-horizon));
}

/* Shore: a quiet vignette so foreground content has a calm surface to sit on. */
.rl-ocean__shore {
  background:
    radial-gradient(ellipse 80% 60% at 50% 112%, rgb(2 6 23 / 0.6), transparent 70%),
    linear-gradient(to bottom, transparent 72%, rgb(2 6 23 / 0.65) 100%);
}

/* Vignette: darkens the sides so the eye stays on the lane of light. */
.rl-ocean__vignette {
  background:
    linear-gradient(90deg, rgb(2 6 23 / 0.55), transparent 22%, transparent 78%, rgb(2 6 23 / 0.55)),
    radial-gradient(ellipse 90% 90% at 50% 55%, transparent 55%, rgb(2 6 23 / 0.45) 100%);
}

/* Grain: fine film noise, which keeps large dark gradients from banding and reads as print rather than screen. */
.rl-ocean__grain {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 0.55 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
  background-size: 160px 160px;
  opacity: 0.045;
  mix-blend-mode: overlay;
}

@keyframes rl-ocean-swell {
  0%   { transform: translateZ(0) rotateX(0deg); }
  25%  { transform: translateZ(calc(var(--rl-ocean-amp) / 2)) rotateX(calc(var(--rl-ocean-slope) * -1)); }
  50%  { transform: translateZ(var(--rl-ocean-amp)) rotateX(0deg); }
  75%  { transform: translateZ(calc(var(--rl-ocean-amp) / 2)) rotateX(var(--rl-ocean-slope)); }
  100% { transform: translateZ(0) rotateX(0deg); }
}

@keyframes rl-ocean-swell-2 {
  0%   { transform: translateZ(0); }
  50%  { transform: translateZ(var(--rl-ocean-amp-2)); }
  100% { transform: translateZ(0); }
}

@keyframes rl-ocean-crest {
  0%   { opacity: 0; }
  40%  { opacity: 0; }
  54%  { opacity: var(--rl-ocean-crest); }
  70%  { opacity: 0; }
  100% { opacity: 0; }
}

/* Calm water for people who prefer less motion: a still surface, no swell. */
@media (prefers-reduced-motion: reduce) {
  .rl-ocean__row,
  .rl-ocean__row::before,
  .rl-ocean__row::after {
    animation: none;
  }
  .rl-ocean__row::after {
    opacity: calc(var(--rl-ocean-crest) * 0.25);
  }
}

/* Variants */
.rl-ocean--calm {
  --rl-ocean-amp: 22px;
  --rl-ocean-amp-2: 8px;
  --rl-ocean-period: 13s;
  --rl-ocean-slope: 4deg;
  --rl-ocean-crest: 0.3;
}

.rl-ocean--storm {
  --rl-ocean-amp: 90px;
  --rl-ocean-amp-2: 30px;
  --rl-ocean-period: 6s;
  --rl-ocean-phase: 0.42s;
  --rl-ocean-slope: 19deg;
  --rl-ocean-crest: 0.7;
}
