/*
 * shadcn/typeset
 * https://ui.shadcn.com/docs/typeset.
 * MIT — vendored from https://ui.shadcn.com/typeset.css; upstream rules are
 * unmodified. gisthub-owned rules live in the "gisthub additions" section at
 * the bottom of this file.
 */

@layer components {
  .typeset {
    --typeset-font-body: inherit;
    --typeset-font-heading: var(--font-heading);
    --typeset-font-mono: var(--font-mono);
    --typeset-size: 1em;
    --typeset-leading: 1.75;
    --typeset-flow: 1.25em;
  }
}

@layer components {
  .typeset {
    font-family: var(--typeset-font-body);
    font-size: calc(var(--typeset-size) * 1.125);
    line-height: var(--typeset-leading);
    color: var(--color-foreground, currentColor);
    overflow-wrap: break-word;
    margin-trim: block-start;

    @media (min-width: 48rem), print {
      font-size: var(--typeset-size);
    }

    --typeset-muted: var(
      --color-muted-foreground,
      color-mix(in oklab, currentColor 60%, transparent)
    );
    --typeset-rule: var(--color-border, color-mix(in oklab, currentColor 20%, transparent));
  }

  .typeset *:not(:where(.not-typeset, [data-not-typeset], .not-typeset *, [data-not-typeset] *)) {
    /* Paragraphs. */
    &:where(p) {
      margin-block-start: var(--typeset-flow);
      margin-block-end: 0;
    }

    /* Headings. */
    &:where(h1, h2, h3, h4, h5, h6) {
      color: var(--color-foreground, currentColor);
      font-family: var(--typeset-font-heading);
      font-weight: 600;
      margin-block-end: 0;
    }
    &:where(h1) {
      font-size: 1.75em;
      line-height: 1.3;
      margin-block-start: var(--typeset-flow);
    }
    &:where(h2) {
      font-size: 1.25em;
      line-height: 1.4;
      margin-block-start: calc(var(--typeset-flow) * 1.4);
    }
    &:where(h3) {
      font-size: 1.125em;
      line-height: 1.45;
      margin-block-start: var(--typeset-flow);
    }
    &:where(h4) {
      font-size: 1em;
      line-height: 1.5;
      margin-block-start: var(--typeset-flow);
    }
    &:where(h5) {
      font-size: 0.875em;
      line-height: 1.5;
      font-weight: 500;
      color: var(--typeset-muted);
      margin-block-start: calc(var(--typeset-flow) / 0.875);
    }
    &:where(h6) {
      font-size: 0.8125em;
      line-height: 1.5;
      font-weight: 500;
      letter-spacing: 0.08em;
      text-transform: uppercase;
      color: var(--typeset-muted);
      margin-block-start: calc(var(--typeset-flow) / 0.8125);
    }
    /* Anchors. */
    &:where([id]) {
      scroll-margin-block-start: var(--typeset-flow);
    }

    /* Headings own the space below them. */
    &:where(h1 + *, h2 + *, h3 + *, h4 + *, h5 + *, h6 + *) {
      margin-block-start: 1em;
    }
    &:where(:is(h1, h2, h3, h4) :is(code)) {
      font-size: 0.9em;
    }

    /* Links. */
    &:where(a) {
      color: inherit;
      font-weight: 500;
      text-decoration-line: underline;
      text-decoration-color: color-mix(in oklab, currentColor 30%, transparent);
    }
    &:where(a:hover) {
      text-decoration-color: currentColor;
    }
    &:where(a:focus-visible) {
      outline: 2px solid var(--color-ring, currentColor);
      outline-offset: 2px;
      border-radius: 0.125em;
    }
    &:where(:is(h1, h2, h3, h4, h5, h6) :is(a)) {
      font-weight: inherit;
    }

    /* Inline semantics. */
    &:where(strong, b) {
      font-weight: 600;
    }
    &:where(:is(h1, h2, h3, h4) :is(strong, b)) {
      font-weight: 600;
    }
    &:where(mark) {
      background-color: color-mix(in oklab, oklch(0.86 0.17 95) 40%, transparent);
      color: inherit;
      padding: 0.05em 0.15em;
      border-radius: 0.2em;
    }
    &:where(abbr[title]) {
      text-decoration-line: underline;
      text-decoration-style: dotted;
      text-underline-offset: 0.2em;
      cursor: help;
    }
    &:where(del, s) {
      color: var(--typeset-muted);
      text-decoration-line: line-through;
    }
    &:where(sup, sub) {
      font-size: 0.75em;
      line-height: 0;
      position: relative;
      vertical-align: baseline;
    }
    &:where(sup) {
      top: -0.5em;
    }
    &:where(sub) {
      bottom: -0.25em;
    }
    &:where(sup a) {
      text-decoration-line: none;
      font-weight: 500;
    }

    /* Lists. */
    &:where(ul) {
      list-style-type: disc;
    }
    &:where(ol) {
      list-style-type: decimal;
    }
    &:where(ul ul) {
      list-style-type: circle;
    }
    &:where(ul ul ul) {
      list-style-type: square;
    }
    &:where(ol[type="a" i]) {
      list-style-type: lower-alpha;
    }
    &:where(ol[type="i" i]) {
      list-style-type: lower-roman;
    }
    &:where(ol[type="A" s]) {
      list-style-type: upper-alpha;
    }
    &:where(ol[type="I" s]) {
      list-style-type: upper-roman;
    }
    &:where(ul, ol) {
      margin-block-start: var(--typeset-flow);
      margin-block-end: 0;
      padding-inline-start: 1.5em;
    }
    &:where(li) {
      margin-block-start: 0.5em;
      padding-inline-start: 0.4em;
    }
    &:where(li > p, li > ul, li > ol) {
      margin-block-start: 0.5em;
    }
    &:where(ul > li)::marker {
      color: var(--typeset-muted);
    }
    &:where(ol > li)::marker {
      color: var(--typeset-muted);
    }

    /* GFM task lists. */
    &:where(ul.contains-task-list) {
      list-style-type: none;
      padding-inline-start: 0.25em;
    }
    &:where(li.task-list-item > input[type="checkbox"]) {
      margin-inline-end: 0.5em;
      vertical-align: -0.1em;
      accent-color: var(--color-primary, currentColor);
    }

    /* Disclosures. */
    &:where(details) {
      margin-block-start: var(--typeset-flow);
      margin-block-end: 0;
    }
    &:where(summary) {
      cursor: pointer;
      font-weight: 500;
    }
    &:where(summary)::marker {
      color: var(--typeset-muted);
    }

    /* Keyboard keys. */
    &:where(kbd) {
      font-family: inherit;
      font-size: 0.85em;
      font-weight: 500;
      border: 1px solid var(--typeset-rule);
      border-block-end-width: 2px;
      border-radius: min(calc(var(--radius, 0.5em) * 0.6), 0.35em);
      padding: 0.0625em 0.35em;
    }

    /* Definition lists. */
    &:where(dl) {
      margin-block-start: var(--typeset-flow);
      margin-block-end: 0;
    }
    &:where(dt) {
      font-weight: 500;
      margin-block-start: 1em;
    }
    &:where(dt + dt) {
      margin-block-start: 0.25em;
    }
    &:where(dd) {
      margin-block-start: 0.25em;
      margin-inline-start: 0;
      padding-inline-start: 1em;
      color: var(--typeset-muted);
    }

    /* Inline code. */
    &:where(:not(pre) > code) {
      background-color: var(--color-code-bg, color-mix(in oklab, currentColor 8%, transparent));
      font-family: var(--typeset-font-mono);
      font-size: 0.85em;
      border-radius: min(calc(var(--radius, 0.5em) * 0.6), 0.35em);
      padding: 0.125em 0.3em;
    }

    /* Code blocks. */
    &:where(pre) {
      background-color: var(--color-code-bg, color-mix(in oklab, currentColor 8%, transparent));
      font-family: var(--typeset-font-mono);
      font-size: 0.875em;
      line-height: 1.5;
      tab-size: 2;
      direction: ltr;
      border-radius: var(--radius, 0.5em);
      padding: 0.75em 1em;
      overflow-x: auto;
      margin-block-start: calc(var(--typeset-flow) / 0.875);
      margin-block-end: 0;
    }
    &:where(pre code) {
      background-color: transparent;
      font-family: inherit;
      font-size: inherit;
      padding: 0;
      border-radius: 0;
    }

    /* Blockquote. */
    &:where(blockquote) {
      border-inline-start: 2px solid var(--typeset-rule);
      padding-inline-start: 1em;
      margin-block-start: var(--typeset-flow);
      margin-block-end: 0;
      margin-inline: 0;
    }

    /* Dividers. */
    &:where(hr) {
      border: 0;
      border-block-start: 1px solid var(--typeset-rule);
      margin-block-start: calc(var(--typeset-flow) * 2.4);
      margin-block-end: 0;
    }
    &:where(hr + h1, hr + h2, hr + h3, hr + h4) {
      margin-block-start: var(--typeset-flow);
    }

    /* GFM footnotes. */
    &:where(.footnotes, [data-footnotes]) {
      margin-block-start: calc(var(--typeset-flow) * 2);
      border-block-start: 1px solid var(--typeset-rule);
      padding-block-start: var(--typeset-flow);
      font-size: 0.875em;
      color: var(--typeset-muted);
    }

    /* Math. */
    &:where(math[display="block"]) {
      margin-block-start: var(--typeset-flow);
      margin-block-end: 0;
      overflow-x: auto;
      overflow-y: hidden;
      padding-block: 0.25em;
    }

    /* Media. */
    &:where(img, video) {
      border-radius: var(--radius, 0.5em);
      max-width: 100%;
      height: auto;
      margin-block-start: var(--typeset-flow);
      margin-block-end: 0;
    }
    &:where(p img) {
      margin-block-start: 0;
    }
    &:where(figure) {
      margin-block-start: var(--typeset-flow);
      margin-block-end: 0;
      margin-inline: 0;
    }
    &:where(figcaption, caption) {
      color: var(--typeset-muted);
      font-size: 0.875em;
      text-align: center;
      margin-block-start: calc(0.75em / 0.875);
    }
    &:where(caption) {
      caption-side: bottom;
    }

    /* Tables. Separators sit on cells, not tr:last-child: append-safe.
       Bare tables stay real tables (keep their semantics) and wrap to fit. */
    &:where(table) {
      max-width: 100%;
      font-size: 1em;
      line-height: 1.5;
      font-variant-numeric: tabular-nums;
      border-collapse: separate;
      border-spacing: 0;
      border-block-end: 1px solid var(--typeset-rule);
      margin-block-start: var(--typeset-flow);
      margin-block-end: 0;
    }

    /* Horizontal scroll for any wide block. Wrap it and the wrapper owns the
       flow margin. Tables shrink to fit, so widen the table to make it scroll
       instead of compress. */
    &:where(.typeset-scroll) {
      overflow-x: auto;
      margin-block-start: var(--typeset-flow);
    }
    &:where(.typeset-scroll > *) {
      margin-block-start: 0;
    }
    &:where(.typeset-scroll table) {
      width: max-content;
      max-width: none;
    }
    &:where(thead th) {
      font-weight: 500;
      text-align: start;
      white-space: nowrap;
      padding: 0.65em 1em;
    }
    &:where(:is(tbody, tfoot) :is(td, th)) {
      padding: 0.75em 1em;
      text-align: start;
      vertical-align: top;
      border-block-start: 1px solid var(--typeset-rule);
    }
    &:where(tbody th, tfoot th) {
      font-weight: 500;
    }
    &:where(th:first-child, td:first-child) {
      padding-inline-start: 0;
    }
    &:where(th[align="center"], td[align="center"]) {
      text-align: center;
    }
    &:where(th[align="right"], td[align="right"]) {
      text-align: end;
    }

    /* Blocks inside list items keep the list's tighter rhythm. */
    &:where(li > blockquote, li > table, li > figure) {
      margin-block-start: 0.5em;
    }
    &:where(li > pre) {
      margin-block-start: calc(0.5em / 0.875);
    }

    @media print {
      &:where(pre, table, blockquote, figure) {
        break-inside: avoid;
      }
      &:where(h1, h2, h3, h4) {
        break-after: avoid;
      }
    }

    /* Forced colors strips the code background, so give it a border. */
    @media (forced-colors: active) {
      &:where(:not(pre) > code) {
        border: 1px solid;
      }
    }
  }

  /* First child adds no space above. Last so it wins ties. */
  .typeset
  > :where(:first-child):not(
    :where(.not-typeset, [data-not-typeset], .not-typeset *, [data-not-typeset] *)
  ),
  .typeset
    > :where(:first-child)
    > :where(:first-child):not(
      :where(.not-typeset, [data-not-typeset], .not-typeset *, [data-not-typeset] *)
    ),
  .typeset
    :where(
      li > :first-child,
      blockquote > :first-child,
      td > :first-child,
      th > :first-child,
      dd > :first-child,
      figure > :first-child,
      figure > picture > img
    ):not(:where(.not-typeset, [data-not-typeset], .not-typeset *, [data-not-typeset] *)) {
    margin-block-start: 0;
  }
}

