/* _ds/tokens/colors.css */
:root{
/* brand — from logo mark gradient. 700 and darker pass 4.5:1 on paper surfaces. */
--brand-900:#2a1d18;--brand-700:#42302a;--brand-600:#523c33;--brand-500:#6b4f43;--brand-400:#8c6a57;--brand-300:#b0917f;--brand-200:#d4bfb2;--brand-100:#ede2da;--brand-50:#f8f2ed;

/* pastel accents — tonal ramps; 700 steps are darkened to clear 4.5:1 on their own 100 tint */
--sage-100:#e4efe6;--sage-300:#b7d2ba;--sage-500:#5f8c68;--sage-700:#2f5a3c;
--sky-100:#e3edf4;--sky-300:#b3cee0;--sky-500:#4f86ad;--sky-700:#28536f;
--lavender-100:#ece6f4;--lavender-300:#cbb9de;--lavender-500:#7f66a5;--lavender-700:#4f3a75;
--butter-100:#f9f0da;--butter-300:#eed89f;--butter-500:#b08f2c;--butter-700:#6d5410;
--clay-100:#f6e7e1;--clay-300:#e6bcab;--clay-500:#b56347;--clay-700:#7f3f2b;

/* neutrals — warm paper, bear-notes inspired */
--paper-0:#fffdfb;--paper-50:#faf6f1;--paper-100:#f3ece4;--paper-200:#e7ddd2;--paper-300:#d6c9bb;
--ink-900:#241d1a;--ink-700:#3f342e;--ink-500:#5e5049;--ink-300:#7d6e65;--ink-100:#d8cfc5;

/* dark surface set */
--dark-900:#191412;--dark-800:#221b18;--dark-700:#2d2521;--dark-600:#3b312b;--dark-500:#4d413a;

/* semantic — light */
--surface-app:var(--paper-50);--surface-card:var(--paper-0);--surface-sunken:var(--paper-100);--surface-border:var(--paper-300);
--text-primary:var(--ink-900);--text-secondary:var(--ink-500);--text-tertiary:var(--ink-300);--text-inverse:var(--paper-0);
--accent:var(--brand-600);--accent-hover:var(--brand-700);--accent-soft:var(--brand-100);
--success:var(--sage-700);--success-soft:var(--sage-100);
--info:var(--sky-700);--info-soft:var(--sky-100);
--warning:var(--butter-700);--warning-soft:var(--butter-100);
--danger:var(--clay-700);--danger-soft:var(--clay-100);
--focus-ring:var(--lavender-700);
/* semantic — dark surfaces (text tokens meet 4.5:1 on --dark-800/900) */
--surface-app-dark:var(--dark-900);--surface-card-dark:var(--dark-800);--surface-border-dark:var(--dark-600);
--text-primary-dark:var(--paper-50);--text-secondary-dark:var(--paper-300);
}

/* _ds/tokens/typography.css */
:root{
/* Figtree — display/headings. Atkinson Hyperlegible — body/UI, designed for low-vision legibility. JetBrains Mono — code. */
--font-display:'Figtree',ui-sans-serif,system-ui,sans-serif;
--font-body:'Atkinson Hyperlegible','Figtree',ui-sans-serif,system-ui,sans-serif;
--font-mono:'JetBrains Mono',ui-monospace,'SFMono-Regular',monospace;
/* accessible scale: body never below 16px, captions never below 14px */
--text-xs:14px;--text-sm:15px;--text-base:16px;--text-md:18px;--text-lg:22px;--text-xl:26px;--text-2xl:32px;--text-3xl:40px;--text-4xl:52px;
--leading-tight:1.2;--leading-snug:1.4;--leading-normal:1.6;--leading-relaxed:1.75;
--tracking-tight:-0.015em;--tracking-normal:0;--tracking-wide:0.06em;
--weight-regular:400;--weight-medium:500;--weight-semibold:600;--weight-bold:700;--weight-extrabold:800;
--measure-prose:68ch;
}

