/* ------------------------------------------------------------------ reset */

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

html {
  -webkit-text-size-adjust: 100%;
  tab-size: 4;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body, h1, h2, h3, h4, p, ul, ol, li, dl, dd, figure, blockquote, pre, table {
  margin: 0;
  padding: 0;
}

ul, ol { list-style: none; }

img, svg, video { display: block; max-width: 100%; height: auto; }

a { color: inherit; text-decoration: none; }

button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

code, kbd, pre, samp { font-family: inherit; font-size: inherit; }

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

/* Windows high-contrast mode: keep focus visible against forced colours. */
@media (forced-colors: active) {
  :focus-visible { outline: 2px solid CanvasText; }
  .btn, .tag, .share-btn, .tool { border: 1px solid ButtonText; }
}

/* Readers who ask for more contrast get stronger rules, not a new palette. */
@media (prefers-contrast: more) {
  :root { --line: #767676; --muted: #4a4a4a; }
  html[data-theme="dark"] { --line: #8a8a8a; --muted: #c8c8c8; }
  .btn, .tag, .share-btn, .tool, .field input, .field textarea { border-width: 2px; }
}

/* Respect a reader who asked the OS to reduce motion. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Icon-only controls still need a comfortable target. */
.tool { min-width: 40px; min-height: 40px; }

/* Screen-reader utility, used for labels that should not be visible. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------------------ font */

/* Geist Mono, subset by unicode range. Without these declarations the woff2
   files were never fetched and the page rendered in the system monospace.
   The latin file carries preload priority from the body text it covers, so no
   <link rel=preload> is needed; one caused an unused-preload warning on pages
   that were prerendered and never shown. */
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/fonts/geist-mono-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/fonts/geist-mono-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/fonts/geist-mono-cyrillic.woff2) format("woff2");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/fonts/geist-mono-vietnamese.woff2) format("woff2");
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1,
    U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
    U+1EA0-1EF9, U+20AB;
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/fonts/geist-mono-cyrillic-ext.woff2) format("woff2");
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
    U+FE2E-FE2F;
}
@font-face {
  font-family: "Geist Mono";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(/fonts/geist-mono-symbols.woff2) format("woff2");
  unicode-range: U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F;
}

/* ----------------------------------------------------------------- tokens */

:root {
  --bg: #ffffff;
  --fg: #171717;
  --fg-soft: #3d3d3d;
  --muted: #6b6b6b;
  --line: #e5e5e5;
  --line-soft: #f0f0f0;
  --panel: #fafafa;

  /* One type scale. Nothing else in this file writes a font-size literal. */
  --fs-meta: 12px;
  --fs-small: 13px;
  --fs-body: 14.5px;
  --fs-lead: 16px;
  --fs-row: 16px;
  --fs-h1: clamp(30px, 4.4vw, 44px);
  --fs-h2: clamp(22px, 2.8vw, 27px);

  --measure: 68ch;
  /* Reading measure for full-width article pages. */
  --measure-wide: 78ch;
  --shell: 900px;
  --pad: 20px;
  --serif: "Geist Mono", ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
}

@media (min-width: 641px) {
  :root { --pad: 32px; }
}

html[data-theme="dark"] {
  --bg: #000000;
  --fg: #ededed;
  --fg-soft: #d4d4d4;
  --muted: #9b9b9b;
  --line: #262626;
  --line-soft: #1a1a1a;
  --panel: #0a0a0a;
}

html[data-theme="light"] {
  --bg: #ffffff;
  --fg: #171717;
  --fg-soft: #3d3d3d;
  --muted: #6b6b6b;
  --line: #e5e5e5;
  --line-soft: #f0f0f0;
  --panel: #fafafa;
}

::selection { background: var(--fg); color: var(--bg); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ----------------------------------------------------------------- icons */

.icon { flex-shrink: 0; display: inline-block; vertical-align: -0.15em; }

/* Theme toggle shows exactly one glyph. */
html[data-theme="light"] .only-dark,
html[data-theme="dark"] .only-light { display: none; }

/* A separator dot drawn in CSS, never typed as a character. */
.sep {
  display: inline-block;
  width: 3px;
  height: 3px;
  margin-inline: 0.5em;
  border-radius: 50%;
  background: var(--line);
  vertical-align: 0.2em;
}

.ftr a { display: inline-flex; align-items: center; gap: 4px; }
.crumb { display: inline-flex; align-items: center; gap: 5px; }
.more { display: inline-flex; align-items: center; gap: 5px; }
.next-title { display: inline-flex; align-items: center; gap: 5px; }
.crumb .icon, .more .icon { transition: none; }

/* ------------------------------------------------------------------ shell */

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--serif);
  font-size: var(--fs-body);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}

