:root {
  --brand: 217 91% 56%;
  --brand-2: 268 80% 63%;
  --accent-warm: 28 92% 55%;
  --paper: 36 33% 98%;
  --card: 0 0% 100%;
  --ink: 224 32% 12%;
  --ink-soft: 224 14% 40%;
  --rule: 28 16% 88%;
  --radius: 12px;
  /* Matches the marketing container (tailwind.config.js screens.2xl) so the
     blog nav, footer and card grid line up with the rest of the site instead
     of sitting in a narrower column of their own. */
  --shell: 1400px;
}

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

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: hsl(var(--paper));
  color: hsl(var(--ink));
  font-family: 'Inter Tight', Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-size: 17px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.shell { width: 100%; max-width: 760px; margin: 0 auto; padding: 0 24px; }
/* The index fills the window rather than sitting in a fixed column. A fixed
   max-width only ever looks right at one screen size: 1400px left a quarter of
   a 1920px display empty down each side, and more on anything wider. */
.shell-wide {
  max-width: 100%;
  padding-inline: clamp(28px, 5.5vw, 140px);
}

a { color: hsl(var(--brand)); text-decoration: none; }
a:hover { text-decoration: underline; }
img { max-width: 100%; height: auto; display: block; }
:focus-visible { outline: 2px solid hsl(var(--brand)); outline-offset: 2px; border-radius: 4px; }

h1, h2, h3, h4 {
  font-family: Fraunces, ui-serif, Georgia, 'Times New Roman', serif;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: hsl(var(--ink));
}

.eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: hsl(var(--brand));
  margin: 0 0 10px;
}

/* ── header ─────────────────────────────────────────────────────────── */
.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  /* Opaque by default, ON PURPOSE. The frosted-glass treatment below is an
     enhancement, not the baseline: a translucent header is only legible
     because backdrop-filter blurs what sits behind it, and when that filter
     does not apply the article scrolls straight through the nav at 85%
     opacity and the header becomes unreadable. That is not a rare edge case:
     Safari needs the -webkit- prefix (missing here until now), and any
     browser or machine that drops filter compositing loses it too. The SPA
     header already guards this with Tailwind's supports-[backdrop-filter]
     variant; this one did not. */
  background: hsl(var(--paper));
  border-bottom: 1px solid hsl(var(--rule));
}
@supports ((backdrop-filter: blur(12px)) or (-webkit-backdrop-filter: blur(12px))) {
  .site-header {
    /* 0.96, not 0.85: @supports only reports whether the browser KNOWS the
       property, not whether it actually composites it. A GPU-blacklisted or
       software-rendered client answers yes and then paints no blur at all, so
       the alpha has to be high enough to stay opaque on its own. */
    background: hsl(var(--paper) / 0.96);
    -webkit-backdrop-filter: saturate(180%) blur(12px);
    backdrop-filter: saturate(180%) blur(12px);
  }
}
/* Geometry copied from the SPA marketing header (RootLayout.tsx: "container
   flex h-16 items-center justify-between") and NOT from .shell/.shell-wide,
   which the rest of this page uses. The two headers have to land on the same
   pixels: /blog is server-rendered here while / and /pricing are React, so a
   visitor clicking between them sees one nav that must not move. Tailwind's
   "container" is centred, capped at 1400px (2xl) and padded 1rem — which is
   what --shell already holds; h-16 is 64px, not the 68px this used to be.

   Deliberately independent of .shell-wide: the index grid stays full-bleed on
   purpose (see .shell-wide), and letting the header inherit that pulled the
   logo to the viewport edge, ~170px left of where the SPA puts it. */
