@jant/core 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/{app-Ct9c4zYF.js → app-B-wKZB8f.js} +267 -205
  2. package/dist/app-qwMcaTML.js +6 -0
  3. package/dist/client/.vite/manifest.json +3 -3
  4. package/dist/client/_assets/{client-Bp2IPjDe.js → client-B1XjvRqE.js} +1 -1
  5. package/dist/client/_assets/client-BMPMuwvV.css +2 -0
  6. package/dist/client/_assets/{client-auth-C4hQWqH1.js → client-auth-B9T2QFl2.js} +21 -21
  7. package/dist/{export-O2w3AsZX.js → export-CzuQyg5h.js} +28 -15
  8. package/dist/{github-sync-BUzIYouS.js → github-sync-CerNYCAn.js} +2 -2
  9. package/dist/{github-sync-D49RADci.js → github-sync-Dbrb1DS5.js} +5 -2
  10. package/dist/index.js +3 -3
  11. package/dist/node.js +4 -4
  12. package/package.json +1 -1
  13. package/src/__tests__/export-service.test.ts +127 -0
  14. package/src/app.tsx +7 -0
  15. package/src/client/components/__tests__/jant-collection-directory.test.ts +0 -42
  16. package/src/client/components/collection-manager-types.ts +0 -2
  17. package/src/client/components/jant-collection-directory.ts +0 -23
  18. package/src/i18n/locales/public/en.po +0 -12
  19. package/src/i18n/locales/public/zh-Hans.po +0 -12
  20. package/src/i18n/locales/public/zh-Hant.po +0 -12
  21. package/src/lib/github-sync-site-config.ts +4 -2
  22. package/src/middleware/__tests__/cache-control.test.ts +50 -0
  23. package/src/middleware/cache-control.ts +60 -0
  24. package/src/services/export-theme/styles/main.css +4 -3
  25. package/src/services/export.ts +47 -17
  26. package/src/services/github-sync.ts +8 -2
  27. package/src/styles/ui.css +23 -46
  28. package/src/ui/feed/ThreadPreview.tsx +17 -9
  29. package/src/ui/feed/__tests__/thread-preview.test.ts +131 -6
  30. package/src/ui/feed/thread-preview-state.ts +43 -0
  31. package/src/ui/pages/CollectionsPage.tsx +0 -22
  32. package/src/ui/shared/CollectionsManager.tsx +6 -41
  33. package/dist/app-CUZaVgsC.js +0 -6
  34. package/dist/client/_assets/client-YVrRjAid.css +0 -2
@@ -3276,7 +3276,7 @@ var tokens_default = "/**\n * Design Tokens\n *\n * CSS custom properties for al
3276
3276
  var theme_default = "name = \"jant\"\nlicense = \"MIT\"\nlicenselink = \"https://github.com/jant-me/jant/blob/main/LICENSE\"\ndescription = \"Default theme packaged with Jant exports.\"\nhomepage = \"https://jant.so\"\ntags = [\"blog\", \"microblog\", \"minimal\"]\nfeatures = [\"pagination\", \"aliases\"]\nmin_version = \"0.160.1\"\n\n[author]\n name = \"Jant\"\n homepage = \"https://jant.so\"\n";
3277
3277
  //#endregion
3278
3278
  //#region src/services/export-theme/styles/main.css?raw
