/* ============================================================
   migatchev.co.za – main stylesheet
   Palette: midnight indigo / royal blue / violet / ember mustard
   Type: Fraunces (display) · IBM Plex Sans (body) · IBM Plex Mono (utility)
   ============================================================ */

/* ---------- Tokens ---------- */
:root {
  /* Dark theme (default) */
  --bg: #0c1026;
  --bg-deep: #090c1d;
  --bg-raised: #141938;
  --bg-overlay: rgba(9, 12, 29, 0.86);
  --ink: #e9e7f4;
  --ink-dim: #a8a6c6;
  --ink-faint: #6e6d92;
  --royal: #5b6cff;
  --violet: #8a6bf2;
  --ember: #ffb52e;
  --ember-ink: #ffb52e;          /* accent used as text */
  --ember-soft: rgba(255, 181, 46, 0.12);
  --line: rgba(168, 166, 198, 0.16);
  --line-strong: rgba(168, 166, 198, 0.32);
  --card-shadow: 0 16px 40px rgba(4, 6, 18, 0.45);
  --glow: 0 0 22px rgba(255, 181, 46, 0.35);
  --term-bg: #07091a;
  --term-ink: #d8d6ee;
  --ok: #4cd58a;
  --warn: #ff7a6b;

  --font-display: "Fraunces", Georgia, serif;
  --font-body: "IBM Plex Sans", system-ui, -apple-system, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "Courier New", monospace;

  --maxw: 1080px;
  --radius: 14px;
  --ease: cubic-bezier(0.22, 0.7, 0.3, 1);

  color-scheme: dark;
}

[data-theme="light"] {
  --bg: #f3f2f9;
  --bg-deep: #e9e8f3;
  --bg-raised: #ffffff;
  --bg-overlay: rgba(243, 242, 249, 0.88);
  --ink: #1d2152;
  --ink-dim: #565a85;
  --ink-faint: #8b8eae;
  --royal: #3a48cf;
  --violet: #6a4ed8;
  --ember: #ffb52e;
  --ember-ink: #a86b00;          /* mustard dark enough to read on paper */
  --ember-soft: rgba(255, 181, 46, 0.18);
  --line: rgba(46, 50, 100, 0.14);
  --line-strong: rgba(46, 50, 100, 0.3);
  --card-shadow: 0 14px 34px rgba(38, 42, 96, 0.12);
  --glow: 0 0 18px rgba(255, 181, 46, 0.5);
  --term-bg: #14173a;
  --term-ink: #dddbf0;
  --ok: #128a52;
  --warn: #c2402f;

  color-scheme: light;
}

/* ---------- Base ---------- */
* { margin: 0; padding: 0; box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.65;
  background: var(--bg);
  color: var(--ink);
  overflow-x: hidden;
  transition: background 0.35s var(--ease), color 0.35s var(--ease);
}

::selection { background: var(--ember); color: #1d1303; }

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 560;
  line-height: 1.12;
  letter-spacing: -0.01em;
}

a { color: var(--royal); text-decoration: none; }
a:hover { color: var(--ember-ink); }

img { max-width: 100%; display: block; }

:focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 3px;
  border-radius: 3px;
}

.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }

/* Mono eyebrow – section paths, the site's structural signature */
.path {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  color: var(--ember-ink);
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  margin-bottom: 0.9rem;
}
.path::before { content: "~/"; color: var(--ink-faint); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}

/* ---------- Navigation ---------- */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  backdrop-filter: blur(12px);
  background: var(--bg-overlay);
  border-bottom: 1px solid var(--line);
  transition: background 0.35s var(--ease);
}
.nav-inner {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 24px;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.wordmark {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 1.25rem;
  letter-spacing: -0.02em;
  color: var(--ink);
  display: inline-flex;
  align-items: baseline;
  gap: 2px;
}
.wordmark:hover { color: var(--ink); }
.caret {
  display: inline-block;
  width: 0.55em; height: 1.05em;
  background: var(--ember);
  transform: translateY(0.16em);
  animation: blink 1.15s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }

.nav-links { display: flex; align-items: center; gap: 26px; list-style: none; }
.nav-links a {
  color: var(--ink-dim);
  font-size: 0.95rem;
  position: relative;
}
.nav-links a:hover, .nav-links a[aria-current="page"] { color: var(--ink); }
.nav-links a[aria-current="page"]::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: -6px;
  height: 2px; background: var(--ember); border-radius: 2px;
}

.nav-tools { display: flex; align-items: center; gap: 8px; }
.icon-btn {
  appearance: none;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ink-dim);
  border-radius: 9px;
  width: 38px; height: 38px;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 0.95rem;
  transition: color 0.2s, border-color 0.2s, box-shadow 0.2s;
}
.icon-btn:hover { color: var(--ember-ink); border-color: var(--line-strong); }
.icon-btn svg { width: 17px; height: 17px; }

.menu-btn { display: none; }

@media (max-width: 720px) {
  .nav-links {
    position: fixed; top: 64px; right: 0; left: 0;
    background: var(--bg-overlay);
    backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--line);
    flex-direction: column; align-items: flex-start;
    padding: 18px 24px 22px; gap: 16px;
    display: none;
  }
  .nav-links.open { display: flex; }
  .menu-btn { display: inline-flex; }
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: center;
  overflow: hidden;
  background:
    radial-gradient(1100px 600px at 78% -10%, rgba(138, 107, 242, 0.16), transparent 60%),
    radial-gradient(900px 540px at 8% 110%, rgba(91, 108, 255, 0.14), transparent 60%),
    var(--bg);
}
#hero-field {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
}
.hero-inner { position: relative; z-index: 2; padding-top: 96px; padding-bottom: 64px; }
.hero h1 {
  font-size: clamp(2.9rem, 7.5vw, 5.4rem);
  font-weight: 500;
  max-width: 14ch;
}
.hero h1 em {
  font-style: italic;
  color: var(--ember-ink);
}
.hero-lede {
  margin-top: 1.4rem;
  font-size: clamp(1.05rem, 2vw, 1.25rem);
  color: var(--ink-dim);
  max-width: 56ch;
}
.hero-lede strong { color: var(--ink); font-weight: 600; }
.hero-actions { margin-top: 2.2rem; display: flex; flex-wrap: wrap; gap: 14px; align-items: center; }