.shell {
  width: 100%;
  max-width: calc(var(--shell) + var(--pad) * 2);
  margin-inline: auto;
  padding-inline: var(--pad);
}

.main {
  flex: 1;
  padding-block: 3.5rem 5rem;
}

.skip {
  position: absolute;
  left: -9999px;
}
.skip:focus,
.skip:focus-visible {
  left: 12px;
  top: 12px;
  z-index: 60;
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--line);
  padding: 6px 12px;
  font-size: var(--fs-small);
}

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

.hdr {
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 40;
  background: color-mix(in oklab, var(--bg) 92%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
}

.hdr-inner {
  display: flex;
  align-items: center;
  gap: 1.75rem;
  height: 58px;
}

.brand {
  display: inline-flex;
  align-items: baseline;
  font-size: var(--fs-body);
  font-weight: 600;
  letter-spacing: -0.01em;
  flex-shrink: 0;
}

/* Spacing belongs to the mark only. The wordmark and its TLD read as one word,
   so `--fs-*` gap on the container would break "tahag.dev" apart. */
.brand-mark {
  width: 8px;
  height: 8px;
  margin-right: 10px;
  align-self: center;
  background: var(--fg);
  transform: rotate(45deg);
}

.brand-tld { color: var(--muted); font-weight: 400; }

.nav {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  font-size: var(--fs-small);
  color: var(--muted);
  overflow-x: auto;
  scrollbar-width: none;
  white-space: nowrap;
}
.nav::-webkit-scrollbar { display: none; }

.nav a { transition: color 0.15s; }
.nav a:hover, .nav a[aria-current="page"] { color: var(--fg); }

.hdr-tools {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
}

.tool {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  color: var(--muted);
  border-radius: 3px;
  transition: color 0.15s, background 0.15s;
}
.tool:hover { color: var(--fg); background: var(--line-soft); }

/* On a phone the three nav links have to share the bar with the wordmark and
   the tools, so tighten every gap and use the meta size for the links. These
   numbers keep tahag.dev and all three links visible from 360px up. */
@media (max-width: 560px) {
  .hdr-inner { gap: 0.4rem; }
  .brand { font-size: 13px; }
  .brand-mark { width: 6px; height: 6px; margin-right: 6px; }
  /* A little air after the wordmark, taken out of the slack before the tools. */
  .nav { gap: 0.5rem; margin-left: 0.5rem; font-size: var(--fs-meta); }
  .hdr-tools { gap: 0; }
  .tool { width: 30px; height: 30px; }
  .comment-replies { margin-left: 0.7rem; padding-left: 0.7rem; }
  .comment-replies .comment-replies { margin-left: 0.35rem; padding-left: 0.6rem; }
}

/* Only the 320px class of phone has to drop the TLD to keep the name. */
@media (max-width: 350px) {
  .brand-tld { display: none; }
}

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

.ftr {
  border-top: 1px solid var(--line);
  padding-block: 1.5rem;
  font-size: var(--fs-meta);
  color: var(--muted);
}

.ftr-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem 2rem;
}

.ftr-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 1.2rem;
}

.ftr a { transition: color 0.15s; }
.ftr a:hover { color: var(--fg); }

/* ------------------------------------------------------------------- home */

.intro { max-width: var(--measure); }


.intro-title {
  font-size: var(--fs-h1);
  line-height: 1.1;
  letter-spacing: -0.035em;
  font-weight: 700;
  text-wrap: balance;
  max-width: 24ch;
}

.intro p {
  margin-top: 1.1rem;
  color: var(--fg-soft);
  line-height: 1.7;
}

.intro .links { margin-top: 1.6rem; }

.links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.35rem;
  font-size: var(--fs-small);
}

.links a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--muted);
  transition: color 0.16s;
}
.links a:hover { color: var(--fg); }
/* Icons in a link row keep still. Only the text colour changes. */
.links a .icon { transition: none; }

/* Section headings shared by several pages. */
.sect {
  margin-top: 3.25rem;
}

.sect:first-child { margin-top: 0; }

.sect-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--line);
}

.sect-head h2 {
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--fg);
}

.sect-head span, .sect-head a { font-size: var(--fs-meta); color: var(--muted); }
.sect-head a { transition: color 0.15s; }
.sect-head a:hover { color: var(--fg); }

.more {
  display: inline-block;
  margin-top: 1.6rem;
  font-size: var(--fs-small);
  color: var(--muted);
  transition: color 0.15s;
}
.more:hover { color: var(--fg); }

