diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml
new file mode 100644
index 0000000..cf0e3e5
--- /dev/null
+++ b/.gitea/workflows/deploy.yml
@@ -0,0 +1,42 @@
+name: Build and Deploy DAVE | DMGs Site
+
+on:
+ push:
+ branches: [main]
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '18'
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+ with:
+ version: 8
+
+ - name: Cache pnpm store
+ uses: actions/cache@v4
+ with:
+ path: ~/.pnpm-store
+ key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-
+
+ - name: Install dependencies
+ run: pnpm install --no-frozen-lockfile
+
+ - name: Build
+ run: pnpm run build
+
+ - name: Deploy
+ run: |
+ rm -rf /var/www/dave-dmg/blog/*
+ cp -r dist/* /var/www/dave-dmg/blog/
diff --git a/markdoc.config.mjs b/markdoc.config.mjs
index d4b2233..77b301d 100644
--- a/markdoc.config.mjs
+++ b/markdoc.config.mjs
@@ -1,4 +1,9 @@
-import { defineMarkdocConfig, component, nodes } from '@astrojs/markdoc/config';
+import {
+ defineMarkdocConfig,
+ component,
+ nodes,
+ Markdoc,
+} from '@astrojs/markdoc/config';
export default defineMarkdocConfig({
nodes: {
@@ -16,5 +21,70 @@ export default defineMarkdocConfig({
color: { type: String },
},
},
+ Callout: {
+ render: component('./src/components/content/Callout.astro'),
+ attributes: {
+ type: {
+ type: String,
+ default: 'default',
+ },
+ title: { type: String },
+ },
+ },
+ Figure: {
+ render: component('./src/components/content/Figure.astro'),
+ selfClosing: true,
+ attributes: {
+ src: {
+ type: String,
+ required: true,
+ },
+ alt: {
+ type: String,
+ required: true,
+ },
+ caption: { type: String },
+ credit: { type: String },
+ },
+ },
+ Sidenote: {
+ selfClosing: true,
+ attributes: {
+ id: {
+ type: String,
+ required: true,
+ },
+ title: {
+ type: String,
+ },
+ marker: {
+ type: String,
+ default: '⋄',
+ },
+ content: {
+ type: String,
+ required: true,
+ },
+ type: {
+ type: String,
+ default: 'default',
+ },
+ },
+ transform(node, config) {
+ const attrs = node.transformAttributes(config);
+ return new Markdoc.Tag('sup', {}, [
+ new Markdoc.Tag(
+ 'a',
+ {
+ href: `#${attrs.id}`,
+ id: `ref-${attrs.id}`,
+ class: 'sidenote-ref',
+ style: `anchor-name: --note-${attrs.id}`,
+ },
+ [`[${attrs.marker}]`],
+ ),
+ ]);
+ },
+ },
},
});
diff --git a/src/components/content/Callout.astro b/src/components/content/Callout.astro
new file mode 100644
index 0000000..cd7d071
--- /dev/null
+++ b/src/components/content/Callout.astro
@@ -0,0 +1,103 @@
+---
+interface Props {
+ type: | "default" | "optional" | "example" | "spoiler";
+ title?: string;
+}
+
+const { type, title} = Astro.props;
+const isSpoiler = type === 'spoiler'
+---
+
+{isSpoiler ? (
+
+
+ {title || 'Show spoiler'}
+ {type.toUpperCase()}
+
+
+
+
+
+) : (
+
+ {title &&
}
+
{type.toUpperCase()}
+
+
+
+
+)}
+
+
diff --git a/src/components/content/Figure.astro b/src/components/content/Figure.astro
new file mode 100644
index 0000000..63f125c
--- /dev/null
+++ b/src/components/content/Figure.astro
@@ -0,0 +1,63 @@
+---
+import { Image } from 'astro:assets';
+
+interface Props {
+ src: string;
+ alt: string;
+ caption?: string;
+ credit?: string;
+}
+
+const { src, alt, caption, credit } = Astro.props;
+
+const images = import.meta.glob<{ default: ImageMetadata }>(
+ '/src/content/**/*.{png,jpg,jpeg,webp,avif}',
+ { eager: true },
+);
+
+const imagePath = `/src${src}`;
+console.log(imagePath);
+
+const image = images[imagePath]?.default;
+---
+
+
+ {image ? (
+
+ ) : (
+
+ )}
+ {(caption || credit) && (
+
+ {caption && {caption}}
+ {credit && {credit}}
+
+ )}
+
+
+
diff --git a/src/components/content/Sidenote.astro b/src/components/content/Sidenote.astro
new file mode 100644
index 0000000..ae9ba67
--- /dev/null
+++ b/src/components/content/Sidenote.astro
@@ -0,0 +1,114 @@
+---
+interface Props {
+ id: string;
+ marker: string;
+ type: string;
+ content: string;
+ title: string;
+}
+
+const {id, marker, type, content, title} = Astro.props
+---
+
+
+
+ {title &&
}
+
{type.toUpperCase()}
+
{content}
+
+
+
+
+
diff --git a/src/components/layout/PostHeader/Cover.astro b/src/components/layout/PostHeader/Cover.astro
index 5cb5a19..8e02885 100644
--- a/src/components/layout/PostHeader/Cover.astro
+++ b/src/components/layout/PostHeader/Cover.astro
@@ -57,8 +57,7 @@ const image = images[imagePath]?.default;
}
.caption {
- padding: var(--spacing-snug);
- font-family: var(--font-mono);
+ padding: var(--space-4);
color: var(--color-text-inverse);
background-color: var(--color-surface-inverse);
}
@@ -70,6 +69,6 @@ const image = images[imagePath]?.default;
.caption-text,
.caption-meta {
- font-size: var(--typo-size-sm);
+ @mixin typo_code;
}
diff --git a/src/content/articles/alchemical-materialism/index.mdoc b/src/content/articles/alchemical-materialism/index.mdoc
index e19d7ac..2d07ce9 100644
--- a/src/content/articles/alchemical-materialism/index.mdoc
+++ b/src/content/articles/alchemical-materialism/index.mdoc
@@ -21,7 +21,7 @@ seo:
- **Foundational system** of the crucible
- Generates **entities** – cultures, religions, states, vessels, magical traditions – through **systematic elemental combination,** not arbitrary assignment
- Two interlocking element sets
- - **Three Primes =>** Soul, Body, Spirit; dynamic forces in tension
+ - **Three Primes** **=>** Soul, Body, Spirit; dynamic forces in tension
- **Five Essences =>** Earth, Fire, Water, Air, Aether; multivalent elemental character
- **Elemental Pool Mechanic =>** add tokens from material conditions and history, tally, spend on capabilities, traits, and relationships
- **One Engine =>** elemental pool drives everything; capabilities, identity, and cultural character all flow from the same source
@@ -210,8 +210,6 @@ seo:
- No – the product is neither ingredient
{% /table %}
----
-
## The Seven Aspects
- Provide additional specificity
@@ -243,6 +241,12 @@ seo:
- Mercury
- Thought, consciousness; intellect, emotion, skills
---
+- *Mysteries*
+- {% ElementSymbol element="mysteries" size="var(--type-3)" color="inherit" /%}
+- Gold
+- Sun
+- Hidden, occult; magic, destiny, secrets, *residua*{% Sidenote #residua title="What are Residua?" marker="⋄" content="Residua are our fancy name for otherworldly realms – the energies that were left over when the world was created. Think the underworld, inferno, or the elemental planes." type="default" /%}
+---
- *Society*
- {% ElementSymbol element="society" size="var(--type-3)" color="inherit" /%}
- Tin
@@ -255,12 +259,6 @@ seo:
- Venus
- Creation, expression; artistry, rituals, craft
---
-- *Mysteries*
-- {% ElementSymbol element="mysteries" size="var(--type-3)" color="inherit" /%}
-- Gold
-- Sun
-- Hidden, occult; magic, destiny, secrets, *residua*
----
- *Forces*
- {% ElementSymbol element="forces" size="var(--type-3)" color="inherit" /%}
- Iron
diff --git a/src/content/articles/domain/index.mdoc b/src/content/articles/domain/index.mdoc
new file mode 100644
index 0000000..416c5e6
--- /dev/null
+++ b/src/content/articles/domain/index.mdoc
@@ -0,0 +1,321 @@
+---
+title: Domain
+subtitle: What the earth provides
+summary: >-
+ Climate, topography, biome, features, resources. The ground beneath; what
+ grows, what weathers, what the soil yields. The material conditions that force
+ civilizations into being.
+cover:
+ showInHeader: false
+publishDate: 2026-03-05T08:47:00.000Z
+status: published
+isFeatured: false
+parent: prima-materia
+tags:
+ - Prima Materia
+ - Generation
+ - Domain
+relatedArticles: []
+seo:
+ noIndex: false
+---
+## Overview
+
+- **Map is the Starting Point;** napkin sketch or Azgaar export
+- Domain = Climate + Topography + Biome + Features + Resources
+- **Scale**
+ - Locality → Territory → Province → Region → Area → Landmass
+ - *Features* and *resources* are **locality-level** – a river is *here*, iron is in *this* valley
+ - *Resources* = **abundance,** not exclusive access; most common material is available to anyone; the list marks what a locality has in surplus
+- **Climate =>** 10 types; simplified Köppen-Geiger; temperature, precipitation, characteristics
+- **Topography =>** 6 types; elevation and terrain character
+- **Biome =>** 10 types; dominant vegetation;
+- **Features**{% Sidenote #features-resources title="Relevance of features & resources" marker="⋄" content="Features and resources are only relevant on the locality level" type="optional" /%} **=>** 10 types; rivers, coasts, volcanoes, passes, settlements; affect availability tables downstream
+- **Resources =>** 75 entries; what the land yields in abundance
+- Tables follow below; elemental association appear later when they become mechanically relevant
+
+## Procedure
+
+1. **Pick your area or region** – the broad stroke; a continent's western coast, a mountain range, a river basin
+1. **Generate localities within it** – each locality gets its own Climate, Topography, Biome
+ - For those wanting a more random approach, the following [table](https://docs.google.com/spreadsheets/d/16d9MUMFMqbUXkL6bOHN3sbFSvQiR0K3yta--RiW4Ldw/edit?usp=sharing) can be used to generate biomes based on Climate, Topography, and Features
+1. **Assign features** – rivers, coasts, passes, settlements
+1. **Roll for resources** – each locality has a **1-in-10 chance** to generate a resource
+1. **Read the dominant pattern** – the Climate, Topography, and Biome that appear most across localities become the domain signature for the culture living there
+{% Callout type="example" title="Gwayth Teyd" %}
+Looking at the map for my »Chainbreaker« setting, I decide to start with the area on the western coast of Khimbria – my northern continent which is dominated by temperate rainforest. Knowing that I want to the dominant culture to be inspired by the britannic tribes of the antiquity, I decide to call it: the Gwayth Ted.
+{% /Callout %}
+
+## Climate
+
+- Borrowed and simplified from Köppen-Geiger climate classification
+- Ten types covering the range of conditions your world might produce
+
+{% table %}
+- Climate
+- Temp.
+- Precipiation
+- K-G
+- Characteristics
+---
+- *Artic*
+- Extremely cold winters (< -30°C); cool summers (<10°C)
+- Very low; < 300 mm/year
+- ET, EF
+- Cold year-round; mostly snow; permafrost
+---
+- *Arid*
+- Hot summers (>30°C) or cold winters (<0°C)
+- Very low; < 250mm/year
+- BWh, BWk
+- True desert; hot (BWh) or cold (BWk)
+---
+- *Continental*
+- Cold winters (<0°C); hot summers (>25°C)
+- Moderate; 500 – 1000 mm/year
+- Dfa, Dfb, Dwa, Dwb
+- Sharp seasonal swing; summer rainfall
+---
+- *Mediterranean*
+- Mild winters (0 – 10°C); hot summers (>25°C)
+- Moderate; 500–900mm/year; dry summers
+- Csa, Csb
+- Hot dry summers; wet winters
+---
+- *Monsoon*
+- Warm year-round (>20°C); mild seasonal variation
+- Very high seasonal; 1500–3000+mm/year in wet seasonal
+- Am
+- Extreme wet/dry cycle; flooding defines calendar
+---
+- *Oceanic*
+- Mild winters (0–10°C); cool summers (<25°C)
+- High; > 800mm/year
+- Cfb, Cfc
+- Mild year-round; contant rainfall; grey skies
+---
+- *Semiarid*
+- Cold winters (often <0°C); hot summers (>25°C)
+- Low to moderate; 250mm/year
+- BSh, BSk
+- Steppe; drier than grassland, wetter than desert
+---
+- *Subarctic*
+- Very cold winters (< -20°C); short summers (<15°C)
+- Low to moderate; 300–600mm/year
+- Dfc, Dfd, Dwc, Dwd
+- Brutal winters; brief growing season
+---
+- *Subtropical*
+- Mild winters (5–10°C); hot summers (>22°C)
+- Moderate to high; 600-1200mm/year
+- Cfa, Cwa
+- Humid heat; mild winters; even rainfall
+---
+- *Tropical*
+- Warm year-round; avg > 18°C
+- High; > 1500mm/year
+- Af, Aw/As
+- Constant warmth; heavy rain; Aw/As have dry season
+{% /table %}
+
+## Topography
+
+- Lay of the land
+- Elevation shapes what grows, who lives here, and how hard it is to move
+
+{% table %}
+- Topography
+- Elevation
+- Characteristics
+---
+- *Lowlands*
+- Below sea level to 50 m
+- Deltas, polders, fens, tidal flats; often waterlogged or reclaimed; fertile but floodprone
+---
+- *Plains*
+- 50–300 m
+- Open steppe, prairie, farmland; wide horizons; wind-exposed; good for agriculture and cavalry
+---
+- *Hills*
+- 300–800 m
+- Rolling terrain; mixed land use; defensible ridgelines; broken sightlines
+---
+- *Highlands*
+- 800 – 1500 m (flat)
+- Elevated plateaus; wind-scoured; isolated; natural fortress country
+---
+- *Mountains*
+- 1500+ m
+- Peaks, passes, and valleys; vertical climate zones; barriers to movement; mineral wealth
+---
+- *Badlands*
+- Variable
+- Eroded, broken terrain; gullies, mesas, exposed rock; poor soil; difficult to settle, easy to hide in
+{% /table %}
+
+## Biome
+
+- Dominant Vegetation
+- What grows tells you what lives
+
+{% table %}
+- Biome
+- Description
+---
+- *Barren*
+- Salt flats, volacanic waste, eroded badlands; no significant vegetation; exposed rock and mineral deposits
+---
+- *Tundra*
+- Permafrost beneath low scrub, mosses, lichens; treeless; wind-exposed; brief growing season
+---
+- *Shrublands*
+- Dry-adapted woody scrub; charparral, maquis, fynbos; fire-prone, wind-scoured, quick to regenerate
+---
+- *Grasslands*
+- Open steppe, prairie, pampas; deep fertile soil beneath tall grasses; few trees, wide horizons
+---
+- *Savanna*
+- Tropical or subtropical grassland with scattered trees; distinct wet and dry seasons; periodic burns
+---
+- *Wetlands*
+- Bogs, marshes, fens, swamps; waterlogged ground; dense reed bets, peat deposists; high biodiversity
+---
+- *Woods*
+- Open canopy with clearings; light penetrates to ground level; mixed undergrowth; edge habitat
+---
+- *Forest*
+- Dense temperate woodland; heavy canopy; deep leaf litter, large hardoods; oak, beech, maple
+---
+- *Rainforest*
+- Multi-layered tropical canopy; constant moisture; maximum biodiversity; epiphytes, vines, dense undergrowth
+---
+- *Taiga*
+- Boreal connifer forest; spruce, pine, larch over muskeg and frozen ground;
+{% /table %}
+
+## Features
+- Geographical features at the locality level
+{% table %}
+- Feature
+- Description
+---
+- *River*
+- Flowing freshwater; trade artery, irrigation source, natural boundary; floods deposit fertile silt
+---
+- *Coast*
+- Land meets sea; harbors, tidal flats, salt marshes; access to maritime trade and naval power
+---
+- *Lake*
+- Enclosed body of water; fishery, freshwater reserve, natural landmark; still water hides depth
+---
+- *Floodplain*
+- Low ground beside rivers; seasonally inundated; rich alluvial soik; high yield, high risk
+---
+- *Oasis*
+- Isolated water source in arid terrain; date palms, wells, caravan stops; life surrounded by nothing
+---
+- *Volcano*
+- Active or dormant; volcanic soil is fertile, eruptions devastating; obsidian, sulfur, hot spring nearby
+---
+- *Island*
+- Land surrounded by water; isolation breeds distinctiveness; defensible, resource-limited, reade-dependent
+---
+- *Mountain Pass*
+- Navigable route through high terrain; chokepoint for trade and invasion; whoever holds it controls movement
+---
+- *Town*
+- Permanent settlement; market, craft production, local administration; polulation 1000–5000
+---
+- *City*
+- Major settlement; walls, institutions, dense population, surplus extraction, specialization, political gravity; population 5000+
+{% /table %}
+
+## Resources
+- What the land provides
+- Each locality has a **1-in-10 chance** to generate a resource
+- Mark **abundance** – a defining surplus worth trading or fighting over
+- Resources are used in the *Conjuction* state
+- Use the following [table](https://docs.google.com/spreadsheets/d/1bUUr5F93SrQ4fZm7ONf2YTudJ-1lnbyKiFHbtLgMgrY/edit?usp=sharing) to roll for the resource of a locality
+
+| Resources | Description |
+|--------------------|-------------------------------------------------------------------------------------------------------|
+| Stone | Limestone, granite, sandstone, slate; ubiquitous building and toolmaking material |
+| Base Metals | Copper, tin, lead, zinc; smelted from ore into everyday tools, vessels, fittings |
+| Vegetables | Root crops, legumes, leafy greens; garden staples grown in moist soil |
+| Fruits | Orchard and vine crops; apples, figs, pomegranates, pears; wind-pollinated, sun-ripened |
+| Staple Grains | Wheat, barley, millet, sorghum; the caloric foundation of settled civilization |
+| Clay | Shaped wet, hardened by kiln; pottery, brick, tile, ceramic |
+| Alchemical Earths | Calcium oxide, potash, natron, vitriol; reactive mineral substances used in processing and craft |
+| Fish | River and coastal catch; preserved by salt, smoke, or drying |
+| Hunting Companions | Hawks, falcons, hounds; trained animals used in pursuit and killing of game |
+| Resins | Hardened tree sap; pine tar, frankincense, myrrh; sealant, adhesive, incense |
+| Salt | Mineral preservative and seasoning; harvested from sea, spring, or mine |
+| Oilseeds | Sesame, flax, rapeseed; pressed and heated into oil for lamps, cooking, and craft |
+| Furs | Beaver, rabbit, fox pelts; insulation, clothing, and high-volume trade commodity |
+| Livestock | Cattle, sheep, goats, pigs; kept for meat, milk, wool, leather, traction |
+| Reeds | Marsh grasses; used for weaving, thatching, basket-making, matting |
+| Hemp | Coarse fiber plant; rope, sailcloth, sacking, rough textile |
+| Softwoods | Pine, fir, spruce; fast-growing construction timber and primary fuel wood |
+| Dye Plants | Indigo, madder, weld; plants processed into colorfast textile dyes |
+| Flax | Fine fiber plant; retted and spun into linen; light, breathable fabric and sailcloth |
+| Aromatic Herbs | Lavender, rosemary, sage, thyme; culinary and ritual aromatics |
+| Woad | Leaf-derived blue pigment; body paint, textile dye; labor-intensive extraction |
+| Psychoactives | Mushrooms, ergot, datura, cannabis; mind-altering substances used in ritual and medicine |
+| Honey | Bee product; sweetener, preservative, fermented into mead; harvested from wild or kept hives |
+| Wild Game | Deer, boar, hare, game birds; hunted in open country and woodland |
+| Earth Pigments | Ochre, umber, sienna; mineral pigments ground for paint, body decoration, and burial rites |
+| Seals | Marine mammals hunted for blubber, oil, meat, and heavy pelts; cold-water resource |
+| Iron | Smelted from bog or mountain ore; tools, weapons, structural hardware; the essential metal |
+| Olives | Long-lived orchard tree; fruit pressed into oil for cooking, lighting, preservation, and anointing |
+| Hardwoods | Oak, ash, elm, walnut; dense timber for shipbuilding, furniture, tools, construction |
+| Jade | Nephrite or jadeite; dense, hard stone worked into blades, ornaments, and ritual objects |
+| Dates | Desert palm fruit; high-calorie staple of arid regions; dried for storage and transport |
+| Artisan Stone | Alabaster, marble, pumice, soapstone; decorative and sculptural stone shaped by cutting and polishing |
+| Dyes | Processed colorants; cochineal, kermes, woad extract; requiring boiling, mordanting, vat fermentation |
+| Bamboo | Fast-growing giant grass; hollow, light, strong; construction, scaffolding, weapons, tools |
+| Obsidian | Volcanic glass; fractures to razor edges; blades, arrowheads, mirrors |
+| Rice | Grain cultivated in flooded paddies; requires irrigation infrastructure and water management |
+| Citrus | Lemons, oranges, limes; juicy, acidic fruit; prevents scurvy, flavors food, preserves |
+| Papyrus | Aquatic reed processed into writing material; stripped, layered, pressed, dried |
+| Grapes | Vine fruit; eaten fresh, dried as raisins, crushed and fermented into wine and vinegar |
+| Medicinal Herbs | Healing plants; prepared as poultice, tincture, tea, or salve; specialized knowledge required |
+| Special Livestock | Camels, llamas, yaks, reindeer; animals adapted to extreme terrain; transport, milk, fiber, meat |
+| Cotton | Tropical fiber plant; ginned, carded, spun into soft, dyeable textile |
+| Pelts | Luxury furs; ermine, sable, marten; high-value status goods moving through long-distance trade |
+| Horses | Riding, draft, and pack animal; cavalry, courier, transport; requires pasture and fodder |
+| Silk | Caterpillar cocoon fiber; reeled, twisted, woven into extremely fine, strong fabric |
+| Maize | Corn; high-yield grain requiring processing (nixtamalization); flour, porridge, flatbread |
+| Yew | Slow-growing toxic hardwood; exceptional bow-stave timber; durable, flexible, poisonous |
+| Tea | Dried leaf steeped in hot water; stimulant beverage; requires specific highland growing conditions |
+| Ivory (marine) | Walrus and narwhal tusk; carved into fine objects, jewelry, inlay; rare and highly portable |
+| Amber | Fossilized tree resin; translucent, sometimes containing preserved insects; ornamental and trade good |
+| Porphyry | Dense purple-red igneous stone; extremely hard to quarry and work; imperial building material |
+| Bitumen | Natural tar and petroleum seeps; waterproofing, adhesive, fuel, caulking |
+| Murex | Sea snail harvested for purple dye; thousands of shells yield grams of pigment |
+| Saffron | Crocus flower stigma; harvested by hand, three threads per flower; dye, spice, medicine |
+| Kaolin | Crocus flower stigma; harvested by hand, three threads per flower; dye, spice, medicine |
+| War Beasts | Elephants, rhinoceri, or similar; large animals trained for combat; living siege weapons |
+| Precious Stones | Diamonds, rubies, sapphires, emeralds; formed under extreme pressure and heat; cut and polished |
+| Arsenic | Realgar, orpiment; toxic mineral compounds; pigment, poison, glass-making additive |
+| Sulfur | Yellow mineral from volcanic deposits; combustible; used in fumigation, medicine, fire-weapons |
+| Cinnabar | Mercury sulfide ore; bright vermillion pigment; toxic; source of liquid mercury |
+| Sugar | Cane or beet juice boiled and crystallized; requires heavy irrigation and intensive labor |
+| Spices | Pepper, cinnamon, clove, nutmeg; dried seeds, bark, and buds; flavor, preservation, medicine |
+| Pearls | Formed inside oysters and mussels; harvested by diving; gems requiring no cutting or polishing |
+| Precious Metals | Gold and silver; panned, mined, smelted; currency, jewelry, gilding, ritual objects |
+| Silphium | Extinct or near-extinct plant; medicinal, contraceptive, culinary; once worth its weight in coin |
+| Steppe Horses | Superior war and riding breeds from harsh grassland; exceptional endurance and hardiness |
+| Coffee | Roasted bean brewed into stimulant drink; requires specific tropical highland growing conditions |
+| Whales | Hunted for oil, bone, baleen, ambergris; deep-ocean pursuit requiring specialized vessels |
+| Rare Woods | Ebony, lignum vitae, purpleheart, sandalwood; dense exotic timber from distant sources |
+| Cedar | Aromatic rot-resistant softwood; temple and ship timber; naturally insect-repellent |
+| Cocoa | Tree pod containing bitter seeds; roasted and ground into paste or drink; requires tropical lowlands |
+| Incense | Frankincense, myrrh, and similar gum resins; burned to produce aromatic smoke |
+| Lapis Lazuli | Deep blue metamorphic stone; ground into ultramarine pigment; jewelry, inlay, decoration |
+| Coca | Shrub leaf chewed or brewed as stimulant; suppresses hunger and altitude sickness; highland crop |
+| Poppy | Flower producing opium latex; powerful painkiller and sedative; highly addictive |
+
+{% Callout type="example" title="Gwayth Teyd Part II" %}
+After having generated the localities of the Gwayth Teyd, I discover that most of its localities have a *Oceanic climate* while the terrain is mostly *Lowlands* with a *Forest* vegetation. The Gwayth Teyd also features a surpsingly high number of *Silk* and *Dye* resources.
+{% /Callout %}
diff --git a/src/content/articles/kindred/index.mdoc b/src/content/articles/kindred/index.mdoc
new file mode 100644
index 0000000..60e109a
--- /dev/null
+++ b/src/content/articles/kindred/index.mdoc
@@ -0,0 +1,19 @@
+---
+title: Kindred
+subtitle: What the Body is
+summary: >-
+ Species-level biology. The body you're born in, not the culture you're born
+ into. Humans are the baseline; everything else carries elemental weight.
+cover:
+ showInHeader: false
+publishDate: 2026-03-05T08:51:00.000Z
+status: draft
+isFeatured: false
+parent: prima-materia
+tags:
+ - Prima Materia
+ - Generator
+relatedArticles: []
+seo:
+ noIndex: false
+---
diff --git a/src/content/articles/meta/index.mdoc b/src/content/articles/meta/index.mdoc
new file mode 100644
index 0000000..b07a7a6
--- /dev/null
+++ b/src/content/articles/meta/index.mdoc
@@ -0,0 +1,13 @@
+---
+title: The Metatron
+summary: Where meta stuff is presented
+cover:
+ showInHeader: false
+publishDate: 2026-03-04T23:45:00.000Z
+status: published
+isFeatured: false
+tags: []
+relatedArticles: []
+seo:
+ noIndex: false
+---
diff --git a/src/content/articles/prima-materia/index.mdoc b/src/content/articles/prima-materia/index.mdoc
index f20dc93..83610e5 100644
--- a/src/content/articles/prima-materia/index.mdoc
+++ b/src/content/articles/prima-materia/index.mdoc
@@ -11,7 +11,22 @@ parent: the-crucible
tags:
- Crucible Stage
- Generation
+ - Material Conditions
+ - Domain
+ - Kindred
relatedArticles: []
seo:
noIndex: false
---
+## Overview
+
+- **Stage 1** of the Crucible; tangible foundation
+- Everything built later – kin, faith, magic, states – rests on what exists here
+- The physical world before culture touches it
+- Two halves:
+ - [**Domain:**](/the-crucible/prima-materia/domain) the land; climate, topography, biome, features, resources
+ - [**Kindred:**](/the-crucible/prima-materia/kindred) the biology; species-level bodies, physical traits, innate abilities
+- Neither half generates *Culture* – that's **Calcination**
+- *Prima Materia* generates **Conditions;** the material constraints culture must respond to
+- Different land, different problems; different problems, different solutions; different solutions, different civilisations
+- The land does not care what you believe; the body does not care what you build
diff --git a/src/fonts/JuliaMono/JuliaMono-Black.woff2 b/src/fonts/JuliaMono/JuliaMono-Black.woff2
new file mode 100644
index 0000000..0b68f01
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-Black.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-BlackItalic.woff2 b/src/fonts/JuliaMono/JuliaMono-BlackItalic.woff2
new file mode 100644
index 0000000..f04e075
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-BlackItalic.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-Bold.woff2 b/src/fonts/JuliaMono/JuliaMono-Bold.woff2
new file mode 100644
index 0000000..0cadc8e
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-Bold.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-BoldItalic.woff2 b/src/fonts/JuliaMono/JuliaMono-BoldItalic.woff2
new file mode 100644
index 0000000..50dea7e
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-BoldItalic.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-BoldLatin.woff2 b/src/fonts/JuliaMono/JuliaMono-BoldLatin.woff2
new file mode 100644
index 0000000..4aaee2c
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-BoldLatin.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-ExtraBold.woff2 b/src/fonts/JuliaMono/JuliaMono-ExtraBold.woff2
new file mode 100644
index 0000000..39b7eb4
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-ExtraBold.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-ExtraBoldItalic.woff2 b/src/fonts/JuliaMono/JuliaMono-ExtraBoldItalic.woff2
new file mode 100644
index 0000000..0f373de
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-ExtraBoldItalic.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-Light.woff2 b/src/fonts/JuliaMono/JuliaMono-Light.woff2
new file mode 100644
index 0000000..6391227
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-Light.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-LightItalic.woff2 b/src/fonts/JuliaMono/JuliaMono-LightItalic.woff2
new file mode 100644
index 0000000..cd952e4
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-LightItalic.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-Medium.woff2 b/src/fonts/JuliaMono/JuliaMono-Medium.woff2
new file mode 100644
index 0000000..e22ed87
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-Medium.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-MediumItalic.woff2 b/src/fonts/JuliaMono/JuliaMono-MediumItalic.woff2
new file mode 100644
index 0000000..c8a750d
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-MediumItalic.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-Regular.woff2 b/src/fonts/JuliaMono/JuliaMono-Regular.woff2
new file mode 100644
index 0000000..2d5fc06
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-Regular.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-RegularItalic.woff2 b/src/fonts/JuliaMono/JuliaMono-RegularItalic.woff2
new file mode 100644
index 0000000..498e94a
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-RegularItalic.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-RegularLatin.woff2 b/src/fonts/JuliaMono/JuliaMono-RegularLatin.woff2
new file mode 100644
index 0000000..18479c2
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-RegularLatin.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-SemiBold.woff2 b/src/fonts/JuliaMono/JuliaMono-SemiBold.woff2
new file mode 100644
index 0000000..8582d49
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-SemiBold.woff2 differ
diff --git a/src/fonts/JuliaMono/JuliaMono-SemiBoldItalic.woff2 b/src/fonts/JuliaMono/JuliaMono-SemiBoldItalic.woff2
new file mode 100644
index 0000000..174b388
Binary files /dev/null and b/src/fonts/JuliaMono/JuliaMono-SemiBoldItalic.woff2 differ
diff --git a/src/keystatic/components/callout.ts b/src/keystatic/components/callout.ts
new file mode 100644
index 0000000..f26ab3b
--- /dev/null
+++ b/src/keystatic/components/callout.ts
@@ -0,0 +1,39 @@
+import { wrapper } from '@keystatic/core/content-components';
+import { fields } from '@keystatic/core';
+import { megaphoneIcon } from '@keystar/ui/icon/icons/megaphoneIcon';
+
+const calloutComponent = {
+ Callout: wrapper({
+ label: 'Callout',
+ icon: megaphoneIcon,
+ schema: {
+ type: fields.select({
+ label: 'Type',
+ defaultValue: 'default',
+ options: [
+ {
+ label: 'Default',
+ value: 'default',
+ },
+ {
+ label: 'Optional',
+ value: 'optional',
+ },
+ {
+ label: 'Example',
+ value: 'example',
+ },
+ {
+ label: 'Spoiler',
+ value: 'spoiler',
+ },
+ ],
+ }),
+ title: fields.text({
+ label: 'Title',
+ }),
+ },
+ }),
+};
+
+export default calloutComponent;
diff --git a/src/keystatic/components/figure.ts b/src/keystatic/components/figure.ts
new file mode 100644
index 0000000..6a3b8ef
--- /dev/null
+++ b/src/keystatic/components/figure.ts
@@ -0,0 +1,35 @@
+import { imageIcon } from '@keystar/ui/icon/icons/imageIcon';
+import { block } from '@keystatic/core/content-components';
+import { fields } from '@keystatic/core';
+
+const figureComponent = {
+ Figure: block({
+ label: 'Figure',
+ icon: imageIcon,
+ schema: {
+ src: fields.image({
+ label: 'Image',
+ directory: 'src/content/articles',
+ publicPath: '/content/articles',
+ }),
+ alt: fields.text({
+ label: 'Alt Text',
+ validation: {
+ length: {
+ min: 1,
+ },
+ },
+ }),
+ caption: fields.text({
+ label: 'Caption',
+ multiline: true,
+ }),
+ credit: fields.text({
+ label: 'Credit/Attribution',
+ description: 'Photographer, artist, or source',
+ }),
+ },
+ }),
+};
+
+export default figureComponent;
diff --git a/src/keystatic/components/index.ts b/src/keystatic/components/index.ts
index 63a02fc..f7b2f0e 100644
--- a/src/keystatic/components/index.ts
+++ b/src/keystatic/components/index.ts
@@ -1,5 +1,11 @@
import elementComponent from './element.ts';
+import calloutComponent from './callout.ts';
+import sidenoteComponent from './sidenote.ts';
+import figureComponent from './figure.ts';
export const generalComponents = {
...elementComponent,
+ ...calloutComponent,
+ ...sidenoteComponent,
+ ...figureComponent,
};
diff --git a/src/keystatic/components/sidenote.ts b/src/keystatic/components/sidenote.ts
new file mode 100644
index 0000000..1f65ca1
--- /dev/null
+++ b/src/keystatic/components/sidenote.ts
@@ -0,0 +1,50 @@
+import { inline } from '@keystatic/core/content-components';
+import { fields } from '@keystatic/core';
+import { panelRightDashedIcon } from '@keystar/ui/icon/icons/panelRightDashedIcon';
+
+const sidenoteComponents = {
+ Sidenote: inline({
+ label: 'Sidenote',
+ icon: panelRightDashedIcon,
+ schema: {
+ id: fields.text({
+ label: 'ID',
+ validation: {
+ isRequired: true,
+ },
+ }),
+ title: fields.text({
+ label: 'Title',
+ }),
+ marker: fields.text({
+ label: 'Marker',
+ defaultValue: '⋄',
+ }),
+ content: fields.text({
+ label: 'Body',
+ multiline: true,
+ validation: { isRequired: true },
+ }),
+ type: fields.select({
+ label: 'Type',
+ options: [
+ {
+ value: 'default',
+ label: 'Default',
+ },
+ {
+ value: 'example',
+ label: 'Example',
+ },
+ {
+ value: 'optional',
+ label: 'Optional',
+ },
+ ],
+ defaultValue: 'default',
+ }),
+ },
+ }),
+};
+
+export default sidenoteComponents;
diff --git a/src/layouts/ContentLayout.astro b/src/layouts/ContentLayout.astro
index 7b77dc5..912c265 100644
--- a/src/layouts/ContentLayout.astro
+++ b/src/layouts/ContentLayout.astro
@@ -5,6 +5,8 @@ import Base from '@layouts/Base.astro';
import Header from '@compontents/layout/PostHeader/index.astro';
import { getBreadcrumbs } from '@lib/utils/paths';
import { toMilitaryDTG } from '@lib/utils/date';
+import { extractSidenotes } from '@lib/utils/extractors';
+import Sidenote from '@compontents/content/Sidenote.astro';
interface Props {
entry: CollectionEntry<'articles'> | CollectionEntry<'elements'>;
@@ -15,7 +17,8 @@ const { entry, collectionName } = Astro.props;
const { Content } = await render(entry);
const { title, summary, subtitle, updateDate, publishDate, tags, seo } =
entry.data;
-const hasMargin = Astro.slots.has('margin')
+const sidenotes = entry.body ? extractSidenotes(entry.body) : [];
+const hasMargin = Astro.slots.has('margin') || !!sidenotes;
const breadcrumbs = await getBreadcrumbs(
entry.data.parent ?? null,
@@ -57,6 +60,7 @@ const headerCover =
{hasMargin &&(
)}
@@ -69,8 +73,9 @@ const headerCover =
.frame {
@mixin layout-wrapper;
+ position: relative;
+
@media (--bp-desktop) {
- position: relative;
display: grid;
grid-template-columns: var(--layout-content-width) var(--layout-margin-width);
gap: var(--layout-gutter);
@@ -82,7 +87,6 @@ const headerCover =
}
.margin {
- position: relative;
min-width: 0;
}
diff --git a/src/lib/paths_legacy.ts b/src/lib/paths_legacy.ts
deleted file mode 100644
index 8e5c9b4..0000000
--- a/src/lib/paths_legacy.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import { getCollection, type CollectionEntry } from 'astro:content';
-
-export type ContentType = 'article' | 'page' | 'element';
-
-export interface ResolvedEntry {
- type: ContentType;
- path: string;
- entry:
- | CollectionEntry<'pages'>
- | CollectionEntry<'articles'>
- | CollectionEntry<'elements'>;
-}
-
-function buildPath(
- slug: string,
- parentSlug: string | null,
- articles: CollectionEntry<'articles'>[],
- visited: Set = new Set(),
-): string {
- if (!parentSlug) return slug;
- if (visited.has(parentSlug)) {
- console.warn(`Circular reference detected at ${parentSlug}`);
- return slug;
- }
- visited.add(parentSlug);
-
- const parent = articles.find((a) => a.slug === parentSlug);
- if (!parent) return slug;
-
- const parentPath = buildPath(
- parent.slug,
- parent.data.parent ?? null,
- articles,
- visited,
- );
-
- return `${parentPath}/${slug}`;
-}
-
-function resolveArticles(
- articles: CollectionEntry<'articles'>[],
-): ResolvedEntry[] {
- return articles
- .filter((entry) => entry.data.status === 'published')
- .map((entry) => ({
- type: 'article' as const,
- path: buildPath(entry.slug, entry.data.parent ?? null, articles),
- entry,
- }));
-}
-
-interface GlobEntry {
- id: string;
- data: { status: string; parent?: string | null };
-}
-
-function resolveGlobCollection(
- collection: GlobEntry[],
- type: ContentType,
- articles: CollectionEntry<'articles'>[],
-): { type: ContentType; path: string; entry: GlobEntry }[] {
- return collection
- .filter((entry) => entry.data.status === 'published')
- .map((entry) => ({
- type,
- path: buildPath(entry.id, entry.data.parent ?? null, articles),
- entry,
- }));
-}
-
-export async function resolveAllPaths(): Promise {
- const articles = await getCollection('articles');
- const elements = await getCollection('elements');
- const pages = await getCollection('pages');
-
- const resolvedPages: ResolvedEntry[] = pages.map((page) => ({
- type: 'page' as const,
- path: `static/${page.slug}`,
- entry: page,
- }));
-
- return [
- ...resolveArticles(articles),
- ...resolveGlobCollection(elements, 'element', articles),
- ...resolvedPages,
- ] as ResolvedEntry[];
-}
diff --git a/src/lib/types/content.ts b/src/lib/types/content.ts
index f151ec6..749df63 100644
--- a/src/lib/types/content.ts
+++ b/src/lib/types/content.ts
@@ -41,3 +41,13 @@ export interface PaletteEntry {
parent: string | null;
depth: number;
}
+
+/** Sidenote */
+
+export interface Sidenote {
+ id: string;
+ title: string;
+ marker: string;
+ type: string;
+ content: string;
+}
diff --git a/src/lib/utils/extractors.ts b/src/lib/utils/extractors.ts
new file mode 100644
index 0000000..b7c6214
--- /dev/null
+++ b/src/lib/utils/extractors.ts
@@ -0,0 +1,21 @@
+import Markdoc from '@markdoc/markdoc';
+import type { Sidenote } from '@lib/types/content';
+
+export function extractSidenotes(raw: string): Sidenote[] {
+ const ast = Markdoc.parse(raw);
+ const sidenotes: Sidenote[] = [];
+
+ for (const node of ast.walk()) {
+ if (node.type === 'tag' && node.tag === 'Sidenote') {
+ const attrs = node.attributes;
+ sidenotes.push({
+ id: attrs.id,
+ marker: attrs.marker ?? '⋄',
+ title: attrs.title ?? '',
+ type: attrs.type ?? 'default',
+ content: attrs.content,
+ });
+ }
+ }
+ return sidenotes;
+}
diff --git a/src/styles/base/colors.css b/src/styles/base/colors.css
index b962b1d..cc9e07d 100644
--- a/src/styles/base/colors.css
+++ b/src/styles/base/colors.css
@@ -83,7 +83,8 @@
--color-overlay-heavy: oklch(0% 0 0deg / 60%);
/* == Shadows == */
- --color-shadow: oklch(0% 0 0deg / 10%);
+ --color-shadow: var(--space-2) var(--space-2) 0 oklch(from var(--color-surface-inverse) calc(l - 0.075) c h), var(--space-4) var(--space-4) 0 oklch(from var(--color-surface-inverse) calc(l - 0.2) c h);
+;
--color-shadow-strong: oklch(0% 0 0deg / 25%);
/* == Utility == */
diff --git a/src/styles/base/content.css b/src/styles/base/content.css
index d73a6c8..3c1046c 100644
--- a/src/styles/base/content.css
+++ b/src/styles/base/content.css
@@ -233,7 +233,7 @@
contain: style;
list-style: none;
- & li {
+ & > li {
@mixin ml var(--space-6);
@mixin pl var(--space-4);
@@ -302,7 +302,7 @@
border: var(--size-1) solid var(--color-border-strong);
color: var(--color-text-inverse);
- text-align: left;
+ text-align: center;
background: var(--color-surface-inverse);
}
@@ -311,6 +311,14 @@
@mixin pa var(--space-4);
border: var(--size-1) solid var(--color-border-subtle);
+ text-align: center;
+ }
+
+ & th,
+ & td {
+ &:first-child {
+ text-align: left;
+ }
}
}
@@ -336,7 +344,8 @@
}
/* === DETAILS / SUMMARY === */
- & details {
+
+ /* & details {
@mixin mr var(--space-8);
@mixin border-l var(--size-4), solod, var(--color-border-normal);
@@ -377,7 +386,7 @@
& details > *:not(summary) {
@mixin px var(--space-6);
- }
+ } */
& hr {
@mixin my var(--space-10);
@@ -411,7 +420,8 @@
}
/* === FIGURES === */
- & figure {
+
+ /* & figure {
@mixin my var(--space-10);
& img {
@@ -426,7 +436,7 @@
color: var(--color-text-tertiary);
}
- }
+ } */
/* === INLINE ELEMENTS === */
@@ -459,6 +469,11 @@
text-decoration: none;
background: var(--color-primary);
}
+
+ &.sidenote-ref {
+ scroll-margin-top: calc(var(--layout-header-height) + var(--size-8));
+ text-decoration: none;
+ }
}
&:visited {
@@ -517,11 +532,11 @@
font-style: normal;
}
- & cite {
+ /* & cite {
font-weight: 600;
font-style: normal;
color: var(--color-text-secondary);
- }
+ } */
& q {
font-style: normal;
diff --git a/src/styles/base/fonts.css b/src/styles/base/fonts.css
index 36f919e..6ada313 100644
--- a/src/styles/base/fonts.css
+++ b/src/styles/base/fonts.css
@@ -11,6 +11,8 @@
src: url('/src/fonts/UnigrimDee-Regular.woff2') format('woff2');
}
+/**
+
@font-face {
font-family: 'Iosevka Slab';
font-weight: 300;
@@ -147,4 +149,125 @@
font-display: swap;
src: url('/src/fonts/IosevkaSansMono/IosevkaSansMono-ExtraBoldItalic.woff2')
format('woff2');
+ }
+**/
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 300;
+ font-style: normal;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-Light.woff2')
+ format('woff2');
+}
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 400;
+ font-style: normal;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-Regular.woff2')
+ format('woff2');
+}
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 500;
+ font-style: normal;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-Medium.woff2')
+ format('woff2');
+}
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 600;
+ font-style: normal;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-SemiBold.woff2')
+ format('woff2');
+}
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 700;
+ font-style: normal;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-Bold.woff2')
+ format('woff2');
+}
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 900;
+ font-style: normal;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-Black.woff2')
+ format('woff2');
+}
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 300;
+ font-style: italic;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-LightItalic.woff2')
+ format('woff2');
+}
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 400;
+ font-style: italic;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-RegularItalic.woff2')
+ format('woff2');
+}
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 500;
+ font-style: italic;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-MediumItalic.woff2')
+ format('woff2');
+}
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 600;
+ font-style: italic;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-SemiBoldItalic.woff2')
+ format('woff2');
+}
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 700;
+ font-style: italic;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-BoldItalic.woff2')
+ format('woff2');
+}
+
+@font-face {
+ font-family: 'Julia Mono';
+ font-weight: 900;
+ font-style: italic;
+
+ font-display: swap;
+ src: url('/src/fonts/JuliaMono/JuliaMono-BlackItalic.woff2')
+ format('woff2');
}
diff --git a/src/styles/base/primitives.css b/src/styles/base/primitives.css
index cce17ea..fcea5b5 100644
--- a/src/styles/base/primitives.css
+++ b/src/styles/base/primitives.css
@@ -146,8 +146,8 @@
/* === FONT FAMILIES === */
--font-header: 'Geist Variable', sans;
--font-display: 'Blaka',serif;
- --font-body: 'Iosevka Slab', sans;
- --font-mono: 'Iosevka Mono', monospace;
+ --font-body: 'Julia Mono', sans;
+ --font-mono: 'Julia Mono', monospace;
--font-symbols: 'Unigrim Dee', fantasy;
/* === SIZE (REM-based) === */
@@ -205,9 +205,10 @@
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
/* === LAYOUT === */
- --layout-content-width: minmax(0, 72ch);
- --layout-margin-width: minmax(18ch, 1fr);
- --layout-gutter: var(--space-6);
+ --layout-content-width: minmax(0, 80ch);
+ --layout-margin-width: minmax(10ch, 1fr);
+ --layout-gutter: var(--space-12);
--layout-page-margin: var(--space-8);
--layout-max-width: 90rem;
+ --layout-header-height: 3.125rem;
}
diff --git a/src/styles/mixins/patterns.css b/src/styles/mixins/patterns.css
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/src/styles/mixins/patterns.css
@@ -0,0 +1 @@
+