/* Gruvbox — the original palette by Pavel Pertsev, hard contrast background, in both of
   its published variants. One file, no framework, no build step beyond Jekyll itself.

   Theming works because every colour below goes through a token: nothing in this stylesheet
   names a hex except the two palettes here and the terminal colours at the very bottom, so
   switching themes is a swap of this block and nothing else.

   Each palette is written once. The obvious alternative — a `prefers-color-scheme` media
   query *and* a `[data-theme]` rule — needs the light palette twice, and two copies of a
   colour set is a future edit that lands in one of them. Instead the inline script in the
   page head always stamps an explicit `data-theme`, resolving the OS preference in JS, so
   the CSS only ever has to answer "dark or light", once each.

   With JavaScript off, no attribute is stamped and the page is dark. That is the site's
   default identity rather than a fallback nobody thought about. */

:root {
  color-scheme: dark;
  --bg0-hard: #1d2021;
  --bg0: #282828;
  --bg1: #3c3836;
  --bg2: #504945;
  --bg3: #665c54;
  --bg4: #7c6f64;

  --fg0: #fbf1c7;
  --fg1: #ebdbb2;
  --fg2: #d5c4a1;
  --fg3: #bdae93;
  --fg4: #a89984;
  --gray: #928374;

  --red: #fb4934;
  --green: #b8bb26;
  --yellow: #fabd2f;
  --blue: #83a598;
  --purple: #d3869b;
  --aqua: #8ec07c;
  --orange: #fe8019;

  --mono: ui-monospace, "JetBrains Mono", "Fira Code", "SF Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Inter, Helvetica, Arial, sans-serif;
  --measure: 74ch;
}

/* Gruvbox light, hard contrast. Not an inversion of the above — Pertsev publishes the light
   variant with its own accent values, darkened so they hold contrast against a cream ground
   instead of glowing against a near-black one. Inverting the dark accents instead would give
   a yellow nobody can read. */
:root[data-theme="light"] {
  color-scheme: light;
  --bg0-hard: #f9f5d7;
  --bg0: #fbf1c7;
  --bg1: #ebdbb2;
  --bg2: #d5c4a1;
  --bg3: #bdae93;
  --bg4: #a89984;

  --fg0: #282828;
  --fg1: #3c3836;
  --fg2: #504945;
  --fg3: #665c54;
  --red: #9d0006;
  --blue: #076678;
  --purple: #8f3f71;
  --green: #79740e;
  --orange: #af3a03;

  /* Three values are darkened a shade off canonical gruvbox light, and only these three.
     Measured against the grounds they actually sit on, Pertsev's published values land at
     4.4:1, 3.6:1 and 3.3:1 — under the 4.5:1 that WCAG AA asks for body-size text. They are
     used for muted metadata, inline code and the current nav item, all of which are small.

     Fidelity to a palette is not worth text somebody cannot read, and the deviation is a
     shade rather than a redesign. The dark palette needs no such adjustment and is canonical
     throughout. */
  --fg4: #796c62;   /* canonical #7c6f64 — 4.42:1 on --bg0-hard */
  --gray: #796c62;
  --aqua: #38694b;  /* canonical #427b58 — 3.64:1 on --bg1 behind inline code */
  --yellow: #966110; /* canonical #b57614 — 3.33:1 on --bg0 for the current nav item */
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg0-hard);
  color: var(--fg1);
  font: 16px/1.65 var(--sans);
  -webkit-font-smoothing: antialiased;
}

/* ---------- masthead ---------- */

.masthead {
  border-bottom: 1px solid var(--bg1);
  background:
    radial-gradient(1100px 380px at 12% -10%, rgba(254, 128, 25, 0.10), transparent 60%),
    radial-gradient(900px 320px at 88% -20%, rgba(131, 165, 152, 0.10), transparent 60%),
    var(--bg0);
}

.masthead-inner {
  max-width: 1080px;
  margin: 0 auto;
  padding: 1rem 1.25rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  align-items: baseline;
  justify-content: space-between;
}

.brand {
  font-family: var(--mono);
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--fg0);
  text-decoration: none;
  letter-spacing: -0.01em;
}
.brand::before { content: "▍"; color: var(--orange); margin-right: 0.4rem; }