/* Post list rows. One anchor, no nested links. */
.row {
  position: relative;
  display: grid;
  gap: 4px;
  padding-block: 1.15rem;
  border-bottom: 1px solid var(--line);
  transition: border-color 0.18s;
}
.row:hover { border-bottom-color: var(--muted); }

/* Hover cue: a short accent rule grows in at the leading edge. */
.row::before {
  content: "";
  position: absolute;
  left: -0.9rem;
  top: 1.05rem;
  bottom: 1.05rem;
  width: 2px;
  background: var(--fg);
  opacity: 0;
  transform: scaleY(0.35);
  transition: opacity var(--fast) ease, transform var(--mid) var(--ease);
}
.row:hover::before { opacity: 1; transform: scaleY(1); }

/* Title and a trailing badge share the first line. */
.row-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
}

.row-title {
  font-size: var(--fs-row);
  font-weight: 600;
  line-height: 1.35;
  letter-spacing: -0.01em;
  color: var(--fg);
  transition: color var(--fast) ease;
}

.row-sum {
  color: var(--muted);
  font-size: var(--fs-small);
  line-height: 1.6;
  max-width: var(--measure);
}

.row-meta { font-size: var(--fs-meta); color: var(--muted); }

/* Tags. */
.tags { display: flex; flex-wrap: wrap; gap: 0.4rem; }

.tag {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 2px 7px;
  font-size: var(--fs-meta);
  color: var(--muted);
  transition: border-color 0.15s, color 0.15s;
}
.tag:hover { border-color: var(--muted); color: var(--fg); }

.tag-n { color: color-mix(in oklab, var(--muted) 60%, transparent); font-variant-numeric: tabular-nums; }

.tag-list { margin-top: 1.75rem; }

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

.page-head { max-width: var(--measure); }

.page-title {
  margin-top: 0.7rem;
  font-size: var(--fs-h2);
  line-height: 1.15;
  letter-spacing: -0.025em;
  font-weight: 700;
  text-wrap: balance;
}

.page-lede { margin-top: 0.65rem; color: var(--muted); }

.crumb {
  font-size: var(--fs-meta);
  color: var(--muted);
  transition: color 0.15s;
}
.crumb:hover { color: var(--fg); }

/* --------------------------------------------------------------- article */

.post { display: grid; gap: 2.5rem; }

@media (min-width: 1024px) {
  .post { grid-template-columns: minmax(0, 1fr) 232px; gap: 3rem; }
}

.article { min-width: 0; max-width: var(--measure); }

/* A page with no sidebar can afford a longer line. */
.post.single .article { max-width: var(--measure-wide); }

.article-title {
  margin-top: 0.9rem;
  font-size: var(--fs-h2);
  line-height: 1.16;
  letter-spacing: -0.028em;
  font-weight: 700;
  text-wrap: balance;
}

.article-meta {
  margin-top: 1rem;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem 0.7rem;
  font-size: var(--fs-meta);
  color: var(--muted);
}
.article-meta a {
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  padding-bottom: 1px;
  transition: color 0.16s, border-color 0.16s;
}
.article-meta a:hover { color: var(--fg); border-color: var(--fg); }
.dot { width: 3px; height: 3px; border-radius: 50%; background: var(--line); }

.article .tags { margin-top: 1.1rem; }

.article-summary {
  margin-top: 1.9rem;
  border-left: 2px solid var(--line);
  padding-left: 1rem;
  color: var(--muted);
  line-height: 1.7;
}

/* ------------------------------------------------------------------ prose */

.prose {
  margin-top: 2.6rem;
  font-size: var(--fs-body);
  line-height: 1.78;
  color: var(--fg-soft);
  overflow-wrap: break-word;
}

.prose > * + * { margin-top: 1.35em; }

.prose h2 {
  margin-top: 2.6em;
  padding-top: 0.9em;
  border-top: 1px solid var(--line);
  font-size: var(--fs-lead);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--fg);
}

.prose h3 {
  margin-top: 2em;
  font-size: var(--fs-body);
  font-weight: 700;
  color: var(--fg);
}

.prose h4 { margin-top: 1.6em; font-weight: 600; color: var(--fg); }

.prose a {
  color: var(--fg);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in oklab, var(--muted) 55%, transparent);
  text-underline-offset: 3px;
  transition: text-decoration-color 0.16s, background-color 0.16s;
}
.prose a:hover {
  text-decoration-color: var(--fg);
  background: var(--panel);
  border-radius: 2px;
}

.prose strong { color: var(--fg); font-weight: 700; }

