/* Modelled on the aiforui.dev lesson shell: light grey page, Inter,
   65ch reading column, sticky lesson nav on the left, "On this page" on the right.
   Reviewed against emil-ui-review / typography / ui-polish / surfaces / unslop-design. */

:root {
  color-scheme: light dark;

  --gray-100: #fcfcfc;
  --gray-200: #f7f7f5;
  --gray-300: #f0f0ee;
  --gray-400: #e6e6e3;
  --gray-1000: #70706c; /* 4.85:1 on gray-100 — passes AA at 12–13px */
  --gray-1100: #62625f; /* 5.96:1 */
  --gray-1200: #1c1c1a;

  --surface: #fff;        /* cards, popovers, tips */
  --on-ink: #fff;         /* text on a gray-1200 fill */
  --ink-hover: #000;      /* gray-1200 fill on hover */
  --shadow: 0 0 0;        /* rgb triplet for shadows and rings */
  --bar-2: #8a8a86; --bar-3: #b9b9b5; --bar-4: #dcdcd8; /* chart greys, darkest first */
  /* file-card kinds: the margin-note hues (assets/neat-annotations.css) */
  --kind-prose: #b8751a;
  --kind-config: #3b72c4;
  --kind-script: #2f8f5b;

  --font: Inter, "Inter Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  --mono: "Berkeley Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --weight-normal: 400;
  --weight-medium: 500;

  /* type scale */
  --text-xs: 12px;
  --text-sm: 13px;
  --text-ui: 14px;
  --text-body: 16px;
  --text-h2: 20px;
  --text-h1: 24px;

  --leading-body: 1.6;
  --leading-heading: 1.25;

  /* spacing scale (4px base) */
  --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px; --s-5: 20px;
  --s-6: 24px; --s-8: 32px; --s-12: 48px; --s-16: 64px;

  --radius: 8px;
  --measure: 55ch; /* Inter renders ~1.35 chars per ch; 55ch ≈ 72–75 characters */

  --border-hairline: 1px;

  --elevation-raised:
    0 0 0 1px rgb(var(--shadow) / 0.05),
    0 1px 3px rgb(var(--shadow) / 0.05),
    0 4px 10px -4px rgb(var(--shadow) / 0.05);
  --elevation-raised-hover:
    0 0 0 1px rgb(var(--shadow) / 0.07),
    0 1px 3px rgb(var(--shadow) / 0.07),
    0 4px 10px -4px rgb(var(--shadow) / 0.08);
}

/* Follows the system setting. Same scale, inverted: 100 is the page, 1200 is the ink. */
@media (prefers-color-scheme: dark) {
  :root {
    --gray-100: #121212;
    --gray-200: #181817;
    --gray-300: #222220;
    --gray-400: #2e2e2b;
    --gray-1000: #8f8f8a; /* 5.3:1 on gray-100 */
    --gray-1100: #a8a8a3; /* 7.1:1 */
    --gray-1200: #ededea;

    --surface: #1c1c1b;
    --on-ink: #121212;
    --ink-hover: #fff;
    --shadow: 255 255 255;
    --bar-2: #8e8e89; --bar-3: #5c5c58; --bar-4: #3a3a37;
    --kind-prose: #d39a4a;
    --kind-config: #7ea3e0;
    --kind-script: #6cb890;
  }
}

@media (min-resolution: 2dppx) {
  :root { --border-hairline: 0.5px; }
}

* { box-sizing: border-box; }

html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-synthesis: none;
}

body {
  margin: 0;
  background: var(--gray-100);
  color: var(--gray-1200);
  font-family: var(--font);
  font-size: var(--text-body);
  line-height: var(--leading-body);
  font-weight: var(--weight-normal);
}

a { color: inherit; text-decoration: none; }
button, a { touch-action: manipulation; }

:focus-visible {
  outline: 2px solid var(--gray-1200);
  outline-offset: 2px;
  border-radius: 4px;
}

/* skip link */
.skip {
  position: absolute;
  inset-inline-start: var(--s-4);
  top: -100px;
  padding: var(--s-2) var(--s-3);
  background: var(--surface);
  border-radius: 6px;
  box-shadow: var(--elevation-raised);
  font-size: var(--text-ui);
}
.skip:focus-visible { top: var(--s-4); z-index: 40; }

/* header */
.site-header {
  display: flex;
  align-items: center;
  min-height: 56px;
  padding: 0 var(--s-5);
  border-bottom: var(--border-hairline) solid var(--gray-400);
}
.brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-weight: var(--weight-medium);
}

/* three-column layout */
.layout { display: block; }
@media (min-width: 1024px) {
  .layout { display: grid; grid-template-columns: 260px 1fr; }
}
@media (min-width: 1536px) {
  .layout { grid-template-columns: 260px 1fr 260px; }
}

/* left nav. Below 1024px the same element is a drawer: off-canvas to the
   left, slid in by the header button (build.py wires it). Above, it is the
   sticky column and the drawer chrome hides. DESIGN.md, "Lesson layout and type" and "Page chrome". */
