/* ─── Divider — double rule (tramline) ─── */
/* Two thin lines with a small gap between, classic editorial divider. */
main hr {
  border: 0;
  border-top: 1px solid color-mix(in srgb, var(--c-text) 8%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--c-text) 8%, transparent);
  height: 6px;
  width: 100%;
  max-width: var(--content-width);
  margin: 80px auto;
}

/* ─── Code blocks ─── */
/* Ported from brotzky.com:
   - Inline code: plain monospace 12px / 18px, no pill (matches
     code, pre rule in /styles/global.tsx).
   - Block code: dark band #131419 with content centered at the
     670px brotzky content width (matches /components/Code.tsx
     outer + inner divs). Shiki-highlighted blocks get their
     background from the theme's inline style; plain fallback
     blocks get it from the :not(.shiki) rule below. */
code {
  font-family: var(--f-mono);
  font-size: 13px;
  line-height: 0.8;
}
/* Block code: readable line-height. The earlier 0.8 was tight enough
   that empty Shiki lines collapsed to zero height, so the line-number
   gutter showed two counters stacked at the same Y. */
pre {
  font-family: var(--f-mono);
  font-size: 13px;
  line-height: var(--lh-prose);
}

/* Inline code — Slack-style pill. Subtle filled background + hairline
   border + tinted text, all derived from a single #1937A2 base so the
   three values agree visually. Block code is excluded via :not(pre) > code
   so the dark .post-code wrapper keeps its own treatment. */