.prose ul, .prose ol { padding-left: 1.4em; }
.prose ul li { position: relative; }
.prose ul li::before {
  content: "-";
  position: absolute;
  left: -1.1em;
  color: var(--muted);
}
.prose ol { list-style: decimal; }
.prose ol li::marker { color: var(--muted); }
.prose li + li { margin-top: 0.4em; }

.prose blockquote {
  border-left: 2px solid var(--line);
  padding-left: 1rem;
  color: var(--muted);
}

.prose hr { margin-block: 2.75rem; border: 0; border-top: 1px solid var(--line); }

.prose code {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.05em 0.35em;
  font-size: 0.92em;
  color: var(--fg);
}

.prose pre {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--panel);
  padding: 0;
}

.prose pre code {
  display: block;
  border: 0;
  background: none;
  padding: 1rem 1.1rem;
  font-size: var(--fs-small);
  line-height: 1.65;
  color: var(--fg);
}

.prose table { width: 100%; border-collapse: collapse; font-size: var(--fs-small); }
.prose th, .prose td { border: 1px solid var(--line); padding: 0.45em 0.7em; text-align: left; }
.prose th { color: var(--fg); font-weight: 600; }

.prose img { border: 1px solid var(--line); border-radius: 4px; }
.prose picture { display: block; }
.prose figure img:not(a img) { cursor: zoom-in; }

/* Full size image preview, opened by app.js. */
.lightbox {
  width: fit-content;
  height: fit-content;
  max-width: none;
  max-height: none;
  margin: auto;
  padding: 0;
  border: 0;
  background: none;
  overflow: visible;
}
.lightbox img {
  max-width: 92vw;
  max-height: 92vh;
  border-radius: 4px;
  cursor: zoom-out;
}
.lightbox::backdrop {
  background: rgb(0 0 0 / 0.82);
  backdrop-filter: blur(2px);
}

.prose .footnotes { margin-top: 2.5rem; padding-top: 1rem; border-top: 1px solid var(--line); font-size: var(--fs-small); color: var(--muted); }

/* Heading permalinks. */
.header-anchor {
  margin-left: 0.45ch;
  color: var(--line);
  opacity: 0;
  transition: opacity 0.12s, color 0.12s;
}
h2:hover > .header-anchor, h3:hover > .header-anchor { opacity: 1; }
.header-anchor:hover { color: var(--muted); }

h1, h2, h3, h4 { scroll-margin-top: 80px; }

/* -------------------------------------------------------------------- toc */

.toc {
  display: none;
  position: sticky;
  top: 82px;
  align-self: start;
  max-height: calc(100svh - 7rem);
  overflow-y: auto;
  border-left: 1px solid var(--line);
  padding-left: 0.9rem;
  font-size: var(--fs-meta);
  line-height: 1.6;
}

@media (min-width: 1024px) { .toc { display: block; } }

.toc p { color: var(--muted); margin-bottom: 0.4rem; }
.toc a { display: block; color: var(--muted); transition: color 0.15s; padding-block: 0.12rem; }
.toc a:hover { color: var(--fg); }
.toc a.l3 { padding-left: 0.85rem; }

/* ------------------------------------------------------------- article nav */

.next {
  margin-top: 3.5rem;
  border-top: 1px solid var(--line);
  padding-top: 1.2rem;
  display: grid;
  gap: 1rem;
}
@media (min-width: 641px) { .next { grid-template-columns: 1fr 1fr; } }

.next a { display: grid; gap: 2px; }
.next-title { color: var(--fg); font-size: var(--fs-small); }
.next .right { text-align: right; }

/* ------------------------------------------------------------------ about */

.resume-head { max-width: var(--measure); }

.resume-role { margin-top: 0.6rem; color: var(--muted); }

.resume-note { margin-top: 0.9rem; color: var(--muted); font-size: var(--fs-small); }

.resume-head .links { margin-top: 1.35rem; }

.block { margin-top: 3rem; }

.label {
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--fg);
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--line);
}

.entry { padding-block: 1.35rem; border-bottom: 1px solid var(--line-soft); }
.entry:last-child { border-bottom: 0; padding-bottom: 0; }

.entry-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.3rem 1rem;
}

.entry-title { font-size: var(--fs-body); font-weight: 600; color: var(--fg); }
.entry-org { font-weight: 400; color: var(--muted); }
.entry-org::before { content: " · "; }
.entry-when { font-size: var(--fs-meta); color: var(--muted); font-variant-numeric: tabular-nums; }

.entry ul { margin-top: 0.6rem; padding-left: 1.1rem; }
.entry li { position: relative; color: var(--fg-soft); line-height: 1.65; }
.entry li + li { margin-top: 0.4rem; }
.entry li::before { content: "-"; position: absolute; left: -1.1rem; color: var(--muted); }

