+{/if}
+
+
diff --git a/src/lib/components/ostara-eggs/index.ts b/src/lib/components/ostara-eggs/index.ts
index 96395d9..3f24b90 100644
--- a/src/lib/components/ostara-eggs/index.ts
+++ b/src/lib/components/ostara-eggs/index.ts
@@ -3,4 +3,5 @@ export { default as ThreeToast } from "./ThreeToast.svelte";
export { default as MondayEgg } from "./MondayEgg.svelte";
export { default as IdleEgg } from "./IdleEgg.svelte";
export { default as NotFoundEgg } from "./NotFoundEgg.svelte";
+export { default as BlogArchiveEggs } from "./BlogArchiveEggs.svelte";
export { threeVisible, triggerThree } from "./eggStores";
diff --git a/src/lib/utils/moonPhase.ts b/src/lib/utils/moonPhase.ts
new file mode 100644
index 0000000..2f74179
--- /dev/null
+++ b/src/lib/utils/moonPhase.ts
@@ -0,0 +1,111 @@
+/**
+ * Lunar illumination derived from SunCalc's astronomical implementation.
+ *
+ * The calculation uses Julian days plus geocentric solar and lunar coordinates
+ * to determine the phase angle. Unlike a fixed 29.53-day clock, it accounts for
+ * the Moon's orbital anomaly and changing distance.
+ *
+ * Formula sources:
+ * - Jean Meeus, Astronomical Algorithms, 2nd ed., chapter 48
+ * - https://github.com/mourner/suncalc
+ *
+ * SunCalc is distributed under the BSD 2-Clause License:
+ * Copyright (c) 2026, Volodymyr Agafonkin. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the copyright notice and this
+ * permission notice are retained. THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT
+ * WARRANTY OF ANY KIND. The complete deployed notice is at /licenses/suncalc.txt.
+ */
+
+const PI = Math.PI;
+const RAD = PI / 180;
+const DAY_MS = 86_400_000;
+const JULIAN_1970 = 2_440_588;
+const JULIAN_2000 = 2_451_545;
+const EARTH_OBLIQUITY = RAD * 23.4397;
+const SUN_DISTANCE_KM = 149_598_000;
+
+function toDays(date: Date) {
+ const julianDay = date.valueOf() / DAY_MS - 0.5 + JULIAN_1970;
+ return julianDay - JULIAN_2000;
+}
+
+function rightAscension(longitude: number, latitude: number) {
+ return Math.atan2(
+ Math.sin(longitude) * Math.cos(EARTH_OBLIQUITY) -
+ Math.tan(latitude) * Math.sin(EARTH_OBLIQUITY),
+ Math.cos(longitude),
+ );
+}
+
+function declination(longitude: number, latitude: number) {
+ return Math.asin(
+ Math.sin(latitude) * Math.cos(EARTH_OBLIQUITY) +
+ Math.cos(latitude) * Math.sin(EARTH_OBLIQUITY) * Math.sin(longitude),
+ );
+}
+
+function sunCoordinates(days: number) {
+ const meanAnomaly = RAD * (357.5291 + 0.98560028 * days);
+ const equationOfCentre =
+ RAD *
+ (1.9148 * Math.sin(meanAnomaly) +
+ 0.02 * Math.sin(2 * meanAnomaly) +
+ 0.0003 * Math.sin(3 * meanAnomaly));
+ const eclipticLongitude =
+ meanAnomaly + equationOfCentre + RAD * 102.9372 + PI;
+
+ return {
+ rightAscension: rightAscension(eclipticLongitude, 0),
+ declination: declination(eclipticLongitude, 0),
+ };
+}
+
+function moonCoordinates(days: number) {
+ const meanLongitude = RAD * (218.316 + 13.176396 * days);
+ const meanAnomaly = RAD * (134.963 + 13.064993 * days);
+ const meanDistance = RAD * (93.272 + 13.22935 * days);
+ const longitude = meanLongitude + RAD * 6.289 * Math.sin(meanAnomaly);
+ const latitude = RAD * 5.128 * Math.sin(meanDistance);
+
+ return {
+ rightAscension: rightAscension(longitude, latitude),
+ declination: declination(longitude, latitude),
+ distance: 385_001 - 20_905 * Math.cos(meanAnomaly),
+ };
+}
+
+export function getMoonIllumination(date = new Date()) {
+ const days = toDays(date);
+ const sun = sunCoordinates(days);
+ const moon = moonCoordinates(days);
+ const elongation = Math.acos(
+ Math.sin(sun.declination) * Math.sin(moon.declination) +
+ Math.cos(sun.declination) *
+ Math.cos(moon.declination) *
+ Math.cos(sun.rightAscension - moon.rightAscension),
+ );
+ const incidence = Math.atan2(
+ SUN_DISTANCE_KM * Math.sin(elongation),
+ moon.distance - SUN_DISTANCE_KM * Math.cos(elongation),
+ );
+ const limbAngle = Math.atan2(
+ Math.cos(sun.declination) *
+ Math.sin(sun.rightAscension - moon.rightAscension),
+ Math.sin(sun.declination) * Math.cos(moon.declination) -
+ Math.cos(sun.declination) *
+ Math.sin(moon.declination) *
+ Math.cos(sun.rightAscension - moon.rightAscension),
+ );
+
+ return {
+ fraction: (1 + Math.cos(incidence)) / 2,
+ phase: 0.5 + (0.5 * incidence * (limbAngle < 0 ? -1 : 1)) / PI,
+ angle: limbAngle,
+ };
+}
+
+/** Treat the roughly one-day period above 99% illumination as visibly full. */
+export function isVisiblyFullMoon(date = new Date()) {
+ return getMoonIllumination(date).fraction >= 0.99;
+}
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 4f47b6f..2fa12c6 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -9,6 +9,7 @@
import MondayEgg from '$lib/components/ostara-eggs/MondayEgg.svelte';
import IdleEgg from '$lib/components/ostara-eggs/IdleEgg.svelte';
import ThreeToast from '$lib/components/ostara-eggs/ThreeToast.svelte';
+ import BlogArchiveEggs from '$lib/components/ostara-eggs/BlogArchiveEggs.svelte';
import './layout.css';
let { children } = $props();
@@ -40,6 +41,7 @@
+Skip to content
{#if dev}DEV{/if}
diff --git a/static/assets/happy-mac.svg b/static/assets/happy-mac.svg
new file mode 100644
index 0000000..97e017f
--- /dev/null
+++ b/static/assets/happy-mac.svg
@@ -0,0 +1,37 @@
+
+
+
diff --git a/static/licenses/happy-mac.txt b/static/licenses/happy-mac.txt
new file mode 100644
index 0000000..28f3600
--- /dev/null
+++ b/static/licenses/happy-mac.txt
@@ -0,0 +1,9 @@
+Happy Mac SVG
+
+Author: NiloGlock at Italian Wikipedia
+Source: https://commons.wikimedia.org/wiki/File:Happy_Mac.svg
+License: Creative Commons Attribution-ShareAlike 4.0 International
+License text: https://creativecommons.org/licenses/by-sa/4.0/
+
+The copy used by this site has only had editor metadata, redundant attributes,
+and path formatting removed. Its artwork and colour design are unchanged.
diff --git a/static/licenses/suncalc.txt b/static/licenses/suncalc.txt
new file mode 100644
index 0000000..beb7ad4
--- /dev/null
+++ b/static/licenses/suncalc.txt
@@ -0,0 +1,28 @@
+SunCalc
+
+Copyright (c) 2026, Volodymyr Agafonkin
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+Source: https://github.com/mourner/suncalc