/*
 * li-home-doc — Design Tokens & Layout
 */

/* ── Fonts (all local, no CDN) ── */
/* Source Sans 3 — Latin subset, used for UI (nav, header, chrome) */
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/SourceSans3-Regular.woff2') format('woff2');
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/SourceSans3-Medium.woff2') format('woff2');
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/SourceSans3-Semibold.woff2') format('woff2');
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/SourceSans3-Bold.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/JetBrainsMono-Regular.woff2') format('woff2');
}
/* STIX Two Text — scientific serif, gives body text a LaTeX-like scholarly feel.
   Subset: Latin, Greek, Cyrillic, punctuation, letterlike symbols (607 glyphs).
   unicode-range tells the browser to skip this font for math operators, so
   they fall through to STIX Two Math. */
@font-face {
  font-family: 'STIX Two Text';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/STIXTwoText-Regular.woff2') format('woff2');
  unicode-range: U+0020-007E, U+00A0-017F, U+0370-03FF, U+0400-045F, U+1E00-1EFF, U+2000-206F, U+2070-209F, U+2100-214F, U+2150-218F;
}
@font-face {
  font-family: 'STIX Two Text';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/fonts/STIXTwoText-Medium.woff2') format('woff2');
  unicode-range: U+0020-007E, U+00A0-017F, U+0370-03FF, U+0400-045F, U+1E00-1EFF, U+2000-206F, U+2070-209F, U+2100-214F, U+2150-218F;
}
@font-face {
  font-family: 'STIX Two Text';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/STIXTwoText-SemiBold.woff2') format('woff2');
  unicode-range: U+0020-007E, U+00A0-017F, U+0370-03FF, U+0400-045F, U+1E00-1EFF, U+2000-206F, U+2070-209F, U+2100-214F, U+2150-218F;
}
@font-face {
  font-family: 'STIX Two Text';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/STIXTwoText-Bold.woff2') format('woff2');
  unicode-range: U+0020-007E, U+00A0-017F, U+0370-03FF, U+0400-045F, U+1E00-1EFF, U+2000-206F, U+2070-209F, U+2100-214F, U+2150-218F;
}

/* STIX Two Math — subsetted to Mathematical Operators + Arrows (370 glyphs, 33 KB).
   Matches STIX Two Text for body math; browser only downloads when math is on-page. */
@font-face {
  font-family: 'STIX Two Math';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/STIXTwoMath-Regular.woff2') format('woff2');
  unicode-range: U+2190-21FF, U+2200-22FF;
}
/* Noto Serif SC — TTF (large, cover page only) */
@font-face {
  font-family: 'Noto Serif SC';
  font-style: normal;
  font-weight: 200;
  font-display: swap;
  src: url('/static/fonts/H4cyBXePl9DZ0Xe7gG9cyOj7uK2-n-D2rd4FY7QCqyWv.ttf') format('truetype');
}
@font-face {
  font-family: 'Noto Serif SC';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('/static/fonts/H4cyBXePl9DZ0Xe7gG9cyOj7uK2-n-D2rd4FY7TcqyWv.ttf') format('truetype');
}
@font-face {
  font-family: 'Noto Serif SC';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/H4cyBXePl9DZ0Xe7gG9cyOj7uK2-n-D2rd4FY7SCqyWv.ttf') format('truetype');
}

/* ── Design tokens (light) ── */
:root, [data-theme="light"] {
  --site-default-bg-color: #ffffff;
  --site-default-fg-color: #1f2328;
  --site-default-fg-color--light: #656d76;
  --site-default-fg-color--lighter: #c0c6cc;
  --site-default-fg-color--lightest: #e8eaed;

  --site-primary-fg-color: #2e4a62;
  --site-primary-fg-color--light: #4a7899;
  --site-primary-fg-color--dark: #1a2f40;
  --site-accent-fg-color: #3b82b6;
  --site-accent-fg-color--transparent: #3b82b620;

  --site-code-bg-color: #f0f2f5;
  --site-code-hl-color: #3b82b620;
  --site-code-hl-number-color: #c4713b;
  --site-code-hl-special-color: #3b82b6;
  --site-code-hl-function-color: #7c3aed;
  --site-code-hl-constant-color: #0891b2;
  --site-code-hl-string-color: #059669;
  --site-code-hl-keyword-color: #c4713b;
  --site-code-hl-name-color: #d65d0e;
  --site-code-hl-operator-color: #636c7b;
  --site-code-hl-punctuation-color: #636c7b;
  --site-code-hl-comment-color: #8b949e;
  --site-code-hl-generic-color: #e6edf3;
  --site-code-hl-variable-color: #d65d0e;

  --site-shadow-color: rgba(0,0,0,.06);
  --site-border-color: #d0d7de;
  --site-surface-color: #f6f8fa;
  --site-error-color: #e5484d;
  --site-error-color--transparent: rgba(229,72,77,.12);
}

/* ── Design tokens (dark) ── */
[data-theme="dark"] {
  --site-default-bg-color: #0d1117;
  --site-default-fg-color: #e6edf3;
  --site-default-fg-color--light: #8b949e;
  --site-default-fg-color--lighter: #484f58;
  --site-default-fg-color--lightest: #30363d;

  --site-primary-fg-color: #1e3a5f;
  --site-primary-fg-color--light: #2a5080;
  --site-primary-fg-color--dark: #0f1d30;
  --site-accent-fg-color: #4493f8;
  --site-accent-fg-color--transparent: rgba(68,147,248,.12);

  --site-code-bg-color: #131820;
  --site-code-hl-number-color: #f0a060;
  --site-code-hl-function-color: #a78bfa;
  --site-code-hl-constant-color: #22d3bb;
  --site-code-hl-string-color: #34d399;
  --site-code-hl-keyword-color: #f0a060;
  --site-code-hl-name-color: #fba94c;
  --site-code-hl-operator-color: #9ca3af;
  --site-code-hl-comment-color: #6b7280;
  --site-code-hl-special-color: #38bdf8;
  --site-code-hl-punctuation-color: #9ca3af;
  --site-code-hl-generic-color: #30363d;
  --site-code-hl-variable-color: #fba94c;
  --site-code-hl-color: rgba(68,147,248,.12);

  --site-shadow-color: rgba(0,0,0,.2);
  --site-border-color: #30363d;
  --site-surface-color: #161b22;
  --site-error-color: #e5484d;
  --site-error-color--transparent: rgba(229,72,77,.12);
}

/* ── Design tokens (auto — follows OS preference) ── */
[data-theme="auto"] {
  /* inherits light vars from :root by default */
}
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] {
    --site-default-bg-color: #0d1117;
    --site-default-fg-color: #e6edf3;
    --site-default-fg-color--light: #8b949e;
    --site-default-fg-color--lighter: #484f58;
    --site-default-fg-color--lightest: #30363d;
    --site-primary-fg-color: #1e3a5f;
    --site-primary-fg-color--light: #2a5080;
    --site-primary-fg-color--dark: #0f1d30;
    --site-accent-fg-color: #4493f8;
    --site-accent-fg-color--transparent: rgba(68,147,248,.12);
    --site-code-bg-color: #131820;
    --site-code-hl-number-color: #f0a060;
    --site-code-hl-function-color: #a78bfa;
    --site-code-hl-constant-color: #22d3bb;
    --site-code-hl-string-color: #34d399;
    --site-code-hl-keyword-color: #f0a060;
    --site-code-hl-name-color: #fba94c;
    --site-code-hl-operator-color: #9ca3af;
    --site-code-hl-comment-color: #6b7280;
    --site-code-hl-special-color: #38bdf8;
    --site-code-hl-punctuation-color: #9ca3af;
    --site-code-hl-generic-color: #30363d;
    --site-code-hl-variable-color: #fba94c;
    --site-code-hl-color: rgba(68,147,248,.12);
    --site-shadow-color: rgba(0,0,0,.2);
    --site-border-color: #30363d;
    --site-surface-color: #161b22;
    --site-error-color: #e5484d;
    --site-error-color--transparent: rgba(229,72,77,.12);
  }
}

/* ── Layout dimensions (overridden at breakpoints) ── */
:root {
  --site-sidebar-width: 260px;
  --site-content-max-width: min(110ch, calc(100vw - var(--site-sidebar-width) - 2 * var(--site-content-padding-x)));
  --site-body-font-size: 1.05rem;
  --site-body-line-height: 1.78;

  /* Spacing scale */
  --site-space-1: .25rem;
  --site-space-2: .5rem;
  --site-space-3: .75rem;
  --site-space-4: 1rem;
  --site-space-6: 1.5rem;
  --site-space-8: 2rem;

  --site-content-padding-x: var(--site-space-8);
  --site-content-padding-y: var(--site-space-8);

  /* Radius scale */
  --site-radius-sm: 4px;
  --site-radius: 6px;
  --site-radius-lg: 12px;
  --site-radius-pill: 20px;

  /* Elevation scale (built on --site-shadow-color) */
  --site-shadow-sm: 0 1px 2px var(--site-shadow-color);
  --site-shadow-md: 0 4px 12px var(--site-shadow-color);
  --site-shadow-lg: 0 8px 24px var(--site-shadow-color);
}

/* ── Reset ── */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }

/* Links never reflect visited state. This baseline (lowest specificity, and
   state-agnostic so it applies to :link and :visited alike) removes the UA
   blue/purple; every component sets its own link color, which then applies to
   visited links too — so no per-component :visited rules are needed. */
a { color: inherit; }

