{
  "$schema": "https://williamzujkowski.github.io/remarque/registry-item.schema.json",
  "name": "essay",
  "type": "remarque:contract",
  "title": "Essay Module",
  "description": "Sidenotes + sticky TOC rail for long-form Essay pages: margin notes relocated into real DOM order, right after the paragraph that cites them, numbered by CSS counter and labeled `aria-label=\"Note N\"` in the same order the counter advances — the mechanism that prevents the sidenote aria-label/DOM-order transcription bug found in the flagship migration (#89, williamzujkowski.github.io#380).",
  "version": "0.26.0",
  "integrity": "sha256-zr04H0+0xf01OWLk6B2A9ziZOYJd4Sq5q5/cib65lbU=",
  "dependencies": [
    "remarque-tokens"
  ],
  "cssVars": [
    "--border-style",
    "--border-width",
    "--color-accent",
    "--color-accent-hover",
    "--color-border",
    "--color-border-bold",
    "--color-fg",
    "--color-fg-muted",
    "--color-muted",
    "--content-reading",
    "--font-mono",
    "--leading-meta",
    "--motion-easing",
    "--motion-fast",
    "--space-1",
    "--space-10",
    "--space-11",
    "--space-12",
    "--space-2",
    "--space-3",
    "--space-4",
    "--space-5",
    "--space-6",
    "--space-7",
    "--space-8",
    "--text-meta",
    "--tracking-caps",
    "--weight-semibold",
    "--z-sticky"
  ],
  "docs": "https://github.com/williamzujkowski/remarque/blob/main/REMARQUE.md#essay-module",
  "files": [
    {
      "path": "essay.css",
      "content": "/*\n * Remarque — Essay Module (sidenotes + sticky TOC rail)\n * ───────────────────────────────────────────────────────\n * Optional Essay-archetype module: footnotes become in-flow margin notes\n * (Tufte/gwern-style), and an \"on this page\" table of contents pins to a\n * sticky rail alongside the reading column. Both are progressive\n * enhancements over a plain Essay page — CSS-only, no JavaScript required\n * at either breakpoint.\n *\n * PROVENANCE — graduated, not invented here. This module re-expresses, in\n * Remarque's own token vocabulary, a design first built and shipped on\n * williamzujkowski.github.io (`astro-site/src/lib/rehype-sidenotes.mjs` +\n * `global.css`'s \"Sidenotes\"/\"Sticky TOC side rail\" blocks). Ratified for\n * upstreaming in issue #52. No site-specific value (the flagship's literal\n * `11.5rem`/`13rem`/`14rem`/`1280px` measurements) was copied — every\n * dimension below is re-derived from tokens already in tokens-core.css /\n * tokens-palette.css. See REMARQUE.md's \"Essay Module\" section for the\n * full markup contract, and this file's inline comments for exactly which\n * site value maps to which token and why a few mechanics changed shape\n * (CSS counters instead of a build-time numbering step; `<details open>`\n * by default instead of a JS breakpoint listener) on the way upstream.\n *\n * Own subpath (remarque-tokens/essay, remarque-tokens/essay.css) — NOT\n * included by the tokens.css aggregator, and not required by prose.css:\n * plenty of Essay pages want neither sidenotes nor a TOC rail. Import\n * explicitly, after prose.css:\n *   @import 'remarque-tokens/prose.css';\n *   @import 'remarque-tokens/essay.css';\n * (string form — see AGENT_RULES.md Pitfall #6.)\n *\n * ─── MARKUP CONTRACT (summary — full version in REMARQUE.md) ──────────\n *\n *   <article class=\"remarque-essay\">\n *     <header class=\"content-reading\">...</header>\n *\n *     <nav class=\"remarque-toc-rail\" aria-label=\"Table of contents\">\n *       <details open>\n *         <summary>On this page</summary>\n *         <ul>\n *           <li><a href=\"#section-one\">Section One</a></li>\n *         </ul>\n *       </details>\n *     </nav>\n *\n *     <div class=\"remarque-prose content-reading\">\n *       <p>\n *         A claim worth a citation<a href=\"#note-1\" id=\"note-1-ref\"\n *           class=\"remarque-sidenote-ref\" aria-describedby=\"note-1\"></a>.\n *       </p>\n *       <aside class=\"remarque-sidenote\" id=\"note-1\" role=\"note\">\n *         The note text, in real DOM order right after the paragraph that\n *         cites it — not collected into an end-of-document section.\n *       </aside>\n *     </div>\n *\n *     <footer class=\"content-reading\">...</footer>\n *   </article>\n *\n * DOM order is the accessibility requirement, not a presentational nicety:\n * a screen reader hits a sidenote's content exactly where its reference\n * occurs. `.remarque-sidenote-ref` and its matching `.remarque-sidenote`\n * MUST alternate in strict DOM order (ref, then note, then the next ref,\n * then its note...) — the counter mechanics below depend on it. `role=\n * \"note\"` keeps a page with several sidenotes from scattering that many\n * unlabeled `complementary` landmarks through the document (`<aside>`'s\n * implicit role) — the same fix the flagship's rehype plugin documents.\n *\n * This module is deliberately silent on HOW that markup gets produced. A\n * build-time transform (the flagship's rehype-sidenotes.mjs is one worked\n * example, not a dependency of this package) or hand-authored HTML both\n * satisfy the contract equally — see REMARQUE.md for the no-build-step\n * fallback (`.remarque-footnotes`) if you don't have one.\n *\n * ─── BREAKPOINT ─────────────────────────────────────────────────────────\n * Gated on >= 80rem (1280px, at the platform default 16px root) viewport\n * width — the same threshold the flagship ratified, re-derived here from\n * this package's own tokens instead of asserted as a bare magic number:\n * `--content-standard` (72rem) is the widest a Remarque page's content\n * ever grows, and the rail/gutter tracks below need `--space-11`+\n * `--space-12` (10-12rem) and `--space-10` (8rem) of clearance on top of\n * `--content-reading` (46rem default) plus two `--space-6` gaps — call it\n * roughly 70-76rem of content width before both tracks have their\n * documented minimum room, plus headroom for a scrollbar and window\n * chrome. 80rem clears that with margin to spare across every approved\n * body-face/measure pairing (see REMARQUE.md's Measure Compensation\n * table) without needing a `--breakpoint-*` token this package doesn't\n * otherwise have. Below it: sidenotes render as small-print inline blocks\n * exactly where the plain document flow already places them, and the TOC\n * is a collapsible `<details>` in the reading column — this is also the\n * ONLY rendering a consumer who skips this module's `@media` block ever\n * sees, so nothing here is required for the page to work.\n */\n\n/* ─── Sidenotes — default / narrow-viewport mode ────────────────────────\n * `.remarque-sidenote` renders as a bordered small-print block right in\n * the text flow by default. This IS the narrow-viewport fallback the\n * spec calls for — zero extra markup, zero JS: the note just isn't\n * floated into a margin yet.\n *\n * Numbering is generated, not authored: `counter-reset` on\n * `.remarque-prose`, `counter-increment` on `.remarque-sidenote-ref` only,\n * and `.remarque-sidenote` simply READING that same counter (no increment\n * of its own — see the comment on that rule) keep the visible number in\n * sync across the reference and its note with no number typed into\n * either element — the flagship's rehype plugin injects a literal\n * `<span class=\"sidenote-number\">1</span>`/text node at build time; this\n * package ships no build step, so CSS counters do the same job for a\n * pure-CSS module. A repeat citation of an already-noted footnote should\n * use `.remarque-sidenote-ref--repeat` (no `.remarque-sidenote` follows\n * it) so the shared counter isn't advanced for a note that was already\n * printed — mirrors the flagship's \"only the first reference gets a\n * sidenote\" rule.\n */\n.remarque-prose {\n  counter-reset: remarque-sidenote;\n}\n\n.remarque-sidenote-ref {\n  counter-increment: remarque-sidenote;\n  font-family: var(--font-mono);\n  font-size: var(--text-meta);\n  /* USWDS floor rationale: --text-micro is reserved for timestamps/fine\n     print only (tokens-core.css, REMARQUE.md \"Font Size Floors\") — the\n     flagship's own marker used that size, but this module holds to\n     Remarque's stricter 14px floor for anything read as content. */\n  line-height: 0;\n  vertical-align: super;\n  color: var(--color-accent);\n  text-decoration: none;\n}\n\n.remarque-sidenote-ref::after {\n  content: counter(remarque-sidenote);\n}\n\n.remarque-sidenote-ref:hover {\n  color: var(--color-accent-hover);\n  text-decoration: underline;\n}\n\n.remarque-sidenote-ref--repeat {\n  counter-increment: none;\n}\n\n.remarque-sidenote {\n  /* No counter-increment here: `.remarque-sidenote-ref` already advanced\n     `remarque-sidenote` immediately before this element in DOM order —\n     incrementing again would double-count every pair (ref shows N, the\n     note that follows it would show N+1 instead of N). This element only\n     READS the counter its ref just set. */\n  display: block;\n  margin-block: var(--space-3);\n  padding-inline-start: var(--space-3);\n  /* border-bold: this is the note's ONLY visual separator from running\n     prose at narrow widths, so it must clear the 3:1 functional-border\n     minimum (WCAG 1.4.11) — same reasoning as .remarque-prose blockquote. */\n  border-inline-start: var(--border-width) var(--border-style) var(--color-border-bold);\n  font-family: var(--font-mono);\n  font-size: var(--text-meta);\n  line-height: var(--leading-meta);\n  color: var(--color-fg-muted);\n}\n\n/* The generated number precedes whatever content the note holds. If that\n   content is a single run of inline text (no wrapping <p>), the number\n   sits inline with the opening words — the flagship's \"1 Note text...\"\n   pairing. A `<p>`-wrapped note (multi-paragraph notes need it) instead\n   reads as a small label above the paragraph — a pure-CSS module can't\n   reach inside an arbitrary child the way a build-time transform can, so\n   this is the honest degradation: prefer bare text for single-line notes. */\n.remarque-sidenote::before {\n  content: counter(remarque-sidenote) \"\\2002\";\n  color: var(--color-accent);\n  font-weight: var(--weight-semibold);\n}\n\n.remarque-sidenote :where(p) {\n  margin: 0;\n}\n\n.remarque-sidenote :where(p) + :where(p) {\n  margin-top: var(--space-2);\n}\n\n/* ─── Sidenotes — wide-viewport mode: float into the left margin ────────\n * Classic Tufte CSS technique: `.remarque-sidenote` isn't a grid child\n * itself (it's nested inside `.remarque-prose`, which IS grid-column 2 —\n * see `.remarque-essay` below) so grid placement doesn't apply to it\n * directly. Instead it floats left with a negative inline-start margin\n * exactly wide enough to clear its own box plus one column-gap unit,\n * landing in the grid's column-1 gutter. `clear: left` stacks consecutive\n * notes without any JS-computed offset.\n *\n * Width (`--space-10`, 8rem) and pull-out distance (that width plus\n * `--space-6`, the same gap the grid below uses between tracks) are both\n * drawn from the core spacing scale rather than asserting a bespoke\n * sidebar-width value — this is the token-vocabulary re-expression of the\n * flagship's hand-measured `11.5rem`/`13rem` pair.\n */\n@media (min-width: 80rem) {\n  .remarque-sidenote {\n    float: left;\n    clear: left;\n    width: var(--space-10);\n    margin-inline-start: calc(-1 * (var(--space-10) + var(--space-6)));\n    margin-block: var(--space-1) var(--space-4);\n    padding-inline-start: 0;\n    padding-inline-end: var(--space-2);\n    border-inline-start: none;\n    /* Decorative-only at this width (the gutter's own whitespace is the\n       real separator now) — `--color-border`, not `-bold`, matching the\n       flagship's own choice to relax the border once it's no longer the\n       sole boundary cue. */\n    border-inline-end: var(--border-width) var(--border-style) var(--color-border);\n    text-align: start;\n  }\n}\n\n/* ─── Footnotes fallback (no relocation build step) ─────────────────────\n * `.remarque-footnotes` styles a traditional end-of-document footnotes\n * section — e.g. GFM/mdast-util-to-hast's default\n * `<section data-footnotes><h2>Footnotes</h2><ol>...</ol></section>`\n * output — in the same meta voice as `.remarque-sidenote`, for a consumer\n * who hasn't built (or doesn't want) a DOM-reordering transform. It is a\n * same-voice fallback, not a lesser feature: use `.remarque-sidenote`\n * whenever you can relocate notes into DOM reading order (the real a11y\n * upgrade this module exists for); reach for `.remarque-footnotes` only\n * when that isn't available yet.\n *\n *   <section class=\"remarque-footnotes\">\n *     <h2>Notes</h2>\n *     <ol>\n *       <li id=\"note-1\">The note text. <a href=\"#note-1-ref\">&#8617;</a></li>\n *     </ol>\n *   </section>\n */\n.remarque-footnotes {\n  margin-top: var(--space-7);\n  padding-top: var(--space-3);\n  border-top: var(--border-width) var(--border-style) var(--color-border);\n  font-family: var(--font-mono);\n  font-size: var(--text-meta);\n  line-height: var(--leading-meta);\n  color: var(--color-fg-muted);\n}\n\n.remarque-footnotes h2 {\n  font-family: var(--font-mono);\n  font-size: var(--text-meta);\n  letter-spacing: var(--tracking-caps);\n  font-variant-caps: all-small-caps;\n  color: var(--color-muted);\n  margin: 0 0 var(--space-3);\n}\n\n.remarque-footnotes ol {\n  padding-inline-start: var(--space-5);\n  margin: 0;\n}\n\n.remarque-footnotes li + li {\n  margin-top: var(--space-2);\n}\n\n.remarque-footnotes a {\n  color: var(--color-accent);\n  text-decoration: none;\n}\n\n.remarque-footnotes a:hover {\n  color: var(--color-accent-hover);\n  text-decoration: underline;\n}\n\n/* ─── Sticky TOC rail ────────────────────────────────────────────────────\n * `.remarque-toc-rail` wraps a plain collapsible `<details>` — the SAME\n * markup at every viewport, no JS breakpoint listener. Author it\n * `<details open>` by default: below the rail breakpoint this is an\n * inline collapsible block a reader can close to save space (native\n * disclosure behavior, unchanged); at/above it, it reads as an\n * already-expanded sticky rail. The flagship instead runs a small inline\n * script that force-opens the native `open` attribute on every breakpoint\n * crossing — this module holds to \"no JS required,\" so it authors the\n * open state up front instead of syncing it at runtime. The one\n * degradation this trades away: a reader who manually collapses the TOC\n * below the breakpoint and then widens their window keeps it collapsed\n * until they click it again — a fully static, CSS-only cost worth paying\n * for not requiring a script tag.\n *\n * Marker: reuses the same quiet mono disclosure triangle as\n * `.remarque-prose details summary::before` (prose.css) rather than\n * asking consumers to embed an inline SVG chevron, as the flagship's\n * TableOfContents.astro does.\n */\n.remarque-toc-rail {\n  margin-block: var(--space-5);\n}\n\n.remarque-toc-rail details {\n  border: none;\n  padding: 0;\n}\n\n.remarque-toc-rail summary {\n  cursor: pointer;\n  list-style: none;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  gap: var(--space-2);\n  padding-block: var(--space-2);\n  border-bottom: var(--border-width) var(--border-style) var(--color-border);\n  /* Mono meta voice — same declarations as .text-label (tokens-core.css),\n     repeated rather than shared because .text-label isn't a mixin: true\n     small caps, never text-transform: uppercase (REMARQUE.md \"Small\n     Caps\"). The flagship predates that rule and used `text-transform:\n     uppercase`; this is the one place the re-expression corrects the\n     source rather than just relocating it. */\n  font-family: var(--font-mono);\n  font-size: var(--text-meta);\n  font-variant-caps: all-small-caps;\n  letter-spacing: var(--tracking-caps);\n  color: var(--color-muted);\n  transition: color var(--motion-fast) var(--motion-easing);\n}\n\n.remarque-toc-rail summary::-webkit-details-marker {\n  display: none;\n}\n\n.remarque-toc-rail summary::before {\n  content: \"\\25B8\";\n  display: inline-block;\n  color: var(--color-muted);\n  transition: transform var(--motion-fast) var(--motion-easing);\n}\n\n.remarque-toc-rail details[open] summary::before {\n  transform: rotate(90deg);\n}\n\n.remarque-toc-rail summary:hover {\n  color: var(--color-fg);\n}\n\n.remarque-toc-rail ul {\n  list-style: none;\n  padding: 0;\n  margin: var(--space-3) 0 0;\n}\n\n.remarque-toc-rail li + li {\n  margin-top: var(--space-1);\n}\n\n.remarque-toc-rail a {\n  font-family: var(--font-mono);\n  font-size: var(--text-meta);\n  color: var(--color-fg-muted);\n  text-decoration: none;\n  transition: color var(--motion-fast) var(--motion-easing);\n}\n\n.remarque-toc-rail a:hover {\n  color: var(--color-fg);\n}\n\n/* Forced-colors fallback (issue #93 audit finding): the TOC rail's\n   summary toggle and its links both signal hover with a color-only\n   change (`--color-muted`/`--color-fg-muted` -> `--color-fg`). Structure\n   itself survives forced-colors for free (`.remarque-toc-rail summary`'s\n   `border-bottom` and `.remarque-sidenote`'s `border-inline-*` both use\n   real `border-color`, forced to a visible system border regardless of\n   the authored value — see REMARQUE.md \"Forced Colors & Contrast\n   Preferences\"), so this block is scoped narrowly to the two hover\n   affordances that have no non-color signal at all otherwise. */\n@media (forced-colors: active) {\n  .remarque-toc-rail summary:hover,\n  .remarque-toc-rail a:hover {\n    text-decoration: underline;\n  }\n}\n\n/* ─── Essay grid shell — wide-viewport rail layout ───────────────────────\n * `.remarque-essay` is the opt-in grid container an Essay page's <article>\n * adds to place a sticky TOC rail beside the reading column, mirroring\n * this module's sidenote gutter on the OTHER side (right rail = TOC, left\n * gutter = sidenotes — never the same side, so the two mechanisms never\n * fight over space). Below the breakpoint it is a no-op: every child\n * keeps whatever block-flow centering it already had from `.content-\n * reading` / `.remarque-prose` — this rule only exists inside the\n * `@media` block.\n *\n * Track widths: the center track is `minmax(0, var(--content-reading))` —\n * IDENTICAL to the plain `max-width: content-reading; margin-inline:\n * auto` centering used everywhere else (same visual column, just a grid\n * track instead of block flow), so nothing here changes the reading\n * measure the \"prose measure\" computed-style check already covers. The\n * right track (`minmax(--space-11, --space-12)`, 10-12rem) is this\n * module's re-expression of the flagship's bespoke `minmax(12rem, 14rem)`\n * rail — same shape (a comfortable range, not a fixed width), core\n * spacing tokens instead of asserted rem literals.\n *\n * Children are placed by exclusion (`:not(.remarque-toc-rail)` -> column\n * 2) rather than an explicit tag list, deliberately: nothing about this\n * rule touches `max-width` or `margin-inline` on any child, so each\n * element's own `.content-reading` / `.remarque-prose` centering keeps\n * working exactly as it does outside a grid — the grid track is already\n * sized to match it.\n */\n@media (min-width: 80rem) {\n  .remarque-essay {\n    display: grid;\n    grid-template-columns:\n      minmax(0, 1fr)\n      minmax(0, var(--content-reading))\n      minmax(var(--space-11), var(--space-12));\n    column-gap: var(--space-6);\n  }\n\n  .remarque-essay > :not(.remarque-toc-rail) {\n    grid-column: 2;\n  }\n\n  .remarque-essay > .remarque-toc-rail {\n    grid-column: 3;\n    /* `1 / -1` resolves to row 1 ONLY when the grid has no explicit\n       `grid-template-rows` (as here) — `-1` means \"the last explicit\n       row line,\" which with zero explicit rows IS row 1. That inflates\n       row 1 to the rail's own height instead of spanning the real\n       content rows, shoving everything after it down a gap the height\n       of the TOC. (Exactly the bug the flagship shipped and fixed,\n       2026-07-20 — captured here so this module doesn't reintroduce\n       it.) A generous explicit span covers any Essay's block count;\n       empty implicit rows stay 0-height. */\n    grid-row: 1 / span 999;\n    align-self: start;\n    justify-self: start;\n    position: sticky;\n    /* --z-sticky (tokens-core.css, issue #29) — never a bare z-index\n       number in consumer CSS. */\n    z-index: var(--z-sticky);\n    top: var(--space-6);\n    width: 100%;\n    max-height: calc(100vh - var(--space-8));\n    overflow-y: auto;\n  }\n}\n",
      "type": "remarque:css"
    },
    {
      "path": "usage.html",
      "content": "<article class=\"remarque-essay\">\n  <header class=\"content-reading\">\n    <h1 class=\"text-display font-display\">Essay Title</h1>\n  </header>\n\n  <nav class=\"remarque-toc-rail\" aria-label=\"Table of contents\">\n    <details open>\n      <summary>On this page</summary>\n      <ul>\n        <li><a href=\"#section-one\">Section One</a></li>\n        <li><a href=\"#section-two\">Section Two</a></li>\n      </ul>\n    </details>\n  </nav>\n\n  <div class=\"remarque-prose content-reading\">\n    <h2 id=\"section-one\">Section One</h2>\n    <p>\n      A claim worth a citation<a href=\"#note-1\" id=\"note-1-ref\"\n        class=\"remarque-sidenote-ref\" aria-label=\"Note 1\"\n        aria-describedby=\"note-1\"></a>.\n    </p>\n    <aside class=\"remarque-sidenote\" id=\"note-1\" role=\"note\">\n      The note text, in real DOM order right after the paragraph that\n      cites it — not collected into an end-of-document section.\n    </aside>\n  </div>\n\n  <footer class=\"content-reading\">...</footer>\n</article>\n",
      "type": "remarque:markup"
    }
  ]
}