.btn {
  display: inline-flex; align-items: center; gap: 0.55em;
  font-family: var(--font-mono);
  font-size: 0.92rem;
  padding: 0.78em 1.35em;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  color: var(--ink);
  background: transparent;
  cursor: pointer;
  transition: border-color 0.2s, box-shadow 0.25s, transform 0.2s, background 0.2s;
}
.btn:hover { border-color: var(--ember); box-shadow: var(--glow); color: var(--ink); transform: translateY(-1px); }
.btn-solid {
  background: var(--ember);
  border-color: var(--ember);
  color: #221502;
  font-weight: 600;
}
.btn-solid:hover { color: #221502; }

.hero-hint {
  margin-top: 3rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--ink-dim);
}
.hero-hint kbd {
  font-family: inherit;
  border: 1px solid var(--line-strong);
  border-bottom-width: 2px;
  border-radius: 5px;
  padding: 0.1em 0.45em;
  color: var(--ink-dim);
}

/* ---------- Sections ---------- */
.section { padding: 7rem 0; position: relative; }
.section-tight { padding: 5rem 0; }
.section h2 {
  font-size: clamp(1.9rem, 4vw, 2.7rem);
  margin-bottom: 1.2rem;
}
.section-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px; margin-bottom: 2.6rem; flex-wrap: wrap;
}
.section-head h2 { margin-bottom: 0; }
.more-link {
  font-family: var(--font-mono); font-size: 0.88rem;
  color: var(--ink-dim); white-space: nowrap;
}
.more-link:hover { color: var(--ember-ink); }
.more-link::after { content: " →"; }

.divider { height: 1px; background: var(--line); max-width: var(--maxw); margin: 0 auto; }

/* Reveal on scroll */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease); }
.reveal.in { opacity: 1; transform: none; }
.reveal-d1 { transition-delay: 0.12s; }
.reveal-d2 { transition-delay: 0.24s; }

/* ---------- About ---------- */
.about-grid {
  display: grid;
  grid-template-columns: 1.25fr 0.75fr;
  gap: 56px;
  align-items: start;
}
.about-prose p { color: var(--ink-dim); margin-bottom: 1.1rem; }
.about-prose p strong { color: var(--ink); font-weight: 600; }
.about-aside {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
  padding: 1.6rem 1.7rem;
  box-shadow: var(--card-shadow);
}
.about-aside h3 {
  font-size: 1.05rem;
  font-family: var(--font-mono);
  font-weight: 500;
  color: var(--ember-ink);
  margin-bottom: 1rem;
  letter-spacing: 0.04em;
}
.about-aside ul { list-style: none; }
.about-aside li {
  padding: 0.55rem 0;
  border-bottom: 1px dashed var(--line);
  color: var(--ink-dim);
  font-size: 0.95rem;
  display: flex; gap: 0.7em;
}
.about-aside li::before { content: "▸"; color: var(--violet); }
@media (max-width: 820px) { .about-grid { grid-template-columns: 1fr; gap: 36px; } }

/* ---------- Loop (interactive diagram) ---------- */
.loop-wrap {
  display: grid;
  grid-template-columns: 1.2fr 0.8fr;
  gap: 44px;
  align-items: center;
}
@media (max-width: 880px) { .loop-wrap { grid-template-columns: 1fr; } }

.loop-svg-box {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background:
    radial-gradient(600px 300px at 50% 0%, rgba(91, 108, 255, 0.08), transparent 70%),
    var(--bg-raised);
  padding: 12px;
  box-shadow: var(--card-shadow);
}
.loop-svg { width: 100%; height: auto; display: block; }

.loop-node { cursor: pointer; }
.loop-node circle {
  fill: var(--bg);
  stroke: var(--royal);
  stroke-width: 2;
  transition: stroke 0.25s, fill 0.25s, opacity 0.25s;
}
.loop-node text {
  fill: var(--ink);
  font-family: var(--font-mono);
  font-size: 13px;
  text-anchor: middle;
  pointer-events: none;
  transition: fill 0.25s, opacity 0.25s;
}
.loop-node:hover circle { stroke: var(--ember); }
.loop-node.off circle { stroke: var(--ink-faint); stroke-dasharray: 4 5; fill: transparent; opacity: 0.55; }
.loop-node.off text { fill: var(--ink-faint); opacity: 0.6; text-decoration: line-through; }

.loop-edge {
  stroke: var(--violet);
  stroke-width: 1.8;
  fill: none;
  marker-end: url(#loop-arrow);
  transition: opacity 0.3s, stroke 0.3s;
}
.loop-edge.off { opacity: 0.12; stroke: var(--ink-faint); }

.loop-readout {
  border-left: 3px solid var(--ember);
  padding: 0.2rem 0 0.2rem 1.4rem;
  min-height: 9.5rem;
}
.loop-readout .verdict {
  font-family: var(--font-display);
  font-style: italic;
  font-size: clamp(1.25rem, 2.4vw, 1.6rem);
  margin-bottom: 0.7rem;
}
.loop-readout .detail { color: var(--ink-dim); }
.loop-help {
  margin-top: 1.4rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--ink-faint);
}

/* ---------- Cards (writing & finds) ---------- */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  gap: 22px;
}
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
  padding: 1.6rem 1.6rem 1.4rem;
  color: inherit;
  transition: transform 0.3s var(--ease), border-color 0.3s, box-shadow 0.3s;
}
a.card:hover {
  transform: translateY(-5px);
  border-color: var(--line-strong);
  box-shadow: var(--card-shadow), 0 0 0 1px var(--ember-soft);
  color: inherit;
}
.card-meta {
  font-family: var(--font-mono);
  font-size: 0.76rem;
  color: var(--ink-faint);
  display: flex;
  justify-content: space-between;
  gap: 1em;
  letter-spacing: 0.04em;
}
.card-meta .card-read { text-align: right; }
.card h3 { font-size: 1.28rem; font-weight: 540; }
a.card:hover h3 { color: var(--ember-ink); }
.card p { color: var(--ink-dim); font-size: 0.96rem; flex-grow: 1; margin: 0; }
.card-tags { display: flex; gap: 8px; flex-wrap: wrap; }
.tag {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--violet);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.15em 0.8em;
}
.card .ext {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--royal);
}
a.card:hover .ext { color: var(--ember-ink); }

