/* ------------------------------------------------------------------
   A quiet, monochrome blog.
   Typography: Space Mono for display (titles, wordmark, pull quotes),
   Courier Prime (typewriter) for body text and metadata.
   No colors, no shadows, no cards. Hairlines and whitespace only.
------------------------------------------------------------------- */

:root {
  /* warm greys throughout: paper and ink, not a screen */
  --ink: #161513;
  --paper: #faf9f7;
  --grey: #6f6a62;
  --faint: #77726b;
  --hairline: #e7e3dc;
  --code-bg: #f1eee8;
  --selection: #ded8cd;

  --display: "Space Mono", "Courier Prime", "Courier New", monospace;
  --mono: "Courier Prime", ui-monospace, "Cascadia Mono", Menlo, Consolas,
          "Courier New", monospace;
}

/* dark mode: same warm, strictly-monochrome palette, inverted.
   Default follows the OS preference; an explicit choice made via the
   header toggle (site.js sets data-theme, remembered in localStorage)
   always wins over the OS setting. */
@media (prefers-color-scheme: dark) {
  :root {
    --ink: #eae5db;
    --paper: #17150f;
    --grey: #a29c8f;
    --faint: #8f8a82;
    --hairline: #2d2a23;
    --code-bg: #201d17;
    --selection: #3a352a;
  }
}

:root[data-theme="light"] {
  --ink: #161513;
  --paper: #faf9f7;
  --grey: #6f6a62;
  --faint: #77726b;
  --hairline: #e7e3dc;
  --code-bg: #f1eee8;
  --selection: #ded8cd;
}

:root[data-theme="dark"] {
  --ink: #eae5db;
  --paper: #17150f;
  --grey: #a29c8f;
  --faint: #8f8a82;
  --hairline: #2d2a23;
  --code-bg: #201d17;
  --selection: #3a352a;
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 1.125rem;
  line-height: 1.6;
  font-feature-settings: "liga", "kern";
  text-rendering: optimizeLegibility;
  transition: background-color 0.4s ease, color 0.4s ease;
}

.wrap {
  max-width: 41rem;
  margin: 0 auto;
  padding: 0 1.375rem;
}

/* ---- header ------------------------------------------------------ */

header.site {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 3.5rem 0 1.25rem;
  border-bottom: 1px solid var(--hairline);
}

.wordmark {
  font-family: var(--display);
  font-size: 1.75rem;
  font-weight: 400;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
}

.wordmark-text {
  display: inline-block;
  overflow: hidden;
  white-space: nowrap;
  vertical-align: bottom;
  width: 0;
  animation: wordmark-type 0.7s steps(3, end) forwards;
}

@keyframes wordmark-type {
  to { width: 3ch; }
}

.wordmark-cursor {
  display: inline-block;
  width: 0.5em;
  height: 0.95em;
  margin-left: 0.08em;
  background: currentColor;
  vertical-align: -0.1em;
  /* Solid while "LOG" is still being typed, then blinks for ~10s (9
     cycles at 1.1s) before settling back to solid - indefinite blinking
     in a page header reads as distracting rather than lively. No
     fill-mode: once the last cycle ends, the cursor just reverts to its
     unanimated (fully opaque) state. */
  animation: wordmark-blink 1.1s step-end 0.7s 9;
}

@media (prefers-reduced-motion: reduce) {
  /* The site's global reduced-motion rule disables the typing animation
     entirely, which would otherwise leave .wordmark-text stuck at its
     pre-animation width:0 - permanently invisible. Show the full text
     immediately instead. */
  .wordmark-text { width: auto; }
}

@keyframes wordmark-blink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0; }
}

header.site nav a {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--grey);
  text-decoration: none;
  margin-left: 1.5rem;
}

header.site nav a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 0.25em; }

.theme-toggle,
.sound-toggle {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--grey);
  background: none;
  border: none;
  padding: 0;
  margin-left: 1.5rem;
  cursor: pointer;
}

.theme-toggle:hover,
.sound-toggle:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 0.25em; }

.theme-toggle:focus-visible,
.sound-toggle:focus-visible {
  outline: 1px solid var(--faint);
  outline-offset: 3px;
}

/* Sound is off until asked for, so the control reads as inactive until
   it isn't - the state has to be visible without clicking to find out. */
.sound-toggle[aria-pressed="true"] { color: var(--ink); }

/* ---- projects ----------------------------------------------------- */

/* A list, not a grid of cards. Each project is one hairline-separated
   band: name and stack on one line, what it is underneath, and the log
   entry only where one exists. Nothing is numbered - a numbered list
   would claim a sequence these do not have. */