.skills { display: grid; gap: 1.3rem 2.5rem; margin-top: 1.2rem; }
@media (min-width: 641px) { .skills { grid-template-columns: 1fr 1fr; } }
.skills dt { font-size: var(--fs-meta); color: var(--muted); }
.skills dd { margin-top: 0.25rem; color: var(--fg-soft); line-height: 1.6; }

.facts {
  margin-top: 2.5rem;
  display: grid;
  border-top: 1px solid var(--line);
}
.facts > div {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: 1rem;
  padding-block: 0.55rem;
  border-bottom: 1px solid var(--line-soft);
  font-size: var(--fs-small);
}
.facts dt { color: var(--muted); }
.facts dd { color: var(--fg-soft); }

@media (min-width: 1024px) {
  .post.single { grid-template-columns: minmax(0, 1fr); }
}

/* ----------------------------------------------------------------- search */

.empty { padding-block: 2rem; color: var(--muted); }

/* -------------------------------------------------------------- 404/admin */

.notfound { padding-block: 4rem 2rem; max-width: 620px; }
.leaving-dest code { display: block; overflow-wrap: anywhere; color: var(--fg-soft); }

.form { display: grid; gap: 1.1rem; }
.field { display: grid; gap: 0.4rem; }
.field > span { font-size: var(--fs-meta); color: var(--muted); }

.field input, .field textarea, .field select {
  width: 100%;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--bg);
  color: var(--fg);
  font: inherit;
  font-size: var(--fs-small);
  padding: 0.55rem 0.7rem;
  outline: none;
  transition: border-color 0.15s;
}
.field input:focus, .field textarea:focus { border-color: var(--muted); }

/* Public or private. A radio pair rather than a checkbox, because there is no
   sensible "neither": a post is one or the other. */
.visibility {
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.75rem 0.9rem 0.85rem;
  gap: 0.55rem;
}
.visibility legend {
  padding-inline: 0.35rem;
  font-size: var(--fs-meta);
  color: var(--muted);
}
.vis-option {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  gap: 0.55rem;
  font-size: var(--fs-small);
  color: var(--fg-soft);
  cursor: pointer;
}
.vis-option input { width: auto; margin: 0; accent-color: var(--fg); }
.vis-option strong { color: var(--fg); font-weight: 600; }
.visibility legend { float: none; }
.field textarea { resize: vertical; line-height: 1.6; font-family: var(--serif); }

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.4rem 0.85rem;
  font-size: var(--fs-small);
  font-weight: 600;
  color: var(--fg);
  background: transparent;
  transition: border-color 0.15s, background 0.15s, transform 0.1s;
}
.btn:hover { border-color: var(--fg); background: var(--panel); color: var(--fg); }
.btn:active { transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--fg); outline-offset: 3px; }

/* .prose a is more specific than .btn, so a button inside prose would take the
   link colour and underline. Keep the button plain. */
.prose .btn { color: var(--fg); text-decoration: none; }
.prose .btn:hover { color: var(--fg); background: var(--panel); border-radius: 3px; }

.btn-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; }

.notify-actions { margin-top: 1.5rem; }
.notify-note { margin-top: 0.9rem; color: var(--muted); font-size: var(--fs-small); }
.notify-help {
  display: grid;
  gap: 0.85rem;
  margin-top: 1.5rem;
  max-width: var(--measure);
  font-size: var(--fs-small);
  line-height: 1.7;
  color: var(--fg-soft);
}

.alert {
  border: 1px solid var(--line);
  border-left: 2px solid var(--fg);
  padding: 0.6rem 0.85rem;
  font-size: var(--fs-small);
  color: var(--fg);
}

.admin-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.4rem 1rem;
  padding-block: 0.9rem;
  border-bottom: 1px solid var(--line-soft);
}

.admin-actions { display: flex; gap: 0.9rem; font-size: var(--fs-meta); color: var(--muted); }
.admin-actions a:hover, .admin-actions button:hover { color: var(--fg); }

.badge {
  font-size: var(--fs-meta);
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0 5px;
}

/* Shiki dual themes. */
.shiki, .shiki span { color: var(--shiki-light); }
html[data-theme="dark"] .shiki,
html[data-theme="dark"] .shiki span { color: var(--shiki-dark); }

.shiki {
  background: var(--panel) !important;
  border: 1px solid var(--line);
  border-radius: 4px;
}

.code-wrap { position: relative; }
.code-wrap .shiki { padding: 1rem 1.1rem; font-size: var(--fs-small); line-height: 1.65; }