nav.top { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; font-family: var(--mono); font-size: 0.85rem; }
nav.top a { color: var(--fg4); text-decoration: none; padding: 0.15rem 0; border-bottom: 2px solid transparent; }
nav.top a:hover { color: var(--fg1); border-bottom-color: var(--bg3); }
nav.top a.current { color: var(--yellow); border-bottom-color: var(--yellow); }

/* Theme toggle. Both glyphs are in the DOM and CSS shows the one for the theme you would
   get by pressing it — the affordance is "what this does", not "where you are", which the
   page already tells you by being the colour it is.

   Hidden entirely when JavaScript is off (see .no-js in the head script): a control that
   cannot do anything is worse than no control. */
.theme-toggle {
  appearance: none;
  background: transparent;
  border: 1px solid var(--bg2);
  border-radius: 5px;
  color: var(--fg4);
  cursor: pointer;
  font: inherit;
  line-height: 1;
  padding: 0.3rem 0.45rem;
  display: inline-flex;
  align-items: center;
}
.theme-toggle:hover { color: var(--fg1); border-color: var(--bg3); }
.theme-toggle:focus-visible { outline: 2px solid var(--orange); outline-offset: 2px; }
.theme-toggle svg { width: 16px; height: 16px; display: block; fill: currentColor; }
/* Dark now, so offer the sun. Light now, so offer the moon. */
.theme-toggle .to-light { display: block; }
.theme-toggle .to-dark { display: none; }
:root[data-theme="light"] .theme-toggle .to-light { display: none; }
:root[data-theme="light"] .theme-toggle .to-dark { display: block; }
.no-js .theme-toggle { display: none; }

/* ---------- hero ---------- */

.hero { max-width: 1080px; margin: 0 auto; padding: 3.5rem 1.25rem 1rem; }
.hero h1 {
  font-size: clamp(2rem, 5vw, 3.1rem);
  line-height: 1.1;
  letter-spacing: -0.025em;
  margin: 0 0 0.75rem;
  color: var(--fg0);
}
.hero p.lede { font-size: 1.15rem; color: var(--fg3); max-width: var(--measure); margin: 0 0 1.5rem; }

.cta { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-bottom: 2rem; }
.btn {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.9rem;
  text-decoration: none;
  padding: 0.55rem 1.1rem;
  border-radius: 4px;
  border: 1px solid var(--bg3);
  color: var(--fg1);
  background: var(--bg1);
}
.btn:hover { background: var(--bg2); border-color: var(--bg4); }
.btn-primary { background: var(--orange); border-color: var(--orange); color: var(--bg0-hard); font-weight: 600; }
.btn-primary:hover { background: var(--yellow); border-color: var(--yellow); }

/* ---------- score strip ---------- */

.scores {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1px;
  background: var(--bg1);
  border: 1px solid var(--bg1);
  border-radius: 6px;
  overflow: hidden;
  margin: 0 0 2rem;
}
.score { background: var(--bg0); padding: 1rem 1.15rem; }
.score .n { font-family: var(--mono); font-size: 1.6rem; font-weight: 700; color: var(--green); display: block; line-height: 1.2; }
.score .k { font-size: 0.85rem; color: var(--fg4); }
.score .note { font-size: 0.78rem; color: var(--gray); display: block; margin-top: 0.2rem; }

/* ---------- content ---------- */

main { max-width: 1080px; margin: 0 auto; padding: 0 1.25rem 5rem; }
.prose { max-width: var(--measure); }

h2 { font-size: 1.6rem; letter-spacing: -0.015em; color: var(--fg0); margin: 2.75rem 0 0.75rem; }
h3 { font-size: 1.2rem; color: var(--yellow); margin: 2rem 0 0.5rem; }
h4 { font-size: 1rem; color: var(--fg2); margin: 1.5rem 0 0.4rem; }

h2 + p, h3 + p { margin-top: 0; }

a { color: var(--blue); text-decoration-color: var(--bg3); text-underline-offset: 3px; }
a:hover { color: var(--aqua); text-decoration-color: currentColor; }

