/* One family (IBM Plex Sans), headings set apart by size, weight and tiny caps; hairlines; red for hover and markers. */

:root {
  --bg: #fcfcfa;
  --fg: #161616;
  --muted: #66645f;
  --rule: #e4e3df;
  --underline: #c9c7c1;
  --accent: #c8231d;
  --string: #2b4c9b;
  /* links in a project's "offshoots" tree */
  --orange: #c2410c;
  --code-bg: #f4f4f1;
  --font: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-code: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --heading-weight: 600;
  --ui-size: 15px;
  --prose-size: 17px;
  --prose-leading: 1.65;
  /* about 75 characters per line */
  --measure: 38em;
  color-scheme: light;
}

/* A little smaller on desktop. */
@media (min-width: 900px) {
  :root {
    --ui-size: 14px;
    --prose-size: 16px;
  }
}

/* Light is the default; the header toggle sets data-theme="dark". */
:root[data-theme="dark"] {
  --bg: #121211;
  --fg: #e8e6e1;
  --muted: #9c9a94;
  --rule: #2c2b28;
  --underline: #55534e;
  --accent: #ff6b5e;
  --string: #8fb0ff;
  --orange: #fb923c;
  --code-bg: #1b1a18;
  color-scheme: dark;
}

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font: var(--ui-size) / 1.6 var(--font);
  -webkit-font-smoothing: antialiased;
}

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

.wrap {
  max-width: 1040px;
  margin: 0 auto;
  padding: 0 24px;
}

a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--underline);
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
}

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

a:focus-visible,
button:focus-visible,
input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

a.plain {
  text-decoration: none;
}

.muted {
  color: var(--muted);
}

.accent {
  color: var(--accent);
}

.accent-underline {
  text-decoration-color: var(--accent);
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
  font-size: 1em;
  font-weight: var(--heading-weight);
  line-height: 1.4;
}

strong,
b {
  font-weight: var(--heading-weight);
}

p {
  margin: 0 0 1em;
}

code,
pre,
kbd {
  font-family: var(--font-code);
}

/* Dates line up in lists. */
time,
.dated-list li > .muted:first-child,
.entry-row .muted:first-child {
  font-variant-numeric: tabular-nums;
}

/* header + footer */

.site-header {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-top: 40px;
}

.crumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 8px;
  min-height: 44px;
}

.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin: 0 -14px 0 auto;
  padding: 0;
  color: var(--muted);
  background: none;
  border: 0;
  cursor: pointer;
}

.theme-toggle:hover {
  color: var(--accent);
}

.theme-toggle .icon-sun,
:root[data-theme="dark"] .theme-toggle .icon-moon {
  display: none;
}

:root[data-theme="dark"] .theme-toggle .icon-sun {
  display: block;
}

.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0 20px;
}

.nav a,
.site-footer a,
.pager a,
.inline-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

.nav a[aria-current="page"] {
  color: var(--accent);
  text-decoration: none;
}

main {
  min-height: 50vh;
  padding-top: 56px;
}

.site-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 8px 24px;
  margin-top: 96px;
  padding: 16px 0 56px;
  border-top: 1px solid var(--rule);
}

/* shared */

.h {
  margin: 0 0 16px;
}

.h.rule {
  margin-bottom: 24px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--rule);
}

/* A page's main divisions: the sections of /projects, the years of /archives. */
.section-title {
  font-size: 1.3em;
  line-height: 1.3;
}

/* Smaller section labels ("projects", "writing", "related", "other"):
   tiny caps, about as tall as the lowercase letters around them. Counts and
   links inside stay in normal case at the normal size. */
