/* ---------- tokens (house system, light first, then dark) ---------- */
/* Every pair below is measured against AA by `python3 contrast.py`, which reads the
   computed colours off real pages in both schemes. Three of them are not the obvious
   Apple greys, and each is here for a reason:
   --muted is a step darker than #6e6e73 in light, because the pill tint takes the paper
     down to #ededed (and to #e4e4e5 inside a card), where #6e6e73 only reaches 4.33.
   --faint is a step darker in light and a step lighter in dark, because it also draws the
     boundary of the search box, the sort control and the copy button, and a control edge
     has to clear 3:1.
   --on-accent is what sits on a filled accent button. In dark the accent is a light blue,
     so white on it is only 3.02 and the text has to go dark instead. */
:root {
  --r-card: 18px; --r-doc: 22px; --pad-card: 1.5rem;
  --paper: #ffffff; --surface: #f5f5f7; --surface-2: #ebebef;
  --ink: #1d1d1f; --muted: #606066; --faint: #86868b;
  --accent: #0071e3; --accent-soft: #e8f2fd; --accent-ink: #0060c0; --on-accent: #ffffff;
  --ok: #17683f;
  --line: rgba(0,0,0,.09); --hairline: rgba(0,0,0,.07);
  --glass: rgba(255,255,255,.72);
  --shadow: 0 1px 3px rgba(0,0,0,.04), 0 8px 24px rgba(0,0,0,.05);
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}
/* The reader's own setting is the only switch. The site has no theme control, so there is
   no second copy of these tokens behind a data-theme attribute nothing ever sets. */
@media (prefers-color-scheme: dark) {
  :root {
    --paper: #000000; --surface: #1c1c1e; --surface-2: #2c2c2e;
    --ink: #f5f5f7; --muted: #98989d; --faint: #7a7a80;
    --accent: #2997ff; --accent-soft: #12283d; --accent-ink: #5ab0ff; --on-accent: #001a33;
    --ok: #4ade80;
    --line: rgba(255,255,255,.12); --hairline: rgba(255,255,255,.08);
    --glass: rgba(0,0,0,.6);
    --shadow: none;
  }
}

/* ---------- base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; color-scheme: light dark; }
body {
  background: var(--paper); color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, "Segoe UI", Roboto, sans-serif;
  font-size: 17px; line-height: 1.62; letter-spacing: 0; font-optical-sizing: auto;
  margin: 0; -webkit-font-smoothing: antialiased; min-height: 100vh; display: flex; flex-direction: column;
  /* Guide bodies quote bare URLs and package names. On a 320 px screen those are wider than
     the column, so without this they push the whole page sideways. */
  overflow-wrap: break-word;
}
main { flex: 1 0 auto; width: 100%; }
/* The skip link needs a focusable target, or focus stays on the body and only the next Tab
   lands inside the content. main carries tabindex="-1"; the ring sits inside the edge so a
   full width element does not draw one half off screen. */
main:focus-visible { outline-offset: -3px; border-radius: 0; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 6px; }
button { font: inherit; color: inherit; }
img { max-width: 100%; }
h1, h2, h3 { text-wrap: balance; margin: 0; }
h1 { font-size: clamp(2.5rem, 6vw, 3.9rem); font-weight: 700; letter-spacing: -.038em; line-height: 1.04; }
h2 { font-size: clamp(1.55rem, 3vw, 2rem); font-weight: 650; letter-spacing: -.026em; line-height: 1.14; }
h3 { font-size: 1.12rem; font-weight: 600; letter-spacing: -.008em; line-height: 1.35; }
.wrap { max-width: 52rem; margin: 0 auto; padding: 0 clamp(1.1rem, 4vw, 2rem); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* Fixed, not absolute: a filtered arrival lands on the results, so an absolute skip link
   would sit off the top of the screen and the first Tab would scroll the reader back up. */
.skip { position: fixed; left: -999px; top: 8px; z-index: 200; background: var(--accent); color: var(--on-accent); padding: .5rem .9rem; border-radius: 100px; }
.skip:focus { left: 12px; }
.muted { color: var(--muted); }
.eyebrow { font-size: .78rem; font-weight: 600; letter-spacing: .02em; color: var(--muted); text-transform: none; }

/* ---------- pills (a dot and a word, not a coloured block) ---------- */
.pill { display: inline-flex; align-items: center; gap: .38rem; font-size: .74rem; font-weight: 600;
  padding: .16rem .6rem; border-radius: 100px; background: rgba(127,127,127,.14); color: var(--muted); white-space: nowrap; }
.pill::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.pill.new { background: var(--accent-soft); color: var(--accent-ink); }

/* ---------- glass navigation bar (the only glass on the page) ---------- */
.bar { position: sticky; top: 0; z-index: 50; background: var(--glass);
  -webkit-backdrop-filter: saturate(180%) blur(20px); backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--hairline); transform: translateY(-100%); opacity: 0;
  transition: transform .28s cubic-bezier(.2,.7,.2,1), opacity .2s; }