/* Skip link */
.skip-link {
  position: absolute;
  top: -100%;
  left: var(--site-space-4);
  z-index: 9999;
  padding: var(--site-space-2) var(--site-space-4);
  background: var(--site-accent-fg-color);
  color: var(--site-default-bg-color);
  border-radius: 0 0 var(--site-radius) var(--site-radius);
  font-size: 0.9rem;
  text-decoration: none;
}
.skip-link:focus {
  top: 0;
}

/* Screen-reader-only utility */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Error pages */
.site-error {
  text-align: center;
  padding-top: 6rem;
}
.site-error__mark {
  fill: currentColor;
  color: var(--site-default-fg-color--lighter);
  margin-bottom: var(--site-space-2);
}
.site-error__code {
  font-size: 4rem;
  margin: 0;
  color: var(--site-default-fg-color--lighter);
}
.site-error__msg {
  font-size: 1.1rem;
  color: var(--site-default-fg-color--light);
  margin: var(--site-space-4) 0 var(--site-space-8);
}
.site-error__link {
  color: var(--site-accent-fg-color);
  text-decoration: none;
  font-weight: 500;
}

html {
  background-color: var(--site-default-bg-color);
}

/* Content links — idempotent, no visited color change */
.site-content a { color: var(--site-accent-fg-color); }

/* Reading progress bar */
.site-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  background: var(--site-accent-fg-color);
  z-index: 999;
  width: 0;
  transition: width .15s linear;
}

body {
  margin: 0;
  font-family: "Source Sans 3", "Noto Sans SC", system-ui, -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  color: var(--site-default-fg-color);
  background: var(--site-default-bg-color);
  min-height: 100vh;
  transition: background-color .3s, color .3s;
  display: flex;
  flex-direction: column;
  scrollbar-gutter: stable;
}

/* ── Header ── */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background-color: var(--site-surface-color);
  border-bottom: 1px solid var(--site-default-fg-color--lightest);
  transition: background-color .25s, border-color .25s;
}
.site-header__inner {
  display: flex;
  align-items: center;
  max-width: 100%;
  padding: 0 1.2rem;
  height: 3rem;
  gap: .6rem;
}
.site-logo {
  display: inline-flex;
  align-items: center;
  gap: var(--site-space-2);
  text-decoration: none;
  color: var(--site-default-fg-color);
  font-weight: 600;
  font-size: .95rem;
  letter-spacing: .01em;
  cursor: pointer;
}
.site-logo svg { fill: currentColor; }
.site-header__title {
  font-weight: 500;
  letter-spacing: .03em;
}
.site-header__spacer { flex: 1; }

/* ── Search ── */
.site-search { position: relative; }
.site-search__toggle {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--site-default-fg-color);
  padding: .4rem;
  border-radius: 50%;
  transition: background-color .2s, color .2s;
  touch-action: manipulation;
}
.site-search__toggle svg { fill: currentColor; }
.site-search__toggle:hover { color: var(--site-accent-fg-color); background: var(--site-default-fg-color--lightest); }

/* Dropdown — anchored to button, slides toward bottom-left */
.site-search__dropdown {
  position: absolute;
  top: calc(100% + var(--site-space-2));
  right: 0;
  width: min(480px, calc(100vw - 2rem));
  max-height: 60vh;
  background: var(--site-default-bg-color);
  border: 1px solid var(--site-border-color);
  border-radius: var(--site-radius-lg);
  box-shadow: var(--site-shadow-lg);
  z-index: 200;
  opacity: 0;
  pointer-events: none;
  transform: scale(0.95) translateY(-4px);
  transform-origin: top right;
  transition:
    opacity .15s ease,
    transform .25s cubic-bezier(.34,1.56,.64,1);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.site-search__dropdown--open {
  opacity: 1;
  pointer-events: auto;
  transform: scale(1) translateY(0);
}
.site-search__input {
  width: 100%;
  padding: var(--site-space-4) 1.2rem;
  border: none;
  border-bottom: 2px solid transparent;
  background: var(--site-default-fg-color--lightest);
  border-radius: var(--site-radius-lg) var(--site-radius-lg) 0 0;
  font-size: .95rem;
  color: var(--site-default-fg-color);
  outline: none;
  flex-shrink: 0;
  transition: border-color .25s ease, background-color .25s ease, box-shadow .25s ease;
}
.site-search__input:focus {
  border-bottom-color: var(--site-accent-fg-color);
  background: var(--site-default-bg-color);
  box-shadow: none;
}
.site-search__input::placeholder { color: var(--site-default-fg-color--lighter); }

.site-search__results {
  flex: 1;
  overflow-y: auto;
  padding: var(--site-space-2);
}
.site-search__results:empty::after {
  content: "输入关键词搜索…";
  display: block;
  text-align: center;
  padding: var(--site-space-8) var(--site-space-4);
  color: var(--site-default-fg-color--lighter);
  font-size: .85rem;
}
.site-search__results--loading {
  opacity: .5;
  transition: opacity .15s;
}
.site-search__empty {
  text-align: center;
  padding: var(--site-space-8) var(--site-space-4);
  color: var(--site-default-fg-color--light);
  font-size: .85rem;
}

/* Search spinner */
.site-search__spinner {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--site-space-8);
}
.site-search__spinner-dot {
  width: 24px;
  height: 24px;
  border: 3px solid var(--site-default-fg-color--lightest);
  border-top-color: var(--site-accent-fg-color);
  border-radius: 50%;
  animation: search-spin 0.6s linear infinite;
}
@keyframes search-spin {
  to { transform: rotate(360deg); }
}

/* Result card */
.site-search__result {
  display: block;
  padding: .7rem .8rem;
  text-decoration: none;
  color: var(--site-default-fg-color);
  border-radius: var(--site-radius);
  transition: background-color .15s;
  animation: searchResultIn .3s cubic-bezier(.25,1,.5,1) both;
  animation-delay: calc(var(--i, 0) * 35ms);
}
@keyframes searchResultIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}
.site-search__result:hover,
.site-search__result--active { background: var(--site-accent-fg-color--transparent); }
.site-search__result + .site-search__result { margin-top: 2px; }

.site-search__result-title {
  font-size: .9rem;
  font-weight: 600;
  line-height: 1.4;
  margin-bottom: .2rem;
}
.site-search__result-excerpt {
  font-size: .8rem;
  line-height: 1.5;
  color: var(--site-default-fg-color--light);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.site-search__result mark {
  background: var(--site-accent-fg-color--transparent);
  color: var(--site-accent-fg-color);
  font-weight: 500;
  border-radius: var(--site-radius-sm);
  padding: 0 1px;
}
.site-search__result-footer {
  display: flex;
  gap: .8rem;
  margin-top: var(--site-space-1);
  font-size: .72rem;
  color: var(--site-default-fg-color--lighter);
}
.site-search__result-area {
  text-transform: capitalize;
}
/* Keyboard hint in empty state */
.site-search__kbd {
  display: inline-block;
  padding: 1px 6px;
  font-size: .72rem;
  border: 1px solid var(--site-border-color);
  border-radius: var(--site-radius-sm);
  background: var(--site-default-fg-color--lightest);
  font-family: inherit;
}

/* Search facet chips + result count */
.site-search__facets {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  padding: var(--site-space-2) var(--site-space-2) 0;
}
.site-search__facet {
  font: inherit;
  font-size: .72rem;
  padding: .15rem .55rem;
  border-radius: var(--site-radius-pill);
  border: 1px solid var(--site-border-color);
  background: transparent;
  color: var(--site-default-fg-color--light);
  cursor: pointer;
  text-transform: capitalize;
  transition: background-color .15s, color .15s, border-color .15s;
}
.site-search__facet:hover {
  color: var(--site-accent-fg-color);
  border-color: var(--site-accent-fg-color);
}
.site-search__facet--active {
  background: var(--site-accent-fg-color);
  color: var(--site-default-bg-color);
  border-color: var(--site-accent-fg-color);
}
.site-search__count {
  padding: .4rem .6rem .2rem;
  font-size: .72rem;
  color: var(--site-default-fg-color--lighter);
}

/* Theme toggle */
.theme-toggle {
  background: none;
  border: none;
  cursor: pointer;
  padding: .4rem;
  border-radius: 50%;
  color: var(--site-default-fg-color);
  position: relative;
  transition: background-color .2s, color .2s;
  touch-action: manipulation;
}
.theme-toggle:hover { color: var(--site-accent-fg-color); background: var(--site-default-fg-color--lightest); }
.theme-toggle__icon {
  display: inline-flex;
  transition: opacity .25s, transform .4s cubic-bezier(.34,1.56,.64,1);
}
[data-theme="light"] .theme-toggle__icon--dark,
[data-theme="dark"] .theme-toggle__icon--light {
  position: absolute;
  inset: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
}
.theme-toggle svg {
  fill: currentColor;
  transition: transform .4s cubic-bezier(.34, 1.56, .64, 1);
}
.theme-toggle:hover svg { transform: rotate(30deg) scale(1.08); }
.theme-toggle--spin svg { transform: rotate(360deg); }

/* Hamburger */
.site-header__hamburger {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--site-default-fg-color);
  padding: .4rem;
  touch-action: manipulation;
}

/* ── Layout ── */
.site-main__wrapper {
  display: flex;
  flex: 1;
  /* No justify-content here: the sidebar is fixed-width and .site-main is
     flex:1, so the two always fill 100% — centering is a steady-state no-op.
     Its only visible effect was during streaming: the large sidebar paints
     before <main> is parsed, and with a lone flex child `center` hangs the
     nav in the middle of the viewport for a frame before <main> snaps it back
     left ("目录居中一闪而过"). Left-start (default) keeps it pinned throughout. */
  min-height: calc(100vh - 3rem);
}