.header-inner {
  /* Three explicit columns, matching the SPA header. Not space-between: that
     placed the nav relative to its neighbours, and this header's right-hand
     cluster is 49px narrower than the SPA's (no language switcher), which slid
     the links 24px sideways when you moved between /blog and /pricing. The
     middle column is centred on the row, so both agree regardless of what sits
     either side, and the column survives the nav going display:none under 768. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  height: 64px;
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: 16px;
}
.header-inner > .logo { grid-column: 1; justify-self: start; }
.header-inner > .site-nav { grid-column: 2; justify-self: center; }
.header-inner > .header-cta { grid-column: 3; justify-self: end; }
/* Header type is sized in px, not rem, and mirrors the SPA header's Tailwind
   utilities one-for-one. rem here resolves against a 17px body — the blog's
   reading size — which made every element of the chrome land a fraction larger
   and heavier than the same element on / and /pricing, so the nav visibly
   changed weight when you crossed between them. Body copy below keeps rem. */
.logo {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 16px;
  line-height: 24px;
  letter-spacing: -0.025em;
  color: hsl(var(--ink));
}
.logo:hover { text-decoration: none; }
/* No "margin-right: auto" — that pinned the links against the logo, while the
   SPA centres them between logo and CTA via justify-content on the row. */
.site-nav { display: flex; gap: 4px; }
/* h-9 px-3 rounded-md text-sm, i.e. the SPA's nav link. */
.site-nav a {
  display: inline-flex;
  align-items: center;
  height: 36px;
  color: hsl(var(--ink-soft));
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  padding: 0 12px;
  border-radius: 6px;
  transition: background-color 0.15s, color 0.15s;
}
.site-nav a:hover { background: hsl(var(--ink) / 0.05); color: hsl(var(--ink)); text-decoration: none; }
.site-nav a.active { color: hsl(var(--ink)); background: hsl(var(--ink) / 0.06); }
.header-cta { display: flex; align-items: center; gap: 8px; font-size: 14px; line-height: 20px; }
.signin { color: hsl(var(--ink-soft)); font-weight: 500; padding: 7px 10px; }
.signin:hover { color: hsl(var(--ink)); text-decoration: none; }
/* Scoped to the header so the in-article and side-CTA buttons, which are sized
   for their own contexts, keep the .btn defaults below. */
.header-cta .btn {
  display: inline-flex;
  align-items: center;
  height: 36px;
  padding: 0 12px;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
}

.btn {
  display: inline-block;
  background-image: linear-gradient(135deg, hsl(var(--brand)), hsl(var(--brand-2)));
  color: #fff;
  padding: 9px 16px;
  border-radius: 9px;
  font-weight: 600;
  font-size: 0.92rem;
  box-shadow: 0 1px 2px hsl(var(--ink) / 0.08), 0 6px 18px hsl(var(--brand) / 0.22);
  transition: transform 0.15s, box-shadow 0.15s;
}
.btn:hover {
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: 0 2px 4px hsl(var(--ink) / 0.1), 0 10px 24px hsl(var(--brand) / 0.3);
}
.btn-block { display: block; text-align: center; width: 100%; }

/* ── index ──────────────────────────────────────────────────────────── */
.page-head { padding: 64px 0 4px; }
.page-head h1 { font-size: clamp(2.1rem, 5vw, 3rem); line-height: 1.08; margin: 0 0 14px; }
.lede { color: hsl(var(--ink-soft)); font-size: 1.1rem; margin: 0; max-width: 62ch; }

.topics { display: flex; flex-wrap: wrap; gap: 8px; margin: 30px 0 6px; }
.tags { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 12px; }
.tag {
  display: inline-block;
  padding: 4px 11px;
  border: 1px solid hsl(var(--rule));
  border-radius: 999px;
  font-size: 0.76rem;
  font-weight: 500;
  color: hsl(var(--ink-soft));
  background: hsl(var(--card));
  transition: border-color 0.15s, color 0.15s;
}
.tag:hover, .tag.active {
  border-color: hsl(var(--brand) / 0.5);
  color: hsl(var(--brand));
  background: hsl(var(--brand) / 0.05);
  text-decoration: none;
}

.cards { display: grid; gap: 26px; }
.cards-lead { margin: 34px 0 26px; }
.cards-grid { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); margin-bottom: 40px; }