.copy {
  position: absolute;
  top: 7px;
  right: 7px;
  border: 1px solid var(--line);
  border-radius: 3px;
  background: var(--bg);
  color: var(--muted);
  font: inherit;
  font-size: var(--fs-meta);
  padding: 1px 6px;
  opacity: 0;
  transition: opacity 0.12s, color 0.12s;
}
.code-wrap:hover .copy, .copy:focus-visible { opacity: 1; }
.copy:hover { color: var(--fg); }

/* ------------------------------------------------------- table of contents */

/* Mobile: a collapsible block above the body. Desktop: the sticky sidebar. */
.toc-mobile {
  margin-top: 2rem;
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0.7rem 0.9rem;
  font-size: var(--fs-small);
}
.toc-mobile summary {
  cursor: pointer;
  color: var(--muted);
  list-style: none;
}
.toc-mobile summary::-webkit-details-marker { display: none; }
.toc-mobile summary::after { content: " +"; color: var(--muted); }
.toc-mobile[open] summary::after { content: " −"; }
.toc-mobile nav { margin-top: 0.7rem; display: grid; gap: 0.35rem; }
.toc-mobile a { color: var(--muted); transition: color 0.15s; }
.toc-mobile a:hover { color: var(--fg); }
.toc-mobile a.l3 { padding-left: 0.9rem; }
@media (min-width: 1024px) { .toc-mobile { display: none; } }

/* Pagination, shared by the post index and comments. */
.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 2rem;
  font-size: var(--fs-small);
  color: var(--muted);
}
.pager a { display: inline-flex; align-items: center; gap: 4px; transition: color 0.15s; }
.pager a:hover { color: var(--fg); }

/* Admin stats. */
.stats { display: grid; gap: 3px; margin-top: 1rem; }
.stat-row { display: grid; grid-template-columns: 92px minmax(0, 1fr) 44px 60px; gap: 0.7rem; align-items: center; font-size: var(--fs-meta); color: var(--muted); }
.stat-bar { display: block; height: 6px; background: var(--fg); border-radius: 1px; min-width: 2px; }
.stat-line { display: flex; justify-content: space-between; gap: 1rem; padding-block: 0.4rem; border-bottom: 1px solid var(--line-soft); font-size: var(--fs-small); }

/* -------------------------------------------------------------- comments */

.comments { margin-top: 3.5rem; border-top: 1px solid var(--line); padding-top: 1.25rem; }

.comment {
  padding-block: 1.1rem;
  border-bottom: 1px solid var(--line-soft);
}
.comment:last-of-type { border-bottom: 0; }

.comment-head {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  font-size: var(--fs-meta);
}
.comment-author { font-weight: 600; color: var(--fg); font-size: var(--fs-small); }
.comment-author a { text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 3px; }
.comment-author a:hover { text-decoration-color: var(--fg); }
.comment-when { color: var(--muted); }
.comment-link { margin-left: auto; color: var(--line); transition: color 0.15s; }
.comment:hover .comment-link { color: var(--muted); }
.comment-link:hover { color: var(--fg); }

.comment-body {
  margin-top: 0.4rem;
  color: var(--fg-soft);
  font-size: var(--fs-small);
  line-height: 1.7;
  overflow-wrap: break-word;
}

.comment-empty { color: var(--muted); padding-block: 1rem; font-size: var(--fs-small); }

.comment-form { margin-top: 1.75rem; }
.comment-note { font-size: var(--fs-meta); color: var(--muted); }
#comment-cap-progress {
  display: block;
  width: 100%;
  max-width: 220px;
  height: 4px;
  margin-top: 0.5rem;
  accent-color: var(--fg);
}
#comment-cap-progress[hidden] { display: none; }

/* Comments: threading, replies, pagination. */
.comment-thread { border-bottom: 1px solid var(--line-soft); }
.comment-thread:last-of-type { border-bottom: 0; }

.comment-replies {
  margin-left: 1.35rem;
  padding-left: 1rem;
  border-left: 1px solid var(--line);
}

/* Reddit-style nesting: each answer sits under the comment it replied to.
   Deeper levels step in less, so long threads stay readable on a phone. */
.comment-replies .comment-replies {
  margin-left: 0.5rem;
  padding-left: 0.75rem;
}

.comment-reply { padding-block: 0.85rem; border-bottom: 0; }
.comment-reply:last-child { padding-bottom: 1rem; }

.comment-reply-btn {
  font: inherit;
  font-size: var(--fs-meta);
  color: var(--muted);
  transition: color 0.15s;
}
.comment-reply-btn:hover { color: var(--fg); }

.comment-replying {
  font-size: var(--fs-meta);
  color: var(--fg);
  border-left: 2px solid var(--fg);
  padding-left: 0.6rem;
  margin-bottom: 0.9rem;
}