.project-list {
  list-style: none;
  margin: 2.5rem 0 0;
  padding: 0;
}

.project {
  padding: 1.6rem 0;
  border-top: 1px solid var(--hairline);
}

.project:last-child { border-bottom: 1px solid var(--hairline); }

.project-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin: 0 0 0.5rem;
}

.project-name {
  font-family: var(--display);
  font-size: 1.05rem;
  text-decoration: none;
  border-bottom: 1px solid var(--faint);
}

.project-name:hover { border-bottom-color: var(--ink); }

/* Right-aligned and quiet: the stack is a fact you scan for, not a
   claim the project is making. */
.project-stack {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
}

.project-blurb {
  margin: 0;
  color: var(--grey);
}

.project-entry {
  margin: 0.65rem 0 0;
  font-size: 0.85rem;
}

.project-more {
  margin-top: 2rem;
  font-size: 0.9rem;
  color: var(--grey);
}

/* The source link sits in the article's meta line, in the same register
   as the date and tags - a fact about the piece, not a call to action. */
.post-source {
  color: var(--faint);
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
}

.post-source:hover { color: var(--ink); border-bottom-color: var(--faint); }

@media (max-width: 600px) {
  .project-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.25rem;
  }

  header.site nav a,
  .theme-toggle,
  .sound-toggle {
    margin-left: 0.6rem;
    font-size: 0.625rem;
  }
}

/* ---- links & motion ---------------------------------------------- */

a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--faint);
  text-underline-offset: 0.2em;
}

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

a, button {
  transition: color 0.3s ease, text-decoration-color 0.3s ease,
              opacity 0.3s ease, background-color 0.3s ease;
}

a:active, button:active { opacity: 0.55; }

body {
  animation: page-in 0.45s ease both;
}

@keyframes page-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition: none !important;
    animation: none !important;
  }
}

/* ---- motto ------------------------------------------------------- */

.motto {
  font-family: var(--mono);
  font-size: 0.9375rem;
  letter-spacing: 0.04em;
  color: var(--faint);
  margin: 1rem 0 0;
}

/* ---- intro note ---------------------------------------------------- */

.intro {
  margin: 3.25rem 0 0.75rem;
  padding-left: 1.25rem;
  border-left: 1px solid var(--hairline);
  color: var(--grey);
  font-size: 1.125rem;
}

/* ---- post list --------------------------------------------------- */

.year {
  font-family: var(--mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  color: var(--faint);
  margin: 3.5rem 0 0;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.year::after {
  content: "";
  height: 1px;
  width: 6rem;
  background: var(--hairline);
}

ul.posts {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
}

ul.posts li {
  padding: 1.375rem 0;
  border-bottom: 1px solid var(--hairline);
}

.post-line {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.5rem;
}

.post-line a {
  font-family: var(--display);
  font-size: 1.3125rem;
  font-weight: 400;
  text-decoration: none;
}

.post-line a:hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.2em; }

.post-line time {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--faint);
  white-space: nowrap;
}

.post-desc {
  margin: 0.375rem 0 0;
  color: var(--grey);
  font-size: 1.125rem;
}

/* ---- post page --------------------------------------------------- */

article.post { padding-top: 4rem; }

article.post h1 {
  font-family: var(--display);
  font-size: 3.5rem;
  line-height: 1.08;
  font-weight: 400;
  letter-spacing: -0.04em;
  margin: 0 0 1.5rem;
}

article.post .post-meta {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 4rem;
}

.post-meta .tags::before { content: "· "; }

article.post h2 {
  font-family: var(--display);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.3;
  margin: 2.75rem 0 0.875rem;
}

article.post h3 {
  font-family: var(--display);
  font-size: 1.25rem;
  font-weight: 400;
  font-style: italic;
  margin: 2.25rem 0 0.75rem;
}

/* Body copy: slightly heavier for legibility, especially on dark mode. */
article.post p {
  margin: 0 0 1.375rem;
  font-weight: 500;
}

article.post blockquote {
  margin: 2rem 0;
  padding-left: 1.25rem;
  border-left: 1px solid var(--ink);
  color: var(--grey);
}

article.post blockquote p { margin-bottom: 0.75rem; }
article.post blockquote p:last-child { margin-bottom: 0; }

article.post blockquote.pull {
  font-family: var(--display);
  border-left: none;
  padding-left: 0;
  margin: 3.75rem 0;
  font-size: 1.875rem;
  font-style: italic;
  line-height: 1.4;
  letter-spacing: -0.02em;
  color: var(--ink);
  font-weight: 400;
}