.card {
  border: 1px solid hsl(var(--rule));
  border-radius: var(--radius);
  overflow: hidden;
  background: hsl(var(--card));
  box-shadow: 0 1px 2px hsl(var(--ink) / 0.04);
  transition: transform 0.18s, box-shadow 0.18s, border-color 0.18s;
}
.card:hover {
  transform: translateY(-3px);
  border-color: hsl(var(--brand) / 0.35);
  box-shadow: 0 10px 30px hsl(var(--ink) / 0.09);
}
.card-cover { display: block; background: hsl(var(--ink) / 0.04); }
.card-cover img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.card-cover-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 16 / 9;
  font-family: Fraunces, ui-serif, Georgia, serif;
  font-size: 3rem;
  color: #fff;
  background-image: linear-gradient(135deg, hsl(var(--brand)), hsl(var(--brand-2)));
}
.card-body { padding: 20px 22px 22px; }
.card-title { margin: 0 0 9px; font-size: 1.3rem; line-height: 1.25; }
.card-title a { color: hsl(var(--ink)); }
.card-title a:hover { color: hsl(var(--brand)); text-decoration: none; }
.excerpt { color: hsl(var(--ink-soft)); margin: 0 0 14px; font-size: 0.97rem; }
.meta {
  color: hsl(var(--ink-soft));
  font-size: 0.83rem;
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
  margin: 0;
}
.empty { color: hsl(var(--ink-soft)); padding: 48px 0; }

/* Featured lead card: image beside copy on wide screens. */
@media (min-width: 820px) {
  .card-featured { display: grid; grid-template-columns: 1.05fr 1fr; align-items: stretch; }
  .card-featured .card-cover { height: 100%; }
  .card-featured .card-cover img, .card-featured .card-cover-fallback { height: 100%; aspect-ratio: auto; min-height: 260px; }
  .card-featured .card-body { padding: 30px 32px; align-self: center; }
  .card-featured .card-title { font-size: 1.72rem; }
  .card-featured .excerpt { font-size: 1.02rem; }
}

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 999px;
  background-image: linear-gradient(135deg, hsl(var(--brand)), hsl(var(--brand-2)));
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
}

