/* ════════════════════════════════════════════════════════════════
   zz-dark-reading.css — Dark-mode reading-comfort refinements
   ----------------------------------------------------------------
   Named "zz-" so it concatenates LAST in PaperMod's extended bundle
   (resources.Match "css/extended/*.css" sorts lexicographically), which
   lets equal-specificity dark rules here win over earlier files.

   EVERY rule below is gated on `body.dark` / `[data-theme="dark"]`.
   Light mode is never selected and therefore provably unchanged.
   Goal: calmer, higher-legibility long-form reading in dark mode.
   ════════════════════════════════════════════════════════════════ */

/* ── 1. Text selection ───────────────────────────────────────────
   No ::selection rule existed anywhere, so dark mode fell back to the
   browser default (a washed blue that can swallow text). Give it a
   quiet, theme-tinted highlight that keeps selected text crisp. */
body.dark ::selection,
[data-theme="dark"] ::selection {
  background: rgba(125, 211, 252, 0.28); /* EN accent tint */
  color: #ffffff;
}
html:lang(zh) body.dark ::selection,
html:lang(zh) [data-theme="dark"] ::selection {
  background: rgba(255, 90, 90, 0.30); /* ZH accent tint */
  color: #fff7f5;
}

/* ── 2. Long-form table legibility ──────────────────────────────
   Tables carried only hairline bottom borders, so dense comparison
   grids were hard to track row-to-row on the dark surface. Add a quiet
   header band + zebra striping (faint white overlays) so the eye stays
   on the row. Excludes Chroma line-number tables (.lntable /
   .highlighttable) so code blocks are untouched. Dark mode only. */
body.dark .post-content table:not(.lntable):not(.highlighttable) thead th,
[data-theme="dark"] .post-content table:not(.lntable):not(.highlighttable) thead th {
  background: rgba(255, 255, 255, 0.05);
  color: var(--color-ink, #e2e3e1);
  font-weight: 600;
  border-bottom: 1px solid var(--color-rule, rgba(226, 227, 225, 0.18));
}
body.dark .post-content table:not(.lntable):not(.highlighttable) tbody tr:nth-child(even) td,
[data-theme="dark"] .post-content table:not(.lntable):not(.highlighttable) tbody tr:nth-child(even) td {
  background: rgba(255, 255, 255, 0.025);
}
body.dark .post-content table:not(.lntable):not(.highlighttable) tbody tr:hover td,
[data-theme="dark"] .post-content table:not(.lntable):not(.highlighttable) tbody tr:hover td {
  background: rgba(255, 255, 255, 0.06);
}

/* ── 3. Article images on a dark page ───────────────────────────
   White-background diagrams and screenshots floated as glaring white
   rectangles. Give them a soft border so the edge is defined and trim
   brightness a touch to contain the glare; full brightness returns on
   hover so true colours stay one intentional gesture away. Scoped to
   in-article images in dark mode. */
body.dark .post-content img,
[data-theme="dark"] .post-content img {
  border: 1px solid rgba(255, 255, 255, 0.08);
  filter: brightness(0.94) contrast(1.02);
  transition: filter 200ms ease;
}
body.dark .post-content img:hover,
[data-theme="dark"] .post-content img:hover {
  filter: brightness(1) contrast(1);
}

/* ── 4. In-prose link affordance ────────────────────────────────
   Dark prose links shared the body ink colour and were distinguished
   only by a 0.28-alpha underline that was easy to miss. Strengthen the
   resting underline so links are discoverable without shouting, and let
   hover resolve to the language accent for a clear, vivid cue. */
body.dark .post-content a:not(.footnote-ref):not(.footnote-backref),
[data-theme="dark"] .post-content a:not(.footnote-ref):not(.footnote-backref) {
  text-decoration-color: rgba(226, 227, 225, 0.45);
}
body.dark .post-content a:not(.footnote-ref):not(.footnote-backref):hover,
[data-theme="dark"] .post-content a:not(.footnote-ref):not(.footnote-backref):hover {
  text-decoration-color: var(--color-accent, #7dd3fc);
}

/* ── 5. Page scrollbar ───────────────────────────────────────────
   No global scrollbar styling existed, so dark pages showed the OS
   default light track/thumb — a bright bar glued to the right edge of
   an otherwise dark surface (very visible on Windows/Linux Chrome and
   on hover-revealed macOS bars). Tint the *page* scrollbar to the dark
   surface. Selectors attach the pseudo-element directly to body.dark
   (no descendant space) so component scrollbars — code blocks, TOC,
   chat panels — keep their own carefully-tuned styling. */
body.dark::-webkit-scrollbar,
[data-theme="dark"]::-webkit-scrollbar {
  width: 12px;
  height: 12px;
}
body.dark::-webkit-scrollbar-track,
[data-theme="dark"]::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.03);
}
body.dark::-webkit-scrollbar-thumb,
[data-theme="dark"]::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.16);
  border-radius: 8px;
  border: 3px solid transparent;
  background-clip: content-box;
}
body.dark::-webkit-scrollbar-thumb:hover,
[data-theme="dark"]::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.28);
  background-clip: content-box;
}
/* Firefox — set on the scroll root only, not descendants, to avoid
   recolouring component scrollbars that declare their own. */
html:has(body.dark),
html[data-theme="dark"] {
  scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
  scrollbar-width: thin;
}

/* ── 6. Highlighted text <mark> ──────────────────────────────────
   <mark> had no styling anywhere, so highlighted prose fell back to the
   browser default: a fluorescent yellow block with near-black text that
   glares against the dark page and breaks reading flow. Replace it (dark
   only) with a soft accent-tinted wash that keeps the highlighted words
   in the page ink colour. Light mode keeps its default — untouched. */