.label {
  font-size: 0.8em;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.label .muted {
  font-size: 1.25em;
  letter-spacing: 0;
  text-transform: none;
}

.list,
.entry-list,
.dated-list,
.project-grid {
  margin: 0;
  padding: 0;
  list-style: none;
}

.list li {
  padding: 2px 0 2px 2ch;
  text-indent: -2ch;
}

.list li::before {
  content: "- ";
  color: var(--muted);
}

.dated-list li {
  display: flex;
  gap: 2ch;
  padding: 4px 0;
}

.dated-list li > .muted:first-child {
  flex: none;
}

.notice {
  margin: 0 0 24px;
  color: var(--accent);
}

/* The home intro and pages (about, inspiration) use the site's text size,
   not the larger size posts are read at. */
.prose.compact {
  font-size: var(--ui-size);
  line-height: 1.6;
}

/* home */

.prose.intro {
  max-width: 640px;
}

.home-section {
  max-width: 720px;
  margin-top: 56px;
}

/* posts and pages */

.entry-header {
  max-width: 72ch;
  margin-bottom: 40px;
}

.entry-header h1 {
  margin-bottom: 6px;
  font-size: 1.75em;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

/* Counts and labels inside headings stay regular weight. */
h1 .muted,
h2 .muted,
h3 .muted,
.h.muted {
  font-weight: 400;
}

.meta {
  margin: 0;
}

.lede {
  margin: 12px 0 0;
}

.entry-foot {
  max-width: 72ch;
  margin-top: 48px;
  padding-top: 16px;
  border-top: 1px solid var(--rule);
}

.related {
  margin-top: 8px;
}

/* home: the latest post, then the three before it */

.after-featured {
  margin-top: 20px;
}

/* home page project list */

.project-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.project-row {
  display: grid;
  grid-template-columns: 64px minmax(0, 1fr);
  align-items: start;
  gap: 0 14px;
  text-decoration: none;
}

/* 16:9, like the /projects cards, and like them letterboxed (contain) unless
   a project's thumb_style and thumb_bg say otherwise (inline style). */
.project-row-thumb {
  display: block;
  width: 64px;
  height: auto;
  aspect-ratio: 16 / 9;
  margin-top: 3px;
  object-fit: contain;
  object-position: center;
  background: var(--code-bg);
  border: 1px solid var(--rule);
}

/* Related posts under a post: posts have no thumb_style, and their first
   images come in every shape, so they fill the frame. */
.related-list .project-row-thumb {
  object-fit: cover;
}

.project-row-head,
.project-row-desc {
  display: block;
}

.project-row-head time {
  font-variant-numeric: tabular-nums;
}

/* " – 2021-02-23" wraps as a whole, never inside the date. */
.project-row-head > .muted {
  white-space: nowrap;
}

.project-row-title {
  font-weight: var(--heading-weight);
  text-decoration: underline;
  text-decoration-color: var(--underline);
  text-underline-offset: 4px;
}

.project-row:hover .project-row-title {
  color: var(--accent);
  text-decoration-color: currentColor;
}

/* project pages: the previous and next project in the same section */

/* Two columns, each like a row of the home page's project list; stacked on
   narrow screens. */
.project-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 24px 32px;
  max-width: 720px;
}

.project-nav-item {
  flex: 1 1 260px;
  min-width: 0;
}

.project-nav-label {
  margin: 0 0 10px;
}

/* /projects: a section's archived projects, under its grid. Like /archives,
   but the date column is as wide as its widest entry ("ongoing" or a date)
   so the titles line up. */

.archive-heading {
  margin: 56px 0 12px;
}

.archive-list {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: 2ch;
}

.archive-list li {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.popular-label {
  margin: 32px 0 4px;
}

/* draft mode (npm start only) */

.drafts-index {
  margin-bottom: 56px;
}

.has-devbar {
  padding-bottom: 64px;
}

.devbar {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 10;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 20px;
  padding: 8px 24px;
  color: var(--bg);
  background: var(--fg);
  font: 12px/1.5 var(--font);
}

.devbar a {
  color: inherit;
  text-decoration-color: currentColor;
}

.devbar-mode {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.devbar .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
}

.devbar-note {
  flex: 1 1 280px;
  opacity: 0.8;
}



.project-hero {
  display: block;
  width: 100%;
  max-width: var(--measure);
  height: auto;
  margin-bottom: 32px;
  border: 1px solid var(--rule);
}

.prose {
  max-width: var(--measure);
  font-size: var(--prose-size);
  line-height: var(--prose-leading);
  overflow-wrap: break-word;
}

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

.prose p,
.prose ul,
.prose ol,
.prose pre,
.prose blockquote,
.prose table,
.prose figure,
.prose video,
.prose iframe {
  margin: 0 0 1.1em;
}

.prose h1,
.prose h2,
.prose h3,
.prose h4,
.prose h5,
.prose h6 {
  margin: 2.2em 0 0.8em;
}

/* Heading levels by size; #### and below as tiny caps. All stay smaller
   than the page title. */
.prose h1 {
  font-size: 1.45em;
  line-height: 1.3;
}

.prose h2 {
  font-size: 1.25em;
  line-height: 1.35;
}

.prose h3 {
  font-size: 1.08em;
}

.prose h4,
.prose h5,
.prose h6 {
  font-size: 0.82em;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.prose img,
.prose video {
  display: block;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--rule);
}

.prose p > img {
  margin: 1.4em 0;
}

.prose a:has(> img) {
  text-decoration: none;
}

.prose iframe {
  max-width: 100%;
  border: 0;
}

/* YouTube and Vimeo players from bare video URLs */
.prose .video {
  aspect-ratio: 16 / 9;
  margin: 0 0 1.1em;
  background: var(--code-bg);
  border: 1px solid var(--rule);
}

.prose .video iframe {
  display: block;
  width: 100%;
  height: 100%;
  margin: 0;
}

.prose ul {
  padding-left: 2ch;
  list-style-type: "- ";
}

.prose ol {
  padding-left: 3ch;
}

.prose li::marker {
  color: var(--muted);
}

.prose li > p {
  margin-bottom: 0.5em;
}

.prose blockquote {
  position: relative;
  margin-left: 0;
  margin-right: 0;
  padding-left: 2ch;
  color: var(--muted);
}

.prose blockquote::before {
  content: ">";
  position: absolute;
  top: 0;
  left: 0;
  color: var(--accent);
}

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

.prose :not(pre) > code {
  padding: 0.1em 0.3em;
  background: var(--code-bg);
  font-size: 0.93em;
}

.prose pre {
  overflow-x: auto;
  padding: 14px 16px;
  background: var(--code-bg);
  border: 1px solid var(--rule);
  font-size: 13px;
  line-height: 1.55;
}

.prose hr {
  margin: 2.5em 0;
  border: 0;
  border-top: 1px solid var(--rule);
}

.prose table {
  display: block;
  overflow-x: auto;
  border-collapse: collapse;
}

.prose th,
.prose td {
  padding: 6px 10px;
  border: 1px solid var(--rule);
  text-align: left;
  vertical-align: top;
}

/* syntax highlighting (highlight.js class names) */

.hljs-comment,
.hljs-quote,
.hljs-meta {
  color: var(--muted);
  font-style: italic;
}

.hljs-keyword,
.hljs-selector-tag,
.hljs-literal,
.hljs-built_in,
.hljs-type,
.hljs-number,
.hljs-doctag {
  color: var(--accent);
}

.hljs-string,
.hljs-regexp,
.hljs-symbol,
.hljs-template-string,
.hljs-attr,
.hljs-attribute {
  color: var(--string);
}

/* blog list */

.entry-list {
  display: flex;
  flex-direction: column;
  gap: 32px;
}

.entry-row {
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  align-items: start;
  gap: 20px;
}

.entry-row p {
  margin: 0;
}

.entry-row-title {
  margin: 2px 0 4px;
}

.entry-thumb {
  display: block;
}

.entry-thumb img {
  display: block;
  width: 100%;
  /* height: auto so the width/height attributes don't override the crop */
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border: 1px solid var(--rule);
}

.pager {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  margin-top: 48px;
}

.archive-year {
  margin-top: 40px;
}

/* projects */

.projects-intro {
  max-width: 620px;
  margin-bottom: 24px;
}

.inline-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0 16px;
  margin: 8px 0 0;
}

.project-group {
  padding-top: 56px;
}

/* A section's text (content/projects/sections/<tag>.md), between its heading
   and its grid. */
.section-intro {
  margin-bottom: 40px;
}

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

.project-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr));
  gap: 48px 32px;
}