3279
- var main_default = "/*\n * Jant Hugo Export — main.css\n *\n * Fresh minimal text-first design. All colors, spacing, and type sizing\n * come from tokens.css (loaded first in the <head>). Color theme values\n * are supplied by theme.css; customizations live in custom.css.\n *\n * Load order in <head>: tokens.css → main.css → theme.css → custom.css\n *\n * This file intentionally avoids any hardcoded hex, rgb, or px color\n * values. Everything token-driven so the design evolves with the theme.\n */\n\n/* -------------------------------------------------------------------------\n * Reset + box model\n * ------------------------------------------------------------------------- */\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-size: 15px;\n -webkit-text-size-adjust: 100%;\n text-size-adjust: 100%;\n}\n\nbody {\n margin: 0;\n font-family: var(--font-body);\n font-size: var(--type-body-size, var(--type-content-body));\n line-height: var(--type-body-leading);\n color: var(--site-reading-body);\n background-color: var(--site-page-bg);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nimg,\nvideo,\naudio,\niframe {\n max-width: 100%;\n height: auto;\n}\n\nfigure {\n margin: 0;\n}\n\nhr {\n border: 0;\n border-top: 1px solid var(--site-divider);\n margin: var(--space-xl) 0;\n}\n\n/* -------------------------------------------------------------------------\n * Typography\n * ------------------------------------------------------------------------- */\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-family: var(--font-heading);\n font-weight: var(--type-heading-weight);\n line-height: var(--type-heading-leading);\n letter-spacing: var(--type-heading-tracking);\n color: var(--site-reading-heading);\n margin: 1.6em 0 0.6em;\n}\n\nh1 {\n font-size: var(--type-content-display);\n line-height: var(--type-display-leading);\n font-weight: var(--type-display-weight);\n letter-spacing: var(--type-display-tracking);\n margin: 4rem 0 1.5rem;\n}\n\nh2 {\n font-size: var(--type-content-title);\n font-style: italic;\n margin: 2.1rem 0 1.4rem;\n}\n\nh3 {\n font-size: var(--type-content-subtitle);\n font-style: italic;\n margin: 2rem 0 1.4rem;\n}\n\nh4 {\n font-size: var(--type-content-body);\n font-weight: var(--fw-medium);\n}\n\nh5,\nh6 {\n font-size: var(--type-content-body);\n font-weight: var(--fw-medium);\n color: var(--site-reading-meta);\n}\n\np {\n margin: 1.4rem 0;\n}\n\nsmall {\n font-size: var(--type-sm);\n}\n\ncode,\nkbd,\nsamp {\n font-family: var(--font-mono);\n font-size: var(--type-code);\n}\n\npre {\n font-family: var(--font-mono);\n font-size: var(--type-code-block);\n line-height: 1.5;\n padding: 1rem;\n overflow-x: auto;\n background-color: var(--site-feed-card-bg);\n border: 1px solid var(--site-feed-card-border);\n border-radius: 0.375rem;\n}\n\npre code {\n padding: 0;\n background: transparent;\n border: 0;\n font-size: inherit;\n}\n\n:not(pre) > code {\n padding: 0.1em 0.35em;\n background-color: var(--site-feed-card-bg);\n border: 1px solid var(--site-feed-card-border);\n border-radius: 0.25rem;\n}\n\n/* Blockquotes — tinted background card with quote icon (matches main site). */\nblockquote {\n position: relative;\n margin: 1.4rem 0;\n padding: 1.4rem 1rem 0.75rem;\n border: none;\n background: var(--site-blockquote-bg);\n border-radius: 6px;\n color: var(--site-blockquote-text);\n font-family: var(--font-blockquote);\n font-style: italic;\n font-weight: inherit;\n quotes: none;\n text-wrap: pretty;\n}\n\nblockquote::before {\n content: \"\";\n display: block;\n width: 1.3rem;\n height: 1.3rem;\n margin-bottom: 0.35rem;\n background: var(--site-blockquote-rail);\n opacity: 0.6;\n mask-image: url(\"data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M24.4 10.5C16.9 17.7 11.5 26.8 8.2 37.7C4.9 48.7 4.8 58.9 7.8 68.2C10.3 75.7 15.4 79.5 22.9 79.5C28 79.5 32.2 77.8 35.4 74.2C38.6 70.7 40.2 66.5 40.2 61.4C40.2 56.5 38.8 52.6 36 49.6C33.3 46.6 29.7 45.1 25.2 45.1C23.4 45.1 21.8 45.3 20.2 45.8C22.2 37.3 26.7 29.2 33.6 21.4L24.4 10.5Z'/%3E%3Cpath d='M60.8 10.5C53.3 17.7 47.9 26.8 44.6 37.7C41.3 48.7 41.2 58.9 44.2 68.2C46.7 75.7 51.8 79.5 59.3 79.5C64.4 79.5 68.6 77.8 71.8 74.2C75 70.7 76.6 66.5 76.6 61.4C76.6 56.5 75.2 52.6 72.4 49.6C69.7 46.6 66.1 45.1 61.6 45.1C59.8 45.1 58.2 45.3 56.6 45.8C58.6 37.3 63.1 29.2 70 21.4L60.8 10.5Z'/%3E%3C/svg%3E\");\n mask-size: contain;\n mask-repeat: no-repeat;\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M24.4 10.5C16.9 17.7 11.5 26.8 8.2 37.7C4.9 48.7 4.8 58.9 7.8 68.2C10.3 75.7 15.4 79.5 22.9 79.5C28 79.5 32.2 77.8 35.4 74.2C38.6 70.7 40.2 66.5 40.2 61.4C40.2 56.5 38.8 52.6 36 49.6C33.3 46.6 29.7 45.1 25.2 45.1C23.4 45.1 21.8 45.3 20.2 45.8C22.2 37.3 26.7 29.2 33.6 21.4L24.4 10.5Z'/%3E%3Cpath d='M60.8 10.5C53.3 17.7 47.9 26.8 44.6 37.7C41.3 48.7 41.2 58.9 44.2 68.2C46.7 75.7 51.8 79.5 59.3 79.5C64.4 79.5 68.6 77.8 71.8 74.2C75 70.7 76.6 66.5 76.6 61.4C76.6 56.5 75.2 52.6 72.4 49.6C69.7 46.6 66.1 45.1 61.6 45.1C59.8 45.1 58.2 45.3 56.6 45.8C58.6 37.3 63.1 29.2 70 21.4L60.8 10.5Z'/%3E%3C/svg%3E\");\n -webkit-mask-size: contain;\n -webkit-mask-repeat: no-repeat;\n}\n\nblockquote :where(p:first-of-type)::before,\nblockquote :where(p:last-of-type)::after {\n content: none;\n}\n\nblockquote > :first-child {\n margin-top: 0;\n}\n\nblockquote > :last-child {\n margin-bottom: 0;\n}\n\nblockquote p {\n margin: 0;\n}\n\nblockquote p + p,\nblockquote ul,\nblockquote ol,\nblockquote pre {\n margin-top: 0.6em;\n}\n\nblockquote cite {\n font-style: normal;\n color: var(--site-reading-meta);\n font-size: var(--type-sm);\n}\n\n/* CJK: no italic for blockquotes (no true italic glyph) */\n:lang(zh) blockquote,\n:lang(ja) blockquote,\n:lang(ko) blockquote {\n font-style: normal;\n}\n\nul,\nol {\n padding-left: 1.625em;\n margin: 1.25em 0;\n}\n\nli {\n margin-top: 1.2em;\n margin-bottom: 1.2em;\n}\n\n/* Normalize li > p so list spacing is controlled by li alone,\n regardless of whether the markdown renderer wraps li contents in <p>. */\nli > p:first-child {\n margin-top: 0;\n}\n\nli > p:last-child {\n margin-bottom: 0;\n}\n\na {\n color: var(--site-reading-link);\n text-decoration: underline;\n text-decoration-color: var(--site-reading-link-underline);\n text-underline-offset: 0.15em;\n transition:\n color 0.2s ease,\n text-decoration-color 0.2s ease;\n}\n\na:hover,\na:focus {\n color: var(--site-reading-link-hover);\n text-decoration-color: currentColor;\n}\n\na:focus-visible {\n outline: 2px solid var(--site-accent);\n outline-offset: 2px;\n border-radius: 2px;\n}\n\ntime {\n color: var(--site-reading-meta);\n font-size: var(--type-sm);\n font-variant-numeric: tabular-nums;\n}\n\n/* -------------------------------------------------------------------------\n * Page layout — Tufte horizontal frame\n *\n * Mirrors the main site's `.site-page > header/main/footer` rule. Every\n * top-level section gets the same asymmetric padding so the reading column\n * aligns with the rest of the site.\n *\n * 12.5% left + 4% right = 16.5% padding → content = 83.5% of the box.\n * max-width = body-max-width / 0.835 so the inner content area exactly\n * equals `--layout-body-max-width` on wide viewports. min() caps keep\n * padding from growing beyond 210px / 67px. Mobile widens to 5% / 5%\n * and the 55% content column collapses to 100% (via tokens.css).\n * ------------------------------------------------------------------------- */\n\n.site-page {\n min-height: 100vh;\n min-height: 100dvh;\n background-color: var(--site-page-bg);\n}\n\n.site-page > header,\n.site-page > main,\n.site-page > footer,\n.site-page > .home-branding-credit {\n width: 100%;\n max-width: calc(var(--layout-body-max-width) / 0.835);\n padding-left: min(12.5%, 210px);\n padding-right: min(4%, 67px);\n margin-left: auto;\n margin-right: auto;\n}\n\n@media (max-width: 760px) {\n .site-page > header,\n .site-page > main,\n .site-page > footer,\n .site-page > .home-branding-credit {\n padding-left: max(5%, 28px);\n padding-right: 5%;\n }\n}\n\n.site-main {\n padding-top: var(--space-xl);\n padding-bottom: var(--space-xl);\n}\n\n/* -------------------------------------------------------------------------\n * Header\n * ------------------------------------------------------------------------- */\n\n.site-header {\n padding-top: 24px;\n background-color: var(--site-page-bg);\n}\n\n@media (min-width: 700px) {\n .site-header {\n padding-top: 30px;\n }\n}\n\n.site-header-inner {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 0;\n}\n\n.site-header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: clamp(0.6rem, 2.2vw, 1rem);\n flex-wrap: nowrap;\n min-height: 2.75rem;\n width: 100%;\n}\n\n.site-header-top-bordered {\n padding-bottom: 15px;\n}\n\n@media (min-width: 700px) {\n .site-header-top-bordered {\n padding-bottom: 18px;\n }\n}\n\n.site-logo {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n gap: 10px;\n min-width: 0;\n padding: 0.15rem 0;\n font-family: var(--font-site-title);\n font-size: var(--type-subtitle);\n font-weight: var(--fw-regular);\n letter-spacing: -0.02em;\n line-height: 1.15;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--site-text-primary);\n text-decoration: none;\n}\n\n.site-logo:hover,\n.site-logo:focus {\n color: var(--site-text-primary);\n text-decoration: none;\n}\n\n.site-logo-avatar {\n width: calc(var(--avatar-size) + 2px);\n height: calc(var(--avatar-size) + 2px);\n border-radius: var(--avatar-radius);\n object-fit: cover;\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--site-divider) 82%, transparent);\n flex: none;\n}\n\n.site-logo-text {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.site-header-nav {\n display: flex;\n align-items: center;\n flex-wrap: nowrap;\n justify-content: flex-end;\n gap: clamp(0.6rem, 2.2vw, 1rem);\n margin-left: auto;\n min-width: 0;\n font-family: var(--font-ui);\n}\n\n.site-header-link {\n display: inline-flex;\n flex: none;\n align-items: center;\n position: relative;\n min-height: 2rem;\n padding: 0.15rem 0;\n font-size: var(--type-ui-meta);\n font-weight: var(--fw-medium);\n letter-spacing: 0.01em;\n line-height: 1;\n white-space: nowrap;\n color: color-mix(in srgb, var(--site-text-secondary) 62%, transparent);\n text-decoration: none;\n transition: color 0.15s;\n}\n\n.site-header-link:hover,\n.site-header-link:focus {\n color: color-mix(\n in srgb,\n var(--site-text-primary) 84%,\n var(--site-text-secondary)\n );\n text-decoration: none;\n}\n\n.site-header-link-active {\n color: color-mix(\n in srgb,\n var(--site-text-primary) 84%,\n var(--site-text-secondary)\n );\n}\n\n/* --- \"More\" dropdown ---------------------------------------------------- */\n\n.site-header-more {\n position: relative;\n display: inline-flex;\n align-items: center;\n}\n\n.site-header-more-responsive-only {\n display: none;\n}\n\n.site-header-more-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.45rem;\n min-height: 2rem;\n padding: 0.15rem 0;\n border: none;\n background: transparent;\n cursor: pointer;\n font-family: var(--font-ui);\n font-size: var(--type-ui-meta);\n font-weight: var(--fw-medium);\n letter-spacing: 0.01em;\n line-height: 1;\n color: color-mix(in srgb, var(--site-text-secondary) 62%, transparent);\n transition: color 0.15s;\n}\n\n.site-header-more-btn svg {\n width: 0.82rem;\n height: 0.82rem;\n transition: transform 0.18s ease;\n}\n\n.site-header-more-btn:hover,\n.site-header-more-btn[aria-expanded=\"true\"] {\n color: color-mix(\n in srgb,\n var(--site-text-primary) 84%,\n var(--site-text-secondary)\n );\n}\n\n.site-header-more-btn[aria-expanded=\"true\"] svg {\n transform: rotate(180deg);\n}\n\n.site-header-more-popover {\n display: block;\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 0.6rem;\n min-width: 12.25rem;\n padding: 0.3rem 0;\n background: var(--site-page-bg);\n border: 0.5px solid color-mix(in srgb, var(--site-divider) 80%, transparent);\n border-radius: 0.4rem;\n box-shadow:\n 0 4px 20px -8px rgba(0, 0, 0, 0.12),\n 0 2px 6px -2px rgba(0, 0, 0, 0.06);\n opacity: 0;\n visibility: hidden;\n pointer-events: none;\n transform: translateY(-6px);\n transform-origin: top right;\n transition:\n opacity 0.18s ease,\n transform 0.18s ease,\n visibility 0s linear 0.18s;\n z-index: 50;\n}\n\n.site-header-more-popover[aria-hidden=\"false\"] {\n opacity: 1;\n visibility: visible;\n pointer-events: auto;\n transform: translateY(0);\n transition-delay: 0s;\n}\n\n.site-header-more-link {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.35rem;\n padding: 0.45rem 1rem;\n font-family: var(--font-ui);\n font-size: var(--type-ui-meta);\n color: var(--site-text-secondary);\n text-decoration: none;\n transition:\n color 0.15s,\n background-color 0.15s;\n}\n\n.site-header-more-link:hover,\n.site-header-more-link:focus {\n color: color-mix(\n in srgb,\n var(--site-text-primary) 84%,\n var(--site-text-secondary)\n );\n background: color-mix(in srgb, var(--site-nav-hover-bg) 58%, transparent);\n text-decoration: none;\n}\n\n.site-header-more-link-active {\n color: color-mix(\n in srgb,\n var(--site-text-primary) 84%,\n var(--site-text-secondary)\n );\n}\n\n.site-header-more-link-responsive,\n.site-header-more-divider-responsive {\n display: none;\n}\n\n.site-header-more-divider {\n height: 0;\n margin: 0.35rem 0.75rem;\n border-top: 0.5px solid\n color-mix(in srgb, var(--site-divider) 60%, transparent);\n}\n\n/* --- Tiered responsive collapse ---------------------------------------- *\n *\n * ≤960px — 5th+ inline link collapses into More (tier-lg)\n * ≤780px — also 4th collapses (tier-md)\n * ≤580px — also 3rd collapses (tier-sm)\n * The first two links always stay inline. No hamburger on static export —\n * at very narrow widths 2 inline links + More button is the floor.\n */\n\n@media (max-width: 960px) {\n .site-header-link-collapse-lg {\n display: none;\n }\n\n .site-header-more-responsive-only.site-header-more-tier-lg {\n display: inline-flex;\n }\n\n .site-header-more-link-show-lg {\n display: flex;\n }\n\n .site-header-more-divider-responsive {\n display: block;\n }\n}\n\n@media (max-width: 780px) {\n .site-header-link-collapse-md {\n display: none;\n }\n\n .site-header-more-responsive-only.site-header-more-tier-md {\n display: inline-flex;\n }\n\n .site-header-more-link-show-md {\n display: flex;\n }\n}\n\n@media (max-width: 580px) {\n .site-header-link-collapse-sm {\n display: none;\n }\n\n .site-header-more-responsive-only.site-header-more-tier-sm {\n display: inline-flex;\n }\n\n .site-header-more-link-show-sm {\n display: flex;\n }\n}\n\n/* -------------------------------------------------------------------------\n * Footer\n * ------------------------------------------------------------------------- */\n\n.site-footer {\n margin-top: var(--space-xl);\n padding-bottom: var(--space-xl);\n color: var(--site-text-secondary);\n font-size: var(--type-xs);\n background-color: var(--site-page-bg);\n}\n\n.site-footer-inner {\n border-top: 0.5px solid var(--site-divider);\n padding-top: var(--space-xl);\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.site-footer-content {\n color: var(--site-text-secondary);\n}\n\n.site-footer-content p {\n margin: 0 0 0.5em;\n}\n\n.site-footer-nav-list {\n display: flex;\n flex-wrap: wrap;\n gap: 1rem;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.home-branding-credit {\n margin-top: var(--space-xl);\n padding-bottom: var(--space-xl);\n text-align: center;\n color: var(--site-text-secondary);\n font-size: var(--type-base);\n}\n\n.home-branding-credit a {\n display: inline-flex;\n align-items: center;\n gap: 0.38rem;\n color: inherit;\n text-decoration: none;\n border-bottom: 0.5px solid\n color-mix(in srgb, var(--site-text-secondary) 45%, transparent);\n transition:\n color 160ms ease,\n border-color 160ms ease;\n}\n\n.home-branding-credit a:hover,\n.home-branding-credit a:focus-visible {\n color: var(--site-text-primary);\n border-color: currentColor;\n}\n\n/* -------------------------------------------------------------------------\n * Tufte content-width constraint\n *\n * Mirrors the main site's 55% rule: reading text occupies a narrow\n * column inside the Tufte frame, leaving a wide right margin that\n * would host sidenotes on the main site. Media (images, video, audio)\n * is NOT included — galleries intentionally span the full frame so\n * they can breathe.\n *\n * Mobile (<=760px): `--layout-content-width` collapses to 100% via\n * tokens.css. Tablet: cap at 35rem for readability.\n * ------------------------------------------------------------------------- */\n\n.section-header,\n.section-body,\n.post-card-title,\n.post-card-summary,\n.post-card-link-domain,\n.post-card-quote-content,\n.post-card-quote-attribution,\n.post-card-quote-commentary,\n.post-card-footer,\n.reply-title,\n.reply-body,\n.reply-link-domain,\n.reply-footer,\n.thread-title,\n.thread-body,\n.thread-link-domain,\n.thread-footer,\n.collection-directory,\n.pagination,\n.empty-state,\n.page-summary {\n width: var(--layout-content-width);\n max-width: 100%;\n}\n\n@media (min-width: 761px) and (max-width: 1024px) {\n .section-header,\n .section-body,\n .post-card-title,\n .post-card-summary,\n .post-card-link-domain,\n .post-card-quote-content,\n .post-card-quote-attribution,\n .post-card-quote-commentary,\n .post-card-footer,\n .reply-title,\n .reply-body,\n .reply-link-domain,\n .reply-footer,\n .thread-title,\n .thread-body,\n .thread-link-domain,\n .thread-footer,\n .collection-directory,\n .pagination,\n .empty-state,\n .page-summary {\n width: min(100%, 35rem);\n }\n}\n\n/* -------------------------------------------------------------------------\n * Section headers\n * ------------------------------------------------------------------------- */\n\n.section-header {\n margin-bottom: var(--space-xl);\n padding-bottom: 1rem;\n border-bottom: 1px solid var(--site-border-light);\n}\n\n.section-title {\n margin: 0 0 0.25em;\n}\n\n.section-summary {\n margin: 0;\n color: var(--site-reading-meta);\n font-size: var(--type-secondary);\n}\n\n.section-meta {\n margin: 0.5em 0 0;\n color: var(--site-reading-meta);\n font-size: var(--type-sm);\n}\n\n/* -------------------------------------------------------------------------\n * Post list + post cards\n * ------------------------------------------------------------------------- */\n\n.post-list {\n display: flex;\n flex-direction: column;\n gap: calc(var(--space-xl) * 1.25);\n}\n\n.post-list-pinned {\n margin-bottom: calc(var(--space-xl) * 1.25);\n padding-bottom: calc(var(--space-xl) * 1.25);\n border-bottom: 1px solid var(--site-border-light);\n}\n\n/* Decorative divider between posts in a timeline feed. Mirrors the main\n site's `hr.feed-divider`: a trio of small chevron marks masked from the\n current text color, so it picks up the theme automatically.\n `margin-left` centers the divider within the 55% reading column so it\n visually sits at the middle of the post-card text stack. */\nhr.feed-divider {\n border: none;\n width: 30px;\n height: 9px;\n margin: 0;\n margin-left: calc(var(--layout-content-width) / 2 - 15px);\n color: var(--site-feed-divider-color);\n background-color: currentColor;\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 45 13'%3E%3Cpath fill='black' transform='translate(0,0) rotate(90 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(16,0) rotate(100 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(32,0) rotate(80 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3C/svg%3E\");\n mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 45 13'%3E%3Cpath fill='black' transform='translate(0,0) rotate(90 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(16,0) rotate(100 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(32,0) rotate(80 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3C/svg%3E\");\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-position: center;\n mask-position: center;\n -webkit-mask-size: contain;\n mask-size: contain;\n}\n\n.post-card {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.post-card-title {\n font-family: var(--font-heading);\n font-size: var(--feed-note-title-size);\n font-weight: var(--type-heading-weight);\n line-height: var(--feed-note-title-leading);\n margin: 0;\n color: var(--site-reading-title);\n}\n\n.post-card-title a {\n color: inherit;\n text-decoration: none;\n}\n\n.post-card-title a:hover,\n.post-card-title a:focus {\n text-decoration: underline;\n text-underline-offset: 0.18em;\n}\n\n.post-card-summary {\n margin: 0;\n color: var(--site-reading-body);\n}\n\n/* Link card domain row — shown ABOVE the title (matches main site's\n `.feed-link-domain` pattern). Inline flex with icon + host text. */\n.post-card-link-domain,\n.thread-link-domain,\n.reply-link-domain {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n max-width: 100%;\n margin: 0 0 0.4rem 0;\n font-family: var(--font-ui);\n font-size: var(--type-ui-meta);\n font-weight: var(--fw-regular);\n line-height: 1.3;\n color: var(--site-text-secondary);\n text-decoration: none;\n word-break: break-all;\n transition: color 0.18s ease;\n}\n\n.post-card-link-domain:hover,\n.post-card-link-domain:focus,\n.thread-link-domain:hover,\n.thread-link-domain:focus,\n.reply-link-domain:hover,\n.reply-link-domain:focus {\n color: var(--site-text-primary);\n}\n\n.post-card-link-domain-icon {\n width: 0.72rem;\n height: 0.72rem;\n flex-shrink: 0;\n}\n\n/* Slight extra breathing room below link-card titles to match main site. */\n.post-card-link-title,\n.thread-link-title,\n.reply-link-title {\n text-wrap: pretty;\n}\n\n.post-card-link-title a,\n.thread-link-title a,\n.reply-link-title a {\n text-decoration: none;\n}\n\n.post-card-link-title a:hover,\n.post-card-link-title a:focus,\n.thread-link-title a:hover,\n.thread-link-title a:focus,\n.reply-link-title a:hover,\n.reply-link-title a:focus {\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n\n.post-card-media,\n.reply-media,\n.thread-media {\n display: grid;\n grid-template-columns: 1fr;\n gap: 0.75rem;\n}\n\n.post-card-figure img,\n.reply-figure img,\n.thread-figure img {\n display: block;\n width: 100%;\n border-radius: var(--media-radius);\n border: 1px solid var(--site-media-outline);\n}\n\n.post-card-figure video,\n.reply-figure video,\n.thread-figure video {\n display: block;\n width: 100%;\n height: auto;\n border-radius: var(--media-radius);\n border: 1px solid var(--site-media-outline);\n background: #000;\n}\n\n.post-card-figure audio,\n.reply-figure audio,\n.thread-figure audio {\n display: block;\n width: 100%;\n}\n\n.post-card-figure-video a {\n position: relative;\n display: block;\n}\n\n.post-card-video-badge {\n position: absolute;\n left: 0.5rem;\n bottom: 0.5rem;\n padding: 0.125rem 0.5rem;\n font-size: var(--type-xs);\n color: #fff;\n background: rgba(0, 0, 0, 0.65);\n border-radius: 999px;\n pointer-events: none;\n}\n\n.thread-file a,\n.reply-file a {\n color: var(--site-link);\n text-decoration: underline;\n}\n\n/* -------------------------------------------------------------------------\n * Quote format — decorative mark, serif body, attribution line\n * ------------------------------------------------------------------------- */\n\n.post-card-quote {\n position: static;\n margin: 0;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: var(--site-reading-quote);\n font-family: inherit;\n font-style: normal;\n}\n\n/* Quote-format posts have their own decorative `.post-card-quote-mark`\n SVG inside the blockquote — suppress the global blockquote icon. */\n.post-card-quote::before {\n content: none;\n}\n\n.post-card-quote-mark {\n display: block;\n position: relative;\n width: 1.7rem;\n margin-bottom: -0.1rem;\n margin-left: -0.04rem;\n line-height: 0;\n pointer-events: none;\n color: color-mix(in srgb, var(--site-accent) 14%, var(--site-divider));\n opacity: 0.66;\n}\n\n.post-card-quote-mark svg {\n display: block;\n width: 100%;\n height: auto;\n}\n\n.post-card-quote-content {\n font-family: var(--font-serif);\n color: var(--site-text-primary);\n font-size: var(--type-content-subtitle);\n line-height: var(--type-heading-leading);\n white-space: pre-line;\n text-wrap: pretty;\n margin: 0;\n}\n\n.post-card-quote-attribution {\n display: flex;\n align-items: center;\n gap: 0.45rem;\n flex-wrap: wrap;\n margin-top: 0.95rem;\n color: var(--site-text-secondary);\n font-family: var(--font-ui);\n font-size: var(--type-ui-meta);\n font-style: normal;\n line-height: 1.3;\n}\n\n.post-card-quote-attribution::before {\n content: \"\";\n width: 0.9rem;\n height: 1px;\n background: color-mix(\n in srgb,\n var(--site-text-secondary) 38%,\n var(--site-divider)\n );\n}\n\n.post-card-quote-source {\n color: inherit;\n text-decoration: underline;\n text-decoration-color: color-mix(\n in srgb,\n var(--site-text-secondary) 55%,\n transparent\n );\n text-underline-offset: 3px;\n}\n\n.post-card-quote-source:hover,\n.post-card-quote-source:focus {\n color: var(--site-text-primary);\n text-decoration-color: currentColor;\n}\n\n.post-card-quote-commentary {\n position: relative;\n margin-top: 1.1rem;\n padding-top: 0.95rem;\n color: color-mix(\n in srgb,\n var(--site-text-secondary) 84%,\n var(--site-text-primary)\n );\n text-wrap: pretty;\n}\n\n.post-card-quote-commentary::before {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n height: 1px;\n background: linear-gradient(\n 90deg,\n transparent 0%,\n color-mix(in srgb, var(--site-divider) 48%, transparent) 16%,\n color-mix(in srgb, var(--site-divider) 78%, transparent) 50%,\n color-mix(in srgb, var(--site-divider) 48%, transparent) 84%,\n transparent 100%\n );\n}\n\n.post-card-quote-commentary.prose > :first-child {\n margin-top: 0;\n}\n\n.post-card-quote-commentary.prose > :last-child {\n margin-bottom: 0;\n}\n\n.post-card-quote-commentary p {\n margin: 0;\n}\n\n.post-card-quote-commentary p + p,\n.post-card-quote-commentary ul,\n.post-card-quote-commentary ol,\n.post-card-quote-commentary blockquote,\n.post-card-quote-commentary pre {\n margin-top: 0.55rem;\n}\n\n/* Fade the post meta on quote cards so the quote body stays visually primary. */\n.post-card-quote ~ .post-card-footer,\n.post-card-quote-commentary + .post-card-footer {\n opacity: 0.72;\n}\n\n/* -------------------------------------------------------------------------\n * Post footer — meta (featured, time, external link, collections, pinned)\n * ------------------------------------------------------------------------- */\n\n.post-card-footer,\n.reply-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 10px;\n min-height: 2rem;\n font-size: var(--type-ui-hint);\n}\n\n.post-footer-detail {\n margin-top: 24px;\n /* Match the feed/reply footer size so the root post's footer doesn't\n visually dominate. Main site uses a larger size because every post\n on the detail page is rendered at detail size; here only the root\n post gets this class, which otherwise creates a mismatch with the\n replies below. */\n font-size: var(--type-ui-hint);\n color: var(--site-text-secondary);\n}\n\n.post-footer-meta {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n min-width: 0;\n font-family: var(--font-ui);\n line-height: 1.35;\n color: var(--site-text-secondary);\n}\n\n.post-footer-meta time {\n font-size: inherit;\n color: inherit;\n}\n\n.post-footer-featured {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: color-mix(\n in srgb,\n var(--search-mark-color) 72%,\n var(--site-text-secondary)\n );\n flex-shrink: 0;\n}\n\n.post-footer-featured svg {\n width: 1rem;\n height: 1rem;\n opacity: 0.9;\n}\n\n.post-footer-link {\n color: var(--site-text-secondary);\n text-decoration: none;\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.post-footer-link:hover,\n.post-footer-link:focus {\n color: var(--site-text-primary);\n text-decoration: underline;\n}\n\n.post-footer-external-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.6rem;\n height: 1.6rem;\n border-radius: 0.6rem;\n color: var(--site-text-secondary);\n text-decoration: none;\n transition:\n color 0.18s ease,\n background-color 0.16s ease;\n flex-shrink: 0;\n}\n\n.post-footer-external-link:hover,\n.post-footer-external-link:focus {\n color: var(--site-text-primary);\n}\n\n.post-footer-external-link svg {\n width: 1rem;\n height: 1rem;\n}\n\n.post-collection-tags {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n flex-wrap: wrap;\n min-width: 0;\n max-width: 100%;\n color: var(--site-text-secondary);\n}\n\n.post-collection-tag {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n color: inherit;\n text-decoration: none;\n min-width: 0;\n max-width: min(100%, 22ch);\n}\n\n.post-collection-tag:hover,\n.post-collection-tag:focus {\n color: var(--site-text-primary);\n text-decoration: underline;\n text-underline-offset: 0.18em;\n}\n\n.post-collection-tag-text {\n display: block;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.post-card-pin {\n font-size: var(--type-2xs);\n text-transform: uppercase;\n letter-spacing: var(--type-label-tracking);\n color: var(--site-accent);\n border: 1px solid var(--site-accent);\n padding: 0.1em 0.5em;\n border-radius: 999px;\n flex-shrink: 0;\n}\n\n/* -------------------------------------------------------------------------\n * Thread preview (list pages: root context + hero latest reply)\n *\n * Mirrors the main site's `.thread-group.thread-group-preview` layout:\n * content sits flush-left inside the preview, and the vertical rail +\n * dot markers are positioned OUTSIDE the content (overflowing into the\n * container's left gutter) via a negative `left` on the rail/dots.\n * ------------------------------------------------------------------------- */\n\n.thread-preview {\n /* Rail position: negative = overflow outside content. Mirrors\n `--site-thread-rail-line-left` on the main site. */\n --thread-rail-left: -27px;\n --thread-rail-width: 1px;\n --thread-rail-indent: 0px;\n --thread-dot-size: 10px;\n --thread-dot-ring-width: 2px;\n --thread-dot-border-width: 2px;\n --thread-hero-dot-size: 14px;\n --thread-hero-dot-border-width: 3px;\n --thread-item-spacing: 0.35rem;\n\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 0.6rem;\n padding-left: var(--thread-rail-indent);\n}\n\n@media (max-width: 760px) {\n .thread-preview {\n --thread-rail-left: -11px;\n --thread-rail-indent: 8px;\n }\n}\n\n/* Continuous vertical rail — subtle gradient so the line fades into\n whitespace at the top and tail. Sits outside the content via negative\n `left`. */\n.thread-preview::before {\n content: \"\";\n position: absolute;\n left: var(--thread-rail-left);\n top: 0;\n bottom: 0;\n width: var(--thread-rail-width);\n background: linear-gradient(\n 180deg,\n transparent 0,\n color-mix(in srgb, var(--site-threadline) 85%, transparent) 8%,\n color-mix(in srgb, var(--site-threadline) 55%, transparent) 100%\n );\n pointer-events: none;\n}\n\n.thread-preview-context {\n position: relative;\n display: flex;\n flex-direction: column;\n /* Match runtime `.thread-group-preview .thread-item` which uses\n `padding: var(--site-thread-item-spacing) 0` — adjacent items have\n 2x the token between them, so the flex `gap` here is 2x to match. */\n gap: calc(var(--site-thread-item-spacing) * 2);\n margin: 0;\n padding: 0;\n border-left: 0;\n}\n\n/* Individual entry in a thread preview — wraps a full `.post-card` so\n the root, second, and penultimate replies render with their own\n title/body/footer. Dot marker sits on the rail at the card's\n vertical midpoint. */\n.thread-preview .thread-item {\n position: relative;\n min-width: 0;\n max-width: 100%;\n}\n\n.thread-preview .thread-item::before {\n content: \"\";\n position: absolute;\n left: calc(\n var(--thread-rail-left) + var(--thread-rail-width) / 2 -\n var(--thread-dot-size) / 2 - var(--thread-rail-indent)\n );\n top: 1.4rem;\n width: var(--thread-dot-size);\n height: var(--thread-dot-size);\n border-radius: 50%;\n background-color: var(--site-threadline);\n border: var(--thread-dot-border-width) solid var(--site-page-bg);\n box-shadow: 0 0 0 var(--thread-dot-ring-width) var(--site-thread-dot-ring);\n z-index: 1;\n}\n\n/* Gap \"N more posts\" row has no card so we place the dot at its\n vertical midpoint instead of the card's top area. */\n.thread-preview .thread-item-gap {\n display: flex;\n align-items: center;\n padding: 0.15rem 0 0.35rem;\n}\n\n.thread-preview .thread-item-gap::before {\n top: 50%;\n transform: translateY(-50%);\n}\n\n/* Hidden-posts count — rendered as a rounded dashed pill matching\n the main site's `.thread-gap-link`. */\n.thread-preview-gap {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n align-self: flex-start;\n margin: 0.15rem 0;\n padding: 0.3rem 0.72rem;\n border: 1px dashed var(--site-thread-context-border);\n border-radius: 999px;\n background: var(--site-thread-gap-bg);\n color: var(--site-text-secondary);\n font-size: var(--type-thread-context-meta);\n line-height: 1.3;\n text-decoration: none;\n transition:\n border-color 0.18s ease,\n color 0.18s ease,\n background-color 0.18s ease;\n}\n\n.thread-preview-gap:hover,\n.thread-preview-gap:focus {\n border-color: color-mix(in srgb, var(--site-accent) 22%, var(--site-divider));\n color: var(--site-text-primary);\n text-decoration: none;\n}\n\n/* Hero (latest reply) row: spacing above and below matches the main\n site's `.thread-item-hero`, and the dot is larger + accent-colored. */\n.thread-preview-hero {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n padding-top: calc(var(--space-xl) * 1.1);\n margin-top: 0;\n}\n\n.thread-preview .thread-item-hero::before {\n left: calc(\n var(--thread-rail-left) + var(--thread-rail-width) / 2 -\n var(--thread-hero-dot-size) / 2 - var(--thread-rail-indent)\n );\n top: calc(var(--space-xl) * 1.1 + 1.4rem);\n width: var(--thread-hero-dot-size);\n height: var(--thread-hero-dot-size);\n background-color: var(--site-accent);\n border-width: var(--thread-hero-dot-border-width);\n}\n\n.thread-preview-thread-link {\n align-self: flex-start;\n font-size: var(--type-sm);\n color: var(--site-reading-meta);\n text-decoration: none;\n}\n\n.thread-preview-thread-link:hover,\n.thread-preview-thread-link:focus {\n color: var(--site-text-primary);\n text-decoration: underline;\n}\n\n/* -------------------------------------------------------------------------\n * Thread (single post page with inline replies)\n * ------------------------------------------------------------------------- */\n\n.thread {\n /* Rail variables match `.thread-preview` so the detail-page rail\n shares the same visual position. */\n --thread-rail-left: -27px;\n --thread-rail-width: 1px;\n --thread-rail-indent: 0px;\n --thread-dot-size: 10px;\n --thread-dot-ring-width: 2px;\n --thread-dot-border-width: 2px;\n --thread-hero-dot-size: 14px;\n --thread-hero-dot-border-width: 3px;\n\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 1.25rem;\n padding-left: var(--thread-rail-indent);\n}\n\n@media (max-width: 760px) {\n .thread {\n --thread-rail-left: -11px;\n --thread-rail-indent: 8px;\n }\n}\n\n/* Continuous vertical rail spanning the entire thread (root post + replies).\n Only shown when the root post actually has replies — a lone post should\n not have a rail or dot. Matches `.thread-preview`. */\n.thread-has-replies::before {\n content: \"\";\n position: absolute;\n left: var(--thread-rail-left);\n top: 0;\n bottom: 0;\n width: var(--thread-rail-width);\n background: linear-gradient(\n 180deg,\n transparent 0,\n color-mix(in srgb, var(--site-threadline) 85%, transparent) 8%,\n color-mix(in srgb, var(--site-threadline) 55%, transparent) 100%\n );\n pointer-events: none;\n}\n\n/* Dot marker for each post in the thread (root + replies). Shown only when\n there are replies so a solo post doesn't get an orphaned dot. */\n.thread .thread-item {\n position: relative;\n min-width: 0;\n max-width: 100%;\n}\n\n.thread-has-replies .thread-item::before {\n content: \"\";\n position: absolute;\n left: calc(\n var(--thread-rail-left) + var(--thread-rail-width) / 2 -\n var(--thread-dot-size) / 2 - var(--thread-rail-indent)\n );\n top: 1.4rem;\n width: var(--thread-dot-size);\n height: var(--thread-dot-size);\n border-radius: 50%;\n background-color: var(--site-threadline);\n border: var(--thread-dot-border-width) solid var(--site-page-bg);\n box-shadow: 0 0 0 var(--thread-dot-ring-width) var(--site-thread-dot-ring);\n z-index: 1;\n}\n\n.thread-item-root {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.thread-header {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.thread-title {\n font-family: var(--font-heading);\n font-size: var(--type-content-display);\n font-weight: var(--type-display-weight);\n line-height: var(--type-display-leading);\n margin: 0;\n color: var(--site-reading-title);\n}\n\n.thread-title a {\n color: inherit;\n text-decoration: none;\n}\n\n.thread-title a:hover,\n.thread-title a:focus {\n text-decoration: underline;\n text-underline-offset: 0.18em;\n}\n\n.thread-body {\n font-size: var(--type-content-body);\n line-height: var(--type-body-leading);\n color: var(--site-reading-body);\n}\n\n.thread-body > :first-child {\n margin-top: 0;\n}\n\n.thread-body > :last-child {\n margin-bottom: 0;\n}\n\n.thread-replies {\n /* Flex container for replies. The rail and dots are provided by\n `.thread::before` and `.thread-item::before` so replies stay visually\n connected to the root post. */\n display: flex;\n flex-direction: column;\n gap: var(--space-xl);\n}\n\n/* -------------------------------------------------------------------------\n * Replies\n * ------------------------------------------------------------------------- */\n\n.reply {\n scroll-margin-top: 1.5rem;\n display: flex;\n flex-direction: column;\n gap: 0.6rem;\n}\n\n.reply:target {\n background-color: var(--search-mark-bg);\n border-radius: 0.25rem;\n padding: 0.75rem 1rem;\n margin-left: -1rem;\n margin-right: -1rem;\n}\n\n.reply:target::before {\n background-color: var(--site-accent);\n}\n\n.reply-title {\n font-family: var(--font-heading);\n font-size: var(--type-content-subtitle);\n font-weight: var(--type-heading-weight);\n line-height: var(--type-heading-leading);\n margin: 0;\n color: var(--site-reading-heading);\n}\n\n.reply-body {\n color: var(--site-reading-body);\n}\n\n.reply-body > :first-child {\n margin-top: 0;\n}\n\n.reply-body > :last-child {\n margin-bottom: 0;\n}\n\n/* -------------------------------------------------------------------------\n * Collections page\n *\n * Mirrors the main site's authenticated-less collections view\n * (`ui/pages/CollectionsPage.tsx` + `ui/shared/CollectionDirectory.tsx`):\n * a page-intro block with count, then a two-column grid per row where a\n * monospace sequence label sits beside the collection title, description,\n * and entry/activity meta.\n * ------------------------------------------------------------------------- */\n\n.collections-page-shell {\n position: relative;\n display: flex;\n flex-direction: column;\n width: var(--layout-content-width);\n max-width: 100%;\n gap: clamp(1.25rem, 3vw, 1.75rem);\n}\n\n@media (min-width: 761px) and (max-width: 1024px) {\n .collections-page-shell {\n width: min(100%, 35rem);\n }\n}\n\n@media (max-width: 760px) {\n .collections-page-shell {\n width: 100%;\n }\n}\n\n.collections-page-header {\n position: relative;\n display: flex;\n align-items: flex-start;\n padding-bottom: 0.2rem;\n}\n\n.collections-page-heading {\n min-width: 0;\n flex: 1 1 18rem;\n}\n\n.page-intro {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n padding-bottom: 0.1rem;\n}\n\n.page-intro-title-row {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: 0.7rem;\n min-width: 0;\n}\n\n.page-intro-title {\n margin: 0;\n font-family: var(--font-heading);\n font-size: var(--type-title, 2rem);\n font-weight: var(--type-heading-weight);\n line-height: 1.15;\n letter-spacing: -0.01em;\n color: var(--site-text-primary);\n}\n\n.page-intro-meta-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem 1rem;\n}\n\n.page-intro-meta,\n.page-intro-description {\n margin: 0;\n color: var(--site-text-secondary);\n font-family: var(--font-ui);\n font-size: var(--type-secondary);\n line-height: 1.3;\n}\n\n.collection-directory {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 0.15rem;\n list-style: none;\n padding: 0;\n margin: 0;\n}\n\n.collection-directory-item {\n position: relative;\n display: flex;\n align-items: flex-start;\n gap: 0.75rem;\n padding: 0.95rem 0;\n background: transparent;\n text-decoration: none;\n}\n\n.collection-directory-main {\n --collection-directory-sequence-width: 3.5ch;\n --collection-directory-title-line-height: 1.18;\n min-width: 0;\n flex: 1;\n display: grid;\n grid-template-columns: var(--collection-directory-sequence-width) minmax(\n 0,\n 1fr\n );\n align-items: start;\n column-gap: 0.8rem;\n row-gap: 0.25rem;\n}\n\n.collection-directory-sequence {\n grid-column: 1;\n grid-row: 1;\n display: block;\n width: var(--collection-directory-sequence-width);\n padding-top: 0.2rem;\n font-family: var(--font-mono);\n font-size: var(--type-xs);\n font-variant-numeric: tabular-nums;\n line-height: var(--collection-directory-title-line-height);\n letter-spacing: 0.14em;\n color: var(--site-text-secondary);\n}\n\n.collection-directory-title-row {\n grid-column: 2;\n grid-row: 1;\n min-width: 0;\n display: flex;\n align-items: flex-start;\n}\n\n.collection-directory-title-link {\n color: inherit;\n text-decoration: none;\n transition: color 0.15s ease;\n}\n\n.collection-directory-title-link:hover,\n.collection-directory-title-link:focus-visible {\n color: var(--site-text-primary);\n}\n\n.collection-directory-title-link:hover .collection-directory-title,\n.collection-directory-title-link:focus-visible .collection-directory-title {\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n\n.collection-directory-title {\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 0.45rem;\n font-family: var(--font-heading);\n font-size: var(--type-content-body);\n font-weight: var(--type-heading-weight);\n line-height: var(--collection-directory-title-line-height);\n letter-spacing: -0.02em;\n text-wrap: pretty;\n}\n\n.collection-directory-title-marker {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 0.95rem;\n min-width: 0.95rem;\n height: 0.95rem;\n color: var(--site-text-secondary);\n transition: color 0.15s ease;\n}\n\n.collection-directory-title-link:hover .collection-directory-title-marker,\n.collection-directory-title-link:focus-visible\n .collection-directory-title-marker {\n color: var(--site-text-primary);\n}\n\n.collection-directory-description {\n grid-column: 2;\n grid-row: 2;\n margin: 0;\n color: color-mix(in srgb, var(--site-text-secondary) 80%, transparent);\n font-family: var(--font-body);\n font-size: var(--type-sm);\n line-height: 1.45;\n}\n\n.collection-directory-description :where(p) {\n margin-top: 0.25em;\n margin-bottom: 0.25em;\n}\n\n/* Links inside the description inherit the description color so they sit in\n the same gray tone as the copy around them (matches the main site's\n `.prose { --tw-prose-links: var(--site-content-link); }` which resolves\n to `inherit`). The underline stays, drawn in currentColor. */\n.collection-directory-description :where(a) {\n color: inherit;\n text-decoration: underline;\n text-decoration-color: currentColor;\n text-underline-offset: 0.15em;\n}\n\n.collection-directory-description :where(a:hover),\n.collection-directory-description :where(a:focus-visible) {\n color: var(--site-text-primary);\n}\n\n.collection-directory-description :where(p:first-child) {\n margin-top: 0;\n}\n\n.collection-directory-description :where(p:last-child) {\n margin-bottom: 0;\n}\n\n.collection-directory-description + .collection-directory-summary {\n grid-row: 3;\n}\n\n.collection-directory-summary {\n grid-column: 2;\n grid-row: 2;\n display: flex;\n min-width: 0;\n overflow: hidden;\n align-items: center;\n gap: 0.2rem 0.5rem;\n margin: 0;\n color: var(--site-reading-meta);\n font-family: var(--font-ui);\n font-size: var(--type-sm);\n line-height: 1.3;\n white-space: nowrap;\n}\n\n.collection-directory-meta {\n flex: 0 0 auto;\n color: inherit;\n}\n\n.collection-directory-meta-separator {\n flex: 0 0 auto;\n color: color-mix(in srgb, var(--site-divider) 88%, transparent);\n}\n\n.collection-directory-updated {\n flex: 0 0 auto;\n color: inherit;\n white-space: nowrap;\n}\n\n.collection-directory-divider {\n padding: 1.5rem 0 0.85rem;\n}\n\n.collection-directory-divider-row {\n display: flex;\n align-items: center;\n gap: 0.95rem;\n}\n\n.collection-directory-divider-text {\n font-family: var(--font-heading);\n font-size: var(--type-secondary);\n letter-spacing: 0;\n font-style: normal;\n white-space: nowrap;\n color: var(--site-text-secondary);\n}\n\n.collection-directory-divider-line {\n flex: 1;\n height: 1px;\n border: none;\n margin: 0;\n background: linear-gradient(\n 90deg,\n color-mix(in srgb, var(--site-divider) 100%, transparent),\n color-mix(in srgb, var(--site-divider) 54%, transparent) 34%,\n transparent 86%\n );\n}\n\n/* -------------------------------------------------------------------------\n * Pagination\n * ------------------------------------------------------------------------- */\n\n.pagination {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-wrap: wrap;\n gap: 1rem;\n padding: 1.5rem 0;\n font-size: var(--type-sm);\n font-variant-numeric: tabular-nums;\n}\n\n.pagination-link {\n color: var(--site-text-secondary);\n text-decoration: underline;\n text-underline-offset: 3px;\n transition: color 0.15s ease;\n}\n\n.pagination-link:hover,\n.pagination-link:focus {\n color: var(--site-text-primary);\n}\n\n.pagination-link.is-disabled {\n color: color-mix(in srgb, var(--site-text-secondary) 50%, transparent);\n cursor: default;\n text-decoration: none;\n}\n\n.pagination-current {\n color: var(--site-text-primary);\n font-weight: var(--fw-medium);\n}\n\n.pagination-ellipsis {\n color: var(--site-text-secondary);\n}\n\n/* -------------------------------------------------------------------------\n * Empty states + utility\n * ------------------------------------------------------------------------- */\n\n.empty-state {\n color: var(--site-reading-meta);\n font-style: italic;\n text-align: center;\n padding: var(--space-xl) 0;\n}\n\n.page,\n.section {\n display: block;\n}\n\n.page-summary,\n.section-summary {\n font-size: var(--type-secondary);\n color: var(--site-reading-meta);\n}\n\n/* -------------------------------------------------------------------------\n * Wider viewport refinements\n * ------------------------------------------------------------------------- */\n\n@media (min-width: 768px) {\n .site-main {\n padding-top: calc(var(--space-xl) * 1.5);\n padding-bottom: calc(var(--space-xl) * 1.5);\n }\n\n .post-card-media,\n .thread-media,\n .reply-media {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n\n .post-card-media:has(> :only-child),\n .thread-media:has(> :only-child),\n .reply-media:has(> :only-child) {\n grid-template-columns: 1fr;\n }\n}\n";
3279
+ var main_default = "/*\n * Jant Hugo Export — main.css\n *\n * Fresh minimal text-first design. All colors, spacing, and type sizing\n * come from tokens.css (loaded first in the <head>). Color theme values\n * are supplied by theme.css; customizations live in custom.css.\n *\n * Load order in <head>: tokens.css → main.css → theme.css → custom.css\n *\n * This file intentionally avoids any hardcoded hex, rgb, or px color\n * values. Everything token-driven so the design evolves with the theme.\n */\n\n/* -------------------------------------------------------------------------\n * Reset + box model\n * ------------------------------------------------------------------------- */\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-size: 15px;\n -webkit-text-size-adjust: 100%;\n text-size-adjust: 100%;\n}\n\nbody {\n margin: 0;\n font-family: var(--font-body);\n font-size: var(--type-body-size, var(--type-content-body));\n line-height: var(--type-body-leading);\n color: var(--site-reading-body);\n background-color: var(--site-page-bg);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nimg,\nvideo,\naudio,\niframe {\n max-width: 100%;\n height: auto;\n}\n\nfigure {\n margin: 0;\n}\n\nhr {\n border: 0;\n border-top: 1px solid var(--site-divider);\n margin: var(--space-xl) 0;\n}\n\n/* -------------------------------------------------------------------------\n * Typography\n * ------------------------------------------------------------------------- */\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-family: var(--font-heading);\n font-weight: var(--type-heading-weight);\n line-height: var(--type-heading-leading);\n letter-spacing: var(--type-heading-tracking);\n color: var(--site-reading-heading);\n margin: 1.6em 0 0.6em;\n}\n\nh1 {\n font-size: var(--type-content-display);\n line-height: var(--type-display-leading);\n font-weight: var(--type-display-weight);\n letter-spacing: var(--type-display-tracking);\n margin: 4rem 0 1.5rem;\n}\n\nh2 {\n font-size: var(--type-content-title);\n font-style: italic;\n margin: 2.1rem 0 1.4rem;\n}\n\nh3 {\n font-size: var(--type-content-subtitle);\n font-style: italic;\n margin: 2rem 0 1.4rem;\n}\n\nh4 {\n font-size: var(--type-content-body);\n font-weight: var(--fw-medium);\n}\n\nh5,\nh6 {\n font-size: var(--type-content-body);\n font-weight: var(--fw-medium);\n color: var(--site-reading-meta);\n}\n\np {\n margin: 1.4rem 0;\n}\n\nsmall {\n font-size: var(--type-sm);\n}\n\ncode,\nkbd,\nsamp {\n font-family: var(--font-mono);\n font-size: var(--type-code);\n}\n\npre {\n font-family: var(--font-mono);\n font-size: var(--type-code-block);\n line-height: 1.5;\n padding: 1rem;\n overflow-x: auto;\n background-color: var(--site-feed-card-bg);\n border: 1px solid var(--site-feed-card-border);\n border-radius: 0.375rem;\n}\n\npre code {\n padding: 0;\n background: transparent;\n border: 0;\n font-size: inherit;\n}\n\n:not(pre) > code {\n padding: 0.1em 0.35em;\n background-color: var(--site-feed-card-bg);\n border: 1px solid var(--site-feed-card-border);\n border-radius: 0.25rem;\n}\n\n/* Blockquotes — tinted background card with quote icon (matches main site). */\nblockquote {\n position: relative;\n margin: 1.4rem 0;\n padding: 1.4rem 1rem 0.75rem;\n border: none;\n background: var(--site-blockquote-bg);\n border-radius: 6px;\n color: var(--site-blockquote-text);\n font-family: var(--font-blockquote);\n font-style: italic;\n font-weight: inherit;\n quotes: none;\n text-wrap: pretty;\n}\n\nblockquote::before {\n content: \"\";\n display: block;\n width: 1.3rem;\n height: 1.3rem;\n margin-bottom: 0.35rem;\n background: var(--site-blockquote-rail);\n opacity: 0.6;\n mask-image: url(\"data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M24.4 10.5C16.9 17.7 11.5 26.8 8.2 37.7C4.9 48.7 4.8 58.9 7.8 68.2C10.3 75.7 15.4 79.5 22.9 79.5C28 79.5 32.2 77.8 35.4 74.2C38.6 70.7 40.2 66.5 40.2 61.4C40.2 56.5 38.8 52.6 36 49.6C33.3 46.6 29.7 45.1 25.2 45.1C23.4 45.1 21.8 45.3 20.2 45.8C22.2 37.3 26.7 29.2 33.6 21.4L24.4 10.5Z'/%3E%3Cpath d='M60.8 10.5C53.3 17.7 47.9 26.8 44.6 37.7C41.3 48.7 41.2 58.9 44.2 68.2C46.7 75.7 51.8 79.5 59.3 79.5C64.4 79.5 68.6 77.8 71.8 74.2C75 70.7 76.6 66.5 76.6 61.4C76.6 56.5 75.2 52.6 72.4 49.6C69.7 46.6 66.1 45.1 61.6 45.1C59.8 45.1 58.2 45.3 56.6 45.8C58.6 37.3 63.1 29.2 70 21.4L60.8 10.5Z'/%3E%3C/svg%3E\");\n mask-size: contain;\n mask-repeat: no-repeat;\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg viewBox='0 0 96 96' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M24.4 10.5C16.9 17.7 11.5 26.8 8.2 37.7C4.9 48.7 4.8 58.9 7.8 68.2C10.3 75.7 15.4 79.5 22.9 79.5C28 79.5 32.2 77.8 35.4 74.2C38.6 70.7 40.2 66.5 40.2 61.4C40.2 56.5 38.8 52.6 36 49.6C33.3 46.6 29.7 45.1 25.2 45.1C23.4 45.1 21.8 45.3 20.2 45.8C22.2 37.3 26.7 29.2 33.6 21.4L24.4 10.5Z'/%3E%3Cpath d='M60.8 10.5C53.3 17.7 47.9 26.8 44.6 37.7C41.3 48.7 41.2 58.9 44.2 68.2C46.7 75.7 51.8 79.5 59.3 79.5C64.4 79.5 68.6 77.8 71.8 74.2C75 70.7 76.6 66.5 76.6 61.4C76.6 56.5 75.2 52.6 72.4 49.6C69.7 46.6 66.1 45.1 61.6 45.1C59.8 45.1 58.2 45.3 56.6 45.8C58.6 37.3 63.1 29.2 70 21.4L60.8 10.5Z'/%3E%3C/svg%3E\");\n -webkit-mask-size: contain;\n -webkit-mask-repeat: no-repeat;\n}\n\nblockquote :where(p:first-of-type)::before,\nblockquote :where(p:last-of-type)::after {\n content: none;\n}\n\nblockquote > :first-child {\n margin-top: 0;\n}\n\nblockquote > :last-child {\n margin-bottom: 0;\n}\n\nblockquote p {\n margin: 0;\n}\n\nblockquote p + p,\nblockquote ul,\nblockquote ol,\nblockquote pre {\n margin-top: 0.6em;\n}\n\nblockquote cite {\n font-style: normal;\n color: var(--site-reading-meta);\n font-size: var(--type-sm);\n}\n\n/* CJK: no italic for blockquotes (no true italic glyph) */\n:lang(zh) blockquote,\n:lang(ja) blockquote,\n:lang(ko) blockquote {\n font-style: normal;\n}\n\nul,\nol {\n padding-left: 1.625em;\n margin: 1.25em 0;\n}\n\nli {\n margin-top: 1.2em;\n margin-bottom: 1.2em;\n}\n\n/* Normalize li > p so list spacing is controlled by li alone,\n regardless of whether the markdown renderer wraps li contents in <p>. */\nli > p:first-child {\n margin-top: 0;\n}\n\nli > p:last-child {\n margin-bottom: 0;\n}\n\na {\n color: var(--site-reading-link);\n text-decoration: underline;\n text-decoration-color: var(--site-reading-link-underline);\n text-underline-offset: 0.15em;\n transition:\n color 0.2s ease,\n text-decoration-color 0.2s ease;\n}\n\na:hover,\na:focus {\n color: var(--site-reading-link-hover);\n text-decoration-color: currentColor;\n}\n\na:focus-visible {\n outline: 2px solid var(--site-accent);\n outline-offset: 2px;\n border-radius: 2px;\n}\n\ntime {\n color: var(--site-reading-meta);\n font-size: var(--type-sm);\n font-variant-numeric: tabular-nums;\n}\n\n/* -------------------------------------------------------------------------\n * Page layout — Tufte horizontal frame\n *\n * Mirrors the main site's `.site-page > header/main/footer` rule. Every\n * top-level section gets the same asymmetric padding so the reading column\n * aligns with the rest of the site.\n *\n * 12.5% left + 4% right = 16.5% padding → content = 83.5% of the box.\n * max-width = body-max-width / 0.835 so the inner content area exactly\n * equals `--layout-body-max-width` on wide viewports. min() caps keep\n * padding from growing beyond 210px / 67px. Mobile widens to 5% / 5%\n * and the 55% content column collapses to 100% (via tokens.css).\n * ------------------------------------------------------------------------- */\n\n.site-page {\n min-height: 100vh;\n min-height: 100dvh;\n background-color: var(--site-page-bg);\n}\n\n.site-page > header,\n.site-page > main,\n.site-page > footer,\n.site-page > .home-branding-credit {\n width: 100%;\n max-width: calc(var(--layout-body-max-width) / 0.835);\n padding-left: min(12.5%, 210px);\n padding-right: min(4%, 67px);\n margin-left: auto;\n margin-right: auto;\n}\n\n@media (max-width: 760px) {\n .site-page > header,\n .site-page > main,\n .site-page > footer,\n .site-page > .home-branding-credit {\n padding-left: max(5%, 28px);\n padding-right: 5%;\n }\n}\n\n.site-main {\n padding-top: var(--space-xl);\n padding-bottom: var(--space-xl);\n}\n\n/* -------------------------------------------------------------------------\n * Header\n * ------------------------------------------------------------------------- */\n\n.site-header {\n padding-top: 24px;\n background-color: var(--site-page-bg);\n}\n\n@media (min-width: 700px) {\n .site-header {\n padding-top: 30px;\n }\n}\n\n.site-header-inner {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 0;\n}\n\n.site-header-top {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: clamp(0.6rem, 2.2vw, 1rem);\n flex-wrap: nowrap;\n min-height: 2.75rem;\n width: 100%;\n}\n\n.site-header-top-bordered {\n padding-bottom: 15px;\n}\n\n@media (min-width: 700px) {\n .site-header-top-bordered {\n padding-bottom: 18px;\n }\n}\n\n.site-logo {\n display: flex;\n flex: 0 1 auto;\n align-items: center;\n gap: 10px;\n min-width: 0;\n padding: 0.15rem 0;\n font-family: var(--font-site-title);\n font-size: var(--type-subtitle);\n font-weight: var(--fw-regular);\n letter-spacing: -0.02em;\n line-height: 1.15;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--site-text-primary);\n text-decoration: none;\n}\n\n.site-logo:hover,\n.site-logo:focus {\n color: var(--site-text-primary);\n text-decoration: none;\n}\n\n.site-logo-avatar {\n box-sizing: border-box;\n width: calc(var(--avatar-size) + 4px);\n height: calc(var(--avatar-size) + 4px);\n border-radius: var(--avatar-radius);\n object-fit: cover;\n border: 1px solid color-mix(in srgb, var(--site-divider) 82%, transparent);\n flex: none;\n}\n\n.site-logo-text {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.site-header-nav {\n display: flex;\n align-items: center;\n flex-wrap: nowrap;\n justify-content: flex-end;\n gap: clamp(0.6rem, 2.2vw, 1rem);\n margin-left: auto;\n min-width: 0;\n font-family: var(--font-ui);\n}\n\n.site-header-link {\n display: inline-flex;\n flex: none;\n align-items: center;\n position: relative;\n min-height: 2rem;\n padding: 0.15rem 0;\n font-size: var(--type-ui-meta);\n font-weight: var(--fw-medium);\n letter-spacing: 0.01em;\n line-height: 1;\n white-space: nowrap;\n color: color-mix(in srgb, var(--site-text-secondary) 62%, transparent);\n text-decoration: none;\n transition: color 0.15s;\n}\n\n.site-header-link:hover,\n.site-header-link:focus {\n color: color-mix(\n in srgb,\n var(--site-text-primary) 84%,\n var(--site-text-secondary)\n );\n text-decoration: none;\n}\n\n.site-header-link-active {\n color: color-mix(\n in srgb,\n var(--site-text-primary) 84%,\n var(--site-text-secondary)\n );\n}\n\n/* --- \"More\" dropdown ---------------------------------------------------- */\n\n.site-header-more {\n position: relative;\n display: inline-flex;\n align-items: center;\n}\n\n.site-header-more-responsive-only {\n display: none;\n}\n\n.site-header-more-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.45rem;\n min-height: 2rem;\n padding: 0.15rem 0;\n border: none;\n background: transparent;\n cursor: pointer;\n font-family: var(--font-ui);\n font-size: var(--type-ui-meta);\n font-weight: var(--fw-medium);\n letter-spacing: 0.01em;\n line-height: 1;\n color: color-mix(in srgb, var(--site-text-secondary) 62%, transparent);\n transition: color 0.15s;\n}\n\n.site-header-more-btn svg {\n width: 0.82rem;\n height: 0.82rem;\n transition: transform 0.18s ease;\n}\n\n.site-header-more-btn:hover,\n.site-header-more-btn[aria-expanded=\"true\"] {\n color: color-mix(\n in srgb,\n var(--site-text-primary) 84%,\n var(--site-text-secondary)\n );\n}\n\n.site-header-more-btn[aria-expanded=\"true\"] svg {\n transform: rotate(180deg);\n}\n\n.site-header-more-popover {\n display: block;\n position: absolute;\n top: 100%;\n right: 0;\n margin-top: 0.6rem;\n min-width: 12.25rem;\n padding: 0.3rem 0;\n background: var(--site-page-bg);\n border: 0.5px solid color-mix(in srgb, var(--site-divider) 80%, transparent);\n border-radius: 0.4rem;\n box-shadow:\n 0 4px 20px -8px rgba(0, 0, 0, 0.12),\n 0 2px 6px -2px rgba(0, 0, 0, 0.06);\n opacity: 0;\n visibility: hidden;\n pointer-events: none;\n transform: translateY(-6px);\n transform-origin: top right;\n transition:\n opacity 0.18s ease,\n transform 0.18s ease,\n visibility 0s linear 0.18s;\n z-index: 50;\n}\n\n.site-header-more-popover[aria-hidden=\"false\"] {\n opacity: 1;\n visibility: visible;\n pointer-events: auto;\n transform: translateY(0);\n transition-delay: 0s;\n}\n\n.site-header-more-link {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.35rem;\n padding: 0.45rem 1rem;\n font-family: var(--font-ui);\n font-size: var(--type-ui-meta);\n color: var(--site-text-secondary);\n text-decoration: none;\n transition:\n color 0.15s,\n background-color 0.15s;\n}\n\n.site-header-more-link:hover,\n.site-header-more-link:focus {\n color: color-mix(\n in srgb,\n var(--site-text-primary) 84%,\n var(--site-text-secondary)\n );\n background: color-mix(in srgb, var(--site-nav-hover-bg) 58%, transparent);\n text-decoration: none;\n}\n\n.site-header-more-link-active {\n color: color-mix(\n in srgb,\n var(--site-text-primary) 84%,\n var(--site-text-secondary)\n );\n}\n\n.site-header-more-link-responsive,\n.site-header-more-divider-responsive {\n display: none;\n}\n\n.site-header-more-divider {\n height: 0;\n margin: 0.35rem 0.75rem;\n border-top: 0.5px solid\n color-mix(in srgb, var(--site-divider) 60%, transparent);\n}\n\n/* --- Tiered responsive collapse ---------------------------------------- *\n *\n * ≤960px — 5th+ inline link collapses into More (tier-lg)\n * ≤780px — also 4th collapses (tier-md)\n * ≤580px — also 3rd collapses (tier-sm)\n * The first two links always stay inline. No hamburger on static export —\n * at very narrow widths 2 inline links + More button is the floor.\n */\n\n@media (max-width: 960px) {\n .site-header-link-collapse-lg {\n display: none;\n }\n\n .site-header-more-responsive-only.site-header-more-tier-lg {\n display: inline-flex;\n }\n\n .site-header-more-link-show-lg {\n display: flex;\n }\n\n .site-header-more-divider-responsive {\n display: block;\n }\n}\n\n@media (max-width: 780px) {\n .site-header-link-collapse-md {\n display: none;\n }\n\n .site-header-more-responsive-only.site-header-more-tier-md {\n display: inline-flex;\n }\n\n .site-header-more-link-show-md {\n display: flex;\n }\n}\n\n@media (max-width: 580px) {\n .site-header-link-collapse-sm {\n display: none;\n }\n\n .site-header-more-responsive-only.site-header-more-tier-sm {\n display: inline-flex;\n }\n\n .site-header-more-link-show-sm {\n display: flex;\n }\n}\n\n/* -------------------------------------------------------------------------\n * Footer\n * ------------------------------------------------------------------------- */\n\n.site-footer {\n margin-top: var(--space-xl);\n padding-bottom: var(--space-xl);\n color: var(--site-text-secondary);\n font-size: var(--type-xs);\n background-color: var(--site-page-bg);\n}\n\n.site-footer-inner {\n border-top: 0.5px solid var(--site-divider);\n padding-top: var(--space-xl);\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.site-footer-content {\n color: var(--site-text-secondary);\n}\n\n.site-footer-content p {\n margin: 0 0 0.5em;\n}\n\n.site-footer-nav-list {\n display: flex;\n flex-wrap: wrap;\n gap: 1rem;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.home-branding-credit {\n margin-top: var(--space-xl);\n padding-bottom: var(--space-xl);\n text-align: center;\n color: var(--site-text-secondary);\n font-size: var(--type-base);\n}\n\n.home-branding-credit a {\n display: inline-flex;\n align-items: center;\n gap: 0.38rem;\n color: inherit;\n text-decoration: none;\n border-bottom: 0.5px solid\n color-mix(in srgb, var(--site-text-secondary) 45%, transparent);\n transition:\n color 160ms ease,\n border-color 160ms ease;\n}\n\n.home-branding-credit a:hover,\n.home-branding-credit a:focus-visible {\n color: var(--site-text-primary);\n border-color: currentColor;\n}\n\n/* -------------------------------------------------------------------------\n * Tufte content-width constraint\n *\n * Mirrors the main site's 55% rule: reading text occupies a narrow\n * column inside the Tufte frame, leaving a wide right margin that\n * would host sidenotes on the main site. Media (images, video, audio)\n * is NOT included — galleries intentionally span the full frame so\n * they can breathe.\n *\n * Mobile (<=760px): `--layout-content-width` collapses to 100% via\n * tokens.css. Tablet: cap at 35rem for readability.\n * ------------------------------------------------------------------------- */\n\n.section-header,\n.section-body,\n.post-card-title,\n.post-card-summary,\n.post-card-link-domain,\n.post-card-quote-content,\n.post-card-quote-attribution,\n.post-card-quote-commentary,\n.post-card-footer,\n.reply-title,\n.reply-body,\n.reply-link-domain,\n.reply-footer,\n.thread-title,\n.thread-body,\n.thread-link-domain,\n.thread-footer,\n.collection-directory,\n.pagination,\n.empty-state,\n.page-summary {\n width: var(--layout-content-width);\n max-width: 100%;\n}\n\n@media (min-width: 761px) and (max-width: 1024px) {\n .section-header,\n .section-body,\n .post-card-title,\n .post-card-summary,\n .post-card-link-domain,\n .post-card-quote-content,\n .post-card-quote-attribution,\n .post-card-quote-commentary,\n .post-card-footer,\n .reply-title,\n .reply-body,\n .reply-link-domain,\n .reply-footer,\n .thread-title,\n .thread-body,\n .thread-link-domain,\n .thread-footer,\n .collection-directory,\n .pagination,\n .empty-state,\n .page-summary {\n width: min(100%, 35rem);\n }\n}\n\n/* -------------------------------------------------------------------------\n * Section headers\n * ------------------------------------------------------------------------- */\n\n.section-header {\n margin-bottom: var(--space-xl);\n padding-bottom: 1rem;\n border-bottom: 1px solid var(--site-border-light);\n}\n\n.section-title {\n margin: 0 0 0.25em;\n}\n\n.section-summary {\n margin: 0;\n color: var(--site-reading-meta);\n font-size: var(--type-secondary);\n}\n\n.section-meta {\n margin: 0.5em 0 0;\n color: var(--site-reading-meta);\n font-size: var(--type-sm);\n}\n\n/* -------------------------------------------------------------------------\n * Post list + post cards\n * ------------------------------------------------------------------------- */\n\n.post-list {\n display: flex;\n flex-direction: column;\n gap: calc(var(--space-xl) * 1.25);\n}\n\n.post-list-pinned {\n margin-bottom: calc(var(--space-xl) * 1.25);\n padding-bottom: calc(var(--space-xl) * 1.25);\n border-bottom: 1px solid var(--site-border-light);\n}\n\n/* Decorative divider between posts in a timeline feed. Mirrors the main\n site's `hr.feed-divider`: a trio of small chevron marks masked from the\n current text color, so it picks up the theme automatically.\n `margin-left` centers the divider within the 55% reading column so it\n visually sits at the middle of the post-card text stack. */\nhr.feed-divider {\n border: none;\n width: 30px;\n height: 9px;\n margin: 0;\n margin-left: calc(var(--layout-content-width) / 2 - 15px);\n color: var(--site-feed-divider-color);\n background-color: currentColor;\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 45 13'%3E%3Cpath fill='black' transform='translate(0,0) rotate(90 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(16,0) rotate(100 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(32,0) rotate(80 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3C/svg%3E\");\n mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 45 13'%3E%3Cpath fill='black' transform='translate(0,0) rotate(90 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(16,0) rotate(100 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3Cpath fill='black' transform='translate(32,0) rotate(80 6 6.5)' d='M6.765.5.177 6.093l2.61 5.966 8.39-3.17L6.765.5Z'/%3E%3C/svg%3E\");\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n -webkit-mask-position: center;\n mask-position: center;\n -webkit-mask-size: contain;\n mask-size: contain;\n}\n\n.post-card {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.post-card-title {\n font-family: var(--font-heading);\n font-size: var(--feed-note-title-size);\n font-weight: var(--type-heading-weight);\n line-height: var(--feed-note-title-leading);\n margin: 0;\n color: var(--site-reading-title);\n}\n\n.post-card-title a {\n color: inherit;\n text-decoration: none;\n}\n\n.post-card-title a:hover,\n.post-card-title a:focus {\n text-decoration: underline;\n text-underline-offset: 0.18em;\n}\n\n.post-card-summary {\n margin: 0;\n color: var(--site-reading-body);\n}\n\n/* Link card domain row — shown ABOVE the title (matches main site's\n `.feed-link-domain` pattern). Inline flex with icon + host text. */\n.post-card-link-domain,\n.thread-link-domain,\n.reply-link-domain {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n max-width: 100%;\n margin: 0 0 0.4rem 0;\n font-family: var(--font-ui);\n font-size: var(--type-ui-meta);\n font-weight: var(--fw-regular);\n line-height: 1.3;\n color: var(--site-text-secondary);\n text-decoration: none;\n word-break: break-all;\n transition: color 0.18s ease;\n}\n\n.post-card-link-domain:hover,\n.post-card-link-domain:focus,\n.thread-link-domain:hover,\n.thread-link-domain:focus,\n.reply-link-domain:hover,\n.reply-link-domain:focus {\n color: var(--site-text-primary);\n}\n\n.post-card-link-domain-icon {\n width: 0.72rem;\n height: 0.72rem;\n flex-shrink: 0;\n}\n\n/* Slight extra breathing room below link-card titles to match main site. */\n.post-card-link-title,\n.thread-link-title,\n.reply-link-title {\n text-wrap: pretty;\n}\n\n.post-card-link-title a,\n.thread-link-title a,\n.reply-link-title a {\n text-decoration: none;\n}\n\n.post-card-link-title a:hover,\n.post-card-link-title a:focus,\n.thread-link-title a:hover,\n.thread-link-title a:focus,\n.reply-link-title a:hover,\n.reply-link-title a:focus {\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n\n.post-card-media,\n.reply-media,\n.thread-media {\n display: grid;\n grid-template-columns: 1fr;\n gap: 0.75rem;\n}\n\n.post-card-figure img,\n.reply-figure img,\n.thread-figure img {\n display: block;\n width: 100%;\n border-radius: var(--media-radius);\n border: 1px solid var(--site-media-outline);\n}\n\n.post-card-figure video,\n.reply-figure video,\n.thread-figure video {\n display: block;\n width: 100%;\n height: auto;\n border-radius: var(--media-radius);\n border: 1px solid var(--site-media-outline);\n background: #000;\n}\n\n.post-card-figure audio,\n.reply-figure audio,\n.thread-figure audio {\n display: block;\n width: 100%;\n}\n\n.post-card-figure-video a {\n position: relative;\n display: block;\n}\n\n.post-card-video-badge {\n position: absolute;\n left: 0.5rem;\n bottom: 0.5rem;\n padding: 0.125rem 0.5rem;\n font-size: var(--type-xs);\n color: #fff;\n background: rgba(0, 0, 0, 0.65);\n border-radius: 999px;\n pointer-events: none;\n}\n\n.thread-file a,\n.reply-file a {\n color: var(--site-link);\n text-decoration: underline;\n}\n\n/* -------------------------------------------------------------------------\n * Quote format — decorative mark, serif body, attribution line\n * ------------------------------------------------------------------------- */\n\n.post-card-quote {\n position: static;\n margin: 0;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: var(--site-reading-quote);\n font-family: inherit;\n font-style: normal;\n}\n\n/* Quote-format posts have their own decorative `.post-card-quote-mark`\n SVG inside the blockquote — suppress the global blockquote icon. */\n.post-card-quote::before {\n content: none;\n}\n\n.post-card-quote-mark {\n display: block;\n position: relative;\n width: 1.7rem;\n margin-bottom: -0.1rem;\n margin-left: -0.04rem;\n line-height: 0;\n pointer-events: none;\n color: color-mix(in srgb, var(--site-accent) 14%, var(--site-divider));\n opacity: 0.66;\n}\n\n.post-card-quote-mark svg {\n display: block;\n width: 100%;\n height: auto;\n}\n\n.post-card-quote-content {\n font-family: var(--font-serif);\n color: var(--site-text-primary);\n font-size: var(--type-content-subtitle);\n line-height: var(--type-heading-leading);\n white-space: pre-line;\n text-wrap: pretty;\n margin: 0;\n}\n\n.post-card-quote-attribution {\n display: flex;\n align-items: center;\n gap: 0.45rem;\n flex-wrap: wrap;\n margin-top: 0.95rem;\n color: var(--site-text-secondary);\n font-family: var(--font-ui);\n font-size: var(--type-ui-meta);\n font-style: normal;\n line-height: 1.3;\n}\n\n.post-card-quote-attribution::before {\n content: \"\";\n width: 0.9rem;\n height: 1px;\n background: color-mix(\n in srgb,\n var(--site-text-secondary) 38%,\n var(--site-divider)\n );\n}\n\n.post-card-quote-source {\n color: inherit;\n text-decoration: underline;\n text-decoration-color: color-mix(\n in srgb,\n var(--site-text-secondary) 55%,\n transparent\n );\n text-underline-offset: 3px;\n}\n\n.post-card-quote-source:hover,\n.post-card-quote-source:focus {\n color: var(--site-text-primary);\n text-decoration-color: currentColor;\n}\n\n.post-card-quote-commentary {\n position: relative;\n margin-top: 1.1rem;\n padding-top: 0.95rem;\n color: color-mix(\n in srgb,\n var(--site-text-secondary) 84%,\n var(--site-text-primary)\n );\n text-wrap: pretty;\n}\n\n.post-card-quote-commentary::before {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n height: 1px;\n background: linear-gradient(\n 90deg,\n transparent 0%,\n color-mix(in srgb, var(--site-divider) 48%, transparent) 16%,\n color-mix(in srgb, var(--site-divider) 78%, transparent) 50%,\n color-mix(in srgb, var(--site-divider) 48%, transparent) 84%,\n transparent 100%\n );\n}\n\n.post-card-quote-commentary.prose > :first-child {\n margin-top: 0;\n}\n\n.post-card-quote-commentary.prose > :last-child {\n margin-bottom: 0;\n}\n\n.post-card-quote-commentary p {\n margin: 0;\n}\n\n.post-card-quote-commentary p + p,\n.post-card-quote-commentary ul,\n.post-card-quote-commentary ol,\n.post-card-quote-commentary blockquote,\n.post-card-quote-commentary pre {\n margin-top: 0.55rem;\n}\n\n/* Fade the post meta on quote cards so the quote body stays visually primary. */\n.post-card-quote ~ .post-card-footer,\n.post-card-quote-commentary + .post-card-footer {\n opacity: 0.72;\n}\n\n/* -------------------------------------------------------------------------\n * Post footer — meta (featured, time, external link, collections, pinned)\n * ------------------------------------------------------------------------- */\n\n.post-card-footer,\n.reply-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 10px;\n min-height: 2rem;\n font-size: var(--type-ui-hint);\n}\n\n.post-footer-detail {\n margin-top: 24px;\n /* Match the feed/reply footer size so the root post's footer doesn't\n visually dominate. Main site uses a larger size because every post\n on the detail page is rendered at detail size; here only the root\n post gets this class, which otherwise creates a mismatch with the\n replies below. */\n font-size: var(--type-ui-hint);\n color: var(--site-text-secondary);\n}\n\n.post-footer-meta {\n display: flex;\n flex: 1 1 auto;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n min-width: 0;\n font-family: var(--font-ui);\n line-height: 1.35;\n color: var(--site-text-secondary);\n}\n\n.post-footer-meta time {\n font-size: inherit;\n color: inherit;\n}\n\n.post-footer-featured {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: color-mix(\n in srgb,\n var(--search-mark-color) 72%,\n var(--site-text-secondary)\n );\n flex-shrink: 0;\n}\n\n.post-footer-featured svg {\n width: 1rem;\n height: 1rem;\n opacity: 0.9;\n}\n\n.post-footer-link {\n color: var(--site-text-secondary);\n text-decoration: none;\n white-space: nowrap;\n flex-shrink: 0;\n}\n\n.post-footer-link:hover,\n.post-footer-link:focus {\n color: var(--site-text-primary);\n text-decoration: underline;\n}\n\n.post-footer-external-link {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.6rem;\n height: 1.6rem;\n border-radius: 0.6rem;\n color: var(--site-text-secondary);\n text-decoration: none;\n transition:\n color 0.18s ease,\n background-color 0.16s ease;\n flex-shrink: 0;\n}\n\n.post-footer-external-link:hover,\n.post-footer-external-link:focus {\n color: var(--site-text-primary);\n}\n\n.post-footer-external-link svg {\n width: 1rem;\n height: 1rem;\n}\n\n.post-collection-tags {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n flex-wrap: wrap;\n min-width: 0;\n max-width: 100%;\n color: var(--site-text-secondary);\n}\n\n.post-collection-tag {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n color: inherit;\n text-decoration: none;\n min-width: 0;\n max-width: min(100%, 22ch);\n}\n\n.post-collection-tag:hover,\n.post-collection-tag:focus {\n color: var(--site-text-primary);\n text-decoration: underline;\n text-underline-offset: 0.18em;\n}\n\n.post-collection-tag-text {\n display: block;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.post-card-pin {\n font-size: var(--type-2xs);\n text-transform: uppercase;\n letter-spacing: var(--type-label-tracking);\n color: var(--site-accent);\n border: 1px solid var(--site-accent);\n padding: 0.1em 0.5em;\n border-radius: 999px;\n flex-shrink: 0;\n}\n\n/* -------------------------------------------------------------------------\n * Thread preview (list pages: root context + hero latest reply)\n *\n * Mirrors the main site's `.thread-group.thread-group-preview` layout:\n * content sits flush-left inside the preview, and the vertical rail +\n * dot markers are positioned OUTSIDE the content (overflowing into the\n * container's left gutter) via a negative `left` on the rail/dots.\n * ------------------------------------------------------------------------- */\n\n.thread-preview {\n /* Rail position: negative = overflow outside content. Mirrors\n `--site-thread-rail-line-left` on the main site. */\n --thread-rail-left: -27px;\n --thread-rail-width: 1px;\n --thread-rail-indent: 0px;\n --thread-dot-size: 10px;\n --thread-dot-ring-width: 2px;\n --thread-dot-border-width: 2px;\n --thread-hero-dot-size: 14px;\n --thread-hero-dot-border-width: 3px;\n --thread-item-spacing: 0.35rem;\n\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 0.6rem;\n padding-left: var(--thread-rail-indent);\n}\n\n@media (max-width: 760px) {\n .thread-preview {\n --thread-rail-left: -11px;\n --thread-rail-indent: 8px;\n }\n}\n\n/* Continuous vertical rail — subtle gradient so the line fades into\n whitespace at the top and tail. Sits outside the content via negative\n `left`. */\n.thread-preview::before {\n content: \"\";\n position: absolute;\n left: var(--thread-rail-left);\n top: 0;\n bottom: 0;\n width: var(--thread-rail-width);\n background: linear-gradient(\n 180deg,\n transparent 0,\n color-mix(in srgb, var(--site-threadline) 85%, transparent) 8%,\n color-mix(in srgb, var(--site-threadline) 55%, transparent) 100%\n );\n pointer-events: none;\n}\n\n.thread-preview-context {\n position: relative;\n display: flex;\n flex-direction: column;\n /* Match runtime `.thread-group-preview .thread-item` which uses\n `padding: var(--site-thread-item-spacing) 0` — adjacent items have\n 2x the token between them, so the flex `gap` here is 2x to match. */\n gap: calc(var(--site-thread-item-spacing) * 2);\n margin: 0;\n padding: 0;\n border-left: 0;\n}\n\n/* Individual entry in a thread preview — wraps a full `.post-card` so\n the root, second, and penultimate replies render with their own\n title/body/footer. Dot marker sits on the rail at the card's\n vertical midpoint. */\n.thread-preview .thread-item {\n position: relative;\n min-width: 0;\n max-width: 100%;\n}\n\n.thread-preview .thread-item::before {\n content: \"\";\n position: absolute;\n left: calc(\n var(--thread-rail-left) + var(--thread-rail-width) / 2 -\n var(--thread-dot-size) / 2 - var(--thread-rail-indent)\n );\n top: 1.4rem;\n width: var(--thread-dot-size);\n height: var(--thread-dot-size);\n border-radius: 50%;\n background-color: var(--site-threadline);\n border: var(--thread-dot-border-width) solid var(--site-page-bg);\n box-shadow: 0 0 0 var(--thread-dot-ring-width) var(--site-thread-dot-ring);\n z-index: 1;\n}\n\n/* Gap \"N more posts\" row has no card so we place the dot at its\n vertical midpoint instead of the card's top area. */\n.thread-preview .thread-item-gap {\n display: flex;\n align-items: center;\n padding: 0.15rem 0 0.35rem;\n}\n\n.thread-preview .thread-item-gap::before {\n top: 50%;\n transform: translateY(-50%);\n}\n\n/* Hidden-posts count — rendered as a rounded dashed pill matching\n the main site's `.thread-gap-link`. */\n.thread-preview-gap {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n align-self: flex-start;\n margin: 0.15rem 0;\n padding: 0.3rem 0.72rem;\n border: 1px dashed var(--site-thread-context-border);\n border-radius: 999px;\n background: var(--site-thread-gap-bg);\n color: var(--site-text-secondary);\n font-size: var(--type-thread-context-meta);\n line-height: 1.3;\n text-decoration: none;\n transition:\n border-color 0.18s ease,\n color 0.18s ease,\n background-color 0.18s ease;\n}\n\n.thread-preview-gap:hover,\n.thread-preview-gap:focus {\n border-color: color-mix(in srgb, var(--site-accent) 22%, var(--site-divider));\n color: var(--site-text-primary);\n text-decoration: none;\n}\n\n/* Hero (latest reply) row: spacing above and below matches the main\n site's `.thread-item-hero`, and the dot is larger + accent-colored. */\n.thread-preview-hero {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n padding-top: calc(var(--space-xl) * 1.1);\n margin-top: 0;\n}\n\n.thread-preview .thread-item-hero::before {\n left: calc(\n var(--thread-rail-left) + var(--thread-rail-width) / 2 -\n var(--thread-hero-dot-size) / 2 - var(--thread-rail-indent)\n );\n top: calc(var(--space-xl) * 1.1 + 1.4rem);\n width: var(--thread-hero-dot-size);\n height: var(--thread-hero-dot-size);\n background-color: var(--site-accent);\n border-width: var(--thread-hero-dot-border-width);\n}\n\n.thread-preview-thread-link {\n align-self: flex-start;\n font-size: var(--type-sm);\n color: var(--site-reading-meta);\n text-decoration: none;\n}\n\n.thread-preview-thread-link:hover,\n.thread-preview-thread-link:focus {\n color: var(--site-text-primary);\n text-decoration: underline;\n}\n\n/* -------------------------------------------------------------------------\n * Thread (single post page with inline replies)\n * ------------------------------------------------------------------------- */\n\n.thread {\n /* Rail variables match `.thread-preview` so the detail-page rail\n shares the same visual position. */\n --thread-rail-left: -27px;\n --thread-rail-width: 1px;\n --thread-rail-indent: 0px;\n --thread-dot-size: 10px;\n --thread-dot-ring-width: 2px;\n --thread-dot-border-width: 2px;\n --thread-hero-dot-size: 14px;\n --thread-hero-dot-border-width: 3px;\n\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 1.25rem;\n padding-left: var(--thread-rail-indent);\n}\n\n@media (max-width: 760px) {\n .thread {\n --thread-rail-left: -11px;\n --thread-rail-indent: 8px;\n }\n}\n\n/* Continuous vertical rail spanning the entire thread (root post + replies).\n Only shown when the root post actually has replies — a lone post should\n not have a rail or dot. Matches `.thread-preview`. */\n.thread-has-replies::before {\n content: \"\";\n position: absolute;\n left: var(--thread-rail-left);\n top: 0;\n bottom: 0;\n width: var(--thread-rail-width);\n background: linear-gradient(\n 180deg,\n transparent 0,\n color-mix(in srgb, var(--site-threadline) 85%, transparent) 8%,\n color-mix(in srgb, var(--site-threadline) 55%, transparent) 100%\n );\n pointer-events: none;\n}\n\n/* Dot marker for each post in the thread (root + replies). Shown only when\n there are replies so a solo post doesn't get an orphaned dot. */\n.thread .thread-item {\n position: relative;\n min-width: 0;\n max-width: 100%;\n}\n\n.thread-has-replies .thread-item::before {\n content: \"\";\n position: absolute;\n left: calc(\n var(--thread-rail-left) + var(--thread-rail-width) / 2 -\n var(--thread-dot-size) / 2 - var(--thread-rail-indent)\n );\n top: 1.4rem;\n width: var(--thread-dot-size);\n height: var(--thread-dot-size);\n border-radius: 50%;\n background-color: var(--site-threadline);\n border: var(--thread-dot-border-width) solid var(--site-page-bg);\n box-shadow: 0 0 0 var(--thread-dot-ring-width) var(--site-thread-dot-ring);\n z-index: 1;\n}\n\n.thread-item-root {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.thread-header {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.thread-title {\n font-family: var(--font-heading);\n font-size: var(--type-content-display);\n font-weight: var(--type-display-weight);\n line-height: var(--type-display-leading);\n margin: 0;\n color: var(--site-reading-title);\n}\n\n.thread-title a {\n color: inherit;\n text-decoration: none;\n}\n\n.thread-title a:hover,\n.thread-title a:focus {\n text-decoration: underline;\n text-underline-offset: 0.18em;\n}\n\n.thread-body {\n font-size: var(--type-content-body);\n line-height: var(--type-body-leading);\n color: var(--site-reading-body);\n}\n\n.thread-body > :first-child {\n margin-top: 0;\n}\n\n.thread-body > :last-child {\n margin-bottom: 0;\n}\n\n.thread-replies {\n /* Flex container for replies. The rail and dots are provided by\n `.thread::before` and `.thread-item::before` so replies stay visually\n connected to the root post. */\n display: flex;\n flex-direction: column;\n gap: var(--space-xl);\n}\n\n/* -------------------------------------------------------------------------\n * Replies\n * ------------------------------------------------------------------------- */\n\n.reply {\n scroll-margin-top: 1.5rem;\n display: flex;\n flex-direction: column;\n gap: 0.6rem;\n}\n\n.reply:target {\n background-color: var(--search-mark-bg);\n border-radius: 0.25rem;\n padding: 0.75rem 1rem;\n margin-left: -1rem;\n margin-right: -1rem;\n}\n\n.reply:target::before {\n background-color: var(--site-accent);\n}\n\n.reply-title {\n font-family: var(--font-heading);\n font-size: var(--type-content-subtitle);\n font-weight: var(--type-heading-weight);\n line-height: var(--type-heading-leading);\n margin: 0;\n color: var(--site-reading-heading);\n}\n\n.reply-body {\n color: var(--site-reading-body);\n}\n\n.reply-body > :first-child {\n margin-top: 0;\n}\n\n.reply-body > :last-child {\n margin-bottom: 0;\n}\n\n/* -------------------------------------------------------------------------\n * Collections page\n *\n * Mirrors the main site's authenticated-less collections view\n * (`ui/pages/CollectionsPage.tsx` + `ui/shared/CollectionDirectory.tsx`):\n * a page-intro block with count, then a two-column grid per row where a\n * monospace sequence label sits beside the collection title, description,\n * and entry/activity meta.\n * ------------------------------------------------------------------------- */\n\n.collections-page-shell {\n position: relative;\n display: flex;\n flex-direction: column;\n width: var(--layout-content-width);\n max-width: 100%;\n gap: clamp(1.25rem, 3vw, 1.75rem);\n}\n\n@media (min-width: 761px) and (max-width: 1024px) {\n .collections-page-shell {\n width: min(100%, 35rem);\n }\n}\n\n@media (max-width: 760px) {\n .collections-page-shell {\n width: 100%;\n }\n}\n\n.collections-page-header {\n position: relative;\n display: flex;\n align-items: flex-start;\n padding-bottom: 0.2rem;\n}\n\n.collections-page-heading {\n min-width: 0;\n flex: 1 1 18rem;\n}\n\n.page-intro {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n padding-bottom: 0.1rem;\n}\n\n.page-intro-title-row {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: 0.7rem;\n min-width: 0;\n}\n\n.page-intro-title {\n margin: 0;\n font-family: var(--font-heading);\n font-size: var(--type-title, 2rem);\n font-weight: var(--type-heading-weight);\n line-height: 1.15;\n letter-spacing: -0.01em;\n color: var(--site-text-primary);\n}\n\n.page-intro-meta-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: 0.75rem 1rem;\n}\n\n.page-intro-meta,\n.page-intro-description {\n margin: 0;\n color: var(--site-text-secondary);\n font-family: var(--font-ui);\n font-size: var(--type-secondary);\n line-height: 1.3;\n}\n\n.collection-directory {\n position: relative;\n display: flex;\n flex-direction: column;\n gap: 0.15rem;\n list-style: none;\n padding: 0;\n margin: 0;\n}\n\n.collection-directory-item {\n position: relative;\n display: flex;\n align-items: flex-start;\n gap: 0.75rem;\n padding: 0.95rem 0;\n background: transparent;\n text-decoration: none;\n}\n\n.collection-directory-main {\n --collection-directory-sequence-width: 3.5ch;\n --collection-directory-title-line-height: 1.18;\n min-width: 0;\n flex: 1;\n display: grid;\n grid-template-columns: var(--collection-directory-sequence-width) minmax(\n 0,\n 1fr\n );\n align-items: start;\n column-gap: 0.8rem;\n row-gap: 0.25rem;\n}\n\n.collection-directory-sequence {\n grid-column: 1;\n grid-row: 1;\n display: block;\n width: var(--collection-directory-sequence-width);\n padding-top: 0.2rem;\n font-family: var(--font-mono);\n font-size: var(--type-xs);\n font-variant-numeric: tabular-nums;\n line-height: var(--collection-directory-title-line-height);\n letter-spacing: 0.14em;\n color: var(--site-text-secondary);\n}\n\n.collection-directory-title-row {\n grid-column: 2;\n grid-row: 1;\n min-width: 0;\n display: flex;\n align-items: flex-start;\n}\n\n.collection-directory-title-link {\n color: inherit;\n text-decoration: none;\n transition: color 0.15s ease;\n}\n\n.collection-directory-title-link:hover,\n.collection-directory-title-link:focus-visible {\n color: var(--site-text-primary);\n}\n\n.collection-directory-title-link:hover .collection-directory-title,\n.collection-directory-title-link:focus-visible .collection-directory-title {\n text-decoration: underline;\n text-underline-offset: 3px;\n}\n\n.collection-directory-title {\n min-width: 0;\n display: inline-flex;\n align-items: center;\n gap: 0.45rem;\n font-family: var(--font-heading);\n font-size: var(--type-content-body);\n font-weight: var(--type-heading-weight);\n line-height: var(--collection-directory-title-line-height);\n letter-spacing: -0.02em;\n text-wrap: pretty;\n}\n\n.collection-directory-title-marker {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 0.95rem;\n min-width: 0.95rem;\n height: 0.95rem;\n color: var(--site-text-secondary);\n transition: color 0.15s ease;\n}\n\n.collection-directory-title-link:hover .collection-directory-title-marker,\n.collection-directory-title-link:focus-visible\n .collection-directory-title-marker {\n color: var(--site-text-primary);\n}\n\n.collection-directory-description {\n grid-column: 2;\n grid-row: 2;\n margin: 0;\n color: color-mix(in srgb, var(--site-text-secondary) 80%, transparent);\n font-family: var(--font-body);\n font-size: var(--type-sm);\n line-height: 1.45;\n}\n\n.collection-directory-description :where(p) {\n margin-top: 0.25em;\n margin-bottom: 0.25em;\n}\n\n/* Links inside the description inherit the description color so they sit in\n the same gray tone as the copy around them (matches the main site's\n `.prose { --tw-prose-links: var(--site-content-link); }` which resolves\n to `inherit`). The underline stays, drawn in currentColor. */\n.collection-directory-description :where(a) {\n color: inherit;\n text-decoration: underline;\n text-decoration-color: currentColor;\n text-underline-offset: 0.15em;\n}\n\n.collection-directory-description :where(a:hover),\n.collection-directory-description :where(a:focus-visible) {\n color: var(--site-text-primary);\n}\n\n.collection-directory-description :where(p:first-child) {\n margin-top: 0;\n}\n\n.collection-directory-description :where(p:last-child) {\n margin-bottom: 0;\n}\n\n.collection-directory-description + .collection-directory-summary {\n grid-row: 3;\n}\n\n.collection-directory-summary {\n grid-column: 2;\n grid-row: 2;\n display: flex;\n min-width: 0;\n overflow: hidden;\n align-items: center;\n gap: 0.2rem 0.5rem;\n margin: 0;\n color: var(--site-reading-meta);\n font-family: var(--font-ui);\n font-size: var(--type-sm);\n line-height: 1.3;\n white-space: nowrap;\n}\n\n.collection-directory-meta {\n flex: 0 0 auto;\n color: inherit;\n}\n\n.collection-directory-meta-separator {\n flex: 0 0 auto;\n color: color-mix(in srgb, var(--site-divider) 88%, transparent);\n}\n\n.collection-directory-updated {\n flex: 0 0 auto;\n color: inherit;\n white-space: nowrap;\n}\n\n.collection-directory-divider {\n padding: 1.5rem 0 0.85rem;\n}\n\n.collection-directory-divider-row {\n display: flex;\n align-items: center;\n gap: 0.95rem;\n}\n\n.collection-directory-divider-text {\n font-family: var(--font-heading);\n font-size: var(--type-secondary);\n letter-spacing: 0;\n font-style: normal;\n white-space: nowrap;\n color: var(--site-text-secondary);\n}\n\n.collection-directory-divider-line {\n flex: 1;\n height: 1px;\n border: none;\n margin: 0;\n background: linear-gradient(\n 90deg,\n color-mix(in srgb, var(--site-divider) 100%, transparent),\n color-mix(in srgb, var(--site-divider) 54%, transparent) 34%,\n transparent 86%\n );\n}\n\n/* -------------------------------------------------------------------------\n * Pagination\n * ------------------------------------------------------------------------- */\n\n.pagination {\n display: flex;\n align-items: center;\n justify-content: center;\n flex-wrap: wrap;\n gap: 1rem;\n padding: 1.5rem 0;\n font-size: var(--type-sm);\n font-variant-numeric: tabular-nums;\n}\n\n.pagination-link {\n color: var(--site-text-secondary);\n text-decoration: underline;\n text-underline-offset: 3px;\n transition: color 0.15s ease;\n}\n\n.pagination-link:hover,\n.pagination-link:focus {\n color: var(--site-text-primary);\n}\n\n.pagination-link.is-disabled {\n color: color-mix(in srgb, var(--site-text-secondary) 50%, transparent);\n cursor: default;\n text-decoration: none;\n}\n\n.pagination-current {\n color: var(--site-text-primary);\n font-weight: var(--fw-medium);\n}\n\n.pagination-ellipsis {\n color: var(--site-text-secondary);\n}\n\n/* -------------------------------------------------------------------------\n * Empty states + utility\n * ------------------------------------------------------------------------- */\n\n.empty-state {\n color: var(--site-reading-meta);\n font-style: italic;\n text-align: center;\n padding: var(--space-xl) 0;\n}\n\n.page,\n.section {\n display: block;\n}\n\n.page-summary,\n.section-summary {\n font-size: var(--type-secondary);\n color: var(--site-reading-meta);\n}\n\n/* -------------------------------------------------------------------------\n * Wider viewport refinements\n * ------------------------------------------------------------------------- */\n\n@media (min-width: 768px) {\n .site-main {\n padding-top: calc(var(--space-xl) * 1.5);\n padding-bottom: calc(var(--space-xl) * 1.5);\n }\n\n .post-card-media,\n .thread-media,\n .reply-media {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n\n .post-card-media:has(> :only-child),\n .thread-media:has(> :only-child),\n .reply-media:has(> :only-child) {\n grid-template-columns: 1fr;\n }\n}\n";
3280
3280
  //#endregion