/* Sidebar */
.site-sidebar {
  width: var(--site-sidebar-width);
  flex-shrink: 0;
  border-right: 1px solid var(--site-default-fg-color--lightest);
  background: var(--site-surface-color);
  height: calc(100vh - 3rem);
  position: sticky;
  top: 3rem;
  overflow-y: auto;
  scrollbar-gutter: stable;
}
.site-sidebar__scroll {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  padding: var(--site-space-4) .8rem;
  box-sizing: border-box;
}

/* Backdrop overlay (mobile sidebar) — created by JS */
.site-sidebar__backdrop {
  visibility: hidden;
  position: fixed;
  inset: 0;
  top: 3rem;
  z-index: 49;
  background: rgba(0, 0, 0, 0.35);
  opacity: 0;
  transition: opacity 0.25s ease;
  pointer-events: none;
}
.site-sidebar__backdrop--visible {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
}

/* Sidebar utility entry (e.g. 标签) */
.site-nav__utility {
  display: flex;
  align-items: center;
  gap: var(--site-space-2);
  padding: .6rem .6rem;
  margin-top: auto;
  font-size: .875rem;
  font-weight: 500;
  color: var(--site-default-fg-color--light);
  text-decoration: none;
  border-radius: var(--site-radius);
  border-top: 1px solid var(--site-default-fg-color--lightest);
  transition: color .15s, background-color .15s;
  flex-shrink: 0;
}
.site-nav__utility:hover {
  color: var(--site-accent-fg-color);
  background: var(--site-default-fg-color--lightest);
}
.site-nav__utility svg { opacity: .7; flex-shrink: 0; }
.site-nav__utility--active {
  color: var(--site-accent-fg-color);
  background: var(--site-accent-fg-color--transparent);
}

/* ── Sidebar Navigation ── */

.site-nav summary {
  display: flex;
  align-items: center;
  list-style: none;
  outline: none;
  user-select: none;
}
.site-nav summary::-webkit-details-marker,
.site-nav summary::marker { display: none; content: none; }

/* Chevron — right-pointing, rotates down when open */
.site-nav__chevron {
  flex-shrink: 0;
  opacity: .5;
  margin-right: 2px;
  transition: transform .25s cubic-bezier(.4,0,.2,1), opacity .2s;
}
details[open] > summary > .site-nav__chevron {
  transform: rotate(90deg);
  opacity: .7;
}

/* Page links */
.site-nav__link {
  display: block;
  padding: .28rem .6rem .28rem 1.7rem;
  font-size: .875rem;
  line-height: 1.5;
  color: var(--site-default-fg-color--light);
  text-decoration: none;
  border-left: 2px solid transparent;
  border-radius: 0 var(--site-radius-sm) var(--site-radius-sm) 0;
  transition: color .15s, border-color .15s, background-color .15s;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.site-nav__link:hover {
  color: var(--site-default-fg-color);
  background: var(--site-default-fg-color--lightest);
}
.site-nav__link--active {
  color: var(--site-accent-fg-color);
  border-left-color: var(--site-accent-fg-color);
  font-weight: 500;
  background: var(--site-accent-fg-color--transparent);
  border-radius: 0 var(--site-radius-sm) var(--site-radius-sm) 0;
  animation: navActiveIn .45s .12s cubic-bezier(.25,1,.5,1) both;
}
@keyframes navActiveIn {
  from {
    border-left-color: transparent;
    background: transparent;
    color: var(--site-default-fg-color--light);
  }
  to {
    border-left-color: var(--site-accent-fg-color);
    background: var(--site-accent-fg-color--transparent);
    color: var(--site-accent-fg-color);
  }
}

/* Area group */
.site-nav__area + .site-nav__area { margin-top: 2px; }
.site-nav__area-title {
  font-weight: 600;
  font-size: .875rem;
  color: var(--site-default-fg-color--light);
  padding: .35rem .6rem;
  border-radius: 0 var(--site-radius-sm) var(--site-radius-sm) 0;
  cursor: pointer;
  transition: color .15s, background-color .15s;
}
.site-nav__area-title:hover {
  color: var(--site-default-fg-color);
  background: var(--site-default-fg-color--lightest);
}
.site-nav__area--active > .site-nav__area-title,
.site-nav__section--active > .site-nav__section-title {
  color: var(--site-default-fg-color);
}
.site-nav__area-body {
  overflow: hidden;
  height: 0;
  transition: height .3s ease;
  will-change: height;
}
.site-nav__area[open] > .site-nav__area-body { height: auto; }

/* Section (sub-group) */
.site-nav__section + .site-nav__section { margin-top: 0; }
.site-nav__section-title {
  font-size: .825rem;
  font-weight: 500;
  padding: .22rem .6rem .22rem var(--site-space-4);
  border-radius: 0 var(--site-radius-sm) var(--site-radius-sm) 0;
  cursor: pointer;
  color: var(--site-default-fg-color--light);
  transition: color .15s, background-color .15s;
}
.site-nav__section-title:hover {
  color: var(--site-default-fg-color);
  background: var(--site-default-fg-color--lightest);
}
.site-nav__section-body {
  overflow: hidden;
  height: 0;
  transition: height .25s ease;
  will-change: height;
}
.site-nav__section[open] > .site-nav__section-body { height: auto; }

/* Main content */
.site-main {
  flex: 1;
  min-width: 0;
  padding: 0;
}

/* ── Content arrival + client-side (PJAX) navigation transition ──
   Every non-cover page settles in with a gentle fade+rise, both on a full load
   and after a client-side navigation (the swapped-in <main> is a fresh element,
   so this animation replays automatically). Cover pages run their own bespoke
   arrival sequence, so they opt out. */
@keyframes site-main-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.site-main:not(.site-main--cover) {
  animation: site-main-in .4s cubic-bezier(.25, 1, .5, 1) both;
}
/* Outgoing page fades before it is replaced. `animation: none` is required to
   drop the entrance animation's forwards-fill (which would otherwise pin
   opacity:1 and defeat the fade). */
.site-main--pjax-out {
  animation: none !important;
  opacity: 0;
  transition: opacity .13s ease;
}
.site-main--cover {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: calc(100vh - 3rem);
  overflow: hidden;
}
.site-main--cover .site-content {
  max-width: none;
  margin: 0;
  padding: var(--site-space-8);
}
.site-content-row {
  /* default: let content center itself via margin auto */
}
.site-content {
  max-width: var(--site-content-max-width);
  margin: 0 auto;
  padding: var(--site-content-padding-y) var(--site-content-padding-x) 4rem;
}

.site-breadcrumbs {
  font-size: .8rem;
  color: var(--site-default-fg-color--light);
  margin-bottom: var(--site-space-4);
}

/* Right-side TOC — sticky alongside content on wide screens */
.site-toc {
  display: none;
}
@media (min-width: 1025px) {
  .site-content-row {
    display: flex;
    gap: var(--site-space-8);
    justify-content: center;
  }
  .site-content-row > .site-content {
    flex: 0 1 var(--site-content-max-width);
    min-width: 0;
    margin: 0;
  }
  .site-toc {
    display: block;
    width: 180px;
    flex-shrink: 0;
    align-self: flex-start;
    position: sticky;
    top: 3.5rem;
    max-height: calc(100vh - 3.5rem);
    overflow-y: auto;
    scroll-behavior: smooth;
    padding-top: var(--site-content-padding-y);
  }
}
@media (min-width: 1200px) {
  .site-content-row { gap: 3rem; }
  .site-toc { width: 200px; }
}
.site-toc__inner {
  position: relative;
  border-left: 1px solid var(--site-default-fg-color--lightest);
  padding-left: .8rem;
}
.site-toc__slider {
  position: absolute;
  left: -1px;
  width: 2px;
  background: var(--site-accent-fg-color);
  border-radius: var(--site-radius-pill);
  top: 0;
  height: 0;
  opacity: 0;
  will-change: top, height, opacity;
}
.site-toc__heading {
  font-size: .7rem;
  font-weight: 600;
  color: var(--site-default-fg-color--light);
  opacity: .5;
  margin-bottom: .35rem;
}
.site-toc__link {
  display: block;
  padding: .18rem 0 .18rem calc(0.8rem + 2px);
  margin-left: calc(-0.8rem - 1px);
  font-size: .8125rem;
  line-height: 1.45;
  color: var(--site-default-fg-color--light);
  text-decoration: none;
  border-left: 2px solid transparent;
  transition: color .15s, border-color .2s;
}
.site-toc__link:hover {
  color: var(--site-default-fg-color);
}
.site-toc__link--active {
  color: var(--site-accent-fg-color);
  font-weight: 500;
}
.site-toc__link[data-level="3"] { padding-left: calc(0.8rem + 2px + 0.6rem); }
.site-toc__link[data-level="4"] { padding-left: calc(0.8rem + 2px + 1.2rem); }
.site-toc__link[data-level="5"] { padding-left: calc(0.8rem + 2px + 1.8rem); }

/* Collapsible in-page TOC — narrow screens where the sticky aside is hidden */
.site-toc-mobile {
  display: none;
  margin: 0 0 var(--site-space-6);
  border: 1px solid var(--site-default-fg-color--lightest);
  border-radius: var(--site-radius);
  background: var(--site-surface-color);
  font-size: .85rem;
}
.site-toc-mobile > summary {
  padding: .55rem .9rem;
  cursor: pointer;
  font-weight: 600;
  color: var(--site-default-fg-color--light);
  user-select: none;
  list-style: none;
  display: flex;
  align-items: center;
  gap: .4rem;
}
.site-toc-mobile > summary::-webkit-details-marker { display: none; }
.site-toc-mobile > summary::before {
  content: "";
  width: .45em;
  height: .45em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform .2s;
  flex-shrink: 0;
}
.site-toc-mobile[open] > summary::before { transform: rotate(45deg); }
.site-toc-mobile__nav {
  padding: .2rem .9rem .7rem;
  border-top: 1px solid var(--site-default-fg-color--lightest);
}
.site-toc-mobile__nav a {
  display: block;
  padding: .28rem 0;
  color: var(--site-default-fg-color--light);
  text-decoration: none;
  line-height: 1.45;
}
.site-toc-mobile__nav a:active,
.site-toc-mobile__nav a:hover { color: var(--site-accent-fg-color); }
.site-toc-mobile__nav a[data-level="3"] { padding-left: .9rem; }
.site-toc-mobile__nav a[data-level="4"] { padding-left: 1.8rem; }
.site-toc-mobile__nav a[data-level="5"] { padding-left: 2.7rem; }
@media (max-width: 1024px) {
  .site-toc-mobile { display: block; }
}

/* Tags */
.site-tags {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin-bottom: var(--site-space-6);
}
.site-tag {
  display: inline-block;
  padding: .15rem .6rem;
  font-size: .78rem;
  font-weight: 500;
  color: var(--site-accent-fg-color);
  background: var(--site-accent-fg-color--transparent);
  border-radius: var(--site-radius-pill);
  text-decoration: none;
  transition: background-color .15s, color .15s, transform .2s cubic-bezier(.34,1.56,.64,1);
}
.site-tag:hover {
  background: var(--site-accent-fg-color);
  color: var(--site-default-bg-color);
  transform: scale(1.05);
}

.site-body {
  /* STIX Two Text covers Latin/Greek via unicode-range; CJK chars skip it and
     land on the system CJK sans. STIX Two Math fills arrows/operators (U+2190-22FF). */
  font-family: "STIX Two Text", "STIX Two Math", "Noto Sans SC", system-ui, sans-serif;
  font-size: var(--site-body-font-size);
  line-height: var(--site-body-line-height);
}

/* Code */
.site-content pre {
  background: var(--site-code-bg-color);
  border: 1px solid var(--site-border-color);
  border-radius: var(--site-radius-lg);
  padding: 1.2rem 1.2rem 1.2rem 1.5rem;
  margin: var(--site-space-6) 0;
  overflow-x: auto;
  font-family: "JetBrains Mono", "Fira Code", monospace;
  font-size: .9em;
  line-height: 1.7;
  box-shadow: var(--site-shadow-sm);
}

/* Language label */
.syn-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: -1.2rem -1.2rem .8rem -1.5rem;
  padding: .35rem 1.2rem .35rem 1.5rem;
  background: var(--site-default-fg-color--lightest);
  border-bottom: 1px solid var(--site-border-color);
  border-radius: var(--site-radius-lg) var(--site-radius-lg) 0 0;
  font-family: "Source Sans 3", "Noto Sans SC", system-ui, sans-serif;
  font-size: .72rem;
  font-weight: 500;
  color: var(--site-default-fg-color--light);
  text-transform: uppercase;
  letter-spacing: .05em;
  user-select: none;
}
.syn-header ~ .site-copy-btn { top: 2.2rem; }