.comment-pages {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.25rem;
  font-size: var(--fs-meta);
  color: var(--muted);
}
.comment-pages a { display: inline-flex; align-items: center; gap: 4px; transition: color 0.15s; }
.comment-pages a:hover { color: var(--fg); }

/* Honeypot: present for bots, invisible to people. Not display:none, because
   some bots skip hidden fields. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------------------------------------------------------------- share */

.share {
  position: relative;
  display: inline-block;
  margin-top: 1.5rem;
}
.share > summary { list-style: none; }
.share > summary::-webkit-details-marker { display: none; }

.share-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font: inherit;
  font-size: var(--fs-meta);
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 3px 8px;
  transition: border-color 0.15s, color 0.15s;
  cursor: pointer;
}
.share-btn:hover { border-color: var(--muted); color: var(--fg); }

/* Opens under the trigger, aligned to its left edge. */
.share-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 20;
  display: grid;
  min-width: 150px;
  padding: 4px;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 4px;
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.08);
}

.share-item {
  display: block;
  width: 100%;
  padding: 6px 9px;
  font-size: var(--fs-meta);
  color: var(--muted);
  text-align: left;
  border-radius: 3px;
  transition: color 0.15s, background 0.15s;
}
.share-item:hover { color: var(--fg); background: var(--panel); }

/* --------------------------------------------------------------- worklog */

/* -------------------------------------------------------------- worklog */

/* The list reuses .row, so both archives behave identically on hover. The
   status badge rides on the title line rather than in a second column, which
   is what left the old layout with a dead strip in the middle. */

/* Worklog status badge.
   Uppercase micro-label, tinted fill, and a leading marker whose shape carries
   the state. Small enough to sit in a row without competing with the title. */
.status {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  width: fit-content;
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.7;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  white-space: nowrap;
  border-radius: 3px;
  padding: 0 7px 0 6px;
  color: var(--muted);
  background: var(--panel);
  border: 1px solid var(--line);
}

/* The marker: a small square rotated to match the site's mark. */
.status::before {
  content: "";
  width: 5px;
  height: 5px;
  flex-shrink: 0;
  transform: rotate(45deg);
  background: currentColor;
}

/* In progress: filled, full contrast, on a slightly lifted fill. */
.status-in-progress {
  color: var(--fg);
  background: color-mix(in oklab, var(--fg) 7%, var(--bg));
  border-color: color-mix(in oklab, var(--fg) 22%, var(--line));
}

/* Stuck: hollow marker, so the shape reads as stopped. */
.status-stuck {
  color: var(--fg);
  background: transparent;
  border-color: color-mix(in oklab, var(--fg) 30%, var(--line));
}
.status-stuck::before {
  background: none;
  box-shadow: inset 0 0 0 1.5px currentColor;
}

/* Queued: quieter than the two above, marker hollow and pale. */
.status-next {
  color: var(--muted);
  background: transparent;
}
.status-next::before {
  background: none;
  box-shadow: inset 0 0 0 1.5px currentColor;
  opacity: 0.7;
}

@media (max-width: 560px) {
  .status { justify-self: start; }
}

.use-note { margin-top: 0.45rem; color: var(--fg-soft); font-size: var(--fs-small); line-height: 1.65; }

/* -------------------------------------------------------------- palette */

.palette {
  margin: 12vh auto auto;
  width: min(640px, calc(100vw - 2rem));
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--bg);
  color: var(--fg);
  box-shadow: 0 24px 70px -20px rgb(0 0 0 / 0.45);
}

.palette::backdrop {
  background: rgb(0 0 0 / 0.32);
  backdrop-filter: blur(3px);
}

.palette-input {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--line);
  color: var(--muted);
}

.palette-input input {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: none;
  background: none;
  color: var(--fg);
  font: inherit;
  font-size: var(--fs-body);
}
.palette-input input::placeholder { color: var(--muted); }

.palette-esc {
  font: inherit;
  font-size: var(--fs-meta);
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.05rem 0.4rem;
  transition: color 0.15s, border-color 0.15s;
}
.palette-esc:hover { color: var(--fg); border-color: var(--muted); }

.palette-results {
  max-height: min(52vh, 420px);
  overflow-y: auto;
  padding: 0.4rem;
}

.hit {
  display: grid;
  gap: 2px;
  padding: 0.55rem 0.65rem;
  border-radius: 4px;
  transition: background 0.12s;
}
.hit:hover, .hit:focus-visible { background: var(--panel); outline: none; }

.hit-title { font-size: var(--fs-small); font-weight: 600; color: var(--fg); }
.hit-meta { font-size: var(--fs-meta); color: var(--muted); }