.sidebar {
  position: sticky;
  top: 0;
  height: fit-content;
  max-height: 100dvh;
  overflow-y: auto;
  padding: var(--s-6) var(--s-6) var(--s-6) var(--s-3);
}
.sidebar:focus { outline: none; }
.nav-toggle, .nav-close {
  display: none;
  place-items: center;
  width: 44px;
  height: 44px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--gray-1100);
  cursor: pointer;
  transition: background-color 150ms ease-out, color 150ms ease-out, transform 150ms ease-out;
}
.nav-toggle:active, .nav-close:active { transform: scale(0.96); }
.nav-toggle:focus-visible, .nav-close:focus-visible { outline: 2px solid var(--gray-1200); outline-offset: -2px; }
.drawer-head { display: none; }
.scrim {
  position: fixed;
  inset: 0;
  z-index: 30;
  background: rgb(0 0 0 / 0.32);
  opacity: 0;
  transition: opacity 250ms cubic-bezier(0.32, 0.72, 0, 1);
}
[data-nav-open] .scrim { opacity: 1; }
@media (max-width: 1023px) {
  .site-header { position: sticky; top: 0; z-index: 20; gap: var(--s-1); padding-left: var(--s-2); background: var(--gray-100); }
  .nav-toggle, .nav-close { display: inline-grid; }
  .drawer-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-height: 44px;
    margin-bottom: var(--s-3);
    padding-left: var(--s-3);
    font-size: var(--text-ui);
    font-weight: var(--weight-medium);
  }
  .sidebar {
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 31;
    width: min(300px, 85vw);
    height: auto;
    max-height: none;
    padding: var(--s-2) var(--s-3) calc(var(--s-4) + env(safe-area-inset-bottom));
    overscroll-behavior: contain;
    background: var(--surface);
    box-shadow: 0 0 0 1px rgb(var(--shadow) / 0.08);
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform 250ms cubic-bezier(0.32, 0.72, 0, 1), visibility 0s 250ms;
  }
  [data-nav-open] .sidebar { transform: none; visibility: visible; transition: transform 250ms cubic-bezier(0.32, 0.72, 0, 1); }
  .sidebar li a { min-height: 44px; padding: 0 var(--s-3); border-radius: var(--radius); font-size: var(--text-body); }
  .sidebar li a:active { background: var(--gray-400); }
  .section-label { margin: var(--s-5) 0 var(--s-2); padding: 0 var(--s-3); }
}
@media (prefers-reduced-motion: reduce) {
  .sidebar, .scrim, .nav-toggle, .nav-close { transition: none; }
  .sidebar li a .num { transition: none; }
}
/* lesson list: quiet name-only rows. Hover or keyboard focus darkens the
   name and shows that row's mono number; no fill on the row (Ben, 27
   September 2026). Active row: dark text + number. Wide-screen spacing
   ("Snug") is in navigation.css. */
.sidebar ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-1); }
.sidebar .group { display: block; }
.section-label {
  margin: 20px 0 4px;
  padding: 0 var(--s-2);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: 0.01em;
  color: var(--gray-1000);
}
/* the very first label sits flush with the top of the nav */
.sidebar .group:first-child .section-label { margin-top: 0; }
.sidebar li a {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  min-height: 36px;
  padding: 0 var(--s-2);
  border-radius: var(--radius);
  font-size: var(--text-ui);
  color: var(--gray-1100);
  transition: color 150ms ease-out;
}
/* extend hit area to 40px without colliding with the 4px gap */
.sidebar li a::before { content: ""; position: absolute; inset: -2px 0; }
.sidebar li a .num {
  font-family: var(--mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--gray-1000);
  opacity: 0;
  transform: translateX(6px);
  transition: opacity 150ms ease-out, transform 150ms ease-out;
}
.sidebar li a:hover,
.sidebar li a:focus-visible { color: var(--gray-1200); }
.sidebar li a:hover .num,
.sidebar li a:focus-visible .num,
.sidebar li a.active .num { opacity: 1; transform: none; }
/* touch devices fire no hover: the numbers stay on, the bar stays off */
@media (hover: none) {
  .sidebar li a .num { opacity: 1; transform: none; }
}
/* keyboard focus: a ring, not a fill */
.sidebar li a:focus-visible { outline: 2px solid var(--gray-1200); outline-offset: 2px; }
.sidebar li a.active { color: var(--gray-1200); font-weight: var(--weight-medium); }

/* reading column */
.content { padding: var(--s-12) var(--s-4) 0; }
@media (min-width: 1024px) {
  .content { padding: 92px var(--s-12) var(--s-12); }
}
article { max-width: var(--measure); margin-inline: auto; }

article h1 {
  margin: 0 0 var(--s-4);
  font-size: var(--text-h1);
  line-height: var(--leading-heading);
  font-weight: var(--weight-medium);
  letter-spacing: -0.01em;
  text-wrap: balance;
  scroll-margin-top: var(--s-8);
}
article p {
  margin: var(--s-3) 0;
  color: var(--gray-1100);
  overflow-wrap: break-word;
}
article p a {
  text-decoration: underline;
  text-decoration-thickness: from-font;
  text-underline-position: from-font;
  text-decoration-skip-ink: auto;
  transition: color 150ms ease-out;
}
article p a:hover { color: var(--gray-1200); }