.site-content code {
  font-family: "JetBrains Mono", "Fira Code", monospace;
  font-size: .92em;
}

/* Inline code */
.site-content :not(pre) > code {
  background: var(--site-code-bg-color);
  border: 1px solid var(--site-border-color);
  border-radius: var(--site-radius-sm);
  padding: .1em .35em;
  font-size: .88em;
}

/* Syntax highlighting (syntect classed output with SpacedPrefixed prefix="syn") */
.site-content .syncomment { color: var(--site-code-hl-comment-color); font-style: italic; }
.site-content .synstring { color: var(--site-code-hl-string-color); }
.site-content .synconstant.synnumeric { color: var(--site-code-hl-number-color); }
.site-content .synconstant.synlanguage,
.site-content .synvariable.synlanguage { color: var(--site-code-hl-constant-color); }
.site-content .synkeyword,
.site-content .synstorage { color: var(--site-code-hl-keyword-color); }
.site-content .synentity.synname.synfunction,
.site-content .synsupport.synfunction { color: var(--site-code-hl-function-color); }
.site-content .synconstant { color: var(--site-code-hl-constant-color); }
.site-content .syntype { color: var(--site-code-hl-name-color); }
.site-content .synvariable { color: var(--site-code-hl-variable-color); }
.site-content .synkeyword.synoperator { color: var(--site-code-hl-operator-color); }
.site-content .synpunctuation { color: var(--site-code-hl-punctuation-color); }
.site-content .synmeta { color: var(--site-code-hl-special-color); }
.site-content .synentity { color: var(--site-code-hl-name-color); }
.site-content .synsupport { color: var(--site-code-hl-function-color); }
.site-content .synmarkup { color: var(--site-code-hl-string-color); }
.site-content .synmarkup.synheading { color: var(--site-code-hl-keyword-color); font-weight: 600; }
.site-content .synmarkup.synbold { font-weight: 700; }
.site-content .synmarkup.synitalic { font-style: italic; }
.site-content .synmarkup.syninserted { color: var(--site-code-hl-string-color); }
.site-content .synmarkup.syndeleted { color: var(--site-code-hl-name-color); text-decoration: line-through; }
.site-content .synmarkup.synchanged { color: var(--site-code-hl-function-color); }
.site-content .syninvalid { color: var(--site-code-hl-name-color); text-decoration: underline; }
.site-content .syn-highlight { background: transparent; }

/* Tables */
.site-content table {
  width: 100%;
  border-collapse: collapse;
  font-size: .9em;
  border-radius: var(--site-radius);
  overflow: hidden;
  box-shadow: var(--site-shadow-sm);
}
.site-content th, .site-content td {
  padding: var(--site-space-2) .8rem;
  border: 1px solid var(--site-border-color);
  text-align: left;
}
.site-content th { background: var(--site-default-fg-color--lightest); font-weight: 600; }

/* Blockquote */
.site-content blockquote {
  border-left: .2rem solid var(--site-accent-fg-color);
  border-radius: 0 var(--site-radius) var(--site-radius) 0;
  background: var(--site-accent-fg-color--transparent);
  padding: .6rem var(--site-space-4);
  margin: var(--site-space-4) 0;
}

/* Admonitions / callouts (GitHub `> [!NOTE]` alerts, classed by pulldown-cmark) */
.site-content blockquote[class*="markdown-alert"] {
  --alert: var(--site-accent-fg-color);
  border-left: 4px solid var(--alert);
  border-radius: 0 var(--site-radius) var(--site-radius) 0;
  background: color-mix(in srgb, var(--alert) 8%, transparent);
  padding: .8rem var(--site-space-4);
  margin: 1.2rem 0;
}
.site-content blockquote[class*="markdown-alert"] > :first-child { margin-top: 0; }
.site-content blockquote[class*="markdown-alert"] > :last-child { margin-bottom: 0; }
.site-content blockquote[class*="markdown-alert"]::before {
  display: block;
  font-weight: 600;
  font-size: .85em;
  letter-spacing: .02em;
  color: var(--alert);
  margin-bottom: .4rem;
}
.site-content .markdown-alert-note { --alert: var(--site-accent-fg-color); }
.site-content .markdown-alert-note::before { content: "📌 笔记"; }
.site-content .markdown-alert-tip { --alert: var(--site-code-hl-string-color); }
.site-content .markdown-alert-tip::before { content: "💡 提示"; }
.site-content .markdown-alert-important { --alert: var(--site-code-hl-function-color); }
.site-content .markdown-alert-important::before { content: "❗ 重要"; }
.site-content .markdown-alert-warning { --alert: var(--site-code-hl-number-color); }
.site-content .markdown-alert-warning::before { content: "⚠️ 警告"; }
.site-content .markdown-alert-caution { --alert: var(--site-code-hl-name-color); }
.site-content .markdown-alert-caution::before { content: "🛑 注意"; }

/* Math (MathML rendered server-side).
   STIX Two Math covers mathematical operators; unicode-range keeps it from
   interfering with body text. */
.site-content math {
  font-family: "STIX Two Math", "STIX Two Text", "Noto Serif SC", Georgia, serif;
  font-size: 1.05em;
}
.site-content math[display="block"] {
  display: block;
  overflow-x: auto;
  overflow-y: hidden;
  text-align: center;
  margin: 1.4rem 0;
  padding-bottom: .2rem;
}

/* Mermaid diagrams: fit the content column, aligned like code blocks.
   Wide diagrams scroll inside the box rather than breaking the layout.
   Click/tap toggles a full-size scrollable view (set up in mermaid.js). */
pre.mermaid,
.mermaid {
  background: var(--site-code-bg-color);
  border: 1px solid var(--site-border-color);
  border-radius: var(--site-radius-lg);
  padding: var(--site-space-4);
  margin: var(--site-space-6) 0;
  overflow-x: auto;
  text-align: center;
}
/* Hide raw DSL text while CDN script loads; revealed when SVG is ready */
.mermaid:not([data-ready]) {
  color: transparent;
  user-select: none;
}
.mermaid svg { max-width: 100%; height: auto; }
/* Hide the scrollbar on the diagram box (still scrollable/pannable) */
.mermaid { scrollbar-width: none; }
.mermaid::-webkit-scrollbar { width: 0; height: 0; }