3281
3281
  //#region src/services/export-theme/assets/client-site.js?raw
3282
3282
  var client_site_default$1 = "var e=null,t=0,n=!1,r=null,i=null,a=0;function o(e){return!isFinite(e)||e<0?`0:00`:`${Math.floor(e/60)}:${String(Math.floor(e%60)).padStart(2,`0`)}`}function s(e){return e.querySelector(`audio.media-audio-el`)}function c(e){return e.querySelector(`[data-audio-range]`)}function l(e){return e.querySelector(`[data-audio-waveform]`)}function u(e,t){let n=`${(t*100).toFixed(1)}%`;e.style.background=`linear-gradient(to right, var(--site-text-primary) ${n}, transparent ${n})`}var d=new WeakMap,f=new WeakSet;async function p(e,t){let n=await(await fetch(e)).arrayBuffer(),r=new AudioContext;try{let e=(await r.decodeAudioData(n)).getChannelData(0),i=Math.max(1,Math.floor(e.length/t)),a=Array(t);for(let n=0;n<t;n++){let t=0,r=n*i,o=Math.min(r+i,e.length);for(let n=r;n<o;n++){let r=Math.abs(e[n]);r>t&&(t=r)}a[n]=t}let o=0;for(let e of a)e>o&&(o=e);if(o>0)for(let e=0;e<t;e++)a[e]/=o;return a}finally{await r.close()}}function m(){let e=document.querySelectorAll(`[data-audio-peaks]`),t=[];for(let n of e){let e=n.dataset.audioPeaks;if(!e)continue;let r=n.closest(`.media-audio-card`);if(!(!r||d.has(r)))try{let n=JSON.parse(e);if(!Array.isArray(n))continue;d.set(r,n),r.classList.add(`has-waveform`),t.push(r)}catch{}}t.length>0&&requestAnimationFrame(()=>{for(let e of t)h(e,0)})}async function ee(e){if(d.has(e)||f.has(e))return;f.add(e);let t=e.querySelector(`audio.media-audio-el source`),n=l(e);if(!t?.src||!n)return;let r=n.getBoundingClientRect().width,i=Math.max(20,Math.floor(r/3));try{let n=await p(t.src,i);d.set(e,n),e.classList.add(`has-waveform`);let r=s(e),a=r?.duration??0;h(e,r&&isFinite(a)&&a>0?r.currentTime/a:0)}catch{}}function h(e,t){let n=d.get(e),r=l(e);if(!n||!r)return;let i=window.devicePixelRatio||1,a=r.getBoundingClientRect(),o=Math.round(a.width*i),s=Math.round(a.height*i);if(o===0||s===0)return;(r.width!==o||r.height!==s)&&(r.width=o,r.height=s);let c=r.getContext(`2d`);if(!c)return;c.clearRect(0,0,o,s);let u=n.length,f=o/u,p=Math.max(1,Math.round(f*.6)),m=Math.round(2*i),ee=s*.85,h=getComputedStyle(r).getPropertyValue(`--site-text-primary`).trim()||`#000`;for(let e=0;e<u;e++){let r=Math.round(e*f+(f-p)/2),a=Math.max(m,Math.round(n[e]*ee)),o=Math.round((s-a)/2);c.globalAlpha=(e+.5)/u<=t?.9:.2,c.fillStyle=h;let l=Math.min(p/2,i);c.beginPath(),c.roundRect(r,o,p,a,l),c.fill()}c.globalAlpha=1}function te(e,t){let{currentTime:r,duration:i}=t,a=isFinite(i)&&i>0,s=a?r/i:0;if(!n){let t=c(e);t&&a&&(t.value=String(Math.round(s*1e3)),u(t,s)),d.has(e)&&h(e,s);let n=e.querySelector(`[data-audio-time]`);n&&(n.textContent=a?`${o(r)} / ${o(i)}`:o(r))}}function ne(){if(!e)return;let n=s(e);!n||n.paused||(te(e,n),t=requestAnimationFrame(ne))}function re(){if(e){let t=s(e);t&&!t.paused&&t.pause(),e.classList.remove(`is-playing`),e=null}cancelAnimationFrame(t)}async function ie(n){let r=s(n);if(r)if(e&&e!==n&&re(),r.paused){e=n,n.classList.add(`is-playing`);try{await r.play()}catch{n.classList.remove(`is-playing`),e=null;return}t=requestAnimationFrame(ne),ee(n)}else r.pause(),n.classList.remove(`is-playing`),cancelAnimationFrame(t),e=null}function ae(e){let t=e.closest(`.media-audio-card`);if(!t)return;let n=s(t);if(!n)return;let r=Number(e.value)/1e3,i=n.duration;isFinite(i)&&i>0&&(n.currentTime=r*i)}function oe(e,t){let n=e.getBoundingClientRect();a=Math.max(0,Math.min(1,(t.clientX-n.left)/n.width));let r=e.closest(`.media-audio-card`);if(!r)return;h(r,a);let i=s(r),c=r.querySelector(`[data-audio-time]`);if(i&&c){let e=i.duration;isFinite(e)&&e>0&&(c.textContent=`${o(a*e)} / ${o(e)}`)}}async function se(n){let r=n.closest(`.media-audio-card`);if(!r)return;let i=s(r);if(i)if(i.paused){e&&e!==r&&re(),e=r,r.classList.add(`is-playing`);try{await i.play()}catch{r.classList.remove(`is-playing`),e=null;return}let n=i.duration;isFinite(n)&&n>0&&(i.currentTime=a*n),t=requestAnimationFrame(ne),ee(r)}else{let e=i.duration;isFinite(e)&&e>0&&(i.currentTime=a*e)}}document.addEventListener(`pointerdown`,e=>{let t=e.target;t.matches(`[data-audio-range]`)?(n=!0,r=t):t.matches(`[data-audio-waveform]`)&&(n=!0,i=t,oe(t,e))},!0),document.addEventListener(`pointermove`,e=>{n&&i&&oe(i,e)},!0),document.addEventListener(`pointerup`,()=>{n&&(r?(ae(r),r=null):i&&=(se(i),null)),n=!1},!0),document.addEventListener(`pointercancel`,()=>{r=null,i=null,n=!1},!0),document.addEventListener(`click`,e=>{let t=e.target.closest(`[data-audio-play]`);if(!t)return;e.preventDefault();let n=t.closest(`.media-audio-card`);n&&ie(n)}),document.addEventListener(`input`,e=>{let t=e.target;if(!t.matches(`[data-audio-range]`))return;let n=t.closest(`.media-audio-card`);if(!n)return;let r=s(n);if(!r)return;let i=Number(t.value)/1e3;u(t,i);let a=r.duration,c=n.querySelector(`[data-audio-time]`);c&&isFinite(a)&&a>0&&(c.textContent=`${o(i*a)} / ${o(a)}`)},!0),document.addEventListener(`ended`,n=>{let r=n.target;if(!r.closest)return;let i=r.closest(`.media-audio-card`);if(!i)return;i.classList.remove(`is-playing`),cancelAnimationFrame(t),e=null;let a=c(i);a&&(a.value=`0`,u(a,0)),d.has(i)&&h(i,0);let o=i.querySelector(`[data-audio-time]`);o&&(o.textContent=`0:00`)},!0),document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,m):m();var g=`jant:media-lightbox-toggle`,ce=`75% 0px`,le=.6,_=.25,ue=160,v=new Set,de=new WeakMap,fe=new WeakSet,y=null,b=null,x=null,pe=!1,S=null,C=null,w=null;function me(e){return e.length===0?null:[...e].sort((e,t)=>t.visibleArea===e.visibleArea?e.centerDistance===t.centerDistance?t.intersectionRatio-e.intersectionRatio:e.centerDistance-t.centerDistance:t.visibleArea-e.visibleArea)[0]??null}function he(){return{x:(globalThis.innerWidth||document.documentElement.clientWidth||0)/2,y:(globalThis.innerHeight||document.documentElement.clientHeight||0)/2}}function ge(e){return de.get(e)}function _e(e){return e.closest(`.media-video-wrap`)?.querySelector(`[data-feed-video-mute-toggle]`)??null}function T(e){let t=_e(e);if(!t)return;let n=x!==e||e.muted;t.dataset.muted=n?`true`:`false`,t.setAttribute(`aria-label`,n?`Play with sound`:`Mute video`)}function ve(){for(let e of v)T(e)}function ye(e){if(fe.has(e))return;let t=e.dataset.videoSrc;t&&(e.getAttribute(`src`)!==t&&(e.src=t),e.load(),fe.add(e))}function E(e){e?.pause()}function be(e){ye(e),e.muted=x!==e,e.playsInline=!0,e.loop=!0,T(e),e.play().catch(()=>{})}function xe(){for(let e of v)e.isConnected||(C?.unobserve(e),w?.unobserve(e),v.delete(e),e===y&&(y=null),e===b&&(b=null),e===x&&(x=null))}function Se(){if(S=null,xe(),document.hidden||pe){E(y);return}let e=[];for(let t of v){let n=ge(t);n&&(n.intersectionRatio<le||e.push({video:t,...n}))}let t=null;if(b?.isConnected){let e=ge(b);e&&e.intersectionRatio>_?t=b:(!e||e.intersectionRatio<=_)&&(b=null)}if(t||=me(e)?.video??null,!t){let e=y?ge(y):void 0;if(y&&e&&e.intersectionRatio>_)return;E(y),y=null;return}t!==y&&(E(y),y=t),be(t);for(let e of v)e!==t&&(E(e),T(e))}function D(){S!==null&&globalThis.clearTimeout(S),S=globalThis.setTimeout(Se,ue)}function Ce(e){let t=he();for(let n of e){let e=n.target,r=n.boundingClientRect,i=r.left+r.width/2,a=r.top+r.height/2,o=n.intersectionRect.width*n.intersectionRect.height,s=Math.hypot(i-t.x,a-t.y);de.set(e,{intersectionRatio:n.intersectionRatio,visibleArea:o,centerDistance:s})}D()}function we(e){for(let t of e)t.isIntersecting&&ye(t.target)}function Te(){C&&w||globalThis.IntersectionObserver!==void 0&&(C=new globalThis.IntersectionObserver(Ce,{threshold:[0,_,le,1]}),w=new globalThis.IntersectionObserver(we,{rootMargin:ce,threshold:0}))}function Ee(e){v.has(e)||(Te(),!(!C||!w)&&(v.add(e),C.observe(e),w.observe(e),_e(e)?.addEventListener(`click`,De),T(e)))}function De(e){e.preventDefault(),e.stopPropagation();let t=e.currentTarget.closest(`.media-video-wrap`)?.querySelector(`[data-feed-short-video]`);t&&(b=t,x!==t||t.muted?(x&&x!==t&&(x.muted=!0),x=t,y!==t&&(E(y),y=t),be(t)):(x=null,t.muted=!0,T(t)),ve(),D())}function Oe(e=document){let t=e.querySelectorAll(`[data-feed-short-video]`);for(let e of t)Ee(e);D()}document.addEventListener(g,e=>{pe=e.detail?.open===!0,pe&&E(y),D()}),document.addEventListener(`visibilitychange`,()=>{document.hidden&&E(y),D()}),document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,()=>Oe(),{once:!0}):queueMicrotask(()=>Oe());var ke=4;function Ae(e){return e.querySelector(`[data-post-media]`)}function je(e){let t=Ae(e);if(!t)return;let{scrollLeft:n,scrollWidth:r,clientWidth:i}=t;e.classList.toggle(`can-scroll-start`,n>ke),e.classList.toggle(`can-scroll-end`,n+i<r-ke)}function Me(e){return Math.max(160,Math.round(e.clientWidth*.85))}function O(e,t){e.scrollBy({left:t*Me(e),behavior:`smooth`})}function k(e){if(e.dataset.scrollHintReady===`1`)return;let t=Ae(e);t&&(e.dataset.scrollHintReady=`1`,je(e),t.addEventListener(`scroll`,()=>je(e),{passive:!0}),e.querySelector(`.media-gallery-nav-prev`)?.addEventListener(`click`,()=>O(t,-1)),e.querySelector(`.media-gallery-nav-next`)?.addEventListener(`click`,()=>O(t,1)),t.addEventListener(`keydown`,e=>{if(e.target===t)switch(e.key){case`ArrowRight`:e.preventDefault(),O(t,1);break;case`ArrowLeft`:e.preventDefault(),O(t,-1);break;case`Home`:e.preventDefault(),t.scrollTo({left:0,behavior:`smooth`});break;case`End`:e.preventDefault(),t.scrollTo({left:t.scrollWidth,behavior:`smooth`});break}}))}function Ne(){document.querySelectorAll(`.media-gallery-scroll-wrap`).forEach(k)}var Pe=new globalThis.MutationObserver(e=>{for(let t of e)for(let e of t.addedNodes)e instanceof HTMLElement&&(e.matches(`.media-gallery-scroll-wrap`)&&k(e),e.querySelectorAll(`.media-gallery-scroll-wrap`).forEach(k))});document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,()=>{Ne(),Pe.observe(document.body,{childList:!0,subtree:!0})}):(Ne(),Pe.observe(document.body,{childList:!0,subtree:!0}));function Fe(e){let t=e.querySelector(`.site-header-more-btn`),n=e.querySelector(`.site-header-more-popover`);if(!t||!n||t.dataset.moreInitialized===`true`)return;t.dataset.moreInitialized=`true`;function r(){n.setAttribute(`aria-hidden`,`false`),t.setAttribute(`aria-expanded`,`true`),document.dispatchEvent(new CustomEvent(`basecoat:popover`,{detail:{source:t.parentElement}}))}function i(e=!1){n.setAttribute(`aria-hidden`,`true`),t.setAttribute(`aria-expanded`,`false`),e&&t.focus()}t.addEventListener(`click`,e=>{e.preventDefault(),e.stopPropagation(),t.getAttribute(`aria-expanded`)===`true`?i():r()}),document.addEventListener(`click`,e=>{e.target instanceof Node&&(t.parentElement?.contains(e.target)||i())}),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&n.getAttribute(`aria-hidden`)===`false`&&i(!0)}),document.addEventListener(`basecoat:popover`,e=>{e.detail?.source!==t.parentElement&&i()})}var Ie=`jant:nav-fresh-visits`;function Le(){let e=document.createElement(`span`);return e.className=`site-header-link-fresh`,e.setAttribute(`aria-hidden`,`true`),e.textContent=`*`,e}function Re(e){try{let t=JSON.parse(localStorage.getItem(Ie)||`{}`),n=location.pathname,r=e.querySelectorAll(`[data-fresh-at]`);for(let e of r){let r=new URL(e.href).pathname,i=parseInt(e.dataset.freshAt,10);if(r===n)t[r]=Math.floor(Date.now()/1e3);else{let n=t[r];if(!n||n<i){let t=e.querySelector(`svg`);e.insertBefore(Le(),t)}}}localStorage.setItem(Ie,JSON.stringify(t))}catch{}}function ze(e=document){let t=e.querySelector(`.site-header-hamburger`),n=e.querySelector(`#site-nav-drawer`),r=e.querySelector(`.site-nav-drawer-backdrop`),i=n?.querySelector(`.site-nav-drawer-close`);if(Re(e),Fe(e),!t||!n||!r||t.dataset.drawerInitialized===`true`)return;t.dataset.drawerInitialized=`true`;function a(){n.setAttribute(`aria-hidden`,`false`),n.removeAttribute(`inert`),r.setAttribute(`aria-hidden`,`false`),t.setAttribute(`aria-expanded`,`true`),document.documentElement.classList.add(`drawer-open`);let e=n.querySelector(`.site-nav-drawer-close`)??n.querySelector(`a[href], button`);e&&e.focus()}function o(e=!0){n.setAttribute(`aria-hidden`,`true`),r.setAttribute(`aria-hidden`,`true`),t.setAttribute(`aria-expanded`,`false`),document.documentElement.classList.remove(`drawer-open`),n.addEventListener(`transitionend`,()=>{n.getAttribute(`aria-hidden`)===`true`&&n.setAttribute(`inert`,``)},{once:!0}),e&&t.focus()}t.addEventListener(`click`,()=>{t.getAttribute(`aria-expanded`)===`true`?o():a()}),i?.addEventListener(`click`,()=>o()),r.addEventListener(`click`,()=>o()),n.addEventListener(`click`,e=>{e.target instanceof Element&&e.target.closest(`a[href]`)&&o(!1)}),n.addEventListener(`keydown`,e=>{e.key===`Escape`&&(e.preventDefault(),o())})}ze();var A=globalThis,j=A.ShadowRoot&&(A.ShadyCSS===void 0||A.ShadyCSS.nativeShadow)&&`adoptedStyleSheets`in Document.prototype&&`replace`in CSSStyleSheet.prototype,Be=Symbol(),Ve=new WeakMap,He=class{constructor(e,t,n){if(this._$cssResult$=!0,n!==Be)throw Error(\"CSSResult is not constructable. Use `unsafeCSS` or `css` instead.\");this.cssText=e,this.t=t}get styleSheet(){let e=this.o,t=this.t;if(j&&e===void 0){let n=t!==void 0&&t.length===1;n&&(e=Ve.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),n&&Ve.set(t,e))}return e}toString(){return this.cssText}},Ue=e=>new He(typeof e==`string`?e:e+``,void 0,Be),We=(e,t)=>{if(j)e.adoptedStyleSheets=t.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let n of t){let t=document.createElement(`style`),r=A.litNonce;r!==void 0&&t.setAttribute(`nonce`,r),t.textContent=n.cssText,e.appendChild(t)}},Ge=j?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t=``;for(let n of e.cssRules)t+=n.cssText;return Ue(t)})(e):e,{is:Ke,defineProperty:qe,getOwnPropertyDescriptor:Je,getOwnPropertyNames:Ye,getOwnPropertySymbols:Xe,getPrototypeOf:Ze}=Object,M=globalThis,Qe=M.trustedTypes,$e=Qe?Qe.emptyScript:``,et=M.reactiveElementPolyfillSupport,N=(e,t)=>e,P={toAttribute(e,t){switch(t){case Boolean:e=e?$e:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let n=e;switch(t){case Boolean:n=e!==null;break;case Number:n=e===null?null:Number(e);break;case Object:case Array:try{n=JSON.parse(e)}catch{n=null}}return n}},tt=(e,t)=>!Ke(e,t),nt={attribute:!0,type:String,converter:P,reflect:!1,useDefault:!1,hasChanged:tt};Symbol.metadata??=Symbol(`metadata`),M.litPropertyMetadata??=new WeakMap;var F=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=nt){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){let n=Symbol(),r=this.getPropertyDescriptor(e,n,t);r!==void 0&&qe(this.prototype,e,r)}}static getPropertyDescriptor(e,t,n){let{get:r,set:i}=Je(this.prototype,e)??{get(){return this[t]},set(e){this[t]=e}};return{get:r,set(t){let a=r?.call(this);i?.call(this,t),this.requestUpdate(e,a,n)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??nt}static _$Ei(){if(this.hasOwnProperty(N(`elementProperties`)))return;let e=Ze(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(N(`finalized`)))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(N(`properties`))){let e=this.properties,t=[...Ye(e),...Xe(e)];for(let n of t)this.createProperty(n,e[n])}let e=this[Symbol.metadata];if(e!==null){let t=litPropertyMetadata.get(e);if(t!==void 0)for(let[e,n]of t)this.elementProperties.set(e,n)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let n=this._$Eu(e,t);n!==void 0&&this._$Eh.set(n,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){let t=[];if(Array.isArray(e)){let n=new Set(e.flat(1/0).reverse());for(let e of n)t.unshift(Ge(e))}else e!==void 0&&t.push(Ge(e));return t}static _$Eu(e,t){let n=t.attribute;return!1===n?void 0:typeof n==`string`?n:typeof e==`string`?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),this.renderRoot!==void 0&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){let e=new Map,t=this.constructor.elementProperties;for(let n of t.keys())this.hasOwnProperty(n)&&(e.set(n,this[n]),delete this[n]);e.size>0&&(this._$Ep=e)}createRenderRoot(){let e=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return We(e,this.constructor.elementStyles),e}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,n){this._$AK(e,n)}_$ET(e,t){let n=this.constructor.elementProperties.get(e),r=this.constructor._$Eu(e,n);if(r!==void 0&&!0===n.reflect){let i=(n.converter?.toAttribute===void 0?P:n.converter).toAttribute(t,n.type);this._$Em=e,i==null?this.removeAttribute(r):this.setAttribute(r,i),this._$Em=null}}_$AK(e,t){let n=this.constructor,r=n._$Eh.get(e);if(r!==void 0&&this._$Em!==r){let e=n.getPropertyOptions(r),i=typeof e.converter==`function`?{fromAttribute:e.converter}:e.converter?.fromAttribute===void 0?P:e.converter;this._$Em=r;let a=i.fromAttribute(t,e.type);this[r]=a??this._$Ej?.get(r)??a,this._$Em=null}}requestUpdate(e,t,n,r=!1,i){if(e!==void 0){let a=this.constructor;if(!1===r&&(i=this[e]),n??=a.getPropertyOptions(e),!((n.hasChanged??tt)(i,t)||n.useDefault&&n.reflect&&i===this._$Ej?.get(e)&&!this.hasAttribute(a._$Eu(e,n))))return;this.C(e,t,n)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(e,t,{useDefault:n,reflect:r,wrapped:i},a){n&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,a??t??this[e]),!0!==i||a!==void 0)||(this._$AL.has(e)||(this.hasUpdated||n||(t=void 0),this._$AL.set(e,t)),!0===r&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}let e=this.scheduleUpdate();return e!=null&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(let[e,t]of this._$Ep)this[e]=t;this._$Ep=void 0}let e=this.constructor.elementProperties;if(e.size>0)for(let[t,n]of e){let{wrapped:e}=n,r=this[t];!0!==e||this._$AL.has(t)||r===void 0||this.C(t,void 0,n,r)}}let e=!1,t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(e=>e.hostUpdate?.()),this.update(t)):this._$EM()}catch(t){throw e=!1,this._$EM(),t}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(e){}firstUpdated(e){}};F.elementStyles=[],F.shadowRootOptions={mode:`open`},F[N(`elementProperties`)]=new Map,F[N(`finalized`)]=new Map,et?.({ReactiveElement:F}),(M.reactiveElementVersions??=[]).push(`2.1.2`);var I=globalThis,rt=e=>e,L=I.trustedTypes,it=L?L.createPolicy(`lit-html`,{createHTML:e=>e}):void 0,at=`$lit$`,R=`lit$${Math.random().toFixed(9).slice(2)}$`,ot=`?`+R,st=`<${ot}>`,z=document,B=()=>z.createComment(``),V=e=>e===null||typeof e!=`object`&&typeof e!=`function`,ct=Array.isArray,lt=e=>ct(e)||typeof e?.[Symbol.iterator]==`function`,ut=`[ \n\\f\\r]`,H=/<(?:(!--|\\/[^a-zA-Z])|(\\/?[a-zA-Z][^>\\s]*)|(\\/?$))/g,dt=/-->/g,ft=/>/g,U=RegExp(`>|${ut}(?:([^\\\\s\"'>=/]+)(${ut}*=${ut}*(?:[^ \\t\\n\\f\\r\"'\\`<>=]|(\"|')|))|$)`,`g`),pt=/'/g,mt=/\"/g,ht=/^(?:script|style|textarea|title)$/i,gt=e=>(t,...n)=>({_$litType$:e,strings:t,values:n}),W=gt(1),_t=gt(2),G=Symbol.for(`lit-noChange`),K=Symbol.for(`lit-nothing`),vt=new WeakMap,q=z.createTreeWalker(z,129);function yt(e,t){if(!ct(e)||!e.hasOwnProperty(`raw`))throw Error(`invalid template strings array`);return it===void 0?t:it.createHTML(t)}var bt=(e,t)=>{let n=e.length-1,r=[],i,a=t===2?`<svg>`:t===3?`<math>`:``,o=H;for(let t=0;t<n;t++){let n=e[t],s,c,l=-1,u=0;for(;u<n.length&&(o.lastIndex=u,c=o.exec(n),c!==null);)u=o.lastIndex,o===H?c[1]===`!--`?o=dt:c[1]===void 0?c[2]===void 0?c[3]!==void 0&&(o=U):(ht.test(c[2])&&(i=RegExp(`</`+c[2],`g`)),o=U):o=ft:o===U?c[0]===`>`?(o=i??H,l=-1):c[1]===void 0?l=-2:(l=o.lastIndex-c[2].length,s=c[1],o=c[3]===void 0?U:c[3]===`\"`?mt:pt):o===mt||o===pt?o=U:o===dt||o===ft?o=H:(o=U,i=void 0);let d=o===U&&e[t+1].startsWith(`/>`)?` `:``;a+=o===H?n+st:l>=0?(r.push(s),n.slice(0,l)+at+n.slice(l)+R+d):n+R+(l===-2?t:d)}return[yt(e,a+(e[n]||`<?>`)+(t===2?`</svg>`:t===3?`</math>`:``)),r]},xt=class e{constructor({strings:t,_$litType$:n},r){let i;this.parts=[];let a=0,o=0,s=t.length-1,c=this.parts,[l,u]=bt(t,n);if(this.el=e.createElement(l,r),q.currentNode=this.el.content,n===2||n===3){let e=this.el.content.firstChild;e.replaceWith(...e.childNodes)}for(;(i=q.nextNode())!==null&&c.length<s;){if(i.nodeType===1){if(i.hasAttributes())for(let e of i.getAttributeNames())if(e.endsWith(at)){let t=u[o++],n=i.getAttribute(e).split(R),r=/([.?@])?(.*)/.exec(t);c.push({type:1,index:a,name:r[2],strings:n,ctor:r[1]===`.`?Ct:r[1]===`?`?wt:r[1]===`@`?Tt:X}),i.removeAttribute(e)}else e.startsWith(R)&&(c.push({type:6,index:a}),i.removeAttribute(e));if(ht.test(i.tagName)){let e=i.textContent.split(R),t=e.length-1;if(t>0){i.textContent=L?L.emptyScript:``;for(let n=0;n<t;n++)i.append(e[n],B()),q.nextNode(),c.push({type:2,index:++a});i.append(e[t],B())}}}else if(i.nodeType===8)if(i.data===ot)c.push({type:2,index:a});else{let e=-1;for(;(e=i.data.indexOf(R,e+1))!==-1;)c.push({type:7,index:a}),e+=R.length-1}a++}}static createElement(e,t){let n=z.createElement(`template`);return n.innerHTML=e,n}};function J(e,t,n=e,r){if(t===G)return t;let i=r===void 0?n._$Cl:n._$Co?.[r],a=V(t)?void 0:t._$litDirective$;return i?.constructor!==a&&(i?._$AO?.(!1),a===void 0?i=void 0:(i=new a(e),i._$AT(e,n,r)),r===void 0?n._$Cl=i:(n._$Co??=[])[r]=i),i!==void 0&&(t=J(e,i._$AS(e,t.values),i,r)),t}var St=class{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){let{el:{content:t},parts:n}=this._$AD,r=(e?.creationScope??z).importNode(t,!0);q.currentNode=r;let i=q.nextNode(),a=0,o=0,s=n[0];for(;s!==void 0;){if(a===s.index){let t;s.type===2?t=new Y(i,i.nextSibling,this,e):s.type===1?t=new s.ctor(i,s.name,s.strings,this,e):s.type===6&&(t=new Et(i,this,e)),this._$AV.push(t),s=n[++o]}a!==s?.index&&(i=q.nextNode(),a++)}return q.currentNode=z,r}p(e){let t=0;for(let n of this._$AV)n!==void 0&&(n.strings===void 0?n._$AI(e[t]):(n._$AI(e,n,t),t+=n.strings.length-2)),t++}},Y=class e{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,n,r){this.type=2,this._$AH=K,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=n,this.options=r,this._$Cv=r?.isConnected??!0}get parentNode(){let e=this._$AA.parentNode,t=this._$AM;return t!==void 0&&e?.nodeType===11&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=J(this,e,t),V(e)?e===K||e==null||e===``?(this._$AH!==K&&this._$AR(),this._$AH=K):e!==this._$AH&&e!==G&&this._(e):e._$litType$===void 0?e.nodeType===void 0?lt(e)?this.k(e):this._(e):this.T(e):this.$(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==K&&V(this._$AH)?this._$AA.nextSibling.data=e:this.T(z.createTextNode(e)),this._$AH=e}$(e){let{values:t,_$litType$:n}=e,r=typeof n==`number`?this._$AC(e):(n.el===void 0&&(n.el=xt.createElement(yt(n.h,n.h[0]),this.options)),n);if(this._$AH?._$AD===r)this._$AH.p(t);else{let e=new St(r,this),n=e.u(this.options);e.p(t),this.T(n),this._$AH=e}}_$AC(e){let t=vt.get(e.strings);return t===void 0&&vt.set(e.strings,t=new xt(e)),t}k(t){ct(this._$AH)||(this._$AH=[],this._$AR());let n=this._$AH,r,i=0;for(let a of t)i===n.length?n.push(r=new e(this.O(B()),this.O(B()),this,this.options)):r=n[i],r._$AI(a),i++;i<n.length&&(this._$AR(r&&r._$AB.nextSibling,i),n.length=i)}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(!1,!0,t);e!==this._$AB;){let t=rt(e).nextSibling;rt(e).remove(),e=t}}setConnected(e){this._$AM===void 0&&(this._$Cv=e,this._$AP?.(e))}},X=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,n,r,i){this.type=1,this._$AH=K,this._$AN=void 0,this.element=e,this.name=t,this._$AM=r,this.options=i,n.length>2||n[0]!==``||n[1]!==``?(this._$AH=Array(n.length-1).fill(new String),this.strings=n):this._$AH=K}_$AI(e,t=this,n,r){let i=this.strings,a=!1;if(i===void 0)e=J(this,e,t,0),a=!V(e)||e!==this._$AH&&e!==G,a&&(this._$AH=e);else{let r=e,o,s;for(e=i[0],o=0;o<i.length-1;o++)s=J(this,r[n+o],t,o),s===G&&(s=this._$AH[o]),a||=!V(s)||s!==this._$AH[o],s===K?e=K:e!==K&&(e+=(s??``)+i[o+1]),this._$AH[o]=s}a&&!r&&this.j(e)}j(e){e===K?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,e??``)}},Ct=class extends X{constructor(){super(...arguments),this.type=3}j(e){this.element[this.name]=e===K?void 0:e}},wt=class extends X{constructor(){super(...arguments),this.type=4}j(e){this.element.toggleAttribute(this.name,!!e&&e!==K)}},Tt=class extends X{constructor(e,t,n,r,i){super(e,t,n,r,i),this.type=5}_$AI(e,t=this){if((e=J(this,e,t,0)??K)===G)return;let n=this._$AH,r=e===K&&n!==K||e.capture!==n.capture||e.once!==n.once||e.passive!==n.passive,i=e!==K&&(n===K||r);r&&this.element.removeEventListener(this.name,this,n),i&&this.element.addEventListener(this.name,this,e),this._$AH=e}handleEvent(e){typeof this._$AH==`function`?this._$AH.call(this.options?.host??this.element,e):this._$AH.handleEvent(e)}},Et=class{constructor(e,t,n){this.element=e,this.type=6,this._$AN=void 0,this._$AM=t,this.options=n}get _$AU(){return this._$AM._$AU}_$AI(e){J(this,e)}},Dt=I.litHtmlPolyfillSupport;Dt?.(xt,Y),(I.litHtmlVersions??=[]).push(`3.3.2`);var Ot=(e,t,n)=>{let r=n?.renderBefore??t,i=r._$litPart$;if(i===void 0){let e=n?.renderBefore??null;r._$litPart$=i=new Y(t.insertBefore(B(),e),e,void 0,n??{})}return i._$AI(e),i},Z=globalThis,Q=class extends F{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){let e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=Ot(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return G}};Q._$litElement$=!0,Q.finalized=!0,Z.litElementHydrateSupport?.({LitElement:Q});var kt=Z.litElementPolyfillSupport;kt?.({LitElement:Q}),(Z.litElementVersions??=[]).push(`4.2.2`);function At(e){if(!e.mimeType?.startsWith(`video/`)||!Number.isFinite(e.durationSeconds)||!e.durationSeconds||e.durationSeconds<=0||e.durationSeconds>15)return!1;let t=e.size;return!(typeof t==`number`&&t>12582912)}var jt=640,Mt=8,Nt=72,Pt=16,Ft=704,It=.9,Lt=.85;function $(e){if(!(!Number.isFinite(e)||!e||e<=0))return e}function Rt(){return{width:globalThis.innerWidth||document.documentElement.clientWidth||0,height:globalThis.innerHeight||document.documentElement.clientHeight||0}}function zt(e,t){let n=e<=jt?Mt:Nt;return{width:Math.max(0,e-n*2),height:Math.max(0,t-Pt*2)}}function Bt(e,t,n){let r=$(e?.width),i=$(e?.height);if(!r||!i)return null;let a=zt(t,n);if(a.width<=0||a.height<=0)return null;let o=Math.min(a.width/r,a.height/i);return{width:Math.max(1,Math.round(r*o)),height:Math.max(1,Math.round(i*o))}}function Vt(e,t,n){if(!e||e.mimeType?.startsWith(`video/`)||!$(e.width)||!$(e.height)||t<=0||n<=0)return!1;let r=$(e.width),i=$(e.height);if(!r||!i)return!1;let a=t<=jt,o=zt(t,n),s=o.width,c=o.height;if(s<=0||c<=0)return!1;let l=r/i,u=Math.min(s,c*l);return l<It&&u<(a?s:Math.min(s,Ft))*Lt}var Ht=class extends Q{static properties={_images:{state:!0},_currentIndex:{state:!0},_open:{state:!0},_viewportWidth:{state:!0},_viewportHeight:{state:!0},_videoCurrentTime:{state:!0},_videoDuration:{state:!0},_videoMuted:{state:!0},_imageZoomed:{state:!0}};createRenderRoot(){return this.innerHTML=``,this}constructor(){super();let e=Rt();this._images=[],this._currentIndex=0,this._open=!1,this._viewportWidth=e.width,this._viewportHeight=e.height,this._videoCurrentTime=0,this._videoDuration=0,this._videoMuted=!1,this._imageZoomed=!1}connectedCallback(){super.connectedCallback(),document.addEventListener(`click`,this.#e),window.addEventListener(`resize`,this.#l),this.#u()}disconnectedCallback(){super.disconnectedCallback(),document.removeEventListener(`click`,this.#e),window.removeEventListener(`resize`,this.#l)}open(e,t){this.#u(),this.#d(),this._images=e,this._currentIndex=Math.max(0,Math.min(t,e.length-1)),this.#m(this._images[this._currentIndex]),this._imageZoomed=!1,this._open=!0,document.dispatchEvent(new CustomEvent(g,{detail:{open:!0}})),this.updateComplete.then(()=>{this.querySelector(`.media-lightbox`)?.showModal(),this.#f()})}close(){this.#d(),this.querySelector(`.media-lightbox`)?.close(),this._open=!1,document.dispatchEvent(new CustomEvent(g,{detail:{open:!1}}))}#e=e=>{let t=e.target,n=t.closest(`[data-post-media] a[data-lightbox-index]`);if(n){let t=n.closest(`[data-lightbox-group]`);if(!t)return;e.preventDefault();let r=parseInt(n.dataset.lightboxIndex??`0`,10);try{let e=JSON.parse(t.dataset.lightboxGroup??`[]`);e.length>0&&this.open(e,r)}catch{}return}let r=t.closest(`[data-post-body] img`);if(r){e.preventDefault();let t=r.closest(`[data-post-body]`);if(!t)return;let n=Array.from(t.querySelectorAll(`img`)),i=n.map(e=>({url:e.src,alt:e.alt||``,width:$(e.naturalWidth||Number(e.getAttribute(`width`))),height:$(e.naturalHeight||Number(e.getAttribute(`height`)))})),a=n.indexOf(r);i.length>0&&this.open(i,Math.max(0,a))}};#t(){this._images.length<=1||(this.#d(),this._imageZoomed=!1,this._currentIndex=(this._currentIndex-1+this._images.length)%this._images.length)}#n(){this._images.length<=1||(this.#d(),this._imageZoomed=!1,this._currentIndex=(this._currentIndex+1)%this._images.length)}#r=e=>{let t=this._images[this._currentIndex];Vt(t,this._viewportWidth,this._viewportHeight)&&(e.stopPropagation(),this._imageZoomed=!this._imageZoomed)};#i=e=>{let t=e,n=e.target;if(t.key===`Escape`){e.preventDefault(),this.close();return}if(n instanceof HTMLInputElement||n instanceof HTMLButtonElement||n instanceof HTMLVideoElement)return;if(!this._images[this._currentIndex]?.mimeType?.startsWith(`video/`)){t.key===`ArrowLeft`?(e.preventDefault(),this.#t()):t.key===`ArrowRight`&&(e.preventDefault(),this.#n());return}let r=this.querySelector(`.media-lightbox-video`);r&&this.#a(t,r)};#a(e,t){let n=Number.isFinite(t.duration)&&t.duration>0?t.duration:null,r=e=>{let r=n==null?Math.max(0,e):Math.max(0,Math.min(e,n));t.currentTime=r,this._videoCurrentTime=r},i=e.key,a=i.toLowerCase();if(i===` `||a===`k`)e.preventDefault(),t.paused?t.play().catch(()=>{}):t.pause();else if(i===`ArrowLeft`)e.preventDefault(),r(t.currentTime-5);else if(i===`ArrowRight`)e.preventDefault(),r(t.currentTime+5);else if(i===`Home`)e.preventDefault(),r(0);else if(i===`End`)n!=null&&(e.preventDefault(),r(n));else if(i.length===1&&i>=`0`&&i<=`9`)n!=null&&(e.preventDefault(),r(Number(i)/10*n));else if(i===`ArrowUp`)e.preventDefault(),t.volume=Math.min(1,t.volume+.05);else if(i===`ArrowDown`)e.preventDefault(),t.volume=Math.max(0,t.volume-.05);else if(a===`m`){e.preventDefault();let n=!t.muted;t.muted=n,this._videoMuted=n}else a===`f`&&(e.preventDefault(),this.#o(t))}#o(e){let t=document,n=e;if(document.fullscreenElement??t.webkitFullscreenElement){document.exitFullscreen?document.exitFullscreen().catch(()=>{}):t.webkitExitFullscreen?.();return}e.requestFullscreen?e.requestFullscreen().catch(()=>{}):n.webkitRequestFullscreen?n.webkitRequestFullscreen():n.webkitEnterFullscreen&&n.webkitEnterFullscreen()}#s=e=>{let t=e.target;(t===e.currentTarget||t.classList.contains(`media-lightbox-content`)||t.classList.contains(`media-lightbox-stage`))&&this.close()};#c=()=>{this.#d(),this._open&&document.dispatchEvent(new CustomEvent(g,{detail:{open:!1}})),this._open=!1};#l=()=>{this.#u()};#u(){let e=Rt();e.width===this._viewportWidth&&e.height===this._viewportHeight||(this._viewportWidth=e.width,this._viewportHeight=e.height)}#d(){this.querySelector(`.media-lightbox-video`)?.pause()}#f(){this.querySelector(`.media-lightbox-content`)?.focus()}#p=()=>{this.querySelector(`.media-lightbox-content`)?.focus({preventScroll:!0})};#m(e){this._videoCurrentTime=0,this._videoDuration=e?.durationSeconds&&e.durationSeconds>0?e.durationSeconds:0,this._videoMuted=!1}#h(){let e=this._images[this._currentIndex];if(!At(e)){this.#m(e);return}let t=this.querySelector(`.media-lightbox-video`);t&&(t.currentTime=0,t.muted=this._videoMuted,t.play().catch(()=>{}))}#g=e=>{let t=e.currentTarget;Number.isFinite(t.duration)&&t.duration>0&&(this._videoDuration=t.duration),this._videoCurrentTime=t.currentTime,t.muted=this._videoMuted};#_=e=>{let t=e.currentTarget;this._videoCurrentTime=t.currentTime,Number.isFinite(t.duration)&&t.duration>0&&(this._videoDuration=t.duration)};#v=e=>{let t=e.currentTarget,n=this.querySelector(`.media-lightbox-video`),r=Number.parseFloat(t.value);!n||!Number.isFinite(r)||r<0||(n.currentTime=r,this._videoCurrentTime=r)};#y=()=>{this._videoMuted=!this._videoMuted;let e=this.querySelector(`.media-lightbox-video`);e&&(e.muted=this._videoMuted)};updated(e){if(super.updated(e),!this._open||!e.has(`_currentIndex`)&&!e.has(`_open`)&&!e.has(`_imageZoomed`))return;let t=this.querySelector(`.media-lightbox-stage`);t&&(t.scrollTop=0,t.scrollLeft=0,(e.has(`_currentIndex`)||e.has(`_open`))&&(this.#h(),this.#f()))}render(){if(!this._open)return K;let e=this._images[this._currentIndex],t=this._images.length>1,n=e?.mimeType?.startsWith(`video/`),r=At(e),i=Vt(e,this._viewportWidth,this._viewportHeight),a=i&&this._imageZoomed,o=r?Bt(e,this._viewportWidth,this._viewportHeight):null,s=r&&!!o&&o.height>o.width,c=o?`width:${o.width}px;height:${o.height}px;`:K,l=this._videoDuration>0?this._videoDuration:e?.durationSeconds??1,u=Math.min(this._videoCurrentTime,l),d=l>0?u/l*100:0;return W`\n <dialog\n class=${`media-lightbox${r?` media-lightbox-short`:``}`}\n @keydown=${this.#i}\n @click=${this.#s}\n @close=${this.#c}\n >\n <div class=\"media-lightbox-content\" tabindex=\"-1\">\n <button\n type=\"button\"\n class=\"media-lightbox-close\"\n @click=${()=>this.close()}\n aria-label=\"Close\"\n >\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n </button>\n\n ${t?W`<div class=\"media-lightbox-counter\">\n ${this._currentIndex+1} / ${this._images.length}\n </div>`:K}\n <div\n class=${`media-lightbox-stage${a?` media-lightbox-stage-scroll`:``}`}\n >\n ${n?r?W`<div\n class=${`media-lightbox-short-frame${s?` media-lightbox-short-frame-portrait`:` media-lightbox-short-frame-landscape`}`}\n style=${c}\n >\n <video\n class=\"media-lightbox-video media-lightbox-video-short\"\n src=${e?.url??``}\n poster=${e?.posterUrl??``}\n autoplay\n playsinline\n loop\n ?muted=${this._videoMuted}\n @focus=${this.#p}\n @loadedmetadata=${this.#g}\n @timeupdate=${this.#_}\n ></video>\n <div\n class=${`media-lightbox-short-controls${s?` media-lightbox-short-controls-portrait`:``}`}\n >\n <input\n class=\"media-lightbox-short-progress\"\n type=\"range\"\n min=\"0\"\n max=${l}\n step=\"0.01\"\n .value=${String(u)}\n style=${`--media-progress:${d}%`}\n aria-label=\"Video progress\"\n @input=${this.#v}\n />\n <button\n type=\"button\"\n class=\"media-lightbox-short-mute\"\n @click=${this.#y}\n aria-label=${this._videoMuted?`Unmute video`:`Mute video`}\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 48 48\"\n fill=\"currentColor\"\n color=\"#fff\"\n aria-hidden=\"true\"\n >\n ${this._videoMuted?_t`\n <path d=\"M1.5 13.3c-.8 0-1.5.7-1.5 1.5v18.4c0 .8.7 1.5 1.5 1.5h8.7l12.9 12.9c.9.9 2.5.3 2.5-1v-9.8c0-.4-.2-.8-.4-1.1l-22-22c-.3-.3-.7-.4-1.1-.4h-.6zm46.8 31.4-5.5-5.5C44.9 36.6 48 31.4 48 24c0-11.4-7.2-17.4-7.2-17.4-.6-.6-1.6-.6-2.2 0L37.2 8c-.6.6-.6 1.6 0 2.2 0 0 5.7 5 5.7 13.8 0 5.4-2.1 9.3-3.8 11.6L35.5 32c1.1-1.7 2.3-4.4 2.3-8 0-6.8-4.1-10.3-4.1-10.3-.6-.6-1.6-.6-2.2 0l-1.4 1.4c-.6.6-.6 1.6 0 2.2 0 0 2.6 2 2.6 6.7 0 1.8-.4 3.2-.9 4.3L25.5 22V1.4c0-1.3-1.6-1.9-2.5-1L13.5 10 3.3-.3c-.6-.6-1.5-.6-2.1 0L-.2 1.1c-.6.6-.6 1.5 0 2.1L4 7.6l26.8 26.8 13.9 13.9c.6.6 1.5.6 2.1 0l1.4-1.4c.7-.6.7-1.6.1-2.2z\" />\n `:_t`\n <path d=\"M1.5 13.3c-.8 0-1.5.7-1.5 1.5v18.4c0 .8.7 1.5 1.5 1.5h8.7l12.9 12.9c.9.9 2.5.3 2.5-1V1.4c0-1.3-1.6-1.9-2.5-1L10.2 13.3H1.5z\" />\n <path d=\"M30.1 15.9c-.6-.6-.6-1.6 0-2.2l1.4-1.4c.6-.6 1.6-.6 2.2 0 0 0 4.1 3.5 4.1 11.7s-4.1 11.7-4.1 11.7c-.6.6-1.6.6-2.2 0l-1.4-1.4c-.6-.6-.6-1.6 0-2.2 0 0 2.6-2 2.6-8.1s-2.6-8.1-2.6-8.1z\" />\n <path d=\"M37.2 8c-.6-.6-.6-1.6 0-2.2l1.4-1.4c.6-.6 1.6-.6 2.2 0 0 0 5.7 5.1 5.7 19.6s-5.7 19.6-5.7 19.6c-.6.6-1.6.6-2.2 0L37.2 42c-.6-.6-.6-1.6 0-2.2 0 0 4.3-4.4 4.3-15.8S37.2 8 37.2 8z\" />\n `}\n </svg>\n </button>\n </div>\n </div>`:W`<video\n class=\"media-lightbox-video\"\n src=${e?.url??``}\n poster=${e?.posterUrl??``}\n controls\n autoplay\n playsinline\n @focus=${this.#p}\n ></video>`:W`<img\n class=${`media-lightbox-img${i?` media-lightbox-img-zoomable`:``}${a?` media-lightbox-img-scroll`:``}`}\n src=${e?.url??``}\n alt=${e?.alt??``}\n @click=${this.#r}\n />`}\n </div>\n ${t?W`\n <button\n type=\"button\"\n class=\"media-lightbox-nav media-lightbox-nav-prev\"\n @click=${()=>this.#t()}\n aria-label=\"Previous\"\n >\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m15 18-6-6 6-6\" />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"media-lightbox-nav media-lightbox-nav-next\"\n @click=${()=>this.#n()}\n aria-label=\"Next\"\n >\n <svg\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n </button>\n `:K}\n </div>\n </dialog>\n `}};customElements.define(`jant-media-lightbox`,Ht);";