strong { color: var(--fg0); }
em { color: var(--fg2); }

hr { border: 0; border-top: 1px solid var(--bg1); margin: 2.5rem 0; }

blockquote {
  margin: 1.6rem 0;
  padding: 0.9rem 1.1rem;
  background: var(--bg0);
  border: 1px solid var(--bg2);
  border-radius: 5px;
  color: var(--fg3);
}
blockquote p:first-child { margin-top: 0; }
blockquote p:last-child { margin-bottom: 0; }
/* The accent lives in the words, not in a slab down the side. Nearly every callout on
   this site opens `**Like this.**`, so the lead takes the colour the border used to —
   which puts the emphasis on what the callout says rather than on its packaging.
   Scoped to the opening strong so bold used mid-paragraph stays ordinary bold. */
blockquote > p:first-child > strong:first-child { color: var(--orange); }

code {
  font-family: var(--mono);
  font-size: 0.88em;
  background: var(--bg1);
  color: var(--aqua);
  padding: 0.12em 0.38em;
  border-radius: 3px;
}

/* The quietest of the three boxes, deliberately: monospace on its own ground already reads
   as code, so the border only has to contain it. Callouts get the lighter --bg2 edge and
   sit slightly forward of this. */
pre {
  background: var(--bg0);
  border: 1px solid var(--bg1);
  border-radius: 5px;
  padding: 1rem 1.1rem;
  overflow-x: auto;
  font-size: 0.87rem;
  line-height: 1.55;
}
pre code { background: none; color: var(--fg2); padding: 0; font-size: inherit; }

/* A rendered diagram is a picture, not a listing: drop the code-block chrome and centre it.
   Only once the <svg> has actually landed, though. Until then `pre.mermaid` still holds its
   own source, and that source stays styled as the code block it is and stays *visible* — if
   the CDN is blocked or scripts are off, a reader gets the diagram as legible text instead
   of an empty gap. Hiding it until render would look tidier and fail silently, which is the
   trade this repository argues against everywhere else. */
pre.mermaid:has(svg) {
  background: none;
  border: none;
  padding: 0.5rem 0;
  margin: 1.6rem 0;
  text-align: center;
  line-height: normal;
}
pre.mermaid svg { max-width: 100%; height: auto; }

/* The infographic is a cream whiteboard, so it arrives as a bright block on a dark page.
   A neutral surround and a rounded clip stop it reading as a hole punched in the layout.
   width/height are on the <img> so the browser reserves the space before it loads and the
   page does not jump. */
figure.infographic { margin: 1.5rem 0 2rem; }
figure.infographic img {
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--bg2);
  border-radius: 6px;
  background: var(--bg1);
}
figure.infographic figcaption {
  margin-top: 0.7rem;
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--fg4);
}
figure.infographic figcaption strong { color: var(--fg3); font-weight: 600; }

/* The native <audio> control, given room to breathe. No player library: the browser's own
   is accessible and keyboard-navigable, and adds no third-party script to a site whose
   playbook spends a section on supply chain. */
.audio-card {
  margin: 1.75rem 0;
  padding: 1.1rem 1.2rem;
  background: var(--bg0);
  border: 1px solid var(--bg2);
  border-radius: 6px;
}
.audio-card audio { width: 100%; display: block; }
.audio-meta {
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  color: var(--fg4);
}
.audio-meta strong { color: var(--fg2); font-weight: 600; }

/* tables scroll inside themselves; the page never scrolls sideways */
table {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  font-size: 0.92rem;
  margin: 1.5rem 0;
}
th, td { text-align: left; padding: 0.55rem 0.85rem; border-bottom: 1px solid var(--bg1); vertical-align: top; }
th { color: var(--yellow); font-weight: 600; border-bottom-color: var(--bg2); white-space: nowrap; }
tbody tr:hover { background: var(--bg0); }

ul, ol { padding-left: 1.35rem; }
li { margin: 0.3rem 0; }
li::marker { color: var(--bg4); }

img { max-width: 100%; height: auto; }