/* ---------------------------------------------------------------------------
 * gisthub additions — everything below is ours.
 *
 * Constraints this section encodes:
 * - gistdown wraps every top-level block in <div class="gd-block"> (the
 *   anchor surface for block comments), and hosts interleave thread UI
 *   between blocks, so inter-block flow must never key on block adjacency.
 * - Reader preferences arrive as --gh-size / --gh-leading / --gh-flow /
 *   --gh-font, set inline on the typeset container (web app) or the document
 *   root (renderer). The presets read them with house defaults as fallbacks,
 *   so Reset is simply the absence of the variables. The JSON blob behind
 *   them (localStorage "gh-typeset") is specified in
 *   apps/web/src/lib/typeset.ts; the renderer mirrors it in vanilla JS.
 * ------------------------------------------------------------------------ */

@layer components {
  /* Upstream points headings/mono at --font-heading / --font-mono, which
     neither surface defines — an undefined var() in font-family invalidates
     the declaration and code blocks would inherit the body serif. */
  .typeset {
    --typeset-font-heading: ui-sans-serif, system-ui, sans-serif;
    --typeset-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
  }

  /* gd-block flow. Each block owns the space above itself — never
     `.gd-block + .gd-block`: a comment thread inserted between two blocks
     breaks adjacency and would strip its lower neighbor's lead. The wrapper
     is unpadded, so its margin collapses with the first child's own typeset
     margin (max wins) — heading-led blocks keep their larger lead, and an
     inserted sibling never restyles either neighbor. */
  .typeset :where(.gd-block) {
    margin-block-start: var(--typeset-flow);
  }
  /* Upstream zeroes .typeset > :first-child, but the flow rule above is
     later in the file and would win the specificity tie — re-zero. */
  .typeset > :where(.gd-block:first-child) {
    margin-block-start: 0;
  }

  /* House presets. typeset-doc is the reading default — the renderer's
     long-standing feel: 17px-equivalent serif, 1.7 leading, roomy flow. */
  .typeset-doc {
    --typeset-size: var(--gh-size, 1.0625em);
    --typeset-leading: var(--gh-leading, 1.7);
    --typeset-flow: var(--gh-flow, 1.5em);
    --typeset-font-body: var(--gh-font, ui-serif, "Iowan Old Style", Georgia, serif);
  }
  /* typeset-chat: compact panels (ChatDock, comment bodies) — sans, tighter,
     slightly smaller. Reader deltas scale in rather than replace, so
     "larger text" enlarges chat without turning it into the doc layout. */
  .typeset-chat {
    --typeset-size: calc(var(--gh-size, 1.0625em) * 0.875);
    --typeset-leading: calc(var(--gh-leading, 1.7) * 0.88);
    --typeset-flow: calc(var(--gh-flow, 1.5em) * 0.45);
    --typeset-font-body: ui-sans-serif, system-ui, sans-serif;
  }

  /* not-typeset exempts chrome from typeset's RULES, but font-family and
     line-height still INHERIT from the serif container — reset them to the
     app baseline so UI chrome inside a doc never goes serif. Sizing stays
     with the chrome's own utility classes (utilities layer wins). */
  .typeset :where(.not-typeset, [data-not-typeset]) {
    font-family: ui-sans-serif, system-ui, sans-serif;
    line-height: 1.5;
  }

  /* Dark canvases read denser at equal leading — loosen the default a step.
     Expressed as a different fallback so an explicit reader choice
     (--gh-leading) wins in both color schemes. */
  @media (prefers-color-scheme: dark) {
    .typeset-doc {
      --typeset-leading: var(--gh-leading, 1.8);
    }
    .typeset-chat {
      --typeset-leading: calc(var(--gh-leading, 1.8) * 0.88);
    }
  }
}