.bar.show { transform: none; opacity: 1; }
.bar .in { max-width: 52rem; margin: 0 auto; padding: 0 clamp(1.1rem, 4vw, 2rem); height: 52px; display: flex; align-items: center; gap: 1rem; }
.bar .brand { display: inline-flex; align-items: center; gap: .6rem; font-weight: 600; color: var(--ink); font-size: .95rem; }
.bar .brand:hover { text-decoration: none; }
.bar .brand img { width: 28px; height: 28px; border-radius: 50%; background: var(--surface-2); }
.bar nav { margin-left: auto; display: flex; gap: .15rem; }
.bar nav a { color: var(--muted); font-size: .9rem; font-weight: 500; padding: .35rem .65rem; border-radius: 100px; }
.bar nav a:hover { color: var(--ink); background: rgba(127,127,127,.12); text-decoration: none; }
.bar nav a.cta { color: var(--accent-ink); }
.bar nav a[aria-current] { color: var(--ink); }
@media (max-width: 600px) { .bar nav a.hide-sm { display: none; } }

/* ---------- masthead ---------- */
.masthead { padding: clamp(4.5rem, 10vw, 7.5rem) 0 3.5rem; }
.masthead .who { display: flex; align-items: center; gap: .9rem; margin-bottom: 2rem; flex-wrap: wrap; }
.masthead .who img { width: 60px; height: 60px; border-radius: 50%; background: var(--surface); }
.masthead .who strong { display: block; font-size: 1rem; font-weight: 600; }
.masthead .who span { display: block; color: var(--muted); font-size: .9rem; }
.masthead .socials { display: flex; gap: .3rem; margin-left: auto; }
@media (max-width: 480px) { .masthead .socials { margin-left: 0; flex-basis: 100%; margin-left: -.6rem; } }
.masthead .socials a { color: var(--muted); font-size: .85rem; font-weight: 500; padding: .3rem .6rem; border-radius: 100px; }
.masthead .socials a:hover { color: var(--ink); background: rgba(127,127,127,.12); text-decoration: none; }
.masthead h1 { margin-bottom: 1.1rem; }
.standfirst { font-size: 1.28rem; color: var(--muted); letter-spacing: -.012em; line-height: 1.5; max-width: 36rem; margin: 0; }
.masthead .meta { margin-top: 1.4rem; color: var(--muted); font-size: .9rem; display: flex; gap: 1.1rem; flex-wrap: wrap; align-items: center; }
.masthead .meta b { color: var(--ink); font-weight: 600; }

section.block { margin-bottom: 5.5rem; }
/* A filtered arrival lands here (src/preflight.js), so leave room for the sticky bar,
   the same gap the prose headings use. */
#browse { scroll-margin-top: 76px; }
.sechead { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 1.4rem; }
.sechead p { margin: .35rem 0 0; color: var(--muted); }

/* ---------- start-here cards: a tint, no border, no shadow ---------- */
/* minmax(0, 1fr): without it a track cannot shrink below its content, so the meta line
   below pushed the three cards past the viewport between 760 and about 880 px. */