.h2-link { display: inline-block; width: fit-content; margin: var(--s-16) 0 var(--s-6); }
.h2-link:has(+ p), .h2-link:has(+ ul) { margin-bottom: var(--s-2); }
article h2 {
  margin: 0;
  font-size: var(--text-h2);
  line-height: var(--leading-heading);
  font-weight: var(--weight-medium);
  text-wrap: balance;
  scroll-margin-top: var(--s-4);
}
article h3 {
  margin: var(--s-8) 0 0;
  font-size: 17px;
  line-height: var(--leading-heading);
  font-weight: var(--weight-medium);
  color: var(--gray-1200);
  text-wrap: balance;
  scroll-margin-top: var(--s-4);
}
article em, article strong { font-weight: var(--weight-medium); color: var(--gray-1200); font-style: normal; }
article ul { margin: var(--s-3) 0; padding-inline-start: var(--s-5); color: var(--gray-1100); }
article ul ul { margin: var(--s-1) 0; }
article li { margin: var(--s-1) 0; }
article s { color: var(--gray-1000); }
article code {
  font-family: var(--mono);
  font-size: 0.875em;
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--gray-300);
  color: var(--gray-1200);
}

.pager {
  display: flex;
  justify-content: space-between;
  gap: var(--s-4);
  margin-top: var(--s-16);
  padding: var(--s-16) 0;
  border-top: var(--border-hairline) solid var(--gray-400);
  font-size: var(--text-ui);
}
.pager a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 var(--s-4);
  border-radius: 999px;
  background: var(--surface);
  box-shadow: var(--elevation-raised);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  transition: box-shadow 150ms ease-out, transform 150ms ease-out;
}
@media (hover: hover) and (pointer: fine) {
  .pager a:hover { box-shadow: var(--elevation-raised-hover); }
}
.pager a:active { transform: scale(0.96); }
/* narrow screens: the nowrap pills overflow the viewport side by side, so stack them */
@media (max-width: 640px) {
  .pager { flex-direction: column; }
  .pager a { justify-content: center; }
}

/* right toc */
.toc {
  display: none;
  position: sticky;
  top: 0;
  height: fit-content;
  padding: var(--s-6);
  font-size: var(--text-sm);
}
@media (min-width: 1536px) { .toc { display: block; } }
.toc-label { display: block; color: var(--gray-1100); }
.toc ul {
  list-style: none;
  margin: var(--s-4) 0 0;
  padding: 0;
  border-inline-start: var(--border-hairline) solid var(--gray-400);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
}
.toc li a {
  display: block;
  margin-inline-start: var(--s-5);
  max-width: 200px;
  color: var(--gray-1100);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 150ms ease-out;
}
.toc li a:hover { color: var(--gray-1200); }

