@import url('https://api.fontshare.com/v2/css?f[]=satoshi@900,700,500,400&display=swap');
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&display=swap');

:root {
  --background: #fafaf9;
  --surface: #f0efeb;
  --border: #e5e5e2;
  --text: #1a1a1a;
  --text-muted: #6b6b6b;
  --accent: #ff4f00;
  --accent-dark: #d34000;
}

* {
  box-sizing: border-box;
}

body {
  font-family:
    Satoshi,
    system-ui,
    -apple-system,
    sans-serif;
  max-width: 720px;
  margin: 0 auto;
  padding: 32px 24px;
  background: var(--background);
  color: var(--text);
  line-height: 1.6;
  font-size: 16px;
}

.masthead {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  margin-bottom: 48px;
}
.masthead-name {
  margin: 0;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
  text-decoration: none;
}
a.masthead-name:hover {
  color: var(--accent);
}
.parent-site {
  flex-shrink: 0;
  font-size: 14px;
  font-weight: 700;
  color: var(--accent);
  text-decoration: none;
  white-space: nowrap;
  border: 1.5px solid var(--accent);
  border-radius: 999px;
  padding: 6px 14px;
  transition:
    background-color 0.15s ease,
    color 0.15s ease;
}
.parent-site:hover {
  background-color: var(--accent);
  color: var(--background);
}

/* Language switcher — one item per language the page exists in, each
   named in its own language, the current one plain. Rendered only on
   blogs with more than one language: under the masthead on a home page,
   under the date on a post. Wraps rather than crowding the masthead row.
   `dir="auto"` on each item keeps an Arabic or Hebrew name from
   reordering its neighbours. */
.translations {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 16px;
  font-size: 14px;
  color: var(--text-muted);
}
/* Each name is a 44px-tall touch target (11px above and below a 22px
   line); the surrounding margins take the padding back so the text sits
   where a plain line would. Wrapped rows abut without overlapping. */
.translations > * {
  padding-block: 11px;
}
.masthead + .translations {
  margin: -43px 0 37px;
}
article header .translations {
  margin-top: -3px;
}
/* Underlined so the clickable names read as links on touch screens,
   where hover never happens; the current language is plain and darker. */
.translations a,
article .translations a {
  color: var(--text-muted);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.translations a:hover,
article .translations a:hover {
  color: var(--accent);
}
.translations [aria-current] {
  color: var(--text);
  font-weight: 500;
}

article header h1 {
  font-size: 32px;
  font-weight: 900;
  letter-spacing: -0.02em;
  line-height: 1.2;
  margin: 0 0 8px;
}
article header time {
  font-size: 14px;
  color: var(--text-muted);
}
article header img.cover {
  display: block;
  width: 100%;
  height: auto;
  margin: 24px 0 0;
  border-radius: 4px;
}

article {
  margin-bottom: 64px;
}
article p {
  font-size: 18px;
  margin: 16px 0;
}
article h2 {
  font-size: 24px;
  font-weight: 700;
  margin-top: 32px;
  letter-spacing: -0.01em;
}
article h3 {
  font-size: 20px;
  font-weight: 500;
  margin-top: 24px;
}
article a {
  color: var(--accent);
}
article a:hover {
  color: var(--accent-dark);
}
article ul,
article ol {
  padding-inline-start: 24px;
}
article li {
  margin: 8px 0;
}
article blockquote {
  border-inline-start: 4px solid var(--accent);
  padding-inline-start: 16px;
  color: var(--text-muted);
  margin: 24px 0;
}
article code {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 14px;
  background: var(--surface);
  padding: 2px 4px;
  border-radius: 4px;
}
article pre {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px;
  overflow-x: auto;
}
article pre code {
  background: transparent;
  padding: 0;
}
article img {
  /* Constrain to column width; preserve aspect ratio. Without this an
     800-960px Wikimedia thumb (or any phone-camera JPEG) blows past the
     720px body width. Centering matches the inline post-flow shape and
     a small radius matches the existing <pre> block. */
  max-width: 100%;
  height: auto;
  display: block;
  margin: 24px auto;
  border-radius: 8px;
}
article iframe {
  /* The only iframe a post can contain: the YouTube player renderMarkdown
     builds from a bare YouTube URL. Full column width at 16:9. */
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  border: 0;
  margin: 24px 0;
  border-radius: 8px;
}

/* Contents rail — a long post's sections (renderToc), hung in the margin
   beside the column on screens wide enough to have one. Below that it
   is hidden: on a phone or tablet it could only push the post down. The
   rail spans the post body and its list sticks while you read, then
   scrolls away with the body's end. One motion idea: a short accent
   marker that glides along the hairline to the section being read. */
.post-body {
  position: relative;
}
.post-body :is(h2, h3, h4, h5, h6) {
  scroll-margin-top: 32px;
}
.toc {
  display: none;
}
@media (min-width: 1240px) {
  .toc {
    display: block;
    position: absolute;
    inset-block: 0;
    inset-inline-end: calc(100% + 56px);
    width: 188px;
  }
  @media (prefers-reduced-motion: no-preference) {
    html:has(.toc) {
      scroll-behavior: smooth;
    }
  }
}
.toc ol {
  position: sticky;
  top: 96px;
  max-height: calc(100vh - 128px);
  overflow-y: auto;
  scrollbar-width: none;
  list-style: none;
  margin: 0;
  padding: 0;
}
/* The hairline is drawn by the items, not the list: the list scrolls when
   a post has more sections than fit, and a scroller clips anything drawn
   over its own border, which would halve the marker. */
.toc ol::before {
  content: '';
  position: absolute;
  top: 0;
  inset-inline-start: 0;
  width: 2px;
  height: var(--toc-h, 0);
  border-radius: 1px;
  background: var(--accent);
  transform: translateY(var(--toc-y, 0));
  opacity: 0;
  transition:
    transform 0.5s cubic-bezier(0.22, 1, 0.36, 1),
    height 0.5s cubic-bezier(0.22, 1, 0.36, 1),
    opacity 0.4s ease;
}
.toc ol:has([aria-current])::before {
  opacity: 1;
}
.toc li {
  margin: 0;
  border-inline-start: 1px solid var(--border);
}
.toc a {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  padding-block: 6px;
  padding-inline-start: 16px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--text-muted);
  text-decoration: none;
  transition: color 0.3s ease;
}
.toc a:hover,
.toc a[aria-current] {
  color: var(--text);
}
.toc a:focus-visible {
  outline: 1.5px solid var(--accent);
  outline-offset: -1.5px;
  border-radius: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .toc ol::before,
  .toc a {
    transition: none;
  }
}