@media (min-width: 760px) {
  article.post blockquote.pull {
    margin-left: -2.5rem;
  }
}

article.post figure {
  margin: 0;
}

article.post img {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 2.5rem auto;
  filter: grayscale(1);
}

article.post figure.color img {
  filter: none;
}

article.post figcaption {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--faint);
  text-align: center;
  margin-top: -1.5rem;
  margin-bottom: 2.5rem;
}

article.post hr {
  border: none;
  border-top: 1px solid var(--hairline);
  margin: 3rem 0;
}

/* ---- code -------------------------------------------------------- */

code {
  font-family: var(--mono);
  font-size: 0.85em;
  background: var(--code-bg);
  padding: 0.125em 0.375em;
  border-radius: 2px;
}

pre {
  background: var(--code-bg);
  padding: 1.125rem 1.25rem;
  margin: 2rem 0;
  overflow-x: auto;
  line-height: 1.55;
  border-radius: 2px;
}

pre code {
  background: none;
  padding: 0;
  font-size: 0.8125rem;
  color: var(--ink);
}

pre .cm { color: var(--faint); }

/* ---- math ----------------------------------------------------------
   The one deliberate exception to "two typefaces, both monospace" (see
   DESIGN.md) - MathML renders in the browser's own math face (a serif,
   italic-variable style with a real OpenType MATH table), which a
   monospace font cannot fake: proper fraction bars, radicals and
   superscripts need glyph variants Courier Prime and Space Mono don't
   have. Framed the same way a code block is - its own bordered register,
   not silently blended into body text - rather than pretending it's the
   same typeface making an exception. Color still inherits var(--ink), so
   dark mode needs nothing extra here. */
article.post math[display="block"] {
  display: block;
  max-width: 100%;
  margin: 1.75rem 0;
  padding: 1rem 1.25rem;
  background: var(--code-bg);
  text-align: center;
  font-size: 1.2em;
  overflow-x: auto;
}

/* ---- inline term previews -------------------------------------------
   A term whose definition sits in a hover/focus card instead of an
   inline aside - for a phrase used more than once, where writing the
   definition out in the sentence every time it's needed would repeat
   itself. Dotted underline and cursor:help are the same signal
   <abbr title> already uses; the difference here is a styled card that
   actually matches the page, not the browser's own plain tooltip.
   Keyboard-reachable (tabindex, :focus) and readable without JS - the
   preview text is real DOM content, just visually hidden until hovered
   or focused, not injected by script. */
.term {
  position: relative;
  /* --grey rather than --faint: it's the higher-contrast of the two
     against paper in both themes (darker in light mode, lighter in
     dark), which --faint's own hairline-adjacent job doesn't need but a
     dotted underline meant to be noticed does. Darkens further to --ink
     on hover/focus, same as a real link. */
  border-bottom: 1px dotted var(--grey);
  cursor: help;
  transition: border-color 0.15s ease;
}

.term:hover,
.term:focus {
  border-bottom-color: var(--ink);
}

@media (prefers-reduced-motion: reduce) {
  .term { transition: none; }
}

.term:focus-visible {
  outline: 1px solid var(--faint);
  outline-offset: 3px;
}

.term-preview {
  position: absolute;
  left: 50%;
  bottom: 100%;
  width: 19rem;
  max-width: min(19rem, 80vw);
  margin-bottom: 0.6rem;
  padding: 0.8rem 1rem;
  background: var(--code-bg);
  border: 1px solid var(--hairline);
  font: 0.8rem/1.55 var(--mono);
  color: var(--grey);
  text-align: left;
  letter-spacing: 0;
  text-transform: none;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 4px);
  transition: opacity 0.18s ease, transform 0.18s ease,
    visibility 0s linear 0.18s;
  pointer-events: none;
  z-index: 5;
}

.term:hover .term-preview,
.term:focus .term-preview {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  transition: opacity 0.18s ease, transform 0.18s ease;
}

@media (prefers-reduced-motion: reduce) {
  .term-preview { transition: none; }
}

@media (max-width: 600px) {
  .term-preview { width: 15rem; font-size: 0.76rem; }
}

/* ---- prose tables --------------------------------------------------
   A data table in running text - not the widget's own scale table,
   which has its own class. Kept inside the prose column, same as code
   blocks and <hr>, rather than stretched to figure width: these tables
   are short (a handful of columns of numbers), and figure-width would
   just spread the digits apart with nothing gained. */