/* index */
.index-wrap { max-width: 1144px; margin: var(--s-12) auto 160px; padding: 0 var(--s-6); }
.pill {
  display: inline-flex;
  align-items: center;
  height: 28px;
  padding: 0 var(--s-3);
  border-radius: 999px;
  background: var(--gray-300);
  font-size: var(--text-ui);
  margin-bottom: var(--s-8);
  user-select: none;
}
.cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-6) var(--s-8);
}
@media (min-width: 640px) { .cards { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .cards { grid-template-columns: repeat(3, 1fr); } }
.card {
  display: block;
  padding: var(--s-5);
  min-height: 180px;
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--elevation-raised);
  transition: box-shadow 150ms ease-out;
}
@media (hover: hover) and (pointer: fine) {
  .card:hover { box-shadow: var(--elevation-raised-hover); }
}
.card-num {
  font-family: var(--mono);
  font-size: var(--text-xs);
  color: var(--gray-1000);
  font-variant-numeric: tabular-nums;
}
.card h3 {
  margin: var(--s-6) 0 var(--s-1);
  font-size: var(--text-body);
  line-height: var(--leading-heading);
  font-weight: var(--weight-medium);
  text-wrap: balance;
}
.card p { margin: 0; color: var(--gray-1100); font-size: 15px; line-height: 1.6; text-wrap: pretty; }
/* home: the Start here prose above the lesson cards */
.home-intro { max-width: var(--measure); margin-bottom: var(--s-12); }
.home-intro h1 {
  margin: 0 0 var(--s-4);
  font-size: var(--text-h1);
  line-height: var(--leading-heading);
  font-weight: var(--weight-medium);
  letter-spacing: -0.01em;
}
.home-intro p { margin: var(--s-3) 0; color: var(--gray-1100); }
.home-intro ul { margin: var(--s-3) 0; padding-inline-start: var(--s-5); color: var(--gray-1100); }
.home-intro li { margin: var(--s-1) 0; }
.home-intro strong { font-weight: var(--weight-medium); color: var(--gray-1200); }
.home-intro a { text-decoration: underline; text-underline-position: from-font; transition: color 150ms ease-out; }
.home-intro a:hover { color: var(--gray-1200); }
/* home: narrow single column, list of lessons after the intro (benji.org) */
.index-wrap.home { max-width: 36.375rem; margin-top: var(--s-16); }
.index-wrap.home .home-intro { max-width: none; margin-bottom: 0; }
.index-wrap.home .home-intro h1 { font-size: var(--text-h2); margin-bottom: var(--s-2); }
.index-wrap.home .home-intro p,
.index-wrap.home .home-intro li { font-size: var(--text-ui); line-height: 1.45; }
.post-list { padding-top: var(--s-12); }
.post-list ul { list-style: none; margin: 0; padding: 0; }
.list-title {
  margin: 0;
  padding-bottom: var(--s-2);
  border-bottom: 1px solid var(--gray-300);
  font-size: var(--text-ui);
  font-weight: var(--weight-normal);
  color: var(--gray-1000);
}
.post-list > ul > li { border-bottom: 1px solid var(--gray-300); }
.post-list > ul > li:last-child { border-bottom: none; }
.post-list > ul > li > ul > li > a {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: 12px 0 12px 6.75rem; /* 12 + 20 + 12 = 44px row */
  font-size: var(--text-ui);
  line-height: 20px;
  text-decoration: none;
}
.post-list > ul > li > ul > li > a:focus-visible { outline-offset: 0; }
@media (max-width: 520px) { .post-list > ul > li > ul > li > a { padding-left: 4.5rem; } }
.post-list > ul > li > ul > li > a::after {
  content: "";
  position: absolute;
  left: 6.75rem; right: 0; bottom: 0;
  border-bottom: 1px solid var(--gray-300);
}
@media (max-width: 520px) { .post-list > ul > li > ul > li > a::after { left: 4.5rem; } }
.post-list > ul > li > ul > li:last-child > a::after { display: none; }
.list-name {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  color: var(--gray-1200);
  transition: opacity 140ms ease;
}
.list-group {
  position: absolute;
  left: 0;
  visibility: hidden;
  color: var(--gray-1000);
}
.post-list > ul > li > ul > li:first-child > a .list-group { visibility: visible; }
.list-num {
  font-family: var(--mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--gray-1000);
  transition: opacity 140ms ease;
}
.list-draft {
  font-size: var(--text-xs);
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--gray-300);
  color: var(--gray-1100);
}
@media (hover: hover) and (min-width: 521px) {
  .post-list > ul:hover .list-name,
  .post-list > ul:hover .list-num { opacity: 0.3; }
  .post-list > ul:hover a:hover .list-name,
  .post-list > ul:hover a:hover .list-num,
  .post-list > ul:hover a:focus-visible .list-name,
  .post-list > ul:hover a:focus-visible .list-num { opacity: 1; }
}
.archive-note { margin: var(--s-12) 0 0; font-size: var(--text-sm); color: var(--gray-1000); }
.archive-note a { color: var(--gray-1200); text-decoration: underline; text-underline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition-duration: 0.01ms !important; }
  .pager a:active { transform: none; }
}

/* ---- lesson components ---- */

/* prompt card: what you type to the agent. one row. copy tucked in the corner.
   flat grey like the code figures on benji.org/drawesome */
.prompt {
  position: relative;
  margin: var(--s-4) 0;
  padding: var(--s-3) var(--s-4);
  padding-inline-end: 52px; /* room for the copy control */
  border-radius: 12px;
  background: var(--gray-200);
}
@media (min-width: 1024px) { .prompt { margin-inline: -32px; padding-inline-start: 32px; } }
.prompt-body {
  display: flex;
  gap: var(--s-2);
  margin: 0;
  font-family: var(--mono);
  font-size: var(--text-sm);
  line-height: 24px;
  color: var(--gray-1100);
}
.prompt-text {
  white-space: pre-wrap;
  overflow-wrap: break-word;
  max-height: 72px; /* 3 lines at 24px line-height */
  overflow-y: auto;
  scrollbar-width: thin;
}
.prompt-text::-webkit-scrollbar { width: 6px; }
.prompt-text::-webkit-scrollbar-thumb { background: var(--gray-300); border-radius: 3px; }

/* ---- tweet hover preview (built by build.py: :::prompt tweet="id") ----
   the card is anchored to the prompt card, NOT inside .prompt-text —
   that one is a 3-line scroll box and would clip the popover */
.tp-wrap { position: relative; }
.tp-link {
  color: inherit;
  text-decoration: underline dotted var(--gray-400);
  text-underline-offset: 3px;
  /* the ann inline-block sizes an unbroken x.com URL to max-content and
     overflows narrow columns; cap it so break-word can wrap the URL */
  max-width: 100%;
  overflow-wrap: break-word;
}
@media (hover: hover) and (pointer: fine) {
  .tp-link:hover { text-decoration-style: solid; text-decoration-color: var(--gray-1100); }
}
.tp-card {
  position: absolute; z-index: 40;
  bottom: calc(100% + 12px); /* opens above the url by default */
  left: 0; right: 0;
  margin-inline: auto; /* centred on the prompt, which shares the lesson column's centre */
  width: min(400px, 100%);
  background: var(--surface);
  border-radius: 14px;
  box-shadow: 0 0 0 1px rgb(var(--shadow) / 0.08), 0 12px 32px -8px rgb(var(--shadow) / 0.18);
  text-align: left;
  opacity: 0; visibility: hidden;
  transform: translateY(-5px) scale(0.98);
  transform-origin: bottom center;
  transition: opacity 120ms ease-in 300ms, transform 120ms ease-in 300ms, visibility 0s linear 420ms;
}
.tp-card.is-below {
  bottom: auto; top: calc(100% + 12px);
  transform-origin: top center;
  transform: translateY(5px) scale(0.98);
}
/* prose cards open per link via the script's is-open toggle: a paragraph can
   hold several links, and a :has() on the paragraph cannot tell their cards
   apart (it opened all of them at once) */