.project-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-decoration: none;
}

/* A 16:9 frame; the whole image shows inside it, letterboxed if it's another
   shape. A project's thumb_style and thumb_bg override this (inline style). */
.project-card img,
.thumb-placeholder {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  margin-bottom: 12px;
  object-fit: contain;
  object-position: center;
  background: var(--code-bg);
  border: 1px solid var(--rule);
}

.project-card-title {
  font-weight: var(--heading-weight);
}

.project-card:hover .project-card-title {
  color: var(--accent);
}

/* search */

.search-form label {
  display: block;
  margin-bottom: 8px;
}

.search-row {
  display: flex;
  gap: 8px;
  max-width: 560px;
  margin-bottom: 16px;
}

.search-row input {
  flex: 1;
  min-width: 0;
  min-height: 44px;
  padding: 10px 12px;
  color: inherit;
  background: transparent;
  border: 1px solid var(--underline);
  border-radius: 0;
  font: inherit;
}

.search-row button {
  min-height: 44px;
  padding: 0 16px;
  color: var(--bg);
  background: var(--fg);
  border: 0;
  font: inherit;
  cursor: pointer;
}

.search-row button:hover {
  background: var(--accent);
}

@media (max-width: 560px) {
  .wrap {
    padding: 0 16px;
  }

  main {
    padding-top: 40px;
  }

  .entry-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
  }

  .entry-thumb:empty {
    display: none;
  }
}