/* Mouse devices: wheel-zoom + drag-pan (transform-based, clipped to the box) */
.mermaid--pan { overflow: hidden; cursor: grab; text-align: left; }
.mermaid--pan.is-grabbing { cursor: grabbing; }
.mermaid--pan svg { display: block; margin: 0 auto; }

/* Touch devices: tap to toggle a full-size scrollable view */
.mermaid--zoom { cursor: zoom-in; }
.mermaid--zoomed {
  overflow: auto;
  max-height: 85vh;
  text-align: left;
  cursor: zoom-out;
}
.mermaid--zoomed svg { max-width: none; }

/* SVG image wrapper — gives the expand button a positioning context.
   Mirrors .mermaid for the expand-button UX on inline SVG <img> tags. */
.img-svg {
  position: relative;
  display: block;
  margin: var(--site-space-6) 0;
}
.img-svg svg {
  display: block;
  max-width: 100%;
  height: auto;
}

/* Mermaid expand button — appears in the top-right corner of each diagram.
   On mouse devices it rests at 0 opacity until hover/focus.
   On touch devices it is always visible at reduced opacity. */
.mermaid {
  position: relative;
}
.mermaid__expand {
  position: absolute;
  top: .65rem;
  right: .65rem;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 1px solid var(--site-border-color);
  border-radius: var(--site-radius);
  background: var(--site-default-bg-color);
  color: var(--site-default-fg-color--light);
  cursor: pointer;
  transition: opacity .2s, color .2s, background .2s;
}

/* Mouse: hidden until hover/focus */
@media (hover: hover) {
  .mermaid__expand { opacity: 0; }
  .mermaid:hover .mermaid__expand,
  .img-svg:hover .mermaid__expand,
  .mermaid__expand:focus-visible {
    opacity: 1;
  }
}

/* Touch: always visible, but small and tucked into the corner so it reads
   as a floating control instead of colliding with wide diagrams */
@media (hover: none) {
  .mermaid__expand {
    opacity: 0.7;
    top: .45rem;
    right: .45rem;
    width: 1.75rem;
    height: 1.75rem;
  }
  .mermaid__expand svg { width: .95rem; height: .95rem; }
}
.mermaid__expand:hover,
.mermaid__expand:focus-visible {
  color: var(--site-accent-fg-color);
  background: var(--site-default-fg-color--lightest);
}
.mermaid__expand svg {
  width: 1.1rem;
  height: 1.1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Lightbox: full-screen overlay for diagram inspection. Scales up from
   semi-transparent with a gentle transition. */
.mermaid-lightbox {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(0,0,0,0);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background .25s ease;
  cursor: default;
}
.mermaid-lightbox.is-open {
  background: rgba(0,0,0,.7);
}
.mermaid-lightbox__stage {
  width: 92vw;
  height: 90vh;
  overflow: hidden;
  cursor: grab;
  border-radius: var(--site-radius-lg);
  background: var(--site-default-bg-color);
  padding: var(--site-space-6);
  transform: scale(.92) translateY(16px);
  opacity: 0;
  transition: transform .3s cubic-bezier(.34,1.56,.64,1), opacity .2s ease;
}
[data-theme="dark"] .mermaid-lightbox__stage {
  background: var(--site-code-bg-color);
}
.mermaid-lightbox.is-open .mermaid-lightbox__stage {
  transform: scale(1) translateY(0);
  opacity: 1;
}
.mermaid-lightbox__stage.is-grabbing {
  cursor: grabbing;
}
.mermaid-lightbox__stage svg {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transform-origin: 0 0;
}
/* Sits on top of the (nearly full-screen, light) stage panel, so it must be
   styled for the panel, not the dark backdrop: solid themed surface with a
   border and shadow reads in both themes. */
.mermaid-lightbox__close {
  position: absolute;
  top: 1.25rem;
  right: 1.25rem;
  z-index: 2;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--site-border-color);
  border-radius: var(--site-radius);
  background: var(--site-default-bg-color);
  color: var(--site-default-fg-color);
  box-shadow: 0 2px 10px var(--site-shadow-color);
  cursor: pointer;
  transition: background .2s, color .2s;
}
.mermaid-lightbox__close:hover {
  color: var(--site-accent-fg-color);
  background: var(--site-default-fg-color--lightest);
}
/* Touch: ensure minimum 44px tap target */
@media (hover: none) {
  .mermaid-lightbox__close {
    width: 44px;
    height: 44px;
    top: var(--site-space-4);
    right: var(--site-space-4);
  }
  .site-copy-btn {
    width: 44px;
    height: 44px;
    opacity: 0.7;
  }
}
.mermaid-lightbox__close svg {
  width: 1.25rem;
  height: 1.25rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Focus-visible ring for keyboard navigation */
.site-search__toggle:focus-visible,
.theme-toggle:focus-visible,
.site-header__hamburger:focus-visible,
.site-nav__link:focus-visible,
.site-nav__area-title:focus-visible,
.site-nav__section-title:focus-visible,
.site-nav__utility:focus-visible,
.site-copy-btn:focus-visible,
.mermaid__expand:focus-visible,
.mermaid-lightbox__close:focus-visible,
.site-back-to-top:focus-visible,
.site-pager__link:focus-visible,
.site-search__result:focus-visible,
.skip-link:focus-visible {
  outline: 2px solid var(--site-accent-fg-color);
  outline-offset: 2px;
  border-radius: var(--site-radius-sm);
}
/* Scrollbar */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: var(--site-default-fg-color--lightest); border-radius: var(--site-radius); }
::-webkit-scrollbar-thumb:hover { background: var(--site-default-fg-color--lighter); }
html {
  scrollbar-color: var(--site-default-fg-color--lightest) transparent;
  scrollbar-width: thin;
}

/* Back to top */
.site-back-to-top {
  position: fixed;
  bottom: var(--site-space-6);
  right: var(--site-space-6);
  z-index: 90;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--site-border-color);
  background: var(--site-default-bg-color);
  color: var(--site-default-fg-color--light);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(8px);
  transition: opacity .25s ease, transform .3s cubic-bezier(.34,1.56,.64,1), visibility .25s, background-color .2s, color .2s;
  display: flex;
  align-items: center;
  justify-content: center;
}
.site-back-to-top--visible {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0);
}
.site-back-to-top:hover {
  color: var(--site-accent-fg-color);
  background: var(--site-default-fg-color--lightest);
}

/* Copy code button */
.site-content pre {
  position: relative;
}
.site-copy-btn {
  position: absolute;
  top: var(--site-space-2);
  right: var(--site-space-2);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: var(--site-radius);
  background: transparent;
  color: var(--site-default-fg-color--lighter);
  cursor: pointer;
  opacity: 0.5;
  transform: scale(1);
  transition: opacity .2s ease, transform .2s cubic-bezier(.34,1.56,.64,1), color .15s, background .15s;
}
.site-content pre:hover .site-copy-btn,
.site-content pre:focus-within .site-copy-btn {
  opacity: 1;
}
.site-copy-btn:hover {
  background: var(--site-default-fg-color--lightest);
  color: var(--site-default-fg-color);
}
.site-copy-btn--done {
  opacity: 1 !important;
  color: var(--site-code-hl-string-color) !important;
  transform: scale(1.15) !important;
}

/* Backlinks */
.site-backlinks {
  margin-top: var(--site-space-8);
  padding-top: var(--site-space-6);
  border-top: 1px solid var(--site-default-fg-color--lightest);
}
.site-backlinks h2 {
  font-size: 1rem;
  font-weight: 600;
  color: var(--site-default-fg-color--light);
  margin-bottom: var(--site-space-3);
}
.site-backlinks ul { list-style: none; padding: 0; margin: 0; }
.site-backlinks li { margin-bottom: .35rem; }
.site-backlinks a {
  font-size: .9rem;
  color: var(--site-accent-fg-color);
  text-decoration: none;
  padding: .15rem .3rem;
  margin: 0 -.3rem;
  border-radius: var(--site-radius-sm);
  transition: background-color .15s, color .15s;
}
.site-backlinks a:hover {
  background: var(--site-accent-fg-color--transparent);
}

/* Prev / Next pager */
.site-pager {
  display: flex;
  justify-content: space-between;
  gap: var(--site-space-4);
  margin-top: 3rem;
  padding-top: var(--site-space-6);
  border-top: 1px solid var(--site-default-fg-color--lightest);
}
.site-pager__link {
  flex: 1;
  max-width: 48%;
  text-decoration: none;
  padding: .8rem var(--site-space-4);
  border: 1px solid var(--site-border-color);
  border-radius: var(--site-radius-lg);
  background: var(--site-surface-color);
  transition: border-color .15s, background-color .15s, transform .2s cubic-bezier(.34,1.56,.64,1);
}
.site-pager__link:hover {
  border-color: var(--site-accent-fg-color);
  background: var(--site-accent-fg-color--transparent);
  transform: translateX(3px);
}
.site-pager__link--prev:hover { transform: translateX(-3px); }
.site-pager__link--next {
  text-align: right;
  margin-left: auto;
}
.site-pager__label {
  display: block;
  font-size: .75rem;
  color: var(--site-default-fg-color--lighter);
  margin-bottom: .2rem;
}
.site-pager__title {
  font-size: .9rem;
  font-weight: 500;
  color: var(--site-default-fg-color);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  html { scroll-behavior: auto; }
}

/* ── Tablet landscape / small desktop ── */
@media (max-width: 1024px) {
  :root {
    --site-sidebar-width: 220px;
    --site-content-max-width: min(900px, calc(100vw - var(--site-sidebar-width) - 3rem));
    --site-content-padding-x: var(--site-space-6);
    --site-content-padding-y: var(--site-space-6);
  }
  .site-nav__link { font-size: 0.78rem; }
}