body.dark .post-content mark,
[data-theme="dark"] .post-content mark {
  background: rgba(125, 211, 252, 0.18); /* EN accent wash */
  color: var(--color-ink, #e2e3e1);
  padding: 0.05em 0.28em;
  border-radius: 0.2rem;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}
html:lang(zh) body.dark .post-content mark,
html:lang(zh) [data-theme="dark"] .post-content mark {
  background: rgba(255, 90, 90, 0.20); /* ZH accent wash */
  color: var(--color-ink, #f5f3ee);
}

/* ── 7. Footnotes section ────────────────────────────────────────
   The footnote definitions at the foot of an article had no dark
   treatment, so they sat in full-strength body ink with the default
   light <hr> separator — louder than footnotes should be and a hard
   visual edge before them. Quiet the block to a muted ink and give it a
   soft rule, so references stay reachable without competing with the
   main prose. Dark mode only. */
body.dark .post-content .footnotes,
[data-theme="dark"] .post-content .footnotes {
  color: var(--color-ink-muted, #b4bcb2);
  font-size: 0.92em;
}
body.dark .post-content .footnotes hr,
body.dark .post-content hr.footnotes-sep,
[data-theme="dark"] .post-content .footnotes hr,
[data-theme="dark"] .post-content hr.footnotes-sep {
  border: none;
  border-top: 1px solid var(--color-rule, rgba(226, 227, 225, 0.12));
  background: none !important;
}

/* ── 8. Collapsible <details> blocks ─────────────────────────────
   In-article <details>/<summary> blocks (used in several guides) had
   NO styling anywhere, so dark mode fell back to the browser default:
   a borderless block with a bare disclosure triangle and a summary that
   was visually indistinguishable from surrounding prose — the affordance
   read as accidental rather than interactive. Give them a quiet recessed
   surface, a soft rule, and an accent-tinted marker so the toggle is
   discoverable and the revealed content sits in its own contained panel.
   Token-driven, so the ZH palette is picked up automatically. Gated on
   body.dark — light mode keeps the browser default, provably unchanged. */
body.dark .post-content details,
[data-theme="dark"] .post-content details {
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid var(--color-rule, rgba(226, 227, 225, 0.12));
  border-radius: var(--radius-md, 8px);
  padding: 0.4rem 1rem;
  margin: 1.25rem 0;
  transition: background 180ms ease, border-color 180ms ease;
}
body.dark .post-content details[open],
[data-theme="dark"] .post-content details[open] {
  background: rgba(255, 255, 255, 0.04);
  border-color: var(--color-rule, rgba(226, 227, 225, 0.18));
}
body.dark .post-content details > summary,
[data-theme="dark"] .post-content details > summary {
  cursor: pointer;
  color: var(--color-ink, #e2e3e1);
  font-family: var(--font-meta, 'Inter', system-ui, sans-serif);
  font-weight: 600;
  padding: 0.35rem 0;
  list-style: none;
  outline: none;
  transition: color 150ms ease;
}
body.dark .post-content details > summary:hover,
body.dark .post-content details[open] > summary,
[data-theme="dark"] .post-content details:hover > summary,
[data-theme="dark"] .post-content details[open] > summary {
  color: var(--color-accent, #7dd3fc);
}
/* Replace the default triangle with a quiet accent-tinted marker that
   rotates open, so the toggle state is legible on the dark surface. */
body.dark .post-content details > summary::-webkit-details-marker,
[data-theme="dark"] .post-content details > summary::-webkit-details-marker {
  display: none;
}
body.dark .post-content details > summary::before,
[data-theme="dark"] .post-content details > summary::before {
  content: "▸";
  display: inline-block;
  margin-right: 0.55em;
  color: var(--color-accent, #7dd3fc);
  transition: transform 180ms ease;
}
body.dark .post-content details[open] > summary::before,
[data-theme="dark"] .post-content details[open] > summary::before {
  transform: rotate(90deg);
}
/* Soft separating rule between the summary and the revealed body. */
body.dark .post-content details[open] > summary,
[data-theme="dark"] .post-content details[open] > summary {
  border-bottom: 1px solid var(--color-rule, rgba(226, 227, 225, 0.10));
  margin-bottom: 0.6rem;
}

/* ── 9. Comments loading skeleton ────────────────────────────────
   The utterances loading shimmer (post-footer.css) painted a light
   gradient — var(--color-surface-low, #f4f4f2), which is undefined and so
   falls back to a near-white #f4f4f2 in BOTH modes. On the dark page this
   flashed a glaring pale band before the comment iframe replaced it.
   Repaint the shimmer with quiet dark-surface tones. Only the gradient
   IMAGE is overridden (via background-image) so the base rule's
   background-size:200% and shimmer animation keep working. Dark only. */
body.dark .post-comments__body:empty::before,
body.dark .post-comments__body:has(> script:only-child)::before,
[data-theme="dark"] .post-comments__body:empty::before,
[data-theme="dark"] .post-comments__body:has(> script:only-child)::before {
  background-image: linear-gradient(90deg,
    rgba(255, 255, 255, 0.05) 0%,
    rgba(255, 255, 255, 0.015) 50%,
    rgba(255, 255, 255, 0.05) 100%);
  opacity: 0.5;
}

/* The utterances iframe itself ships transparent, so once it loads it sits
   correctly on the dark page; the script tag now requests the github-dark
   theme in dark mode (see layouts/partials/comments.html). No further iframe
   styling needed here. */

/* ── 10. Task-list checkboxes ────────────────────────────────────
   Goldmark renders Markdown task lists (- [ ] / - [x]) as a bare,
   disabled native <input type=checkbox> with NO wrapper class, so dark
   mode fell back to the browser's native control: a glaring light box
   that punches a hole in the dark surface, and — when checked — a
   washed-out greyed tick that's hard to read. Replace the box (dark only)
   with a quiet token-drawn square and an accent checkmark when ticked, so
   checklists read as a calm part of the page. Token-driven, so the ZH
   accent is picked up automatically. Light mode keeps the native control,
   provably unchanged. */
body.dark .post-content input[type="checkbox"],
[data-theme="dark"] .post-content input[type="checkbox"] {
  -webkit-appearance: none;
  appearance: none;
  width: 1.05em;
  height: 1.05em;
  margin: 0 0.5em 0 0;
  vertical-align: -0.16em;
  border: 1px solid var(--color-rule, rgba(226, 227, 225, 0.30));
  border-radius: 0.28em;
  background: rgba(255, 255, 255, 0.04);
  position: relative;
  cursor: default;
  opacity: 1; /* override the dimmed native [disabled] rendering */
  transition: background 150ms ease, border-color 150ms ease;
}
body.dark .post-content input[type="checkbox"]:checked,
[data-theme="dark"] .post-content input[type="checkbox"]:checked {
  background: var(--color-accent-soft, rgba(125, 211, 252, 0.16));
  border-color: var(--color-accent, #7dd3fc);
}
/* CSS-drawn checkmark in the language accent. */
body.dark .post-content input[type="checkbox"]:checked::after,
[data-theme="dark"] .post-content input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 0.34em;
  top: 0.12em;
  width: 0.26em;
  height: 0.54em;
  border: solid var(--color-accent, #7dd3fc);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* ── 11. In-prose <kbd> keyboard keys ────────────────────────────
   Markdown like `<kbd>Ctrl</kbd>` is common in this blog's technical
   posts, but no in-prose `.post-content kbd` rule exists anywhere —
   the only `kbd` styling is scoped to the search trigger / hp-search
   button. So a keyboard shortcut in an article fell back to bare
   monospace text (custom.css only sets font-family on kbd), reading as
   indistinguishable inline code rather than a key. Give it a quiet,
   token-drawn keycap with a subtle bottom edge so the "press me"
   affordance is legible on the dark surface. Token-driven, so the ZH
   accent border is picked up automatically. Gated on body.dark — light
   mode keeps the browser/base rendering, provably unchanged. */
body.dark .post-content kbd,
[data-theme="dark"] .post-content kbd {
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 0.82em;
  line-height: 1;
  padding: 0.18em 0.5em;
  margin: 0 0.12em;
  white-space: nowrap;
  color: var(--color-ink, #e2e3e1);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--color-rule, rgba(226, 227, 225, 0.22));
  /* Soft bottom edge → the "raised key" cue, kept quiet for dark. */
  border-bottom-width: 2px;
  border-bottom-color: var(--color-rule, rgba(226, 227, 225, 0.30));
  border-radius: 0.35em;
  box-shadow: 0 1px 0 rgba(0, 0, 0, 0.35);
}
/* Keys chained with a "+" (e.g. Ctrl + C) keep the joiner unboxed. */
body.dark .post-content kbd kbd,
[data-theme="dark"] .post-content kbd kbd {
  margin: 0;
  box-shadow: none;
}

/* ── 12. Abbreviations <abbr title> ──────────────────────────────
   <abbr> has no styling anywhere, so in dark mode an abbreviation with
   a title fell back to the browser default: either nothing, or a hard
   black dotted underline (UA-dependent) that's invisible or jarring on
   the dark page, with no hint that hovering reveals an expansion. Give
   it a quiet accent-tinted dotted underline and a help cursor so the
   affordance reads clearly. Dark mode only; light keeps the default. */
body.dark .post-content abbr[title],
[data-theme="dark"] .post-content abbr[title] {
  text-decoration: underline dotted;
  text-decoration-color: var(--color-rule, rgba(226, 227, 225, 0.40));
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  cursor: help;
}

/* ── 14. Inline citation superscripts <sup> ──────────────────────
   This blog's research-style posts cite sources with hand-authored
   <sup>N</sup> markers — ~2,780 across the content — that point to a
   numbered reference list at the foot of the article. They are plain
   superscripts, NOT links, so NO `.post-content sup` rule existed
   anywhere: in dark mode each citation rendered in the body ink colour,
   a tiny raised number visually indistinguishable from the surrounding
   prose, leaving the reader no cue that it marks a citation. Give them a
   quiet accent tint and a touch more weight so they read clearly as
   reference markers (the familiar encyclopaedia convention) while
   staying small and calm in dense prose. Scoped with :not(:has(a)) so
   Goldmark's real footnote-ref markers — <sup><a class="footnote-ref">>
   — keep their own link styling, untouched (:has is already used
   elsewhere in this file, e.g. the Firefox scrollbar rule). The accent
   token resolves per language, so EN gets sky-blue and ZH red
   automatically. Gated on body.dark — light mode keeps the browser
   default, provably unchanged. */
body.dark .post-content sup:not(:has(a)),
[data-theme="dark"] .post-content sup:not(:has(a)) {
  color: var(--color-accent, #7dd3fc);
  font-weight: 600;
  padding-left: 0.06em;
}
/* Adjacent citation markers (e.g. <sup>4</sup><sup>5</sup>) get a hair of
   breathing room so stacked numbers stay individually legible. */
body.dark .post-content sup:not(:has(a)) + sup:not(:has(a)),
[data-theme="dark"] .post-content sup:not(:has(a)) + sup:not(:has(a)) {
  margin-left: 0.12em;
}

/* ── 13. Hero cover image glare ──────────────────────────────────
   In-prose images already get a subtle edge ring + brightness trim in
   dark (section 3, `.post-content img`), but the LARGEST image on an
   article — the 16:9 hero cover at `.post-single .entry-cover img`, which
   lives OUTSIDE `.post-content` and so the section-3 rule never reached —
   was left at full brightness. It therefore read as a glaring bright
   block crowning an otherwise calm dark page, and its edge bled softly
   into the surface. Apply the *same* restrained, already-proven treatment
   used for prose images: a faint inset edge ring on the frame so the
   boundary is crisp, and a touch less brightness to contain the glare,
   with true colour returning on hover so the full cover is one
   intentional gesture away. Values mirror section 3 exactly for visual
   consistency across every image on the page. Dark mode only — light
   keeps the cover untouched, provably unchanged. */
body.dark .post-single .entry-cover,
[data-theme="dark"] .post-single .entry-cover {
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
body.dark .post-single .entry-cover img,
[data-theme="dark"] .post-single .entry-cover img {
  filter: brightness(0.94) contrast(1.02);
  transition: filter 200ms ease;
}
body.dark .post-single .entry-cover:hover img,
[data-theme="dark"] .post-single .entry-cover:hover img {
  filter: brightness(1) contrast(1);
}

/* ── 15. In-prose <aside> callouts ───────────────────────────────
   Many articles (kubernetes, vector-database, hugo-advanced-tutorial,
   stage-growth-of-open-source, emerging-challenges, 2024-annual-review,
   gtd…, brain-friendly… — 20+ posts) write notes/tips as a bare,
   class-less <aside> in Markdown. NO `.post-content aside` rule exists
   anywhere, so the block fell back to plain body prose: in dark mode the
   note was visually indistinguishable from the surrounding paragraphs,
   with no surface, rule, or affordance to mark it as a set-aside aside.
   Give it a quiet recessed callout — accent-tinted left rule on a faint
   lifted surface — consistent with the <details> treatment in section 8,
   so notes read as a calm, contained aside on the dark page.

   Scoped with `aside:not([class])` so the theme's CLASSED layout asides —
   .article-tldr, .article-marginalia, .reading-companion / .editorial-
   tools-panel, .article-tools, .profile_sidecard — keep their own styling,
   untouched. Token-driven, so the accent resolves per language (sky-blue
   EN / red ZH) and --color-accent-soft supplies the wash automatically.
   Gated on body.dark / [data-theme="dark"] — light mode is never selected
   and keeps the plain rendering, provably unchanged. */
body.dark .post-content aside:not([class]),
[data-theme="dark"] .post-content aside:not([class]) {
  margin: 1.75rem 0;
  padding: 1rem 1.35rem;
  background: var(--color-accent-soft, rgba(125, 211, 252, 0.10));
  border-left: 3px solid var(--color-accent, #7dd3fc);
  border-radius: 0 var(--radius-md, 8px) var(--radius-md, 8px) 0;
  color: var(--color-ink, #e2e3e1);
  font-size: 0.97em;
  line-height: 1.7;
}
/* Trim the leading/trailing child margins so the callout isn't over-padded
   (post-content paragraphs carry a 2rem bottom margin by default). */
body.dark .post-content aside:not([class]) > :first-child,
[data-theme="dark"] .post-content aside:not([class]) > :first-child {
  margin-top: 0;
}
body.dark .post-content aside:not([class]) > :last-child,
[data-theme="dark"] .post-content aside:not([class]) > :last-child {
  margin-bottom: 0;
}

/* ── 16. In-prose figure captions <figcaption> ───────────────────
   The PaperMod `figure` shortcode (layouts/shortcodes/figure.html) emits a
   <figcaption> for any image given a title/caption/attr, and several posts
   use it. NO `.post-content figcaption` rule exists anywhere — and the
   article-typography base note (lines 57-64) confirms captions simply
   "inherit this base", i.e. the FULL body ink (#e2e3e1 EN / #f5f3ee ZH).
   So in dark mode a caption renders at the exact weight and colour of the
   surrounding prose: the eye can't tell where the paragraph ends and the
   image's caption begins, and the caption competes with the body text it
   should sit quietly beneath. Demote it to the muted ink, nudge the size
   down, centre it under the figure, and give the credit/attr link the
   language accent so the one interactive part stays discoverable. Token-
   driven, so EN sky-blue / ZH red resolve automatically. Scoped to
   .post-content so the lightbox overlay's own .lightbox-caption (built at
   body level by image-lightbox.js, already styled for the dark backdrop in
   image-lightbox.css) is untouched. Gated on body.dark / [data-theme="dark"]
   — light mode keeps the current (browser/base) rendering, provably
   unchanged. */
body.dark .post-content figcaption,
[data-theme="dark"] .post-content figcaption {
  margin-top: 0.6rem;
  text-align: center;
  font-family: var(--font-meta, 'Inter', system-ui, sans-serif);
  font-size: 0.85em;
  line-height: 1.55;
  color: var(--color-ink-muted, #b4bcb2);
}
/* The caption body (credit / attribution line) sits in a <p> — keep it
   tight against the title so the block reads as one quiet unit. */
body.dark .post-content figcaption p,
[data-theme="dark"] .post-content figcaption p {
  margin: 0.15rem 0 0;
}
/* Credit links (attrlink) are the one interactive part — give them the
   language accent with a soft underline so they're reachable without
   pulling weight back into the caption. */
body.dark .post-content figcaption a,
[data-theme="dark"] .post-content figcaption a {
  color: var(--color-accent, #7dd3fc);
  text-decoration: underline;
  text-decoration-color: rgba(125, 211, 252, 0.4);
  text-underline-offset: 0.18em;
}
html:lang(zh) body.dark .post-content figcaption a,
html:lang(zh) [data-theme="dark"] .post-content figcaption a {
  text-decoration-color: rgba(255, 90, 90, 0.4);
}

/* ── 17. In-content horizontal scrollbars (code blocks & wide tables) ──
   Long code lines (`.post-content pre code` carries overflow-x:auto in the
   PaperMod base; `.post-content .highlight / pre` get it on mobile) and wide
   tables (`.post-content table` becomes display:block; overflow-x:auto on
   narrow viewports) produce a HORIZONTAL scrollbar inside the block. NO
   `::-webkit-scrollbar` rule targets these containers anywhere — and section
   5 above intentionally scoped the page-scrollbar fix to body.dark *directly*
   (no descendant) precisely so it would NOT reach component scrollbars,
   noting they "keep their own carefully-tuned styling." But code blocks and
   tables never actually got their own styling, so they fell back to the OS
   default: a bright light track + thumb glued to the bottom edge of an
   otherwise calm dark code block (very visible in Chrome on Windows/Linux,
   and on hover/scroll-reveal elsewhere). Repaint just these inner scrollbars
   to the same quiet dark treatment used for the page bar in section 5, so a
   scrollable code sample or table stays a contained, low-glare element on the
   dark surface. Slightly thinner than the page bar since these are nested.
   Gated on body.dark / [data-theme="dark"] — light mode keeps the default
   rendering, provably unchanged. */
body.dark .post-content pre::-webkit-scrollbar,
body.dark .post-content pre code::-webkit-scrollbar,
body.dark .post-content .highlight::-webkit-scrollbar,
body.dark .post-content table::-webkit-scrollbar,
[data-theme="dark"] .post-content pre::-webkit-scrollbar,
[data-theme="dark"] .post-content pre code::-webkit-scrollbar,
[data-theme="dark"] .post-content .highlight::-webkit-scrollbar,
[data-theme="dark"] .post-content table::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
body.dark .post-content pre::-webkit-scrollbar-track,
body.dark .post-content pre code::-webkit-scrollbar-track,
body.dark .post-content .highlight::-webkit-scrollbar-track,
body.dark .post-content table::-webkit-scrollbar-track,
[data-theme="dark"] .post-content pre::-webkit-scrollbar-track,
[data-theme="dark"] .post-content pre code::-webkit-scrollbar-track,
[data-theme="dark"] .post-content .highlight::-webkit-scrollbar-track,
[data-theme="dark"] .post-content table::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.04);
  border-radius: 8px;
}
body.dark .post-content pre::-webkit-scrollbar-thumb,
body.dark .post-content pre code::-webkit-scrollbar-thumb,
body.dark .post-content .highlight::-webkit-scrollbar-thumb,
body.dark .post-content table::-webkit-scrollbar-thumb,
[data-theme="dark"] .post-content pre::-webkit-scrollbar-thumb,
[data-theme="dark"] .post-content pre code::-webkit-scrollbar-thumb,
[data-theme="dark"] .post-content .highlight::-webkit-scrollbar-thumb,
[data-theme="dark"] .post-content table::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  border: 2px solid transparent;
  background-clip: content-box;
}
body.dark .post-content pre::-webkit-scrollbar-thumb:hover,
body.dark .post-content pre code::-webkit-scrollbar-thumb:hover,
body.dark .post-content .highlight::-webkit-scrollbar-thumb:hover,
body.dark .post-content table::-webkit-scrollbar-thumb:hover,
[data-theme="dark"] .post-content pre::-webkit-scrollbar-thumb:hover,
[data-theme="dark"] .post-content pre code::-webkit-scrollbar-thumb:hover,
[data-theme="dark"] .post-content .highlight::-webkit-scrollbar-thumb:hover,
[data-theme="dark"] .post-content table::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.30);
  background-clip: content-box;
}
/* Keep the track corner (where H and V bars meet) from flashing the OS
   default light square. */
body.dark .post-content pre::-webkit-scrollbar-corner,
body.dark .post-content pre code::-webkit-scrollbar-corner,
body.dark .post-content .highlight::-webkit-scrollbar-corner,
body.dark .post-content table::-webkit-scrollbar-corner,
[data-theme="dark"] .post-content pre::-webkit-scrollbar-corner,
[data-theme="dark"] .post-content pre code::-webkit-scrollbar-corner,
[data-theme="dark"] .post-content .highlight::-webkit-scrollbar-corner,
[data-theme="dark"] .post-content table::-webkit-scrollbar-corner {
  background: transparent;
}
/* Firefox — thin, tinted thumb on the same containers. Set directly on the
   scroll containers (not a descendant selector) so no other component bar is
   affected. */
body.dark .post-content pre,
body.dark .post-content pre code,
body.dark .post-content .highlight,
body.dark .post-content table,
[data-theme="dark"] .post-content pre,
[data-theme="dark"] .post-content pre code,
[data-theme="dark"] .post-content .highlight,
[data-theme="dark"] .post-content table {
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.20) transparent;
}

/* ── 18. List markers (bullets & ordered numbers) ────────────────
   Markdown lists are everywhere in this blog's technical posts, but no
   `::marker` rule exists anywhere — the only list styling sets spacing
   (custom.css) and list-style:none on a few layout lists. So in dark mode
   the disc bullets and the "1." "2." ordered numbers inherit the FULL body
   ink (#e2e3e1 EN / #f5f3ee ZH): each marker renders at the exact weight
   and colour of the prose beside it, so on a long list the markers compete
   for attention with the text they're only meant to index. Demote them to
   the muted ink so the marker reads as a quiet guide and the sentence
   leads — the same "lead with prose, support with structure" move already
   applied to footnotes (§7) and figcaptions (§16). Token-driven, so the ZH
   palette (#a8a29e) is picked up automatically; muted ink on the dark paper
   still clears WCAG AA, so nothing becomes hard to find. Scoped to
   `.post-content` so component lists (TOC, nav, cards) keep their styling,
   and only `::marker` is touched — the list item's own text stays full ink.
   Gated on body.dark / [data-theme="dark"] — light mode keeps the browser
   default marker, provably unchanged. */
body.dark .post-content li::marker,
[data-theme="dark"] .post-content li::marker {
  color: var(--color-ink-muted, #b4bcb2);
}

/* ── 19. Strikethrough / retracted text <del> <s> ────────────────
   GFM strikethrough (`~~text~~`) is used in several posts (e.g. the
   prow / langchain / annual-review notes) and Goldmark renders it as
   <del>; hand-written <s> appears too. NO `del`/`s` rule exists anywhere,
   so in dark mode struck text renders at the FULL body ink (#e2e3e1 EN /
   #f5f3ee ZH) with a full-ink line through it — i.e. the one thing the
   author marked as removed/superseded shouts at the exact weight of the
   live prose around it, and the heavy line crowds the glyphs. Semantically
   struck text should read as quiet and secondary, so demote it to the
   muted ink (the same "lead with prose, support with structure" move used
   for footnotes §7, figcaptions §16 and list markers §18) and thin the
   line a touch so it marks the words without smothering them. Token-driven,
   so the ZH muted palette (#a8a29e) is picked up automatically; muted ink
   on the dark paper still clears WCAG AA, so retracted text stays legible
   for anyone who wants to read it. Scoped to `.post-content` so any
   component strike (none today) is untouched, and `del a`/`s a` keep their
   link colour so a struck-through link is still reachable. Gated on
   body.dark / [data-theme="dark"] — light mode keeps the browser default,
   provably unchanged. */
body.dark .post-content del,
body.dark .post-content s,
[data-theme="dark"] .post-content del,
[data-theme="dark"] .post-content s {
  color: var(--color-ink-muted, #b4bcb2);
  text-decoration-line: line-through;
  text-decoration-color: var(--color-rule, rgba(226, 227, 225, 0.45));
  text-decoration-thickness: 1px;
}
/* A struck link keeps its own accent so it stays discoverable. */
body.dark .post-content del a,
body.dark .post-content s a,
[data-theme="dark"] .post-content del a,
[data-theme="dark"] .post-content s a {
  color: var(--color-accent, #7dd3fc);
}

/* ── 20. Goldmark footnote markers <a.footnote-ref> / <a.footnote-backref> ─
   This blog mixes two citation systems. Hand-authored encyclopaedia-style
   markers (<sup>N</sup>) already get a quiet accent tint and a touch of
   weight in dark mode (§14) so they read clearly as reference cues. But the
   REAL Goldmark footnotes — the clickable in-text marker
   <sup id="fnref:N"><a class="footnote-ref">N</a></sup> and the return arrow
   <a class="footnote-backref">↩︎</a> at the foot of each definition — were
   never given a colour: §4 explicitly EXCLUDES `.footnote-ref` /
   `.footnote-backref` from the prose-link treatment, and §14's sup rule is
   scoped `sup:not(:has(a))`, so a footnote marker (a <sup> that DOES contain
   an <a>) is skipped there too. The result in dark mode: the functional
   footnote number rendered at the full body ink — visually identical to the
   surrounding prose — while carrying the base link's full-strength underline,
   which on a tiny raised numeral reads as a heavy smudge rather than a clean
   citation. So the one marker the reader can actually click looked LESS like
   a reference than the decorative hand-authored ones beside it.

   Bring them into line with the §14 citation convention: the language accent
   plus a hair of weight so the marker reads as a reference cue, and drop the
   awkward resting underline on the superscript so the numeral stays crisp
   (matching §14, which carries no underline). Hover/focus resolves to a soft
   accent underline as the discoverable "this is a link" cue — the affordance
   stays one gesture away without shouting in the resting state. The accent
   token resolves per language automatically (EN sky-blue / ZH red), so the
   two citation systems finally read as one coherent family in dark mode.
   Gated on body.dark / [data-theme="dark"] — light mode keeps its current
   (full-ink, underlined) rendering, provably unchanged. */
body.dark .post-content a.footnote-ref,
body.dark .post-content a.footnote-backref,
[data-theme="dark"] .post-content a.footnote-ref,
[data-theme="dark"] .post-content a.footnote-backref {
  color: var(--color-accent, #7dd3fc);
  font-weight: 600;
  text-decoration: none;
  transition: text-decoration-color 150ms ease, color 150ms ease;
}
/* The in-text marker is a superscript numeral — give it the same hair of
   left padding §14 uses so it sits a touch off the preceding word. */
body.dark .post-content a.footnote-ref,
[data-theme="dark"] .post-content a.footnote-ref {
  padding-left: 0.06em;
}
/* The return arrow sits at the end of each footnote definition — nudge it a
   little clear of the definition text so the affordance is legible. */
body.dark .post-content a.footnote-backref,
[data-theme="dark"] .post-content a.footnote-backref {
  margin-left: 0.3em;
}
/* Hover/focus → a soft accent underline so the link is discoverable without
   pulling weight into the resting state. underline-offset keeps the rule off
   the raised numeral's baseline. */
body.dark .post-content a.footnote-ref:hover,
body.dark .post-content a.footnote-ref:focus-visible,
body.dark .post-content a.footnote-backref:hover,
body.dark .post-content a.footnote-backref:focus-visible,
[data-theme="dark"] .post-content a.footnote-ref:hover,
[data-theme="dark"] .post-content a.footnote-ref:focus-visible,
[data-theme="dark"] .post-content a.footnote-backref:hover,
[data-theme="dark"] .post-content a.footnote-backref:focus-visible {
  text-decoration: underline;
  text-decoration-color: var(--color-accent, #7dd3fc);
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
}

/* ── 21. Article meta line (date · reading time · author) ────────
   The .post-meta line under every article title (custom.css ~line 2264)
   paints var(--secondary) — rgb(155,156,157) in PaperMod dark — and then
   stacks an extra `opacity: 0.7` on top. That double-dims the most-visible
   secondary text on the page to an effective rgb(114,115,116) on the dark
   paper: contrast ≈ 3.89:1 (EN) / 3.80:1 (ZH), UNDER the WCAG-AA 4.5:1 floor
   for this small (0.78rem), uppercase, letter-spaced meta — exactly the size
   where AA matters most. The single-post rule carries specificity (0,1,0);
   this rule (body.dark .post-meta → 0,1,1, and loaded LAST in the extended
   bundle) wins, drops the extra dim, and repaints the line in the design's
   muted-ink token — the same colour already used for footnotes (§7),
   figcaptions (§16), list markers (§18) and struck text (§19), so the meta
   finally reads as one coherent muted-ink family in dark. Resolves to
   9.49:1 (EN) / 6.93:1 (ZH) — comfortably AA, and still clearly recessed
   below the body prose (≈14.5:1) so the visual hierarchy is unchanged. The
   list/card meta (`.post-entry .post-meta` / `.first-entry .post-meta`,
   specificity 0,2,0, colour #94a3b8 at ≈7.2:1) is more specific and already
   AA, so it keeps its own colour — only the no-op `opacity:1` lands there.
   Token-driven, so EN/ZH dark resolve automatically. Gated on body.dark /
   [data-theme="dark"] — light mode keeps var(--secondary) × 0.7, provably
   unchanged. */
body.dark .post-meta,
[data-theme="dark"] .post-meta {
  color: var(--color-ink-muted, #b4bcb2);
  opacity: 1;
}

/* ── 22. AI-tech card/list title-hover & category in dark ────────
   The home / section "AI 技术" cards and list rows colour their title
   HOVER state and their category label with the brand violet #7c3aed
   (custom.css: .ai-tech-card-title a:hover, .ai-tech-list-title a:hover,
   .ai-tech-list-category). Those three rules are NOT theme-gated, so they
   apply in BOTH modes. On the LIGHT white card #7c3aed sits at a healthy
   5.70:1 — but in dark mode the same colour lands on the dark card surface
   (.dark .ai-tech-card ≈ slate-800 over the warm paper) at just 2.72:1, and
   on the darker list row at 2.92:1 — BOTH well under the WCAG-AA 4.5:1 floor.
   So hovering an article title, or reading its category tag, in dark mode
   rendered the text as a barely-legible dark-violet-on-dark smudge.

   The fix is the convention this codebase ALREADY uses for the same hue in
   dark: the badges next to these very cards were given color:#a78bfa for
   dark mode (custom.css: .dark .ai-tech-badge line ~150, .dark .posts-badge
   --ai line ~4729). These three states were simply missed — the dark title
   override only set the RESTING colour (.dark .ai-tech-card-title a → #f3f4f6)
   and let :hover fall through to the shared #7c3aed, and the list category
   had no dark override at all. Reuse #a78bfa here so the violet reads
   consistently across every AI-tech surface: it resolves to 5.69:1 on the
   card and 6.10:1 on the list row — i.e. it restores the exact contrast the
   light card already enjoys (5.70:1), clearing AA comfortably.

   (The sibling "成长 / Growth" cards use amber #d97706 for the same states;
   that hue already clears AA on the dark surface — 4.86:1 card / 5.21:1 list
   — so it is intentionally left unchanged.)

   Each selector below is body.dark-scoped (specificity ≥ the un-gated source
   rule, and loaded last in the extended bundle), so it wins in dark and is
   never selected in light — light mode is provably unchanged. */
body.dark .ai-tech-card-title a:hover,
body.dark .ai-tech-list-title a:hover,
body.dark .ai-tech-list-category,
[data-theme="dark"] .ai-tech-card-title a:hover,
[data-theme="dark"] .ai-tech-list-title a:hover,
[data-theme="dark"] .ai-tech-list-category {
  color: #a78bfa;
}

/* ── 23. Bold emphasis lift in dark (EN) ─────────────────────────
   <strong> is by far the most-used inline element in this blog's prose
   — ~5,570 occurrences across the articles, the way authors flag the
   key phrase in almost every paragraph. But the only `strong` rule
   anywhere just sets font-weight:600 (custom.css ~799); the element is
   given NO colour, so in dark mode it inherits the body ink
   (#e2e3e1 EN) and is set apart from the prose by weight ALONE. On the
   dark paper a 600-weight run in the exact body tone reads as barely
   distinct from the 400-weight text around it, so on a bold-dense page
   the emphasis the author placed for scanning is muffled — the reader
   loses the at-a-glance "this is the important bit" cue that bold is
   for. Give emphasised text a gentle tonal lift — a soft cool white a
   few steps above the body ink — so a strong run reads as a clear,
   calm highlight without the glare of pure #fff. This is the one place
   the file PROMOTES rather than demotes, because <strong> is primary
   content the author chose to surface (the inverse of the muted
   footnotes §7 / markers §18 / struck text §19 moves), and a restrained
   lift serves the same goal: a legible reading hierarchy on the dark
   page. EN-only: the ZH dark ink (#f5f3ee) already sits near-white with
   no comfortable headroom to lift, so brightening it would glare —
   there the 600 weight carries the emphasis on its own. The same
   per-language judgement the code palettes (§3-4 in article-typography)
   already make. Contrast rises above the body ink's ≈14.5:1, so AA is
   never at risk. Gated on body.dark / [data-theme="dark"] AND
   html:not(:lang(zh)) — light mode and Chinese dark are never selected
   and are provably unchanged. */
html:not(:lang(zh)) body.dark .post-content strong,
html:not(:lang(zh)) body.dark .post-content b,
html:not(:lang(zh)) [data-theme="dark"] .post-content strong,
html:not(:lang(zh)) [data-theme="dark"] .post-content b {
  color: #f0f1ef;
}
/* A <strong>/<b> inside a link keeps the link's own colour so an
   emphasised link stays one coherent element, not a two-tone smudge. */
html:not(:lang(zh)) body.dark .post-content a strong,
html:not(:lang(zh)) body.dark .post-content a b,
html:not(:lang(zh)) [data-theme="dark"] .post-content a strong,
html:not(:lang(zh)) [data-theme="dark"] .post-content a b {
  color: inherit;
}
/* Inside the intentionally-quieted blocks — footnotes (§7), figure
   captions (§16), struck/retracted text (§19) and blockquotes — emphasis
   must stay muted, not jump back to the lifted ink and re-loud the very
   passages those sections calmed. Restore the inherited (muted) colour
   there. The blockquote case matters most in practice: article-typography
   demotes a dark blockquote's text to `color-mix(--color-ink 82%, …)` so a
   pull-quote reads as a quiet, set-aside passage, but §23 lifts every
   <strong> to the brighter #f0f1ef — so a bold run *inside* a quote popped
   ABOVE the quoted words around it, breaking the calm the quote is meant to
   hold. (This blog leans on bold-in-blockquote heavily — e.g. the monthly
   thought-notes open each quote with a bold summary line.) `inherit` pulls
   the bold back to the quote's own 82% ink, so weight still marks the phrase
   while the tone stays contained. Light mode already behaves this way (quote
   text is --color-text-muted and <strong> sets no colour), so this only
   brings dark into line. */
html:not(:lang(zh)) body.dark .post-content .footnotes strong,
html:not(:lang(zh)) body.dark .post-content .footnotes b,
html:not(:lang(zh)) body.dark .post-content figcaption strong,
html:not(:lang(zh)) body.dark .post-content figcaption b,
html:not(:lang(zh)) body.dark .post-content del strong,
html:not(:lang(zh)) body.dark .post-content del b,
html:not(:lang(zh)) body.dark .post-content s strong,
html:not(:lang(zh)) body.dark .post-content s b,
html:not(:lang(zh)) body.dark .post-content blockquote strong,
html:not(:lang(zh)) body.dark .post-content blockquote b,
html:not(:lang(zh)) [data-theme="dark"] .post-content .footnotes strong,
html:not(:lang(zh)) [data-theme="dark"] .post-content .footnotes b,
html:not(:lang(zh)) [data-theme="dark"] .post-content figcaption strong,
html:not(:lang(zh)) [data-theme="dark"] .post-content figcaption b,
html:not(:lang(zh)) [data-theme="dark"] .post-content del strong,
html:not(:lang(zh)) [data-theme="dark"] .post-content del b,
html:not(:lang(zh)) [data-theme="dark"] .post-content s strong,
html:not(:lang(zh)) [data-theme="dark"] .post-content s b,
html:not(:lang(zh)) [data-theme="dark"] .post-content blockquote strong,
html:not(:lang(zh)) [data-theme="dark"] .post-content blockquote b {
  color: inherit;
}

/* ── 24. EN blockquote decorative quote glyph in dark ─────────────
   The English blockquote carries an oversized opening-quote glyph
   (article-typography.css: `html:lang(en) .post-content blockquote::before`,
   an 8rem ‟ set at `color: var(--color-accent); opacity: 0.1`). That rule is
   NOT theme-gated, so the same colour×opacity is used in both modes. In
   LIGHT it resolves to the near-black green accent (#2e352e) at 10% over the
   pale paper — a soft, clearly-visible editorial mark that gives the quote
   its character. But in DARK the accent becomes sky-blue (#7dd3fc), and a
   10%-opacity sky-blue glyph over the near-black blockquote surface
   (rgba(255,255,255,0.03) on #121413) is effectively invisible — the dark
   audit render shows the quote with no glyph at all. So a deliberate design
   flourish silently vanishes in dark, flattening the blockquote's identity.

   Restore it with the faithful dark analog: swap the faint-blue tint for a
   neutral pale ink (the page's own --color-ink) and lift the opacity a touch
   so the mark reads as the same quiet, soft watermark the light quote has —
   present and characterful, never glaring (still only ~12% over the dark
   surface, well below the body prose so it never competes with the quoted
   words). EN-only and dark-only: the selector carries html:lang(en) +
   body.dark (specificity 0,3,3, and loaded last in the extended bundle) so it
   beats the un-gated source rule (0,2,2) in dark while never being selected in
   light — light mode keeps the accent glyph, provably unchanged. ZH has no
   glyph (its corner brackets were removed), so it is intentionally untouched. */
html:lang(en) body.dark .post-content blockquote::before,
html:lang(en) [data-theme="dark"] .post-content blockquote::before {
  color: var(--color-ink, #e2e3e1);
  opacity: 0.12;
}

/* ── 25. Prev/Next pagination eyebrow label in dark ──────────────
   The "« PREV" / "NEXT »" eyebrow above each article-footer pagination
   card (post-footer.css §"Eyebrow label", .paginav .title) paints
   `color: var(--color-ink-muted) !important` and then stacks an extra
   `opacity: 0.75` on top — the EXACT double-dim pattern §21 found and
   fixed on the article meta line. It lands on a small (0.62rem ≈ 10px),
   uppercase, 0.22em-letter-spaced monospace label sitting on the dark
   paginav card (background rgba(255,255,255,0.02) over the dark paper,
   post-footer.css §"Dark theme"). The double-dim drops the muted ink to an
   effective contrast of ≈5.64:1 (EN) and ≈4.32:1 (ZH) on that card —
   and the ZH value falls UNDER the WCAG-AA 4.5:1 floor, exactly the small,
   letter-spaced size where AA matters most: the footer nav label the reader
   uses to move between articles reads as a barely-there grey smudge in
   Chinese dark.

   Same remedy as §21, but lighter-touch: the source resting label ALREADY
   uses the right colour — `var(--color-ink-muted)` — so the ONLY thing that
   needs fixing is the extra `opacity: 0.75`. This rule (body.dark .paginav
   .title → specificity 0,3,1, loaded LAST in the extended bundle) beats the
   source rule (.paginav .title → 0,2,0) for the un-!important `opacity`, so
   dropping the dim is enough; the label then resolves to the full muted-ink
   token — the same family already used for the meta line (§21), footnotes
   (§7), figcaptions (§16), list markers (§18) and struck text (§19) — at
   ≈9.09:1 (EN) / ≈6.60:1 (ZH), comfortably AA and still clearly recessed
   below the card's article-title text so the visual hierarchy is unchanged.

   Deliberately NOT touching `color`: the source hover rule
   (.paginav a:hover .title) carries `color: var(--color-accent) !important`
   at equal specificity (0,3,1) but is loaded earlier, so a `color !important`
   here would override the hover accent (later-wins between equal-important
   declarations) and kill the hover cue. Leaving colour alone lets the resting
   muted ink and the hover accent each apply in their own state, and only the
   opacity double-dim — the actual bug — is removed. Token-driven, so EN/ZH
   dark resolve automatically. Gated on body.dark / [data-theme="dark"] —
   light mode keeps var(--color-ink-muted) × 0.75, provably unchanged. */
body.dark .paginav .title,
[data-theme="dark"] .paginav .title {
  opacity: 1;
}

/* ── 26. Keyboard focus ring (dark) ──────────────────────────────
   NO global :focus-visible rule exists anywhere in the codebase — the
   only focus-visible styling is the per-element footnote-marker tweak in
   §20. So every other interactive element (in-prose links, header / TOC /
   pagination nav, the copy-code button, <summary> toggles, the search and
   AI-composer inputs) relied entirely on the BROWSER-DEFAULT focus ring for
   keyboard navigation — and several controls explicitly null it out with
   `outline: none` (custom.css .blog-ai-input ~1205, .rc-ai-textarea ~2714,
   editorial-tools.css ~480, homepage inputs…), leaving those with NO visible
   keyboard-focus indicator at all (a WCAG 2.4.7 failure). On the near-black
   dark paper the UA default ring is also faint and easy to lose, so a reader
   tabbing through an article in dark mode could not tell where focus was.

   Give dark mode a single, consistent, token-driven focus ring: the language
   accent (sky-blue EN / red ZH, resolved via --color-accent) at a 2px outline
   with a 3px offset so it traces the element without crowding the glyphs. It
   is keyed on :focus-visible, so it appears ONLY for keyboard / programmatic
   focus and never on a mouse click — normal mouse reading is visually
   unchanged. The accent clears the WCAG 3:1 non-text-contrast floor for focus
   indicators comfortably (EN 11.1:1 / ZH 6.30:1 on the paper). Each selector
   is body.dark / [data-theme="dark"]-scoped (specificity ≥ the un-gated
   `outline:none` source rules, and loaded LAST in the extended bundle) so it
   wins in dark and is never selected in light — light mode keeps the browser
   default ring, provably unchanged. */
body.dark a:focus-visible,
body.dark button:focus-visible,
body.dark summary:focus-visible,
body.dark [tabindex]:focus-visible,
body.dark input:focus-visible,
body.dark textarea:focus-visible,
body.dark select:focus-visible,
[data-theme="dark"] a:focus-visible,
[data-theme="dark"] button:focus-visible,
[data-theme="dark"] summary:focus-visible,
[data-theme="dark"] [tabindex]:focus-visible,
[data-theme="dark"] input:focus-visible,
[data-theme="dark"] textarea:focus-visible,
[data-theme="dark"] select:focus-visible {
  outline: 2px solid var(--color-accent, #7dd3fc);
  outline-offset: 3px;
  border-radius: 3px;
}

/* ── 27. Newsletter form status messages (dark) ──────────────────
   The RSS / newsletter sign-up form (layouts/partials/rss-subscribe.html)
   shows a one-line status after submit, coloured by state in custom.css:
   success #15803d, warning #b45309, error #b91c1c. Those three rules are
   NOT theme-gated and the form carries NO dark styling at all, so in dark
   mode the message lands on the near-black page surface (#121413 EN /
   #1c1917 ZH) at: success 3.69:1 / 3.49:1, warning 3.68:1 / 3.48:1,
   error 2.86:1 / 2.70:1 — every state UNDER the WCAG-AA 4.5:1 text floor,
   the error badly so. The feedback the reader most needs to read after
   acting (did my sign-up work?) rendered as a dim, hard-to-parse smudge.
   Repaint each state in the lighter -400 tint of the same hue family the
   rest of the dark palette already uses, so the messages read clearly on
   the dark surface while keeping the green/amber/red semantic coding:
   success #4ade80 → 10.62:1 / 10.04:1, warning #fbbf24 → 11.08:1 / 10.48:1,
   error #f87171 → 6.69:1 / 6.32:1 — all comfortably AA. Each selector is
   body.dark-scoped (specificity 0,3,0 vs the source 0,2,0, and loaded last
   in the extended bundle) so it wins in dark and is never selected in light
   — light mode keeps its darker tints, provably unchanged. */
body.dark .newsletter-form__status[data-state="success"],
[data-theme="dark"] .newsletter-form__status[data-state="success"] { color: #4ade80; }
body.dark .newsletter-form__status[data-state="warning"],
[data-theme="dark"] .newsletter-form__status[data-state="warning"]  { color: #fbbf24; }
body.dark .newsletter-form__status[data-state="error"],
[data-theme="dark"] .newsletter-form__status[data-state="error"]    { color: #f87171; }

/* ── 28. AI-projects sidebar widget heading (dark) ───────────────
   The "AI Projects" sidebar widget (layouts/partials/sidebar-ai-projects.html)
   titles itself with `.widget-ai-projects h3 { color: #6b46c1 }` (custom.css),
   un-gated, on a faint violet/blue gradient that over the dark paper is still
   essentially the dark surface. In dark mode that deep violet resolves to just
   2.88:1 (EN) / 2.72:1 (ZH) — well under the AA 4.5:1 floor, so the widget's
   own heading read as a dark-violet-on-dark smudge. Reuse #a78bfa — the exact
   dark violet this codebase already standardised for the same hue (the AI-tech
   badges/titles, see §22 and custom.css .dark .ai-tech-badge) — so the widget
   heading joins that established violet family: it resolves to 6.80:1 (EN) /
   6.43:1 (ZH), clearing AA. body.dark-scoped (specificity 0,2,1 vs source
   0,1,1, loaded last) so it wins in dark and is never selected in light —
   light mode keeps #6b46c1, provably unchanged. */
body.dark .widget-ai-projects h3,
[data-theme="dark"] .widget-ai-projects h3 {
  color: #a78bfa;
}

/* ── 29. Profile-panel AI icon tint (dark) ───────────────────────
   The home profile panel's AI icon (layouts/partials/index_profile.html)
   paints `.profile_panel__icon--ai { color: #6366f1 }` (indigo-500), un-gated.
   As a non-text glyph it only needs the 3:1 contrast floor, which it clears on
   the dark paper (4.14:1 EN / 3.92:1 ZH) — so this is NOT a failure — but at
   that level the indigo reads dim and slightly muddy against the near-black
   surface. Lift it one step to indigo-400 #818cf8 (6.20:1 EN / 5.86:1 ZH) so
   the icon stays crisp and on-palette with the §28 / §22 violet family. Purely
   a dark-only clarity nudge; body.dark-scoped (0,2,0 vs source 0,1,0, loaded
   last) so light mode keeps #6366f1, provably unchanged. */
body.dark .profile_panel__icon--ai,
[data-theme="dark"] .profile_panel__icon--ai {
  color: #818cf8;
}

/* ── 30. Inline <code> chip — bring into the contained-chip family (dark) ──
   Inline code is one of the most-frequent inline elements in this blog's
   technical prose. The base dark rule (article-typography.css, "Inline Code —
   pill style": `body.dark .post-content :not(pre) > code`) paints a FLAT
   `rgba(255,255,255,0.10)` fill with `border: none`. At 0.10 white over the
   near-black paper that pill is the BRIGHTEST small surface in otherwise-calm
   dark prose — every other lifted surface in the dark design sits far quieter
   and is given a hairline rule to contain it: <kbd> (§11) uses a 0.06 fill +
   a 0.22 token border, <details> (§8) a 0.025 fill + a token border, table
   headers (§2) a 0.05 band. So a paragraph peppered with inline code reads as
   a row of glaring patches that pull the eye off the sentence — the one
   element meant to sit quietly INSIDE the prose shouts above it.

   Bring the inline-code chip into that same contained-chip family: drop the
   fill to a calmer 0.06 lift (matching <kbd>) and add a 1px token rule so the
   chip is defined by its EDGE rather than a bright fill — the standard dark-UI
   idiom, and the move this file already makes for every other inline surface.
   The code text itself stays `var(--color-ink)` (≈14:1 on the paper — set by
   the base rule, untouched here), so legibility and discoverability are
   preserved; only the surrounding glare is calmed. ZH resolves automatically
   via --color-rule. The selector matches the base rule's specificity exactly
   (0,2,1) and loads LAST in the extended bundle, so it wins in dark; light
   mode keeps its `rgba(0,0,0,0.06)` pill, provably unchanged. */
body.dark .post-content :not(pre) > code,
[data-theme="dark"] .post-content :not(pre) > code {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--color-rule, rgba(226, 227, 225, 0.14));
}

/* ── 31. Related-posts card cover glare (dark) ───────────────────
   Every image on an article is already tamed in dark: in-prose images
   (§3, `.post-content img`) and the hero cover (§13,
   `.post-single .entry-cover img`) each get a faint inset edge ring +
   a slight brightness trim so a white-background diagram doesn't read
   as a glaring block on the dark page. The "Related Posts" cards at the
   foot of an article carry their OWN cover strip — `.rpc__cover img`,
   which lives outside both `.post-content` and `.entry-cover`, so
   neither rule reached it. A related post whose cover is a white-canvas
   diagram (e.g. the "Vector Databases" thumbnail) therefore renders as
   a hard, full-brightness white rectangle flush against the calm dark
   `#1a1c1b` card — the single loudest surface in the footer, and the
   one image class the dark pass had missed. Bring it into the same
   already-proven family: a faint inset ring on the cover frame so the
   bright strip has a crisp boundary against the card instead of bleeding
   into it, and the identical brightness/contrast trim used in §3/§13 so
   the glare is contained. True colour returns on hover — and the card's
   existing `transform: scale(1.04)` hover (custom.css) is on a different
   property, so both apply. Dark mode only; light keeps the cover
   untouched, where a white canvas already blends with the light card. */
body.dark .rpc__cover,
[data-theme="dark"] .rpc__cover {
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
body.dark .rpc__cover img,
[data-theme="dark"] .rpc__cover img {
  filter: brightness(0.94) contrast(1.02);
  transition: transform 0.3s ease, filter 200ms ease;
}
body.dark .rpc:hover .rpc__cover img,
[data-theme="dark"] .rpc:hover .rpc__cover img {
  filter: brightness(1) contrast(1);
}

/* ── 32. "Projects" cyan accent UI in dark (AA repair) ────────────
   The site uses a cyan "projects" brand colour, #0e7490 (cyan-700), for a
   cluster of NON-prose UI: the home profile's title accent + terminal
   prompt + "view more" ghost button + featured-post bold lead, the Archives
   hero stat number and the per-year count badge, and the Posts hero count.
   All seven rules paint #0e7490 and are NOT theme-gated, so the same dark cyan
   is reused in dark mode — where it lands on the near-black paper (and, for
   the featured-post lead, on its dark gradient card) (#121413 EN / #1c1917 ZH) at
   just 3.45:1 (EN) / 3.27:1 (ZH), UNDER the WCAG-AA 4.5:1 text floor. So the
   profile's accent, the archive/posts counters and the year badges — several
   of the first numbers a reader's eye lands on — render as a dim cyan-on-dark
   smudge.

   This codebase ALREADY standardised the dark form of this exact cyan: the
   projects badge and the "signal" icon were given color:#22d3ee (cyan-400)
   for dark (custom.css: .dark .posts-badge--projects, .dark .signal-icon).
   These six states were simply missed. Reuse #22d3ee so the cyan reads
   consistently across every projects surface — it resolves to 10.24:1 (EN) /
   9.68:1 (ZH), restoring comfortable AA and matching the established family
   (the same move §22 made for the violet #7c3aed → #a78bfa). The faint chip /
   border tints (rgba(14,116,144,…)) on the year badge and ghost button are
   non-text and already read fine, so only the text colour is repaired.

   !important is carried because two of the source rules (.profile_title__accent,
   .profile_view_more) declare `color: …7490 !important`; an equal-importance
   body.dark rule with higher specificity (0,2,0 vs 0,1,0) wins, and the flag
   is harmless on the other four (none has a dark hover that recolours text).
   Each selector is body.dark / [data-theme="dark"]-scoped and loads LAST in
   the extended bundle, so it wins in dark and is never selected in light —
   light mode keeps #0e7490, provably unchanged. */
body.dark .profile_title__accent,
body.dark .profile_terminal__prompt,
body.dark .profile_view_more,
body.dark .profile_post--featured strong,
body.dark .archives-stat__number,
body.dark .archives-year__count,
body.dark .posts-hero__count,
[data-theme="dark"] .profile_title__accent,
[data-theme="dark"] .profile_terminal__prompt,
[data-theme="dark"] .profile_view_more,
[data-theme="dark"] .profile_post--featured strong,
[data-theme="dark"] .archives-stat__number,
[data-theme="dark"] .archives-year__count,
[data-theme="dark"] .posts-hero__count {
  color: #22d3ee !important;
}

/* ── 33. Section-list tag text in dark (AA repair) ────────────────
   The "AI 技术" and "成长 / Growth" section LIST rows label their tags with a
   flat grey — .ai-tech-list-tags #6b7280 and .growth-list-tags #78716c — and
   neither rule is theme-gated, so the same grey is reused in dark. On the
   near-black paper that lands at 3.83:1 / 3.86:1 respectively — UNDER the AA
   4.5:1 floor for this small (≈0.8rem) tag text, so the tag line reads as a
   barely-there grey smudge under each list title. Repaint with the design's
   muted-ink token (the same family already used for the meta line §21, list
   markers §18, footnotes §7 and struck text §19), which resolves per language
   to #b4bcb2 (EN, 9.49:1) / #a8a29e (ZH, 6.93:1) — comfortably AA and still
   clearly recessed below the list title, so the hierarchy is unchanged. The
   sibling CARD tags already inherit an AA-safe colour, so only the list-row
   tags needed this. body.dark-scoped, loaded last — light mode keeps the
   grey, provably unchanged. */
body.dark .ai-tech-list-tags,
body.dark .growth-list-tags,
[data-theme="dark"] .ai-tech-list-tags,
[data-theme="dark"] .growth-list-tags {
  color: var(--color-ink-muted, #b4bcb2);
}

/* ── 34. Archives "draft" badge in dark (AA repair) ──────────────
   The Archives list flags unpublished entries with a draft badge:
   .archives-post__draft paints amber #b45309 on a faint amber chip
   (background rgba(234,179,8,0.15)), un-gated. In dark the chip is a faint
   amber wash over the near-black paper and the #b45309 text lands at just
   3.68:1 (EN) / 3.49:1 (ZH) — under the AA floor for this small uppercase
   label. Lift the text to amber-400 #fbbf24 — the exact tint §27 already
   uses for the dark "warning" state — so the badge keeps its amber semantic
   while clearing AA at 11.08:1 (EN) / 10.48:1 (ZH) over the paper, and reads
   crisply against its own faint chip. body.dark-scoped, loaded last — light
   mode keeps #b45309, provably unchanged. */
body.dark .archives-post__draft,
[data-theme="dark"] .archives-post__draft {
  color: #fbbf24;
}

/* ── 35. Quiet-UI text on lifted chip surfaces in dark (AA repair) ─
   PaperMod's dark secondary text token is --secondary: rgb(155,156,157)
   (#9b9c9d). It clears AA comfortably where it sits straight on the dark
   paper (#121413 EN / #1c1917 ZH) — e.g. the inline TOC links read at
   ~6.7:1. But two reading-adjacent components paint their quiet text/icons
   over a FAINTLY-LIFTED surface rather than the bare paper: the article
   share bar (.share-btn — resting colour var(--secondary)) and the
   end-of-post tag chips (.post-tags a). The lift (an rgba-white wash on the
   chip / button) raises the local background luminance, so the SAME
   #9b9c9d text drops to just 4.25:1 there — UNDER the WCAG-AA 4.5:1 floor
   for this small UI text. So the share icons and the post tags read as a
   dim grey smudge exactly where a reader reaches to share or browse on.

   Fix at the token level, scoped to just these two component subtrees:
   re-bind --secondary to a HAIR-brighter neutral of the same hue —
   rgb(176,177,178) (#b0b1b2), a proportional lift of the original grey, so
   every var(--secondary) consumer inside the share bar (icons + label) and
   the tag list inherits it at once. That resolves to 5.44:1 on the lifted
   chip and 8.6:1 on the bare paper — comfortably AA in both EN and ZH dark —
   while staying clearly recessed below the primary #dadada text, so the
   visual hierarchy is unchanged. Only text/icon colours key off --secondary
   in these blocks (borders use --border), so nothing non-text shifts; the
   :hover states recolour to var(--primary) and are untouched.

   Scoped to .share-container / .post-tags under body.dark /
   [data-theme="dark"] and loaded last in the extended bundle, so it wins in
   dark and is never selected in light — light mode keeps #9b9c9d, provably
   unchanged. */
body.dark .share-container,
body.dark .post-tags,
[data-theme="dark"] .share-container,
[data-theme="dark"] .post-tags {
  --secondary: rgb(176, 177, 178);
}
