@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;
}

/* Tables. renderMarkdown puts each one in a .table-scroll box: a wide
   table scrolls inside the column instead of widening the page, and the
   first column (a comparison's row labels) stays put while the rest
   slides under it. A soft shadow on the trailing edge says there is more
   to the side and fades once you reach the end (background-attachment:
   local covers it). Hairline rows, a tinted header, top-aligned cells. */
article .table-scroll {
  margin: 28px 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  border: 1px solid var(--border);
  border-radius: 10px;
  scrollbar-width: thin;
  background:
    linear-gradient(to left, var(--background) 40%, transparent) right center / 48px 100% no-repeat
      local,
    radial-gradient(farthest-side at 100% 50%, rgb(0 0 0 / 0.1), transparent) right center / 16px
      100% no-repeat scroll,
    var(--background);
}
article .table-scroll:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
article table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 15px;
  line-height: 1.5;
  font-variant-numeric: tabular-nums;
}
article th,
article td {
  padding: 12px 16px;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
  overflow-wrap: break-word;
}
/* Five columns or more (a comparison grid): tighter type and padding so
   it fits the 720px column on a laptop instead of scrolling. */
article table:has(tr > :nth-child(5)) {
  font-size: 14px;
}
article table:has(tr > :nth-child(5)) :is(th, td) {
  padding: 10px 12px;
}
article :is(th, td):not([align]) {
  text-align: start;
}
article tbody tr:last-child > * {
  border-bottom: 0;
}
article thead th {
  background: var(--surface);
  font-size: 14px;
  font-weight: 700;
  vertical-align: bottom;
}
article :is(th, td):first-child {
  position: sticky;
  inset-inline-start: 0;
  z-index: 1;
  background: var(--background);
  border-inline-end: 1px solid var(--border);
  font-weight: 500;
}
article thead th:first-child {
  background: var(--surface);
  font-weight: 700;
}
article td code {
  font-size: 13px;
  overflow-wrap: anywhere;
}
@media (hover: hover) {
  article tbody tr:hover > * {
    background: color-mix(in srgb, var(--surface) 55%, var(--background));
  }
}
@media (max-width: 640px) {
  /* Narrow screens: give columns a readable width and let the box
     scroll, rather than squeezing every cell to a word per line. */
  article th,
  article td {
    min-width: 10rem;
    padding: 10px 12px;
  }
  article :is(th, td):first-child {
    min-width: 6.5rem;
  }
}

/* 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: block;
  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;
}
/* Clamp the text, not the padded link: line-clamp clips at the padding
   box, so clamping the link let the top of a third line show in its
   bottom padding. Three lines fits a question-style heading. */
.toc a > span {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.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;
}

/* Live facts — one line under a post whose figures the publisher keeps
   current. A footnote, not a banner: small, muted, a hairline above.
   The class outranks `article p`, so the body's 18px never applies. */
.facts-note {
  margin: 40px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-muted);
}

/* "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;
  }
}