/* ── Tablet portrait & mobile ── */
@media (max-width: 768px) {
  /* ---- header ---- */
  .site-header__hamburger { display: inline-flex; }
  .site-header__title { display: none; }

  /* ---- sidebar: fixed flyout ---- */
  .site-sidebar {
    position: fixed;
    left: 0;
    top: 3rem;
    bottom: 0;
    z-index: 50;
    width: min(280px, 85vw);
    transform: translateX(-100%);
    transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    box-shadow: none;
  }
  .site-sidebar--open {
    transform: translateX(0);
    box-shadow: var(--site-shadow-lg);
  }

  /* ---- body scroll lock when sidebar open ---- */
  body:has(.site-sidebar--open) {
    overflow: hidden;
  }

  /* ---- layout ---- */
  :root {
    --site-content-max-width: 100%;
    --site-content-padding-x: 1.2rem;
    --site-content-padding-y: 1.2rem;
  }
  .site-main { padding: 0; }
  .site-content { max-width: 100%; }
  .site-content table { display: block; overflow-x: auto; }

  /* ---- typography ---- */
  body { font-size: 15px; }
  .site-body {
    font-size: 1rem;
    line-height: 1.7;
  }
  .site-content h1 { font-size: 1.6rem; }
  .site-content h2 { font-size: 1.3rem; }
  .site-content h3 { font-size: 1.1rem; }

  /* ---- touch targets (≥44px) ---- */
  .site-search__toggle,
  .theme-toggle,
  .site-header__hamburger {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .site-back-to-top {
    width: 44px;
    height: 44px;
    bottom: var(--site-space-4);
    right: var(--site-space-4);
  }
  /* ---- pager ---- */
  .site-pager { gap: var(--site-space-2); margin-top: var(--site-space-8); padding-top: var(--site-space-4); }
  .site-pager__link { padding: .6rem .8rem; max-width: 50%; }
  .site-pager__title { font-size: .82rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .site-pager__label { font-size: .7rem; }

  .site-copy-btn {
    width: 44px;
    height: 44px;
    top: 0.2rem;
    right: 0.2rem;
  }
  .site-nav__link {
    padding: 0.7rem 0.9rem;
    font-size: 0.85rem;
  }
  .site-nav__area-title {
    padding: 0.7rem 0.9rem;
    font-size: 0.85rem;
  }
  .site-nav__section-title {
    padding: 0.7rem 0.9rem;
    font-size: 0.82rem;
  }

  /* ---- search: full-width takeover ---- */
  .site-search__dropdown {
    position: fixed;
    top: 3rem;
    left: 0;
    right: 0;
    width: 100%;
    max-height: calc(100vh - 3rem);
    border-radius: 0;
    border-left: none;
    border-right: none;
    box-shadow: var(--site-shadow-lg);
    transform-origin: top center;
  }
  .site-search__input {
    padding: 0.9rem 1rem;
    border-radius: 0;
    font-size: 16px;
  }
  .site-search__result {
    padding: 0.8rem 1rem;
  }
  .site-search__result-title {
    font-size: 0.95rem;
  }
  .site-search__results:empty::after {
    padding: 3rem 1rem;
  }

  /* ---- code blocks ---- */
  .site-content pre {
    font-size: 0.82em;
    padding: 0.8rem;
    border-radius: var(--site-radius);
    margin: var(--site-space-4) 0;
    line-height: 1.55;
  }
  .syn-header {
    margin: -0.8rem -0.8rem var(--site-space-2) -0.8rem;
    padding: 0.3rem 0.8rem;
    font-size: 0.68rem;
    border-radius: var(--site-radius) var(--site-radius) 0 0;
  }
  .site-content :not(pre) > code {
    font-size: 0.84em;
  }

  /* ---- hamburger icon animation: three lines → X ---- */
  .hamburger-line {
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    transition: transform 0.25s ease, opacity 0.2s ease;
    transform-box: fill-box;
    transform-origin: center;
  }
  .site-header__hamburger--open .hamburger-line--top {
    transform: translateY(6px) rotate(45deg);
  }
  .site-header__hamburger--open .hamburger-line--mid {
    opacity: 0;
  }
  .site-header__hamburger--open .hamburger-line--bot {
    transform: translateY(-6px) rotate(-45deg);
  }
}

/* ── Large phones ── */
@media (max-width: 480px) {
  body { font-size: 15px; }
  .site-body {
    font-size: 0.98rem;
    line-height: 1.65;
  }
  .site-content { padding: var(--site-space-4); }
  .site-content h1 { font-size: 1.4rem; }
  .site-content h2 { font-size: 1.2rem; }
  .site-content h3 { font-size: 1.05rem; }

  /* ---- code blocks: even tighter ---- */
  .site-content pre {
    font-size: 0.78em;
    padding: 0.6rem;
    border-radius: var(--site-radius);
    line-height: 1.5;
  }
  .syn-header {
    margin: -0.6rem -0.6rem 0.4rem -0.6rem;
    padding: 0.25rem 0.6rem;
    font-size: 0.65rem;
    border-radius: var(--site-radius) var(--site-radius) 0 0;
  }

  .site-pager__link--next {
    text-align: left;
    margin-left: 0;
  }

  /* ---- error pages ---- */
  .site-error { padding-top: 3rem; }
  .site-error__code { font-size: 3rem; }
  .site-error__msg { font-size: 1rem; }

  /* ---- cover page ---- */
  .home-cover {
    padding: var(--site-space-6) var(--site-space-4);
    min-height: calc(100vh - 6rem);
  }
  .home-cover .cover-mark,
  .home-cover .cover-tagline,
  .home-cover .cover-rule,
  .home-cover .cover-foot,
  .home-cover .cover-mail {
    animation: none;
  }
  .home-cover .cover-mark {
    font-size: clamp(2.2rem, 13vw, 3.5rem);
    letter-spacing: 0.18em;
    text-indent: 0.18em;
  }
  .home-cover .cover-tagline {
    font-size: 0.9rem;
    letter-spacing: 0.1em;
    margin-top: 1.2rem;
  }
  .home-cover .cover-rule { margin: 1.6rem auto; }
  .home-cover .cover-foot {
    font-size: 0.76rem;
    max-width: 28ch;
  }
}

/* ── Small phones (iPhone SE etc.) ── */
@media (max-width: 360px) {
  body { font-size: 14px; }
  .site-body { font-size: 0.95rem; }
  .site-content { padding: 0.75rem; }
  .site-header__inner { padding: 0 0.6rem; }
  .site-logo { gap: 0.3rem; }
}

/* ── Landscape mobile (short viewport) ── */
@media (max-height: 500px) and (orientation: landscape) {
  .site-header { position: static; }
  .site-sidebar { top: 0; height: 100vh; }
  .site-main--cover { min-height: auto; padding: var(--site-space-8) 0; }
  .home-cover { min-height: auto; }
}

/* ── Notched phones ── */
@supports (padding: env(safe-area-inset-bottom)) {
  .site-header__inner {
    padding-left: max(1.2rem, env(safe-area-inset-left));
    padding-right: max(1.2rem, env(safe-area-inset-right));
  }
  .site-sidebar { padding-bottom: env(safe-area-inset-bottom); }
  .site-back-to-top {
    bottom: max(1rem, env(safe-area-inset-bottom));
    right: max(1rem, env(safe-area-inset-right));
  }
}

/* ── Typography ── */
.site-content h1 { font-size: 1.75rem; font-weight: 700; letter-spacing: -.02em; }
.site-content h2 {
  font-size: 1.35rem;
  font-weight: 650; letter-spacing: -.01em;
  border-bottom: 1px solid var(--site-default-fg-color--lightest);
  padding-bottom: .25em;
}
.site-content h3 { font-size: 1.15rem; font-weight: 600; }
/* Offset hash-scroll targets below the sticky header */
.site-content h1[id],
.site-content h2[id],
.site-content h3[id] {
  scroll-margin-top: 3.5rem;
}


/* Cover page header — seamless with background */
/* The cover is a single full-viewport moment — nothing to scroll or drag.
   Locked only when the viewport is tall enough to hold the whole cover
   (thresholds track the compact/full cover heights); shorter viewports
   keep scrolling so no content becomes unreachable. */
@media (max-width: 480px) and (min-height: 600px), (min-width: 480.02px) and (min-height: 800px) {
  body:has(.home-cover) {
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
  }
}
body:has(.home-cover) .site-header {
  border-bottom-color: transparent;
  background: var(--site-default-bg-color);
}
body:has(.home-cover) .site-sidebar {
  border-right-color: transparent;
  background: var(--site-default-bg-color);
}
/* The homepage's arrival is a moment, not just the mark — the area list
   cascades in too. Scoped to the cover so regular page-to-page browsing
   (where the sidebar persists across full reloads) never replays this. */
body:has(.home-cover) .site-nav__area {
  animation: searchResultIn .4s cubic-bezier(.25,1,.5,1) both;
  animation-delay: calc(min(var(--i, 0), 14) * 40ms);
}

/* Home cover typography */
.home-cover {
  min-height: calc(100vh - 10rem);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: var(--site-space-8) var(--site-space-4);
}
.home-cover .headerlink { display: none; }
.home-cover .cover-mark {
  margin: 0; padding: 0; border: 0;
  font-family: "Noto Serif SC", Georgia, "Songti SC", serif;
  font-size: clamp(3rem, 10vw, 5.4rem); font-weight: 300;
  line-height: 1.1; letter-spacing: .26em; text-indent: .26em;
  color: var(--site-default-fg-color);
  animation: cover-ink-in 1.7s cubic-bezier(.2, .6, .2, 1) both;
}
.home-cover .cover-tagline {
  margin: 2.2rem 0 0; font-size: clamp(.9rem, 2vw, 1.05rem);
  font-weight: 400; letter-spacing: .3em; text-indent: .3em;
  color: var(--site-default-fg-color--light);
  animation: coverFade .9s .9s cubic-bezier(.2, .6, .2, 1) both;
}
.home-cover .cover-rule {
  width: 40px; height: 1px; margin: 2.4rem auto;
  border: 0; display: block;
  background: var(--site-default-fg-color--lighter);
  animation: coverFade .9s 1.15s cubic-bezier(.2, .6, .2, 1) both;
}
.home-cover .cover-foot {
  margin: 0; max-width: 36ch; font-size: .82rem;
  font-style: italic; line-height: 1.7; letter-spacing: .02em;
  color: var(--site-default-fg-color--lighter);
  animation: coverFade .9s 1.3s cubic-bezier(.2, .6, .2, 1) both;
}
.home-cover .cover-mail {
  font-size: clamp(.78rem, 1.6vw, .9rem);
  font-weight: 400; letter-spacing: .12em;
  color: var(--site-default-fg-color--light);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: color .25s, border-color .25s;
  animation: coverFade .9s 1.3s cubic-bezier(.2, .6, .2, 1) both;
}
.home-cover .cover-mail:hover {
  color: var(--site-accent-fg-color);
  border-bottom-color: var(--site-accent-fg-color);
}

/* Signature moment: the cover mark is the site's one emotional outlet, so it
   gets its own slower ink-reveal (blur + collapsing letter-spacing) instead
   of the plain fade the rest of the page uses — everything else follows in
   its wake once the mark has resolved. */
@keyframes cover-ink-in {
  from {
    opacity: 0;
    filter: blur(8px);
    letter-spacing: .6em;
    text-indent: .6em;
  }
  to {
    opacity: 1;
    filter: blur(0);
    letter-spacing: .26em;
    text-indent: .26em;
  }
}

@keyframes coverFade {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 600px) {
  .home-cover { padding: var(--site-space-6) var(--site-space-4); }
}

/* ── Unified list (directory index + tag listing) ── */
/* Resting state is a clean typographic list: no repeated file icons, only
   folders carry an icon (few, need distinguishing). Hairline dividers give
   structure; hover adds the accent + a chevron affordance. */
.site-list { list-style: none; padding: 0; margin: var(--site-space-6) 0; }
.site-list__item { margin: 0; padding-left: calc(var(--depth, 0) * 1.4rem); }
.site-list__item + .site-list__item > a { border-top: 1px solid var(--site-default-fg-color--lightest); }
.site-list__item > a {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .7rem .6rem;
  text-decoration: none;
  color: var(--site-default-fg-color);
  transition: background-color .15s, color .15s, padding-left .15s;
  animation: searchResultIn .35s cubic-bezier(.25,1,.5,1) both;
  animation-delay: calc(min(var(--i, 0), 16) * 30ms);
}
.site-list__item > a:hover {
  background: var(--site-accent-fg-color--transparent);
  padding-left: .85rem;
}
.site-list__item > a:hover .site-list__title { color: var(--site-accent-fg-color); }

/* Icon column: only folders render an icon; files keep an invisible spacer so
   titles align when a listing mixes folders and files. */
.site-list__icon {
  flex-shrink: 0;
  width: 1.05rem; height: 1.05rem;
}
.site-list__icon--dir {
  background: var(--site-accent-fg-color);
  opacity: .65;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z'/%3E%3C/svg%3E") no-repeat center;
  mask-size: contain;
}
.site-list__title {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: .95rem;
  transition: color .15s;
}
/* chevron affordance, slides in on hover */
.site-list__item > a::after {
  content: '';
  flex-shrink: 0;
  width: .85rem; height: .85rem;
  background: var(--site-accent-fg-color);
  opacity: 0;
  transform: translateX(-.3rem);
  transition: opacity .15s, transform .15s;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 18l6-6-6-6'/%3E%3C/svg%3E") no-repeat center;
  mask-size: contain;
}
.site-list__item > a:hover::after { opacity: .6; transform: translateX(0); }

/* Tag index (cloud) */
.site-tag-cloud {
  display: flex;
  flex-wrap: wrap;
  gap: var(--site-space-2);
  margin: var(--site-space-6) 0;
}
.site-tag-cloud .site-tag {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  font-size: .85rem;
  animation: searchResultIn .35s cubic-bezier(.25,1,.5,1) both;
  animation-delay: calc(min(var(--i, 0), 20) * 22ms);
}
.site-tag__count {
  font-size: .72rem;
  opacity: .7;
  background: var(--site-default-bg-color);
  border-radius: var(--site-radius-pill);
  padding: 0 .4rem;
  min-width: 1.2rem;
  text-align: center;
}

/* Breadcrumbs (shared partial) */
/* Clickable crumbs = accent; current = muted, non-link */
.site-breadcrumbs a {
  color: var(--site-accent-fg-color);
  text-decoration: none;
  transition: color .15s;
}
.site-breadcrumbs a:hover { text-decoration: underline; }
.site-breadcrumbs [aria-current="page"] { color: var(--site-default-fg-color--light); }
.site-breadcrumbs__sep { margin: 0 .4rem; opacity: .45; color: var(--site-default-fg-color--lighter); }

/* Empty state (directory, tags, etc.) */
.site-empty {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--site-default-fg-color--lighter);
}
.site-empty svg { margin-bottom: var(--site-space-4); opacity: .4; }
.site-empty p { margin: 0; font-size: .9rem; }

/* Page footer (in-content) */
.site-footer {
  margin-top: 3rem;
  padding-top: var(--site-space-6);
  border-top: 1px solid var(--site-default-fg-color--lightest);
  font-size: .8rem;
  color: var(--site-default-fg-color--lighter);
}
.site-footer__meta {
  display: flex;
  gap: var(--site-space-4);
  flex-wrap: wrap;
  margin-bottom: .6rem;
}
.site-footer a { color: var(--site-accent-fg-color); text-decoration: none; }
.site-footer a:hover { text-decoration: underline; }
.site-footer__bottom {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--site-space-2);
}