/* lightbox (assets/lightbox.js): images in posts, pages and projects open
   large on click, unless they're inside a link. */

.entry img:not(a[href] img) {
  cursor: zoom-in;
}

.lightbox {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 16px;
  background: transparent;
  border: 0;
  outline: none;
  cursor: zoom-out;
}

.lightbox[open] {
  display: flex;
  align-items: center;
  justify-content: center;
}

.lightbox::backdrop {
  background: rgb(0 0 0 / 0.94);
}

/* lightbox.js sets the size: fitted to the window, never past the real size. */
.lightbox img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/* Keep the page behind still while the lightbox is open. */
html:has(.lightbox[open]) {
  overflow: hidden;
}

/* ```related block (lib/project-tree.js): "offshoots", the projects that came
   out of this one, as a tree under its title, as wide as the text. A hairline
   frame with no fill, so it reads apart from both the text and the (filled)
   code blocks. The branches are borders on nested lists, so they join up at
   any font and size. */

.prose .project-tree {
  margin: 1.6em 0;
  padding: 14px 18px 16px;
  border: 1px solid var(--rule);
}

.prose .project-tree-head {
  position: relative;
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 8px;
}

/* The info icon by the label; its note shows only while the pointer is on
   the icon (or it has keyboard focus, or on phones, after a tap). The note
   itself ignores the pointer, so it never blocks the links under it. */
.tree-info-button {
  display: inline-flex;
  margin: -6px;
  padding: 6px;
  color: var(--muted);
  background: none;
  border: 0;
  cursor: help;
}

.tree-info-button:hover,
.tree-info-button:focus-visible {
  color: var(--fg);
}

.tree-info-tip {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 5;
  width: min(26em, 100%);
  padding: 10px 12px;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--rule);
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.08);
  font-size: var(--ui-size);
  line-height: 1.5;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s, visibility 0.12s;
}

.tree-info-button:hover + .tree-info-tip,
.tree-info-button:focus-visible + .tree-info-tip {
  opacity: 1;
  visibility: visible;
}

@media (hover: none) {
  .tree-info-button:focus + .tree-info-tip {
    opacity: 1;
    visibility: visible;
  }
}

.prose .project-tree .tree-root {
  margin: 0;
  font-weight: var(--heading-weight);
}

.prose .project-tree .tree {
  margin: 0;
  padding: 0;
  list-style: none;
}