/* _ds/tokens/spacing.css */
:root{
--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-8:32px;--space-10:40px;--space-12:48px;--space-16:64px;--space-20:80px;--space-24:96px;
--radius-sm:8px;--radius-md:12px;--radius-lg:16px;--radius-xl:24px;--radius-pill:999px;
/* accessibility: minimum interactive target, and a visible 2px focus outline offset from the control */
--target-min:44px;/* @kind spacing */
--focus-outline-width:3px;/* @kind spacing */
--focus-outline-offset:2px;/* @kind spacing */
--shadow-sm:0 1px 2px rgba(36,29,26,0.07),0 1px 1px rgba(36,29,26,0.05);
--shadow-md:0 4px 10px rgba(36,29,26,0.09),0 1px 3px rgba(36,29,26,0.06);
--shadow-lg:0 12px 28px rgba(36,29,26,0.13),0 3px 8px rgba(36,29,26,0.08);
--shadow-focus:0 0 0 3px var(--lavender-300),0 0 0 5px var(--lavender-700);
--border-hairline:1px solid var(--surface-border);
--ease-standard:cubic-bezier(0.2,0,0,1);/* @kind other */--ease-out:cubic-bezier(0.05,0.7,0.1,1);/* @kind other */
--duration-fast:120ms;/* @kind other */--duration-base:180ms;/* @kind other */--duration-slow:280ms;/* @kind other */
}
@media (prefers-reduced-motion:reduce){:root{--duration-fast:0ms;/* @kind other */--duration-base:0ms;/* @kind other */--duration-slow:0ms/* @kind other */}}

/* _ds/tokens/base.css */
/* Base element defaults so every consuming page inherits accessible type, links and focus rings. */
body{font-family:var(--font-body);font-size:var(--text-base);line-height:var(--leading-normal);color:var(--text-primary);background:var(--surface-app);-webkit-font-smoothing:antialiased}
h1,h2,h3,h4{font-family:var(--font-display);color:var(--text-primary);line-height:var(--leading-tight);letter-spacing:var(--tracking-tight);margin:0}
p{max-width:var(--measure-prose);text-wrap:pretty}
a{color:var(--brand-700);text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:1px}
a:hover{color:var(--brand-900);text-decoration-thickness:2px}
:focus-visible{outline:var(--focus-outline-width) solid var(--focus-ring);outline-offset:var(--focus-outline-offset)}
button,input,select,textarea{font-family:inherit;font-size:var(--text-base)}
button,a[role="button"]{min-height:var(--target-min)}
code,pre{font-family:var(--font-mono)}
::selection{background:var(--brand-200)}

/* components.css */
/* Design-system components, ported from the vendored React bundle to plain CSS.
   Values are copied verbatim from _ds/<version>/_ds_bundle.js so the rendered
   result is identical; hover states that were React state are now :hover. */

/* Badge — _ds components/feedback/Badge.jsx */
.badge {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 10px;
  border-radius: var(--radius-pill);
  font-family: var(--font-display);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.badge-neutral { background: var(--surface-sunken); color: var(--text-secondary) }
.badge-brand   { background: var(--accent-soft);    color: var(--brand-700) }
.badge-success { background: var(--success-soft);   color: var(--success) }
.badge-info    { background: var(--info-soft);      color: var(--info) }
.badge-warning { background: var(--warning-soft);   color: var(--warning) }
.badge-danger  { background: var(--danger-soft);    color: var(--danger) }

/* Button — _ds components/primitives/Button.jsx.
   Rendered as an <a> because every button on this site navigates. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 44px;
  border: none;
  border-radius: var(--radius-md);
  font-family: var(--font-display);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--duration-base) var(--ease-standard);
}
.btn-sm { height: 36px; padding: 0 14px; font-size: var(--text-sm) }
.btn-md { height: 44px; padding: 0 20px; font-size: var(--text-base) }
.btn-lg { height: 52px; padding: 0 26px; font-size: var(--text-md) }

.btn-primary        { background: var(--brand-600); color: var(--text-inverse) }
.btn-primary:hover  { background: var(--brand-700); color: var(--text-inverse) }
.btn-secondary      { background: var(--surface-sunken); color: var(--text-primary); border: 1px solid var(--surface-border) }
.btn-secondary:hover{ background: var(--paper-200); color: var(--text-primary) }
.btn-ghost          { background: transparent; color: var(--brand-700) }
.btn-ghost:hover    { background: var(--accent-soft); color: var(--brand-700) }

/* Card — _ds components/surfaces/Card.jsx */
.ds-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 20px;
  background: var(--surface-card);
  border: 1px solid var(--surface-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  font-family: var(--font-body);
}

/* Blog tag filter — replaces the React useState toggle. Server renders the
   pressed state; htmx swaps in a new fragment on click. */
.pill-filter {
  min-height: 44px;
  border: none;
  background: transparent;
  color: var(--ink-500);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard), color var(--duration-fast);
}
.pill-filter:hover { background: var(--brand-50); color: var(--brand-700) }
.pill-filter[aria-current] { background: var(--brand-100); color: var(--brand-700) }