.prompt:has(.tp-link:hover) .tp-card,
.prompt:has(.tp-card:hover) .tp-card,
.tp-card:focus-within,
.tp-card.is-open {
  opacity: 1; visibility: visible;
  transform: translateY(0) scale(1);
  transition: opacity 180ms cubic-bezier(0.32, 0.72, 0, 1) 200ms,
              transform 180ms cubic-bezier(0.32, 0.72, 0, 1) 200ms,
              visibility 0s linear 0s;
}
@media (prefers-reduced-motion: reduce) {
  .tp-card, .tp-card.is-below,
  .prompt:has(.tp-link:hover) .tp-card,
  .prompt:has(.tp-card:hover) .tp-card,
  .tp-card:focus-within,
  .tp-card.is-open { transition: none; }
}
.tp-head { display: flex; align-items: center; gap: 10px; padding: 14px 16px 0; }
.tp-avatar { width: 40px; height: 40px; border-radius: 50%; flex: none; }
.tp-id { min-width: 0; }
.tp-name { display: flex; align-items: center; gap: 4px; font-weight: 600; color: var(--gray-1200); font-size: var(--text-ui); }
.tp-sub { display: block; margin: 0; font-size: var(--text-sm); color: var(--gray-1000); }
.tp-x { margin-left: auto; flex: none; color: var(--gray-1000); }
.tp-body {
  display: block;
  padding: 10px 16px 16px;
  max-height: min(45vh, 420px); overflow: auto;
  font-size: var(--text-ui); line-height: 1.55; color: var(--gray-1200);
}
.tp-body .tp-p { display: block; margin: 0 0 9px; }
.tp-body .tp-p:last-child { margin-bottom: 0; }
.tp-body .tp-sub-p { color: var(--gray-1000); }

/* prose tweets: bare x.com links enhanced by build.py get the hover preview
   too. the card is a sibling of the link, anchored to the paragraph. */
p.has-tweet { position: relative; }

/* long prompt cards: a 3-line preview with a fade, and an expand control in
   the bottom right corner. build.py adds .is-long and the button. */
.expand {
  position: absolute;
  bottom: var(--s-2);
  inset-inline-end: var(--s-2);
  display: none;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--gray-1000);
  cursor: pointer;
  touch-action: manipulation;
  transition: background-color 150ms ease-out, color 150ms ease-out, transform 150ms ease-out;
}
.expand::before { content: ""; position: absolute; inset: -6px; } /* 44px hit area */
@media (hover: hover) and (pointer: fine) {
  .expand:hover { background: var(--gray-300); color: var(--gray-1200); }
}
.expand:active { transform: scale(0.96); }
.expand svg { display: block; transition: transform 180ms ease-out; }
.prompt.is-long .expand { display: grid; }
.prompt.is-open .expand svg { transform: rotate(180deg); }
/* overflowing prompt cards: a fade over the third line. any card whose text
   runs past the 3-line window gets it (JS toggles .is-clipped by real
   overflow, not a character estimate); scrolling to the end lifts the fade
   so the last line reads. cards stay scrollable; expand (.is-open) shows all. */
