/*
Theme Name: Hanlin Portal Base
Theme URI: https://github.com/meimouren/wordpress-site-builder
Author: Hanlin Education
Author URI: https://github.com/meimouren/wordpress-site-builder
Description: Neutral FSE block theme baseline used by the wordpress-site-builder skill. Ships architecture only — no project-specific palette, copy, chrome, or shortcode body. Each project sets its design via /design-brief and applies it via /build-chrome (parts/header.html + parts/footer.html + chrome.css). Bakes in 90+ lessons from production projects (see docs/lessons-learned.md): pre_get_document_title bypass for wptexturize, dynamic blocks (avoid wpautop), self-hosted fonts + Material Symbols subset, FSE templates, REST binary upload for theme files, admin Site Tools panel (verification meta + analytics + QR), L88 is_home/is_front_page handling, L90 admin bar offset, etc.
Version: 2.0.0
Requires at least: 6.4
Tested up to: 6.6
Requires PHP: 7.4
License: GPL v2 or later
Text Domain: hl-baseline
Tags: full-site-editing, block-styles, education, blog, news, neutral
*/

/* ==========================================================================
   1. Base reset + global
   ========================================================================== */

@import url("assets/fonts.css");

html { scroll-behavior: smooth; }

body,
.wp-site-blocks { overflow-x: clip; }