.trio { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem; }
@media (max-width: 760px) { .trio { grid-template-columns: 1fr; } }
.card { container-type: inline-size;
  display: flex; flex-direction: column; background: var(--surface); border-radius: var(--r-card);
  padding: var(--pad-card); color: var(--ink); min-height: 100%; transition: background .18s, transform .18s; }
.card:hover { text-decoration: none; background: var(--surface-2); transform: translateY(-2px); }
.card .eyebrow { margin-bottom: .75rem; display: flex; gap: .4rem; align-items: center; flex-wrap: nowrap; white-space: nowrap; min-height: 1.4em; }
/* Always one line, so the three titles sit level. A card too narrow for the whole meta line
   drops the read time (or the domain, which the link at the foot of the card repeats) rather
   than clipping it, and clips only as a last resort. Browsers without container queries get
   the clip. */
.card .eyebrow .muted { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.card .eyebrow .pill { flex: 0 0 auto; }
@container (max-width: 17rem) { .card .eyebrow .tail { display: none; } }
.card h3 { margin-bottom: .45rem; }
.card p { color: var(--muted); font-size: .95rem; margin: 0 0 1.1rem; line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.card .go { margin-top: auto; color: var(--accent-ink); font-weight: 600; font-size: .92rem; }

/* ---------- controls ---------- */
.controls { display: grid; gap: 1.1rem; margin-bottom: .6rem; }
.search { position: relative; }
.search input { width: 100%; height: 48px; padding: 0 3rem 0 2.75rem; font: inherit; font-size: 1rem; color: var(--ink);
  background: var(--surface); border: 1px solid var(--faint); border-radius: 14px; outline: none; -webkit-appearance: none; appearance: none; }
/* A tint alone is not a boundary: --surface on --paper is 1.09:1, so with a transparent
   border there was nothing at 3:1 saying "type here". */
.search input::placeholder { color: var(--muted); }
.search input:focus { border-color: var(--accent); background: var(--paper); box-shadow: 0 0 0 4px var(--accent-soft); }
.search svg { position: absolute; left: .95rem; top: 50%; transform: translateY(-50%); width: 18px; height: 18px; color: var(--muted); pointer-events: none; }
.search kbd { position: absolute; right: .8rem; top: 50%; transform: translateY(-50%); font: 600 .72rem/1 var(--mono); color: var(--muted);
  background: var(--paper); border: 1px solid var(--line); border-radius: 6px; padding: .3rem .42rem; pointer-events: none; }
.search input:focus ~ kbd { display: none; }
@media (hover: none), (pointer: coarse) { .search kbd { display: none; } }
.group { display: flex; align-items: flex-start; gap: .9rem; }
.group > .lbl { flex: 0 0 4.2rem; font-size: .78rem; font-weight: 600; color: var(--muted); padding-top: .45rem; }
.chips { display: flex; flex-wrap: wrap; gap: .4rem; }
.chip { border: 0; background: rgba(127,127,127,.12); color: var(--ink); font-size: .88rem; font-weight: 500;
  padding: .36rem .85rem; border-radius: 100px; cursor: pointer; transition: background .15s, color .15s; }
.chip:hover { background: rgba(127,127,127,.2); }
.chip[aria-pressed="true"] { background: var(--ink); color: var(--paper); }
/* .7, not .55: at .55 the count faded to #7c7c7d on the chip tint, which is 3.66:1. */
.chip .n { color: inherit; opacity: .7; margin-left: .3rem; font-variant-numeric: tabular-nums; }
.resultbar { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  padding: 1rem 0 .5rem; color: var(--muted); font-size: .92rem; border-bottom: 1px solid var(--hairline); }
.resultbar b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.resultbar .right { display: flex; align-items: center; gap: .8rem; }
.resultbar select { font: inherit; font-size: .9rem; color: var(--ink); background: var(--surface); border: 1px solid var(--faint); border-radius: 100px;
  padding: .3rem 2rem .3rem .85rem; -webkit-appearance: none; appearance: none; cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236e6e73' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><path d='m6 9 6 6 6-6'/></svg>");
  background-repeat: no-repeat; background-position: right .8rem center; }
.linkbtn { background: none; border: 0; color: var(--accent-ink); font-weight: 600; font-size: .9rem; cursor: pointer; padding: .3rem .4rem; border-radius: 6px; }
.linkbtn:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Pre-paint filtering, switched on by src/preflight.js and off again by app.js. The rules
   that hide rows for a given tool, format or level are generated by build_library.py and
   appended to this file, because the values come from the data. These two are fixed:
   a text search cannot be matched in CSS, and the count is wrong until app.js renders. */
/* The rows ship in recommended order, so a `?sort=` arrival needs them re-ordered before
   the bundle lands, and only flex or grid children can be re-ordered. The `order` each row
   takes in each sort is generated with the rules above. The list is a column of blocks
   either way: the rows carry no vertical margin, so nothing about it moves. */
html[data-pre-sort] .list { display: flex; flex-direction: column; }
html[data-pre-q] .list { display: none; }
html[data-pre] .resultbar .count { visibility: hidden; }
.pending { display: none; }
html[data-pre-q] .pending { display: block; padding: 3.5rem 1rem; text-align: center; color: var(--muted); }
/* A chip count is how many rows that chip would leave, so it is only right for the group
   being filtered: the build wrote them all with nothing filtered. The ones a filter makes
   wrong are held back until app.js renders, the same as the count line above. */
html[data-pre-tool] .chip[data-f="type"] .n,
html[data-pre-tool] .chip[data-f="level"] .n,
html[data-pre-type] .chip[data-f="tool"] .n,
html[data-pre-type] .chip[data-f="level"] .n,
html[data-pre-level] .chip[data-f="tool"] .n,
html[data-pre-level] .chip[data-f="type"] .n,
html[data-pre-q] .chip .n { visibility: hidden; }

/* ---------- list rows: hairline separators ---------- */
.list { list-style: none; margin: 0; padding: 0; }
.row { display: flex; align-items: center; gap: 1.2rem; padding: 1.15rem .6rem; margin: 0 -.6rem; border-bottom: 1px solid var(--hairline);
  color: var(--ink); border-radius: 12px; transition: background .15s; }
.row:hover { background: var(--surface); text-decoration: none; }
.row .txt { flex: 1; min-width: 0; }
.row .eyebrow { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; margin-bottom: .3rem; }
.row h3 { font-size: 1.15rem; margin-bottom: .2rem; }
.row p { margin: 0; color: var(--muted); font-size: .95rem; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.row .arrow { flex: 0 0 auto; color: var(--faint); transition: transform .18s, color .18s; }
.row:hover .arrow { transform: translateX(3px); color: var(--ink); }
.row .arrow svg { width: 20px; height: 20px; display: block; }
.empty { padding: 3.5rem 1rem; text-align: center; color: var(--muted); }
.empty b { display: block; color: var(--ink); font-weight: 600; margin-bottom: .3rem; }

/* ---------- newsletter + footer ---------- */
.signup { background: var(--surface); border-radius: var(--r-card); padding: 2rem var(--pad-card);
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap; }
.signup h3 { font-size: 1.25rem; margin-bottom: .3rem; }
.signup p { margin: 0; color: var(--muted); max-width: 30rem; }
.btn { display: inline-flex; align-items: center; gap: .45rem; background: var(--accent); color: var(--on-accent); font-weight: 600; font-size: .95rem;
  padding: .7rem 1.25rem; border-radius: 100px; border: 0; cursor: pointer; white-space: nowrap; }
.btn:hover { text-decoration: none; filter: brightness(1.08); }
.btn.quiet { background: rgba(127,127,127,.14); color: var(--ink); }
.btn.quiet:hover { background: rgba(127,127,127,.22); }
.btn svg { width: 16px; height: 16px; }
footer { padding: 3rem 0 4rem; color: var(--muted); font-size: .88rem; border-top: 1px solid var(--hairline); margin-top: 3rem; }
footer .in { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
footer a { color: var(--muted); }
footer nav { display: flex; gap: 1.1rem; flex-wrap: wrap; }

/* ---------- guide page ---------- */
.guide { padding-top: 2.5rem; }
.back { display: inline-flex; align-items: center; gap: .35rem; color: var(--muted); font-size: .92rem; font-weight: 500; margin-bottom: 2.4rem; }
.back:hover { color: var(--ink); text-decoration: none; }
.back svg { width: 16px; height: 16px; }
.guide header { margin-bottom: 2.4rem; }
.guide header .eyebrow { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; margin-bottom: 1.1rem; }
.guide header h1 { font-size: clamp(2rem, 4.6vw, 3rem); margin-bottom: .9rem; }
.guide header .standfirst { font-size: 1.2rem; }
.actions { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: 1.5rem; }
.actions .btn { font-size: .88rem; padding: .55rem 1rem; }
.layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 3rem; }
@media (min-width: 1120px) {
  /* "On this page" comes first in the source so tab order and reading order match what the
     eye sees: a short list of links, then the article. The grid puts it back on the right. */
  .layout.toc { grid-template-columns: minmax(0, 46rem) 14rem; }
  .layout.toc .prose { grid-area: 1 / 1; }
  .layout.toc .onpage { grid-area: 1 / 2; }
  .wrap.wide, .bar.wide .in { max-width: 64rem; }
}
.onpage { display: none; }
@media (min-width: 1120px) {
  .onpage { display: block; position: sticky; top: 76px; align-self: start; font-size: .86rem; }
  .onpage .lbl { font-weight: 600; color: var(--muted); margin-bottom: .6rem; font-size: .76rem; }
  .onpage a { display: block; color: var(--muted); padding: .28rem 0 .28rem .75rem; border-left: 2px solid var(--hairline); line-height: 1.35; }
  .onpage a:hover { color: var(--ink); text-decoration: none; }
  .onpage a.on { color: var(--ink); border-left-color: var(--ink); }
}
.prose { max-width: 46rem; }
.prose > * { margin: 0 0 1.05rem; }
.prose h2 { font-size: clamp(1.35rem, 2.4vw, 1.7rem); margin: 2.6rem 0 .8rem; scroll-margin-top: 76px; }
.prose h3 { margin: 1.8rem 0 .5rem; scroll-margin-top: 76px; }
.prose ul, .prose ol { padding-left: 1.4rem; }
.prose li { margin-bottom: .45rem; }
.prose li > ul, .prose li > ol { margin-top: .45rem; }
.prose code { font-family: var(--mono); font-size: .88em; background: rgba(127,127,127,.14); padding: .1em .38em; border-radius: 6px; }
.prose hr { border: 0; border-top: 1px solid var(--hairline); margin: 2.4rem 0; }
.prose blockquote { margin: 1.4rem 0; padding: .2rem 0 .2rem 1.1rem; border-left: 2px solid var(--line); color: var(--muted); }
.prose details { background: var(--surface); border-radius: 14px; padding: .8rem 1.1rem; }
.prose summary { cursor: pointer; font-weight: 600; }
.prose .todo { display: flex; gap: .6rem; align-items: baseline; }
.prose .tablewrap { overflow-x: auto; }
.prose table { border-collapse: collapse; width: 100%; font-size: .95rem; }
.prose th, .prose td { text-align: left; padding: .7rem 1rem .7rem 0; border-bottom: 1px solid var(--hairline); vertical-align: top; }
.prose th { font-size: .8rem; font-weight: 600; color: var(--muted); }
.prose img { border-radius: 14px; height: auto; }
/* Callouts stay neutral: a tint, an icon, no colour wash */
.note { display: flex; gap: .9rem; background: var(--surface); border-radius: 14px; padding: 1rem 1.15rem; margin: 1.4rem 0; }
.note .ic { flex: 0 0 auto; font-size: 1.05rem; line-height: 1.6; }
/* A flex item will not shrink below its longest word unless it is told it may. */
.note > div { min-width: 0; }
.note div > p:last-child { margin-bottom: 0; }
.note p { margin: 0 0 .6rem; }
/* Prompt blocks: the one component that must look different, so it can be found and copied */
.prompt { margin: 1.4rem 0; background: var(--surface); border-radius: 14px; overflow: hidden; }
.prompt figcaption { display: flex; align-items: center; justify-content: space-between; padding: .55rem .6rem .55rem 1rem;
  font-size: .76rem; font-weight: 600; color: var(--muted); border-bottom: 1px solid var(--hairline); }
.prompt pre { margin: 0; padding: 1rem 1.1rem; max-height: 26rem; overflow: auto; font-family: var(--mono); font-size: .86rem; line-height: 1.55;
  white-space: pre-wrap; word-break: break-word; color: var(--ink); }
.prompt pre code { background: none; padding: 0; font-size: inherit; border-radius: 0; }
.prompt .cap { padding: .5rem 1rem .8rem; font-size: .85rem; color: var(--muted); }
.copy { background: var(--paper); border: 1px solid var(--faint); color: var(--ink); font-size: .76rem; font-weight: 600;
  padding: .3rem .7rem; border-radius: 100px; cursor: pointer; transition: background .15s; }
.copy:hover { background: var(--surface-2); }
.copy.done { color: var(--ok); border-color: transparent; background: rgba(29,122,79,.12); }
.related { margin-top: 4.5rem; padding-top: 2.5rem; border-top: 1px solid var(--hairline); }
.related h2 { margin-bottom: 1.2rem; }
.guide .signup { margin-top: 3rem; }
.toast { position: fixed; left: 50%; bottom: 1.5rem; transform: translate(-50%, 1rem); opacity: 0; background: var(--ink); color: var(--paper);
  padding: .6rem 1rem; border-radius: 100px; font-size: .9rem; font-weight: 500; z-index: 100; transition: opacity .2s, transform .2s; pointer-events: none; }
.toast.show { opacity: 1; transform: translate(-50%, 0); }

@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition: none !important; animation: none !important; } html { scroll-behavior: auto; } }
@media (prefers-contrast: more) { .card, .signup, .prompt, .note, .search input { outline: 1px solid var(--muted); } .chip { outline: 1px solid var(--muted); } .bar { background: var(--paper); -webkit-backdrop-filter: none; backdrop-filter: none; border-bottom: 1px solid var(--muted); } :focus-visible { outline: 2px solid var(--accent) !important; } }
@media print {
  .bar, .back, .actions, .onpage, .related, .signup, footer, .toast { display: none !important; }
  body { font-size: 12pt; color: #000; background: #fff; }
  .prompt, .note { break-inside: avoid; border: 1px solid #ccc; background: #fff; }
  .prompt pre { max-height: none; }
  a { color: #000; }
}

/* ---------- not found ---------- */
.notfound { padding: clamp(5rem, 12vw, 9rem) 0 4rem; max-width: 36rem; }
.notfound h1 { font-size: clamp(2rem, 5vw, 3rem); margin-bottom: .8rem; }
.notfound p { color: var(--muted); font-size: 1.15rem; margin-bottom: 1.6rem; }
.notfound .actions { margin-top: 0; }

/* generated by build_library.py: pre-paint filters, see src/preflight.js */
html[data-pre-tool="chatgpt"] .list > li:not([data-tool="chatgpt"]) { display: none; }
html[data-pre-tool="claude"] .list > li:not([data-tool="claude"]) { display: none; }
html[data-pre-type="guide"] .list > li:not([data-type="guide"]) { display: none; }
html[data-pre-type="learn"] .list > li:not([data-type="learn"]) { display: none; }
html[data-pre-type="prompts"] .list > li:not([data-type="prompts"]) { display: none; }
html[data-pre-type="skills"] .list > li:not([data-type="skills"]) { display: none; }
html[data-pre-level="beginner"] .list > li:not([data-level="beginner"]) { display: none; }

/* generated by build_library.py: pre-paint sort order, see src/preflight.js */
html[data-pre-sort="newest"] .list > li { order: var(--n); }
html[data-pre-sort="oldest"] .list > li { order: var(--o); }
html[data-pre-sort="az"] .list > li { order: var(--a); }
html[data-pre-sort="quick"] .list > li { order: var(--k); }
html[data-pre-sort] .list>li:nth-child(1){--n:30;--o:3;--a:30;--k:29}
html[data-pre-sort] .list>li:nth-child(2){--n:25;--o:7;--a:0;--k:24}
html[data-pre-sort] .list>li:nth-child(3){--n:23;--o:10;--a:1;--k:30}
html[data-pre-sort] .list>li:nth-child(4){--n:24;--o:11;--a:2;--k:31}
html[data-pre-sort] .list>li:nth-child(5){--n:21;--o:12;--a:3;--k:32}
html[data-pre-sort] .list>li:nth-child(6){--n:33;--o:1;--a:4;--k:1}
html[data-pre-sort] .list>li:nth-child(7){--n:22;--o:13;--a:5;--k:2}
html[data-pre-sort] .list>li:nth-child(8){--n:1;--o:15;--a:16;--k:25}
html[data-pre-sort] .list>li:nth-child(9){--n:20;--o:14;--a:6;--k:0}
html[data-pre-sort] .list>li:nth-child(10){--n:26;--o:8;--a:7;--k:3}
html[data-pre-sort] .list>li:nth-child(11){--n:29;--o:5;--a:9;--k:4}
html[data-pre-sort] .list>li:nth-child(12){--n:27;--o:9;--a:10;--k:5}
html[data-pre-sort] .list>li:nth-child(13){--n:2;--o:16;--a:23;--k:6}
html[data-pre-sort] .list>li:nth-child(14){--n:3;--o:17;--a:26;--k:7}
html[data-pre-sort] .list>li:nth-child(15){--n:4;--o:18;--a:8;--k:8}
html[data-pre-sort] .list>li:nth-child(16){--n:5;--o:19;--a:18;--k:9}
html[data-pre-sort] .list>li:nth-child(17){--n:6;--o:20;--a:20;--k:10}
html[data-pre-sort] .list>li:nth-child(18){--n:7;--o:21;--a:29;--k:11}
html[data-pre-sort] .list>li:nth-child(19){--n:8;--o:22;--a:32;--k:12}
html[data-pre-sort] .list>li:nth-child(20){--n:9;--o:23;--a:33;--k:13}
html[data-pre-sort] .list>li:nth-child(21){--n:10;--o:24;--a:27;--k:14}
html[data-pre-sort] .list>li:nth-child(22){--n:11;--o:25;--a:25;--k:15}
html[data-pre-sort] .list>li:nth-child(23){--n:12;--o:26;--a:13;--k:26}
html[data-pre-sort] .list>li:nth-child(24){--n:13;--o:27;--a:19;--k:16}
html[data-pre-sort] .list>li:nth-child(25){--n:14;--o:28;--a:15;--k:17}
html[data-pre-sort] .list>li:nth-child(26){--n:15;--o:29;--a:22;--k:18}
html[data-pre-sort] .list>li:nth-child(27){--n:34;--o:0;--a:11;--k:34}
html[data-pre-sort] .list>li:nth-child(28){--n:16;--o:30;--a:24;--k:19}
html[data-pre-sort] .list>li:nth-child(29){--n:17;--o:31;--a:21;--k:20}
html[data-pre-sort] .list>li:nth-child(30){--n:18;--o:32;--a:34;--k:21}
html[data-pre-sort] .list>li:nth-child(31){--n:32;--o:2;--a:12;--k:33}
html[data-pre-sort] .list>li:nth-child(32){--n:31;--o:4;--a:14;--k:27}
html[data-pre-sort] .list>li:nth-child(33){--n:19;--o:33;--a:17;--k:28}
html[data-pre-sort] .list>li:nth-child(34){--n:28;--o:6;--a:28;--k:22}
html[data-pre-sort] .list>li:nth-child(35){--n:0;--o:34;--a:31;--k:23}