/* ---------- Writing cards: fixed section heights + hover-to-expand ----------
   Scoped to .card-post (rendered by postCard) so the same look applies wherever
   writing cards appear – the writing page and the home page's latest strip.
   Each section (heading, summary, tags, series) is given a fixed footprint so
   card heights don't drift with content length. On hover the summary clamp
   lifts and the card grows downward to reveal the full description. */
.card-grid--posts { align-items: start; }
a.card-post:hover { z-index: 2; }

/* Taller cards: extra padding + an overall min-height (was ~15.7rem before). */
.card-post {
  padding: 1.9rem 1.8rem 1.7rem;
  min-height: 24rem;
}

/* Title: always reserves two lines; clamps with ellipsis if longer. */
.card-post h3 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.25;
  min-height: 3.2rem;       /* 2 * (1.28rem * 1.25) – reserves both lines */
}

/* Summary: clamped to a fixed 6-line block. The --short modifier drops the
   clamp to 5 lines so a series card can use the 6th body line for the series
   name, keeping every card the same height.

   line-clamp itself isn't transitionable, so max-height mirrors the clamp
   line count and the hover reveal animates max-height instead. Crucially,
   overflow stays hidden on hover – the text is clipped to the box and only
   becomes visible as max-height (and therefore the card) grows downward.
   No delay: the growth begins on hover and eases out over ~280ms. */
.card-post .card-summary {
  flex-grow: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
  -webkit-line-clamp: 6;
  line-height: 1.55;
  max-height: 9.3rem;       /* ≈ 6 lines @ 0.96rem * 1.55 */
  transition: max-height 0.28s var(--ease);
}
.card-post .card-summary--short {
  -webkit-line-clamp: 5;
  max-height: 7.8rem;       /* ≈ 5 lines, leaves the 6th for the series line */
}
a.card-post:hover .card-summary {
  -webkit-line-clamp: initial;
  max-height: 60rem;        /* generous ceiling; the description eases up to it */
}

/* Series line – directly under the summary (the 6th body line for series
   cards). One line, ellipsised. */
.card-post .card-series {
  flex-grow: 0;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  line-height: 1.55;
  color: var(--ink-faint);
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.card-post .card-series code {
  font-family: var(--font-mono);
  color: var(--violet);
}

/* Tags: pinned to the card foot, kept to a single symmetrical row.
   Smaller font + nowrap so three tags fit on one line; each tag can ellipsis
   if its name is unusually long. */
.card-post .card-tags {
  margin-top: auto;
  min-height: 1.6em;
  flex-wrap: nowrap;
  overflow: hidden;
}
.card-post .card-tags .tag {
  font-size: 0.66rem;
  flex-shrink: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-top: 0.35em;
}

/* ---------- Finds cards: mirror the writing card treatment ----------
   Same fixed-height sections and hover-to-expand behaviour as writing cards,
   with two differences from the brief:
   - the title clamps to 3 lines (not 2) and also expands on hover;
   - the source in the meta row is ellipsised to a single line and never
     expands, even on hover. */
.card-grid--finds { align-items: start; }
a.card-find:hover { z-index: 2; }

.card-find {
  padding: 1.9rem 1.8rem 1.7rem;
  min-height: 24rem;
}

/* Meta: date pinned left, source pinned right. The source shrinks and
   ellipsises; the date is fixed so it never gets squeezed. */
.card-find .card-meta .card-date { flex-shrink: 0; }
.card-find .card-meta .card-source {
  flex-shrink: 1;
  min-width: 0;
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Title: 3 lines max with ellipsis, expanding to full on hover. min-height
   reserves all 3 lines so a 1-line title still keeps its slot. */
.card-find h3 {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.25;
  min-height: 4.8rem;        /* 3 * (1.28rem * 1.25) */
  max-height: 4.8rem;
  transition: max-height 0.28s var(--ease);
}
a.card-find:hover h3 {
  -webkit-line-clamp: initial;
  max-height: 30rem;
}

/* Description: 6-line clamp, expands on hover (mirrors writing cards). */
.card-find .card-summary {
  flex-grow: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
  -webkit-line-clamp: 6;
  line-height: 1.55;
  max-height: 9.3rem;
  transition: max-height 0.28s var(--ease);
}
a.card-find:hover .card-summary {
  -webkit-line-clamp: initial;
  max-height: 60rem;
}

/* Tags: single symmetrical row (same treatment as writing cards). */
.card-find .card-tags {
  flex-wrap: nowrap;
  overflow: hidden;
  min-height: 1.6em;
}
.card-find .card-tags .tag {
  font-size: 0.66rem;
  flex-shrink: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-top: 0.35em;
}

/* "visit ↗" pinned to the card foot – the call-to-action sits at the bottom
   regardless of whether the card has tags. */
.card-find .ext { margin-top: auto; }

.empty-note, .error-note {
  font-family: var(--font-mono);
  color: var(--ink-faint);
  font-size: 0.9rem;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
  padding: 1.5rem;
}

/* ---------- Page headers (inner pages) ---------- */
.page-head { padding: 10rem 0 3.5rem; }
.page-head h1 { font-size: clamp(2.4rem, 5.5vw, 3.6rem); }
.page-head .lede { margin-top: 1rem; color: var(--ink-dim); max-width: 60ch; font-size: 1.1rem; }

/* ---------- Article ---------- */
.article-body {
  max-width: var(--maxw);
  margin: 0 auto;
  padding-bottom: 6rem;
}
.article-meta {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--ink-faint);
  margin-bottom: 2.5rem;
  display: flex; align-items: center; gap: 1.4em; flex-wrap: wrap;
}

/* Read-aloud (Listen) button – sits in the article byline */
.listen-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  line-height: 1;
  color: var(--ember-ink);
  background: var(--ember-soft);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 0.4em 0.9em;
  cursor: pointer;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.listen-btn:hover { border-color: var(--ember); }
.listen-btn:focus-visible { outline: 2px solid var(--ember); outline-offset: 2px; }
.listen-btn[data-state="playing"] { border-color: var(--ember); box-shadow: var(--glow); }

.listen-icon {
  position: relative;
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
}
/* Play triangle (idle / paused) */
.listen-btn:not([data-state="playing"]) .listen-icon::before {
  content: "";
  position: absolute;
  top: 50%; left: 55%;
  transform: translate(-50%, -50%);
  border-style: solid;
  border-width: 0.34em 0 0.34em 0.55em;
  border-color: transparent transparent transparent currentColor;
}
/* Pause bars (playing) */
.listen-btn[data-state="playing"] .listen-icon::before,
.listen-btn[data-state="playing"] .listen-icon::after {
  content: "";
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 0.17em;
  height: 0.7em;
  background: currentColor;
}
.listen-btn[data-state="playing"] .listen-icon::before { left: 0.16em; }
.listen-btn[data-state="playing"] .listen-icon::after  { right: 0.16em; }

/* Live read-aloud highlight – the sentence currently being spoken.
   Painted via the CSS Custom Highlight API (no DOM changes). */
::highlight(tts-active) {
  background-color: rgba(255, 181, 46, 0.28);
  color: inherit;
}
[data-theme="light"] ::highlight(tts-active) {
  background-color: rgba(255, 181, 46, 0.4);
}
.prose { font-size: 1.06rem; }
.prose h2, .prose h3 { margin: 2.4rem 0 1rem; }
.prose h2 { font-size: 1.7rem; }
.prose h3 { font-size: 1.3rem; }
.prose p { margin-bottom: 1.25rem; color: var(--ink-dim); }
.prose p:first-of-type { color: var(--ink); font-size: 1.14rem; }
.prose strong { color: var(--ink); }
.prose ul, .prose ol { margin: 0 0 1.25rem 1.4rem; color: var(--ink-dim); }
.prose li { margin-bottom: 0.45rem; }
.prose blockquote {
  border-left: 3px solid var(--ember);
  padding: 0.3rem 0 0.3rem 1.3rem;
  margin: 1.8rem 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.2rem;
  color: var(--ink);
}
.prose code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  background: var(--ember-soft);
  color: var(--ember-ink);
  border-radius: 5px;
  padding: 0.12em 0.42em;
}
.prose pre {
  background: var(--term-bg);
  color: var(--term-ink);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 1.1rem 1.3rem;
  overflow-x: auto;
  margin: 1.6rem 0;
  font-size: 0.88rem;
  line-height: 1.55;
}
.prose pre code { background: none; color: inherit; padding: 0; }
.prose a { border-bottom: 1px solid var(--line-strong); }
.prose hr { border: 0; border-top: 1px solid var(--line); margin: 2.5rem 0; }