.tags {
  margin-top: 32px;
}
.tags span {
  display: inline-block;
  background: var(--surface);
  color: var(--text-muted);
  font-size: 14px;
  padding: 4px 8px;
  border-radius: 4px;
  margin-inline-end: 4px;
}

/* "More from this blog" — three newest sibling posts at the end of a post.
   Scoped overrides: the block sits inside <article>, so the article's
   h2 / p / ul / li typography would otherwise apply. */
.more-from {
  margin-top: 48px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
}
.more-from h2 {
  margin: 0 0 16px;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.more-from ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.more-from li {
  position: relative;
  min-height: 44px;
  margin: 0 0 16px;
}
.more-from li a {
  color: var(--text);
  font-size: 17px;
  font-weight: 500;
  text-decoration: none;
}
/* Stretch the link's hit area over the whole item (title + blurb) so the
   tap target on mobile is at least 44px tall without changing the layout. */
.more-from li a::after {
  content: '';
  position: absolute;
  inset: 0;
}
.more-from li a:hover {
  color: var(--accent);
}
.more-from li p {
  margin: 4px 0 0;
  font-size: 15px;
  color: var(--text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

main .post-item {
  border-bottom: 1px solid var(--border);
  padding: 24px 0;
}
main .post-item:last-child {
  border-bottom: none;
}
main .post-item h2 {
  font-size: 20px;
  margin: 0 0 8px;
  font-weight: 500;
}
main .post-item h2 a {
  color: var(--text);
  text-decoration: none;
}
main .post-item h2 a:hover {
  color: var(--accent);
}
main .post-item time {
  font-size: 14px;
  color: var(--text-muted);
}
main .post-item p {
  color: var(--text-muted);
  margin: 8px 0 0;
  font-size: 16px;
}

footer {
  margin-top: 64px;
  font-size: 14px;
  color: var(--text-muted);
  text-align: center;
}
footer a {
  color: var(--text-muted);
}
footer a:hover {
  color: var(--text);
}

/* Empty state — rendered into {{{postList}}} when a blog has no
   published posts yet. One motion idea, executed precisely: a blank
   page that types its own status, caret left blinking. */
.empty-state {
  text-align: center;
  padding: 72px 0 24px;
}
.empty-type-line {
  width: 26ch;
  max-width: 100%;
  margin: 0 auto 32px;
  text-align: left;
  font-family: 'JetBrains Mono', monospace;
  font-size: 14px;
  color: var(--text-muted);
  min-height: 1.5em;
}
.empty-type {
  display: inline-block;
  white-space: nowrap;
  overflow: hidden;
  vertical-align: bottom;
  width: 26ch;
  border-right: 0.6ch solid var(--accent);
  animation:
    empty-typing 2.6s steps(26) 0.5s backwards,
    empty-caret 1.1s step-end infinite;
}
.empty-state h2 {
  font-size: 34px;
  font-weight: 900;
  letter-spacing: -0.02em;
  margin: 0 0 10px;
  animation: empty-rise 0.55s ease 0.2s backwards;
}
.empty-sub {
  color: var(--text-muted);
  margin: 0 0 36px;
  animation: empty-rise 0.55s ease 0.35s backwards;
}
.empty-owner {
  display: inline-block;
  text-align: left;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text);
  background: var(--surface);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
  padding: 14px 18px;
  max-width: 420px;
  margin: 0 auto;
  animation: empty-rise 0.55s ease 0.5s backwards;
}
.empty-owner strong {
  font-weight: 700;
}
@keyframes empty-typing {
  from {
    width: 0;
  }
  to {
    width: 26ch;
  }
}
@keyframes empty-caret {
  0%,
  100% {
    border-color: var(--accent);
  }
  50% {
    border-color: transparent;
  }
}
@keyframes empty-rise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .empty-type,
  .empty-state h2,
  .empty-sub,
  .empty-owner {
    animation: none;
  }
}
