/* Self-hosted Source Serif 4, latin subset. `optional` means the text never
   swaps fonts after first paint: if the file is not ready in time, the fallback
   serif is used for that load and the font is cached for the next one. */
@font-face {
  font-family: "Source Serif 4";
  font-style: normal;
  font-weight: 400 600;
  font-display: optional;
  src: url("/static/fonts/source-serif-4-latin.woff2") format("woff2");
}

@font-face {
  font-family: "Source Serif 4";
  font-style: italic;
  font-weight: 400;
  font-display: optional;
  src: url("/static/fonts/source-serif-4-italic-latin.woff2") format("woff2");
}

/* Nerd Font icon glyphs (Powerline, devicons, Font Awesome, etc.), subset
   from JetBrainsMono Nerd Font Mono. The unicode-range means the browser
   only fetches this file when a code block actually uses one of these
   private-use codepoints, so plain code blocks never pay for it. */
@font-face {
  font-family: "Nerd Font Icons";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/nerd-icons.woff2") format("woff2");
  unicode-range: U+23FB-23FE, U+2665, U+26A1, U+2B58, U+E000-E00A,
    U+E0A0-E0A3, U+E0B0-E0D4, U+E200-E2A9, U+E300-E3EB, U+E5FA-E6B7,
    U+E700-E7C5, U+EA60-EC1E, U+F000-F2FF, U+F300-F381, U+F400-F532;
}

/* Every color is written once as light-dark(light, dark). Which side is used
   follows `color-scheme`: the system setting by default, or the data-theme
   attribute the toggle sets on <html>. The dark side is kanagawa, so the
   page and the code blocks come from the same family. */