/* ---------- Prose tables ----------
   The Build overviews lean on tables heavily, and marked emits a bare <table>.
   Full width, roomy cells, headers left-aligned to match their columns and
   lifted onto the raised surface so the header row reads as a header. */
.prose table {
  width: 100%;
  border-collapse: collapse;
  margin: 1.8rem 0;
  font-size: 0.95rem;
}
.prose th,
.prose td {
  text-align: left;
  vertical-align: top;
  padding: 0.8rem 1.1rem;
  border-bottom: 1px solid var(--line);
}
.prose thead th {
  background: var(--bg-raised);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.76rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--line-strong);
}
.prose tbody td { color: var(--ink-dim); }
.prose tbody tr:last-child td { border-bottom: 0; }

/* A date broken across two lines inside a narrow column reads as two numbers
   rather than one date. content.js wraps every ISO date it finds in a table
   cell in this class; the cell itself stays wrappable, so a cell carrying a
   date and a sentence still behaves. Mono, because a date is metadata and the
   rest of the site already says so that way. */
.prose td .nowrap,
.prose th .nowrap {
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: 0.92em;
}

/* Narrow screens: let a wide table scroll inside itself rather than forcing
   the page to scroll sideways. */
@media (max-width: 620px) {
  .prose table { display: block; overflow-x: auto; }
}
.back-link { font-family: var(--font-mono); font-size: 0.88rem; }

/* ---------- Prose media (screenshots in build entries) ----------
   Nothing styled markdown images before the Build section existed, so they
   rendered raw and full-bleed. Figures carry their own caption; a bare image
   without a figure still gets the frame. */
.prose img {
  display: block;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-raised);
  margin: 1.8rem 0;
}
.prose figure { margin: 2.2rem 0; }
.prose figure img { margin: 0; }
.prose figure .diagram { margin: 0; }
.prose figcaption {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  line-height: 1.55;
  color: var(--ink-faint);
  margin-top: 0.7rem;
}

/* Two screenshots side by side (the light/dark pair of the same screen).
   Collapses to a single column on narrow viewports. */
.prose .figure-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  margin: 2.2rem 0;
}
.prose .figure-pair figure { margin: 0; }
@media (max-width: 720px) { .prose .figure-pair { grid-template-columns: 1fr; } }

/* ---------- Prose diagrams ----------
   Hand-authored inline SVG carrying class attributes only – never inline style
   or literal colours. Everything below drives off the theme tokens, so one SVG
   serves both themes and there is no light/dark pair to drift apart. Class
   survives DOMPurify; style is a bet not worth making. */
.prose .diagram {
  display: block;
  width: 100%;
  height: auto;
  margin: 2.2rem 0 0;
  overflow: visible;
}
.diagram .d-box {
  fill: var(--bg-raised);
  stroke: var(--line-strong);
  stroke-width: 1.2;
}
.diagram .d-box-accent {
  fill: var(--ember-soft);
  stroke: var(--ember);
  stroke-width: 1.4;
}
.diagram .d-edge {
  fill: none;
  stroke: var(--violet);
  stroke-width: 1.4;
}
.diagram .d-edge-dim {
  fill: none;
  stroke: var(--line-strong);
  stroke-width: 1.2;
  stroke-dasharray: 4 4;
}
.diagram .d-arrow { fill: var(--violet); }
.diagram .d-label {
  fill: var(--ink);
  font-family: var(--font-mono);
  font-size: 13px;
}
.diagram .d-label-strong {
  fill: var(--ember-ink);
  font-family: var(--font-mono);
  font-size: 13px;
}
.diagram .d-label-dim {
  fill: var(--ink-faint);
  font-family: var(--font-mono);
  font-size: 11px;
}

/* ---------- Build ----------
   The shelf and a young project's page are both short by nature – a couple of
   cards, or an overview and one log entry. Without a floor, the footer creeps
   into view at the top of a tall screen. Holding main to a full viewport keeps
   the footer where a footer belongs on every screen size. */
body.page-build #main { min-height: 100vh; }

/* ---------- Build: project cards ---------- */
.card-grid--projects { align-items: start; }
a.card-project:hover { z-index: 2; }

.card-project {
  padding: 1.9rem 1.8rem 1.7rem;
  min-height: 22rem;
}