.palette-foot {
  display: flex;
  gap: 1.1rem;
  padding: 0.55rem 1rem;
  border-top: 1px solid var(--line);
  font-size: var(--fs-meta);
  color: var(--muted);
}
.palette-foot kbd {
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0 4px;
  font-size: 11px;
}

.palette .empty { padding: 1.25rem 0.65rem; }

/* ---------------------------------------------------------------- motion */

/* One easing curve for everything, so the whole site feels like one object. */
/* One easing curve for everything, so the whole site feels like one object. */
:root {
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --fast: 140ms;
  --mid: 220ms;
  --slow: 380ms;
}

@keyframes fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes underline-in {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

/*
 * Motion is confined to interaction. Nothing animates on load or on
 * navigation: a page that fades in on every click reads as lag, and with
 * prerendering the next page is already painted when the click lands.
 */
@media (prefers-reduced-motion: no-preference) {
  /* Rows: the accent rule grows in, the summary lifts a shade. */
  .row-sum { transition: color var(--mid) var(--ease); }
  .row:hover .row-sum { color: var(--fg-soft); }

  /* The wordmark's diamond turns on hover. */
  .brand-mark { transition: transform var(--slow) var(--ease); }
  .brand:hover .brand-mark { transform: rotate(225deg); }

  .tag { transition: border-color var(--fast) ease, color var(--fast) ease; }

  .copy { transition: opacity var(--fast) ease, color var(--fast) ease, transform var(--fast) ease; }
  .copy:active { transform: scale(0.95); }

  /* Section headings draw their rule once, on the heading itself. */
  .label::after {
    content: "";
    display: block;
    height: 1px;
    margin-bottom: -0.6rem;
    background: var(--line);
    transform-origin: left;
    animation: underline-in var(--slow) var(--ease) both;
  }

  /* Palette and its backdrop arrive together. */
  .palette[open] { animation: fade var(--fast) ease both; }
  .palette::backdrop { animation: fade var(--fast) ease both; }

  /* Focus rings ease in rather than popping. */
  a:focus-visible, button:focus-visible {
    transition: outline-offset var(--fast) var(--ease);
    outline-offset: 3px;
  }
}

  /* Focus rings ease in rather than popping. */
  a:focus-visible, button:focus-visible {
    transition: outline-offset var(--fast) var(--ease);
    outline-offset: 3px;
  }
}

/* ------------------------------------------------------------------ loads */

.offline-note {
  border-bottom: 1px solid var(--line);
  border-left: 2px solid var(--fg);
  background: var(--panel);
  padding: 0.55rem var(--pad);
  font-size: var(--fs-small);
  text-align: center;
}

/* --------------------------------------------------------------- series */

.series {
  margin: 1.4rem 0;
  border: 1px solid var(--line);
  border-left: 2px solid var(--fg);
  padding: 0.8rem 1rem;
  background: var(--panel);
}
.series-kicker { font-size: var(--fs-small); color: var(--fg-soft); }
.series-kicker a { color: var(--fg); text-decoration: underline; text-underline-offset: 2px; }
.series-list { margin-top: 0.55rem; display: grid; gap: 0.3rem; }
.series-list a { display: flex; gap: 0.55rem; align-items: baseline; font-size: var(--fs-small); color: var(--fg-soft); }
.series-list a:hover { color: var(--fg); }
.series-list a[aria-current="page"] { color: var(--fg); font-weight: 600; }
.series-num {
  display: inline-grid;
  place-items: center;
  min-width: 1.35rem;
  height: 1.35rem;
  border: 1px solid var(--line);
  border-radius: 50%;
  font-size: var(--fs-meta);
  color: var(--fg-soft);
  flex: none;
}

/* ---------------------------------------------------- errata / changelog */

.errata {
  margin: 1rem 0;
  border-left: 2px solid var(--fg);
  padding: 0.55rem 0 0.55rem 0.8rem;
  font-size: var(--fs-small);
  color: var(--fg-soft);
}
.errata strong { color: var(--fg); }
.changelog {
  margin: 0.8rem 0;
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.55rem 0.75rem;
  font-size: var(--fs-small);
}
.changelog summary { cursor: pointer; color: var(--fg); font-weight: 600; }
.changelog ol { margin: 0.55rem 0 0 1.1rem; display: grid; gap: 0.3rem; }
.changelog li { color: var(--fg-soft); }
.changelog time { color: var(--fg); font-variant-numeric: tabular-nums; }

.related { margin-top: 2rem; }

/* ------------------------------------------------------------------ print */

@media print {
  .btn, .offline-note, .series, .changelog, .related, .errata { display: none; }
}