body {
  background-color: var(--wp--preset--color--background, #F7F9FB);
  color: var(--wp--preset--color--body, #44474B);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-family: var(--wp--preset--font-family--sans-cjk, "Noto Sans SC"), -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Hiragino Sans GB", sans-serif;
}

img,
video {
  max-width: 100%;
  height: auto;
  display: block;
}

a { transition: color 0.2s ease, background-color 0.2s ease, opacity 0.2s ease; }

/* ==========================================================================
   2. Heading size constraints (mobile)
   ========================================================================== */

@media (max-width: 880px) {
  h1.wp-block-heading { font-size: 36px !important; line-height: 1.25 !important; }
  h2.wp-block-heading { font-size: 24px !important; line-height: 1.3 !important; }
  h3.wp-block-heading { font-size: 20px !important; }
}

/* ==========================================================================
   3. WP global padding reset — let template-parts edge-to-edge
   ========================================================================== */

body.wp-site-blocks,
body,
.wp-site-blocks {
  --wp--style--root--padding-top:    0 !important;
  --wp--style--root--padding-bottom: 0 !important;
  --wp--style--root--padding-left:   0 !important;
  --wp--style--root--padding-right:  0 !important;
  --wp--style--block-gap:            0 !important;
}

/* 🔴 L115: This rule strips margin/padding-top|bottom from ALL direct children of
   .wp-site-blocks (every block at the top level of a template: header, wp:html,
   wp:group, etc.). Its purpose is to prevent WP from inserting default block-gap
   between template sections.
   CONSEQUENCE FOR PROJECT CSS: any padding/margin you put on a block that is a
   direct child of .wp-site-blocks (e.g. a <header> inside wp:html, or main.wp:group)
   will be silently zeroed by this rule — even if you write padding:52px in project CSS.
   CORRECT PATTERN: put vertical spacing on an INNER element (one level down), not on
   the block root itself. E.g. for a hero section: .hero{background:...} (no padding),
   .hero-wrap{padding:52px 28px 44px} (padding on the inner container). */
.wp-site-blocks > *,
.wp-site-blocks > .wp-block-template-part {
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}

.wp-block-template-part {
  margin: 0 !important;
  padding: 0 !important;
}

/* ==========================================================================
   4. WP page content container reset — let project section HTML self-own width
       Removes:
       - has-global-padding side effects
       - is-layout-constrained / is-layout-flow auto margins
       - is-layout-flow blockGap (the cause of stray white rows between sections)

   🔴 L111: SCOPED TO body.page ONLY. This reset exists for the full-bleed PAGE
   workflow (pages are full-bleed wp:html whose own *-wrap sections own the width).
   It must NOT apply to single posts / archives — otherwise it force-flattens any
   styled article reading column to full-width with !important, a trap every project
   that styles a single.html otherwise rediscovers. Single posts (body.single) and
   archives keep normal block width so article templates can build a constrained
   reading column / two-column layout.
   ========================================================================== */

body.page .wp-block-post-content,
body.page .wp-block-post-content.has-global-padding,
body.page .wp-block-post-content.is-layout-constrained,
body.page main.wp-block-group.is-layout-flow {
  padding-left: 0 !important;
  padding-right: 0 !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  margin-left: 0 !important;
  margin-right: 0 !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  max-width: 100% !important;
  --wp--style--block-gap: 0 !important;
}

body.page .wp-block-post-content > *,
body.page .wp-block-post-content > section,
body.page main.wp-block-group > *,
body.page main.wp-block-group > .wp-block-post-content,
body.page main.wp-block-group.is-layout-flow > * {
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  margin-block: 0 !important;
}

body.page .wp-block-post-content > section {
  width: 100%;
  max-width: 100%;
  margin-left: 0;
  margin-right: 0;
}

/* ==========================================================================
   5. Full-bleed banner utility (used by project HTML for hero sections)
   ========================================================================== */

.hl-banner-fullbleed {
  width: 100%;
  max-width: 100%;
  margin-left: 0;
  margin-right: 0;
  box-sizing: border-box;
}

/* ==========================================================================
   6. Section background utility classes (neutral · projects override colors)
   ========================================================================== */

.hl-section-white { background-color: var(--wp--preset--color--surface, #FFFFFF); }
.hl-section-soft  { background-color: var(--wp--preset--color--surface-alt, #F2F4F6); }
.hl-section-bg    { background-color: var(--wp--preset--color--background, #F7F9FB); }
.hl-section-ink   { background-color: var(--wp--preset--color--ink, #0F172A); color: var(--wp--preset--color--surface, #FFFFFF); }

/* ==========================================================================
   6b. Mobile-safe horizontal gutter (L121) — 杜绝「列表 / 正文文字贴边」
       ─────────────────────────────────────────────────────────────────────
       ROOT CAUSE this prevents: §3 + §4 zero ALL padding (!important), so the
       baseline is full-bleed by default and every project hand-adds side
       padding to its section wraps. Forgetting the MOBILE case (padding only in
       a desktop @media) = text touches the screen edge. It recurred because the
       gutter was 100% manual with NO baseline backstop.

       FIX — invert the model: a safe side gutter is now the DEFAULT for content
       wraps, so forgetting it is impossible. Intentional edge-to-edge bands opt
       OUT explicitly with .hl-bleed.

       USE: wrap every content section's inner container in `.hl-container`
       (max-width + auto-center + clamp gutter — one declaration covers phone →
       desktop). Even if a project forgets and uses its own `.container` /
       `*-wrap` / `*-inner` class, the mobile backstop below still forces a
       ≥16px gutter so nothing touches the edge.
   ========================================================================== */

.hl-container {
  width: 100%;
  max-width: var(--hl-content-max, 1200px);
  margin-inline: auto;
  padding-inline: clamp(16px, 4vw, 32px);   /* mobile-safe by construction */
  box-sizing: border-box;
}

/* Narrow reading variant (articles / legal / forms) */
.hl-container--narrow { max-width: var(--hl-content-narrow, 760px); }

/* Backstop: on phones, common INNER content wraps always get a ≥16px side
   gutter. Targets inner-wrap naming conventions (NOT full-bleed bg sections),
   so it never indents a section background — only its content. Opt out per
   element with .hl-bleed for intentional full-bleed media / bands. */
@media (max-width: 640px) {
  .hl-container,
  .container:not(.hl-bleed),
  [class$="-wrap"]:not(.hl-bleed),
  [class$="-inner"]:not(.hl-bleed),
  [class$="-shell"]:not(.hl-bleed),
  [class$="-col"]:not(.hl-bleed),
  [class$="-container"]:not(.hl-bleed) {
    padding-left: max(16px, env(safe-area-inset-left, 0px));
    padding-right: max(16px, env(safe-area-inset-right, 0px));
    box-sizing: border-box;
  }
}

/* Article reading column safety net (L121): the post body never touches the edge
   even if a project's single.html relies on a bare constrained layout (which has
   NO padding < its content-size width). Scoped to single/archive so it never
   touches full-bleed body.page sections. Baseline single.html already uses
   .hl-container--narrow; this covers project article templates too. */
body.single .wp-block-post-content,
body.archive .wp-block-post-content {
  padding-inline: clamp(16px, 4vw, 32px);
  box-sizing: border-box;
}
body.single .hl-container .wp-block-post-content,
body.archive .hl-container .wp-block-post-content,
body.single .hl-container--narrow .wp-block-post-content {
  padding-inline: 0;   /* already guttered by the container — avoid double */
}

/* Explicit opt-out: intentional full-bleed band / media (image, color band) */
.hl-bleed { padding-left: 0 !important; padding-right: 0 !important; }

/* ==========================================================================
   7. Long-form article prose · .hl-prose
       Used for legal pages / privacy / about copy / blog post bodies.
       Neutral colors — projects can override via project chrome.css.
   ========================================================================== */

.hl-prose {
  font-family: var(--wp--preset--font-family--sans-cjk, "Noto Sans SC"), -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
  font-size: 17px;
  color: var(--wp--preset--color--heading, #1A1C1E);
  line-height: 1.85;
  letter-spacing: 0.01em;
}

.hl-prose p {
  margin: 0 0 1.5rem;
  color: var(--wp--preset--color--heading, #1A1C1E);
}

.hl-prose h2 {
  font-family: inherit;
  font-size: 26px;
  font-weight: 700;
  color: var(--wp--preset--color--heading, #1A1C1E);
  line-height: 1.4;
  margin: 3rem 0 1.25rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--wp--preset--color--border, #E2E8F0);
}

.hl-prose h2:first-child,
.hl-prose > p:first-child + h2 { margin-top: 1rem; }

.hl-prose h3 {
  font-family: inherit;
  font-size: 20px;
  font-weight: 600;
  color: var(--wp--preset--color--heading, #1A1C1E);
  line-height: 1.4;
  margin: 2rem 0 0.75rem;
}

.hl-prose h4 {
  font-family: inherit;
  font-size: 17px;
  font-weight: 600;
  color: var(--wp--preset--color--heading, #1A1C1E);
  margin: 1.5rem 0 0.5rem;
}

.hl-prose ul,
.hl-prose ol {
  margin: 0 0 1.5rem;
  padding-left: 1.75rem;
}

.hl-prose ul { list-style: disc; }
.hl-prose ol { list-style: decimal; }

.hl-prose ul li::marker { color: var(--wp--preset--color--accent, #2563EB); }
.hl-prose ol li::marker { color: var(--wp--preset--color--accent, #2563EB); font-weight: 700; }

.hl-prose li {
  margin-bottom: 0.6rem;
  line-height: 1.8;
  padding-left: 0.25rem;
}

.hl-prose li > ul,
.hl-prose li > ol { margin: 0.5rem 0; }

.hl-prose strong { color: var(--wp--preset--color--heading, #1A1C1E); font-weight: 700; }

.hl-prose em { font-style: italic; color: var(--wp--preset--color--body, #44474B); }

.hl-prose a {
  color: var(--wp--preset--color--accent, #2563EB);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  transition: color 0.2s, text-decoration-color 0.2s;
  font-weight: 600;
}

.hl-prose a:hover {
  color: var(--wp--preset--color--heading, #1A1C1E);
  text-decoration-color: var(--wp--preset--color--heading, #1A1C1E);
}

.hl-prose blockquote {
  border-left: 4px solid var(--wp--preset--color--accent, #2563EB);
  padding: 1.5rem 1.75rem;
  margin: 2rem 0;
  background-color: var(--wp--preset--color--surface-alt, #F2F4F6);
  border-radius: 0 8px 8px 0;
  font-style: italic;
  color: var(--wp--preset--color--heading, #1A1C1E);
  font-size: 16px;
  line-height: 1.75;
}

.hl-prose blockquote p { margin: 0; }
.hl-prose blockquote p + p { margin-top: 0.75rem; }

.hl-prose hr {
  border: 0;
  height: 1px;
  background: linear-gradient(to right, transparent, var(--wp--preset--color--border, #E2E8F0) 20%, var(--wp--preset--color--border, #E2E8F0) 80%, transparent);
  margin: 3rem 0;
}

.hl-prose code {
  font-family: "SF Mono", Consolas, Monaco, monospace;
  background-color: var(--wp--preset--color--surface-alt, #F2F4F6);
  color: var(--wp--preset--color--accent, #2563EB);
  padding: 2px 6px;
  border-radius: 4px;
  font-size: 0.9em;
  border: 1px solid var(--wp--preset--color--border, #E2E8F0);
}

.hl-prose pre {
  background: var(--wp--preset--color--ink, #0F172A);
  color: var(--wp--preset--color--surface, #FFFFFF);
  padding: 1.5rem;
  border-radius: 8px;
  overflow-x: auto;
  margin: 1.5rem 0;
  font-size: 14px;
  line-height: 1.6;
}

.hl-prose pre code {
  background: none;
  color: inherit;
  border: 0;
  padding: 0;
}

.hl-prose table {
  width: 100%;
  border-collapse: collapse;
  margin: 1.5rem 0;
  font-size: 15px;
}

.hl-prose th,
.hl-prose td {
  padding: 0.85rem 1rem;
  border: 1px solid var(--wp--preset--color--border, #E2E8F0);
  text-align: left;
  vertical-align: top;
}

.hl-prose th {
  background: var(--wp--preset--color--ink, #0F172A);
  color: #fff;
  font-weight: 700;
  font-family: var(--wp--preset--font-family--inter, "Inter"), "Noto Sans SC", sans-serif;
  font-size: 14px;
  letter-spacing: 0.04em;
}

.hl-prose tr:nth-child(even) td {
  background: var(--wp--preset--color--background, #F7F9FB);
}

/* First paragraph as "updated on: ..." meta · weakened style.
   🔴 L114: SCOPED TO body.page ONLY. On legal/about PAGES the first prose paragraph
   is a "Last updated: …" caption (weakened + separator). It must NOT apply to single
   POSTS — there the first paragraph is the article LEAD, and this rule would shrink it
   to 14px gray + add a stray hairline. Same trap family as L111/L112 (page-workflow
   rule leaking onto posts). Articles style their own lead via the project prose class. */
body.page .hl-prose > p:first-child {
  color: var(--wp--preset--color--muted, #75777C) !important;
  font-size: 14px !important;
  margin-bottom: 2rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--wp--preset--color--border, #E2E8F0);
}

@media (max-width: 640px) {
  .hl-prose { font-size: 16px; line-height: 1.8; }
  .hl-prose h2 { font-size: 22px; margin-top: 2.25rem; }
  .hl-prose h3 { font-size: 18px; }
}

/* ==========================================================================
   8. Section underline (decorative · projects can override color)
   ========================================================================== */

.hl-section-underline {
  width: 64px;
  height: 4px;
  border-radius: 2px;
  background-color: var(--wp--preset--color--accent, #2563EB);
  margin: 0 auto;
}

/* ==========================================================================
   9. Floating TOC · left-gutter overlay (L110 · behavior locked in theme.js)
      - position:fixed overlay in the reading column's LEFT gutter — it never
        participates in content width and never shifts the prose (user spec:
        「不要计算到正文的宽度中，不要影响正文的排版」)
      - anchored to a 760px centered reading column via calc():
          column left edge   = 50% - 380px
          TOC right edge sits 28px to its left → left = 50% - 380 - width - 28
      - element + auto-gen + scrollspy + show/hide range all driven by theme.js
        (.is-hidden fades it out outside the article body range)
      - shown only ≥ 1240px (narrower viewports have no gutter room → hidden)
      - all colors via theme.json CSS vars; projects restyle via chrome.css
   ========================================================================== */

.hl-toc-float {
  --hl-toc-width: 200px;
  --hl-reading-half: 380px; /* half of the 760px reading column */
  display: none;            /* shown by the ≥1240px media query below */
  position: fixed;
  top: 120px;
  left: calc(50% - var(--hl-reading-half) - var(--hl-toc-width) - 28px);
  width: var(--hl-toc-width);
  max-height: calc(100vh - 180px);
  overflow-y: auto;
  z-index: 40;
  padding: 4px 0;
  transition: opacity 0.3s ease;
  opacity: 1;
}

/* JS adds .is-hidden when scrolled above the article body or past its end */
.hl-toc-float.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.hl-toc-head {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--wp--preset--color--muted, #75777C);
  margin: 0 0 12px;
  padding-left: 14px;
}

.hl-toc-list { list-style: none; padding: 0; margin: 0; }

.hl-toc-item {
  margin: 2px 0;
  border-left: 2px solid var(--wp--preset--color--border, #E2E8F0);
  transition: border-color 0.2s ease;
}
.hl-toc-item.hl-toc-h3 { padding-left: 14px; }

.hl-toc-item a {
  display: block;
  color: var(--wp--preset--color--body, #44474B);
  opacity: 0.7;
  text-decoration: none;
  font-size: 12.5px;
  line-height: 1.4;
  padding: 5px 0 5px 14px;
  transition: color 0.15s ease, opacity 0.15s ease;
}
.hl-toc-item a:hover { color: var(--wp--preset--color--heading, #1A1C1E); opacity: 1; }

/* Scrollspy active state — accent border + bold (border on same side as the
   container rail so it's always visible · F18 / L54 defense) */
.hl-toc-item.is-active {
  border-left-color: var(--wp--preset--color--accent, #2563EB);
}
.hl-toc-item.is-active > a {
  color: var(--wp--preset--color--heading, #1A1C1E);
  opacity: 1;
  font-weight: 700;
}

/* thin, hover-only scrollbar (F18 defense — no permanent thumb) */
.hl-toc-float { scrollbar-width: thin; scrollbar-color: transparent transparent; }
.hl-toc-float::-webkit-scrollbar { width: 4px; }
.hl-toc-float::-webkit-scrollbar-thumb { background: transparent; border-radius: 2px; }
.hl-toc-float:hover::-webkit-scrollbar-thumb { background: var(--wp--preset--color--border, #E2E8F0); }

@media (min-width: 1240px) {
  .hl-toc-float { display: block; }
}

/* ==========================================================================
   10. Back to top button · neutral, projects override colors
   ========================================================================== */

.hl-back-to-top {
  position: fixed;
  right: 24px;
  bottom: 24px;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--wp--preset--color--ink, #0F172A);
  color: var(--wp--preset--color--surface, #FFFFFF);
  border: 0;
  cursor: pointer;
  display: none;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  z-index: 40;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  transition: opacity 0.3s, transform 0.2s;
}

.hl-back-to-top:hover { transform: translateY(-2px); }
.hl-back-to-top.is-visible { display: inline-flex; }

/* ==========================================================================
   10b. Contact CTA card (L119) · .hl-article-cta
        Auto-injected on post detail (functions.php §17) + [hl_contact_cta].
        Neutral / CSS-var driven; projects restyle via chrome.css. All copy /
        QR / button come from Site Tools — never hardcoded in page HTML.
   ========================================================================== */

.hl-article-cta {
  display: flex;
  flex-direction: column;
  gap: 24px;
  background: var(--wp--preset--color--ink, #0F172A);
  color: var(--wp--preset--color--surface, #FFFFFF);
  border-radius: 16px;
  padding: 28px;
  margin: 40px 0;
}
.hl-article-cta-body { flex: 1; min-width: 0; }
.hl-article-cta-title { margin: 0 0 8px; font-size: 22px; font-weight: 800; line-height: 1.2; color: #fff; }
.hl-article-cta-sub { margin: 0 0 14px; font-size: 14px; line-height: 1.5; opacity: 0.82; }
.hl-article-cta-bullets { list-style: none; padding: 0; margin: 0 0 16px; font-size: 13px; line-height: 1.6; opacity: 0.92; }
.hl-article-cta-bullets li { position: relative; padding-left: 18px; margin-bottom: 4px; }
.hl-article-cta-bullets li::before { content: "\2713"; position: absolute; left: 0; color: var(--wp--preset--color--accent, #2563EB); font-weight: 700; }
.hl-article-cta-qr { flex-shrink: 0; text-align: center; }
.hl-cta-qr-img { width: 200px; height: 200px; object-fit: cover; background: #fff; border-radius: 8px; padding: 6px; box-sizing: border-box; display: block; }
.hl-article-cta-note { margin: 8px 0 0; font-size: 12px; opacity: 0.7; }

@media (min-width: 640px) {
  .hl-article-cta { flex-direction: row; align-items: center; gap: 32px; }
}

/* Online-contact button (L116) · used by CTA card + footer + section templates */
.hl-cta-online-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--wp--preset--color--accent, #2563EB);
  color: #FFFFFF;
  padding: 12px 24px;
  border-radius: 9999px;
  font-size: 15px;
  font-weight: 700;
  text-decoration: none;
  transition: opacity 0.15s ease;
}
.hl-cta-online-btn:hover { opacity: 0.88; }

/* ==========================================================================
   11. Breadcrumb · neutral
   ========================================================================== */

.hl-breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 14px;
  font-family: var(--wp--preset--font-family--inter, "Inter"), sans-serif;
  font-size: 14px;
  color: var(--wp--preset--color--body, #44474B);
}

.hl-breadcrumb a {
  color: var(--wp--preset--color--body, #44474B);
  text-decoration: none;
}

.hl-breadcrumb a:hover { color: var(--wp--preset--color--heading, #1A1C1E); }
.hl-breadcrumb .sep { font-size: 12px; opacity: 0.5; }
.hl-breadcrumb-current {
  color: var(--wp--preset--color--heading, #1A1C1E);
  font-weight: 600;
}

/* ==========================================================================
   12. Skip-to-content (a11y · L47 baseline-required)
   ========================================================================== */

.hl-skip-link {
  position: absolute;
  left: -9999px;
  top: 8px;
  z-index: 99999;
  background: var(--wp--preset--color--ink, #0F172A);
  color: #fff;
  padding: 12px 16px;
  text-decoration: none;
  font-size: 14px;
  border-radius: 4px;
}

.hl-skip-link:focus {
  left: 16px;
  outline: 2px solid var(--wp--preset--color--accent, #2563EB);
  outline-offset: 2px;
}

/* ==========================================================================
   13. Focus-visible (a11y · L47 baseline-required)
   ========================================================================== */

:focus-visible {
  outline: 2px solid var(--wp--preset--color--accent, #2563EB);
  outline-offset: 2px;
  border-radius: 2px;
}

/* ==========================================================================
   14. Single-post prev/next nav · CARD style (L123 — was plain links, looked
       unfinished on every project). Neutral; recolor via chrome.css.
   ========================================================================== */

.hl-post-nav {
  border-top: 1px solid var(--wp--preset--color--border, #E2E8F0);
  /* spacing 放 CSS 层（L123 v2.1.1）：手写模板里块属性 JSON 的 margin/padding
     不会被 WP 回填成行内样式 → 之前卡片贴着分隔线。CSS 兜底对所有站生效。 */
  margin-top: 48px;
  padding-top: 24px;
}
/* 没有上一篇/下一篇时 WP 仍输出空容器 → 别让它渲染成一个空边框药丸 */
.hl-post-nav .wp-block-post-navigation-link:empty {
  display: none;
  border: 0;
  padding: 0;
}
.hl-post-nav .wp-block-post-navigation-link {
  flex: 1 1 240px;
  max-width: 100%;
  font-size: 0.95rem;
  background: var(--wp--preset--color--surface, #FFFFFF);
  border: 1px solid var(--wp--preset--color--border, #E2E8F0);
  border-radius: 12px;
  padding: 14px 18px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.hl-post-nav .wp-block-post-navigation-link:hover {
  border-color: var(--wp--preset--color--accent, #2563EB);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
}
.hl-post-nav .post-navigation-link-next { text-align: right; }
.hl-post-nav .wp-block-post-navigation-link a {
  text-decoration: none;
  color: var(--wp--preset--color--heading, #1A1C1E);
  font-weight: 600;
  line-height: 1.4;
}
.hl-post-nav .wp-block-post-navigation-link a:hover { text-decoration: none; color: var(--wp--preset--color--accent, #2563EB); }

/* ==========================================================================
   15. News list + archive (L123) · used by templates/index.html + archive.html
       The posts page / tag archive ship editorial OUT OF THE BOX — no more
       bare-loop "最基础排版" lists. Projects recolor via chrome.css only.
   ========================================================================== */

.hl-archive-head { margin-bottom: 8px; }
.hl-archive-title {
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--wp--preset--color--heading, #1A1C1E);
  margin: 0 0 8px;
}
.hl-archive-desc { color: var(--wp--preset--color--muted, #75777C); font-size: 15px; margin: 0; }

.hl-news-list { list-style: none; padding: 0; margin: 0; }
.hl-news-list > li {
  margin: 0;
  padding: 26px 0;
  border-bottom: 1px solid var(--wp--preset--color--border, #E2E8F0);
}
.hl-news-list > li:first-child { padding-top: 8px; }

.hl-news-date {
  font-size: 13px;
  color: var(--wp--preset--color--muted, #75777C);
  margin: 0 0 6px;
}
.hl-news-title {
  font-size: 20px;
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: -0.005em;
  margin: 0 0 8px;
}
.hl-news-title a {
  color: var(--wp--preset--color--heading, #1A1C1E);
  text-decoration: none;
  transition: color 0.15s ease;
}
.hl-news-title a:hover { color: var(--wp--preset--color--accent, #2563EB); }
.hl-news-excerpt p { margin: 0; font-size: 15px; line-height: 1.65; color: var(--wp--preset--color--body, #44474B); }
.hl-news-excerpt .wp-block-post-excerpt__more-link { display: none; }
.hl-news-empty { color: var(--wp--preset--color--muted, #75777C); padding: 32px 0; }

/* date | title/excerpt two-column row on wide screens */
@media (min-width: 720px) {
  .hl-news-list > li {
    display: grid;
    grid-template-columns: 150px 1fr;
    column-gap: 28px;
  }
  .hl-news-date     { grid-column: 1; grid-row: 1; padding-top: 4px; }
  .hl-news-title    { grid-column: 2; grid-row: 1; }
  .hl-news-excerpt  { grid-column: 2; grid-row: 2; }

  /* L132: items WITH a featured thumbnail switch to cover | text and let the
     cover span all three text rows. `:has()` means image-less items keep the
     date-left layout above — no empty 150px gap. (Per-site chrome.css overrides
     this entirely to build cards / magazine / timeline.) */
  .hl-news-list > li:has(.hl-news-cover) { grid-template-columns: 200px 1fr; }
  .hl-news-list > li:has(.hl-news-cover) .hl-news-cover    { grid-column: 1; grid-row: 1 / 4; }
  .hl-news-list > li:has(.hl-news-cover) .hl-news-date     { grid-column: 2; grid-row: 1; padding-top: 0; }
  .hl-news-list > li:has(.hl-news-cover) .hl-news-title    { grid-column: 2; grid-row: 2; }
  .hl-news-list > li:has(.hl-news-cover) .hl-news-excerpt  { grid-column: 2; grid-row: 3; }
}

.hl-pagination { margin-top: 36px; font-size: 14px; }
.hl-pagination a { color: var(--wp--preset--color--heading, #1A1C1E); text-decoration: none; font-weight: 600; }
.hl-pagination a:hover { color: var(--wp--preset--color--accent, #2563EB); }
.hl-pagination .page-numbers.current { color: var(--wp--preset--color--accent, #2563EB); font-weight: 700; }

/* --------------------------------------------------------------------------
   15b. Featured images (L132) · list thumbnail + article cover masthead.
   These ship in the markup (templates/index.html, archive.html, single.html)
   and render nothing when a post has no thumbnail. The defaults below are a
   tasteful baseline; per-site chrome.css restyles them into cover-hero / card /
   magazine layouts. Native ratio via object-fit so images never stretch (L83).
   -------------------------------------------------------------------------- */
.hl-news-cover { margin: 0 0 12px; display: block; }
.hl-news-cover img {
  width: 100%; height: auto; aspect-ratio: 16 / 10;
  object-fit: cover; border-radius: 10px; display: block;
}
@media (min-width: 720px) { .hl-news-cover { margin: 0; } }

.hl-article-head { margin-bottom: 4px; }
.hl-article-cover { margin: 0 0 22px; display: block; }
.hl-article-cover img {
  width: 100%; height: auto; max-height: 460px;
  object-fit: cover; border-radius: 12px; display: block;
}

/* ==========================================================================
   16. Article meta chips + tag chips + related cards (L123 · auto-rendered by
       functions.php; no template / page code). Neutral; recolor via chrome.css.
   ========================================================================== */

/* meta row extras appended inside the date block: · Category · N min read */
.hl-post-meta-extra { display: inline; }
.hl-post-meta-sep { margin: 0 8px; opacity: 0.5; }
.hl-post-cat {
  display: inline-block;
  padding: 1px 10px;
  border-radius: 999px;
  background: var(--wp--preset--color--surface-alt, #F2F4F6);
  color: var(--wp--preset--color--heading, #1A1C1E);
  font-size: 12px;
  font-weight: 600;
  text-decoration: none;
}
.hl-post-cat:hover { background: var(--wp--preset--color--accent-soft, #DBEAFE); }
.hl-post-readtime { font-size: 13px; }

/* tag chips under the article body (wp:post-terms · empty when no tags) */
.hl-post-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 32px;
  padding-top: 20px;
  border-top: 1px solid var(--wp--preset--color--border, #E2E8F0);
  font-size: 13px;
}
.hl-post-tags a {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--wp--preset--color--surface-alt, #F2F4F6);
  border: 1px solid var(--wp--preset--color--border, #E2E8F0);
  color: var(--wp--preset--color--body, #44474B);
  font-weight: 500;
  text-decoration: none;
  transition: all 0.15s ease;
}
.hl-post-tags a::before { content: "#"; margin-right: 2px; opacity: 0.6; }
.hl-post-tags a:hover {
  border-color: var(--wp--preset--color--accent, #2563EB);
  color: var(--wp--preset--color--heading, #1A1C1E);
}

/* related posts ("More to read") */
.hl-related { margin: 44px 0 8px; }
.hl-related-head {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--wp--preset--color--muted, #75777C);
  padding-top: 24px;
  border-top: 1px solid var(--wp--preset--color--border, #E2E8F0);
  margin: 0 0 16px;
}
.hl-related-grid {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.hl-related-card {
  display: block;
  background: var(--wp--preset--color--surface, #FFFFFF);
  border: 1px solid var(--wp--preset--color--border, #E2E8F0);
  border-radius: 12px;
  padding: 16px;
  text-decoration: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.hl-related-card:hover {
  border-color: var(--wp--preset--color--accent, #2563EB);
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
}
.hl-related-date { display: block; font-size: 12px; color: var(--wp--preset--color--muted, #75777C); margin-bottom: 6px; }
.hl-related-title {
  display: block;
  font-size: 15px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--wp--preset--color--heading, #1A1C1E);
  margin-bottom: 6px;
}
.hl-related-x { display: block; font-size: 13px; line-height: 1.55; color: var(--wp--preset--color--body, #44474B); }

/* ==========================================================================
   17. Prose-defense for injected furniture (L123 · fixes the USNCO screenshot)
       The CTA / related / tags are injected INSIDE .wp-block-post-content
       .hl-prose, so prose typography (dark h3 color, accent underlined links,
       list markers, paragraph margins) bleeds into them — the CTA title turned
       dark-on-dark and the button rendered as an underlined purple link.
       These higher-specificity rules restore the furniture's own look.
   ========================================================================== */

.hl-prose .hl-article-cta-title { color: #FFFFFF; margin: 0 0 8px; padding: 0; border: 0; }
.hl-prose .hl-article-cta p { color: #FFFFFF; }
.hl-prose .hl-article-cta-sub { opacity: 0.82; margin: 0 0 14px; font-size: 14px; }
.hl-prose .hl-article-cta-note { opacity: 0.7; margin: 8px 0 0; font-size: 12px; }
.hl-prose .hl-article-cta a { color: #FFFFFF; text-decoration: none; }
.hl-prose .hl-cta-online-btn { color: #FFFFFF; text-decoration: none; font-weight: 700; }
.hl-prose .hl-cta-online-btn:hover { color: #FFFFFF; text-decoration: none; }
.hl-prose .hl-article-cta-bullets { list-style: none; padding-left: 0; margin: 0 0 16px; }
.hl-prose .hl-article-cta-bullets li { padding-left: 18px; margin-bottom: 4px; }
.hl-prose .hl-article-cta-bullets li::marker { content: ""; }
.hl-prose .hl-related a,
.hl-prose .hl-related-card { text-decoration: none; }
.hl-prose .hl-related-title { color: var(--wp--preset--color--heading, #1A1C1E); }
.hl-prose .hl-related-x { color: var(--wp--preset--color--body, #44474B); }

/* ==========================================================================
   18. 3D effect hosts (L125 · assets/hl-3d.js, lazy-loaded by theme.js)
       <section class="hl-3d-host"> + <div class="hl-3d-bg" data-hl-3d="…">
       behind + <div class="hl-3d-content"> in front. Canvas never captures
       clicks; content stacks above it.
   ========================================================================== */

.hl-3d-host { position: relative; overflow: hidden; }
.hl-3d-bg { position: absolute; inset: 0; pointer-events: none; }
.hl-3d-bg canvas.hl-3d-canvas,
.hl-3d-host > canvas.hl-3d-canvas {
  position: absolute;
  inset: 0;
  width: 100% !important;
  height: 100% !important;
  display: block;
  pointer-events: none;
}
.hl-3d-content { position: relative; z-index: 1; }

/* ==========================================================================
   19. Template-part phantom line-box fix (L131)
   A `wp:html` template part (parts/header.html, parts/footer.html) keeps a
   whitespace text node before the <header>/<footer> element — the block-marker
   comment plus source indentation. The wrapper's inherited line-height turns
   that whitespace into a ~26px phantom line box, leaking a body-coloured sliver
   UNDER the fixed header and ABOVE the footer (looks like a chrome gap bug, but
   it is the wrapper, not the chrome). Zero line-height on the wrapper; restore
   it on the direct child so the real chrome text is untouched. Neutral + global
   so every derived project inherits the fix without re-diagnosing it.
   ========================================================================== */
.wp-block-template-part { line-height: 0; }
.wp-block-template-part > * { line-height: normal; }
/* Also collapse WP root flow-gap between header-part / main / footer-part. */
.wp-site-blocks > * { margin-block-start: 0; margin-block-end: 0; }