/* ---------- Status LED ----------
   A lit indicator to the left of the project name, sized in em so it scales
   with whatever it sits in – a card heading or the project page h1.
   amber = active · green = shipped · red = stopped · unlit = planned */
.led {
  display: inline-block;
  width: 0.6em;
  height: 0.6em;
  /* The left margin is not decoration: the heading clips its overflow, so
     without it the glow is sheared off flat against the edge. It has to clear
     the widest the glow ever gets, which is the pulse peak below (0.45em), so
     every value here and in the keyframes is held under this margin. */
  margin-left: 0.5em;
  margin-right: 0.55em;
  border-radius: 50%;
  vertical-align: middle;
  flex-shrink: 0;
  /* The highlight is what makes it read as a lens rather than a dot. */
  background:
    radial-gradient(circle at 34% 30%, rgba(255, 255, 255, 0.5), transparent 55%),
    var(--led);
  box-shadow: 0 0 0.3em var(--led);
}
.led[data-status="active"]  { --led: var(--ember); }
.led[data-status="shipped"] { --led: var(--ok); }
.led[data-status="stopped"] { --led: var(--warn); }
/* Planned reads as an LED that hasn't been switched on: no light, just the lens. */
.led[data-status="planned"] {
  --led: transparent;
  box-shadow: inset 0 0 0 1px var(--line-strong);
}

/* Active projects breathe, the way a working status light does. */
@media (prefers-reduced-motion: no-preference) {
  .led[data-status="active"] { animation: led-pulse 2.6s var(--ease) infinite; }
}
/* Blur only, no spread, and the peak stays under the 0.5em left margin so the
   glow never reaches the clipping edge at any point in the cycle. */
@keyframes led-pulse {
  0%, 100% { box-shadow: 0 0 0.25em var(--led); }
  50%      { box-shadow: 0 0 0.45em var(--led); }
}

.card-project h3 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: 1.25;
  min-height: 3.2rem;
  margin-left: -0.5em;  /* LED margin is part of the heading, so the clamp counts it */
}
.card-project .card-summary {
  flex-grow: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
  -webkit-line-clamp: 5;
  line-clamp: 5;
  line-height: 1.55;
  max-height: 7.8rem;
  transition: max-height 0.28s var(--ease);
}
a.card-project:hover .card-summary {
  -webkit-line-clamp: initial;
  line-clamp: initial;
  max-height: 60rem;
}

/* Stack sits in the metadata register – one line, ellipsised. */
.card-project .card-stack {
  flex-grow: 0;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  line-height: 1.55;
  color: var(--ink-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.card-project .card-tags {
  flex-wrap: nowrap;
  overflow: hidden;
  min-height: 1.6em;
}
.card-project .card-tags .tag {
  font-size: 0.66rem;
  flex-shrink: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-top: 0.35em;
}
/* Entry count pinned to the card foot, mirroring "visit ↗" on finds. */
.card-project .card-count {
  margin-top: auto;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--royal);
}
a.card-project:hover .card-count { color: var(--ember-ink); }

/* ---------- Build: project page ---------- */
.project-meta {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--ink-faint);
  margin-bottom: 2.5rem;
  display: flex; align-items: center; gap: 1.2em; flex-wrap: wrap;
}
.project-repo { color: var(--royal); border-bottom: 1px solid var(--line-strong); }
.project-repo:hover { color: var(--ember-ink); }
.project-repo.is-pending {
  color: var(--ink-faint);
  border-bottom: 1px dashed var(--line);
}

/* ---------- Build: the log ----------
   One card per entry, full width and stacked, because a log is read top to
   bottom. Everything else – surface, border, radius, hover lift, title colour –
   comes from the shared .card rules, which is why these rows carry that class.
   Only the internals are set here. */
.build-docs { margin-top: 4rem; }
.build-log { margin-top: 4rem; }
.build-log-head {
  border-top: 1px solid var(--line);
  padding-top: 2rem;
  margin-bottom: 1.6rem;
}
.build-log-head h2 { font-size: 1.5rem; }
.section-note {
  margin-top: 0.4rem;
  color: var(--ink-dim);
  font-size: 0.94rem;
  max-width: 60ch;
}

.log-list { display: flex; flex-direction: column; gap: 22px; }
.log-row, .doc-row { padding: 1.6rem 1.8rem 1.5rem; }
.log-meta {
  font-family: var(--font-mono);
  font-size: 0.76rem;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
  display: flex;
  gap: 1.4em;
  flex-wrap: wrap;
  align-items: baseline;
}
.log-row h3, .doc-row h3 { font-size: 1.28rem; font-weight: 540; line-height: 1.3; }
.log-summary { max-width: 78ch; }

/* A milestone ID is a cross-reference, not decoration: it reads as a label so
   that "M2" on an entry and "M2" in the plan document are visibly the same
   token. Ember, because it is the site's one pointing colour. */
.log-milestone {
  color: var(--ember-ink);
  font-weight: 500;
  letter-spacing: 0.06em;
}

/* The document shelf. The slot label does the work the date does on a log row:
   it tells you which of the fixed set this is before you read the title. */
.doc-label {
  color: var(--royal);
  font-weight: 500;
  letter-spacing: 0.06em;
}
.doc-state {
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: 0.1em 0.7em;
  color: var(--ink-dim);
}

/* ---------- Contact ---------- */
.contact-grid {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 52px;
  align-items: start;
  padding-bottom: 6rem;
}
@media (max-width: 820px) { .contact-grid { grid-template-columns: 1fr; } }

.field { margin-bottom: 1.3rem; }
.field label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  color: var(--ink-dim);
  margin-bottom: 0.45rem;
}
.field input, .field textarea {
  width: 100%;
  font: inherit;
  color: var(--ink);
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 0.85rem 1rem;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.field input:focus, .field textarea:focus {
  outline: none;
  border-color: var(--ember);
  box-shadow: 0 0 0 3px var(--ember-soft);
}
.form-status {
  margin-top: 1rem;
  font-family: var(--font-mono);
  font-size: 0.88rem;
  min-height: 1.4em;
}
.form-status.ok { color: var(--ok); }
.form-status.err { color: var(--warn); }
.hp { position: absolute; left: -9999px; opacity: 0; height: 0; overflow: hidden; }

.contact-ways { display: flex; flex-direction: column; gap: 14px; }
.way {
  display: flex; align-items: baseline; gap: 1em;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg-raised);
  padding: 1.05rem 1.3rem;
  color: var(--ink-dim);
  transition: border-color 0.25s, transform 0.25s;
}
.way:hover { border-color: var(--ember); transform: translateX(4px); color: var(--ink-dim); }
.way .k { font-family: var(--font-mono); font-size: 0.78rem; color: var(--ember-ink); min-width: 6.5em; }
.way .v { color: var(--ink); }