/* ── article ────────────────────────────────────────────────────────── */
.breadcrumb {
  font-size: 0.83rem;
  color: hsl(var(--ink-soft));
  padding: 30px 0 0;
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.breadcrumb a { color: hsl(var(--ink-soft)); }
.breadcrumb a:hover { color: hsl(var(--brand)); }
.breadcrumb span:last-child {
  color: hsl(var(--ink));
  max-width: 46ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.post-head { padding: 22px 0 8px; }
.post-head h1 { font-size: clamp(2.1rem, 4.6vw, 3.15rem); line-height: 1.06; margin: 0 0 16px; }
.post-lede { font-size: 1.16rem; color: hsl(var(--ink-soft)); margin: 0 0 24px; }
.byline { display: flex; align-items: center; gap: 11px; }
.byline .avatar { width: 38px; height: 38px; font-size: 0.95rem; }
.byline-name { display: block; font-weight: 600; font-size: 0.94rem; }
.byline-meta { display: block; color: hsl(var(--ink-soft)); font-size: 0.85rem; }

.hero { margin: 34px 0 8px; }
.hero img { width: 100%; border-radius: 14px; border: 1px solid hsl(var(--rule)); }

/* Two-column: article + sticky sidebar.
   The sidebar is second in the DOM so screen readers and crawlers reach the
   article first. On narrow screens the order property pulls it ABOVE the body —
   a table of contents printed after the article would be useless. */
.post-layout { display: grid; gap: 48px; padding: 34px 0 0; align-items: start; }
.post-layout .sidebar { order: -1; }
.post-layout .post { order: 0; }
@media (min-width: 1000px) {
  .post-layout { grid-template-columns: minmax(0, 1fr) 300px; }
  .post-layout .sidebar { order: 0; }
}
.sidebar { display: flex; flex-direction: column; gap: 20px; }
@media (min-width: 1000px) {
  /* Sticks as one block so the CTA stays in view alongside the ToC rather than
     scrolling away and leaving the column empty. Capped to the viewport with
     its own scroll for very long tables of contents. */
  .sidebar {
    position: sticky;
    top: 92px;
    max-height: calc(100vh - 112px);
    overflow-y: auto;
    scrollbar-width: thin;
  }
  /* min-height:0 lets flex shrink the ToC so the CTA below it stays on
     screen. Without an overflow of its own that shrinking did not clip
     anything: the <ol> kept its full height, rendered straight out through
     the bottom of its own bordered box, and on a short viewport landed on top
     of the CTA card. A long ToC now scrolls inside its own box instead. */
  .toc {
    flex: 0 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

.toc {
  border: 1px solid hsl(var(--rule));
  border-radius: var(--radius);
  background: hsl(var(--card));
  padding: 18px 20px;
}
.toc-heading {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.7rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: hsl(var(--ink-soft));
  margin: 0 0 12px;
}
.toc ol { list-style: none; margin: 0; padding: 0; counter-reset: toc; }
.toc li { margin: 0; }
.toc li a {
  display: block;
  padding: 6px 0 6px 14px;
  border-left: 2px solid hsl(var(--rule));
  color: hsl(var(--ink-soft));
  font-size: 0.89rem;
  line-height: 1.45;
  transition: color 0.15s, border-color 0.15s;
}
.toc li a:hover { color: hsl(var(--brand)); border-left-color: hsl(var(--brand)); text-decoration: none; }
.toc .toc-l3 a { padding-left: 26px; font-size: 0.85rem; }
/* :target highlights the section you jumped to — active-section tracking
   without JavaScript (no inline scripts under the edge CSP). */
.prose h2:target, .prose h3:target { scroll-margin-top: 96px; }
.prose h2, .prose h3 { scroll-margin-top: 96px; }

.side-cta {
  border: 1px solid hsl(var(--brand) / 0.28);
  border-radius: var(--radius);
  padding: 20px;
  background-image: linear-gradient(160deg, hsl(var(--brand) / 0.07), hsl(var(--brand-2) / 0.07));
}
.side-cta-eyebrow {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: hsl(var(--brand));
  margin: 0 0 8px;
}
.side-cta-title {
  font-family: Fraunces, ui-serif, Georgia, serif;
  font-size: 1.12rem;
  font-weight: 600;
  line-height: 1.25;
  margin: 0 0 8px;
}
.side-cta-body { font-size: 0.88rem; color: hsl(var(--ink-soft)); margin: 0 0 16px; }

/* ── prose ──────────────────────────────────────────────────────────── */
/* Fills its column rather than capping at a 72ch measure. That cap is the
   typographically correct default, but it left the text in a ~730px ribbon
   with half the article column empty beside it once the shell went fluid. */
.prose { font-size: 1.08rem; max-width: none; }
.prose > *:first-child { margin-top: 0; }
.prose h2 { font-size: 1.72rem; margin: 46px 0 14px; line-height: 1.2; }
.prose h3 { font-size: 1.28rem; margin: 34px 0 10px; line-height: 1.3; }
.prose p { margin: 0 0 20px; }
.prose ul, .prose ol { margin: 0 0 20px; padding-left: 24px; }
.prose li { margin-bottom: 9px; }
.prose li::marker { color: hsl(var(--brand)); }
.prose strong { font-weight: 650; color: hsl(var(--ink)); }
.prose a { text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }

.prose blockquote {
  margin: 28px 0;
  padding: 4px 0 4px 22px;
  border-left: 3px solid hsl(var(--brand));
  font-family: Fraunces, ui-serif, Georgia, serif;
  font-size: 1.15rem;
  font-style: italic;
  color: hsl(var(--ink));
}
.prose code {
  background: hsl(var(--ink) / 0.05);
  border: 1px solid hsl(var(--rule));
  border-radius: 5px;
  padding: 1px 6px;
  font-size: 0.88em;
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}
.prose pre {
  background: hsl(224 32% 12%);
  color: hsl(210 40% 96%);
  border-radius: var(--radius);
  padding: 18px 20px;
  overflow-x: auto;
  margin: 0 0 22px;
  font-size: 0.9rem;
}
.prose pre code { border: 0; padding: 0; background: none; color: inherit; }

.prose figure { margin: 30px 0; }
.prose figure img { width: 100%; border-radius: var(--radius); border: 1px solid hsl(var(--rule)); }
.prose figcaption {
  margin-top: 10px;
  font-size: 0.86rem;
  color: hsl(var(--ink-soft));
  text-align: center;
}

.prose table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 24px;
  font-size: 0.94rem;
  display: block;
  overflow-x: auto;
}
.prose th, .prose td { border: 1px solid hsl(var(--rule)); padding: 10px 13px; text-align: left; }
.prose th { background: hsl(var(--ink) / 0.04); font-weight: 600; }

/* Editor callouts (aside[data-callout]) */
.prose aside {
  margin: 26px 0;
  padding: 16px 20px;
  border-radius: var(--radius);
  border: 1px solid hsl(var(--rule));
  background: hsl(var(--card));
}
.prose aside > *:last-child { margin-bottom: 0; }
.prose aside[data-callout='info'] { border-color: hsl(var(--brand) / 0.35); background: hsl(var(--brand) / 0.06); }
.prose aside[data-callout='warning'] { border-color: hsl(var(--accent-warm) / 0.4); background: hsl(var(--accent-warm) / 0.08); }
.prose aside[data-callout='success'] { border-color: hsl(152 60% 42% / 0.4); background: hsl(152 60% 42% / 0.08); }

.prose iframe {
  width: 100%;
  aspect-ratio: 16 / 9;
  height: auto;
  border: 0;
  border-radius: var(--radius);
  margin: 30px 0;
}
.prose div[data-youtube-video] { margin: 30px 0; }
.prose hr { border: 0; border-top: 1px solid hsl(var(--rule)); margin: 40px 0; }

.post-foot { margin-top: 46px; padding-top: 22px; border-top: 1px solid hsl(var(--rule)); }
.back { font-size: 0.94rem; margin: 6px 0 0; }

/* ── related ────────────────────────────────────────────────────────── */
.related { margin: 70px 0 20px; padding-top: 40px; border-top: 1px solid hsl(var(--rule)); }
.section-title { font-size: 1.5rem; margin: 0 0 22px; }

/* ── footer ─────────────────────────────────────────────────────────── */
.site-footer { border-top: 1px solid hsl(var(--rule)); margin-top: 80px; background: hsl(var(--card)); }
.footer-inner {
  display: flex;
  justify-content: space-between;
  gap: 28px;
  flex-wrap: wrap;
  padding-top: 40px;
  padding-bottom: 26px;
}
.footer-brand p { color: hsl(var(--ink-soft)); font-size: 0.9rem; margin: 10px 0 0; }
.footer-nav { display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-start; }
.footer-nav a { color: hsl(var(--ink-soft)); font-size: 0.9rem; }
.footer-nav a:hover { color: hsl(var(--ink)); }
.footer-legal {
  border-top: 1px solid hsl(var(--rule));
  padding-top: 20px;
  padding-bottom: 30px;
  color: hsl(var(--ink-soft));
  font-size: 0.84rem;
}

/* Drops at the same width the SPA does (Tailwind "hidden md:block", md = 768px),
   not at 640px. Matching matters more than the exact number: between 641 and
   767px the blog used to show Home/Pricing/Blog while / and /pricing hid them. */
@media (max-width: 767px) {
  .site-nav { display: none; }
}

/* The wordmark goes at Tailwind's sm, mirroring "hidden sm:inline" on the SPA
   brand: below 640 the mark stands alone there, and the blog kept the word. */
@media (max-width: 639.98px) {
  .logo span { display: none; }
}

@media (max-width: 640px) {
  body { font-size: 16px; }
  .shell { padding: 0 18px; }
  .page-head { padding-top: 40px; }
  .prose { font-size: 1.02rem; }
  .cards-grid { grid-template-columns: 1fr; }
}