/* htmx: fade the region while a fragment is in flight. The pills declare
   #blog-filter as their hx-indicator, so htmx adds .htmx-request to it. */
#blog-filter { transition: opacity var(--duration-fast) var(--ease-standard) }
#blog-filter.htmx-request { opacity: .55 }

/* Skip link — keyboard users land here first. */
.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 12px 20px;
  background: var(--brand-700);
  color: var(--paper-0);
  border-radius: 0 0 var(--radius-md) 0;
  font-family: var(--font-display);
  font-weight: 700;
}
.skip:focus { left: 0; color: var(--paper-0) }

/* Analytics consent notice. Fixed to the bottom, revealed by the client script
   only when no choice has been stored. Motion is opacity only, to stay inside
   the studio's stated motion budget. */
.consent {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  padding: 16px 0;
  background: var(--paper-0);
  border-top: 1px solid var(--surface-border);
  box-shadow: var(--shadow-md);
  transition: opacity var(--duration-base) var(--ease-standard);
}
.consent[hidden] { display: none }

.consent-in {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  align-items: center;
  justify-content: space-between;
}
.consent-text { max-width: 62ch }
.consent-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-left: auto }

/* Stack the buttons under the text where a single row will not fit. */
@media (max-width: 640px) {
  .consent-in { flex-direction: column; align-items: stretch }
  .consent-actions { margin-left: 0 }
  .consent-actions .btn { flex: 1 }
}