@media print {
  .site-header, .site-sidebar, .site-footer, .site-pager, .site-back-to-top { display: none; }
}

/* ── Header icon link (knowledge graph entry) ── */
.header-icon-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: .4rem;
  border-radius: 50%;
  color: var(--site-default-fg-color);
  transition: background-color .2s, color .2s;
  touch-action: manipulation;
}
.header-icon-link:hover {
  color: var(--site-accent-fg-color);
  background: var(--site-default-fg-color--lightest);
}
.header-icon-link svg { transition: transform .35s cubic-bezier(.34,1.56,.64,1); }
.header-icon-link:hover svg { transform: scale(1.1) rotate(12deg); }

/* ── Cover coding heatmap — the cover cascade's final beat.
   A bare, unlabeled grid in ink tones: it reads as quiet texture under the
   wordmark, and all detail (dates, hours, languages) lives in the tooltip. ── */
.cover-heatmap {
  margin-top: 3rem;
  position: relative;
  /* reserved caption slot — the absolutely-positioned line below can never
     resize this box, so tapping cells never nudges the layout */
  padding-bottom: 1.75rem;
  animation: coverFade .9s 1.6s cubic-bezier(.2, .6, .2, 1) both;
  /* the grid is tap-to-inspect — never a text-selection target on touch */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}
.cover-heatmap__grid {
  display: block;
  margin: 0 auto;
  overflow: visible;
  /* the grid owns its touches outright (the cover page cannot scroll):
     drags in any direction scrub days, and no native gesture — scroll,
     loupe, callout — ever competes with them */
  touch-action: none;
}
/* Intensity scale: ink over the surface via fill-opacity — theme-aware for
   free and no color-mix dependency (older engines rendered that as black). */
svg rect.hm-cell {
  fill: var(--site-default-fg-color);
  /* Columns drift in left→right only after the mail above has settled
   * (its fade ends at ~2.2s) — unhurried, same easing family as the cover,
   * no bouncy pop: the grid is the cascade's final beat, not a firework. */
  animation: hmCellIn .65s calc(1.75s + var(--c, 0) * 26ms) cubic-bezier(.2, .6, .2, 1) both;
  transform-box: fill-box;
  transform-origin: center;
  stroke: transparent;
  stroke-width: 1.3;
  transition: fill-opacity .3s, stroke .15s;
}
svg rect.hm-l0 { fill-opacity: .08; }
svg rect.hm-l1 { fill-opacity: .15; }
svg rect.hm-l2 { fill-opacity: .28; }
svg rect.hm-l3 { fill-opacity: .42; }
svg rect.hm-l4 { fill-opacity: .58; }
svg rect.hm-cell:hover,
svg rect.hm-cell.is-active { stroke: var(--site-accent-fg-color); }
/* today gets a quiet resting outline so the grid reads "live" */
svg rect.hm-cell.hm-today { stroke: var(--site-default-fg-color--lighter); }
svg rect.hm-cell.hm-today:hover,
svg rect.hm-cell.hm-today.is-active { stroke: var(--site-accent-fg-color); }
@keyframes hmCellIn {
  from { opacity: 0; transform: scale(.7); }
  to   { opacity: 1; transform: scale(1); }
}
/* One-line detail slot under the grid. Absolutely positioned into the
   host's reserved padding: a long line (nowrap) can be wider than the grid
   without widening the shrink-to-fit host — zero layout shift on tap. */
