heartwood every commit a ring

the page remembers more and forgets less

b41462bb by Isaac Bythewood · 1 month ago

the page remembers more and forgets less

storm folklore for every season, deeper star mythology,
richer wisdom with weather proverbs woven in.
sunrise and sunset times, season countdowns,
a service worker so the almanac works offline
like a book you own. accessibility and error
handling for the edges. the page now refreshes
itself when the hour changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
modified site/almanac.js
@@ -9,6 +9,10 @@  var LAT = 35.78; // north carolina, zone 7a  var ANIM_WORD_DELAY = 18;  var ANIM_LIST_DELAY = 60;  var ANIM_NAV_DELAY = 80;  // --- time and season ---
@@ -54,11 +58,11 @@  // which content categories belong to each time of day  var TIME_CONTENT = {    night:     ['sky/', 'names/', 'remedies/'],    night:     ['sky/', 'names/', 'remedies/', 'storms/'],    dawn:      ['planting/', 'foraging/'],    morning:   ['planting/', 'chores/', 'bugs/'],    afternoon: ['kitchen/', 'bugs/', 'chores/'],    evening:   ['kitchen/', 'remedies/', 'foraging/', 'names/']    evening:   ['kitchen/', 'remedies/', 'foraging/', 'names/', 'storms/']  };  var TIME_LABELS = ['night', 'dawn', 'morning', 'afternoon', 'evening'];
@@ -317,7 +321,8 @@  function daylight(date, lat) {    var doy = Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 86400000);    var decl = 23.45 * Math.sin((2 * Math.PI / 365) * (doy - 81));    var yearLen = new Date(date.getFullYear(), 1, 29).getDate() === 29 ? 366 : 365;    var decl = 23.45 * Math.sin((2 * Math.PI / yearLen) * (doy - 81));    var cosH = -Math.tan(lat * Math.PI / 180) * Math.tan(decl * Math.PI / 180);    cosH = Math.max(-1, Math.min(1, cosH));    return (2 * Math.acos(cosH) * 180 / Math.PI) / 15;
@@ -381,6 +386,8 @@    yesterday.setDate(yesterday.getDate() - 1);    var gained = ((hours - daylight(yesterday, LAT)) * 60).toFixed(1);    var sign = gained > 0 ? '+' : '';    var sunrise = 12 - hours / 2;    var sunset = 12 + hours / 2;    var lines = [];
@@ -389,17 +396,17 @@      lines.push('the world is turned away from the sun.');      lines.push(formatHM(hours) + ' of daylight today. ' + sign + gained + ' minutes from yesterday.');    } else if (time === 'dawn') {      lines.push('the sun is finding the edge of things.');      lines.push('the sun rises around ' + formatHM(sunrise) + '.');      lines.push('the moon is ' + name + ', ' + illum + '% lit.');      lines.push(formatHM(hours) + ' of daylight ahead.');      lines.push(formatHM(hours) + ' of daylight ahead. it sets around ' + formatHM(sunset) + '.');    } else if (time === 'evening') {      lines.push('the light is going.');      lines.push('the sun set around ' + formatHM(sunset) + '.');      lines.push('the moon is ' + name + ', ' + illum + '% lit.');      lines.push('there were ' + formatHM(hours) + ' of daylight today. ' + sign + gained + ' minutes from yesterday.');    } else {      lines.push('the moon is ' + name + ', ' + illum + '% lit.');      lines.push(formatHM(hours) + ' of daylight today.');      lines.push(sign + gained + ' minutes from yesterday, quietly.');      lines.push('the sun rose around ' + formatHM(sunrise) + ' and sets around ' + formatHM(sunset) + '.');      lines.push(formatHM(hours) + ' of daylight today. ' + sign + gained + ' minutes from yesterday.');    }    return lines.join('\n');
@@ -506,7 +513,7 @@    var delay = 0;    allItems.forEach(function (item) {      item.style.animationDelay = delay + 'ms';      delay += item.tagName === 'LI' ? 60 : 18;      delay += item.tagName === 'LI' ? ANIM_LIST_DELAY : ANIM_WORD_DELAY;    });  }
@@ -519,6 +526,29 @@  var naturalSeason = null;  var naturalTime = null;  // cached DOM references  var dom = {};  function cacheDOM() {    dom.dateLine = document.querySelector('.date-line');    dom.seasonName = document.querySelector('.season-name');    dom.seasonNote = document.querySelector('.season-note');    dom.haikuBlock = document.querySelector('.hero-haiku blockquote');    dom.haikuCite = document.querySelector('.hero-haiku cite');    dom.moonGlyph = document.querySelector('.moon-glyph');    dom.moonGardenTip = document.querySelector('.moon-garden-tip');    dom.weatherMoodText = document.querySelector('.weather-mood-text');    dom.skyData = document.querySelector('.sky-data');    dom.glossaryNote = document.querySelector('.glossary-note');    dom.footerP = document.querySelector('footer p');    dom.wisdom = document.querySelector('.wisdom');    dom.almanac = document.querySelector('.glossary-entries');    dom.hero = document.querySelector('.hero');    dom.glossary = document.querySelector('.glossary');    dom.footer = document.querySelector('footer');    dom.seasonsNav = document.querySelector('.seasons-nav');    dom.timesNav = document.querySelector('.times-nav');  }  function loadContent(seasonOverride, timeOverride) {    var now = new Date();    var season = seasonOverride || getSeason(now);
@@ -531,43 +561,45 @@    applyDaylightCycle(realTime, season);    // date line    document.querySelector('.date-line').textContent = writtenDate(now);    dom.dateLine.textContent = writtenDate(now);    // season header    document.querySelector('.season-name').textContent = season.label;    document.querySelector('.season-note').innerHTML = season.note.replace(/\n/g, '<br>');    dom.seasonName.textContent = season.label;    var noteHTML = season.note.replace(/\n/g, '<br>');    var next = daysUntilNextSeason(now);    if (next.days <= 7) {      noteHTML += '<br>' + next.label + ' begins in ' + next.days + (next.days === 1 ? ' day.' : ' days.');    }    dom.seasonNote.innerHTML = noteHTML;    // haiku    var haiku = getHaiku(season);    var haikuBlock = document.querySelector('.hero-haiku blockquote');    var haikuCite = document.querySelector('.hero-haiku cite');    if (haiku) {      var haikuLines = haiku.lines[0].split('\n');      haikuBlock.innerHTML = haikuLines.map(function (l) {      dom.haikuBlock.innerHTML = haikuLines.map(function (l) {        return '<span class="haiku-line">' + l + '</span>';      }).join('');      haikuCite.textContent = '';      dom.haikuCite.textContent = '— ' + haiku.author;    }    // moon glyph — large, standalone    var phase = moonPhase(now);    document.querySelector('.moon-glyph').textContent = moonGlyph(phase);    dom.moonGlyph.textContent = moonGlyph(phase);    // moon gardening tip    document.querySelector('.moon-garden-tip').textContent = moonGardenTip(phase);    dom.moonGardenTip.textContent = moonGardenTip(phase);    // weather mood    document.querySelector('.weather-mood-text').textContent = getWeatherMood(season, contentTime);    dom.weatherMoodText.textContent = getWeatherMood(season, contentTime);    // sky data    document.querySelector('.sky-data').innerHTML = skyText(now, contentTime).replace(/\n/g, '<br>');    dom.skyData.innerHTML = skyText(now, contentTime).replace(/\n/g, '<br>');    // glossary note    document.querySelector('.glossary-note').textContent = 'what the ' + contentTime + ' brings';    dom.glossaryNote.textContent = 'what the ' + contentTime + ' brings';    // footer countdown    var next = daysUntilNextSeason(now);    document.querySelector('footer p').textContent =    dom.footerP.textContent =      next.days + ' days until ' + next.label + ' \u00b7 zone 7a \u00b7 north carolina';    // crossfade: fade out content areas, then rebuild
@@ -601,14 +633,15 @@        return Promise.all(relevant.map(function (entry) {          return fetch('/data/' + entry.path)            .then(function (r) { return r.text(); })            .then(function (text) { return parseFrontmatter(text); });        }));            .then(function (text) { return parseFrontmatter(text); })            .catch(function () { return null; });        })).then(function (results) {          return results.filter(function (r) { return r !== null; });        });      })      .then(function (entries) {        var wisdom = document.querySelector('.wisdom');        var almanac = document.querySelector('.glossary-entries');        wisdom.innerHTML = '';        almanac.innerHTML = '';        dom.wisdom.innerHTML = '';        dom.almanac.innerHTML = '';        entries.forEach(function (entry) {          var div = document.createElement('div');
@@ -633,9 +666,9 @@          }          if (entry.meta.time) {            wisdom.appendChild(div);            dom.wisdom.appendChild(div);          } else {            almanac.appendChild(div);            dom.almanac.appendChild(div);          }        });
@@ -645,13 +678,20 @@          if (el) el.style.opacity = '1';        });        revealWords(document.querySelector('.hero'));        revealWords(document.querySelector('.glossary'));        revealWords(document.querySelector('footer'));        revealWords(dom.hero);        revealWords(dom.glossary);        revealWords(dom.footer);        // update navs        updateNav(season);        updateTimeNav(contentTime);      })      .catch(function () {        dom.almanac.innerHTML = '<p style="color:var(--ash);font-style:italic;text-align:center;">the pages could not be found. try again in a moment.</p>';        fadeTargets.forEach(function (sel) {          var el = document.querySelector(sel);          if (el) el.style.opacity = '1';        });      });  }
@@ -666,8 +706,7 @@  });  function updateNav(activeSeason) {    var nav = document.querySelector('.seasons-nav');    nav.innerHTML = '';    dom.seasonsNav.innerHTML = '';    var delay = 0;    seasonOrder.forEach(function (s) {
@@ -675,14 +714,15 @@      a.textContent = s.label;      if (s.name === activeSeason.name) {        a.className = 'active';        a.setAttribute('aria-current', 'true');      }      a.style.animationDelay = delay + 'ms';      delay += 80;      delay += ANIM_NAV_DELAY;      a.addEventListener('click', function () {        window.scrollTo({ top: 0, behavior: 'smooth' });        loadContent(s, currentTimeOverride);      });      nav.appendChild(a);      dom.seasonsNav.appendChild(a);    });    if (activeSeason.name !== naturalSeason.name) {
@@ -694,14 +734,13 @@        window.scrollTo({ top: 0, behavior: 'smooth' });        loadContent(null, currentTimeOverride);      });      nav.appendChild(ret);      dom.seasonsNav.appendChild(ret);    }  }  function updateTimeNav(activeTime) {    var nav = document.querySelector('.times-nav');    if (!nav) return;    nav.innerHTML = '';    if (!dom.timesNav) return;    dom.timesNav.innerHTML = '';    var delay = 0;    TIME_LABELS.forEach(function (t) {
@@ -709,13 +748,14 @@      a.textContent = t;      if (t === activeTime) {        a.className = 'active';        a.setAttribute('aria-current', 'true');      }      a.style.animationDelay = delay + 'ms';      delay += 80;      delay += ANIM_NAV_DELAY;      a.addEventListener('click', function () {        loadContent(currentSeason === naturalSeason ? null : currentSeason, t);      });      nav.appendChild(a);      dom.timesNav.appendChild(a);    });    if (currentTimeOverride && currentTimeOverride !== naturalTime) {
@@ -726,14 +766,34 @@      ret.addEventListener('click', function () {        loadContent(currentSeason === naturalSeason ? null : currentSeason, null);      });      nav.appendChild(ret);      dom.timesNav.appendChild(ret);    }  }  // initial render  cacheDOM();  var now = new Date();  naturalSeason = getSeason(now);  naturalTime = getTimeOfDay(now);  loadContent(null);  // auto-refresh when time of day or season changes  setInterval(function () {    var check = new Date();    var newTime = getTimeOfDay(check);    var newSeason = getSeason(check);    if (newTime !== naturalTime || newSeason.name !== naturalSeason.name) {      naturalTime = newTime;      naturalSeason = newSeason;      if (!currentTimeOverride) {        loadContent(currentSeason === naturalSeason ? null : currentSeason, null);      }    }  }, 60000);  // register service worker for offline access  if ('serviceWorker' in navigator) {    navigator.serviceWorker.register('/sw.js');  }})();
modified site/data/manifest.json
@@ -56,6 +56,13 @@  { "path": "names/early-fall.md", "season": "early-fall" },  { "path": "names/late-fall.md", "season": "late-fall" },  { "path": "names/winter.md", "season": "winter" },  { "path": "storms/winter.md", "season": "winter" },  { "path": "storms/early-spring.md", "season": "early-spring" },  { "path": "storms/late-spring.md", "season": "late-spring" },  { "path": "storms/early-summer.md", "season": "early-summer" },  { "path": "storms/midsummer.md", "season": "midsummer" },  { "path": "storms/early-fall.md", "season": "early-fall" },  { "path": "storms/late-fall.md", "season": "late-fall" },  { "path": "wisdom/dawn.md", "time": "dawn" },  { "path": "wisdom/morning.md", "time": "morning" },  { "path": "wisdom/afternoon.md", "time": "afternoon" },
modified site/data/sky/early-fall.md
@@ -11,3 +11,7 @@ section: what the stars are showing- the milky way still crosses overhead but will fade as autumn deepensfomalhaut was one of the four royal stars of ancient persia, marking the sky's seasons. they called it the "solitary one." it appears when the harvest comes.the andromeda galaxy is the farthest thing you can see without a telescope. two million years of travel for that light to reach your eye. the old arabs called it "the little cloud." it is another galaxy the size of ours, coming toward us slowly. in four billion years they will merge.cassiopeia was the vain queen who boasted she was more beautiful than the sea nymphs. the gods chained her to the sky. she circles the north pole forever, sometimes upside down. the old sailors used her and the big dipper together to find true north.
modified site/data/sky/early-spring.md
@@ -11,3 +11,7 @@ section: what the stars are showing- the winter triangle (betelgeuse, sirius, procyon) fades lower each nightthe old farmers watched orion leave and knew the planting was close. when he disappears below the western horizon, the frost is nearly done.arcturus was the star the old plowmen followed. its name means "bear watcher" in greek, because it follows ursa major across the sky. but to the farmers it simply meant warmth is returning. when it rises at sunset, the soil is ready.regulus, the heart of the lion, was another of the four royal stars of persia. it marked the summer solstice four thousand years ago. the name means "little king," and in spring it rules the high sky.
modified site/data/sky/early-summer.md
@@ -11,3 +11,7 @@ section: what the stars are showing- corona borealis, the northern crown, a small perfect arc of stars near arcturusantares means "rival of mars" because of its red color. the old greeks saw it and thought of war. the old farmers saw it and thought of heat.vega was the north star fourteen thousand years ago and will be again. the chinese saw it as the weaving girl, separated from her lover the cowherd (altair) by the river of the milky way. they meet once a year, on the seventh night of the seventh month.corona borealis, the northern crown, was ariadne's wedding crown, thrown into the sky by dionysus. it is small and easy to miss, but once you find it you will always see it. a perfect half-circle of stars, like something placed there on purpose.
modified site/data/sky/late-fall.md
@@ -11,3 +11,7 @@ section: what the stars are showing- orion begins to rise late in the east, the hunter returning for winterthe pleiades were the most watched stars in the ancient world. their return to the evening sky meant the growing season was over. time to bring everything in and close the ground.aldebaran, the eye of the bull, is the fourth of the old persian royal stars. its name means "the follower" in arabic, because it follows the pleiades across the sky. it is old and red and sixty-five light-years away, nearing the end of its life.the return of orion closes the year. the old greeks said he was placed opposite scorpius in the sky so they would never meet. when one rises, the other sets. the hunter comes back with the cold, and the cycle begins again.
modified site/data/sky/late-spring.md
@@ -11,3 +11,7 @@ section: what the stars are showing- corvus the crow, four stars in a small square, sits low in the southin the old almanacs, when arcturus stood high and orion was gone, it meant the soil was truly warm. plant anything now.spica in virgo was the grain sheaf in the goddess's hand. the romans timed their grain harvest by this star. its name comes from the latin for "ear of wheat." it appears with the planting and leaves with the reaping.corvus the crow was placed in the sky by apollo as punishment for bringing bad news. the old farmers saw the four stars and thought of the real crows in their fields, always watching, always waiting for the seeds.
modified site/data/sky/midsummer.md
@@ -11,3 +11,7 @@ section: what the stars are showing- the perseid meteors begin in late july, peaking in august, bits of comet swift-tuttlethe milky way was called the "backbone of night" by some old cultures. in midsummer it arches overhead like a bridge from horizon to horizon. go somewhere dark and look up.the perseids have been watched for two thousand years. the old catholics called them "the tears of saint lawrence," who was martyred in august. the chinese recorded them in 36 AD. they are the most reliable meteor shower and the best one for warm-weather watching.when you look toward sagittarius, you are looking toward the center of the galaxy. twenty-six thousand light-years of stars, dust, and darkness. the teapot shape is pouring out the densest part of the milky way. the old astronomers did not know what they were seeing. we barely do.
modified site/data/sky/winter.md
@@ -11,3 +11,7 @@ section: what the stars are showing- gemini the twins stand east of orion, castor and pollux side by sidethe old name for sirius was "the scorcher." the romans believed it added its heat to the sun in summer. in winter it is just cold light, the most brilliant thing in a dark sky. the clearest nights are the coldest ones.the pleiades told the greeks when to sail and when to stop. when they set with the sun, the seas closed for winter. every old farming culture watched them. the japanese called them subaru. the old english name was "the hen and her chicks."orion was the great hunter in every tradition that watched him. the egyptians saw their god osiris. the lakota saw a hand reaching into the sky. he is the one constellation that everyone recognizes, and he belongs to winter.
added site/data/storms/early-fall.md
@@ -0,0 +1,12 @@---season: early-fallsection: old names for storms---the equinox gale. like its spring counterpart, it arrives when the seasons are changing hands. the wind shifts from south to north and brings the first real cold behind it. the old sailors feared the autumn equinox more than any other date.a norther. the temperature drops thirty degrees in an hour. the sky turns grey-green. it is the first reminder that summer is something you had, not something you have.the line squall. a wall of wind and rain that crosses the landscape like a curtain being drawn. you can watch it approach. the pressure drops and your ears pop. then it hits.hurricane season peaks in september. the old coastal farmers watched the swell and the birds. when the pelicans move inland, you move with them. there is no arguing with a storm that has a name.
added site/data/storms/early-spring.md
@@ -0,0 +1,12 @@---season: early-springsection: old names for storms---the line storm. it comes at the equinox, when winter and spring argue over the same ground. the old almanacs expected it, marked it, planned around it.a late snow in march was called a "poor man's fertilizer." the snow carries nitrogen from the atmosphere and works it into the soil as it melts. the old farmers welcomed it even as they cursed the cold.the onion snow. a light snow that falls when the wild onions are up. it comes and goes in a day. the ground is too warm to hold it.wind in early spring is different from winter wind. it is warmer and wetter and it smells like mud. the old word for it was simply "the thaw wind."
added site/data/storms/early-summer.md
@@ -0,0 +1,12 @@---season: early-summersection: old names for storms---the heat burst. a sudden blast of hot dry air that falls from a collapsing thunderstorm. temperatures can jump twenty degrees in minutes, late at night, with no warning. the old farmers had no name for it because it made no sense.the dry storm. lightning with no rain. the most dangerous kind for starting fires. the sky flickers and nothing falls. the old word was "dry lightning" and it was feared.a june thunderstorm that arrives at four in the afternoon was just called "the daily." it builds, it pours, it passes, and the steam rises off the road. the regularity of it is a comfort.the derecho. a straight-line wind that flattens everything in its path. not a tornado but worse in some ways because it covers more ground. the name is spanish for "straight."
added site/data/storms/late-fall.md
@@ -0,0 +1,12 @@---season: late-fallsection: old names for storms---the grey wind. november wind has no warmth left in it. it strips the last leaves and pushes through every crack. the old houses were not built to stop it, just to slow it down.the spitting snow. not a real snowfall but a reminder. it comes sideways and does not stick. the old farmers took it as a signal to finish the last outdoor work.a killing frost is not a storm but it arrives like one. clear, still, and cold enough to end everything still growing. the old timers read the sky at sunset: if it was clear and the wind was calm, they covered what they could and mourned the rest.the thanksgiving storm. the first real snow that means it. it may melt, but winter has made its case. the old almanacs marked it as the true beginning of the cold season, regardless of what the calendar said.
added site/data/storms/late-spring.md
@@ -0,0 +1,12 @@---season: late-springsection: old names for storms---the thundergust. the first real thunderstorms of the year, sudden and warm and electric. they build in the west by afternoon and arrive without much warning. the old farmers read the clouds and got the hay in.a hailstorm in spring can ruin a garden in five minutes. the old timers called it "the hammer." there is no remedy but replanting.the blackberry winter. a cold snap that comes after the warmth has settled in, just when the blackberries are blooming. it feels like a betrayal but the old farmers expected it. they did not plant the tender things until it passed.a warm spring rain with no wind was called "a growing rain." you can almost hear the garden respond to it.
added site/data/storms/midsummer.md
@@ -0,0 +1,12 @@---season: midsummersection: old names for storms---the dog days storm. it comes when the air is so heavy you can feel it pressing down. the buildup takes all day and the relief when it breaks is physical. the old farmers said you could smell it coming.a cloudburst. not just rain but the whole sky falling at once. half an inch in ten minutes. the ditches fill, the creek jumps its bank, and then it stops. the sun comes out like nothing happened.heat lightning. no thunder, just the sky flickering at the horizon. the storm is too far away to hear but the light carries. the old people watched it from the porch and called it "the shimmer."the tropical remnant. what is left of a hurricane after it has come inland and lost its name. days of grey warm rain. everything floods slowly. the old farmers knew it by the way the wind circled.
added site/data/storms/winter.md
@@ -0,0 +1,12 @@---season: wintersection: old names for storms---the nor'easter. it comes up the coast with its shoulder turned, pulling cold air from canada and wet air from the atlantic. the old fishermen named it for the wind direction that hit first.an ice storm has no folk name. people just called it "the glaze." everything coated, everything heavy, branches snapping in the dark. the most dangerous storm is the one that looks beautiful.a whiteout was called "the blind." you cannot see your hand. the old rule was to tie a rope from the house to the barn so you could find your way back. people died walking thirty feet.the january thaw is not a storm but the pause between them. a few days of warmth that fools the crocuses and worries the farmers. it always ends.
modified site/data/wisdom/afternoon.md
@@ -7,3 +7,8 @@ do not plant now. the roots will scorch.water at the base, not the leaves, or the sunwill burn through the droplets like tiny lenses.rest if you can. the garden does not need you every hour.if you must work, work in the shade. clean pots, sort seeds, mend what is broken.the hottest part of the day is not noon but two hours after. the ground stores heat and gives it back.watch the clouds in the west. if they are building towers, rain before dark.a ring around the sun means rain within a day. the old saying: "sun with a circle, rain tomorrow."when the leaves turn their pale undersides to the wind, a storm is near. the trees feel the humidity before you do.
modified site/data/wisdom/dawn.md
@@ -5,4 +5,10 @@ section: at this hourthe old farmers were already up.there is dew on everything, which means the day will be fair.no dew means clouds held the heat in, and rain may follow.the birds started before you did.if the spider webs are heavy with water, no rain today. the spiders know.this is the hour to transplant seedlings. the roots settle before the heat.sharpen your tools now. a dull blade tears what a sharp one cuts clean.the cool air holds scent close to the ground. you can smell the soil waking.a grey dawn after a red sunset means the fair weather is holding. trust what the sky told you last night.
modified site/data/wisdom/evening.md
@@ -5,5 +5,10 @@ section: at this hourwater now if the soil is dry. the night gives it time to soak.check the sky for tomorrow. red in the west means fair weather.red sky at night is the oldest forecast there is. it works because weather moves west to east, and red means clear air where the sun is setting.the evening is for walking the rows and noticing what changed.pull a weed or two on your way back in.slugs come out at dusk. if you have a problem, now is when you will see it.listen for the frogs. if they are loud, the moisture is good. if they are quiet, something has changed.the last light is the best light for seeing what the garden actually looks like, without the glare.if the crickets are chirping, count the chirps in fourteen seconds and add forty. that is the temperature in fahrenheit. the old farmers did not need a thermometer.
modified site/data/wisdom/morning.md
@@ -7,3 +7,8 @@ the best time to transplant is early, before the sun is high.the best time to harvest herbs is after the dew driesbut before the heat draws the oils out.the morning is for doing. the afternoon is for bearing it.pick beans and peas now while they are cool and crisp.if you see the bees working a particular flower, leave it alone. they were here first.watch where the shadows fall. that is where the lettuce wants to be in july.if the morning glory closes early, rain is coming. the old farmers read the flowers like a forecast.when smoke from the chimney falls instead of rising, a low pressure system is moving in. finish your planting before it arrives.
modified site/data/wisdom/night.md
@@ -7,3 +7,8 @@ the garden is working without you now.roots grow in the dark. the soil breathes.moths are pollinating the flowers you thought were sleeping.go to bed. tomorrow there will be something new.the earthworms are pulling leaves down into the soil, feeding what feeds you.if you cannot sleep, step outside and listen. you will hear things the day covers up.when the stars are sharp and close, the air is dry. expect frost if it is cold enough.a halo around the moon means rain within three days. ice crystals in the high clouds bend the light.the old farmers planned by lamplight. seed orders, crop rotations, next year's beds. the quiet hours are for thinking ahead.
modified site/index.html
@@ -15,12 +15,14 @@  <meta property="og:title" content="dark furrow">  <meta property="og:description" content="old knowledge preserved. a seasonal almanac of planting, harvest, moon phases, and the quiet rhythms the modern world forgot.">  <meta property="og:site_name" content="dark furrow">  <meta property="og:image" content="https://darkfurrow.com/og.svg">  <meta property="og:locale" content="en_US">  <!-- twitter / x -->  <meta name="twitter:card" content="summary">  <meta name="twitter:title" content="dark furrow">  <meta name="twitter:description" content="old knowledge preserved. what to plant, what to cook, what the moon is doing. an almanac that breathes with the season and the hour.">  <meta name="twitter:image" content="https://darkfurrow.com/og.svg">  <!-- misc -->  <meta name="robots" content="index, follow">
added site/og.svg
@@ -0,0 +1,7 @@<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630">  <rect width="1200" height="630" fill="#0e0c0a"/>  <text x="600" y="280" text-anchor="middle" font-family="Georgia, 'Times New Roman', serif" font-size="72" fill="#d4c8b8" letter-spacing="0.03em">dark furrow</text>  <text x="600" y="340" text-anchor="middle" font-family="Georgia, 'Times New Roman', serif" font-size="24" fill="#a89b8e" font-style="italic" letter-spacing="0.02em">old knowledge preserved</text>  <line x1="536" y1="370" x2="664" y2="370" stroke="#2a2420" stroke-width="1" opacity="0.6"/>  <text x="600" y="410" text-anchor="middle" font-family="Georgia, 'Times New Roman', serif" font-size="16" fill="#5a7d4a" letter-spacing="0.04em">a seasonal almanac of planting, harvest, and forgotten rhythms</text></svg>
modified site/style.css
@@ -1,6 +1,5 @@:root {  --earth: #0e0c0a;  --loam: #1a1714;  --bark: #2a2420;  --ash: #a89b8e;  --bone: #d4c8b8;
@@ -276,6 +275,14 @@ main {.seasons-nav a.active {  color: var(--sprout);  text-decoration: underline;  text-underline-offset: 3px;}.seasons-nav a:focus-visible,.times-nav a:focus-visible {  outline: 1px solid var(--ember);  outline-offset: 2px;}.seasons-nav a.return-now {
@@ -366,6 +373,8 @@ main {.times-nav a.active {  color: var(--sprout);  text-decoration: underline;  text-underline-offset: 3px;}.times-nav a.return-now {
added site/sw.js
@@ -0,0 +1,54 @@// service worker for dark furrow// the almanac should work like a book you own, not a service you rent.var CACHE_NAME = 'dark-furrow-v1';var CORE_ASSETS = [  '/',  '/index.html',  '/style.css',  '/almanac.js',  '/favicon.svg',  '/og.svg',  '/data/manifest.json'];self.addEventListener('install', function (event) {  event.waitUntil(    caches.open(CACHE_NAME).then(function (cache) {      return cache.addAll(CORE_ASSETS);    }).then(function () {      return self.skipWaiting();    })  );});self.addEventListener('activate', function (event) {  event.waitUntil(    caches.keys().then(function (names) {      return Promise.all(        names.filter(function (name) { return name !== CACHE_NAME; })             .map(function (name) { return caches.delete(name); })      );    }).then(function () {      return self.clients.claim();    })  );});self.addEventListener('fetch', function (event) {  event.respondWith(    fetch(event.request).then(function (response) {      // cache successful responses for offline use      if (response.ok) {        var clone = response.clone();        caches.open(CACHE_NAME).then(function (cache) {          cache.put(event.request, clone);        });      }      return response;    }).catch(function () {      return caches.match(event.request);    })  );});