/* ---------- cards ---------- */

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); gap: 1rem; margin: 1.5rem 0; }
.card {
  background: var(--bg0);
  border: 1px solid var(--bg1);
  border-radius: 6px;
  padding: 1.1rem 1.2rem;
}
.card:hover { border-color: var(--bg3); }
.card h3 { margin: 0 0 0.4rem; font-size: 1rem; color: var(--fg0); }
.card p { margin: 0; font-size: 0.9rem; color: var(--fg3); }
.card .tag { font-family: var(--mono); font-size: 0.72rem; color: var(--orange); text-transform: uppercase; letter-spacing: 0.06em; }

/* ---------- stage rail ---------- */

.stage { border-left: 2px solid var(--bg2); padding: 0 0 1.25rem 1.25rem; margin-left: 0.5rem; position: relative; }
.stage::before {
  content: "";
  position: absolute; left: -6px; top: 0.55rem;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--orange); box-shadow: 0 0 0 3px var(--bg0-hard);
}
.stage h3 { margin-top: 0; }
.stage:last-child { border-left-color: transparent; }

/* ---------- footer ---------- */

footer.page {
  border-top: 1px solid var(--bg1);
  color: var(--gray);
  font-size: 0.85rem;
}
footer.page .inner { max-width: 1080px; margin: 0 auto; padding: 1.75rem 1.25rem 3rem; }
footer.page a { color: var(--fg4); }

/* Same shape as a blockquote — it is one, semantically — set a size smaller, with the
   caution carried by the yellow lead rather than by a stripe. */
.disclaimer {
  border: 1px solid var(--bg2);
  border-radius: 5px;
  background: var(--bg0);
  padding: 0.9rem 1.1rem;
  font-size: 0.9rem;
  color: var(--fg3);
  margin: 2rem 0;
}
.disclaimer strong { color: var(--yellow); }

/* ---------- rouge, gruvbox dark ---------- */

.highlight .c, .highlight .cm, .highlight .c1, .highlight .cs { color: var(--gray); font-style: italic; }
.highlight .k, .highlight .kd, .highlight .kn, .highlight .kp, .highlight .kr, .highlight .kt { color: var(--red); }
.highlight .s, .highlight .s1, .highlight .s2, .highlight .sb, .highlight .sd, .highlight .se, .highlight .sh, .highlight .sx { color: var(--green); }
.highlight .m, .highlight .mi, .highlight .mf, .highlight .mh, .highlight .il { color: var(--purple); }
.highlight .nf, .highlight .nx { color: var(--fg1); }
.highlight .nc, .highlight .nn { color: var(--yellow); }
.highlight .nt { color: var(--red); }
.highlight .na { color: var(--aqua); }
.highlight .nb, .highlight .bp { color: var(--orange); }
.highlight .o, .highlight .ow { color: var(--fg2); }
.highlight .gd { color: var(--red); }
.highlight .gi { color: var(--green); }
.highlight .err { color: var(--red); background: none; }

@media (max-width: 620px) {
  .masthead-inner { flex-direction: column; align-items: flex-start; }
  .hero { padding-top: 2.25rem; }
}

/* ---------- asciicast player ---------- */

.cast {
  margin: 1.5rem 0;
  border: 1px solid var(--bg1);
  border-radius: 6px;
  overflow: hidden;
  background: var(--bg0-hard);
  min-height: 120px;
}
.cast-note { font-size: 0.9rem; color: var(--fg4); margin-top: -0.5rem; }

/* The player ships its own palettes; this is the Gruvbox dark one the site uses. */
.asciinema-theme-gruvbox-dark {
  --term-color-foreground: #ebdbb2;
  --term-color-background: #1d2021;
  --term-color-0: #282828;  --term-color-1: #cc241d;
  --term-color-2: #98971a;  --term-color-3: #d79921;
  --term-color-4: #458588;  --term-color-5: #b16286;
  --term-color-6: #689d6a;  --term-color-7: #a89984;
  --term-color-8: #928374;  --term-color-9: #fb4934;
  --term-color-10: #b8bb26; --term-color-11: #fabd2f;
  --term-color-12: #83a598; --term-color-13: #d3869b;
  --term-color-14: #8ec07c; --term-color-15: #ebdbb2;
}