@@ -3421,9 +3421,10 @@ function createExportService(services, siteConfig, deps = {}) {
3421
3421
  }
3422
3422
  for (const list of repliesByThread.values()) list.sort((a, b) => a.createdAt - b.createdAt);
3423
3423
  const exportFiles = [];
3424
+ const bundleMedia = deps.bundleMedia ?? true;
3424
3425
  for (const root of roots) {
3425
3426
  const slug = slugMap.get(root.id) ?? root.slug;
3426
- const bundleFiles = await buildThreadBundle(root, repliesByThread.get(root.id) ?? [], slug, [...aliasMap.get(root.id) ?? []], buildExportedCollectionEntriesForPost(root.id, collectionEntriesByPost, collectionSlugMap, collectionTitleMap), slugMap, collectionEntriesByPost, collectionSlugMap, collectionTitleMap, rawMediaByPost, siteConfig, deps.storage ?? null);
3427
+ const bundleFiles = await buildThreadBundle(root, repliesByThread.get(root.id) ?? [], slug, [...aliasMap.get(root.id) ?? []], buildExportedCollectionEntriesForPost(root.id, collectionEntriesByPost, collectionSlugMap, collectionTitleMap), slugMap, collectionEntriesByPost, collectionSlugMap, collectionTitleMap, rawMediaByPost, siteConfig, deps.storage ?? null, bundleMedia);
3427
3428
  exportFiles.push(...bundleFiles);
3428
3429
  }