/* Shiki syntax-highlighting dual-theme flip. Highlighted tokens carry the light
   colour inline (`color:`) and the dark one in `--shiki-dark`; swap to it for
   dark readers. !important is load-bearing: the light colour is an INLINE style,
   which beats any selector — without it dark readers get github-light's
   near-black tokens on a dark page (Shiki's own dual-theme guidance). An
   explicit theme toggle (data-theme) wins over the system preference. Spans
   without the var (plain fences, .gd-line wrappers) fall back to inherited
   colour, so unhighlighted code is untouched. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .gd-code span {
    /* biome-ignore lint/complexity/noImportantStyles: load-bearing — must beat Shiki's INLINE light colour on dark readers (see rationale above) */
    color: var(--shiki-dark) !important;
  }
}
:root[data-theme="dark"] .gd-code span {
  /* biome-ignore lint/complexity/noImportantStyles: load-bearing — must beat Shiki's INLINE light colour under an explicit dark theme (see rationale above) */
  color: var(--shiki-dark) !important;
}

/* --- gistdown v3: callouts, footnotes, math ------------------------------- */
.gd-callout {
  border-left-width: 3px;
  border-radius: 0 6px 6px 0;
  padding: 0.6em 1em;
}
.gd-callout-badge {
  display: block;
  font-weight: 600;
  font-size: 0.85em;
  letter-spacing: 0.02em;
  margin-bottom: 0.15em;
}
.gd-callout-note {
  border-left-color: #3b82f6;
  background: color-mix(in srgb, #3b82f6 7%, transparent);
}
.gd-callout-note .gd-callout-badge {
  color: #3b82f6;
}
.gd-callout-tip {
  border-left-color: #10b981;
  background: color-mix(in srgb, #10b981 7%, transparent);
}
.gd-callout-tip .gd-callout-badge {
  color: #10b981;
}
.gd-callout-important {
  border-left-color: #8b5cf6;
  background: color-mix(in srgb, #8b5cf6 7%, transparent);
}
.gd-callout-important .gd-callout-badge {
  color: #8b5cf6;
}
.gd-callout-warning {
  border-left-color: #f59e0b;
  background: color-mix(in srgb, #f59e0b 8%, transparent);
}
.gd-callout-warning .gd-callout-badge {
  color: #b45309;
}
.gd-callout-caution {
  border-left-color: #ef4444;
  background: color-mix(in srgb, #ef4444 7%, transparent);
}
.gd-callout-caution .gd-callout-badge {
  color: #ef4444;
}
.gd-footnote {
  font-size: 0.875em;
  color: color-mix(in srgb, currentColor 75%, transparent);
  padding-left: 0.25em;
}
.gd-footnote-num {
  text-decoration: none;
  font-weight: 600;
}
.gd-fnref a {
  text-decoration: none;
  font-weight: 600;
  padding: 0 0.15em;
}
.gd-math-block {
  overflow-x: auto;
  text-align: center;
  padding: 0.5em 0;
}
.gd-math code,
.gd-math-block code {
  background: color-mix(in srgb, currentColor 8%, transparent);
  border-radius: 4px;
  padding: 0.1em 0.35em;
}

/* --- gistdown v5: heading deep-link affordance ---------------------------- */
/* The static <a href="#slug"> gistdown appends to every top-level heading —
   pure markup styled here for BOTH reading surfaces (SPA and renderer; the
   renderer ships no script, so hover-reveal is CSS only). Color rides the
   host's token when defined (--color-faint, ALLOY) and falls back to the
   typeset muted mix on token-less pages. */
.gd-headlink {
  margin-left: 0.35em;
  font-size: 0.75em;
  font-weight: 400;
  text-decoration: none;
  color: var(--color-faint, var(--typeset-muted, currentColor));
  opacity: 0;
  transition: opacity 120ms ease;
}
:is(h1, h2, h3, h4, h5, h6):hover .gd-headlink,
.gd-headlink:focus-visible {
  opacity: 1;
}
/* No hover on touch — keep the affordance faintly visible instead of gone. */
@media (hover: none) {
  .gd-headlink {
    opacity: 0.55;
  }
}