main :not(pre) > code {
  font-family: var(--f-mono);
  font-size: 12px;
  color: #1937A2;
  background: color-mix(in srgb, #1937A2 7%, transparent);
  border: 1px solid color-mix(in srgb, #1937A2 22%, transparent);
  border-radius: 16px;
  padding: 1px 5px;
  /* Pill optical-aligns to the serif baseline. The 1px nudge keeps it
     centered on the x-height instead of riding high. */
  position: relative;
  bottom: 1px;
  vertical-align: baseline;
}
/* Dark mode — same hue, brighter foreground so it reads against the
   warm dark bg. */
:root.theme-dark main :not(pre) > code {
  color: #7e9be5;
  background: color-mix(in srgb, #7e9be5 14%, transparent);
  border-color: color-mix(in srgb, #7e9be5 30%, transparent);
}
/* Blockquote — 893 breakout matching code blocks. Pull-quote text
   spans the full container width. */
/* Traditional left-line blockquote — sits inside the 728px prose
   column, left-aligned, vertical rule on the leading edge. Same body
   size as the surrounding prose so it reads as a quoted aside, not a
   breakout pull-quote. */
main blockquote {
  position: relative;
  margin: 32px 0;
  padding: 4px 0 4px 20px;
  border-left: 2px solid color-mix(in srgb, var(--c-text) 30%, transparent);
  max-width: var(--content-width);
}
main blockquote p,
main blockquote li {
  font-family: var(--f-prose);
  font-size: 18px;
  line-height: var(--lh-prose);
  font-weight: 400;
  color: color-mix(in srgb, var(--c-text) 75%, transparent);
}
main blockquote p:not(:last-child) { margin-bottom: 12px; }

/* Code blocks break out to 893px. Breakout container (.post-code)
   carries the squircle and the noise overlay; the inner <pre> is just
   content. Code text aligns to the 728 prose column via symmetric
   82.5px gutters; line numbers float in the left gutter. */
main .post-code {
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  width: min(var(--breakout-width), 100vw);
  max-width: var(--breakout-width);
  margin: 16px 0;
  border-radius: var(--squircle-radius);
  corner-shape: var(--squircle-shape);
  -webkit-corner-shape: var(--squircle-shape-webkit);
  overflow: hidden;
  /* Default colors come from Shiki's CSS variables (per-token); the
     pre element below sets them on the surface itself. Posts rendered
     before dual-theme support keep their inline-styled dark colors. */
}
/* Subtle paper-grain overlay. Sits between the pre and the chrome,
   pointer-events disabled so it doesn't intercept the copy button.
   Inherits the squircle corner-shape so the grain follows the curve. */
main .post-code::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27160%27%20height%3D%27160%27%3E%3Cfilter%20id%3D%27n%27%3E%3CfeTurbulence%20type%3D%27fractalNoise%27%20baseFrequency%3D%270.85%27%20numOctaves%3D%272%27%20stitchTiles%3D%27stitch%27%2F%3E%3CfeColorMatrix%20type%3D%27saturate%27%20values%3D%270%27%2F%3E%3C%2Ffilter%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20filter%3D%27url(%23n)%27%2F%3E%3C%2Fsvg%3E");
  opacity: 0.06;
  mix-blend-mode: overlay;
  border-radius: inherit;
  corner-shape: inherit;
  -webkit-corner-shape: inherit;
}
main .post-code pre {
  position: relative;
  margin: 0;
  padding: 42px 0;
  overflow-x: auto;
  width: 100%;
  max-width: 100%;
}
/* Dual-theme color swap — Shiki's --shiki-light / --shiki-dark vars
   are set per-token; CSS picks the matching one based on theme class. */
main .post-code pre.shiki {
  background-color: var(--shiki-light-bg);
  color: var(--shiki-light);
}
main .post-code pre.shiki span { color: var(--shiki-light); }
:root.theme-dark main .post-code pre.shiki {
  background-color: var(--shiki-dark-bg);
  color: var(--shiki-dark);
}
:root.theme-dark main .post-code pre.shiki span { color: var(--shiki-dark); }
/* Plain (unhighlighted) fallback */
main .post-code pre:not(.shiki) {
  background: #131419;
  color: #e6e6e6;
}
main .post-code pre code {
  display: block;
  /* Extra ~80px of left padding so the code text + line-number gutter
     line up with the paragraph column above/below the codeblock. */
  padding: 0 82.5px 0 128px;
  counter-reset: code-line;
}
main .post-code pre.shiki .line {
  display: block;
  counter-increment: code-line;
  position: relative;
}
main .post-code pre.shiki .line::before {
  content: counter(code-line);
  position: absolute;
  left: -47px;
  width: 32px;
  text-align: left;
  color: color-mix(in srgb, currentColor 35%, transparent);
}
/* Empty lines have no children, so their inline box collapses to 0.
   Inject a zero-width space via ::after so they reserve a normal
   line-height worth of space and stay aligned with the gutter. */
main .post-code pre.shiki .line:empty::after {
  content: "\200B";
}

/* Always-visible copy button, positioned in the top-right gutter of
   the codeblock. */
.post-code-copy {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  color: currentColor;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: var(--radius-pill);
  cursor: pointer;
  opacity: 0.5;
  background: transparent;
  z-index: 3;
  transition: opacity var(--ease), background var(--ease);
}
.post-code-copy:hover {
  opacity: 1;
  background: color-mix(in srgb, currentColor 6%, transparent);
}
.post-code-copy-success {
  opacity: 1 !important;
  color: #4EBE96;
}

/* ─── Video block — S/M/L breakouts mirror images ─── */
/* The wrapper holds the <video> + a bottom-left play/pause toggle.
   /forms.js binds an intersection observer (pause when off-screen)
   and wires the toggle button. */
main .post-video {
  position: relative;
  margin: var(--media-margin) 0;
}
/* Frame anchors the absolute play/pause toggle to the video's
   bounding box, not the surrounding figure (which would include the
   caption and push the toggle below the video). */
main .post-video-frame {
  position: relative;
  display: block;
}
main .post-video.post-video-regular {
  width: min(var(--content-width), 100%);
  max-width: var(--content-width);
  margin-left: auto;
  margin-right: auto;
}
main .post-video.post-video-large,
main .post-video.post-video-xlarge {
  position: relative;
  left: 50%;
  transform: translateX(-50%);
}
main .post-video.post-video-large {
  width: min(var(--breakout-large), 100vw);
  max-width: var(--breakout-large);
}
main .post-video.post-video-xlarge {
  width: min(var(--breakout-xlarge), 100vw);
  max-width: var(--breakout-xlarge);
}
main .post-video-el {
  display: block;
  width: 100%;
  height: auto;
  max-width: 100%;
  margin: 0;
  border-radius: var(--squircle-radius);
  corner-shape: var(--squircle-shape);
  -webkit-corner-shape: var(--squircle-shape-webkit);
  /* Inverse of the hero border — hero uses rgba(255,255,255,.08)
     because heroes are typically dark on light. Videos are often the
     opposite (light recordings on light page bg). Bind to text color
     at low alpha so it adapts: dark border in light mode, light
     border in dark mode. Plus the same layered hero shadow for depth. */
  border: 1px solid color-mix(in srgb, var(--c-text) 12%, transparent);
}
/* Liquid-glass play/pause — Wealthsimple-style. Subtle linear-gradient
   fill + layered shadows + blur(10px) backdrop + a ::before gradient
   border via the mask content-box exclude trick. Inner SVG needs
   z-index to render above the ::before. */
main .post-video-toggle {
  position: absolute;
  bottom: 16px;
  left: 16px;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(
    182.51deg,
    rgba(255, 255, 255, 0.04) 27.09%,
    rgba(90, 90, 90, 0.04) 58.59%,
    rgba(0, 0, 0, 0.04) 92.75%
  );
  backdrop-filter: blur(var(--blur-glass));
  -webkit-backdrop-filter: blur(var(--blur-glass));
  border: none;
  color: var(--c-bg);
  border-radius: var(--radius-pill);
  cursor: pointer;
  overflow: hidden;
  z-index: 3;
  --gradientBorder-size: 1px;
  --gradientBorder-gradient: linear-gradient(
    178.8deg,
    rgba(255, 255, 255, 0.32) 10.85%,
    rgba(20, 20, 20, 0.46) 24.36%,
    rgba(50, 50, 50, 0.46) 73.67%,
    rgba(255, 255, 255, 0.46) 90.68%
  );
  box-shadow: var(--shadow-toggle);
  transition: background var(--ease), transform var(--ease);
}
main .post-video-toggle::before {
  content: "";
  pointer-events: none;
  user-select: none;
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: var(--gradientBorder-size, 1px);
  background: var(--gradientBorder-gradient);
  mask:
    linear-gradient(black, black) content-box exclude,
    linear-gradient(black, black);
  -webkit-mask:
    linear-gradient(black, black) content-box exclude,
    linear-gradient(black, black);
}
main .post-video-toggle > svg {
  position: relative;
  z-index: 1;
}
main .post-video-toggle:hover {
  background: linear-gradient(
    182.51deg,
    rgba(255, 255, 255, 0.08) 27.09%,
    rgba(90, 90, 90, 0.08) 58.59%,
    rgba(0, 0, 0, 0.08) 92.75%
  );
}
main .post-video-toggle:active { transform: scale(0.96); }

/* ─── Captions for row items + single videos ─── */
/* All four contexts (single image figure, image-row item, single video,
   video-row item) share the same figcaption typography. Single image
   figcaption is already styled above with a 728px max — these row
   captions instead constrain to the parent figure's width so they
   read like callouts under each tile. */
main .post-image-row figure figcaption,
main .post-video > figcaption,
main .post-video-row figure figcaption {
  font-family: var(--f-body);
  /* Mirror editor's .editor-image-row-caption (13px / 1.4) so row
     captions read identically in published and edit views. */
  font-size: 13px;
  line-height: 1.4;
  color: color-mix(in srgb, var(--c-text) 55%, transparent);
  text-align: center;
  margin: 16px 0 0;
}
main .post-image-row figure,
main .post-video-row figure {
  flex: 1;
  margin: 0;
  min-width: 0;
}

/* ─── Video rows (1-3 videos side-by-side) ─── */
/* Mirrors image-row geometry: 1 = prose column, 2 = 50/50, 3 = thirds.
   Sizes mirror image-row breakouts (regular → 728, large → 1140,
   xlarge → 1440). */
main .post-video-row {
  display: flex;
  gap: 16px;
  justify-content: center;
  margin: 32px 0;
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  width: min(var(--breakout-width), 100vw);
  max-width: var(--breakout-width);
}
main .post-video-row video,
main .post-video-row figure video {
  display: block;
  width: 100%;
  max-width: 100%;
  margin: 0;
  border-radius: var(--squircle-radius);
  corner-shape: var(--squircle-shape);
  -webkit-corner-shape: var(--squircle-shape-webkit);
  border: 1px solid color-mix(in srgb, var(--c-text) 12%, transparent);
}
main .post-video-row.post-video-row-1 video {
  width: min(var(--content-width), 100%);
  max-width: 100%;
}
main .post-video-row.post-video-row-size-large {
  width: min(var(--breakout-large), 100vw);
  max-width: var(--breakout-large);
}
main .post-video-row.post-video-row-size-xlarge {
  width: min(var(--breakout-xlarge), 100vw);
  max-width: var(--breakout-xlarge);
}
main .post-video-row.post-video-row-1.post-video-row-size-large video,
main .post-video-row.post-video-row-1.post-video-row-size-xlarge video {
  width: 100%;
  max-width: 100%;
}
main .post-video-row.post-video-row-2 video,
main .post-video-row.post-video-row-3 video {
  flex: 1;
  min-width: 0;
  width: 100%;
  height: auto;
  aspect-ratio: var(--aspect, 1);
  object-fit: cover;
}

/* ─── Image rows (1-3 images side-by-side) ─── */
/* 1 = centered at the prose-column width; 2 = 50/50; 3 = thirds. */
main .post-image-row {
  display: flex;
  gap: 16px;
  justify-content: center;
  margin: var(--media-margin) 0;
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  width: min(var(--breakout-width), 100vw);
  max-width: var(--breakout-width);
}
main .post-image-row img { margin: 0; }
main .post-image-row-1 img {
  width: min(var(--content-width), 100%);
  max-width: 100%;
  /* Figure has flex: 1 so it stretches to the row's 893 breakout.
     The img is capped at 728 — center it within that stretched
     figure (margin: 0 auto on a block element). */
  margin: 0 auto;
}
/* Size applies to any row count — row width grows to 1140 / 1440. */
main .post-image-row.post-image-row-size-large {
  width: min(var(--breakout-large), 100vw);
  max-width: var(--breakout-large);
}
main .post-image-row.post-image-row-size-xlarge {
  width: min(var(--breakout-xlarge), 100vw);
  max-width: var(--breakout-xlarge);
}
/* Single-image rows: at large/xlarge let the image fill the row
   (override the regular 728-cap). */
main .post-image-row-1.post-image-row-size-large img,
main .post-image-row-1.post-image-row-size-xlarge img {
  width: 100%;
  max-width: 100%;
}
main .post-image-row-2 img,
main .post-image-row-3 img {
  flex: 1;
  min-width: 0;
  width: 100%;
  height: auto;
  aspect-ratio: var(--aspect, 1);
  object-fit: cover;
}
/* 4+ images: grid layout. 4 = 2 cols, 5-9 = 3 cols (auto-flow rows).
   Each cell honors --aspect (square / 16:9 / etc.). */
main .post-image-row-4,
main .post-image-row-5,
main .post-image-row-6,
main .post-image-row-7,
main .post-image-row-8,
main .post-image-row-9 {
  display: grid;
  gap: 16px;
}
main .post-image-row-4 { grid-template-columns: 1fr 1fr; }
main .post-image-row-5,
main .post-image-row-6,
main .post-image-row-7,
main .post-image-row-8,
main .post-image-row-9 { grid-template-columns: 1fr 1fr 1fr; }
main .post-image-row-4 img,
main .post-image-row-5 img,
main .post-image-row-6 img,
main .post-image-row-7 img,
main .post-image-row-8 img,
main .post-image-row-9 img {
  width: 100%;
  height: auto;
  aspect-ratio: var(--aspect, 1);
  object-fit: cover;
}
main .post-image-row-4 figure,
main .post-image-row-5 figure,
main .post-image-row-6 figure,
main .post-image-row-7 figure,
main .post-image-row-8 figure,
main .post-image-row-9 figure {
  display: flex;
  flex-direction: column;
  gap: var(--gap-tight);
  flex: unset;
  margin: 0;
  min-width: 0;
}

/* ─── Demo blocks (interactive React embeds) ─── */
/* Same 893 breakout as code blocks. /demos.js mounts the matching
   component into each .post-demo div on page load. */
main .post-demo {
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  width: min(var(--breakout-width), 100vw);
  max-width: var(--breakout-width);
  margin: 64px 0;
}

/* ─── Newsletter form — frosted-glass pill ─── */
/* Same gradient-border masked-pseudo technique as the video toggle.
   Layered shadows, subtle linear-gradient wash, backdrop-blur, and a
   pill-shaped outer with the submit button as a smaller pill on the
   right. */
form[data-newsletter] {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 64px;
  padding: 12px 14px 12px 32px;
  width: 100%;
  background: #fff;
  box-shadow: var(--shadow-pill);
  --gradientBorder-size: 1px;
  --gradientBorder-gradient: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.25) 10%,
    rgba(20, 20, 20, 0.30) 25%,
    rgba(50, 50, 50, 0.30) 75%,
    rgba(255, 255, 255, 0.50) 90%
  );
  backdrop-filter: blur(var(--blur-glass));
  -webkit-backdrop-filter: blur(var(--blur-glass));
  border: 0;
  border-radius: var(--radius-pill);
  overflow: hidden;
}
/* Gradient border — same masked-padding-box trick used elsewhere. */
form[data-newsletter]::before {
  content: "";
  pointer-events: none;
  user-select: none;
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: var(--gradientBorder-size, 1px);
  background: var(--gradientBorder-gradient);
  -webkit-mask:
    linear-gradient(black, black) content-box exclude,
    linear-gradient(black, black);
  mask:
    linear-gradient(black, black) content-box exclude,
    linear-gradient(black, black);
}
/* Dark-mode override — keep the input field readable on a dark page
   while bumping shadow opacity for the same depth. */
:root.theme-dark form[data-newsletter] {
  background: #fff;
  box-shadow:
    0 30px 16px -8px rgba(0, 0, 0, 0.45),
    0 15px 8px -4px rgba(0, 0, 0, 0.25),
    0 6px 4px -2px rgba(0, 0, 0, 0.20);
}
/* Pill background is fixed white in both themes; input + button use
   absolute colors so they stay readable independent of theme. */
form[data-newsletter] input {
  flex: 1;
  min-width: 0;
  padding: 16px 0;
  font-family: var(--f-body);
  font-size: 17px;
  background: transparent;
  border: 0;
  color: #111;
  outline: none;
}
form[data-newsletter] input::placeholder {
  color: rgba(0, 0, 0, 0.40);
}
/* Subscribe button — black glass pill (btn--dark variant). Two
   masked-pseudo layers: ::before is the outer 1px rim, ::after is the
   inner top-edge highlight that gives the "lifted" glass look. */
form[data-newsletter] button {
  position: relative;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 18px 32px;
  font-family: var(--f-body);
  font-size: 16px;
  font-weight: 500;
  background: #0f152d;
  color: #fff;
  border: 0;
  border-radius: var(--radius-pill);
  box-shadow: 0 8px 16px rgba(15, 21, 45, 0.28);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  cursor: pointer;
  transition: box-shadow 0.2s ease;
  -webkit-tap-highlight-color: transparent;
  overflow: hidden;
}
form[data-newsletter] button:hover {
  background: #1c2347;
}
/* Outer rim — 1px gradient stroke around the button. */
form[data-newsletter] button::before {
  content: "";
  position: absolute;
  inset: 0;
  padding: 1px;
  border-radius: inherit;
  background: linear-gradient(146deg, #0f152d 9%, #06091a 57%, #0f152d 87%);
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
/* Inner stroke — white-to-transparent gradient on the top edge gives
   the lifted-glass highlight. Inset 1px so it sits inside ::before. */
form[data-newsletter] button::after {
  content: "";
  position: absolute;
  inset: 1px;
  padding: 1px;
  border-radius: inherit;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.50) 0%,
    rgba(255, 255, 255, 0)   25%,
    rgba(255, 255, 255, 0.20) 100%
  );
  -webkit-mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask:
    linear-gradient(#fff 0 0) content-box,
    linear-gradient(#fff 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
/* Status text after submit. Absolutely positioned so the empty span
   doesn't reserve space (and a section flex-gap) above the next
   element. :empty also hides any stray padding while idle. */
[data-newsletter-status] {
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: 32px;
  font-family: var(--f-body);
  font-size: var(--fs-meta);
  color: color-mix(in srgb, var(--c-text) 60%, transparent);
}
[data-newsletter-status]:empty {
  display: none;
}

/* ─── Site footer (full-width, light grey, border-top) ─── */
/* Sits OUTSIDE <main> so the band can span the viewport while its
   content centers within the prose column. Border-top replaces the
   old <hr class="post-footer-divider">. */
.site-footer {
  position: relative;
  z-index: 1;
  width: 100%;
  padding: var(--section-pad) 24px;
  background: #f5f5f5;
  overflow: hidden;
}
/* Soft whiteish radial-gradient glow centered on the footer — sits
   on top of the #f5f5f5 base and lifts the middle so the band reads
   as a designed surface rather than a flat slab. */
.site-footer::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse at top,
    rgba(255, 255, 255, 0.85),
    transparent 70%
  );
  pointer-events: none;
}
:root.theme-dark .site-footer {
  background: color-mix(in srgb, #d4a574 5%, var(--c-bg));
}
.site-footer-inner {
  position: relative;
  max-width: var(--content-width);
  margin: 0 auto;
  padding: 0 var(--gutter);
  display: flex;
  flex-direction: column;
  gap: 32px;
}

/* Site credit — sub-footer row sitting BELOW the grey .site-footer
   band on the page background. Avatar+name on the left, domain on
   the right. */
.site-credit {
  position: relative;
  z-index: 1;
  width: 100%;
  padding: 40px 24px;
  background: var(--c-bg);
}
.site-credit-inner {
  max-width: var(--content-width);
  margin: 0 auto;
  padding: 0 var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 64px;
  font-family: var(--f-body);
  font-size: var(--fs-meta);
  color: color-mix(in srgb, var(--c-text) 56%, transparent);
}
.site-credit-author,
.site-credit-domain {
  color: inherit;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: var(--gap-tight);
}
.site-credit-avatar {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  object-fit: cover;
  display: inline-block;
}
.site-credit-author:hover,
.site-credit-domain:hover {
  color: var(--c-text);
}
/* Legacy alias — older renders may still emit .post-footer */
.post-footer { display: flex; flex-direction: column; gap: 56px; }
.post-footer-section { position: relative; display: flex; flex-direction: column; gap: 32px; }
.post-footer-heading {
  font-family: var(--f-body);
  font-size: 16px;
  font-weight: 400;
  color: var(--c-text-soft);
  margin: 0;
}
.post-footer-prose {
  font-family: var(--f-prose);
  font-size: 28px;
  line-height: 1.5;
  color: var(--c-text);
  text-align: left;
  text-wrap: balance;
  margin: 0;
}
.post-footer-button {
  align-self: flex-start;
  padding: 12px 18px;
  font-family: var(--f-body);
  font-size: 15px;
  font-weight: 500;
  background: var(--c-text);
  color: var(--c-bg);
  border: 1px solid var(--c-text);
  border-radius: 6px;
  cursor: pointer;
}
.post-footer-button:hover { opacity: 0.85; }

/* ─── Architecture diagram (custom node) ─── */
/* ASCII-art diagrams rendered as a <pre> with inline color spans.
   CSS values mirror Linear's own ArchitectureDiagram component:
   font-size 14px, line-height 1.2, optimizeLegibility, tab-size 4 —
   so the box-drawing characters connect cleanly into continuous frame
   lines and the typography reads at the same density as their docs.
   Six color tiers map to existing tokens at the alpha levels that
   match their muted-on-bg hierarchy. */
main .post-diagram-wrap {
  position: relative;
  left: 50%;
  transform: translateX(-50%);
  width: min(1080px, 100vw);
  max-width: 1080px;
  margin: 64px 0;
  display: flex;
  justify-content: center;
  overflow-x: auto;
  padding: 0 var(--gutter);
}
main .post-diagram {
  margin: 0;
  padding: 0;
  background: transparent;
  /* Space Mono — the monospace bleedingedge.ai uses for technical
     diagrams. Loaded from Google Fonts in <head> with font-display:
     swap. Falls back to the project mono stack while loading. */
  font-family: "Space Mono", var(--f-mono);
  font-size: 14px;
  line-height: 1.2;
  letter-spacing: normal;
  font-feature-settings: normal;
  font-variation-settings: normal;
  text-rendering: optimizeLegibility;
  tab-size: 4;
  white-space: pre;
  /* Linear's diagram body color is a muted purple-gray. Map to ~58%
     of our text token so the contrast matches. */
  color: color-mix(in srgb, var(--c-text) 58%, transparent);
}
main .post-diagram code {
  display: block;
  margin: 0;
  padding: 0;
  background: transparent;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  color: inherit;
}

/* Color tiers, matched to Linear's hierarchy:
   - primary  : the strong labels inside frames (~text)
   - secondary: connective body text (~70% text)
   - border   : outermost frame (~hairline strong)
   - outer    : the title that sits on the outer border (~tertiary)
   - quaternary: inner frame lines (lightest hairline)
   - accent   : highlight characters (▓▒▒░ bars, "(N-1 cores)") */
.diagram-primary    { color: var(--c-text); }
.diagram-secondary  { color: color-mix(in srgb, var(--c-text) 70%, transparent); }
.diagram-border     { color: color-mix(in srgb, var(--c-text) 28%, transparent); }
.diagram-outer      { color: var(--c-text-soft); }
.diagram-quaternary { color: color-mix(in srgb, var(--c-text) 18%, transparent); }
.diagram-accent     { color: var(--c-accent); }