article.post table {
  width: 100%;
  margin: 2rem 0;
  border-collapse: collapse;
  font: 0.85rem var(--mono);
}

article.post th,
article.post td {
  text-align: left;
  padding: 0.5rem 0.9rem 0.5rem 0;
  border-bottom: 1px solid var(--hairline);
  font-variant-numeric: tabular-nums;
}

article.post th {
  font-weight: 400;
  color: var(--faint);
  letter-spacing: 0.03em;
}

article.post td { color: var(--grey); }

@media (max-width: 600px) {
  article.post table { font-size: 0.78rem; }
  article.post th, article.post td { padding-right: 0.5rem; }
}

/* ---- prev/next --------------------------------------------------- */

.post-nav {
  display: flex;
  justify-content: space-between;
  gap: 2rem;
  margin-top: 4rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--hairline);
  font-family: var(--mono);
  font-size: 0.8125rem;
}

.post-nav a {
  color: var(--grey);
  text-decoration: none;
  max-width: 45%;
}

.post-nav .newer { text-align: right; }

.post-nav a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 0.25em; }

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

footer.site {
  margin-top: 5rem;
  padding: 1.5rem 0 3.5rem;
  border-top: 1px solid var(--hairline);
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--faint);
  display: flex;
  justify-content: space-between;
  gap: 1rem;
}

footer.site a { color: var(--faint); text-decoration: none; }
footer.site a:hover { color: var(--ink); }

/* ---- details ----------------------------------------------------- */

::selection {
  background: var(--selection);
  color: var(--ink);
}

html {
  scrollbar-width: thin;
  scrollbar-color: transparent transparent;
  transition: scrollbar-color 0.4s ease;
}

html.scrolling { scrollbar-color: var(--hairline) transparent; }
html.scrolling:hover { scrollbar-color: var(--faint) transparent; }

@supports not (scrollbar-color: auto) {
  ::-webkit-scrollbar { width: 6px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb { background: var(--hairline); }
  ::-webkit-scrollbar-thumb:hover { background: var(--faint); }
}

.resume {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin: -2.5rem 0 4rem;
  animation: page-in 0.6s ease 0.2s both;
}

.resume a {
  color: var(--grey);
  text-decoration: none;
}

.resume a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 0.25em; }

.continue {
  margin: 0.5rem 0 0;
  padding-left: 1.25rem;
  border-left: 1px solid var(--hairline);
  font-size: 1.0625rem;
  animation: page-in 0.6s ease 0.2s both;
}

.continue .label {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--faint);
  display: block;
  margin-bottom: 0.375rem;
}

.continue a {
  color: var(--grey);
  text-decoration: none;
}

.continue a:hover { color: var(--ink); text-decoration: underline; text-underline-offset: 0.25em; }

article.post.entry::after {
  content: "∎";
  display: block;
  margin-top: 3rem;
  font-family: var(--mono);
  font-size: 0.875rem;
  color: var(--grey);
}

/* Visually-hidden but readable by screen readers. Lives here rather than
   in home.css because article pages need it too and only load style.css. */
.sr-only {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

/* ---- grokking figure --------------------------------------------- */
/* Interactive accuracy plot. Hidden until site.js has fetched the run
   data and added .is-ready, so a failed fetch shows nothing rather than
   empty axes; <noscript> carries the original static plot. */

/* article.post figure sets margin:0, so this needs the same prefix to
   win on specificity and keep the figure clear of the surrounding prose. */
article.post .grok-demo { margin: 2.75rem 0 3rem; }
.grok-demo .grok-head,
.grok-demo .grok-plot,
.grok-demo .grok-scrub { display: none; }
/* .grok-head must come back as flex, not block: a blanket display:block
   here outranks the flex declaration below and silently kills the
   switch/readout row layout. */
.grok-demo.is-ready .grok-head { display: flex; }
.grok-demo.is-ready .grok-plot,
.grok-demo.is-ready .grok-scrub { display: block; }

.grok-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem 1.25rem;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 1.25rem;
}

/* ---- segmented switch indicator -----------------------------------
   Shared by every real single-select switch (weight decay, ray mode,
   hash-digest length): one block that slides to whichever button is
   pressed, mounted by site.js's mountSwitchIndicator(). Scoped with
   :has() rather than a shared markup class so the birthday-demo run
   controls (step / auto-run / reset - independent actions, not a
   choice) are untouched: site.js only ever mounts an indicator on the
   three containers that are genuinely one-of-many switches, and this
   rule only fires where that indicator actually exists. */