.prompt.is-clipped:not(.is-open) .prompt-text {
  mask-image: linear-gradient(#000 55%, transparent);
  -webkit-mask-image: linear-gradient(#000 55%, transparent);
}
.prompt.is-clipped.is-at-end:not(.is-open) .prompt-text {
  mask-image: none;
  -webkit-mask-image: none;
}
.prompt.is-open .prompt-text {
  max-height: none;
}
.prompt-actions {
  position: absolute;
  top: var(--s-2);
  inset-inline-end: var(--s-2);
  display: flex;
  align-items: center;
  gap: var(--s-1);
}
.copy {
  position: relative;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--gray-1000);
  cursor: pointer;
  touch-action: manipulation;
  transition: background-color 150ms ease-out, color 150ms ease-out, transform 150ms ease-out;
}
.copy::before { content: ""; position: absolute; inset: -6px; } /* 44px hit area */
@media (hover: hover) and (pointer: fine) {
  .copy:hover { background: var(--gray-300); color: var(--gray-1200); }
}
.copy:active { transform: scale(0.96); }
.copy.is-done { color: var(--gray-1200); }
/* icon morph (from benji.org/drawesome): the two sheets of the copy glyph merge
   into one rounded square, then the check draws itself in. reverting waits for
   the check to undraw (.13s delay) before the sheets split apart. */
.copy-morph { display: block; overflow: visible; }
.copy-sheet {
  width: 10.5px;
  height: 10.5px;
  rx: 2px;
  transition:
    transform 0.12s ease-out,
    width 0.12s ease-out,
    height 0.12s ease-out,
    rx 0.12s ease-out;
}
.copy:not(.is-done) .copy-sheet { transition-delay: 0.13s; }
.copy.is-done .copy-sheet { width: 14.5px; height: 14.5px; rx: 7.25px; }
.copy.is-done .copy-sheet-back,
.copy.is-done .copy-sheet-mask { transform: translateY(-4px); }
.copy.is-done .copy-sheet-front { transform: translateX(-4px); }
.copy-check {
  stroke-dasharray: 10;
  stroke-dashoffset: 10;
  opacity: 0;
  transition: stroke-dashoffset 0.1s ease-out, opacity 0s 0.1s;
}
.copy.is-done .copy-check {
  stroke-dashoffset: 0;
  opacity: 1;
  transition: stroke-dashoffset 0.1s ease-out 0.09s, opacity 0s;
}
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
/* prompt directly after a short lead-in paragraph sits closer to it */
p:has(+ .prompt) { margin-bottom: var(--s-2); }

/* reply card: what the agent said back. white page against the grey prompt,
   proportional type because the agent writes prose. a reply right after its
   prompt tucks up under it, so the pair reads as one exchange */
.reply {
  position: relative;
  margin: var(--s-4) 0;
  padding: var(--s-3) var(--s-4);
  border-radius: 12px;
  background: var(--surface);
  box-shadow: 0 0 0 1px rgb(var(--shadow) / 0.08);
  font-size: var(--text-sm);
  line-height: 24px;
  color: var(--gray-1100);
}
@media (min-width: 1024px) { .reply { margin-inline: -32px; padding-inline-start: 32px; } }
.prompt + .reply, .ann:has(> .prompt) + .reply, .prompt + .ann:has(> .reply), .ann:has(> .prompt) + .ann:has(> .reply) { margin-top: calc(-1 * var(--s-2)); }
.reply > :first-child { margin-top: 0; }
.reply > :last-child { margin-bottom: 0; }
.reply p { margin: var(--s-2) 0; }
.reply ul, .reply ol { margin: var(--s-2) 0; padding-inline-start: var(--s-5); }
.reply li { margin: 2px 0; }
.reply strong { font-weight: var(--weight-medium); color: var(--gray-1200); }
.reply code { font-family: var(--mono); font-size: 0.92em; }
.reply a { text-decoration: underline; text-underline-position: from-font; }
/* file card: an excerpt from a real file. same page as the reply card, with
   the file's path in a thin header, like the doc viewer in Talking to agents */
.file {
  position: relative;
  margin: var(--s-4) 0;
  border-radius: 12px;
  background: var(--surface);
  box-shadow: 0 0 0 1px rgb(var(--shadow) / 0.08);
  overflow: hidden;
  font-size: var(--text-sm);
  line-height: 24px;
  color: var(--gray-1100);
}
@media (min-width: 1024px) { .file { margin-inline: -32px; } }
.file-head {
  display: flex; align-items: center;
  padding: 7px var(--s-4);
  background: var(--gray-200);
  border-bottom: 1px solid var(--gray-300);
}
.file-head { gap: var(--s-2); }
.file-name { font-family: var(--mono); font-size: var(--text-xs); color: var(--gray-1000); overflow-wrap: anywhere; }
/* the kind of file, read from its extension: the icon is the only colour the
   card adds. same hues as the margin notes */
.file-kind { display: grid; place-items: center; flex: none; color: var(--gray-1000); }
.file[data-file-kind="prose"] .file-kind { color: var(--kind-prose); }
.file[data-file-kind="config"] .file-kind { color: var(--kind-config); }
.file[data-file-kind="script"] .file-kind { color: var(--kind-script); }
/* key -> value rows (a toml, a json, a frontmatter). one grid for the list,
   so the key column lines up on the longest key */
.file-kv {
  display: grid;
  grid-template-columns: fit-content(40%) minmax(0, 1fr);
  column-gap: var(--s-4);
  margin: var(--s-2) 0;
  padding: 0;
}
.file-kv-row { display: contents; }
.file-kv dt, .file-kv dd {
  margin: 0;
  padding: 5px 0;
  border-bottom: 1px solid var(--gray-300);
  font-family: var(--mono);
  font-size: var(--text-xs);
  line-height: 20px;
  overflow-wrap: anywhere;
}
.file-kv-row:last-child > * { border-bottom: 0; }
.file-kv dt { color: var(--gray-1000); }
.file-kv dd { color: var(--gray-1200); }
/* a long string is prose: proportional, wrapped, paragraph breaks kept */
.file-kv dd.is-prose {
  font-family: var(--font);
  font-size: var(--text-sm);
  line-height: 22px;
  color: var(--gray-1100);
  white-space: pre-line;
  text-wrap: pretty;
}
.file-muted { color: var(--gray-1000); }
@media (max-width: 560px) {
  .file-kv { grid-template-columns: 1fr; }
  .file-kv dt { padding-bottom: 0; border-bottom: 0; }
  .file-kv dd { padding-top: 0; }
}
/* frontmatter rows sit above the document they head */
.file-kv + * { margin-top: var(--s-3); padding-top: var(--s-3); border-top: 1px solid var(--gray-300); }
.file-tree { grid-template-columns: max-content minmax(0, 1fr); }
.file-tree dt, .file-tree dd { padding: 1px 0; border-bottom: 0; }
.file-tree .is-branch dt { color: var(--gray-1200); }
.file-tree-child {
  grid-column: 1 / -1;
  margin-inline-start: 3px;
  padding-inline-start: var(--s-4);
  border-inline-start: 1px solid var(--gray-300);
}
/* a script's comments, read as prose between the code */
.file-prose { margin: var(--s-2) 0; text-wrap: pretty; }
.file-body { padding: var(--s-3) var(--s-4); }
/* the icon, the path and the file's own text share one left edge */
@media (min-width: 1024px) { .file-head, .file-body { padding-inline-start: 32px; } }
.file-body > :first-child { margin-top: 0; }
.file-body > :last-child { margin-bottom: 0; }
.file-body p { margin: var(--s-2) 0; }
.file-body ul, .file-body ol { margin: var(--s-2) 0; padding-inline-start: var(--s-5); }
.file-body li { margin: 2px 0; }
.file-body strong { font-weight: var(--weight-medium); color: var(--gray-1200); }
.file-body code { font-family: var(--mono); font-size: 0.92em; }
/* fenced block inside a reply or file card: a file tree or a file's own text.
   plain mono, no pill, the card is already the frame */
.reply pre, .file-body pre {
  margin: var(--s-2) 0;
  padding: 0;
  overflow-x: auto;
  white-space: pre;
  tab-size: 2;
  font-family: var(--mono);
  font-size: var(--text-xs);
  line-height: 20px;
  color: var(--gray-1200);
}
.reply pre code, .file-body pre code { padding: 0; background: none; border: 0; border-radius: 0; font-size: inherit; }
.ann:has(> .file) { display: block; }
.file table, .reply table, .lesson table {
  width: 100%;
  margin: var(--s-2) 0;
  border-collapse: collapse;
  font-size: var(--text-sm);
  line-height: 1.4;
}
.file th, .file td, .reply th, .reply td, .lesson th, .lesson td {
  padding: 5px 10px 5px 0;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--gray-300);
}
.file th, .reply th, .lesson th { font-weight: var(--weight-medium); color: var(--gray-1200); }
.file tr:last-child td, .reply tr:last-child td, .lesson tr:last-child td { border-bottom: 0; }
.file .n, .reply .n, .lesson .n { font-variant-numeric: tabular-nums; white-space: nowrap; }
.ann:has(> .reply) { display: block; }