/* ---------- Footer ---------- */
.footer {
  border-top: 1px solid var(--line);
  background: var(--bg-deep);
  padding: 3.2rem 0 2.6rem;
}
.footer-inner {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 28px; flex-wrap: wrap;
}
.footer .who { max-width: 38ch; }
.footer .who .name { font-family: var(--font-display); font-style: italic; font-size: 1.2rem; }
.footer .who p { color: var(--ink-faint); font-size: 0.9rem; margin-top: 0.5rem; }
.footer-links { display: flex; gap: 22px; flex-wrap: wrap; }
.footer-links a { color: var(--ink-dim); font-size: 0.92rem; }
.footer-links a:hover { color: var(--ember-ink); }
.footer .stamp {
  width: 100%;
  margin-top: 2.2rem;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  color: var(--ink-faint);
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
}
.footer .stamp button {
  appearance: none; background: none; border: 0; padding: 0;
  font: inherit; color: var(--ink-faint); cursor: pointer;
  /* Own row, centred – the two spans above stay on the space-between line. */
  flex-basis: 100%;
  text-align: center;
}
.footer .stamp button:hover { color: var(--ember-ink); }

/* ---------- Terminal ---------- */
.term-shell {
  position: fixed;
  left: 50%;
  bottom: 0;
  transform: translate(-50%, 105%);
  width: min(860px, calc(100vw - 24px));
  z-index: 90;
  border: 1px solid var(--line-strong);
  border-bottom: 0;
  border-radius: 14px 14px 0 0;
  background: var(--term-bg);
  box-shadow: 0 -18px 60px rgba(3, 4, 14, 0.6);
  transition: transform 0.4s var(--ease);
  font-family: var(--font-mono);
}
.term-shell.open { transform: translate(-50%, 0); }
.term-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px;
  border-bottom: 1px solid rgba(168, 166, 198, 0.14);
  color: #8c8ab0;
  font-size: 0.78rem;
}
.term-bar .dots { display: flex; gap: 6px; }
.term-bar .dots span { width: 10px; height: 10px; border-radius: 50%; background: #3a3f6e; }
.term-bar .dots span:first-child { background: var(--ember); }
.term-bar .title { flex: 1; text-align: center; }
.term-close {
  appearance: none; background: none; border: 0;
  color: #8c8ab0; cursor: pointer; font: inherit; padding: 2px 6px;
}
.term-close:hover { color: var(--ember); }
.term-screen {
  height: 330px;
  overflow-y: auto;
  padding: 14px 18px;
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--term-ink);
}
.term-screen .ln { white-space: pre-wrap; word-break: break-word; }
.term-screen .ln a { color: var(--ember); text-decoration: underline; }
.term-screen .muted { color: #8c8ab0; }
.term-screen .accent { color: var(--ember); }
.term-screen .good { color: var(--ok); }
.term-screen .bad { color: var(--warn); }
.term-input-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 18px 14px;
}
.term-prompt { color: var(--ember); font-size: 0.85rem; white-space: nowrap; }
.term-input {
  flex: 1;
  font: inherit;
  font-size: 0.85rem;
  color: var(--term-ink);
  background: transparent;
  border: 0;
  outline: none;
  caret-color: var(--ember);
}

/* Game canvas inside terminal */
.term-game {
  display: block;
  width: 100%;
  height: 330px;
  background: var(--term-bg);
}

/* ---------- Misc ---------- */
.skip-link {
  position: absolute; top: -48px; left: 16px;
  z-index: 100;
  background: var(--ember); color: #221502;
  padding: 0.5em 1em; border-radius: 0 0 8px 8px;
  font-family: var(--font-mono); font-size: 0.85rem;
  transition: top 0.2s;
}
.skip-link:focus { top: 0; color: #221502; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  .caret { animation: none; }
  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
}

/* ---------- Page figures (the resident, in various rooms) ---------- */
.page-figure {
  position: relative;
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background:
    radial-gradient(420px 260px at 60% 18%, rgba(91, 108, 255, 0.10), transparent 70%),
    var(--bg-raised);
  overflow: hidden;
}
.page-figure canvas { position: absolute; inset: 0; }
.page-figure img {
  position: relative; z-index: 1;
  display: block; width: 100%; height: 100%;
  object-fit: contain; object-position: center bottom;
  padding: 10px 10px 0;
  filter: drop-shadow(0 6px 24px rgba(91, 108, 255, 0.28));
}
[data-theme="light"] .page-figure img { filter: drop-shadow(0 6px 22px rgba(29, 33, 82, 0.18)); }
.fig-34 { aspect-ratio: 3 / 4; }
.fig-43 { aspect-ratio: 4 / 3; }
[data-theme="dark"] .page-figure .img-light { display: none; }
[data-theme="light"] .page-figure .img-dark { display: none; }

/* Page-head with a figure beside the title block */
.page-head-grid {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 48px;
  align-items: end;
}
@media (max-width: 820px) {
  .page-head-grid { grid-template-columns: 1fr; }
  .page-head-grid .page-figure { max-width: 300px; }
}

/* About: portrait stacked above the aside card */
.about-side { display: grid; gap: 28px; align-content: start; }

/* ---------- Ambient wall (fills the wide-screen margins) ----------
   --wall-texture / --wall-opacity are set on :root once a texture
   is chosen; until then this layer is invisible. */
body::before {
  content: "";
  position: fixed; inset: 0; z-index: -1;
  background-image: var(--wall-texture, none);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: calc(50% + var(--wall-pan, 0px)) center;
  opacity: var(--wall-opacity, 0);
  pointer-events: none;
  -webkit-mask-image: linear-gradient(90deg, rgba(0,0,0,1) 0%, rgba(0,0,0,0.18) 32%, rgba(0,0,0,0.18) 68%, rgba(0,0,0,1) 100%);
  mask-image: linear-gradient(90deg, rgba(0,0,0,1) 0%, rgba(0,0,0,0.18) 32%, rgba(0,0,0,0.18) 68%, rgba(0,0,0,1) 100%);
}