.grok-switch,
.ray-switch,
.birthday-switch,
.blocksize-switch {
  position: relative;
}

.grok-switch button,
.ray-switch button,
.birthday-switch button,
.blocksize-switch button {
  position: relative;
  z-index: 1;
}

.switch-indicator {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background: var(--ink);
  z-index: 0;
  pointer-events: none;
  transition: transform 200ms cubic-bezier(.16, .78, .18, 1),
    width 200ms cubic-bezier(.16, .78, .18, 1);
}

.grok-switch:has(.switch-indicator) button[aria-pressed="true"],
.ray-switch:has(.switch-indicator) button[aria-pressed="true"],
.birthday-switch:has(.switch-indicator) button[aria-pressed="true"],
.blocksize-switch:has(.switch-indicator) button[aria-pressed="true"] {
  background: none;
}

@media (prefers-reduced-motion: reduce) {
  .switch-indicator { transition: none; }
}

.grok-switch { display: flex; gap: 0; }

.grok-switch button {
  font: 0.7rem var(--mono);
  letter-spacing: 0.04em;
  padding: 0.35rem 0.7rem;
  background: none;
  color: var(--grey);
  border: 1px solid var(--hairline);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}

.grok-switch button + button { border-left: 0; }
.grok-switch button:hover { color: var(--ink); border-color: var(--ink); }

.grok-switch button[aria-pressed="true"] {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.grok-readout {
  margin: 0;
  display: flex;
  gap: 1rem;
  font: 0.7rem var(--mono);
  letter-spacing: 0.03em;
  color: var(--faint);
}

.grok-readout b { font-weight: 400; color: var(--ink); }

.grok-plot { width: 100%; height: auto; overflow: visible; }

.grok-plot .grok-grid { stroke: var(--hairline); stroke-width: 1; }
.grok-plot .grok-axis,
.grok-plot .grok-tick { stroke: var(--grey); stroke-width: 1; }

.grok-plot .grok-label {
  fill: var(--faint);
  font-family: var(--mono);
  font-size: 11px;
}

.grok-plot .grok-label-y { text-anchor: end; }
.grok-plot .grok-label-x { text-anchor: middle; }

.grok-plot .grok-line-train,
.grok-plot .grok-line-val { fill: none; stroke-linejoin: round; stroke-linecap: round; }
.grok-plot .grok-line-train,
.grok-plot .grok-swatch-train { stroke: var(--grey); stroke-width: 1.2; }
.grok-plot .grok-line-val,
.grok-plot .grok-swatch-val { stroke: var(--ink); stroke-width: 2; }

.grok-plot .grok-grokline {
  stroke: var(--grey);
  stroke-width: 1;
  stroke-dasharray: 4 5;
}

.grok-plot .grok-grokline[hidden] { display: none; }
.grok-plot .grok-head-line { stroke: var(--ink); stroke-width: 1; opacity: 0.35; }
.grok-plot .grok-dot-train { fill: var(--grey); }
.grok-plot .grok-dot-val { fill: var(--ink); }

.grok-scrub { margin-top: 0.5rem; }
.grok-scrub input { width: 100%; accent-color: var(--ink); cursor: pointer; }

/* article.post figcaption pulls captions up under an <img>; this figure
   has a slider there instead, so cancel the negative margin. Needs the
   article.post prefix to outrank that rule's specificity. */
article.post .grok-demo figcaption {
  margin-top: 1rem;
  margin-bottom: 0;
  text-align: left;
}

@media (prefers-reduced-motion: reduce) {
  .grok-switch button { transition: none; }
}

/* ---- Lorenz divergence figure -------------------------------------- */

article.post .chaos-demo { margin: 2.75rem 0 3rem; }

.chaos-demo .chaos-head,
.chaos-demo .chaos-canvas { display: none; }
.chaos-demo.is-ready .chaos-head { display: flex; }
.chaos-demo.is-ready .chaos-canvas { display: block; }

.chaos-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem 1.25rem;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 1.25rem;
}

