0
Skip to Content
Cyperus
Cyperus

Cyperus

<!-- Cyperus Preservation Blog runtime: paste this whole block into Squarespace Footer Code Injection. -->
<script>
    (function () {
      function cyPreservationInit() {
      var roots = document.querySelectorAll("[data-cy-preservation-blog]");
      if (!roots.length) return;

      var TAU = Math.PI * 2;
      var prefersReducedMotion = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
      var palette = {
        cream: [5, 5, 5],
        oat: [78, 100, 125],
        taupe: [158, 148, 139],
        slate: [78, 100, 125],
        blue: [45, 64, 143],
        black: [5, 5, 5]
      };

      function clamp(value, min, max) {
        return Math.max(min, Math.min(max, value));
      }

      function easeInOutCubic(t) {
        var c = clamp(t, 0, 1);
        return c < 0.5 ? 4 * c * c * c : 1 - Math.pow(-2 * c + 2, 3) / 2;
      }

      function easeOutCubic(t) {
        var c = clamp(t, 0, 1);
        return 1 - Math.pow(1 - c, 3);
      }

      function rgba(color, alpha) {
        return "rgba(" + color[0] + ", " + color[1] + ", " + color[2] + ", " + alpha + ")";
      }

      function seeded(seed) {
        var x = Math.sin(seed) * 10000;
        return x - Math.floor(x);
      }

      function cubicPoint(p0, p1, p2, p3, t) {
        var mt = 1 - t;
        return {
          x: mt * mt * mt * p0.x + 3 * mt * mt * t * p1.x + 3 * mt * t * t * p2.x + t * t * t * p3.x,
          y: mt * mt * mt * p0.y + 3 * mt * mt * t * p1.y + 3 * mt * t * t * p2.y + t * t * t * p3.y
        };
      }

      function buildQuestionPath() {
        var points = [];
        var curves = [
          { p0: { x: -42, y: -22 }, p1: { x: -45, y: -64 }, p2: { x: -18, y: -88 }, p3: { x: 12, y: -86 } },
          { p0: { x: 12, y: -86 }, p1: { x: 48, y: -84 }, p2: { x: 66, y: -58 }, p3: { x: 60, y: -29 } },
          { p0: { x: 60, y: -29 }, p1: { x: 55, y: -5 }, p2: { x: 34, y: 8 }, p3: { x: 10, y: 22 } },
          { p0: { x: 10, y: 22 }, p1: { x: -3, y: 30 }, p2: { x: -7, y: 40 }, p3: { x: -7, y: 58 } }
        ];
        curves.forEach(function (curve, curveIndex) {
          for (var i = 0; i <= 34; i += 1) {
            if (curveIndex > 0 && i === 0) continue;
            points.push(cubicPoint(curve.p0, curve.p1, curve.p2, curve.p3, i / 34));
          }
        });
        return points;
      }

      var QUESTION_POINTS = buildQuestionPath();
      var TRACE_POINTS = QUESTION_POINTS.concat([{ x: -6, y: 68 }, { x: -3, y: 78 }, { x: 0, y: 88 }]);

      function drawPolyline(ctx, points, progress) {
        var c = clamp(progress, 0, 1);
        var exact = c * (points.length - 1);
        var whole = Math.floor(exact);
        var local = exact - whole;

        ctx.beginPath();
        points.forEach(function (point, index) {
          if (index === 0) {
            ctx.moveTo(point.x, point.y);
          } else if (index <= whole) {
            ctx.lineTo(point.x, point.y);
          }
        });

        if (whole < points.length - 1) {
          var current = points[whole];
          var next = points[whole + 1];
          ctx.lineTo(current.x + (next.x - current.x) * local, current.y + (next.y - current.y) * local);
        }
      }

      function roundRect(ctx, x, y, width, height, radius) {
        var r = Math.min(radius, width / 2, height / 2);
        ctx.beginPath();
        ctx.moveTo(x + r, y);
        ctx.lineTo(x + width - r, y);
        ctx.quadraticCurveTo(x + width, y, x + width, y + r);
        ctx.lineTo(x + width, y + height - r);
        ctx.quadraticCurveTo(x + width, y + height, x + width - r, y + height);
        ctx.lineTo(x + r, y + height);
        ctx.quadraticCurveTo(x, y + height, x, y + height - r);
        ctx.lineTo(x, y + r);
        ctx.quadraticCurveTo(x, y, x + r, y);
        ctx.closePath();
      }

      function traceCubic(ctx, p0, p1, p2, p3, progress) {
        var steps = Math.max(2, Math.floor(56 * clamp(progress, 0, 1)));
        ctx.beginPath();
        for (var i = 0; i <= steps; i += 1) {
          var p = cubicPoint(p0, p1, p2, p3, i / 56);
          if (i === 0) ctx.moveTo(p.x, p.y);
          else ctx.lineTo(p.x, p.y);
        }
      }

      function drawWave(ctx, x, y, w, amp, progress, t, color, lineWidth) {
        var visible = w * clamp(progress, 0, 1);
        ctx.beginPath();
        for (var i = 0; i <= 80; i += 1) {
          var nx = i / 80;
          var px = x + nx * visible;
          var py = y + Math.sin(nx * TAU * 4 + t) * amp * (0.45 + 0.55 * Math.sin(nx * Math.PI));
          if (i === 0) ctx.moveTo(px, py);
          else ctx.lineTo(px, py);
        }
        ctx.strokeStyle = color;
        ctx.lineWidth = lineWidth;
        ctx.lineCap = "round";
        ctx.stroke();
      }

      function Scene(element, index) {
        this.element = element;
        this.canvas = element.querySelector("canvas");
        this.ctx = this.canvas && this.canvas.getContext("2d", { alpha: true });
        this.theme = element.getAttribute("data-theme") || "preserve";
        this.index = index;
        this.width = 1;
        this.height = 1;
        this.dpr = 1;
        this.progress = 0;
        this.visible = false;
        this.particles = [];
        this.nodes = [];
        this.lastDraw = 0;

        for (var i = 0; i < 34; i += 1) {
          this.nodes.push({
            angle: (i * TAU) / 34,
            radius: 70 + (i % 7) * 18,
            speed: 0.03 + (i % 5) * 0.012,
            size: 1 + (i % 3) * 0.45,
            phase: i * 0.67 + index
          });
        }

        this.resize = this.resize.bind(this);
        this.resize();

        if (window.ResizeObserver) {
          this.observer = new ResizeObserver(this.resize);
          this.observer.observe(this.canvas.parentElement || this.element);
        } else {
          window.addEventListener("resize", this.resize);
        }
      }

      Scene.prototype.resize = function () {
        if (!this.canvas || !this.ctx) return;
        var parent = this.canvas.parentElement || this.element;
        var rect = parent.getBoundingClientRect();
        this.width = Math.max(1, rect.width || 800);
        this.height = Math.max(1, rect.height || 288);
        this.dpr = Math.min(window.devicePixelRatio || 1, 2);
        this.canvas.width = Math.floor(this.width * this.dpr);
        this.canvas.height = Math.floor(this.height * this.dpr);
        this.canvas.style.width = this.width + "px";
        this.canvas.style.height = this.height + "px";
        this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);
        this.draw(performance.now(), true);
      };

      Scene.prototype.updateProgress = function () {
        var rect = this.element.getBoundingClientRect();
        var vh = window.innerHeight || document.documentElement.clientHeight || 800;
        var raw = (vh * 0.88 - rect.top) / (vh * 0.72 + rect.height * 0.45);
        this.progress = easeInOutCubic(raw);
        this.visible = rect.bottom > -80 && rect.top < vh + 80;
        return this.visible;
      };

      Scene.prototype.emit = function (x, y, unit, count, angle, spread) {
        var colors = [palette.blue, palette.slate, palette.taupe, palette.oat, palette.cream];
        for (var i = 0; i < count; i += 1) {
          var seed = performance.now() * 0.001 + i * 19 + this.index * 31;
          var a = angle + (seeded(seed) - 0.5) * spread;
          var speed = (0.8 + seeded(seed + 2) * 3.2) * unit;
          var life = 0.65 + seeded(seed + 4) * 0.7;
          this.particles.push({
            x: x,
            y: y,
            vx: Math.cos(a) * speed,
            vy: Math.sin(a) * speed,
            life: life,
            maxLife: life,
            size: (1.2 + seeded(seed + 6) * 2.8) * unit,
            color: colors[Math.floor(seeded(seed + 8) * colors.length)]
          });
        }
      };

      Scene.prototype.background = function (ctx, cx, cy, unit, p, t) {
        var glow = ctx.createRadialGradient(cx, cy, 0, cx, cy, Math.max(this.width, this.height) * 0.58);
        glow.addColorStop(0, rgba(palette.blue, 0.12 + p * 0.08));
        glow.addColorStop(0.48, rgba(palette.slate, 0.06));
        glow.addColorStop(1, rgba(palette.black, 0));
        ctx.fillStyle = glow;
        ctx.fillRect(0, 0, this.width, this.height);

        ctx.save();
        ctx.translate(cx, cy);
        ctx.scale(1, 0.58);
        for (var i = 0; i < 3; i += 1) {
          var radius = (70 + i * 38) * unit;
          ctx.save();
          ctx.rotate(t * (i % 2 ? -0.11 : 0.13) + this.index);
          ctx.beginPath();
          ctx.arc(0, 0, radius, 0, TAU);
          ctx.strokeStyle = rgba(i === 1 ? palette.taupe : palette.blue, 0.055 + p * 0.045);
          ctx.lineWidth = 1.1 * unit;
          ctx.stroke();
          for (var j = 0; j < 4; j += 1) {
            var start = (j * TAU) / 4 + Math.sin(t + j) * 0.08;
            ctx.beginPath();
            ctx.arc(0, 0, radius, start, start + 0.24 + p * 0.12);
            ctx.strokeStyle = rgba(i === 1 ? palette.taupe : palette.oat, 0.12 + p * 0.14);
            ctx.lineWidth = 1.5 * unit;
            ctx.lineCap = "round";
            ctx.stroke();
          }
          ctx.restore();
        }

        this.nodes.forEach(function (node, nodeIndex) {
          var a = node.angle + t * node.speed;
          var x = Math.cos(a) * node.radius * unit;
          var y = Math.sin(a) * node.radius * unit;
          var alpha = (0.06 + Math.max(0, Math.sin(t * 1.2 + node.phase)) * 0.14) * (0.7 + p * 0.8);
          ctx.fillStyle = rgba(nodeIndex % 4 === 0 ? palette.taupe : palette.oat, alpha);
          ctx.beginPath();
          ctx.arc(x, y, node.size * unit, 0, TAU);
          ctx.fill();
        });
        ctx.restore();
      };

      Scene.prototype.drawParticles = function (ctx, dt) {
        for (var i = this.particles.length - 1; i >= 0; i -= 1) {
          var particle = this.particles[i];
          particle.x += particle.vx * dt * 60;
          particle.y += particle.vy * dt * 60;
          particle.vx *= 0.984;
          particle.vy += 0.015 * dt * 60;
          particle.life -= dt;
          if (particle.life <= 0) {
            this.particles.splice(i, 1);
            continue;
          }
          var alpha = clamp(particle.life / particle.maxLife, 0, 1);
          ctx.fillStyle = rgba(particle.color, alpha * 0.78);
          ctx.beginPath();
          ctx.arc(particle.x, particle.y, particle.size * alpha, 0, TAU);
          ctx.fill();
        }
      };

      Scene.prototype.draw = function (now, forceStatic) {
        if (!this.ctx) return;
        var ctx = this.ctx;
        var dt = Math.min(0.033, (now - (this.lastDraw || now)) / 1000 || 0.016);
        this.lastDraw = now;

        var t = prefersReducedMotion ? 2.8 + this.index : now / 1000;
        var p = prefersReducedMotion ? 0.86 : this.progress;
        var cx = this.width / 2;
        var cy = this.height / 2;
        var unit = Math.min(this.width / 900, this.height / 300);

        ctx.clearRect(0, 0, this.width, this.height);
        this.background(ctx, cx, cy, unit, p, t);

        var drawTheme = this["draw_" + this.theme] || this.draw_preserve;
        drawTheme.call(this, ctx, cx, cy, unit, p, t);

        if (!forceStatic) this.drawParticles(ctx, dt);
      };

      Scene.prototype.draw_hero = function (ctx, cx, cy, unit, p, t) {
        var scale = Math.min(this.width / 1200, this.height / 650);
        ctx.save();
        ctx.translate(cx, cy + 26 * scale);
        ctx.scale(scale, scale);

        for (var i = 0; i < 9; i += 1) {
          var y = -210 + i * 54;
          var width = 480 + i * 82;
          var alpha = 0.08 + i * 0.012;
          ctx.beginPath();
          ctx.ellipse(0, y, width * (0.42 + p * 0.18), 24 + i * 6, Math.sin(t * 0.16 + i) * 0.06, 0, TAU);
          ctx.strokeStyle = rgba(i % 2 ? palette.taupe : palette.blue, alpha);
          ctx.lineWidth = 1.5;
          ctx.stroke();
        }

        for (var j = 0; j < 56; j += 1) {
          var seed = j * 7.1;
          var x = -520 + seeded(seed) * 1040;
          var y2 = -270 + seeded(seed + 2) * 500;
          var drift = Math.sin(t * 0.36 + seed) * 16;
          ctx.fillStyle = rgba(j % 5 === 0 ? palette.taupe : palette.oat, 0.1 + seeded(seed + 5) * 0.2);
          ctx.beginPath();
          ctx.arc(x + drift, y2, 1.1 + seeded(seed + 6) * 2.3, 0, TAU);
          ctx.fill();
        }

        ctx.beginPath();
        traceCubic(ctx, { x: -470, y: -20 }, { x: -240, y: -132 }, { x: 230, y: 116 }, { x: 490, y: -28 }, p);
        ctx.strokeStyle = rgba(palette.oat, 0.42);
        ctx.lineWidth = 4.8;
        ctx.lineCap = "round";
        ctx.stroke();

        ctx.restore();
      };

      Scene.prototype.draw_preserve = function (ctx, cx, cy, unit, p, t) {
        var r = 72 * unit;
        ctx.save();
        ctx.translate(cx, cy);

        ctx.strokeStyle = rgba(palette.oat, 0.18);
        ctx.lineWidth = 14 * unit;
        ctx.beginPath();
        ctx.arc(0, 0, r, 0, TAU);
        ctx.stroke();

        var gradient = ctx.createLinearGradient(-r, -r, r, r);
        gradient.addColorStop(0, rgba(palette.oat, 0.9));
        gradient.addColorStop(0.45, rgba(palette.blue, 0.88));
        gradient.addColorStop(1, rgba(palette.taupe, 0.88));
        ctx.strokeStyle = gradient;
        ctx.lineWidth = 5.8 * unit;
        ctx.lineCap = "round";
        ctx.beginPath();
        ctx.arc(0, 0, r, -Math.PI / 2, -Math.PI / 2 + TAU * p);
        ctx.stroke();

        for (var i = 0; i < 10; i += 1) {
          var a = -Math.PI / 2 + TAU * (i / 10);
          var local = clamp(p * 1.3 - i * 0.08, 0, 1);
          var x = Math.cos(a) * (r + 34 * unit);
          var y = Math.sin(a) * (r + 34 * unit);
          ctx.fillStyle = rgba(i % 2 ? palette.taupe : palette.cream, local * 0.75);
          ctx.beginPath();
          ctx.arc(x, y, (2.2 + local * 3.2) * unit, 0, TAU);
          ctx.fill();
        }

        ctx.fillStyle = rgba(palette.black, 0.34);
        roundRect(ctx, -48 * unit, -35 * unit, 96 * unit, 70 * unit, 7 * unit);
        ctx.fill();
        ctx.strokeStyle = rgba(palette.oat, 0.36 + p * 0.28);
        ctx.lineWidth = 1.2 * unit;
        ctx.stroke();

        for (var row = 0; row < 5; row += 1) {
          var lineP = clamp(p * 1.5 - row * 0.14, 0, 1);
          ctx.beginPath();
          ctx.moveTo(-30 * unit, (-20 + row * 10) * unit);
          ctx.lineTo((-30 + 60 * lineP) * unit, (-20 + row * 10) * unit);
          ctx.strokeStyle = rgba(row % 2 ? palette.taupe : palette.oat, 0.7);
          ctx.lineWidth = 1.8 * unit;
          ctx.lineCap = "round";
          ctx.stroke();
        }

        ctx.restore();
      };

      Scene.prototype.draw_dignity = function (ctx, cx, cy, unit, p, t) {
        ctx.save();
        ctx.translate(cx, cy + 20 * unit);

        for (var i = 0; i < 7; i += 1) {
          var x = (-210 + i * 70) * unit;
          var h = (56 + Math.sin(t * 0.6 + i) * 8 + p * 68) * unit;
          ctx.fillStyle = rgba(i % 2 ? palette.slate : palette.blue, 0.1 + p * 0.09);
          roundRect(ctx, x - 12 * unit, -h / 2, 24 * unit, h, 6 * unit);
          ctx.fill();
          ctx.strokeStyle = rgba(palette.oat, 0.16 + p * 0.16);
          ctx.lineWidth = 1 * unit;
          ctx.stroke();
        }

        var lift = easeOutCubic(p) * 52 * unit;
        roundRect(ctx, -84 * unit, 35 * unit - lift, 168 * unit, 24 * unit, 5 * unit);
        ctx.fillStyle = rgba(palette.oat, 0.5);
        ctx.fill();
        ctx.strokeStyle = rgba(palette.taupe, 0.42);
        ctx.stroke();

        for (var j = 0; j < 4; j += 1) {
          ctx.beginPath();
          ctx.ellipse(0, -10 * unit - lift, (92 + j * 22) * unit, (18 + j * 4) * unit, 0, 0, TAU);
          ctx.strokeStyle = rgba(j % 2 ? palette.blue : palette.taupe, (0.16 + j * 0.035) * p);
          ctx.lineWidth = 1.4 * unit;
          ctx.stroke();
        }

        ctx.restore();
      };

      Scene.prototype.draw_checklist = function (ctx, cx, cy, unit, p, t) {
        ctx.save();
        ctx.translate(cx, cy);
        for (var i = 0; i < 6; i += 1) {
          var local = clamp(p * 1.25 - i * 0.11, 0, 1);
          var y = (-78 + i * 31) * unit;
          var x = -178 * unit;
          roundRect(ctx, x, y, 356 * unit, 17 * unit, 5 * unit);
          ctx.fillStyle = rgba(palette.black, 0.24);
          ctx.fill();
          ctx.strokeStyle = rgba(i % 2 ? palette.slate : palette.taupe, 0.16 + local * 0.34);
          ctx.lineWidth = 1 * unit;
          ctx.stroke();

          roundRect(ctx, x + 10 * unit, y + 3.5 * unit, 10 * unit, 10 * unit, 2 * unit);
          ctx.fillStyle = rgba(palette.oat, 0.12 + local * 0.38);
          ctx.fill();

          ctx.beginPath();
          ctx.moveTo(x + 34 * unit, y + 8.5 * unit);
          ctx.lineTo(x + (34 + 230 * local) * unit, y + 8.5 * unit);
          ctx.strokeStyle = rgba(i % 2 ? palette.oat : palette.blue, 0.32 + local * 0.5);
          ctx.lineWidth = 2 * unit;
          ctx.lineCap = "round";
          ctx.stroke();
        }
        ctx.restore();
      };

      Scene.prototype.draw_continue = function (ctx, cx, cy, unit, p, t) {
        ctx.save();
        ctx.translate(cx, cy);
        var nodes = [
          { x: -180, y: 10 },
          { x: -54, y: -48 },
          { x: 72, y: 36 },
          { x: 190, y: -18 }
        ];

        for (var i = 0; i < nodes.length - 1; i += 1) {
          var a = nodes[i];
          var b = nodes[i + 1];
          traceCubic(ctx, { x: a.x * unit, y: a.y * unit }, { x: (a.x + 54) * unit, y: (a.y - 80) * unit }, { x: (b.x - 54) * unit, y: (b.y + 80) * unit }, { x: b.x * unit, y: b.y * unit }, clamp(p * 1.2 - i * 0.16, 0, 1));
          ctx.strokeStyle = rgba(i % 2 ? palette.taupe : palette.oat, 0.28 + p * 0.46);
          ctx.lineWidth = 2.4 * unit;
          ctx.lineCap = "round";
          ctx.stroke();
        }

        nodes.forEach(function (node, index) {
          var pulse = 1 + Math.sin(t * 2 + index) * 0.08;
          ctx.beginPath();
          ctx.arc(node.x * unit, node.y * unit, (10 + p * 5) * pulse * unit, 0, TAU);
          ctx.fillStyle = rgba(index % 2 ? palette.blue : palette.taupe, 0.5 + p * 0.28);
          ctx.fill();
          ctx.beginPath();
          ctx.arc(node.x * unit, node.y * unit, (24 + index * 6) * unit, 0, TAU);
          ctx.strokeStyle = rgba(palette.oat, 0.1 + p * 0.2);
          ctx.lineWidth = 1 * unit;
          ctx.stroke();
        });

        drawWave(ctx, -210 * unit, 86 * unit, 420 * unit, 12 * unit, p, t * 2.4, rgba(palette.oat, 0.52), 2 * unit);
        ctx.restore();
      };

      Scene.prototype.draw_social = function (ctx, cx, cy, unit, p, t) {
        ctx.save();
        ctx.translate(cx, cy);
        var frames = [
          { x: -172, y: -68, w: 74, h: 132 },
          { x: -42, y: -78, w: 84, h: 150 },
          { x: 112, y: -63, w: 72, h: 128 }
        ];
        frames.forEach(function (frame, i) {
          var local = clamp(p * 1.2 - i * 0.14, 0, 1);
          ctx.save();
          ctx.translate(frame.x * unit, frame.y * unit);
          ctx.rotate(Math.sin(t * 0.4 + i) * 0.035);
          roundRect(ctx, 0, 0, frame.w * unit, frame.h * unit, 8 * unit);
          ctx.fillStyle = rgba(palette.black, 0.32);
          ctx.fill();
          ctx.strokeStyle = rgba(i % 2 ? palette.oat : palette.blue, 0.22 + local * 0.48);
          ctx.lineWidth = 1.5 * unit;
          ctx.stroke();
          for (var bar = 0; bar < 5; bar += 1) {
            ctx.fillStyle = rgba(bar % 2 ? palette.taupe : palette.slate, 0.16 + local * 0.25);
            roundRect(ctx, 10 * unit, (18 + bar * 18) * unit, (frame.w - 20) * unit * (0.25 + local * (0.7 - bar * 0.05)), 4 * unit, 2 * unit);
            ctx.fill();
          }
          drawWave(ctx, 11 * unit, (frame.h - 20) * unit, (frame.w - 22) * unit, 4 * unit, local, t * 3 + i, rgba(palette.oat, 0.55), 1.4 * unit);
          ctx.restore();
        });

        ctx.beginPath();
        ctx.moveTo(-86 * unit, 4 * unit);
        ctx.lineTo(86 * unit, 4 * unit);
        ctx.strokeStyle = rgba(palette.taupe, 0.42 * p);
        ctx.lineWidth = 1.2 * unit;
        ctx.setLineDash([5 * unit, 10 * unit]);
        ctx.stroke();
        ctx.setLineDash([]);
        ctx.restore();
      };

      Scene.prototype.draw_series = function (ctx, cx, cy, unit, p, t) {
        ctx.save();
        ctx.translate(cx, cy);
        ctx.beginPath();
        ctx.moveTo(-260 * unit, 0);
        ctx.lineTo(( -260 + 520 * p) * unit, 0);
        ctx.strokeStyle = rgba(palette.oat, 0.42);
        ctx.lineWidth = 2 * unit;
        ctx.lineCap = "round";
        ctx.stroke();

        for (var i = 0; i < 6; i += 1) {
          var x = (-240 + i * 96) * unit;
          var local = clamp(p * 1.35 - i * 0.12, 0, 1);
          roundRect(ctx, x - 32 * unit, (-26 - (i % 2) * 24) * unit, 64 * unit, 25 * unit, 12 * unit);
          ctx.fillStyle = rgba(i % 2 ? palette.blue : palette.slate, 0.22 + local * 0.28);
          ctx.fill();
          ctx.strokeStyle = rgba(palette.oat, 0.15 + local * 0.32);
          ctx.stroke();
          drawWave(ctx, x - 22 * unit, (-13 - (i % 2) * 24) * unit, 44 * unit, 4 * unit, local, t * 2 + i, rgba(palette.oat, 0.62), 1.3 * unit);
          ctx.beginPath();
          ctx.arc(x, 0, (5 + local * 5) * unit, 0, TAU);
          ctx.fillStyle = rgba(i % 2 ? palette.taupe : palette.oat, 0.45 + local * 0.38);
          ctx.fill();
        }
        ctx.restore();
      };

      Scene.prototype.draw_course = function (ctx, cx, cy, unit, p, t) {
        ctx.save();
        ctx.translate(cx, cy);
        var cols = 4;
        var rows = 3;
        for (var row = 0; row < rows; row += 1) {
          for (var col = 0; col < cols; col += 1) {
            var i = row * cols + col;
            var local = clamp(p * 1.35 - i * 0.055, 0, 1);
            var x = (-174 + col * 116) * unit;
            var y = (-70 + row * 60) * unit;
            roundRect(ctx, x, y, 86 * unit, 38 * unit, 5 * unit);
            ctx.fillStyle = rgba(i % 3 === 0 ? palette.blue : palette.black, 0.15 + local * 0.22);
            ctx.fill();
            ctx.strokeStyle = rgba(i % 2 ? palette.taupe : palette.oat, 0.12 + local * 0.32);
            ctx.lineWidth = 1 * unit;
            ctx.stroke();
            ctx.beginPath();
            ctx.moveTo(x + 14 * unit, y + 15 * unit);
            ctx.lineTo(x + (14 + 48 * local) * unit, y + 15 * unit);
            ctx.strokeStyle = rgba(palette.oat, 0.48);
            ctx.lineWidth = 1.8 * unit;
            ctx.stroke();
            ctx.beginPath();
            ctx.moveTo(x + 14 * unit, y + 25 * unit);
            ctx.lineTo(x + (14 + 30 * local) * unit, y + 25 * unit);
            ctx.strokeStyle = rgba(palette.taupe, 0.44);
            ctx.stroke();
          }
        }
        ctx.restore();
      };

      Scene.prototype.draw_gated = function (ctx, cx, cy, unit, p, t) {
        ctx.save();
        ctx.translate(cx, cy);
        for (var i = 0; i < 5; i += 1) {
          var local = clamp(p * 1.2 - i * 0.1, 0, 1);
          var w = (250 - i * 34) * unit;
          var h = (154 - i * 20) * unit;
          roundRect(ctx, -w / 2, -h / 2, w, h, 10 * unit);
          ctx.strokeStyle = rgba(i % 2 ? palette.blue : palette.taupe, 0.12 + local * 0.22);
          ctx.lineWidth = (1 + i * 0.18) * unit;
          ctx.stroke();
        }

        ctx.beginPath();
        ctx.arc(0, -10 * unit, 36 * unit, Math.PI, TAU);
        ctx.strokeStyle = rgba(palette.oat, 0.38 + p * 0.28);
        ctx.lineWidth = 5 * unit;
        ctx.lineCap = "round";
        ctx.stroke();
        roundRect(ctx, -48 * unit, -8 * unit, 96 * unit, 70 * unit, 9 * unit);
        ctx.fillStyle = rgba(palette.black, 0.34);
        ctx.fill();
        ctx.strokeStyle = rgba(palette.oat, 0.34 + p * 0.32);
        ctx.lineWidth = 1.5 * unit;
        ctx.stroke();
        ctx.beginPath();
        ctx.arc(0, 24 * unit, 7 * unit, 0, TAU);
        ctx.fillStyle = rgba(palette.taupe, 0.72);
        ctx.fill();
        ctx.fillRect(-2.5 * unit, 28 * unit, 5 * unit, 18 * unit);
        ctx.restore();
      };

      Scene.prototype.draw_questions = function (ctx, cx, cy, unit, p, t) {
        ctx.save();
        ctx.translate(cx, cy - 10 * unit);
        ctx.scale(unit, unit);

        ctx.beginPath();
        QUESTION_POINTS.forEach(function (point, index) {
          if (index === 0) ctx.moveTo(point.x, point.y);
          else ctx.lineTo(point.x, point.y);
        });
        ctx.strokeStyle = rgba(palette.blue, 0.18);
        ctx.lineWidth = 18;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.stroke();

        var gradient = ctx.createLinearGradient(-55, -90, 65, 90);
        gradient.addColorStop(0, rgba(palette.oat, 0.95));
        gradient.addColorStop(0.35, rgba(palette.taupe, 0.95));
        gradient.addColorStop(0.62, rgba(palette.blue, 0.94));
        gradient.addColorStop(1, rgba(palette.cream, 0.92));
        ctx.beginPath();
        QUESTION_POINTS.forEach(function (point, index) {
          if (index === 0) ctx.moveTo(point.x, point.y);
          else ctx.lineTo(point.x, point.y);
        });
        ctx.strokeStyle = gradient;
        ctx.lineWidth = 6.5;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.stroke();

        drawPolyline(ctx, TRACE_POINTS, p);
        ctx.strokeStyle = rgba(palette.cream, 0.84);
        ctx.lineWidth = 7.5;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.stroke();

        drawPolyline(ctx, TRACE_POINTS, p);
        ctx.strokeStyle = "rgba(255,255,255,0.32)";
        ctx.lineWidth = 2.2;
        ctx.stroke();

        var exact = p * (TRACE_POINTS.length - 1);
        var cursor = TRACE_POINTS[Math.min(TRACE_POINTS.length - 1, Math.max(0, Math.floor(exact)))];
        ctx.beginPath();
        ctx.arc(cursor.x, cursor.y, 4.2 + Math.sin(t * 8) * 0.5, 0, TAU);
        ctx.fillStyle = rgba(palette.oat, 0.92);
        ctx.fill();

        ctx.restore();

        var dotX = cx;
        var dotY = cy + 78 * unit;
        ctx.beginPath();
        ctx.arc(dotX, dotY, (12 + Math.sin(t * 2.5) * 1.2) * unit, 0, TAU);
        ctx.fillStyle = rgba(palette.blue, 0.9);
        ctx.fill();
        ctx.beginPath();
        ctx.arc(dotX, dotY, 5.5 * unit, 0, TAU);
        ctx.fillStyle = rgba(palette.oat, 0.74);
        ctx.fill();
      };

      Scene.prototype.draw_hindsight = function (ctx, cx, cy, unit, p, t) {
        ctx.save();
        ctx.translate(cx, cy);
        for (var i = 0; i < 4; i += 1) {
          var r = (58 + i * 30) * unit;
          ctx.beginPath();
          ctx.arc(0, 0, r, Math.PI * 0.2, Math.PI * 1.75 * p + Math.PI * 0.2);
          ctx.strokeStyle = rgba(i % 2 ? palette.taupe : palette.oat, 0.12 + p * 0.18);
          ctx.lineWidth = 1.6 * unit;
          ctx.lineCap = "round";
          ctx.stroke();
        }
        var x = -120 * unit + p * 240 * unit;
        roundRect(ctx, x - 45 * unit, -34 * unit, 90 * unit, 68 * unit, 6 * unit);
        ctx.fillStyle = rgba(palette.black, 0.32);
        ctx.fill();
        ctx.strokeStyle = rgba(palette.oat, 0.28 + p * 0.36);
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo((-170 + Math.sin(t) * 8) * unit, 66 * unit);
        ctx.lineTo(-136 * unit, 48 * unit);
        ctx.lineTo(-170 * unit, 30 * unit);
        ctx.strokeStyle = rgba(palette.taupe, 0.52);
        ctx.lineWidth = 2.5 * unit;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.stroke();
        ctx.restore();
      };

      Scene.prototype.draw_becoming = function (ctx, cx, cy, unit, p, t) {
        ctx.save();
        ctx.translate(cx, cy);
        for (var row = 0; row < 5; row += 1) {
          for (var col = 0; col < 7; col += 1) {
            var i = row * 7 + col;
            var startX = (-210 + col * 70) * unit;
            var startY = (-88 + row * 44) * unit;
            var angle = (i / 35) * TAU + t * 0.04;
            var targetX = Math.cos(angle) * (62 + (i % 5) * 12) * unit;
            var targetY = Math.sin(angle) * (36 + (i % 4) * 8) * unit;
            var x = startX + (targetX - startX) * p;
            var y = startY + (targetY - startY) * p;
            ctx.beginPath();
            ctx.arc(x, y, (2.2 + p * 1.8) * unit, 0, TAU);
            ctx.fillStyle = rgba(i % 3 ? palette.oat : palette.blue, 0.28 + p * 0.42);
            ctx.fill();
          }
        }
        for (var ring = 0; ring < 4; ring += 1) {
          ctx.beginPath();
          ctx.ellipse(0, 0, (90 + ring * 34) * unit * p, (42 + ring * 16) * unit * p, 0, 0, TAU);
          ctx.strokeStyle = rgba(ring % 2 ? palette.taupe : palette.blue, 0.12 + p * 0.16);
          ctx.stroke();
        }
        ctx.restore();
      };

      Scene.prototype.draw_gesture = function (ctx, cx, cy, unit, p, t) {
        ctx.save();
        ctx.translate(cx, cy);
        var sweep = easeOutCubic(p);
        traceCubic(ctx, { x: -280 * unit, y: 35 * unit }, { x: -124 * unit, y: -120 * unit }, { x: 120 * unit, y: 118 * unit }, { x: 286 * unit, y: -30 * unit }, sweep);
        ctx.strokeStyle = rgba(palette.oat, 0.54);
        ctx.lineWidth = 7 * unit;
        ctx.lineCap = "round";
        ctx.stroke();
        traceCubic(ctx, { x: -280 * unit, y: 35 * unit }, { x: -124 * unit, y: -120 * unit }, { x: 120 * unit, y: 118 * unit }, { x: 286 * unit, y: -30 * unit }, sweep);
        ctx.strokeStyle = rgba(palette.blue, 0.72);
        ctx.lineWidth = 2.2 * unit;
        ctx.stroke();

        for (var i = 0; i < 36; i += 1) {
          var a = (i / 36) * TAU + t * 0.08;
          var r = (64 + (i % 9) * 12) * unit;
          var x = Math.cos(a) * r * p;
          var y = Math.sin(a) * r * 0.48 * p;
          ctx.beginPath();
          ctx.arc(x, y, (1.3 + (i % 4) * 0.38) * unit, 0, TAU);
          ctx.fillStyle = rgba(i % 5 === 0 ? palette.taupe : palette.cream, 0.16 + p * 0.38);
          ctx.fill();
        }

        ctx.beginPath();
        ctx.arc(0, 0, (20 + Math.sin(t * 1.8) * 1.5) * unit, 0, TAU);
        ctx.fillStyle = rgba(palette.taupe, 0.48 + p * 0.18);
        ctx.fill();
        ctx.beginPath();
        ctx.arc(0, 0, 7 * unit, 0, TAU);
        ctx.fillStyle = rgba(palette.oat, 0.82);
        ctx.fill();
        ctx.restore();
      };

      var scenes = [];
      roots.forEach(function (root) {
        if (root.getAttribute("data-cy-preservation-ready") === "true") return;
        root.setAttribute("data-cy-preservation-ready", "true");
        root.querySelectorAll("[data-cy-motion-scene]").forEach(function (element, index) {
          var scene = new Scene(element, scenes.length + index);
          if (scene.canvas && scene.ctx) scenes.push(scene);
        });
      });

      if (!scenes.length) return;

      var ticking = false;
      function frame(now) {
        ticking = false;
        var active = false;
        scenes.forEach(function (scene) {
          if (scene.updateProgress()) {
            active = true;
            scene.draw(now, false);
          }
        });
        if (active && !prefersReducedMotion) requestTick();
      }

      function requestTick() {
        if (!ticking) {
          ticking = true;
          window.requestAnimationFrame(frame);
        }
      }

      window.addEventListener("scroll", requestTick, { passive: true });
      window.addEventListener("resize", requestTick);
      document.addEventListener("visibilitychange", function () {
        if (!document.hidden) requestTick();
      });

      scenes.forEach(function (scene) {
        scene.updateProgress();
        scene.draw(performance.now(), true);
      });
      requestTick();
      }

      window.CyperusPreservationBlog = window.CyperusPreservationBlog || {};
      window.CyperusPreservationBlog.init = cyPreservationInit;

      function cyPreservationBoot() {
        cyPreservationInit();
        window.setTimeout(cyPreservationInit, 120);
        window.setTimeout(cyPreservationInit, 900);
      }

      if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", cyPreservationBoot, { once: true });
      } else {
        cyPreservationBoot();
      }

      window.addEventListener("pageshow", cyPreservationBoot);
      window.addEventListener("mercury:load", cyPreservationBoot);

      if (window.MutationObserver) {
        var cyPreservationObserver = new MutationObserver(function () {
          if (document.querySelector("[data-cy-preservation-blog]:not([data-cy-preservation-ready='true'])")) {
            cyPreservationBoot();
          }
        });
        cyPreservationObserver.observe(document.documentElement, { childList: true, subtree: true });
      }
    })();
</script>

SF Bay Area, California

(760) 604-1367
info@cyperusmedia.com

Copyright © 2026 Cyperus LLC. All rights reserved.