/* The wall: texture B (constellation), chosen 2026-06-12 */
:root { --wall-texture: url("../img/wall-dark.webp"); --wall-opacity: 0.45; }
[data-theme="light"] { --wall-texture: url("../img/wall-light.webp"); --wall-opacity: 0.6; }

/* ---------- Phase 2: photographic scene backgrounds ----------
   Home hero gets a full-bleed themed panorama that pans horizontally with
   scroll; a thin "slit" reveals it again between the loop and writing; inner
   pages reuse the wall layer with the same panorama so it fills the
   wide-screen margins and stays faint behind the content column. */

/* Theme swap (mirrors the .page-figure rules) */
[data-theme="dark"] .scene-img.img-light,
[data-theme="light"] .scene-img.img-dark { display: none; }
.scene-img { pointer-events: none; user-select: none; }

/* Hero panorama */
.scene-bg { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.scene-bg .scene-img {
  position: absolute; top: 50%; left: 50%;
  height: 118%; width: auto; max-width: none;
  transform: translate(calc(-50% + var(--pan, 0px)), -50%);
  will-change: transform;
}
.scene-bg::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(90deg, rgba(9,12,29,0) 0%, rgba(9,12,29,0.42) 22%, rgba(9,12,29,0.72) 50%, rgba(9,12,29,0.42) 78%, rgba(9,12,29,0) 100%),
    linear-gradient(180deg, rgba(9,12,29,0.20), rgba(9,12,29,0) 45%, rgba(9,12,29,0.34));
}
[data-theme="light"] .scene-bg::after {
  background:
    linear-gradient(90deg, rgba(243,242,249,0) 0%, rgba(243,242,249,0.34) 22%, rgba(243,242,249,0.60) 50%, rgba(243,242,249,0.34) 78%, rgba(243,242,249,0) 100%),
    linear-gradient(180deg, rgba(243,242,249,0.12), rgba(243,242,249,0) 45%, rgba(243,242,249,0.26));
}
.hero #hero-field { z-index: 1; }

/* The slit: a thin static reveal between Loop and Writing */
.scene-slit {
  position: relative;
  height: 60vh; min-height: 360px;
  overflow: hidden;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
}
.scene-slit .scene-img {
  position: absolute; top: 50%; left: 50%;
  min-width: 132%; height: 100%; object-fit: cover;
  transform: translate(calc(-50% + var(--pan, 0px)), -50%);
  will-change: transform;
}
.scene-slit::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(90deg, rgba(9,12,29,0) 0%, rgba(9,12,29,0.38) 24%, rgba(9,12,29,0.66) 50%, rgba(9,12,29,0.38) 76%, rgba(9,12,29,0) 100%),
    linear-gradient(180deg, rgba(9,12,29,0.34), rgba(9,12,29,0.20) 50%, rgba(9,12,29,0.34));
}
[data-theme="light"] .scene-slit::after {
  background:
    linear-gradient(90deg, rgba(243,242,249,0) 0%, rgba(243,242,249,0.32) 24%, rgba(243,242,249,0.56) 50%, rgba(243,242,249,0.32) 76%, rgba(243,242,249,0) 100%),
    linear-gradient(180deg, rgba(243,242,249,0.44), rgba(243,242,249,0.24) 50%, rgba(243,242,249,0.44));
}

/* Slit pull-quote: mirrors hero typography over the panorama */
.scene-slit .slit-inner {
  position: relative; z-index: 2;
  text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 1.2rem;
}
.scene-slit .slit-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(2.4rem, 6vw, 4.2rem);
  line-height: 1.08;
  max-width: 16ch;
  text-shadow: 0 2px 30px rgba(9, 12, 29, 0.55);
}
.scene-slit .slit-title em {
  font-style: italic;
  color: var(--ember-ink);
}
.scene-slit .slit-lede {
  margin: 0;
  font-size: clamp(1.05rem, 2vw, 1.25rem);
  color: var(--ink-dim);
  max-width: 46ch;
  text-shadow: 0 2px 18px rgba(9, 12, 29, 0.5);
}

/* Inner pages: the panorama fills the wide margins via the wall layer */
body:not(:has(.hero)) { --wall-texture: url("../img/bg-scene-dark.webp"); --wall-opacity: 0.5; }
[data-theme="light"] body:not(:has(.hero)) { --wall-texture: url("../img/bg-scene-light.webp"); --wall-opacity: 0.55; }

/* Writing gets its own panorama instead of the shared hero scene */
body.page-writing { --wall-texture: url("../img/bg-writing-dark.webp"); --wall-opacity: 0.5; }
[data-theme="light"] body.page-writing { --wall-texture: url("../img/bg-writing-light.webp"); --wall-opacity: 0.55; }

/* Finds and Contact get their own panoramas instead of the shared hero scene */
body.page-finds { --wall-texture: url("../img/bg-finds-dark.webp"); --wall-opacity: 0.5; }
[data-theme="light"] body.page-finds { --wall-texture: url("../img/bg-finds-light.webp"); --wall-opacity: 0.55; }
body.page-contact { --wall-texture: url("../img/bg-contact-dark.webp"); --wall-opacity: 0.5; }
[data-theme="light"] body.page-contact { --wall-texture: url("../img/bg-contact-light.webp"); --wall-opacity: 0.55; }

/* Build gets the desk panorama – the workbench, which is what the section is */
body.page-build { --wall-texture: url("../img/bg-slit-dark.webp"); --wall-opacity: 0.5; }
[data-theme="light"] body.page-build { --wall-texture: url("../img/bg-slit-light.webp"); --wall-opacity: 0.55; }