.chaos-restart {
  font: 0.7rem var(--mono);
  letter-spacing: 0.04em;
  padding: 0.35rem 0.7rem;
  background: none;
  color: var(--grey);
  border: 1px solid var(--hairline);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.chaos-restart:hover { color: var(--ink); border-color: var(--ink); }

.chaos-readout {
  margin: 0;
  font: 0.7rem var(--mono);
  letter-spacing: 0.03em;
  color: var(--faint);
}

.chaos-readout b { font-weight: 400; color: var(--ink); }

.chaos-demo .chaos-params { display: none; }
.chaos-demo.is-ready .chaos-params { display: flex; }

.chaos-params {
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem 1.5rem;
  margin-bottom: 1.1rem;
  padding-bottom: 1.1rem;
  border-bottom: 1px solid var(--hairline);
}

.chaos-params label {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font: 0.7rem var(--mono);
  letter-spacing: 0.03em;
  color: var(--faint);
}

.chaos-params output {
  min-width: 2.6em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.chaos-params input[type="range"] {
  width: 6.5rem;
  accent-color: var(--ink);
  cursor: pointer;
}

.chaos-defaults {
  font: 0.7rem var(--mono);
  letter-spacing: 0.04em;
  padding: 0.3rem 0.6rem;
  background: none;
  color: var(--grey);
  border: 1px solid var(--hairline);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.chaos-defaults:hover { color: var(--ink); border-color: var(--ink); }

.chaos-canvas {
  width: 100%;
  height: auto;
  aspect-ratio: 852 / 480;
  border: 1px solid var(--hairline);
}

article.post .chaos-demo figcaption {
  margin-top: 1rem;
  margin-bottom: 0;
  text-align: left;
}

@media (prefers-reduced-motion: reduce) {
  .chaos-restart,
  .chaos-defaults { transition: none; }
}

/* ---- sphere-tracing figure ----------------------------------------- */

article.post .ray-demo { margin: 2.75rem 0 3rem; }

.ray-demo .ray-head,
.ray-demo .ray-canvas { display: none; }
.ray-demo.is-ready .ray-head { display: flex; }
.ray-demo.is-ready .ray-canvas { display: block; }

.ray-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem 1.25rem;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 1.25rem;
}

.ray-switch { display: flex; gap: 0; }

.ray-switch button {
  font: 0.7rem var(--mono);
  letter-spacing: 0.04em;
  padding: 0.35rem 0.7rem;
  background: none;
  color: var(--grey);
  border: 1px solid var(--hairline);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}

.ray-switch button + button { border-left: 0; }
.ray-switch button:hover { color: var(--ink); border-color: var(--ink); }

.ray-switch button[aria-pressed="true"] {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.ray-readout {
  margin: 0;
  font: 0.7rem var(--mono);
  letter-spacing: 0.03em;
  color: var(--faint);
}

.ray-readout b { font-weight: 400; color: var(--ink); }

.ray-canvas {
  width: 100%;
  height: auto;
  aspect-ratio: 852 / 420;
  border: 1px solid var(--hairline);
  cursor: crosshair;
}

article.post .ray-demo figcaption {
  margin-top: 1rem;
  margin-bottom: 0;
  text-align: left;
}

@media (prefers-reduced-motion: reduce) {
  .ray-switch button { transition: none; }
}

/* ---- birthday-collision figures ------------------------------------ */

article.post .birthday-demo,
article.post .birthday-scale { margin: 2.75rem 0 3rem; }

.birthday-demo .birthday-head,
.birthday-demo .birthday-readout,
.birthday-demo .birthday-grid,
.birthday-demo .birthday-hist-wrap,
.birthday-scale .birthday-scale-head,
.birthday-scale .birthday-scale-body { display: none; }

.birthday-demo.is-ready .birthday-head,
.birthday-demo.is-ready .birthday-readout,
.birthday-demo.is-ready .birthday-grid,
.birthday-demo.is-ready .birthday-hist-wrap { display: block; }
.birthday-demo.is-ready .birthday-head { display: flex; }

.birthday-scale.is-ready .birthday-scale-head,
.birthday-scale.is-ready .birthday-scale-body { display: block; }

.birthday-head {
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.85rem 1.25rem;
  margin-bottom: 1.1rem;
}

.birthday-n {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font: 0.7rem var(--mono);
  letter-spacing: 0.03em;
  color: var(--faint);
}

.birthday-n output {
  min-width: 3.4em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.birthday-n input[type="range"] {
  width: 9rem;
  accent-color: var(--ink);
  cursor: pointer;
}

/* Reuses the switch-button look already established by grok/chaos/ray -
   same visual language for every widget's controls, on purpose. */
.birthday-switch,
.birthday-scale-head .birthday-switch { display: flex; gap: 0; flex-wrap: wrap; }

.birthday-switch button {
  font: 0.7rem var(--mono);
  letter-spacing: 0.04em;
  padding: 0.35rem 0.7rem;
  background: none;
  color: var(--grey);
  border: 1px solid var(--hairline);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}

.birthday-switch button + button { border-left: 0; }
.birthday-switch button:hover { color: var(--ink); border-color: var(--ink); }
.birthday-switch button:disabled { opacity: 0.4; cursor: default; }

.birthday-switch button[aria-pressed="true"] {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.birthday-readout {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  margin: 0 0 0.85rem;
  font: 0.7rem var(--mono);
  letter-spacing: 0.03em;
  color: var(--faint);
}

.birthday-readout b { font-weight: 400; color: var(--ink); font-variant-numeric: tabular-nums; }
.birthday-readout .is-hit { color: var(--ink); }

.birthday-grid {
  width: 100%;
  height: auto;
  aspect-ratio: 852 / 280;
  border: 1px solid var(--hairline);
  margin-bottom: 1.25rem;
}

.birthday-hist-label {
  margin: 0 0 0.5rem;
  font: 0.68rem var(--mono);
  letter-spacing: 0.03em;
  color: var(--faint);
}

.birthday-hist-label b { font-weight: 400; color: var(--ink); font-variant-numeric: tabular-nums; }

.birthday-hist {
  width: 100%;
  height: auto;
  aspect-ratio: 852 / 140;
  border: 1px solid var(--hairline);
}

article.post .birthday-demo figcaption,
article.post .birthday-scale figcaption {
  margin-top: 1rem;
  margin-bottom: 0;
  text-align: left;
}

.birthday-scale-head { margin-bottom: 1.25rem; }

.birthday-scale-body {
  border: 1px solid var(--hairline);
  padding: 1.25rem 1.4rem;
}

.birthday-scale-stat {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin: 0;
  padding: 0.4rem 0;
  font: 0.85rem var(--mono);
  color: var(--grey);
  border-bottom: 1px solid var(--hairline);
}

.birthday-scale-stat b { font-weight: 400; color: var(--ink); font-variant-numeric: tabular-nums; }

.birthday-scale-table {
  width: 100%;
  margin-top: 0.9rem;
  border-collapse: collapse;
  font: 0.78rem var(--mono);
}

.birthday-scale-table th,
.birthday-scale-table td {
  text-align: left;
  padding: 0.5rem 0.6rem 0.5rem 0;
  border-bottom: 1px solid var(--hairline);
  font-variant-numeric: tabular-nums;
}

.birthday-scale-table th {
  font-weight: 400;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-size: 0.66rem;
  color: var(--faint);
}

.birthday-scale-table td:last-child { color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .birthday-switch button { transition: none; }
}

@media (max-width: 600px) {
  .birthday-n input[type="range"] { width: 6rem; }
  .birthday-scale-table { font-size: 0.72rem; }
}

/* ---- colormap carousel ------------------------------------------- */
/* Lives here rather than in a per-post <style> block: the page CSP sets
   style-src without 'unsafe-inline', so an inline <style> is blocked and
   the carousel silently collapsed into 11 stacked full-size images. */

.colormap-carousel { margin: 1.5rem 0 2rem; }

.colormap-track {
  display: flex;
  gap: 1rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  padding-bottom: 0.25rem;
}

.colormap-slide {
  flex: 0 0 85%;
  max-width: 420px;
  scroll-snap-align: center;
  scroll-margin-left: 1rem;
  margin: 0;
}

.colormap-slide img {
  width: 100%;
  display: block;
  border-radius: 4px;
}

.colormap-slide figcaption {
  text-align: center;
  margin-top: 0.4rem;
  font-size: 0.85em;
  opacity: 0.75;
}

.colormap-dots {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  justify-content: center;
  margin-top: 0.75rem;
}

.colormap-dots a {
  font-size: 0.6rem;
  line-height: 1;
  text-decoration: none;
  opacity: 0.35;
  transition: opacity 0.15s;
}

.colormap-dots a:hover,
.colormap-dots a:focus { opacity: 1; }

@media (min-width: 640px) {
  .colormap-slide { flex-basis: 45%; }
}

/* ---- article page: centred prose, wide figures -------------------- */
/* Real posts get a wider shell than the rest of the site. Prose keeps a
   narrow, centred measure and only figures break out past it, staying
   centred on the same axis. Code blocks deliberately stay in the text
   column: almost every block on the site is under 400px of actual
   content, so widening them just produced large empty slabs that broke
   the reading rhythm (the rare wide one scrolls via pre's overflow-x).
   .entry is only ever set on posts, so About / Contact / Privacy / 404
   keep the original narrow wrap. Below 60rem the layout is unchanged. */

@media (min-width: 60rem) {
  .wrap:has(article.post.entry) { max-width: 56rem; }

  article.post.entry {
    display: grid;
    grid-template-columns:
      [wide-start] minmax(0, 1fr)
      [text-start] min(46rem, 100%)
      [text-end] minmax(0, 1fr)
      [wide-end];
  }

  article.post.entry > * { grid-column: text-start / text-end; min-width: 0; }
  article.post.entry::after { grid-column: text-start / text-end; }

  article.post.entry > figure,
  article.post.entry > .colormap-carousel { grid-column: wide-start / wide-end; }
}

@media print {
  body { animation: none; font-size: 11pt; background: #fff; color: #000; }
  header.site nav, footer.site, .now { display: none; }
  a { text-decoration: none; color: #000; }
  pre { background: none; border: 1px solid #ccc; white-space: pre-wrap; }
  article.post h1 { font-size: 24pt; }
}

/* ---- small screens ----------------------------------------------- */

@media (max-width: 540px) {
  body { font-size: 1.125rem; }
  header.site { padding-top: 2.25rem; }
  article.post { padding-top: 2.75rem; }
  article.post h1 { font-size: 2.375rem; }
  article.post blockquote.pull { font-size: 1.5rem; }

  .post-line {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.25rem;
  }

  pre {
    padding: 0.875rem 1rem;
    white-space: pre-wrap;
    word-break: break-word;
  }

  pre code { font-size: 0.75rem; }
}

/* ---- in-app browser banner ----------------------------------------- */

.iab-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem 0.75rem;
  padding: 0.65rem 2.25rem 0.65rem 1rem;
  background: var(--paper);
  color: var(--ink);
  border-bottom: 1px solid var(--hairline);
  font: 0.75rem/1.5 var(--mono);
  text-align: center;
  position: relative;
}

.iab-banner a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.iab-close {
  position: absolute;
  top: 0.4rem;
  right: 0.6rem;
  font: 1rem/1 var(--mono);
  color: inherit;
  background: none;
  border: 0;
  padding: 0.2rem 0.4rem;
  cursor: pointer;
  opacity: 0.7;
}

.iab-close:hover { opacity: 1; }

/* ---- BLOCK_SIZE boundary demo --------------------------------------- */

article.post .blocksize-demo { margin: 2.75rem 0 3rem; }

.blocksize-demo .blocksize-head,
.blocksize-demo .blocksize-readout,
.blocksize-demo .blocksize-grid { display: none; }

.blocksize-demo.is-ready .blocksize-readout,
.blocksize-demo.is-ready .blocksize-grid { display: block; }
.blocksize-demo.is-ready .blocksize-head { display: flex; }

.blocksize-head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem 1.25rem;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 1.25rem;
}

.blocksize-n {
  font: 0.7rem var(--mono);
  letter-spacing: 0.03em;
  color: var(--faint);
}

.blocksize-n output {
  min-width: 3.4em;
  display: inline-block;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.blocksize-n input[type="range"] {
  width: 9rem;
  accent-color: var(--ink);
  cursor: pointer;
  vertical-align: middle;
}

.blocksize-switch { display: flex; gap: 0; }

.blocksize-switch button {
  font: 0.7rem var(--mono);
  letter-spacing: 0.04em;
  padding: 0.35rem 0.7rem;
  background: none;
  color: var(--grey);
  border: 1px solid var(--hairline);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}

.blocksize-switch button + button { border-left: 0; }
.blocksize-switch button:hover { color: var(--ink); border-color: var(--ink); }

.blocksize-switch button[aria-pressed="true"] {
  background: var(--ink);
  color: var(--paper);
  border-color: var(--ink);
}

.blocksize-readout {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  margin: 0 0 0.85rem;
  font: 0.7rem var(--mono);
  letter-spacing: 0.03em;
  color: var(--faint);
}

.blocksize-readout b { font-weight: 400; color: var(--ink); font-variant-numeric: tabular-nums; }
.blocksize-readout .is-broken { color: var(--ink); }

.blocksize-grid {
  width: 100%;
  height: auto;
  aspect-ratio: 852 / 140;
  border: 1px solid var(--hairline);
}

article.post .blocksize-demo figcaption {
  margin-top: 1rem;
  margin-bottom: 0;
  text-align: left;
}

@media (prefers-reduced-motion: reduce) {
  .blocksize-switch button { transition: none; }
}

@media (max-width: 600px) {
  .blocksize-n input[type="range"] { width: 6rem; }
}