.prose .project-tree .tree li {
  position: relative;
  margin: 0;
  padding-left: 1.5em;
}

/* The line down to the next item... */
.prose .project-tree .tree li::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0.4em;
  border-left: 1px solid var(--muted);
}

/* ...starting right under the title... */
.prose .project-tree .tree-root + .tree > li:first-child::before {
  top: -0.3em;
}

/* ...and stopping at the last item's elbow. */
.prose .project-tree .tree li:last-child::before {
  bottom: auto;
  height: 0.82em;
}

.prose .project-tree .tree-root + .tree > li:first-child:last-child::before {
  height: 1.12em;
}

/* The elbow, level with the middle of the item's first line. */
.prose .project-tree .tree li::after {
  content: "";
  position: absolute;
  top: 0.82em;
  left: 0.4em;
  width: 0.75em;
  border-top: 1px solid var(--muted);
}

.prose .project-tree a {
  color: var(--orange);
  text-decoration-color: currentColor;
}

.prose .project-tree a:hover {
  color: var(--orange);
  text-decoration-thickness: 2px;
}

/* An arrow after each link (inline-block, so it isn't underlined): → to a
   project, ↗ out of the site, as elsewhere on the site. */
.prose .project-tree a::after {
  content: "→";
  display: inline-block;
  margin-left: 0.3em;
}

.prose .project-tree a.tree-external::after {
  content: "↗";
}

/* By the title: a right-then-up arrow to each project this one came out of.
   Just the arrow (underlined on hover); its card says which project. */
.prose .project-tree .tree-parent {
  position: relative;
  display: inline-block;
  margin-left: 0.5em;
  padding: 0 0.1em 2px;
  font-weight: 400;
  line-height: 1;
  vertical-align: -0.15em;
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

.prose .project-tree .tree-parent:hover,
.prose .project-tree .tree-parent:focus-visible {
  border-bottom-color: currentColor;
}

.prose .project-tree .tree-parent::after {
  content: none;
}

.tree-parent-icon {
  display: block;
  width: 0.95em;
  height: 0.95em;
}

/* The card: thumbnail (16:9, the project's thumb_style/thumb_bg), title and
   summary. Shown on hover where there is hover, and on keyboard focus. It
   ignores the pointer, so only the link itself is hovered or clicked, and the
   card never blocks what's under it. */
.tree-card {
  position: absolute;
  top: calc(100% + 8px);
  left: -12px;
  z-index: 5;
  display: none;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: 12px;
  width: 22rem;
  max-width: calc(100vw - 48px);
  padding: 12px;
  color: var(--fg);
  background: var(--bg);
  border: 1px solid var(--rule);
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.08);
  font-size: var(--ui-size);
  line-height: 1.45;
  text-align: left;
  white-space: normal;
  pointer-events: none;
}

/* A child project's link is the card's anchor too. */
.prose .project-tree .tree-project {
  position: relative;
}

/* Under a project page's title: "⮥ zinc" to each project it came out of,
   in the offshoots orange, with the same card on hover. */
.project-parents {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin: 0 0 6px;
}

.project-parents .tree-parent {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  color: var(--orange);
  text-decoration: none;
}

.project-parents .tree-parent-name {
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-underline-offset: 4px;
}

.project-parents .tree-parent:hover {
  color: var(--orange);
}

.project-parents .tree-parent:hover .tree-parent-name {
  text-decoration-thickness: 2px;
}

@media (hover: hover) {
  .tree-parent:hover .tree-card,
  .tree-project:hover .tree-card {
    display: grid;
  }
}

.tree-parent:focus-visible .tree-card,
.tree-project:focus-visible .tree-card {
  display: grid;
}

.tree-card-thumb {
  display: block;
  width: 96px;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: contain;
  object-position: center;
  background: var(--code-bg);
  border: 1px solid var(--rule);
}

.tree-card-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.tree-card-title {
  font-weight: var(--heading-weight);
}

.tree-card-summary {
  color: var(--muted);
}