/* Withdrawal control in the footer — a button, styled as the footer's own text. */
.consent-reopen {
  margin-left: 12px;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.consent-reopen:hover { color: var(--paper-0) }

/* site.css */
html{scroll-behavior:smooth;scroll-padding-top:96px}
body{background:var(--paper-50);overflow-x:hidden}
*{box-sizing:border-box}
a{text-decoration:none;color:var(--brand-700)}
a:hover{color:var(--brand-900);text-decoration:none}
.prose a,.linked{text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px}
p{margin:0}
ul{margin:0}
.wrap{width:min(1240px,100% - 96px);margin-inline:auto}
@media(max-width:720px){.wrap{width:min(1240px,100% - 40px)}}
@media(min-width:1800px){.wrap{width:min(1440px,100% - 200px)}}

/* header + bookmark */
.hdr{position:sticky;top:0;z-index:40;background:color-mix(in srgb,var(--paper-50) 88%,transparent);backdrop-filter:blur(10px)}
.hdr-in{display:flex;align-items:center;gap:24px;min-height:92px;padding-left:176px;position:relative}
.bookmark{position:absolute;left:36px;top:0;display:grid;place-items:start center;min-height:0;transition:opacity var(--duration-base) var(--ease-standard)}
.bookmark:hover{opacity:.78}
.bookmark img{width:130px;height:130px;object-fit:contain;margin-top:-6px}
.mark{font-family:var(--font-display);font-weight:800;font-size:20px;letter-spacing:-0.02em;color:var(--brand-700)}
.mark span{font-weight:500;color:var(--brand-400)}
.nav{display:flex;gap:4px;margin-left:auto;align-items:center}
.nav a{font-family:var(--font-display);font-weight:600;font-size:16px;padding:0 16px;height:44px;display:inline-flex;align-items:center;border-radius:var(--radius-pill);color:var(--ink-700);transition:background var(--duration-fast) var(--ease-standard),color var(--duration-fast)}
.nav a:hover{background:var(--brand-50);color:var(--brand-700)}
.nav a[aria-current]{background:var(--brand-100);color:var(--brand-700)}
.navtoggle{display:none}
@media(max-width:860px){.hdr-in{padding-left:104px;min-height:76px}.bookmark{left:20px}.bookmark img{width:74px;height:74px}.nav{gap:0}.nav a{padding:0 10px;font-size:15px}}
/* Below this the wordmark, the logo and four nav links cannot share a row.
   The logo mark carries the brand, so the repeated text is what gives way. */
@media(max-width:560px){.mark{display:none}}
@media(max-width:520px){.nav a{padding:0 8px;font-size:14px}}
@media(max-width:400px){.hdr-in{padding-left:74px;gap:6px}.bookmark{left:16px}.bookmark img{width:54px;height:54px}.nav a{padding:0 6px;font-size:13px}}

/* type */
.h-xl{font-size:clamp(38px,5.6vw,72px);font-weight:800;line-height:1.03;letter-spacing:-0.03em}
.h-lg{font-size:clamp(28px,3.2vw,44px);font-weight:800;line-height:1.1;letter-spacing:-0.025em}
.h-md{font-size:clamp(21px,1.7vw,26px);font-weight:700;line-height:1.25}
.lead{font-size:clamp(18px,1.35vw,22px);line-height:1.65;color:var(--ink-700);max-width:46ch}
.body{font-size:var(--text-base);line-height:var(--leading-relaxed);color:var(--ink-700);max-width:62ch}
.small{font-size:var(--text-sm);color:var(--text-secondary)}
.eyebrow{font-family:var(--font-display);font-size:var(--text-sm);font-weight:700;letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--brand-400)}
.mono{font-family:var(--font-mono);font-size:var(--text-sm);color:var(--brand-400)}

/* layout helpers */
.col{display:flex;flex-direction:column}
.sec{padding:clamp(64px,8vh,120px) 0}
.pill{display:inline-flex;align-items:center;gap:8px;height:34px;padding:0 14px;border-radius:var(--radius-pill);font-size:var(--text-sm);font-weight:700;font-family:var(--font-display)}
.dot{width:8px;height:8px;border-radius:50%}
.soft{background:var(--paper-0);border-radius:var(--radius-lg);padding:clamp(24px,2.4vw,36px);box-shadow:var(--shadow-sm)}
.cta{display:inline-flex;align-items:center;justify-content:center;height:52px;padding:0 26px;border-radius:var(--radius-md);font-family:var(--font-display);font-weight:700;font-size:var(--text-md);background:var(--brand-600);color:var(--paper-0);transition:background var(--duration-base) var(--ease-standard)}
.cta:hover{background:var(--brand-700);color:var(--paper-0)}
.cta.ghost{background:var(--brand-100);color:var(--brand-700)}
.cta.ghost:hover{background:var(--brand-200);color:var(--brand-900)}

/* pinned sections */
.pin{position:sticky;top:112px;align-self:start}
.stack{display:flex;flex-direction:column;gap:clamp(16px,2vw,28px)}
.stack .card{position:sticky;top:calc(120px + var(--i)*18px);border-radius:var(--radius-xl);padding:clamp(28px,3vw,52px);box-shadow:var(--shadow-sm)}
.split{display:grid;grid-template-columns:minmax(0,0.85fr) minmax(0,1.15fr);gap:clamp(32px,5vw,88px)}
/* minmax(0,1fr), not 1fr: a bare 1fr is minmax(auto,1fr), so a wide child such as
   a code block sets the track's minimum and pushes the page sideways. */