/* lesson images: full column width, same breakout as the other components.
   border treatment matches the website preview (wb-window): 12px corners and a
   hairline ring. outset ring, because an inset one hides under replaced content */
.lesson-image { margin: var(--s-6) 0; }
.lesson-image img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 12px;
  box-shadow: 0 0 0 1px rgb(var(--shadow) / 0.08);
}

/* card annotations: neat-annotations is inline-block, which shrink-to-fits —
   an iframe inside it collapses to a default ~300px. block-level keeps the
   wrapped card at its normal width, negative margins included */
.ann:has(> .prompt), .ann:has(> figure.demo) { display: block; }

/* demo placeholder: swap for a screenshot / interactive / embed */
.demo { margin: var(--s-6) 0; }
.demo-frame {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: var(--s-1);
  min-height: 220px;
  padding: var(--s-4);
  border-radius: var(--radius);
  background: var(--gray-100);
  box-shadow: 0 0 0 1px rgb(var(--shadow) / 0.06) inset;
}
/* placeholder frames only: striped surface, inert content */
.demo-frame:not([data-demo]) {
  background:
    repeating-linear-gradient(-45deg, transparent 0 8px, rgb(var(--shadow) / 0.025) 8px 9px),
    var(--gray-200);
  user-select: none;
}
.demo-tag {
  font-family: var(--mono);
  font-size: var(--text-xs);
  color: var(--gray-1000);
}
.demo-title { font-size: var(--text-ui); font-weight: var(--weight-medium); color: var(--gray-1200); text-wrap: balance; }
.demo figcaption { margin-top: var(--s-2); font-size: var(--text-sm); color: var(--gray-1000); }
.demo figcaption p { margin: var(--s-1) 0; color: inherit; }

/* callouts */
.callout {
  margin: var(--s-6) 0;
  padding: var(--s-4) var(--s-5);
  border-radius: var(--radius);
  background: var(--gray-200);
}
.callout p { margin: var(--s-2) 0; }
.callout p:first-child { margin-top: 0; }
.callout p:last-child { margin-bottom: 0; }
.callout-title { font-weight: var(--weight-medium); color: var(--gray-1200) !important; }
.callout-yours { background: var(--surface); box-shadow: var(--elevation-raised); }