/* Mobile: only the home page keeps imagery; inner pages fall back to the wall */
@media (max-width: 820px) {
  body:not(:has(.hero)) { --wall-texture: url("../img/wall-dark.webp"); --wall-opacity: 0.30; }
  [data-theme="light"] body:not(:has(.hero)) { --wall-texture: url("../img/wall-light.webp"); --wall-opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
  .scene-bg .scene-img, .scene-slit .scene-img { transform: translate(-50%, -50%) !important; }
}

/* ---------- Depth: content layers float above the scene images ---------- */
.nav { box-shadow: 0 10px 30px rgba(4, 6, 18, 0.40); }
[data-theme="light"] .nav { box-shadow: 0 10px 28px rgba(38, 42, 96, 0.10); }

.hero, .scene-slit { z-index: 0; }
#about, #loop, #build { z-index: 1; }
#about   { box-shadow: 0 -26px 52px -14px rgba(4, 6, 18, 0.6); }
#loop    { box-shadow: 0 26px 52px -14px rgba(4, 6, 18, 0.6); }
#build   { box-shadow: 0 -26px 52px -14px rgba(4, 6, 18, 0.6); }
[data-theme="light"] #about,
[data-theme="light"] #build { box-shadow: 0 -26px 52px -16px rgba(38, 42, 96, 0.20); }
[data-theme="light"] #loop { box-shadow: 0 26px 52px -16px rgba(38, 42, 96, 0.20); }

/* ---------- Tag filters (writing & finds) ---------- */
.filters {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  margin-bottom: 2.2rem;
}
/* A filter row with nothing in it (e.g. no series exist yet) takes no space. */
.filters:empty { display: none; }
/* The series row sits directly under the tag row – tighten its top margin. */
.filters--series { margin-top: -1.2rem; }
.filter-label {
  font-family: var(--font-mono); font-size: 0.78rem;
  letter-spacing: 0.06em; color: var(--ink-faint); margin-right: 2px;
}
.filter-chip {
  appearance: none; cursor: pointer;
  font-family: var(--font-mono); font-size: 0.78rem;
  color: var(--ink-dim); background: transparent;
  border: 1px solid var(--line); border-radius: 999px;
  padding: 0.32em 0.95em;
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}
.filter-chip:hover { color: var(--ember-ink); border-color: var(--line-strong); }
.filter-chip.is-active {
  color: #221502; background: var(--ember); border-color: var(--ember); font-weight: 500;
}
/* Series chips pick out the violet used for tags/series names on the cards. */
.filter-chip--series.is-active {
  color: #fff; background: var(--violet); border-color: var(--violet);
}

/* ---------- Breadcrumb / clickable path eyebrow ----------
   Renders contiguously as ~/section/slug; the leading crumbs are links. */
.path.breadcrumb { gap: 0; flex-wrap: wrap; }
.path.breadcrumb::before { content: none; }
.breadcrumb .crumb { color: var(--ink-dim); transition: color 0.2s; }
.breadcrumb a.crumb:hover { color: var(--ember-ink); }
.breadcrumb .crumb.current { color: var(--ember-ink); }

/* ---------- Pagination (writing & finds) ----------
   Mirrors the filter-chip language: mono pills, ember for the active page,
   faint mono for the label & count, so it reads as one terminal interface. */
.pager {
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
  margin-top: 2.6rem;
}
.pager.is-hidden { display: none; }
.pager-label {
  font-family: var(--font-mono); font-size: 0.78rem;
  letter-spacing: 0.06em; color: var(--ink-faint); margin-right: 2px;
}
.pager-btn {
  appearance: none; cursor: pointer;
  font-family: var(--font-mono); font-size: 0.78rem;
  color: var(--ink-dim); background: transparent;
  border: 1px solid var(--line); border-radius: 999px;
  padding: 0.32em 0.95em; min-width: 2.4em;
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}
.pager-btn:hover:not(:disabled):not(.is-active) { color: var(--ember-ink); border-color: var(--line-strong); }
.pager-btn.is-active {
  color: #221502; background: var(--ember); border-color: var(--ember); font-weight: 500;
  cursor: default;
}
.pager-btn:disabled { opacity: 0.38; cursor: not-allowed; }
.pager-ellipsis { font-family: var(--font-mono); font-size: 0.78rem; color: var(--ink-faint); padding: 0 0.15em; }
.pager-status {
  font-family: var(--font-mono); font-size: 0.78rem;
  letter-spacing: 0.06em; color: var(--ink-faint); margin-left: 4px;
}
@media (max-width: 560px) {
  .pager-status { display: none; }
}

/* ---------- Page transitions: native cross-fade between documents ----------
   The browser snapshots the outgoing page and cross-fades it into the incoming
   one (real page-to-page fade, not a fade through the background). Unsupported
   browsers simply navigate instantly. */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation-duration: 0.4s; }
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
}

/* Footer hugs the floor on short pages */
body { min-height: 100vh; display: flex; flex-direction: column; }
main { flex: 1 0 auto; width: 100%; }

/* Loop nodes: no UA focus rectangle; ember ring for keyboard users */
.loop-node { outline: none; }
.loop-node:focus { outline: none; }
.loop-node:focus-visible circle { stroke: var(--ember); stroke-width: 2.5; }

/* Things I care about – full-width strip */
.care-strip ul {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0.7rem 2.2rem;
}
@media (max-width: 880px) { .care-strip ul { grid-template-columns: 1fr; } }

/* Mobile: figures match the width of the content below them */
@media (max-width: 820px) {
  .page-head-grid .page-figure { max-width: none; }
}

/* Contact: fields and cards share grid rows – equal heights by construction */
.contact-ways { gap: 1.3rem; }
.way { align-items: center; }
@media (min-width: 821px) {
  .contact-grid { row-gap: 1.3rem; column-gap: 52px; }
  #contact-form, .contact-ways { display: contents; }
  #contact-form .field { margin-bottom: 0; display: flex; }
  #contact-form .field:nth-child(1) { grid-column: 1; grid-row: 1; align-self: stretch; }
  #contact-form .field:nth-child(2) { grid-column: 1; grid-row: 2; align-self: stretch; }
  #contact-form .field:nth-child(3) { grid-column: 1; grid-row: 3; }
  #contact-form .btn { grid-column: 1; grid-row: 4; justify-self: start; }
  #contact-form .form-status { grid-column: 1; grid-row: 5; margin-top: 0; }
  .contact-ways .way:nth-child(1) { grid-column: 2; grid-row: 1; align-self: stretch; }
  .contact-ways .way:nth-child(2) { grid-column: 2; grid-row: 2; align-self: stretch; }
  .contact-ways .way:nth-child(3) { grid-column: 2; grid-row: 3; }
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}

/* Terminal: mac-style controls + maximised state */
.term-bar .dots span { cursor: pointer; }
.term-bar .dots span:first-child { background: #ff5f57; }
.term-bar .dots span:nth-child(2) { background: #febc2e; }
.term-bar .dots span:nth-child(3) { background: #28c840; }
.term-shell.term-max { width: min(80vw, 1500px); }
.term-shell.term-max .term-screen { height: 60vh; }
.term-shell.term-max .term-game { height: 62vh; }