@media(max-width:900px){.split{grid-template-columns:minmax(0,1fr);gap:28px}.split>*{min-width:0}.pin{position:static}.stack .card{position:static}}

/* reveal */
.reveal{transition:opacity 500ms var(--ease-out),transform 500ms var(--ease-out)}
.js-reveal .reveal:not(.in){opacity:0;transform:translateY(18px)}
.reveal.in{opacity:1;transform:none}
@media(prefers-reduced-motion:reduce){.js-reveal .reveal:not(.in){opacity:1;transform:none}.reveal{transition:none}html{scroll-behavior:auto}}

/* footer */
.ftr{background:var(--brand-900);color:var(--paper-100);padding:clamp(48px,6vw,80px) 0 32px;margin-top:clamp(48px,6vw,96px)}
.ftr .mark{color:var(--paper-0)}
.ftr .mark span{color:var(--brand-300)}
.ftr-grid{display:grid;grid-template-columns:1.4fr repeat(3,1fr);gap:clamp(28px,4vw,64px)}
@media(max-width:860px){.ftr-grid{grid-template-columns:1fr 1fr}}
.ftr h4{font-size:var(--text-sm);font-weight:700;letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--brand-300)}
.ftr a{color:var(--paper-100);font-size:var(--text-sm);display:inline-block;padding:5px 0;transition:color var(--duration-fast)}
.ftr a:hover{color:var(--paper-0)}
.ftr .rule{margin-top:clamp(32px,4vw,56px);padding-top:20px;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;color:var(--paper-300);font-size:var(--text-sm)}

/* blog + prose */
.postlist{display:grid;gap:clamp(12px,1.4vw,20px)}
.postlink>div{transition:box-shadow var(--duration-base) var(--ease-standard)}
.postlink:hover>div{box-shadow:var(--shadow-md)}
.postcard-in{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:16px 32px;align-items:start}
@media(max-width:720px){.postcard-in{grid-template-columns:1fr}}
.postcard{background:var(--paper-0);border-radius:var(--radius-lg);padding:clamp(20px,2vw,32px);box-shadow:var(--shadow-sm);display:grid;grid-template-columns:minmax(0,1fr) auto;gap:16px 32px;align-items:start;transition:box-shadow var(--duration-base) var(--ease-standard),background var(--duration-base)}
.postcard:hover{box-shadow:var(--shadow-md)}
@media(max-width:720px){.postcard{grid-template-columns:1fr}}
.prose{max-width:68ch;overflow-wrap:break-word}
.prose pre{max-width:100%}
.prose>*:first-child{margin-top:0}
.prose h2{font-size:clamp(24px,2.2vw,32px);font-weight:700;margin:2em 0 .5em}
.prose h3{font-size:var(--text-lg);font-weight:700;margin:1.6em 0 .4em}
.prose p,.prose li{font-size:var(--text-md);line-height:1.75;color:var(--ink-700);max-width:68ch}
.prose p{margin:0 0 1.1em}
.prose ul,.prose ol{margin:0 0 1.2em;padding-left:1.3em;display:flex;flex-direction:column;gap:.5em}
.prose blockquote{margin:1.4em 0;background:var(--sage-100);border-radius:var(--radius-lg);padding:20px 24px}
.prose blockquote p{margin:0;color:var(--sage-700)}
.prose code{background:var(--paper-100);border-radius:6px;padding:2px 6px;font-size:.9em}
.prose pre{background:var(--dark-800);color:var(--paper-50);border-radius:var(--radius-md);padding:20px 22px;overflow-x:auto;margin:1.4em 0}
.prose pre code{background:none;color:inherit;padding:0}
.prose hr{border:none;height:1px;background:var(--paper-200);margin:2.4em 0}
.prose strong{color:var(--ink-900)}
.toc a{display:block;padding:6px 0;font-size:var(--text-sm);color:var(--ink-500)}
.toc a:hover{color:var(--brand-700)}
.toc a.active{color:var(--brand-700);font-weight:700}