/* ---- data charts ---- */
.chart, .funnel { margin: var(--s-6) 0; }
.chart-svg { display: block; width: 100%; height: auto; }
.bar { fill: var(--gray-1200); }
.bar-codex { fill: var(--gray-1200); }
.bar-pi { fill: var(--bar-2); }
.bar-claude-code { fill: var(--bar-3); }
.bar-factory { fill: var(--bar-4); }
.axis { font-family: var(--mono); font-size: 10px; fill: var(--gray-1000); }
.axis-line { stroke: var(--gray-400); stroke-width: 1; }
.chart figcaption, .funnel figcaption {
  margin-top: var(--s-3);
  font-size: var(--text-sm);
  color: var(--gray-1100);
  line-height: 1.6;
}
.chart-total { font-weight: var(--weight-medium); color: var(--gray-1200); }
.chart-range { display: block; font-family: var(--mono); font-size: 11px; color: var(--gray-1000); margin-top: 2px; }
.num { font-variant-numeric: tabular-nums; }
.chart-legend {
  display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4);
  list-style: none; margin: var(--s-2) 0 0; padding: 0;
  color: var(--gray-1100);
}
.chart-legend li { display: inline-flex; align-items: center; gap: 6px; }
.chart-legend .num { color: var(--gray-1000); }
.swatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; background: var(--gray-1200); }
.swatch.bar-codex { background: var(--gray-1200); }
.swatch.bar-pi { background: var(--bar-2); }
.swatch.bar-claude-code { background: var(--bar-3); }
.swatch.bar-factory { background: var(--bar-4); }

/* funnel: how far sessions (or folders) got. bars shrink at each stage, live in green. */
.funnel-rows { display: flex; flex-direction: column; gap: var(--s-2); }
.funnel-row {
  display: grid;
  grid-template-columns: 4.6em minmax(11em, auto) 1fr;
  gap: var(--s-3);
  align-items: center;
}
.funnel-count {
  font-size: var(--text-h2);
  font-weight: var(--weight-medium);
  color: var(--gray-1200);
  text-align: end;
}
.funnel-label { color: var(--gray-1100); }
.funnel-pct { font-size: var(--text-xs); color: var(--gray-1000); }
.funnel-track { display: block; min-width: 0; }
.funnel-bar {
  display: block;
  height: 18px;
  border-radius: 4px;
  background: var(--gray-1200);
  min-width: 4px;
}
.funnel-bar.funnel-artifact { background: var(--bar-3); }
.funnel-bar.funnel-repo { background: var(--bar-2); }
.funnel-bar.funnel-live { background: #2f8f4e; }

/* demos, callouts, charts and funnels break out of the reading column, same as the prompt card.
   lives last so the `margin` shorthands above cannot reset it */
@media (min-width: 1024px) {
  .demo, .callout, .chart, .funnel, .lesson-image { margin-inline: -32px; }
}

/* inline tool marks: [[tool:name]] in a lesson -> the logo and the tool's name
   as one unbreakable unit, sized in em so it follows the text (aiforui.dev).
   is-investor tools reveal the investment on hover. */
.tool {
  position: relative; display: inline-block;
  white-space: nowrap; font-weight: var(--weight-medium); color: var(--gray-1200);
}
/* inline-block so the link's underline stops at the text and never runs
   under the mark or the gap after it */
.tool i { display: inline-block; font-style: normal; font-weight: 700; margin-right: .3em; }
.tool i svg, .tool i img { display: inline-block; height: .95em; width: auto; vertical-align: -.08em; }
.tool i svg { fill: currentColor; }
/* craft.gustavofior.com treatment: a faint underline sits under mark and name;
   on hover a grey pill fills the box (negative margin so the text does not
   shift) and the underline fades to the pill colour */
.tool a, article p .tool a {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--gray-400);
  text-underline-offset: 4px;
  margin: 0 -2px; padding: 3px 5px; border-radius: 4px;
  transition: color .15s ease-out, background-color .15s ease-out, text-decoration-color .15s ease-out;
}
.tool a:hover, .tool a:focus-visible {
  background: var(--gray-400);
  text-decoration-color: var(--gray-400);
}
/* lettermark fallback, for a tool with no svg in assets/logos */
.tool i:not(:has(svg)) { font-size: .8em; vertical-align: .12em; letter-spacing: .01em; }
.tool.is-investor::after {
  content: attr(data-investor);
  position: absolute; left: 50%; bottom: calc(100% + 7px);
  transform: translateX(-50%) scale(.96);
  background: var(--gray-1200); color: var(--gray-100);
  font-size: 12px; font-weight: var(--weight-medium); line-height: 1;
  padding: 6px 9px; border-radius: 6px; white-space: nowrap;
  opacity: 0; pointer-events: none;
  transition: opacity .12s ease, transform .12s ease;
}
.tool.is-investor:hover::after,
.tool.is-investor:focus-within::after { opacity: 1; transform: translateX(-50%) scale(1); }
@media (prefers-reduced-motion: reduce) {
  .tool.is-investor::after { transition: none; }
}

/* prompts show in full, no 3-line preview (Ben, 22 Sep 2026).
   delete this block to bring back the preview, fade and expand control. */
.prompt-text { max-height: none; overflow-y: visible; }
.prompt.is-clipped .prompt-text { mask-image: none; -webkit-mask-image: none; }
.prompt.is-long .expand { display: none; }

/* lede: the opening line under a page title */
.lede { font-size: 16px; line-height: 1.6; color: var(--gray-1200); margin-top: 8px; }