.cover-heatmap__caption {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translate(-50%, 5px);
  opacity: 0;
  display: grid; /* the two lines stack in one cell and take turns */
  justify-items: center;
  width: max-content; /* pre-measurement fallback; JS sets a pixel width */
  max-width: calc(100vw - 2rem);
  font-size: .72rem;
  letter-spacing: .05em;
  line-height: 1.4;
  text-align: center;
  color: var(--site-default-fg-color--light);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  transition:
    opacity .22s cubic-bezier(.2, .6, .2, 1),
    transform .22s cubic-bezier(.2, .6, .2, 1),
    width .2s cubic-bezier(.2, .6, .2, 1);
}
.cover-heatmap__caption.is-on {
  opacity: 1;
  transform: translate(-50%, 0);
}
/* Odometer roll on date change: the new line resolves in from below out of
   a slight ink blur (the cover mark's own entrance language); the outgoing
   line exits upward faster and undelayed, so the two never read as a
   double exposure. */
.cover-heatmap__caption-line {
  grid-area: 1 / 1;
  width: max-content;
  max-width: 100%;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  opacity: 0;
  transform: translateY(6px);
  filter: blur(3px);
  transition:
    opacity .24s cubic-bezier(.2, .6, .2, 1) .04s,
    transform .24s cubic-bezier(.2, .6, .2, 1) .04s,
    filter .24s cubic-bezier(.2, .6, .2, 1) .04s;
}
.cover-heatmap__caption-line.is-cur { opacity: 1; transform: none; filter: none; }
.cover-heatmap__caption-line.is-out {
  opacity: 0;
  transform: translateY(-6px);
  filter: blur(3px);
  transition-duration: .16s, .16s, .16s;
  transition-delay: 0s, 0s, 0s;
}

/* ── Liquid-glass loupe: a lens riding above the finger while scrubbing.
   Two stacked clones of the cover — the rim ring runs at a stronger
   magnification, so content visibly bends at the edge like real glass.
   The wobble wrapper squashes along the direction of travel (liquid),
   and the light is layered: thin specular arcs on the rim, a broad top
   sheen, and a soft bottom counter-light. ── */
.hm-lens {
  position: absolute;
  z-index: 6;
  /* iOS-style resting shape: a wide ellipse; JS morphs it toward the
     equal-area circle (√(112·64) ≈ ⌀85) as the drag speeds up */
  width: 112px;
  height: 64px;
  margin: 0;
  border-radius: 50%;
  pointer-events: none;
  filter: drop-shadow(0 12px 22px rgba(0, 0, 0, .25)) drop-shadow(0 3px 7px rgba(0, 0, 0, .16));
  opacity: 0;
  transform: translate(-50%, -50%) scale(.35);
  transition:
    opacity .16s cubic-bezier(.2, .6, .2, 1),
    transform .26s cubic-bezier(.34, 1.56, .64, 1);
}
.hm-lens.is-on {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}
/* deforms with velocity — the glass body, its content and its light all
   stretch together, which is what sells the liquid */
.hm-lens__wobble {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  overflow: hidden;
  background: var(--site-default-bg-color);
}
/* thin bright rim arcs (top-left key light, faint bottom-right counter) */
.hm-lens__wobble::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: 50%;
  background: conic-gradient(from 215deg,
    rgba(255, 255, 255, 0) 0deg,
    rgba(255, 255, 255, .95) 38deg,
    rgba(255, 255, 255, 0) 100deg,
    rgba(255, 255, 255, 0) 205deg,
    rgba(255, 255, 255, .45) 245deg,
    rgba(255, 255, 255, 0) 310deg);
  /* ellipse-shaped gradient tracks the lens box, so the arc hugs the rim
     through the whole speed morph */
  -webkit-mask: radial-gradient(transparent 80%, #000 88%, #000 96%, transparent 100%);
  mask: radial-gradient(transparent 80%, #000 88%, #000 96%, transparent 100%);
}
/* broad glass sheen above the content */
.hm-lens__wobble::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: 50%;
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, .35),
    inset 0 1px 10px rgba(255, 255, 255, .3),
    inset 0 -14px 24px -18px rgba(0, 0, 0, .3);
  background:
    radial-gradient(120% 86% at 30% 6%, rgba(255, 255, 255, .38), rgba(255, 255, 255, 0) 42%),
    radial-gradient(150% 130% at 76% 104%, rgba(255, 255, 255, .12), rgba(255, 255, 255, 0) 46%);
}
.hm-lens__layer {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  overflow: hidden;
  filter: saturate(1.08) contrast(1.02);
}
.hm-lens__layer > * {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  will-change: transform;
}
/* the refraction ring: same content, pushed harder, visible only at the rim */
.hm-lens__layer--rim {
  filter: saturate(1.12) brightness(1.02);
  -webkit-mask: radial-gradient(transparent 56%, #000 72%);
  mask: radial-gradient(transparent 56%, #000 72%);
}
/* clones must hold still: no entrance cascades, no eased ink */
.hm-lens * { animation: none !important; transition: none !important; }
[data-theme="dark"] .hm-lens {
  filter: drop-shadow(0 12px 26px rgba(0, 0, 0, .6)) drop-shadow(0 3px 9px rgba(0, 0, 0, .45));
}
[data-theme="dark"] .hm-lens__wobble::before {
  background: conic-gradient(from 215deg,
    rgba(255, 255, 255, 0) 0deg,
    rgba(255, 255, 255, .5) 38deg,
    rgba(255, 255, 255, 0) 100deg,
    rgba(255, 255, 255, 0) 205deg,
    rgba(255, 255, 255, .22) 245deg,
    rgba(255, 255, 255, 0) 310deg);
}
[data-theme="dark"] .hm-lens__wobble::after {
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, .2),
    inset 0 1px 10px rgba(255, 255, 255, .12),
    inset 0 -14px 24px -18px rgba(0, 0, 0, .55);
  background:
    radial-gradient(120% 86% at 30% 6%, rgba(255, 255, 255, .14), rgba(255, 255, 255, 0) 42%),
    radial-gradient(150% 130% at 76% 104%, rgba(255, 255, 255, .05), rgba(255, 255, 255, 0) 46%);
}

/* ── Knowledge graph page ── */
.site-main--graph { padding: 0; }
.graph-stage {
  position: relative;
  height: calc(100vh - 3rem);
  /* dynamic viewport units track the collapsing mobile URL bar */
  height: calc(100dvh - 3rem);
  min-height: 420px;
  overflow: hidden;
}
#graph-canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}
.graph-hud {
  position: absolute;
  top: 1.1rem;
  left: 1.4rem;
  pointer-events: none;
}
.graph-hud__title {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 650;
  letter-spacing: .12em;
  color: var(--site-default-fg-color);
}
.graph-hud__meta {
  margin: .2rem 0 0;
  font-size: .72rem;
  letter-spacing: .04em;
  color: var(--site-default-fg-color--light);
}
.graph-hud__toggle {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  margin-top: .65rem;
  font-size: .75rem;
  color: var(--site-default-fg-color--light);
  cursor: pointer;
  user-select: none;
  transition: color .15s;
}
.graph-hud__toggle:hover { color: var(--site-default-fg-color); }
.graph-hud__toggle input { accent-color: var(--site-accent-fg-color); margin: 0; }
.graph-legend {
  position: absolute;
  bottom: 1rem;
  left: 1.4rem;
  right: 1.4rem;
  display: flex;
  flex-wrap: wrap;
  gap: .3rem .85rem;
  font-size: .72rem;
  color: var(--site-default-fg-color--light);
}
.graph-legend__chip {
  display: inline-flex;
  align-items: center;
  gap: .34rem;
  cursor: default;
  transition: color .15s;
}
.graph-legend__chip:hover { color: var(--site-default-fg-color); }
.graph-legend__chip i {
  width: 8px; height: 8px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
}
.graph-card {
  position: absolute;
  z-index: 6;
  max-width: 250px;
  background: var(--site-default-bg-color);
  border: 1px solid var(--site-border-color);
  border-radius: var(--site-radius);
  box-shadow: 0 8px 28px var(--site-shadow-color);
  padding: .7rem .85rem .65rem;
}
.graph-card--in { animation: graphCardIn .22s cubic-bezier(.25, 1, .5, 1) both; }
@keyframes graphCardIn {
  from { opacity: 0; transform: translateY(6px) scale(.97); }
  to   { opacity: 1; transform: none; }
}
.graph-card__area {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  font-size: .68rem;
  letter-spacing: .08em;
  color: var(--site-default-fg-color--light);
}
.graph-card__area i { width: 7px; height: 7px; border-radius: 50%; }
.graph-card__title {
  display: block;
  margin: .3rem 0 .25rem;
  font-size: .92rem;
  font-weight: 600;
  line-height: 1.45;
  color: var(--site-default-fg-color);
  text-decoration: none;
}
.graph-card__title:hover { color: var(--site-accent-fg-color); }
.graph-card__meta {
  font-size: .72rem;
  line-height: 1.5;
  color: var(--site-default-fg-color--light);
  overflow-wrap: anywhere;
}

@media (max-width: 480px) {
  .graph-hud { left: 1rem; }
  .graph-legend { left: 1rem; right: 1rem; }
}
@media (prefers-reduced-motion: reduce) {
  .cover-heatmap,
  svg rect.hm-cell,
  .graph-card--in { animation: none !important; }
  .cover-heatmap__caption,
  .cover-heatmap__caption-line,
  .hm-lens { transition: none !important; }
}