:root {
  color-scheme: light dark;
  --paper: light-dark(#ffffff, #16161d); /* sumiInk0 */
  --ink: light-dark(#000000, #ede8cf); /* fujiWhite, lifted for contrast */
  --muted: light-dark(#6f6f6f, #b5b2a9); /* dragonGray2, lifted for contrast */
  --quote: light-dark(#333333, #dcd7ba); /* fujiWhite */
  --hairline: light-dark(#e6e6e6, #363646); /* sumiInk3 */
  --underline: light-dark(#b8b8b8, #54546d); /* sumiInk4 */
  --inline-code: light-dark(#f0f0f0, #2a2a37); /* neutral gray / sumiInk2 */
  --code-bg: light-dark(#f6f6f6, #1f1f28); /* neutral gray / sumiInk1 */
  --code-fg: light-dark(#545464, #dcd7ba); /* lotusInk1 / fujiWhite */
  --code-scrollbar: light-dark(#a09cac, #54546d); /* lotusViolet1 / sumiInk4 */
  --code-border: light-dark(transparent, #2a2a37); /* sumiInk2 */
  --svg-invert: 0;
  --toggle-label: "Dark";
  --measure: 40rem;
  --gutter: 1.25rem;
}

:root[data-theme="light"] {
  color-scheme: light;
}

:root[data-theme="dark"] {
  color-scheme: dark;
}

/* Two values are not colors, so the dark condition is spelled out for them:
   the diagram inversion and the toggle's label. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --svg-invert: 0.88;
    --toggle-label: "Light";
  }
}

:root[data-theme="dark"] {
  --svg-invert: 0.88;
  --toggle-label: "Light";
}

* {
  box-sizing: border-box;
}

html {
  background: var(--paper);
}

body {
  margin: 0;
  color: var(--ink);
  font-family: "Source Serif 4", Georgia, "Times New Roman", serif;
  font-size: 1.125rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* Page frame: one column, left aligned. */
.site,
main {
  max-width: calc(var(--measure) + 2 * var(--gutter));
  margin: 0 auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

.site {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: 1.75rem;
  padding-bottom: 1.75rem;
  font-size: 0.9375rem;
}

.site a,
.theme-toggle {
  color: inherit;
  text-decoration: none;
}

.site a:hover,
.theme-toggle:hover {
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

/* Right side of the header: contact icons plus the theme toggle, grouped
   so they read as one cluster set apart from the Home link. */
.site-actions {
  display: flex;
  align-items: center;
  gap: 1.1rem;
}

.icon-link {
  display: inline-flex;
}

.icon-link svg {
  width: 1.05rem;
  height: 1.05rem;
}

/* An underline reads oddly under a bare glyph, so contact icons dim on
   hover instead of picking up the text-link underline above. */
.site-actions .icon-link:hover {
  text-decoration: none;
  opacity: 0.6;
}

/* The toggle is a plain text button. Its label comes from CSS, so it is
   right on first paint with no script involved. */
.theme-toggle {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
}

.theme-toggle::before {
  content: var(--toggle-label);
}

.theme-toggle:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

main {
  padding-top: 4rem;
  padding-bottom: 6rem;
}

/* Home: introduction, photo beside the text */
.intro {
  display: flex;
  align-items: flex-start;
  gap: 1.75rem;
  margin-bottom: 4.5rem;
}

/* The photo column: the headshot, with a short list of links under it.
   The column is as wide as its widest item, so a label never wraps. */
.intro-photo {
  flex: none;
}

.intro img {
  display: block;
  width: 7.5rem;
  height: auto;
  border-radius: 4px;
}

/* Each link is an icon beside its name. Only the name is underlined, like
   the other intro links. */
.intro-links {
  margin: 0.875rem 0 0;
  padding: 0;
  list-style: none;
}

.intro-links li + li {
  margin-top: 0.375rem;
}

.intro .intro-links a {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.9375rem;
  white-space: nowrap;
  text-decoration: none;
}

.intro-links svg {
  flex: none;
  width: 1.125rem;
  height: 1.125rem;
}

.intro-links span {
  text-decoration: underline;
  text-decoration-color: var(--underline);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

.intro-links a:hover span {
  text-decoration-color: var(--ink);
}

.intro h1 {
  margin: 0 0 0.75rem;
  font-size: 1.75rem;
  line-height: 1.2;
  font-weight: 600;
  letter-spacing: -0.015em;
}

.intro p {
  margin: 0 0 0.875rem;
}

.intro p:last-child {
  margin-bottom: 0;
}

.intro a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--underline);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

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

/* Home: the list of posts */
.section-title {
  margin: 0 0 2rem;
  font-size: 1.75rem;
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: -0.01em;
}

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

.posts > li {
  margin-bottom: 2.25rem;
}

.posts a {
  color: inherit;
  font-size: 1.25rem;
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-decoration: none;
  text-wrap: balance;
}

.posts a:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

.posts p {
  margin: 0.25rem 0 0;
  font-size: 1.0625rem;
  line-height: 1.5;
  color: var(--muted);
  text-wrap: pretty;
}

.posts time {
  display: block;
  margin-top: 0.375rem;
  font-size: 0.9375rem;
  color: var(--muted);
}

/* Tags read as small code tokens, tying them to the inline-code style
   used in post bodies rather than introducing a new color language. */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
}

.chip {
  padding: 0.1em 0.45em;
  border-radius: 3px;
  background: var(--inline-code);
  color: var(--muted);
  font-family:
    ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
  font-size: 0.75rem;
  line-height: 1.6;
}

.empty {
  margin: 0;
  color: var(--muted);
}

/* Post header */
.post-header {
  margin-bottom: 3rem;
}

.post-header h1 {
  margin: 0 0 0.75rem;
  font-size: 2.5rem;
  line-height: 1.15;
  font-weight: 600;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.post-header .dek {
  margin: 0 0 1.25rem;
  font-size: 1.25rem;
  line-height: 1.4;
  color: var(--muted);
  text-wrap: pretty;
}

.post-header time {
  display: block;
  font-size: 0.9375rem;
  color: var(--muted);
}

.body {
  overflow-wrap: break-word;
}

.body > :first-child {
  margin-top: 0;
}

.body h2 {
  margin: 3rem 0 1rem;
  font-size: 1.5rem;
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.body h3 {
  margin: 2rem 0 0.75rem;
  font-size: 1.125rem;
  line-height: 1.35;
  font-weight: 600;
}

.body h4 {
  margin: 1.5rem 0 0.5rem;
  font-size: 1rem;
  font-weight: 600;
}

.body p,
.body ul,
.body ol,
.body blockquote,
.body table {
  margin: 0 0 1.25rem;
}

.body ul,
.body ol {
  padding-left: 1.5rem;
}

.body li {
  margin-bottom: 0.375rem;
  padding-left: 0.25rem;
}

/* GFM task lists: the checkbox replaces the bullet. */
.body li:has(input[type="checkbox"]) {
  list-style: none;
}

.body li input[type="checkbox"] {
  margin: 0 0.5em 0 0;
  vertical-align: -0.1em;
}

.body a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--underline);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.15em;
}

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

.body strong {
  font-weight: 600;
}

.body blockquote {
  padding-left: 1.25rem;
  border-left: 2px solid var(--hairline);
  color: var(--quote);
}

.body blockquote p {
  margin-bottom: 0.75rem;
}

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

/* GitHub-style alerts (> [!NOTE] and friends). Same shape as a blockquote;
   the rule, icon, and title take a kanagawa hue so the kind reads at a glance. */
.body .markdown-alert {
  --alert: var(--ink);
  margin: 0 0 1.25rem;
  padding: 0.125rem 0 0.125rem 1.25rem;
  border-left: 2px solid var(--alert);
}

.body .markdown-alert > :last-child {
  margin-bottom: 0;
}

.body .markdown-alert-title {
  margin: 0 0 0.375rem;
  font-weight: 600;
  color: var(--alert);
}

.body .markdown-icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  margin-right: 0.5em;
  vertical-align: -0.125em;
  fill: currentColor;
}

.body .markdown-alert-note {
  --alert: #7e9cd8; /* crystalBlue */
}

.body .markdown-alert-tip {
  --alert: light-dark(#76946a, #98bb6c); /* autumnGreen / springGreen */
}

.body .markdown-alert-important {
  --alert: #957fb8; /* oniViolet */
}

.body .markdown-alert-warning {
  --alert: light-dark(#c0a36e, #e6c384); /* boatYellow2 / carpYellow */
}

.body .markdown-alert-caution {
  --alert: light-dark(#c34043, #ff5d62); /* autumnRed / peachRed */
}

/* Tables: rules only between rows. */
.body table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
  line-height: 1.5;
}

.body th,
.body td {
  padding: 0.5rem 1rem 0.5rem 0;
  border-bottom: 1px solid var(--hairline);
  text-align: left;
  vertical-align: top;
}

.body th {
  font-weight: 600;
}

/* Diagrams break out of the measure so their labels stay readable. */
.body p:has(> img:only-child) {
  margin: 0;
}

.body img {
  display: block;
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  width: min(100% + 12rem, 100vw - 2 * var(--gutter));
  max-width: none;
  height: auto;
  margin: 2.5rem 0;
}

/* The diagrams are dark strokes on a transparent background. In dark mode
   they invert most of the way, which gives fujiWhite strokes. Photos are
   not touched. */
.body img[src$=".svg"] {
  filter: invert(var(--svg-invert));
}

/* An SVG scales as one picture, so a wide diagram shrinks its labels past
   reading size on a phone. Wide diagrams therefore ship as a pair: a
   -wide.svg laid out across, and a -narrow.svg laid out down. The narrow
   one waits until the breakpoint below. Both names end in .svg, so the
   invert rule above still catches them. */
.body img[src$="-narrow.svg"] {
  display: none;
}

/* Code: kanagawa lotus in light mode, kanagawa wave in dark mode. */
.body code {
  font-family:
    ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono",
    "Nerd Font Icons", monospace;
  font-size: 0.85em;
}

.body :not(pre) > code {
  padding: 0.05em 0.2em;
  border-radius: 3px;
  background: var(--inline-code);
  overflow-wrap: break-word;
}

.body pre {
  margin: 1.75rem -1.5rem;
  padding: 1.25rem 1.5rem;
  border: 1px solid var(--code-border);
  border-radius: 6px;
  background: var(--code-bg);
  color: var(--code-fg);
  font-size: 0.875rem;
  line-height: 1.55;
  overflow-x: auto;
  scrollbar-color: var(--code-scrollbar) transparent;
  tab-size: 2;
}

.body pre code {
  font-size: inherit;
  padding: 0;
  background: none;
}

/* Token colors, one class per chroma token type. Each color is
   light-dark(kanagawa-lotus, kanagawa-wave), generated from chroma's two
   style files, so the tokens follow the page theme like everything else. */
.chroma .err { color: light-dark(#e82424, #e82424); }
.chroma .lnt { color: light-dark(#a09cac, #54546d); }
.chroma .ln { color: light-dark(#a09cac, #54546d); }
.chroma .k { color: light-dark(#624c83, #957fb8); }
.chroma .kc { color: light-dark(#cc6d00, #ffa066); }
.chroma .kd { color: light-dark(#624c83, #957fb8); }
.chroma .kn { color: light-dark(#77713f, #e6c384); }
.chroma .kp { color: light-dark(#624c83, #957fb8); }
.chroma .kr { color: light-dark(#624c83, #957fb8); }
.chroma .kt { color: light-dark(#597b75, #7aa89f); }
.chroma .na { color: light-dark(#77713f, #e6c384); }
.chroma .nc { color: light-dark(#597b75, #7aa89f); }
.chroma .no { color: light-dark(#cc6d00, #ffa066); }
.chroma .nd { color: light-dark(#cc6d00, #ffa066); font-weight: 600; }
.chroma .ni { color: light-dark(#77713f, #e6c384); }
.chroma .ne { color: light-dark(#cc6d00, #ffa066); }
.chroma .nl { color: light-dark(#6693bf, #7fb4ca); }
.chroma .nn { color: light-dark(#77713f, #e6c384); }
.chroma .py { color: light-dark(#77713f, #e6c384); }
.chroma .nt { color: light-dark(#4d699b, #7e9cd8); }
.chroma .nb { color: light-dark(#c84053, #e46876); }
.chroma .bp { color: light-dark(#c84053, #e46876); }
.chroma .nf { color: light-dark(#4d699b, #7e9cd8); }
.chroma .fm { color: light-dark(#4d699b, #7e9cd8); }
.chroma .s { color: light-dark(#6f894e, #98bb6c); }
.chroma .sa { color: light-dark(#c84053, #ff5d62); }
.chroma .sb { color: light-dark(#6f894e, #98bb6c); }
.chroma .sc { color: light-dark(#6f894e, #98bb6c); }
.chroma .dl { color: light-dark(#6693bf, #7fb4ca); }
.chroma .sd { color: light-dark(#8a8980, #727169); }
.chroma .s2 { color: light-dark(#6f894e, #98bb6c); }
.chroma .se { color: light-dark(#836f4a, #c0a36e); }
.chroma .sh { color: light-dark(#8a8980, #727169); }
.chroma .si { color: light-dark(#6693bf, #7fb4ca); }
.chroma .sx { color: light-dark(#6f894e, #98bb6c); }
.chroma .sr { color: light-dark(#836f4a, #c0a36e); }
.chroma .s1 { color: light-dark(#6f894e, #98bb6c); }
.chroma .ss { color: light-dark(#6f894e, #98bb6c); }
.chroma .m { color: light-dark(#b35b79, #d27e99); }
.chroma .mb { color: light-dark(#b35b79, #d27e99); }
.chroma .mf { color: light-dark(#b35b79, #d27e99); }
.chroma .mh { color: light-dark(#b35b79, #d27e99); }
.chroma .mi { color: light-dark(#b35b79, #d27e99); }
.chroma .il { color: light-dark(#b35b79, #d27e99); }
.chroma .mo { color: light-dark(#b35b79, #d27e99); }
.chroma .o { color: light-dark(#836f4a, #c0a36e); font-weight: 600; }
.chroma .ow { color: light-dark(#836f4a, #c0a36e); font-weight: 600; }
.chroma .or { color: light-dark(#836f4a, #c0a36e); font-weight: 600; }
.chroma .c { color: light-dark(#8a8980, #727169); font-style: italic; }
.chroma .ch { color: light-dark(#8a8980, #727169); font-style: italic; }
.chroma .cm { color: light-dark(#8a8980, #727169); font-style: italic; }
.chroma .c1 { color: light-dark(#8a8980, #727169); font-style: italic; }
.chroma .cs { color: light-dark(#8a8980, #727169); font-style: italic; }
.chroma .cp { color: light-dark(#c84053, #e46876); font-style: italic; }
.chroma .cpf { color: light-dark(#c84053, #e46876); font-weight: 600; font-style: italic; }
.chroma .gd { color: light-dark(#d7474b, #c34043); }
.chroma .ge { font-style: italic; }
.chroma .gr { color: light-dark(#e82424, #e82424); }
.chroma .gh { color: light-dark(#4d699b, #7e9cd8); font-weight: 600; }
.chroma .gi { color: light-dark(#6e915f, #76946a); }
.chroma .gs { font-weight: 600; }
.chroma .gu { color: light-dark(#4d699b, #7e9cd8); font-weight: 600; }
.chroma .gt { color: light-dark(#e82424, #e82424); }
.chroma .gl { text-decoration: underline; }

/* Post footer */
.post-footer {
  margin-top: 4rem;
  font-size: 0.9375rem;
  color: var(--muted);
}

@media (max-width: 44rem) {
  main {
    padding-top: 2.5rem;
    padding-bottom: 4rem;
  }

  .intro {
    flex-direction: column;
    gap: 1.25rem;
  }

  .intro h1 {
    font-size: 1.5rem;
  }

  .post-header h1 {
    font-size: 2rem;
  }

  .post-header .dek {
    font-size: 1.125rem;
  }

  .body h2 {
    font-size: 1.375rem;
  }

  .body pre {
    margin-left: calc(-1 * var(--gutter));
    margin-right: calc(-1 * var(--gutter));
    padding-left: var(--gutter);
    padding-right: var(--gutter);
    border-radius: 0;
  }

  /* Swap each wide diagram for its narrow companion. The cap keeps the
     narrow file near its drawn size just under the breakpoint, where the
     column is still about 660px wide. */
  .body img[src$="-wide.svg"] {
    display: none;
  }

  .body img[src$="-narrow.svg"] {
    display: block;
    width: min(100vw - 2 * var(--gutter), 26rem);
  }
}