3429
3430
  for (const collection of allCollections) {
@@ -3671,16 +3672,28 @@ function collectionEntriesToRefs(entries) {
3671
3672
  * When the media's provider has a reachable public URL (R2/S3/local
3672
3673
  * proxy configured with a `*_public_url`), `src` points at that absolute
3673
3674
  * URL and no bytes are emitted — the exported site stays small and the
3674
- * media keeps being served from wherever it already lives. Without a
3675
- * public URL the bytes are written to `static/media/{id}.ext` and `src`
3676
- * is the site-relative path.
3677
- */ function buildMediaEmission(media, siteConfig) {
3678
- const publicUrl = getPublicUrlForProvider(media.provider, siteConfig.r2PublicUrl, siteConfig.s3PublicUrl, siteConfig.localPublicUrl);
3679
- const hasPublic = Boolean(publicUrl);
3675
+ * media keeps being served from wherever it already lives.
3676
+ *
3677
+ * Otherwise behavior depends on `bundleMedia`:
3678
+ * - `true` (the `jant site export` archive): bytes are written to
3679
+ * `static/media/{id}.ext` and `src` is the site-relative path, so the
3680
+ * archive is self-contained.
3681
+ * - `false` (GitHub Sync): no bytes are emitted. `src` falls back to the
3682
+ * site's own URL so it stays an absolute, resolvable link — the worker
3683
+ * already serves these objects at `/{storageKey}`. This keeps Sync
3684
+ * from reading and base64-encoding every attachment on every push.
3685
+ *
3686
+ * When `bundleMedia` is false but the site URL is unknown, bundling is
3687
+ * used as a last resort to avoid emitting a broken relative link.
3688
+ */ function buildMediaEmission(media, siteConfig, bundleMedia) {
3689
+ const dedicatedPublicUrl = getPublicUrlForProvider(media.provider, siteConfig.r2PublicUrl, siteConfig.s3PublicUrl, siteConfig.localPublicUrl);
3690
+ const siteFallbackUrl = !bundleMedia && siteConfig.siteUrl.trim() ? siteConfig.siteUrl : void 0;
3691
+ const mediaBaseUrl = dedicatedPublicUrl || siteFallbackUrl;
3692
+ const hasRemoteUrl = Boolean(mediaBaseUrl);
3680
3693
  const ext = extOfFilename(media.filename);
3681
3694
  const localName = `${media.id}${ext}`;
3682
3695
  const localPath = `/media/${localName}`;
3683
- const src = hasPublic ? getMediaUrl(media.storageKey, publicUrl) : localPath;
3696
+ const src = hasRemoteUrl ? getMediaUrl(media.storageKey, mediaBaseUrl) : localPath;
3684
3697
  const entry = {
3685
3698
  id: media.id,
3686
3699
  kind: media.mediaKind,
@@ -3704,13 +3717,13 @@ function collectionEntriesToRefs(entries) {
3704
3717
  if (media.posterKey) {
3705
3718
  const posterExt = extOfStorageKey(media.posterKey);
3706
3719
  const posterLocalName = `${media.id}-poster.${posterExt}`;
3707
- entry.poster = hasPublic ? getMediaUrl(media.posterKey, publicUrl) : `/media/${posterLocalName}`;
3720
+ entry.poster = hasRemoteUrl ? getMediaUrl(media.posterKey, mediaBaseUrl) : `/media/${posterLocalName}`;
3708
3721
  entry.poster_key = media.posterKey;
3709
- if (!hasPublic) inlinePosterPath = `static/media/${posterLocalName}`;
3722
+ if (!hasRemoteUrl) inlinePosterPath = `static/media/${posterLocalName}`;
3710
3723
  }
3711
3724
  return {
3712
3725
  entry,
3713
- inlinePath: hasPublic ? null : `static/media/${localName}`,
3726
+ inlinePath: hasRemoteUrl ? null : `static/media/${localName}`,
3714
3727
  inlinePosterPath
3715
3728
  };
3716
3729
  }
@@ -3731,7 +3744,7 @@ function extOfStorageKey(key) {
3731
3744
  * Build a complete set of ExportFile entries for a single thread bundle:
3732
3745
  * the root `_index.md`, one `index.md` per reply, and resource blobs for
3733
3746
  * attached media when the storage driver can fetch them.
3734
- */ async function buildThreadBundle(root, threadReplies, rootSlug, rootAliases, rootCollectionEntries, slugMap, collectionEntriesByPost, collectionSlugMap, collectionTitleMap, mediaByPost, siteConfig, storage) {
3747
+ */ async function buildThreadBundle(root, threadReplies, rootSlug, rootAliases, rootCollectionEntries, slugMap, collectionEntriesByPost, collectionSlugMap, collectionTitleMap, mediaByPost, siteConfig, storage, bundleMedia) {
3735
3748
  const files = [];
3736
3749
  const aliases = [...rootAliases];
3737
3750
  for (const reply of threadReplies) {
@@ -3739,7 +3752,7 @@ function extOfStorageKey(key) {
3739
3752
  aliases.push(`/${replySlug}/`);
3740
3753
  }
3741
3754
  const rootMedia = mediaByPost.get(root.id) ?? [];
3742
- const rootEmissions = rootMedia.map((m) => buildMediaEmission(m, siteConfig));
3755
+ const rootEmissions = rootMedia.map((m) => buildMediaEmission(m, siteConfig, bundleMedia));
3743
3756
  const rootMediaList = rootEmissions.map((e) => e.entry);
3744
3757
  const rootFrontMatter = {
3745
3758
  id: root.id,
@@ -3787,7 +3800,7 @@ function extOfStorageKey(key) {
3787
3800
  for (const reply of threadReplies) {
3788
3801
  const replySlug = slugMap.get(reply.id) ?? reply.slug;
3789
3802
  const replyMedia = mediaByPost.get(reply.id) ?? [];
3790
- const replyEmissions = replyMedia.map((m) => buildMediaEmission(m, siteConfig));
3803
+ const replyEmissions = replyMedia.map((m) => buildMediaEmission(m, siteConfig, bundleMedia));
3791
3804
  const replyMediaList = replyEmissions.map((e) => e.entry);
3792
3805
  const replyCollectionEntries = buildExportedCollectionEntriesForPost(reply.id, collectionEntriesByPost, collectionSlugMap, collectionTitleMap);
3793
3806
  const replyFrontMatter = {
@@ -1,4 +1,4 @@
1
1
  import "./url-XF0GbKGO.js";
2
- import "./export-O2w3AsZX.js";
3
- import { i as classifyRepoForSync, o as createGitHubSyncService } from "./github-sync-D49RADci.js";
2
+ import "./export-CzuQyg5h.js";
3
+ import { i as classifyRepoForSync, o as createGitHubSyncService } from "./github-sync-Dbrb1DS5.js";
4
4
  export { classifyRepoForSync, createGitHubSyncService };
@@ -1,4 +1,4 @@
1
- import { c as parseMarkdownDocument, r as parseFrontMatter, t as createExportService } from "./export-O2w3AsZX.js";
1
+ import { c as parseMarkdownDocument, r as parseFrontMatter, t as createExportService } from "./export-CzuQyg5h.js";
2
2
  import { r as getInstallationToken } from "./github-app-DeX6Td1O.js";
3
3
  import { r as parseRepoSlug, t as createGitHubClient } from "./github-api-UD4u_7fa.js";
4
4
  //#region src/lib/markdown-to-tiptap.ts
@@ -274,7 +274,10 @@ function createGitHubSyncService(services, siteId, siteConfig, deps = {}) {
274
274
  const config = await loadConfig();
275
275
  if (!config) throw new Error("GitHub Sync is not configured");
276
276
  const { client, owner, repo } = createClient(config);
277
- const exportFiles = await createExportService(services, siteConfig, deps).generateHugoFiles();
277
+ const exportFiles = await createExportService(services, siteConfig, {
278
+ storage: deps.storage,
279
+ bundleMedia: false
280
+ }).generateHugoFiles();
278
281
  const defaultBranch = (await client.getRepo(owner, repo)).default_branch;
279
282
  const now = Math.floor(Date.now() / 1e3);
280
283
  const existingMarkerBeforeInit = await client.getFileContent(owner, repo, JANT_SYNC_MARKER_PATH).catch(() => null);
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { v as url_exports } from "./url-XF0GbKGO.js";
2
- import { A as MAX_MEDIA_ATTACHMENTS, C as toMediaView, D as toPostViews, E as toPostView, F as STATUSES, I as TEXT_ATTACHMENT_CONTENT_FORMATS, M as MEDIA_KINDS, N as NAV_ITEM_TYPES, O as toSearchResultView, P as SORT_ORDERS, S as toArchiveGroupsWithMedia, T as toNavItemViews, b as createMediaContext, h as defaultFeedRenderer, j as MAX_PINNED_POSTS, k as FORMATS, t as createApp, w as toNavItemView, x as toArchiveGroups } from "./app-Ct9c4zYF.js";
3
- import { T as time_exports, a as markdown_exports } from "./export-O2w3AsZX.js";
2
+ import { A as MAX_MEDIA_ATTACHMENTS, C as toMediaView, D as toPostViews, E as toPostView, F as STATUSES, I as TEXT_ATTACHMENT_CONTENT_FORMATS, M as MEDIA_KINDS, N as NAV_ITEM_TYPES, O as toSearchResultView, P as SORT_ORDERS, S as toArchiveGroupsWithMedia, T as toNavItemViews, b as createMediaContext, h as defaultFeedRenderer, j as MAX_PINNED_POSTS, k as FORMATS, t as createApp, w as toNavItemView, x as toArchiveGroups } from "./app-B-wKZB8f.js";
3
+ import { T as time_exports, a as markdown_exports } from "./export-CzuQyg5h.js";
4
4
  import "./env-CoSe-1y4.js";
5
- import "./github-sync-D49RADci.js";
5
+ import "./github-sync-Dbrb1DS5.js";
6
6
  export { FORMATS, MAX_MEDIA_ATTACHMENTS, MAX_PINNED_POSTS, MEDIA_KINDS, NAV_ITEM_TYPES, SORT_ORDERS, STATUSES, TEXT_ATTACHMENT_CONTENT_FORMATS, createApp, createMediaContext, defaultFeedRenderer, markdown_exports as markdown, time_exports as time, toArchiveGroups, toArchiveGroupsWithMedia, toMediaView, toNavItemView, toNavItemViews, toPostView, toPostViews, toSearchResultView, url_exports as url };
package/dist/node.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import "./url-XF0GbKGO.js";
2
- import { B as isAssetPath, L as buildThemeStyle, R as BUILTIN_COLOR_THEMES, _ as sqliteSchemaBundle, a as resolveDatabaseDialect, c as resolveConfig, d as setWebhook, f as BUILTIN_FONT_THEMES, g as pgSchemaBundle, i as createSiteService, l as getWebhookUrl, m as getFontThemeCssVariables, n as createNodeCliRuntime, o as getHostBasedStartupConfigurationIssues, p as getCjkSerifCssVariables, r as createNodeRequestRuntime, s as createStorageDriver, t as createApp, u as setMyCommands, v as createNodeDatabase, y as schema_exports, z as getPublicAssetBasePath } from "./app-Ct9c4zYF.js";
3
- import { t as createExportService } from "./export-O2w3AsZX.js";
2
+ import { B as isAssetPath, L as buildThemeStyle, R as BUILTIN_COLOR_THEMES, _ as sqliteSchemaBundle, a as resolveDatabaseDialect, c as resolveConfig, d as setWebhook, f as BUILTIN_FONT_THEMES, g as pgSchemaBundle, i as createSiteService, l as getWebhookUrl, m as getFontThemeCssVariables, n as createNodeCliRuntime, o as getHostBasedStartupConfigurationIssues, p as getCjkSerifCssVariables, r as createNodeRequestRuntime, s as createStorageDriver, t as createApp, u as setMyCommands, v as createNodeDatabase, y as schema_exports, z as getPublicAssetBasePath } from "./app-B-wKZB8f.js";
3
+ import { t as createExportService } from "./export-CzuQyg5h.js";
4
4
  import { C as shouldTrustProxy, S as getTelegramWebhookSecret, b as getSiteResolutionMode, d as getHostedControlPlaneBaseUrl, i as getConfiguredSingleSitePathPrefix, l as getEnvString, r as getConfiguredSingleSiteOrigin, x as getTelegramBotPool, y as getPort } from "./env-CoSe-1y4.js";
5
- import "./github-sync-D49RADci.js";
5
+ import "./github-sync-Dbrb1DS5.js";
6
6
  import { drizzle } from "drizzle-orm/better-sqlite3";
7
7
  import { serve } from "@hono/node-server";
8
8
  import Database from "better-sqlite3";
@@ -529,7 +529,7 @@ async function createNodeRequestHandler(options) {
529
529
  async function start(env = process.env, app) {
530
530
  const handler = await createNodeRequestHandler({
531
531
  env,
532
- app: async () => app ?? (await import("./app-CUZaVgsC.js")).createApp()
532
+ app: async () => app ?? (await import("./app-qwMcaTML.js")).createApp()
533
533
  });
534
534
  const hostname = resolveHost(env);
535
535
  const port = resolvePort(env);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jant/core",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "A modern, open-source microblogging platform built on Cloudflare Workers",
5
5
  "type": "module",
6
6
  "exports": {