/*
Theme Name: Bursa Decode
Theme URI: https://bursadecode.com
Author: Bursa Decode
Description: 马股解码 (Bursa Decode) 自定义经典主题 —— 深藏青 header/footer + 米白正文的三明治结构，姜黄金强调色，编辑质感的大马华人投资内容站。首页动态抓取最新文章，兼容 Paid Memberships Pro。
Version: 1.1.0
Requires at least: 6.0
Requires PHP: 7.4
Text Domain: bursa-decode
*/

/* =========================================================
   1. Design tokens
   ========================================================= */
/*
   Two-surface system:
   - LIGHT (default, :root) — used by the reading area: hero, article
     grids, single articles, static pages, forms, footer widgets area.
   - DARK — scoped to .site-header / .site-footer only, via a local
     custom-property override further down. Every component below just
     reads var(--color-*); which physical colour it gets depends on
     which surface it's nested inside. This means header/footer content
     (nav links, account link, footer nav, site title) automatically
     get the right colours without needing separate dark-mode rules per
     component.
*/
:root {
  --color-bg:            #F2EFE6; /* 米白：正文区背景 */
  --color-surface:       #FFFFFF; /* 卡片/输入框等"纸面"，比页面背景更白，制造层次 */
  --color-surface-2:     #FBF7EF; /* 再一层，用于图片占位/hover */
  --color-border:        rgba(13, 23, 48, 0.12);
  --color-border-strong: rgba(156, 107, 18, 0.45);

  --color-text:          #0D1730; /* 深藏青：正文字 */
  --color-text-muted:    rgba(13, 23, 48, 0.68);
  --color-text-faint:    rgba(13, 23, 48, 0.48);

  --color-accent:        #D9A441; /* 姜黄金：按钮/分隔线等色块填色，任何背景下都用这个 */
  --color-accent-strong: #C08A2E; /* 金色按钮 hover，浅底上稍微加深显得"按下去"了 */
  --color-accent-text:   #805A12; /* 姜黄金"文字/链接"专用色——浅底上纯金色对比度不够，这个深琥珀色够清楚，配色家族不变 */
  --color-accent-ink:    #2A1D07; /* 金色底色上使用的深色文字，两种背景通用 */

  --font-serif: "Noto Serif SC", "Noto Serif TC", "PingFang TC", "PingFang SC",
                "Microsoft JhengHei", "Microsoft YaHei", Georgia, serif;
  --font-sans: "Noto Sans SC", "PingFang SC", "Microsoft YaHei",
               "Helvetica Neue", Arial, sans-serif;

  --container-width: 1160px;
  --container-pad: 24px;

  --radius-sm: 3px;
  --radius-md: 6px;

  --shadow-card: 0 1px 2px rgba(13, 23, 48, 0.06), 0 12px 24px -14px rgba(13, 23, 48, 0.22);

  /* PMPro's own markup (e.g. the checkout password-show/hide icon) reads
     this custom property directly in an inline SVG stroke attribute.
     We dequeue PMPro's base.css (see section 17), which is where it
     would normally be defined, so without this the icon falls back to
     an unstyled default instead of the site's gold accent. Name is
     "--pmpro--color--accent" — TWO dashes before "accent", matching
     PMPro's own base.css exactly (paid-memberships-pro/css/frontend/
     base.css). A single-dash version here previously silently defined
     a different, unused variable — every password-toggle eye icon on
     the site (login, change-password) was rendering with no stroke
     colour at all as a result, not just an unthemed one. */
  --pmpro--color--accent: var(--color-accent);
}

/* Dark surface: header + footer bookend the page, and the homepage
   headline ticker (§20) borrows the same dark band. Re-declaring the
   same custom-property names here means every rule elsewhere in this
   file that reads var(--color-bg)/var(--color-text)/etc. "just works"
   inside these elements without any component needing a separate dark
   variant written out by hand. (The hero briefly joined this group too
   — dark hero + AI background image — but that direction didn't land
   after several rounds of feedback; reverted to the plain cream hero
   below.) */
.site-header,
.site-footer,
.headline-ticker {
  --color-bg:            #0D1730;
  --color-surface:       #142140;
  --color-surface-2:     #1A2A4E;
  --color-border:        rgba(242, 239, 230, 0.12);
  --color-border-strong: rgba(217, 164, 65, 0.35);

  --color-text:          #F2EFE6;
  --color-text-muted:    rgba(242, 239, 230, 0.68);
  --color-text-faint:    rgba(242, 239, 230, 0.45);

  --color-accent-strong: #E8B95E;
  --color-accent-text:   #D9A441; /* on navy, plain gold is already readable — no need to darken */

  background: var(--color-bg);
  color: var(--color-text);
}

/* =========================================================
   2. Reset
   ========================================================= */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: 17px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

img, svg { max-width: 100%; height: auto; display: block; }

a { color: inherit; text-decoration: none; }

ul, ol { padding: 0; margin: 0; list-style: none; }

button, input, textarea, select { font-family: inherit; font-size: inherit; color: inherit; }

button { cursor: pointer; background: none; border: none; }

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-serif);
  font-weight: 700;
  line-height: 1.35;
  margin: 0 0 0.6em;
  color: var(--color-text);
  letter-spacing: 0.01em;
}

p { margin: 0 0 1.3em; }

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

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10000;
  background: var(--color-accent);
  color: var(--color-accent-ink);
  padding: 12px 20px;
  font-weight: 600;
}
.skip-link:focus { left: 12px; top: 12px; }

/* =========================================================
   3. Layout
   ========================================================= */
.container {
  width: 100%;
  max-width: var(--container-width);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.site { display: flex; flex-direction: column; min-height: 100vh; }
.site-content { flex: 1 0 auto; background: var(--color-bg); }

.eyebrow {
  display: inline-block;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-accent-text);
}

.rule {
  border: none;
  border-top: 1px solid var(--color-border);
  margin: 48px 0;
}

.rule--accent {
  border-top: 2px solid var(--color-accent);
  width: 56px;
  margin: 20px 0 32px;
}

/* =========================================================
   4. Header / Navigation
   ========================================================= */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(13, 23, 48, 0.92);
  backdrop-filter: saturate(140%) blur(8px);
  border-bottom: 1px solid var(--color-border);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: nowrap;
  gap: 24px;
  padding-block: 18px;
}

/* Branding must never be squeezed or allowed to balloon: fixed on its
   own intrinsic size so a stray oversized logo can't eat the row and
   force the nav into a sliver (which makes Chinese nav labels wrap
   character-by-character — a very easy accident to reintroduce). */
.site-branding { display: flex; align-items: baseline; gap: 10px; flex: 0 0 auto; min-width: 0; }

.site-branding a { display: flex; align-items: baseline; gap: 10px; }

.site-branding img,
.site-branding .custom-logo { max-height: 40px; width: auto; }


.site-title {
  font-family: var(--font-serif);
  font-weight: 700;
  font-size: 22px;
  letter-spacing: 0.02em;
  margin: 0;
  color: var(--color-text);
}

.site-title-en {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent-text);
}

.primary-nav-wrap { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; justify-content: flex-end; min-width: 0; }

.nav-menu {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 4px;
}

.nav-menu a {
  display: inline-block;
  padding: 8px 16px;
  font-size: 15px;
  font-weight: 500;
  color: var(--color-text-muted);
  border-radius: var(--radius-sm);
  white-space: nowrap;
  transition: color .15s ease, background-color .15s ease;
}

.nav-menu a:hover,
.nav-menu .current-menu-item > a {
  color: var(--color-accent-text);
  background: rgba(217, 164, 65, 0.08);
}

/* Dropdown — "最新文章" carries the site's categories as real child
   menu items (wp_nav_menu depth is 2 now, was 1). .nav-menu li needs
   position:relative as the anchor for the absolutely-positioned
   .sub-menu; everything else here is inside .site-header's dark-scope
   (§1), so var(--color-surface) etc. already resolve to the navy
   palette without a separate dark variant. */
.nav-menu li { position: relative; }

.nav-menu .menu-item-has-children > a { display: inline-flex; align-items: center; gap: 5px; }
.nav-menu .menu-item-has-children > a::after {
  content: "";
  width: 7px;
  height: 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  opacity: .65;
  transform: rotate(45deg) translateY(-2px);
}

.nav-menu .sub-menu {
  position: absolute;
  top: 100%;
  left: 0;
  min-width: 180px;
  margin-top: 6px;
  padding: 8px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity .15s ease, transform .15s ease, visibility .15s;
  z-index: 10;
}
.nav-menu li:hover > .sub-menu,
.nav-menu li:focus-within > .sub-menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.nav-menu .sub-menu a {
  display: block;
  padding: 9px 12px;
  font-size: 14px;
  color: var(--color-text-muted);
  white-space: nowrap;
}
.nav-menu .sub-menu a:hover { color: var(--color-accent-text); background: rgba(217, 164, 65, 0.08); }

/* Search icon + dropdown panel — nested inside .site-header's dark
   scope (§1), so var(--color-surface) etc. below resolve to the navy
   palette automatically, same as .nav-menu .sub-menu does. */
.header-search { position: relative; }

.header-search__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  flex: none;
  color: var(--color-text-muted);
  transition: color .15s ease, background-color .15s ease;
}
.header-search__toggle:hover,
.header-search__toggle[aria-expanded="true"] {
  color: var(--color-accent-text);
  background: rgba(217, 164, 65, 0.08);
}

/* Panel was inheriting .site-header's own dark scope (§1), so its
   background (--color-surface, #142140) sat almost on top of the
   header's own navy (#0D1730 at 92% opacity) with barely any contrast
   — and the search input inside used that same --color-surface too,
   so input and panel were literally the same colour. Re-declaring the
   LIGHT token values here (the reverse of the usual dark-on-light
   trick like .ebook-cover uses) gives a cream panel with a white input
   popping out of it — same light hierarchy as everywhere else on the
   site, just flipped onto a dark-header context. */
.header-search__panel {
  --color-bg:            #F2EFE6;
  --color-surface:       #FFFFFF;
  --color-border:        rgba(13, 23, 48, 0.12);
  --color-border-strong: rgba(156, 107, 18, 0.45);
  --color-text:          #0D1730;
  --color-accent-text:   #805A12;

  position: absolute;
  top: 100%;
  right: 0;
  margin-top: 10px;
  width: 280px;
  max-width: calc(100vw - 32px);
  padding: 14px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: 0 20px 40px -16px rgba(13, 23, 48, 0.35);
  z-index: 20;
}
.header-search__panel .search-form { gap: 8px; }
.header-search__panel .search-form input[type="search"] { padding: 10px 12px; font-size: 14px; }
.header-search__panel .search-form button { padding: 10px 16px; font-size: 14px; }

.nav-toggle {
  display: none;
  width: 40px;
  height: 40px;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-sm);
}

.nav-toggle-bars,
.nav-toggle-bars::before,
.nav-toggle-bars::after {
  content: "";
  display: block;
  width: 20px;
  height: 2px;
  background: var(--color-text);
  transition: transform .2s ease, opacity .2s ease;
}
.nav-toggle-bars::before { transform: translateY(-6px); }
.nav-toggle-bars::after { transform: translateY(4px); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::before { transform: translateY(0) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::after { transform: translateY(-2px) rotate(-45deg); }

.account-link a {
  display: inline-block;
  white-space: nowrap; /* never let 登录 / 会员账户 break into one character per line on a narrow header */
  font-size: 14px;
  font-weight: 600;
  color: var(--color-accent-text);
  border: 1px solid var(--color-border-strong);
  padding: 7px 16px;
  border-radius: var(--radius-sm);
  transition: background-color .15s ease, color .15s ease;
}
.account-link a:hover { background: var(--color-accent); color: var(--color-accent-ink); }

/* =========================================================
   5. Buttons
   ========================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 26px;
  font-size: 15px;
  font-weight: 600;
  border-radius: var(--radius-sm);
  text-decoration: none; /* .entry-content a underlines links generically (§9) —
    never mattered before since every other .btn usage sat outside .entry-content,
    but the 会员专区 feature list now puts one inside it, and the underline showed. */
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}

.btn--solid {
  background: var(--color-accent);
  color: var(--color-accent-ink);
}
.btn--solid:hover { background: var(--color-accent-strong); }

.btn--outline {
  border: 1px solid var(--color-border-strong);
  color: var(--color-accent-text);
}
.btn--outline:hover { background: rgba(217, 164, 65, 0.1); }

/* =========================================================
   6. Hero (front-page)
   ========================================================= */
.hero {
  padding-block: 68px 52px; /* was 96px 72px — tighter so the real article photos below (§8) come into view sooner instead of the empty feeling coming mainly from how far down they start */
  border-bottom: 1px solid var(--color-border);
  background:
    radial-gradient(ellipse 60% 50% at 85% 0%, rgba(217, 164, 65, 0.12), transparent 70%),
    var(--color-bg);
}

/* Two columns at desktop widths (copy + illustration); collapses to
   one in the existing 900px breakpoint (§ responsive) alongside the
   site's other 2-column-to-1 layouts (footer-grid, ebook-optin__panel). */
.hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  /* was center — the illustration renders taller than the copy block,
     so centering left a noticeable empty gap above the eyebrow (the
     copy floating in the middle of a row sized to the taller column).
     Top-aligning both means the hero's height still follows the
     illustration, but the text now starts flush with it instead of
     floating below empty space. */
  align-items: start;
  gap: 56px;
}

.hero__copy { min-width: 0; }

/* Single-column grid instead of flex — a grid column sized to
   max-content is exactly as wide as its widest row (the CN text), and
   with no fixed width of its own that column reliably stays that
   width at any viewport size. .hero__title-en below relies on being
   in that same-width column to justify-stretch to it. */
.hero__title {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 45px; /* was 20px — more air between the title block and the subtitle/buttons/stats below it, which read as one cramped cluster at 20px */
}
.hero__title-cn {
  font-size: clamp(34px, 5vw, 54px);
  font-weight: 700;
  color: var(--color-text);
}
/* Same treatment as .site-title-en in the nav (sans, uppercase)
   rather than inheriting the h1's serif — scaled up for the hero, not
   the nav's literal 11px. A justify-stretch version that matched this
   line's width to the CN line above at any viewport size was tried
   and reverted — it required equalising every gap (including the one
   between "Bursa" and "Decode"), which erased the word break and read
   as "BURSADECODE". Plain letter-spacing keeps that original
   word-space proportionally wider than the added letter-gaps, so it
   only lines up with the CN line's width at one viewport size, but
   stays legible as two words at every size. */
.hero__title-en {
  font-family: var(--font-sans);
  font-size: clamp(11px, 1.3vw, 14px);
  font-weight: 500; /* matches .site-title-en's weight — not bold */
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--color-text);
}

.hero__subtitle {
  font-family: var(--font-serif);
  font-size: clamp(17px, 2vw, 20px);
  color: var(--color-text-muted);
  font-weight: 400;
  max-width: 620px;
  margin-bottom: 32px;
}

.hero__actions { display: flex; gap: 14px; flex-wrap: wrap; }

.hero__stats {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin-top: 28px;
  font-size: 14px;
  color: var(--color-text-faint);
}
.hero__stats strong { color: var(--color-accent-text); font-weight: 700; }
.hero__stats-divider { color: var(--color-border-strong); }

.hero__visual { display: flex; align-items: center; justify-content: center; min-width: 0; }
.hero__illustration { width: 100%; height: auto; max-width: 480px; }

/* =========================================================
   6b. Report hero (single.php, opt-in per post — inc/report-hero.php)
   First version of this used the same dark-surface token-rescoping
   pattern as .site-header/.site-footer (§1) — a full dark banner.
   Reverted after feedback ("不要深色") to a light masthead instead: a
   cream/paper surface with a gold top rule, reading as a premium
   report cover rather than a night-mode block. The one deliberately
   dark element left is the stat strip — a small inset "plate" that
   still uses that same rescoping trick, just scoped to
   .report-hero__stats instead of the whole hero, so the numbers get
   a moment of contrast without darkening the page again. Colours
   throughout are this site's own gold/navy — no red, matching the
   original recolour from the coral/red reference design this was
   modelled on. */
.report-hero {
  background: linear-gradient(180deg, #FBF7EF 0%, var(--color-bg) 100%);
  border-top: 3px solid var(--color-accent);
  padding-block: 48px 40px;
  position: relative;
}
.report-hero__inner { max-width: 720px; position: relative; }
.report-hero__brand-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 32px; }
.report-hero__brand { font-weight: 700; color: var(--color-text); letter-spacing: 0.1em; font-size: 14px; }
.report-hero__badge {
  background: var(--color-surface); color: var(--color-accent-text);
  border: 1px solid var(--color-border-strong);
  padding: 6px 16px; border-radius: 999px; font-size: 12px; letter-spacing: 0.03em;
  box-shadow: var(--shadow-card);
  white-space: nowrap;
}
/* The date half of the badge reads a shade quieter than the category
   half — same "label vs. detail" contrast .quick-facts/.data-grid use
   elsewhere, so the badge doesn't read as one flat string. */
.report-hero__badge time { color: var(--color-text-faint); font-weight: 500; }
.report-hero__badge-dot { margin: 0 5px; color: var(--color-border-strong); }
.report-hero__title {
  font-size: clamp(26px, 4.2vw, 36px);
  font-weight: 700;
  line-height: 1.42;
  letter-spacing: -0.01em;
  color: var(--color-text);
  margin-bottom: 16px;
}
.report-hero__lede { font-size: 15.5px; color: var(--color-text-muted); max-width: 540px; margin-bottom: 0; }

/* Four small dark "data cards" — the one dark element on this now-
   light hero, same rescoping trick as .site-header but scoped to just
   this grid. Each cell is its own bordered card (not one fused plate
   with hairline seams, which is what read as flat/plain) with a gold
   eyebrow label above a big serif number, the same "label above value"
   order .data-grid/.price-callout already use elsewhere — this was
   previously value-above-label, the one place on the page that broke
   that pattern. */
.report-hero__stats {
  --color-bg:         #0D1730;
  --color-text-faint: rgba(242, 239, 230, 0.5);

  display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
  margin-top: 30px;
}
.report-hero__stat {
  background: linear-gradient(160deg, #1B2B52 0%, var(--color-bg) 100%);
  border: 1px solid rgba(217, 164, 65, 0.22);
  border-radius: 10px;
  padding: 16px 10px;
  text-align: center;
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.06), var(--shadow-card);
}
.report-hero__stat-label {
  display: block;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  /* var(--color-accent), not --color-accent-strong — the latter is
     tuned for light backgrounds (hover states on gold buttons) and
     reads muddy on this card's dark navy; --color-accent is the one
     token documented as safe on any surface (see §1). */
  color: var(--color-accent);
  margin-bottom: 8px;
}
.report-hero__stat-value { display: block; font-family: var(--font-serif); font-weight: 700; font-size: 20px; color: #fff; line-height: 1.3; }

.report-hero__range { margin-top: 28px; }
.report-hero__range-labels { display: flex; justify-content: space-between; font-size: 12.5px; color: var(--color-text-faint); margin-bottom: 10px; }
.report-hero__range-track {
  position: relative; height: 6px; border-radius: 4px;
  background: linear-gradient(90deg, rgba(217,164,65,0.25), var(--color-accent-text) 100%);
}
.report-hero__range-marker {
  position: absolute; top: 50%; width: 14px; height: 14px; border-radius: 50%;
  background: var(--color-accent-strong); border: 3px solid var(--color-bg);
  transform: translate(-50%, -50%);
  box-shadow: 0 0 0 3px rgba(232,185,94,0.25);
}
.report-hero__range-marker-tag {
  position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%);
  background: var(--color-accent-text); color: #FFFFFF;
  font-size: 11.5px; font-weight: 700;
  padding: 4px 10px; border-radius: 6px; white-space: nowrap;
}
.report-hero__range-marker-tag::after {
  content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  border: 5px solid transparent; border-top-color: var(--color-accent-text);
}

/* Sticky anchor nav — ids are auto-injected onto the post's own H2s
   by bursa_decode_inject_heading_ids() (inc/report-hero.php), so this
   list always matches the real sections without separate manual
   upkeep. Light surface (not dark like the hero) since it sits over
   the ordinary cream reading area once you scroll past the hero. */
.report-toc {
  position: sticky; top: 0; z-index: 40;
  background: rgba(242,239,230,0.94); backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--color-border);
}
.report-toc__inner { display: flex; gap: 22px; overflow-x: auto; scrollbar-width: none; }
.report-toc__inner::-webkit-scrollbar { display: none; }
.report-toc__inner a {
  white-space: nowrap; font-size: 13px; font-weight: 500; color: var(--color-text-muted);
  text-decoration: none; padding: 13px 0; border-bottom: 2px solid transparent; display: inline-block;
}
.report-toc__inner a:hover,
.report-toc__inner a.is-active { color: var(--color-accent-text); border-bottom-color: var(--color-accent); }

/* Sticky left sidebar — the wide-viewport replacement for the
   horizontal .report-toc bar above (see single.php: both read the
   same heading list, CSS below just shows one or the other). Hidden
   below 1024px — there isn't room for a 200px rail next to 720px of
   body copy under that, so the horizontal bar takes over instead,
   same "breaks out only past 1024px" threshold .mindset-about-layout
   already uses elsewhere on the site. */
.report-sidenav { display: none; }

@media (min-width: 1024px) {
  .report-toc { display: none; }

  /* .report-hero__inner carries both "container" (which brings
     .container's 24px padding-inline along with it) and
     "report-hero__inner" (max-width:720px, winning over .container's
     1160px since it's declared later). The grid's content column
     below is a bare 720px track with no padding of its own, so as
     long as the hero copy ALSO still had that 24px padding eating
     into its 720px box, its text sat 24px narrower and shifted from
     the grid column's flush edges — the actual cause of "文章本身不
     对齐了", not the grid math itself (that part was already right).
     Zeroing the padding only here, at the width where the grid is
     actually active, fixes the two without touching narrow-viewport
     spacing where .report-layout isn't a grid at all and this padding
     is what keeps hero text off the screen edge. */
  .report-hero__inner { padding-inline: 0; }

  /* Symmetric 3-column grid, NOT sidebar+content — the middle column
     is exactly .entry-content's own 720px, landing in the same spot
     the (now unpadded) hero copy above sits in. Putting the sidebar
     and content in a plain 2-column grid instead (as this first
     shipped) recenters the pair as a unit and shifts the content off
     that line. The sidebar fills the whole left column rather than
     hugging its right edge, so it sits nearer the container's own
     left edge than the article text — "直接放在左侧... 不要和文章本
     身太靠近". */
  .report-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 720px minmax(0, 1fr);
    column-gap: 48px;
    align-items: start;
  }
  .report-sidenav {
    grid-column: 1;
    display: flex;
    flex-direction: column;
    position: sticky;
    /* .site-header (§4) is itself sticky at top:0 and ~64px tall — a
       plain top:24px here made the two sticky elements overlap as
       soon as you scrolled, with the header sitting in front and
       covering the sidenav's first item or two ("看不到公司简介").
       90px clears the header the same way h2's scroll-margin-top
       does, plus the original 24px of breathing room. */
    top: 90px;
    max-height: calc(100vh - 114px);
    overflow-y: auto;
    max-width: 220px;
  }
  .report-main { grid-column: 2; }
  .report-sidenav a {
    font-size: 13px;
    line-height: 1.5;
    color: var(--color-text-muted);
    text-decoration: none;
    padding: 9px 0 9px 14px;
    border-left: 2px solid var(--color-border);
  }
  .report-sidenav a:hover,
  .report-sidenav a.is-active { color: var(--color-accent-text); border-left-color: var(--color-accent); }
  .report-sidenav a.is-active { font-weight: 700; }
}

/* Fills left-to-right as the reader scrolls through .entry-content
   (see the small inline script in report-hero.php) — sits flush under
   the sticky toc so it reads as part of the same nav bar. */
.report-progress { height: 2px; background: var(--color-border); }
.report-progress__bar { display: block; height: 100%; width: 0; background: var(--color-accent); transition: width 0.1s linear; }

/* Section markers, scoped to report-hero posts only via the
   .has-report-hero body class (see bursa_decode_report_hero_body_
   class() in inc/report-hero.php) so ordinary articles' headings are
   untouched. A vertical bar to the left of the heading — reusing the
   same accent-bar language .pull-quote/.feature-list__item already
   use for "this is marked content" elsewhere on the site — rather
   than a standalone bar sitting above it, which was a one-off idiom
   this page didn't share with anything else. A soft glow behind the
   heading number-less mark gives it a touch more presence without
   adding another new shape. */
.has-report-hero .entry-content h2 {
  position: relative;
  padding-left: 22px;
  scroll-margin-top: 90px; /* clears the sticky .site-header (§4, ~64px) when a toc/sidenav link jumps here */
}
.has-report-hero .entry-content h2::before {
  content: '';
  position: absolute; left: 0; top: 0.2em; bottom: 0.15em;
  width: 4px;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--color-accent) 0%, var(--color-accent-strong) 100%);
  box-shadow: 0 0 10px rgba(217, 164, 65, 0.35);
}

@media (max-width: 720px) {
  .report-hero__stats { grid-template-columns: repeat(2, 1fr); }
}

/* =========================================================
   7. Section headings
   ========================================================= */
.section-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 32px;
  flex-wrap: wrap;
}

.section-heading h2 {
  font-size: 28px;
  margin: 0;
}

.section-heading .view-all {
  font-size: 14px;
  font-weight: 600;
  color: var(--color-accent-text);
  white-space: nowrap;
}
.section-heading .view-all:hover { text-decoration: underline; }

.section { padding-block: 64px; }

/* =========================================================
   8. Entry cards / grid
   ========================================================= */
.entry-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
}

.entry-card {
  position: relative; /* anchors the __title link's stretched ::after below */
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-card);
  transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
.entry-card:hover {
  border-color: var(--color-border-strong);
  transform: translateY(-4px);
  box-shadow: 0 20px 36px -18px rgba(13, 23, 48, 0.28);
}

/* Stretched-link: the title's own <a> gets an invisible layer covering
   the whole card, so clicking the excerpt/meta/empty padding navigates
   too, not just the title text and thumbnail. Those two keep their own
   real links (the thumbnail's is tabindex="-1" — decorative/redundant
   once this is added, harmless to leave) so this doesn't change what
   a screen reader or the tab order sees, only the clickable area. */
/* No position:relative on the <a> itself — that would make the <a>'s
   own (inline, title-text-sized) box the nearest positioned ancestor
   for its ::after, stretching it only over the title text and quietly
   defeating the whole point. .entry-card already has position:relative
   (above), which is what inset:0 here needs to reach. */
.entry-card__title a::after { content: ""; position: absolute; inset: 0; }

/* "会员专属" badge — sits over the thumbnail's top-right corner (or,
   for a post with no featured image, the card's own top-right corner,
   since .entry-card is already position:relative). Solid fill rather
   than the translucent-pill treatment used elsewhere (§17 tags) since
   this sits directly on top of a photo, not a plain surface — needs
   to stay legible against any image. z-index lifts it above the
   title's stretched-link ::after (both are absolutely positioned
   within the same .entry-card; without this the badge would still be
   visually painted first and covered). */
.entry-card__badge {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px 4px 8px;
  border-radius: 999px;
  background: var(--color-accent);
  color: var(--color-accent-ink);
  font-size: 12px;
  font-weight: 700;
  box-shadow: 0 2px 8px rgba(13, 23, 48, 0.25);
}
.entry-card__badge::before {
  content: '';
  width: 12px;
  height: 12px;
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%232A1D07' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='11' width='18' height='11' rx='2' ry='2'%3E%3C/rect%3E%3Cpath d='M7 11V7a5 5 0 0 1 10 0v4'%3E%3C/path%3E%3C/svg%3E");
}

.entry-card__media {
  aspect-ratio: 16 / 10;
  background: var(--color-surface-2);
  overflow: hidden;
}
.entry-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .3s ease;
}
.entry-card:hover .entry-card__media img { transform: scale(1.05); }

.entry-card__body { padding: 22px 22px 26px; display: flex; flex-direction: column; flex: 1; }

.entry-card__cat {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent-text);
  margin-bottom: 10px;
}

.entry-card__title {
  font-size: 19px;
  margin-bottom: 10px;
}
.entry-card__title a:hover { color: var(--color-accent-text); }

.entry-card__excerpt {
  font-size: 14.5px;
  color: var(--color-text-muted);
  margin-bottom: 14px;
  flex: 1;
}

.entry-card__meta {
  font-size: 12.5px;
  color: var(--color-text-faint);
  display: flex;
  gap: 10px;
  align-items: center;
}


/* =========================================================
   9. Single article
   ========================================================= */
.entry-header { max-width: 720px; margin: 0 auto; padding-block: 56px 32px; text-align: left; }

/* Was an inline style on single.php (max-width:960px) — 240px wider
   than .entry-header/.entry-content's 720px, so each block centered
   independently and the image's edges landed outside the title's,
   which is exactly the misalignment reported. Matching the width here
   fixes it for good instead of leaving it as markup that's easy to
   forget lives outside this stylesheet. */
.entry-thumbnail {
  max-width: 720px;
  margin: 0 auto 40px;
  border-radius: var(--radius-md);
  overflow: hidden;
}
/* The global `img{display:block}` reset (§1) means an image narrower
   than this 720px container — e.g. one of the auto-padded 16:10 logo
   thumbnails from inc/auto-pad-logo-thumbnail.php, often only ~570px
   wide — sits flush left instead of centering, since a block box's
   own width doesn't auto-center the way inline content would. A
   regular photo (near-always wider than 720px, so it gets scaled down
   to fill the container via max-width:100%) never showed this. */
.entry-thumbnail img { margin-inline: auto; }

.entry-header__cat a {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-accent-text);
}

.entry-header__title {
  font-size: clamp(28px, 4vw, 42px);
  margin: 14px 0 18px;
}

.entry-header__meta {
  font-size: 14px;
  color: var(--color-text-faint);
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
}

.entry-content {
  max-width: 720px;
  margin: 0 auto;
  font-size: 18px;
  line-height: 1.85;
}

.entry-content h2 { font-size: 26px; margin-top: 1.6em; }
.entry-content h3 { font-size: 21px; margin-top: 1.4em; }
.entry-content p { margin-bottom: 1.4em; }
.entry-content a { color: var(--color-accent-text); text-decoration: underline; text-underline-offset: 3px; }
/* .btn already sets text-decoration:none, but ".entry-content a" (one
   class + the element) out-specifies ".btn" (one class alone), so its
   underline/color won any time a .btn ends up inside page content —
   exactly what happened on 会员专区. ".entry-content a.btn" is more
   specific again, so this reliably wins back both properties instead
   of just tying and depending on source order. */
.entry-content a.btn { text-decoration: none; }
.entry-content a.btn--solid { color: var(--color-text); }
.entry-content a.btn--outline { color: var(--color-accent-text); }
.entry-content blockquote {
  margin: 2em 0;
  padding: 4px 0 4px 22px;
  border-left: 3px solid var(--color-accent);
  color: var(--color-text-muted);
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 19px;
}
.entry-content ul, .entry-content ol { margin: 0 0 1.4em; padding-left: 1.4em; list-style: revert; }
.entry-content img { border-radius: var(--radius-md); margin: 1.6em 0; }
.entry-content figcaption { font-size: 13px; color: var(--color-text-faint); text-align: center; margin-top: 8px; }
.entry-content code { background: var(--color-surface-2); padding: 2px 6px; border-radius: 3px; font-size: 0.9em; }
.entry-content table { width: 100%; border-collapse: collapse; margin: 1.6em 0; font-size: 15px; }
.entry-content table th, .entry-content table td { border: 1px solid var(--color-border); padding: 10px 12px; text-align: left; }
.entry-content table th { color: var(--color-accent-text); font-family: var(--font-serif); }

/* Used on static pages (e.g. 会员专区) in place of a plain <ul> — same
   hairline-divider rhythm as .pricing-faq__item on the checkout page,
   just with a gold accent bar per row instead of no accent at all, so
   a feature list reads slightly more "as a list of things you get"
   than a FAQ's plain question/answer pairs do. Deliberately not a
   bordered/shadowed card grid — that boxed-panel look has been tried
   and rejected elsewhere on this site in favour of plain typographic
   separation. */
.feature-list { margin: 1.8em 0; }
.feature-list__item {
  position: relative;
  padding: 20px 0 20px 22px;
  border-top: 1px solid var(--color-border);
}
.feature-list__item:last-child { border-bottom: 1px solid var(--color-border); }
.feature-list__item::before {
  content: '';
  position: absolute; left: 0; top: 26px; bottom: 26px;
  width: 3px; background: var(--color-accent); border-radius: 2px;
}
.feature-list__item h3 { font-family: var(--font-serif); font-size: 18px; margin: 0 0 6px; }
.feature-list__item p { color: var(--color-text-muted); font-size: 15px; margin: 0; }

/* Text-left / icon-right layout for 关于我们 — every paragraph of the
   story stays in one unbroken left column; the icon (the user's own
   Lovart-designed "跟风 vs 看懂" scale artwork, used whole rather than
   redrawn/simplified) fills its own right-hand column at real size.
   That artwork is a 1200×900 "poster" with its own eyebrow, big
   跟风/看懂 characters, tagline and credit line — shrinking it into a
   narrow sidebar the way an icon would be sized made all of that
   text illegible, so instead the whole layout breaks out wider than
   the normal 720px reading column (only past 1024px viewport width —
   below that there isn't room for both a real column of prose and a
   legible copy of the poster, so it stacks full-width instead, same
   as .entry-content images already do on narrow screens). */
.mindset-about-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
  margin: 2.4em 0;
}
.mindset-about-layout__text > *:first-child { margin-top: 0; }
.mindset-about-layout__icon { margin: 0 auto; max-width: 480px; }

@media (min-width: 1024px) {
  .mindset-about-layout {
    grid-template-columns: 1fr 480px;
    align-items: start;
    gap: 40px;
    width: 960px;
    max-width: calc(100vw - 48px);
    margin-left: calc(50% - 480px);
    margin-right: calc(50% - 480px);
  }
  .mindset-about-layout__icon { max-width: none; margin: 0; }

  /* .page-header (the "关于我们" H1) normally centers at its own
     760px width — same center point as .page-content's 720px in the
     ordinary case, but .mindset-about-layout above breaks out to a
     960px box on this one page, which shifts the body text's left
     edge without moving the title, throwing them out of alignment.
     Scoped to page 14 by WordPress's own body_class(), giving the
     title that identical width+shift keeps both flush left together
     instead of widening every other page's header. */
  .page-id-14 .page-header {
    width: 960px;
    max-width: calc(100vw - 48px);
    margin-left: calc(50% - 480px);
    margin-right: calc(50% - 480px);
  }
}

/* The artwork itself: a fixed 1200×900 artboard scaled to fit
   whatever width its column actually renders at (via the small script
   alongside it, not a CSS container query — WordPress's own editor
   preview doesn't resolve cqw units reliably, see the 物流全程图解
   embed this pattern was first built for). */
.lv-stage { position: relative; width: 100%; padding-top: 75%; }
.lv-canvas {
  position: absolute; top: 0; left: 0;
  width: 1200px; height: 900px;
  transform-origin: top left;
  display: flex; flex-direction: column; align-items: center;
  font-family: var(--font-sans);
  background: var(--color-bg);
  background-image:
    radial-gradient(ellipse 800px 500px at 600px 450px, rgba(217,164,65,0.06) 0%, transparent 70%),
    radial-gradient(ellipse 1000px 600px at 600px 400px, rgba(13,23,48,0.03) 0%, transparent 80%);
}
/* Sizing note: this artboard renders at 1024–960px wide at its widest
   (the .mindset-about-layout breakout above) — a ~0.4× scale of its
   native 1200px — so, same rule as the 物流全程图解 embed, every
   font-size here is chosen at roughly 2.5× the size it needs to read
   at once scaled down, not the size an unscaled 1200px poster would
   use natively. */
.lv-eyebrow {
  margin-top: 36px;
  font-size: 30px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--color-text); opacity: 0.5; flex-shrink: 0;
}
.lv-comparison-row {
  width: 1200px; display: flex; align-items: flex-end; justify-content: space-between;
  padding: 0 80px; margin-top: 28px; flex-shrink: 0; position: relative; z-index: 2;
}
.lv-side-block { display: flex; flex-direction: column; align-items: center; text-align: center; width: 320px; }
.lv-side-block.lv-left { padding-bottom: 40px; }
.lv-side-tag {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 16px; border-radius: 20px;
  font-size: 28px; font-weight: 600; letter-spacing: 0.06em; margin-bottom: 12px;
}
.lv-side-tag.lv-gray-tag { background: rgba(13,23,48,0.06); color: var(--color-text-muted); border: 1px solid var(--color-border); }
.lv-side-tag.lv-gold-tag { background: rgba(217,164,65,0.14); color: var(--color-accent-text); border: 1px solid var(--color-border-strong); }
.lv-tag-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.lv-gray-tag .lv-tag-dot { background: var(--color-text-faint); }
.lv-gold-tag .lv-tag-dot { background: var(--color-accent); }
.lv-hero-char { font-family: var(--font-sans); font-size: 128px; font-weight: 900; line-height: 1; letter-spacing: -0.02em; }
.lv-hero-char.lv-gray-hero { color: var(--color-text-muted); }
.lv-hero-char.lv-gold-hero { color: var(--color-accent-text); text-shadow: 0 2px 24px rgba(217,164,65,0.25); }
.lv-side-subtitle { font-size: 34px; font-weight: 400; color: var(--color-text-muted); letter-spacing: 0.03em; margin-top: 14px; line-height: 1.5; white-space: nowrap; }
.lv-connector { width: 1px; height: 32px; margin-top: 14px; }
.lv-connector.lv-gray-line { background: linear-gradient(180deg, rgba(13,23,48,0.35) 0%, rgba(13,23,48,0.08) 100%); }
.lv-connector.lv-gold-line { background: linear-gradient(180deg, rgba(217,164,65,0.55) 0%, rgba(217,164,65,0.12) 100%); }
.lv-scale-svg-wrap { width: 1200px; height: 400px; flex-shrink: 0; position: relative; margin-top: -20px; z-index: 1; }
.lv-tagline-section { flex-shrink: 0; display: flex; flex-direction: column; align-items: center; gap: 16px; margin-top: 10px; }
.lv-tagline-rule { width: 520px; height: 1px; background: linear-gradient(90deg, transparent, var(--color-border-strong) 20%, var(--color-border-strong) 80%, transparent); }
.lv-tagline { font-size: 36px; font-weight: 400; color: var(--color-text-muted); letter-spacing: 0.05em; line-height: 1.8; text-align: center; }
.lv-tagline .lv-highlight { color: var(--color-accent-text); font-weight: 700; }
.lv-credit { margin-top: auto; margin-bottom: 24px; font-size: 30px; font-weight: 600; color: var(--color-text); opacity: 0.45; letter-spacing: 0.12em; }

/* A bold, assertive standalone line — not a quoted excerpt (so not
   reusing .entry-content blockquote's italic treatment, and .page-content
   has no blockquote styling of its own anyway), same left-accent-bar
   language as everything else on this page. */
.pull-quote {
  margin: 1.8em 0;
  padding: 4px 0 4px 22px;
  border-left: 3px solid var(--color-accent);
  font-family: var(--font-serif);
  font-weight: 700;
  font-size: 22px;
  color: var(--color-text);
}
/* Centered under the scale illustration — the left accent bar reads
   oddly once it's no longer aligned with a left-aligned paragraph. */
.pull-quote--center { text-align: center; padding-left: 0; border-left: none; }

/* Mirrors the PMPro checkout page's own price-hero treatment
   (.pmpro_price_eyebrow/.pmpro_price_hero) rather than inventing a new
   pattern — eyebrow label + a genuinely large price line + supporting
   detail underneath, with the same card shadow/border/radius language
   .entry-card uses, so this reads as a real focal point instead of a
   slightly-tinted paragraph. */
.price-callout {
  margin: 1.8em 0;
  padding: 28px 32px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.price-callout__eyebrow {
  display: block;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: 10px;
}
.price-callout__price {
  display: block;
  font-family: var(--font-serif);
  font-size: 32px;
  color: var(--color-accent-text);
  margin-bottom: 14px;
}
.price-callout__price strong { font-weight: 700; }
.price-callout p { margin: 0; font-size: 15px; color: var(--color-text-muted); }

/* Non-member banner at the top of the 会员专属文章库 page (page-
   templates/page-member-library.php) — originally reused .price-
   callout as-is, which reads fine as a narrow article-content block
   but sat oddly here: a mostly-empty box the full 1160px container
   wide, with only a "了解创始会员" button even though its own sentence
   promises "登录或加入创始会员" (two actions, one button). This is its
   own component: a horizontal banner (icon + copy + both buttons in
   one row) using the same lock icon language as the article paywall
   card (.pmpro_content_message) so "this needs membership" reads as
   one consistent visual idiom across the site, not two different ones. */
.member-gate-banner {
  display: flex;
  align-items: center;
  gap: 24px;
  margin-bottom: 32px;
  padding: 24px 28px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.member-gate-banner__icon {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: rgba(217, 164, 65, 0.12);
  color: var(--color-accent-text);
}
.member-gate-banner__icon svg { width: 22px; height: 22px; }
.member-gate-banner__body { flex: 1 1 auto; min-width: 0; }
.member-gate-banner__eyebrow {
  display: block;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: 6px;
}
.member-gate-banner p { margin: 0; font-size: 15px; line-height: 1.6; color: var(--color-text-muted); }
.member-gate-banner__actions { flex: none; display: flex; gap: 10px; }

/* Premium-article visual components — built for the first member-only
   analysis piece (国行按兵不动，令吉特升到13个月新高) as a reusable set,
   not one-off markup. Every node/value in these three components is
   copied verbatim from the article's own prose/cited figures — no
   number or category is invented for the visual, matching the site's
   standing no-fabrication rule. */

/* Causal-chain timeline — visualises a reasoning chain the article
   already states in prose as a sequence of short connected steps
   instead of restating it as another paragraph. First/last nodes
   (starting condition, resulting outcome) are highlighted; the
   middle steps are quieter. */
.rate-chain { margin: 2em 0; padding: 4px 0; }
.rate-chain__node {
  position: relative;
  padding: 0 0 22px 28px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--color-text-muted);
}
.rate-chain__node::before {
  content: '';
  position: absolute; left: 3px; top: 6px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--color-border-strong);
}
.rate-chain__node::after {
  content: '';
  position: absolute; left: 6px; top: 16px; bottom: -2px;
  width: 1px;
  background: var(--color-border);
}
.rate-chain__node:last-child { padding-bottom: 0; }
.rate-chain__node:last-child::after { display: none; }
.rate-chain__node:first-child::before,
.rate-chain__node:last-child::before {
  background: var(--color-accent);
  width: 10px; height: 10px; left: 2px;
}
.rate-chain__node:last-child {
  color: var(--color-text);
  font-weight: 700;
  font-size: 17px;
}

/* Two-column sector comparison — every sector name is copied from the
   research-house lists the article quotes ("研究机构点名的受惠板块包
   括…" / "被点名受压的包括…"), not an invented taxonomy. Deliberately
   not red/green — this site doesn't use red anywhere, so the two
   sides read apart via an up/down glyph + gold-vs-neutral tint
   instead of a traffic-light hue pair. */
.sector-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  margin: 2em 0;
  background: var(--color-border);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.sector-split__col { background: var(--color-surface); padding: 22px 24px; }
.sector-split__label {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 700; letter-spacing: 0.02em;
  margin-bottom: 14px;
}
.sector-split__col--up .sector-split__label { color: var(--color-accent-text); }
.sector-split__col--down .sector-split__label { color: var(--color-text-muted); }
.sector-split__tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.sector-split__tags li {
  font-size: 13px; padding: 5px 10px;
  border-radius: 20px;
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
}
.sector-split__col--up .sector-split__tags li { border-color: rgba(217,164,65,0.4); color: var(--color-accent-text); }

/* Small stat cells for hard numbers the article already states
   (revenue, sensitivity %, forecast cuts) — a visual landing point for
   numbers worth seeing at a glance, not a dashboard. Column count
   reflows on its own via auto-fit, so it doesn't need a mobile
   override the way the two-column components above do. */
.data-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 1px;
  margin: 2em 0;
  background: var(--color-border);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.data-grid__cell { background: var(--color-surface); padding: 20px 22px; }
.data-grid__label { display: block; font-size: 12px; color: var(--color-text-faint); margin-bottom: 8px; }
.data-grid__value { display: block; font-family: var(--font-serif); font-size: 23px; color: var(--color-accent-text); line-height: 1.3; }
.data-grid__value strong { font-weight: 700; }
.data-grid__note { display: block; font-size: 13px; color: var(--color-text-muted); margin-top: 6px; }

/* Quick-facts strip — the article's 2-3 headline numbers as small
   pills, meant to sit as the very first thing inside .entry-content
   (right under the title/thumbnail) so a skimming reader lands on the
   punchline before the prose. Values are restated from the article
   body, not new figures. */
.quick-facts { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 2em; }
.quick-facts__item {
  display: flex; align-items: baseline; gap: 7px;
  padding: 9px 16px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 20px;
  font-size: 14px;
}
.quick-facts__item strong { font-family: var(--font-serif); font-size: 16px; color: var(--color-accent-text); }
.quick-facts__item span { color: var(--color-text-muted); }

/* 划重点 — a short numbered summary up front so a member knows what
   the piece argues before committing to the full read. Same card
   language as .price-callout (surface + left accent + shadow), a
   deliberately calmer treatment than the number-heavy .data-grid. */
.key-takeaways {
  margin: 0 0 2.2em;
  padding: 24px 28px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.key-takeaways__title {
  display: block;
  font-family: var(--font-serif);
  font-weight: 700;
  font-size: 15px;
  color: var(--color-text);
  margin-bottom: 14px;
}
.key-takeaways ol { margin: 0; padding: 0 0 0 1.2em; display: flex; flex-direction: column; gap: 10px; }
.key-takeaways li { font-size: 15px; color: var(--color-text-muted); line-height: 1.6; }
.key-takeaways li strong { color: var(--color-text); }

/* 参考来源 — compiles the attributions already named in the prose
   (research house, company filing, official statements) into one
   list at the end, instead of leaving them scattered through the
   article. No links invented for sources the article only cites
   verbally. */
.source-list { margin: 2.4em 0 0; padding-top: 24px; border-top: 1px solid var(--color-border); }
.source-list__title {
  display: block;
  font-size: 13px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--color-text-faint);
  margin-bottom: 12px;
}
.source-list ul { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.source-list li { font-size: 14px; color: var(--color-text-muted); padding-left: 16px; position: relative; }
.source-list li::before { content: '—'; position: absolute; left: 0; color: var(--color-text-faint); }
/* Topic sub-headers inside a long, grouped source list — a small
   gold-caps label (same family as .source-list__title itself) rather
   than another dashed li, so a 30-citation list still scans by
   section instead of reading as one flat dash list. */
.source-list li.source-list__group {
  padding-left: 0;
  margin-top: 14px;
  font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--color-accent-text);
}
.source-list li.source-list__group::before { content: none; }
.source-list li.source-list__group:first-child { margin-top: 0; }

/* Individual-stock-analysis visual components — built for the Pekat
   Group deep-dive as a second reusable set alongside the macro-article
   one above. Every number is copied from the article's own cited
   figures (order book size at each disclosed date, peer PE/D-E/margin
   from its own comparison table) — nothing interpolated or estimated
   for the visual. */

/* A real, to-scale bar chart — not decorative — for a short numeric
   time series the article states in prose (order book size at 4
   disclosed dates). Bar heights are plain percentages of the series
   max, set inline per bar since the values differ per article; this
   component only supplies the chrome. */
.mini-bar-chart { margin: 2em 0; padding: 24px 24px 16px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); box-shadow: var(--shadow-card); }
.mini-bar-chart__title {
  display: block;
  font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-accent-text);
  margin-bottom: 22px;
}
.mini-bar-chart__bars { display: flex; align-items: flex-end; height: 160px; }
.mini-bar-chart__col {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: flex-end;
  height: 100%;
  padding: 0 8px;
  border-left: 1px solid var(--color-border);
}
.mini-bar-chart__col:first-child { border-left: none; }
/* Bars default to the same quiet tan every non-current year/value uses
   in .growth-bars, so a reader's eye goes to whichever point is
   "now" — same two-tier colour logic as that panel, not a chart-
   specific rule of its own. */
.mini-bar-chart__value { font-family: var(--font-serif); font-size: 14px; font-weight: 700; color: var(--color-text-faint); margin-bottom: 6px; }
.mini-bar-chart__bar { position: relative; width: 100%; max-width: 44px; background: var(--color-border-strong); border-radius: 4px 4px 0 0; }
.mini-bar-chart__col--current .mini-bar-chart__value { color: var(--color-accent-text); font-size: 15px; }
.mini-bar-chart__col--current .mini-bar-chart__bar {
  background: linear-gradient(180deg, var(--color-accent) 0%, var(--color-accent-strong) 100%);
  box-shadow: 0 -6px 14px -4px rgba(217, 164, 65, 0.55);
}
.mini-bar-chart__col--current .mini-bar-chart__bar::after {
  content: '';
  position: absolute; top: -3px; left: 0; right: 0;
  height: 3px; border-radius: 2px;
  background: var(--color-accent-strong);
}
.mini-bar-chart__label { font-size: 12px; color: var(--color-text-muted); margin-top: 8px; text-align: center; }

/* Peer-comparison cards — same "row of comparable units" idea as
   .sector-split, but for a stock-vs-stock metric table instead of a
   two-sided split. The subject company's own card is highlighted; a
   peer missing a stat (e.g. Malakoff's D/E or margin wasn't in the
   article's own comparison) simply omits that row rather than
   guessing a value to fill the grid. */
.peer-compare {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 1px;
  margin: 2em 0;
  background: var(--color-border);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.peer-compare__card { background: var(--color-surface); padding: 18px 16px; display: flex; flex-direction: column; gap: 12px; }
.peer-compare__card--highlight { background: rgba(217,164,65,0.07); box-shadow: inset 0 2px 0 var(--color-accent); }
.peer-compare__name { font-family: var(--font-serif); font-weight: 700; font-size: 15px; color: var(--color-text); }
.peer-compare__stat { display: flex; flex-direction: column; gap: 2px; }
.peer-compare__stat span { font-size: 11px; color: var(--color-text-faint); }
.peer-compare__stat strong { font-size: 14px; font-weight: 600; color: var(--color-text); }
.peer-compare__card--highlight .peer-compare__stat strong { color: var(--color-accent-text); }

/* Header-band variant (.peer-compare--header on the wrapper): for a
   row of equal peers (e.g. four glove makers) where no single card is
   "the subject", so nobody gets the tinted-body treatment above.
   Instead every card's NAME row is a soft gold-tinted band with a
   gold top edge — the same tint/accent every other in-article visual
   (--highlight above, key-takeaways, watch-list numbers) uses; a solid
   navy band was tried first and read as a foreign colour block next to
   them — so the stock reads as the header of its own column of
   numbers. Opt-in per article, so existing .peer-compare blocks that
   rely on --highlight are untouched. */
.peer-compare--header .peer-compare__card { padding: 0 0 18px; }
.peer-compare--header .peer-compare__name {
  padding: 12px 16px;
  background: rgba(217, 164, 65, 0.14);
  box-shadow: inset 0 2px 0 var(--color-accent);
  border-bottom: 1px solid var(--color-border);
}
.peer-compare--header .peer-compare__stat { padding: 0 16px; }

/* Segment-mix bar — a single stacked bar for a revenue breakdown that
   is genuinely complete (every segment named + its real share of the
   period's total), unlike .mini-bar-chart above which tracks one
   number over time. Segment widths are set inline per use since the
   shares differ per article; only gold-family shades are used to
   distinguish segments, no off-palette colour. */
/* Card chrome matches .growth-bars/.data-grid/.peer-compare (surface
   background, border, radius, shadow) — this shipped without it
   originally, the one chart on the page not carrying the shared
   panel language. */
.mix-bar {
  margin: 2em 0;
  padding: 20px 24px 22px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.mix-bar__title {
  display: block;
  font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-accent-text);
  margin-bottom: 16px;
}
.mix-bar__track { display: flex; height: 28px; border-radius: 6px; overflow: hidden; background: var(--color-border); }
.mix-bar__seg { height: 100%; background: var(--color-accent); }
.mix-bar__seg--2 { background: var(--color-accent-strong); }
.mix-bar__seg--3 { background: rgba(217,164,65,0.55); }
.mix-bar__seg--4 { background: var(--color-border-strong); }
.mix-bar__legend { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 14px; font-size: 13px; color: var(--color-text-muted); }
.mix-bar__legend span { display: flex; align-items: center; gap: 7px; }
.mix-bar__legend i { width: 9px; height: 9px; border-radius: 2px; flex-shrink: 0; background: var(--color-accent); }
.mix-bar__legend span:nth-child(2) i { background: var(--color-accent-strong); }
.mix-bar__legend span:nth-child(3) i { background: rgba(217,164,65,0.55); }
.mix-bar__legend span:nth-child(4) i { background: var(--color-border-strong); }

/* End-of-article CTA (bursa_decode_after_content_cta(), inc/post-cta.php)
   — sits between .entry-content and .entry-footer, outside the former
   so it needs its own 720px cap rather than inheriting one. Two
   independent halves (membership nudge, 延伸阅读) that can appear
   alone or together; grid collapses to one column when only one half
   is present since the other simply isn't in the markup. */
.post-cta {
  max-width: 720px;
  margin: 2.4em auto 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
}
.post-cta__membership {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  border-radius: var(--radius-md);
  padding: 22px 24px;
  box-shadow: var(--shadow-card);
}
.post-cta__membership p { font-size: 14.5px; color: var(--color-text-muted); margin: 8px 0 16px; }
/* Same card chrome as .post-cta__membership (border + shadow) so the
   two read as a matched pair rather than "a real card + a flat tinted
   box" — that mismatch was the main thing making this side feel
   unfinished. Hairline dividers between links (the same rhythm
   .feature-list__item uses) plus an arrow that slides on hover give
   each link a real "this leads somewhere" affordance instead of
   being plain undecorated text. */
.post-cta__related {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 22px 24px;
  box-shadow: var(--shadow-card);
}
.post-cta__related ul { margin: 10px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; }
.post-cta__related li { border-top: 1px solid var(--color-border); }
.post-cta__related li:first-child { border-top: none; }
.post-cta__related a {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 12px 0;
  font-size: 14.5px; color: var(--color-text); text-decoration: none; line-height: 1.45;
}
.post-cta__related a::after {
  content: '→';
  flex-shrink: 0;
  font-size: 13px;
  color: var(--color-border-strong);
  transition: transform 0.15s ease, color 0.15s ease;
}
.post-cta__related a:hover { color: var(--color-accent-text); }
.post-cta__related a:hover::after { color: var(--color-accent-text); transform: translateX(3px); }
.post-cta__eyebrow {
  display: block;
  font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-accent-text);
}

/* Only one half present (e.g. a locked article shows just 延伸阅读, the
   join pitch already being in PMPro's own gate message above) — let it
   span the row instead of sitting in half of it. */
.post-cta > :only-child { grid-column: 1 / -1; }

@media (max-width: 720px) {
  .post-cta { grid-template-columns: 1fr; }
}

/* Year-over-year bar pairs + a real-proportion donut, as ONE panel —
   these first shipped as two separately-boxed components (a bar-chart
   row, then a whole second card below for the donut) which read as
   two unrelated pieces bolted together rather than one considered
   "financial snapshot". One eyebrow-labelled panel, four cells of
   equal weight (three bar-pairs, one donut) fixes that. Bar heights
   and the donut's stroke-dasharray are the only per-use inline
   values; everything else is shared chrome. */
.growth-bars {
  margin: 2em 0;
  padding: 4px 24px 22px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}
.growth-bars__eyebrow {
  display: block;
  padding-top: 18px;
  font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-accent-text);
}
.growth-bars__row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  column-gap: 8px;
}
.growth-bars__group {
  padding: 18px 6px 4px;
  display: flex; flex-direction: column; align-items: center;
  border-left: 1px solid var(--color-border);
}
.growth-bars__group:first-child { border-left: none; }
.growth-bars__title { font-size: 13px; font-weight: 700; color: var(--color-text); margin-bottom: 16px; }
.growth-bars__pair { display: flex; align-items: flex-end; gap: 14px; height: 108px; }
.growth-bars__col { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; }
.growth-bars__val { font-size: 11.5px; color: var(--color-text-faint); margin-bottom: 4px; white-space: nowrap; }
.growth-bars__bar { position: relative; width: 30px; border-radius: 4px 4px 0 0; background: var(--color-border-strong); }
.growth-bars__col--current .growth-bars__bar {
  background: linear-gradient(180deg, var(--color-accent) 0%, var(--color-accent-strong) 100%);
  box-shadow: 0 -6px 14px -4px rgba(217, 164, 65, 0.55);
}
/* A small bright cap on the current-year bar — the one glowing accent
   in the panel, echoing the highlighted-candle treatment the hero
   illustration already uses for "the number that matters now". */
.growth-bars__col--current .growth-bars__bar::after {
  content: '';
  position: absolute; top: -3px; left: 0; right: 0;
  height: 3px; border-radius: 2px;
  background: var(--color-accent-strong);
}
.growth-bars__col--current .growth-bars__val { color: var(--color-accent-text); font-weight: 700; font-size: 13px; }
.growth-bars__yr { font-size: 10.5px; color: var(--color-text-faint); margin-top: 8px; }
.growth-bars__delta { margin-top: 12px; font-family: var(--font-serif); font-weight: 700; font-size: 14px; color: var(--color-accent-text); }

.growth-bars__group--donut { justify-content: center; }
.growth-bars__donut-chart { margin-bottom: 6px; }
.growth-bars__donut-pct { font-family: var(--font-serif); font-size: 19px; font-weight: 700; fill: var(--color-text); }
.growth-bars__donut-legend { display: flex; flex-direction: column; gap: 3px; margin-top: 10px; font-size: 11.5px; color: var(--color-text-muted); }
.growth-bars__donut-legend span { display: flex; align-items: center; gap: 6px; }
.growth-bars__donut-legend i { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; background: var(--color-accent); }
.growth-bars__donut-legend span:last-child i { background: var(--color-border-strong); }

/* Small footnote citing what a chart was calculated from —
   deliberately quieter than body text so it reads as a source note,
   not another sentence of the argument. Lives inside .growth-bars
   itself now (below the row), so it takes that card's own padding
   rather than needing page-level paragraph spacing. */
.fine-print { font-size: 12px; color: var(--color-text-faint); margin: 14px 0 0; padding-top: 14px; border-top: 1px solid var(--color-border); }

@media (max-width: 640px) {
  .growth-bars__row { grid-template-columns: repeat(2, 1fr); row-gap: 24px; }
  .growth-bars__group:nth-child(odd) { border-left: none; }
  .growth-bars__group:nth-child(-n+2) { border-top: none; }
  .growth-bars__group:nth-child(n+3) { border-top: 1px solid var(--color-border); padding-top: 20px; }
}

/* Numbered watch-list — for a "接下来盯着看这几件事" paragraph that's
   structurally already an enumerated list (一是/二是/三是... or
   semicolon-separated items) but was written as one run-on sentence.
   Numbers are justified here (unlike a generic 01/02/03 decoration)
   because these genuinely are discrete, individually-scannable items
   a reader comes back to check off one at a time — not a sequence
   that has to be read in order. */
.watch-list {
  margin: 2em 0;
  display: flex; flex-direction: column; gap: 1px;
  background: var(--color-border);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.watch-list__item { display: flex; gap: 16px; padding: 18px 20px; background: var(--color-surface); align-items: flex-start; }
.watch-list__num {
  flex-shrink: 0;
  width: 28px; height: 28px;
  border-radius: 50%;
  background: rgba(217,164,65,0.1);
  border: 1px solid var(--color-border-strong);
  color: var(--color-accent-text);
  font-family: var(--font-serif); font-weight: 700; font-size: 13px;
  display: flex; align-items: center; justify-content: center;
}
.watch-list__body h3 { margin: 2px 0 4px; font-size: 15px; font-weight: 700; color: var(--color-text); font-family: var(--font-serif); }
.watch-list__body p { margin: 0; font-size: 14.5px; color: var(--color-text-muted); line-height: 1.6; }

.entry-footer {
  max-width: 720px;
  margin: 0 auto;
  padding-top: 32px;
  border-top: 1px solid var(--color-border);
}

.entry-tags { display: flex; flex-wrap: wrap; gap: 8px; }
.entry-tags a {
  font-size: 13px;
  padding: 6px 12px;
  border: 1px solid var(--color-border);
  border-radius: 20px;
  color: var(--color-text-muted);
}
.entry-tags a:hover { border-color: var(--color-accent-text); color: var(--color-accent-text); }

/* Prev/next article nav — the_post_navigation()'s own markup
   (.navigation.post-navigation > .nav-links > .nav-previous/.nav-next).
   Went through a dark block, then a light card — both read as "a UI
   component stuck on the page." This drops the box entirely: just the
   label + title typography sitting on the page directly, so it feels
   like a continuation of the article rather than a bolted-on widget.
   The top border is the same quiet divider .entry-footer already uses
   above its tags, not a card edge. */
.post-navigation {
  max-width: 720px;
  margin: 48px auto 0;
  padding-top: 32px;
  border-top: 1px solid var(--color-border);
}
.post-navigation .nav-links {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
}
/* Only one of prev/next exists at either end of the archive — let it
   take the full width instead of leaving an empty column. */
.post-navigation .nav-links:has(.nav-previous):not(:has(.nav-next)) .nav-previous,
.post-navigation .nav-links:has(.nav-next):not(:has(.nav-previous)) .nav-next {
  grid-column: 1 / -1;
}

.post-navigation .nav-next { text-align: right; }

.post-navigation .post-nav__label {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent-text);
  margin-bottom: 10px;
}
.post-navigation .post-nav__title {
  display: block;
  font-family: var(--font-serif); /* deliberately not matching .entry-content's sans-serif body text — reads more like a title this way */
  font-size: 16px;
  line-height: 1.5;
  font-weight: 400;
  color: var(--color-text);
  transition: color .15s ease;
}
.post-navigation a:hover .post-nav__title { color: var(--color-accent-text); }

/* =========================================================
   10. Page content (generic pages)
   ========================================================= */
/* Was 760px — .page-content below (the actual page.php reading
   column) is 720px, so the two were centred independently and never
   actually shared a left edge; on a plain prose page the ~20px drift
   is easy to miss, but next to a bold section heading like 我的账户/
   我的会员 (会员账户 page) it reads as a real misalignment. Matched to
   720 so this title and everything below it share one left edge. */
.page-header { max-width: 720px; margin: 0 auto; padding-block: 56px 12px; }
/* .page-header is shared by page.php (a real narrow reading-column
   title) and by home/archive/category/search.php (a listing page
   title that should span the same 1160px width as the .entry-grid
   sitting right below it). Reusing the same class for both meant the
   listing pages' title sat narrower AND centered differently from
   their own grid — this modifier undoes the width constraint so
   .container inside can take over at full width, without touching
   the page.php case that never had a problem. */
.page-header--wide { max-width: none; margin: 0; }
.page-header__title { font-size: clamp(28px, 4vw, 40px); }

/* The 登录/忘记密码/重设密码 page (page ID 680, set as PMPro's Login
   Page) is the one page.php page whose actual content card is much
   narrower than the shared 760px .page-header (440px — see
   .pmpro_login_wrap above) — left-aligned inside that wider box, the
   title started well left of the centred card beneath it and read as
   oddly adrift rather than as its heading. Scoped by WordPress's own
   page-id body class rather than to .page-header generally, so
   ordinary prose pages (关于我们/会员专区) keep their left-aligned title. */
.page-id-680 .page-header { text-align: center; }
/* Same reasoning, same fix, for the 取消会员 page (ID 294, PMPro's
   Cancel Page) — its #pmpro_cancel card was just centred (see the
   "no access"-style treatment in §17), so the plain left-aligned
   title above it now reads adrift the same way 登录's did. */
.page-id-294 .page-header { text-align: center; }
/* Same again for 编辑资料 (ID 667, PMPro's Member Profile Edit page —
   both the "edit profile" and "?view=change-password" forms render
   through this one page/ID), whose #pmpro_change_password card is
   also centred-card width like the others above. */
.page-id-667 .page-header { text-align: center; }
/* Same again for 会员结账 (ID 295, PMPro's Checkout page) and
   更新付款方式 (ID 293, PMPro's Billing page) — both render their
   #pmpro_form at the narrower 560px "focused column" width (see
   #pmpro_form below in §17), so the plain left-aligned title above
   them reads adrift the same way. */
.page-id-295 .page-header,
.page-id-293 .page-header { text-align: center; }

.page-content { max-width: 720px; margin: 0 auto; padding-bottom: 64px; font-size: 18px; }
.page-content p { margin-bottom: 1.4em; }
.page-content ul { list-style: revert; padding-left: 1.4em; margin-bottom: 1.4em; }
.page-content strong { color: var(--color-accent-text); }
/* Same treatment as .entry-content a (§9) — without this, a link here
   just inherits body text colour (the global reset sets a{color:inherit})
   and is visually indistinguishable from plain text. */
.page-content a { color: var(--color-accent-text); text-decoration: underline; text-underline-offset: 3px; }
/* Same override as .entry-content (§9) and for the same reason — this
   is the wrapper actual static Pages (like 会员专区) render into, which
   .entry-content is NOT, so the earlier .entry-content-only fix never
   applied here at all. Dark-navy text (var(--color-text), same token
   as the site's regular body copy) per explicit request, rather than
   the sitewide .btn--solid dark-ink default — this only affects a .btn
   that ends up inside ordinary page content, not the PMPro checkout
   page's own buttons. */
.page-content a.btn { text-decoration: none; }
.page-content a.btn--solid { color: var(--color-text); }
.page-content a.btn--outline { color: var(--color-accent-text); }

/* =========================================================
   11. Widgets / sidebar
   ========================================================= */
.widget { margin-bottom: 40px; }
.widget-title {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-accent-text);
  margin-bottom: 18px;
}
.widget ul li { padding: 8px 0; border-bottom: 1px solid var(--color-border); font-size: 14.5px; }

/* =========================================================
   12. Search form
   ========================================================= */
.search-form { display: flex; gap: 10px; }
.search-form input[type="search"] {
  flex: 1;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  color: var(--color-text);
}
.search-form input[type="search"]:focus { outline: none; border-color: var(--color-accent-text); }
.search-form button {
  background: var(--color-accent);
  color: var(--color-accent-ink);
  padding: 12px 20px;
  border-radius: var(--radius-sm);
  font-weight: 600;
}

/* =========================================================
   13. Pagination
   ========================================================= */
.pagination {
  display: flex;
  gap: 10px;
  justify-content: center;
  padding-top: 48px;
  flex-wrap: wrap;
}
.pagination a, .pagination span {
  min-width: 42px;
  height: 42px;
  padding: 0 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border);
  border-radius: 999px; /* pill for text links, true circle for the square number boxes — same value works for both */
  font-size: 14px;
  font-weight: 600;
  color: var(--color-text-muted);
  background: var(--color-surface);
  transition: border-color .15s ease, color .15s ease, background-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
/* The "…" gap marker shouldn't look like a clickable pill along with
   everything else — strip it back to plain muted text. */
.pagination .dots {
  border: none;
  background: transparent;
  padding: 0;
  min-width: auto;
}
.pagination .current {
  background: var(--color-accent);
  color: var(--color-accent-ink);
  border-color: var(--color-accent);
  box-shadow: 0 8px 18px -8px rgba(217, 164, 65, 0.55);
}
.pagination a:hover {
  border-color: var(--color-border-strong);
  color: var(--color-accent-text);
  background: rgba(217, 164, 65, 0.06);
  transform: translateY(-2px);
  box-shadow: 0 10px 18px -10px rgba(13, 23, 48, 0.22);
}

/* =========================================================
   14. Comments
   ========================================================= */
.comments-area { max-width: 720px; margin: 48px auto 0; }
.comment-list { margin-bottom: 32px; }
.comment-body { padding: 20px 0; border-bottom: 1px solid var(--color-border); }
.comment-meta { font-size: 13px; color: var(--color-text-faint); margin-bottom: 8px; }
.comment-form input, .comment-form textarea {
  width: 100%;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  color: var(--color-text);
  margin-bottom: 14px;
}
.comment-form input:focus, .comment-form textarea:focus { outline: none; border-color: var(--color-accent-text); }
.comment-reply-title { font-size: 22px; }

/* =========================================================
   15. Footer (dark surface — see .site-footer override in §1)
   ========================================================= */
.site-footer {
  border-top: 1px solid var(--color-border);
  padding-block: 56px 32px;
  margin-top: 80px;
}

.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: 40px;
  padding-bottom: 40px;
}

.footer-brand .site-title { margin-bottom: 10px; }
.footer-brand p { color: var(--color-text-muted); font-size: 14.5px; max-width: 320px; }

.footer-social {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 18px;
}
/* Facebook / Instagram get a uniform round icon badge; Xiaohongshu has
   no single widely-recognised monochrome glyph the way those two do,
   so it gets a same-height text pill instead of an invented logo. */
.footer-social__icon,
.footer-social__text {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 34px;
  color: var(--color-text-muted);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.footer-social__icon { width: 34px; border-radius: 50%; }
.footer-social__text { padding: 0 14px; border-radius: 17px; font-size: 13px; white-space: nowrap; }
.footer-social__icon:hover,
.footer-social__text:hover {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-accent-ink);
}

.footer-nav a {
  display: block;
  padding: 6px 0;
  font-size: 14.5px;
  color: var(--color-text-muted);
}
.footer-nav a:hover { color: var(--color-accent-text); }

.site-info {
  border-top: 1px solid var(--color-border);
  padding-top: 24px;
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 13px;
  color: var(--color-text-faint);
}

/* =========================================================
   16. Ebook opt-in (front-page.php, just below the hero)
   ========================================================= */
.ebook-optin { padding-block: 8px 0; }

.ebook-optin__panel {
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: 56px;
  align-items: center;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 48px;
  box-shadow: var(--shadow-card);
}

/* The "cover" is a self-contained dark card — re-declares the dark
   tokens locally (same trick as the header/footer scope in §1) so it
   reads as a deliberate branded object sitting on the page, rather than
   a stretched-thin reuse of the light palette. */
.ebook-cover {
  --color-bg: #0D1730;
  --color-text: #F2EFE6;
  --color-text-muted: rgba(242, 239, 230, 0.68);
  --color-accent-text: #D9A441;
  position: relative;
  aspect-ratio: 3 / 4;
  background: var(--color-bg);
  color: var(--color-text);
  border-radius: var(--radius-md);
  border-top: 4px solid var(--color-accent-text);
  padding: 26px 22px;
  overflow: hidden;
  box-shadow: 0 20px 40px -18px rgba(13, 23, 48, 0.5);
}

.ebook-cover__watermark {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-serif);
  font-size: 72px;
  font-weight: 700;
  color: rgba(242, 239, 230, 0.05);
  white-space: nowrap;
  transform: rotate(-14deg) scale(1.7);
  pointer-events: none;
}

.ebook-cover__content {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
}

.ebook-cover__badge {
  align-self: flex-start;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent-text);
  border: 1px solid rgba(217, 164, 65, 0.4);
  border-radius: 20px;
  padding: 5px 12px;
}

.ebook-cover__title {
  font-size: 23px;
  line-height: 1.3;
  margin: 22px 0 10px;
}

.ebook-cover__subtitle {
  font-size: 13px;
  color: var(--color-text-muted);
  margin: 0;
}

.ebook-cover__byline {
  font-size: 12px;
  color: var(--color-text-muted);
  margin-top: auto;
  padding-top: 16px;
}

.ebook-optin__copy .eyebrow { margin-bottom: 10px; }
.ebook-optin__copy h2 { font-size: 30px; margin-bottom: 16px; }
.ebook-optin__desc { color: var(--color-text-muted); font-size: 16px; margin-bottom: 28px; max-width: 560px; }

.ebook-optin__row { display: flex; gap: 12px; flex-wrap: wrap; }
.ebook-optin__row input[type="email"] {
  flex: 1 1 240px;
  min-width: 0;
  background: var(--color-bg);
  border: 1px solid var(--color-border-strong);
  border-radius: 6px; /* matches the checkout/PMPro form inputs (§17) — was the sharper var(--radius-sm)/3px */
  padding: 13px 16px;
  font-size: 15px;
  color: var(--color-text);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.ebook-optin__row input[type="email"]:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px rgba(217, 164, 65, 0.18);
}
.ebook-optin__row .btn { flex: 0 0 auto; }

/* PDPA-style consent checkbox — required, so ebook-optin.js blocks
   submission until it's checked. Replaces the old passive "不会有垃圾
   邮件" fineprint line with an actual opt-in action. */
.ebook-optin__consent {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-top: 14px;
}
.ebook-optin__consent input[type="checkbox"] {
  flex: none;
  width: 17px;
  height: 17px;
  margin-top: 2px;
  accent-color: var(--color-accent);
}
.ebook-optin__consent label {
  font-size: 13px;
  line-height: 1.55;
  color: var(--color-text-faint);
}

.ebook-optin__hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.ebook-optin__status { min-height: 1.4em; font-size: 14px; margin: 14px 0 0; }
.ebook-optin__status.is-success { color: #2F6B4F; }
.ebook-optin__status.is-error { color: #B3452F; }

.ebook-optin__fineprint { font-size: 12.5px; color: var(--color-text-faint); margin: 6px 0 0; }

/* =========================================================
   17. Paid Memberships Pro
   ========================================================= */
/* PMPro ships its own unstyled markup (account/checkout/levels/billing/
   cancel/invoice pages) that would otherwise render in plain default
   WordPress styling, clashing hard with the rest of the site. This maps
   its class names onto the same tokens/components used everywhere else
   (buttons, cards, form fields, tables) rather than introducing a
   separate visual language just for the membership flow. */

.pmpro_section { margin-bottom: 40px; }
.pmpro_section_content { padding-top: 8px; }
.pmpro_section .pmpro_section-toggle { font-family: var(--font-serif); font-size: 20px; }

.pmpro_card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-top: 3px solid var(--color-accent);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  margin-bottom: 28px;
  overflow: hidden;
}
.pmpro_card_content { padding: 28px 30px; }

/* 会员账户 page ([pmpro_account] shortcode) — its own username/email
   list and the "编辑资料 | 修改密码 | 登出" links below were both
   completely unstyled until now (no rule anywhere referenced these
   classes), so they fell back to the browser's raw bullet-list and
   underlined-link defaults — the one visibly "un-designed" spot on an
   otherwise consistently styled page. */
.pmpro_list.pmpro_list-plain {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.pmpro_list.pmpro_list-plain .pmpro_list_item {
  font-size: 15px;
  color: var(--color-text);
}
.pmpro_list.pmpro_list-plain .pmpro_list_item strong {
  color: var(--color-accent-text);
  font-weight: 600;
  margin-right: 4px;
}
.pmpro_card_actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}
/* Same small-outline-pill treatment as the invoice page's "打印或另存
   为PDF" button (§18-adjacent .pmpro_btn-plain.pmpro_btn-print rule)
   — these were plain coloured text separated by "|" characters, which
   read as leftover unstyled links rather than real actions. PMPro's
   own separator between them is hidden below; the pills' own borders
   plus the flex gap above do that job instead. */
.pmpro_card_action a {
  display: inline-block;
  padding: 7px 14px;
  border: 1px solid var(--color-border-strong);
  border-radius: 999px;
  color: var(--color-accent-text);
  font-weight: 600;
  font-size: 13.5px;
  text-decoration: none;
  transition: border-color .15s ease, background-color .15s ease;
}
.pmpro_card_action a:hover {
  border-color: var(--color-accent);
  background: rgba(217, 164, 65, 0.1);
}
.pmpro_card_action_separator { display: none; }
/* .pmpro_card_title is a sibling of .pmpro_card_content, not nested
   inside it — it has no horizontal padding of its own, so without the
   30px here it sits flush against the card's edge while the body text
   and the divider line's visible width don't line up with anything
   beneath it. That misalignment is what reads as "off" even though the
   heading text itself looks fine in isolation. */
.pmpro_card_title {
  font-family: var(--font-serif);
  font-size: 19px;
  font-weight: 700;
  color: var(--color-text);
  margin: 0;
  padding: 24px 30px 16px;
  border-bottom: 1px solid var(--color-border);
}
.pmpro_card_actions { padding: 20px 30px; border-top: 1px solid var(--color-border); }
/* On the order/invoice page (pages/invoice.php), .pmpro_card_actions
   is reused as a print-button TOOLBAR at the very top of the card —
   before the title, not after the content like on 会员账户. A
   border-top there just doubles up on the card's own gold top edge;
   border-bottom reads as a toolbar being separated from the heading
   below it instead. */
.pmpro_card > .pmpro_card_actions:first-child {
  border-top: none;
  border-bottom: 1px solid var(--color-border);
  padding: 14px 30px;
  justify-content: flex-end;
}
/* A quiet ghost-link doesn't read as a real toolbar action on its own
   — a subtle outline, tighter padding, and a touch smaller text/icon
   turn it into a proper small secondary button instead, matching the
   "least prominent, still clearly clickable" role it plays here. Two
   classes (matching the .pmpro_btn.pmpro_btn-password-toggle pattern
   in §4) so this wins over .pmpro_btn-plain's own padding regardless
   of source order. */
.pmpro_btn-plain.pmpro_btn-print {
  font-size: 13.5px;
  padding: 7px 14px;
  border: 1px solid var(--color-border-strong);
  border-radius: 999px;
}
.pmpro_btn-plain.pmpro_btn-print:hover {
  border-color: var(--color-accent);
  background: rgba(217, 164, 65, 0.1);
}
.pmpro_btn-plain.pmpro_btn-print svg {
  width: 14px;
  height: 14px;
}

/* "Welcome, X" on 会员账户 — get_avatar() returns a plain square
   <img> with no styling of its own, so without this it renders as a
   raw rectangular photo sitting on the baseline next to the heading
   text instead of a proper avatar. */
.pmpro_heading-with-avatar { display: flex; align-items: center; gap: 14px; }
.pmpro_heading-with-avatar img {
  flex: none;
  border-radius: 50%;
  border: 2px solid var(--color-border-strong);
}

/* "创始会员" heading on 会员账户's "我的会员" card — this section only
   ever lists memberships PMPro currently considers active, so a
   plain-text "生效中" badge (not tied to any real status data) is
   always accurate here, same green as the order table's "已付款" tag. */
#pmpro_account-membership .pmpro_card_title::after {
  content: '生效中';
  display: inline-block;
  margin-left: 10px;
  padding: 3px 11px;
  border-radius: 999px;
  /* .pmpro_card_title is serif — without resetting to the sans body
     font here, this badge inherited that serif weight instead of
     matching the (sans, from its own table-cell context) "已付款" tag
     it's meant to look identical to. */
  font-family: var(--font-sans);
  font-size: 12.5px;
  font-weight: 600;
  vertical-align: middle;
  background: rgba(47, 107, 79, 0.12);
  color: #2F6B4F;
}

/* Section-title icons — 我的账户/我的会员/订单记录 read as bare text
   next to every other heading on the site that pairs a circular icon
   badge with its title (登录页锁头像、取消订阅警示图标, etc.). Reuses
   the exact same person/star SVGs already used elsewhere (编辑资料's
   person icon, "选择会员等级" CTA's star) for a receipt/order icon
   uses feather's file-text, new to this stylesheet. */
#pmpro_account-profile > .pmpro_section_title,
#pmpro_account-membership > .pmpro_section_title,
#pmpro_account-orders > .pmpro_section_title,
#pmpro_order_list > .pmpro_section_title {
  display: flex;
  align-items: center;
  gap: 10px;
}
#pmpro_account-profile > .pmpro_section_title::before,
#pmpro_account-membership > .pmpro_section_title::before,
#pmpro_account-orders > .pmpro_section_title::before,
#pmpro_order_list > .pmpro_section_title::before {
  content: '';
  display: inline-block;
  flex: none;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background-color: rgba(217, 164, 65, 0.12);
  background-repeat: no-repeat;
  background-position: center;
  background-size: 16px 16px;
}
#pmpro_account-profile > .pmpro_section_title::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23C9A227' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='7' r='4'%3E%3C/circle%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'%3E%3C/path%3E%3C/svg%3E");
}
#pmpro_account-membership > .pmpro_section_title::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23C9A227' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'%3E%3C/polygon%3E%3C/svg%3E");
}
#pmpro_account-orders > .pmpro_section_title::before,
#pmpro_order_list > .pmpro_section_title::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23C9A227' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'%3E%3C/path%3E%3Cpolyline points='14 2 14 8 20 8'%3E%3C/polyline%3E%3Cline x1='16' y1='13' x2='8' y2='13'%3E%3C/line%3E%3Cline x1='16' y1='17' x2='8' y2='17'%3E%3C/line%3E%3Cpolyline points='10 9 9 9 8 9'%3E%3C/polyline%3E%3C/svg%3E");
}

/* Change-password form (编辑资料 page, ?view=change-password) — was
   just a plain full-width card of fields; narrowed + centred like
   the other single-task cards (登录/取消订阅), with a lock icon over
   the heading (same motif as the paywall gate — this is a security
   action too) and a divider between "prove it's you" (current
   password) and "pick the new one" (new/confirm), which read as one
   undifferentiated block of fields before. */
#pmpro_change_password .pmpro_card { max-width: 480px; margin: 0 auto; }
#pmpro_change_password .pmpro_form_legend { display: block; text-align: center; }
#pmpro_change_password .pmpro_form_heading {
  position: relative;
  padding-top: 58px;
}
#pmpro_change_password .pmpro_form_heading::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background-color: rgba(217, 164, 65, 0.12);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23C9A227' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='11' width='18' height='11' rx='2' ry='2'%3E%3C/rect%3E%3Cpath d='M7 11V7a5 5 0 0 1 10 0v4'%3E%3C/path%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 20px 20px;
}
#pmpro_change_password .pmpro_form_field-password_current {
  padding-bottom: 22px;
  margin-bottom: 6px;
  border-bottom: 1px solid var(--color-border);
}
/* The "当前密码" label sits inside .pmpro_form_field-password-toggle,
   which the *other* password-toggle fields (login/reset-password —
   direct children of the real .pmpro_form_field-password grid class)
   rely on align-self:center to vertically centre against the input
   in their shared grid row. This field's own wrapper is plain
   .pmpro_form_field-password_current though — a flex column, not
   that grid — so in flex terms align-self:center instead centres the
   label HORIZONTALLY, which is the bug (every other label here is
   left-aligned). Overriding it back to flex-start only in this one
   field's context, rather than touching the shared rule other forms
   still depend on for the vertical case. */
#pmpro_change_password .pmpro_form_field-password_current .pmpro_form_field-password-toggle {
  align-self: flex-start;
}
#pmpro_change_password .pmpro_form_hint {
  background: rgba(217, 164, 65, 0.08);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--color-text-muted);
}

/* 编辑资料's OTHER view — the actual "Account Information" name/email
   form (#pmpro_member_profile_edit, pmpro_member_profile_edit_form())
   — was left full-width with a plain left-aligned heading while its
   sibling view (#pmpro_change_password, same page, reached via ?view=
   change-password) got the narrow-card + icon treatment above, so the
   two looked like different pages entirely rather than two tabs of
   the same one. Icon-over-centred-heading language matched to
   #pmpro_change_password's (a person glyph here instead of the lock,
   since this view isn't a security action) — but NOT the 480px card
   width: with 4 fields instead of 3 short password ones, that read as
   too cramped, so this keeps the page's normal full 720px measure. */
#pmpro_member_profile_edit .pmpro_form_legend { display: block; text-align: center; }
#pmpro_member_profile_edit .pmpro_form_heading {
  position: relative;
  padding-top: 58px;
}
#pmpro_member_profile_edit .pmpro_form_heading::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background-color: rgba(217, 164, 65, 0.12);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23C9A227' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='7' r='4'%3E%3C/circle%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'%3E%3C/path%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 20px 20px;
}
#pmpro_member_profile_edit .pmpro_form_hint {
  background: rgba(217, 164, 65, 0.08);
  border-radius: var(--radius-sm);
  padding: 8px 12px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--color-text-muted);
}

/* 忘记密码 (登录 page, ?action=reset_pass) — no in-card heading renders
   here at all (pmpro_is_login_page() suppresses it, see login.php),
   so the page's own H1 ("忘记密码", already centred — see .page-id-680
   above) is the only title; the same lock icon used on the paywall
   gate/取消订阅/编辑资料 goes above the instructions paragraph instead,
   keeping this "account security" family of cards visually
   consistent. Form fields stay left-aligned (only the icon+intro
   text are centred) — same split change-password uses. */
.pmpro_lost_password_wrap .pmpro_lost_password-instructions {
  position: relative;
  text-align: center;
  padding-top: 58px;
  color: var(--color-text-muted);
  font-size: 14.5px;
  max-width: 380px;
  margin: 0 auto 18px;
}
.pmpro_lost_password_wrap .pmpro_lost_password-instructions::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background-color: rgba(217, 164, 65, 0.12);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23C9A227' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='11' width='18' height='11' rx='2' ry='2'%3E%3C/rect%3E%3Cpath d='M7 11V7a5 5 0 0 1 10 0v4'%3E%3C/path%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 20px 20px;
}

/* Password-strength meter (WP core's password-strength-meter.js adds
   one of these classes to #pass-strength-result and fills in its own
   localized text — 弱/中/强/太短 — we only style the states, not the
   copy). Ships completely unstyled on the front end since PMPro's own
   base.css is dequeued (inc/enqueue.php) — its 4-state colour scheme
   is reused here as a reference, translated onto this site's own
   tokens. "强"/strong gets a teal accent rather than sitting on the
   gold scale with "good" — a distinct colour for the one state that
   isn't "acceptable, but keep going" reads clearer at a glance, and
   teal doesn't conflict with the no-red-anywhere rule the gold/navy
   palette otherwise follows. */
#pass-strength-result {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 7px 14px;
  margin-top: 8px;
  font-size: 12.5px;
  font-weight: 600;
  text-align: center;
  opacity: 0;
  transition: opacity .15s ease;
}
#pass-strength-result.short,
#pass-strength-result.bad {
  background: rgba(156, 107, 18, 0.08);
  border-color: var(--color-border-strong);
  color: var(--color-text-muted);
  opacity: 1;
}
#pass-strength-result.good {
  background: rgba(217, 164, 65, 0.12);
  border-color: var(--color-accent);
  color: var(--color-accent-text);
  opacity: 1;
}
#pass-strength-result.strong {
  background: rgba(15, 118, 110, 0.10);
  border-color: #0F766E;
  color: #0F766E;
  opacity: 1;
}

/* The "no access" gate card specifically (inc/pmpro-no-access.php
   rewrites its copy; PMPro renders it via the generic .pmpro_card
   markup) — for an anonymous or non-member reader mid-article this IS
   the conversion moment, so it's treated as a centred empty-state
   rather than a left-aligned generic card like the account/checkout
   ones. The lock icon is promoted from a small inline glyph glued to
   the heading text to its own circular badge above it, echoing the
   eyebrow-label-over-headline rhythm .price-callout already uses. */
.pmpro_content_message {
  max-width: 480px;
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}
.pmpro_content_message .pmpro_card_title {
  display: flex;
  flex-direction: column;
  align-items: center;
  font-size: 21px;
  border-bottom: none;
  padding: 36px 30px 8px;
}
.pmpro_content_message .pmpro_card_title svg {
  display: block;
  width: 44px;
  height: 44px;
  padding: 11px;
  margin-bottom: 16px;
  background: rgba(217, 164, 65, 0.12);
  border-radius: 50%;
  stroke-width: 1.6;
}
.pmpro_content_message .pmpro_card_content { padding: 4px 30px 28px; }
.pmpro_content_message .pmpro_card_content p:first-child {
  color: var(--color-text-muted);
  font-size: 15.5px;
  line-height: 1.7;
}
.pmpro_content_message .pmpro_card_content .pmpro_btn { margin-top: 6px; }
.pmpro_content_message .pmpro_card_actions {
  text-align: center;
  font-size: 14px;
  color: var(--color-text-faint);
}

/* Login / lost-password / reset-password cards, same reasoning as
   #pmpro_form below — a two-field form reads oddly stretched across
   the full 720px prose measure page.php gives ordinary page content. */
.pmpro_login_wrap,
.pmpro_lost_password_wrap,
.pmpro_reset_password_wrap { max-width: 440px; margin: 0 auto; }

/* Checkout / billing / cancel forms read better as a single focused
   column than stretched to the full 720px reading measure — this is a
   short, linear "confirm and pay" flow, not prose. */
#pmpro_form { max-width: 560px; margin: 0 auto; }

/* Level picker grid (Membership Levels page) */
.pmpro_level_group { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.pmpro_level { display: flex; flex-direction: column; height: 100%; }
/* pmpro_level_name_text / _cost_text / _description_text live inside
   the "Membership Information" summary card on checkout/billing —
   PMPro hands us plain sentences ("You have selected the X level.",
   "The price is Y."), so the visual hierarchy below is built entirely
   with CSS on top of that same markup rather than a template rewrite. */
.pmpro_level_name_text,
.pmpro_level_cost_text,
.pmpro_level_description_text { font-size: 16px; line-height: 1.7; color: var(--color-text-muted); margin: 0 0 16px; }

/* The level name is the first thing read on checkout — give it the
   same "badge" treatment as the level name badge on the pricing page,
   instead of just a bolded word sitting mid-sentence. */
.pmpro_level_name_text strong {
  display: inline-block;
  color: var(--color-accent-ink);
  background: var(--color-accent);
  font-weight: 700;
  font-size: 0.85em;
  padding: 3px 10px;
  border-radius: 20px;
}

/* The feature list (level description) — same checkmark treatment as
   the pricing card, at checkout scale, so the value being paid for is
   still visible right up to the point of payment rather than trailing
   off into unstyled prose. */
.pmpro_level_description_text ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.pmpro_level_description_text li {
  position: relative;
  padding-left: 30px;
  margin-bottom: 14px;
  font-size: 15px;
  line-height: 1.6;
}
.pmpro_level_description_text li:last-child { margin-bottom: 0; }
/* A bare "✓" glyph floating in space read as an afterthought — a small
   filled circle gives it the same weight as a real UI element. */
.pmpro_level_description_text li::before {
  content: "✓";
  position: absolute;
  left: 0;
  top: 1px;
  width: 18px;
  height: 18px;
  line-height: 18px;
  text-align: center;
  border-radius: 50%;
  background: var(--color-accent);
  color: var(--color-accent-ink);
  font-size: 11px;
  font-weight: 700;
}

/* The price panel — pulled out of plain paragraph flow into its own
   dark "hero" surface (same custom-property rescoping trick used for
   .site-header/.site-footer — every var(--color-*) rule below just
   picks up the dark palette while nested here), since it's the one
   number a checking-out visitor actually scans for and deserves more
   visual weight than a tinted box in the page's own light surface. */
.pmpro_level_cost_text {
  --color-bg:         #0D1730;
  --color-text:       #F2EFE6;
  --color-text-faint: rgba(242, 239, 230, 0.6);

  background: var(--color-bg);
  border-radius: var(--radius-md);
  padding: 26px 20px;
  margin-top: 20px;
  text-align: center;
}
.pmpro_price_eyebrow {
  display: block;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: 10px;
}
.pmpro_price_hero {
  display: block;
  font-family: var(--font-serif);
  font-size: 30px;
  color: var(--color-text);
}
.pmpro_price_hero strong {
  color: var(--color-accent);
  font-weight: 700;
  font-size: 1.2em;
}
.pmpro_level_expiration_text { color: var(--color-text-faint); font-size: 13px; }

.pmpro_checkout_trust {
  font-size: 12.5px;
  color: var(--color-text-faint);
  text-align: center;
  margin: 12px 0 0;
}

/* .pmpro_level-price is the *Membership Levels table* price cell (a
   different context — the pricing table row, not the checkout summary
   card above) — kept distinctly serif/accent since it's the only price
   indicator on that page. */
.pmpro_level-price { font-family: var(--font-serif); font-size: 17px; color: var(--color-accent-text); margin: 0; }
.pmpro_level-price strong { font-weight: 700; }

/* Forms (checkout / billing / profile) */
.pmpro_form_fields { display: flex; flex-direction: column; gap: 18px; }
.pmpro_form_fields.pmpro_cols-2,
.pmpro_cols-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
/* 会员账户 page's "订阅方案 / 下次扣费日" (PMPro always uses the
   "cols-3" class here regardless of how many fields actually exist —
   2 in our case, since there's no expiration date on a non-expiring
   subscription) was falling back to the plain stacked .pmpro_list
   layout: no .pmpro_cols-3 rule existed at all, and even a bare one
   would lose to .pmpro_list.pmpro_list-plain's own flex-column rule
   (§17) on specificity, hence the .pmpro_list.pmpro_cols-3 compound
   here. auto-fit rather than a fixed 3-column track so it still looks
   intentional whether 2 or 3 fields are present. */
.pmpro_list.pmpro_cols-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 18px; }
.pmpro_form_fieldset { border: none; padding: 0; margin: 0 0 24px; }
.pmpro_form_fieldset legend { font-family: var(--font-serif); font-size: 18px; margin-bottom: 12px; padding: 0; }
.pmpro_form_field { display: flex; flex-direction: column; gap: 6px; }
.pmpro_form_label { font-size: 13.5px; font-weight: 600; color: var(--color-text-muted); }

/* "我已阅读并同意会员服务条款" checkbox, just above the submit button —
   the box sits inline at the left of the sentence (same look as the
   ebook opt-in consent), in normal weight rather than the bold field-
   label style, so it reads as a sentence, not a form field name. */
#pmpro_tos_fields { margin: 22px 0 4px; }
#pmpro_tos_fields .pmpro_form_field { display: block; }
#pmpro_tos_fields .pmpro_form_label {
  display: block;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.7;
  color: var(--color-text-muted);
  cursor: pointer;
}
#pmpro_tos_fields input[type="checkbox"] {
  display: inline-block;
  width: 18px;
  height: 18px;
  margin: 0 10px 0 0;
  vertical-align: -3px;
  accent-color: var(--color-accent);
}
#pmpro_tos_fields a { color: var(--color-accent-text); text-decoration: underline; }
.pmpro_form_label .pmpro_asterisk { color: var(--color-accent-text); }
/* The * itself is wrapped in <abbr title="Required Field">, which
   every browser's default stylesheet underlines with a dotted line —
   on this site's light form backgrounds it reads like a stray "...."
   after every required-field label (checkout, change-password, every
   PMPro form), not an intentional design choice. Keep the real
   tooltip (title attribute still works on hover), drop the dotted
   line that comes bundled with it. */
.pmpro_asterisk abbr {
  text-decoration: none;
  cursor: help;
}

.pmpro_form_input,
.pmpro_form input[type="text"],
.pmpro_form input[type="email"],
.pmpro_form input[type="password"],
.pmpro_form input[type="tel"],
.pmpro_form input[type="number"],
.pmpro_form select,
.pmpro_form textarea,
/* The login / lost-password / reset-password views inside
   .pmpro_login_wrap etc. render through WordPress core's own
   wp_login_form()/retrieve_password_form() — plain <input class="input">
   markup, not PMPro's .pmpro_form_* classes — so without these three
   selectors added to the list, this one page (inc/pmpro-no-access.php's
   login link, and the header's own "登录" link, both now point at PMPro's
   Login page once pmpro_login_page_id is set) would render as raw
   unstyled default WordPress inputs against everything else here. */
.pmpro_login_wrap input.input,
.pmpro_lost_password_wrap input.input,
.pmpro_reset_password_wrap input.input {
  width: 100%;
  background: var(--color-bg);
  border: 1px solid var(--color-border-strong);
  border-radius: 6px;
  padding: 12px 14px;
  font-size: 15px;
  color: var(--color-text);
  font-family: inherit;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.pmpro_form_input:focus,
.pmpro_form input:focus,
.pmpro_form select:focus,
.pmpro_form textarea:focus,
.pmpro_login_wrap input.input:focus,
.pmpro_lost_password_wrap input.input:focus,
.pmpro_reset_password_wrap input.input:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px rgba(217, 164, 65, 0.18);
}

/* Same three wraps' <p class="login-username">/etc. rows and their
   own submit button + "记住我" checkbox — none of this is inside a
   .pmpro_form, so none of the rules above reach it on their own. */
.pmpro_login_wrap p,
.pmpro_lost_password_wrap p,
.pmpro_reset_password_wrap p { margin: 0 0 18px; }
.pmpro_login_wrap label,
.pmpro_lost_password_wrap label,
.pmpro_reset_password_wrap label {
  display: block;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--color-text-muted);
  margin-bottom: 6px;
}
.pmpro_login_wrap .login-remember label,
.pmpro_lost_password_wrap .login-remember label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 400;
  color: var(--color-text-muted);
  margin-bottom: 0;
}
.pmpro_login_wrap .login-submit,
.pmpro_lost_password_wrap .login-submit,
.pmpro_reset_password_wrap .login-submit { margin-bottom: 0; }

/* The show/hide-password toggle's own JS (login.php) moves it INSIDE
   <p class="login-password"> as a plain block sibling after the input
   — the .pmpro_form_field-password grid trick used elsewhere (account
   edit / checkout) doesn't apply here since this paragraph isn't that
   grid container, so without this it would drop to its own line below
   the field instead of sitting on the field's right edge. */
.pmpro_login_wrap .login-password,
.pmpro_lost_password_wrap .login-password { position: relative; }
.pmpro_login_wrap .login-password input.input,
.pmpro_lost_password_wrap .login-password input.input { padding-right: 44px; }
.pmpro_login_wrap .login-password .pmpro_form_field-password-toggle,
.pmpro_lost_password_wrap .login-password .pmpro_form_field-password-toggle {
  position: absolute;
  right: 4px;
  bottom: 4px;
}
.pmpro_login_wrap input[type="submit"],
.pmpro_login_wrap .button.button-primary,
.pmpro_lost_password_wrap input[type="submit"],
.pmpro_lost_password_wrap .button.button-primary,
.pmpro_reset_password_wrap input[type="submit"],
.pmpro_reset_password_wrap .button.button-primary {
  width: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 16px 26px;
  font-size: 16px;
  font-weight: 600;
  font-family: inherit;
  border: none;
  border-radius: 8px;
  background: var(--color-accent);
  color: var(--color-accent-ink);
  box-shadow: 0 8px 20px -8px rgba(217, 164, 65, 0.55);
  transition: background-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
.pmpro_login_wrap input[type="submit"]:hover,
.pmpro_login_wrap .button.button-primary:hover,
.pmpro_lost_password_wrap input[type="submit"]:hover,
.pmpro_lost_password_wrap .button.button-primary:hover,
.pmpro_reset_password_wrap input[type="submit"]:hover,
.pmpro_reset_password_wrap .button.button-primary:hover {
  background: var(--color-accent-strong);
  color: var(--color-accent-ink);
  transform: translateY(-1px);
  box-shadow: 0 10px 24px -8px rgba(217, 164, 65, 0.65);
}

/* PMPro's password field markup puts the show/hide toggle as a plain
   sibling *after* the input, not wrapping it — styled with no overrides
   it just falls to its own line below the field instead of sitting
   inside it like a normal password-reveal control. Grid lets the input
   and the toggle share one cell (row 2) so the toggle can be pinned to
   the input's right edge without knowing any pixel heights. */
.pmpro_form_field-password {
  display: grid;
  grid-template-columns: 1fr;
  row-gap: 6px;
}
.pmpro_form_field-password > label { grid-column: 1; grid-row: 1; }
.pmpro_form_field-password > input {
  grid-column: 1;
  grid-row: 2;
  padding-right: 44px;
}
.pmpro_form_field-password-toggle {
  grid-column: 1;
  grid-row: 2;
  justify-self: end;
  align-self: center;
  margin-right: 4px;
}
/* THE actual root cause of the invisible eye icon, found by bisecting
   the real page in a real browser (headless Chrome, not just Quick
   Look) rather than guessing at the icon/colour mechanism again: this
   selector is a single class, same specificity as the generic
   ".pmpro_btn" rule below (§5) which also matches this same button
   (it carries both classes) — and ".pmpro_btn" is declared LATER in
   this file, so on padding/background/border-radius specifically it
   was winning, every time, regardless of anything done to the icon
   itself. ".pmpro_btn"'s padding:12px 26px alone is wider than this
   34px box; combined with this site's global box-sizing:border-box
   (§2), that leaves zero content-box width for anything inside — not
   a colour or sizing problem on the icon at all, the icon never had
   any room to render in. Fixed by compounding both classes so this
   selector's specificity wins outright, independent of source order —
   more robust than reordering rules, which the NEXT edit to either
   block could silently re-break. */
.pmpro_btn.pmpro_btn-password-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 50%;
  background: transparent;
}
.pmpro_btn.pmpro_btn-password-toggle:hover { background: rgba(217, 164, 65, 0.12); }
/* The icon itself (colour, sizing) was never actually the problem —
   see the .pmpro_btn.pmpro_btn-password-toggle fix above (§4) for the
   real cause (a padding conflict squeezing this span's content-box to
   0, found by bisecting the real page in headless Chrome). PMPro's
   own inline SVG + var(--pmpro--color--accent) renders correctly on
   its own once it actually has room; these two lines are just a small
   safety margin, not load-bearing. */
.pmpro_icon-eye svg {
  width: 20px;
  height: 20px;
}
/* Keep the "显示密码"/"隐藏密码" label for screen readers — the eye
   icon alone reads clearly enough visually and fits inside the field. */
.pmpro_form_field-password-toggle-state {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.pmpro_form_submit { margin-top: 24px; }

/* Buttons — map onto the existing .btn system rather than duplicating it */
.pmpro_btn,
.pmpro_form input[type="submit"],
.pmpro_form button[type="submit"] {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 12px 26px;
  font-size: 15px;
  font-weight: 600;
  border-radius: var(--radius-sm);
  background: var(--color-accent);
  color: var(--color-accent-ink);
  border: none;
  transition: background-color .15s ease;
}
.pmpro_btn:hover,
.pmpro_form input[type="submit"]:hover,
.pmpro_form button[type="submit"]:hover { background: var(--color-accent-strong); color: var(--color-accent-ink); }

/* On checkout/billing/cancel specifically, the submit button is the
   single next action — make it read as the obvious final step rather
   than a button-sized-to-its-label sitting at the left edge. A softer
   radius and a bit of lift (vs. the sharp 3px/flat look used for small
   inline buttons elsewhere) reads more like a deliberate "confirm and
   pay" moment. */
#pmpro_form input[type="submit"],
#pmpro_form button[type="submit"] {
  width: 100%;
  justify-content: center;
  padding: 16px 26px;
  font-size: 16px;
  border-radius: 8px;
  box-shadow: 0 8px 20px -8px rgba(217, 164, 65, 0.55);
  transition: background-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
#pmpro_form input[type="submit"]:hover,
#pmpro_form button[type="submit"]:hover {
  transform: translateY(-1px);
  box-shadow: 0 10px 24px -8px rgba(217, 164, 65, 0.65);
}
#pmpro_form input[type="submit"]:active,
#pmpro_form button[type="submit"]:active { transform: translateY(0); }

.pmpro_btn-plain,
.pmpro_btn-cancel {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  color: var(--color-accent-text);
  padding: 12px 20px;
}
.pmpro_btn-plain:hover,
.pmpro_btn-cancel:hover { background: rgba(217, 164, 65, 0.1); }
/* The global img/svg reset (§1) sets svg{display:block}, which is
   right for standalone icons but breaks an icon+label button like
   .pmpro_btn-print — the feather-printer SVG would otherwise drop
   onto its own line above the text instead of sitting inline beside
   it. inline-flex on the button (above) plus this puts them back in
   a single row. */
.pmpro_btn-plain svg,
.pmpro_btn-cancel svg { flex: none; }

/* Status pills — "Paid"/"Refunded"/etc. next to the order title on
   the invoice page, and in the Order History table's status column. */
.pmpro_tag {
  display: inline-block;
  vertical-align: middle;
  margin-left: 8px;
  padding: 3px 11px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 600;
}
.pmpro_tag-success { background: rgba(47, 107, 79, 0.12); color: #2F6B4F; }
.pmpro_tag-error { background: rgba(179, 69, 47, 0.12); color: #B3452F; }
.pmpro_tag-alert { background: rgba(217, 164, 65, 0.15); color: var(--color-accent-text); }
.pmpro_tag-discount-code {
  background: var(--color-surface-2);
  color: var(--color-text);
  font-family: 'SF Mono', Consolas, monospace;
  letter-spacing: 0.03em;
}

/* Font-size utility classes PMPro's own templates apply directly
   (order page title, "RM25 paid on ..." line, order line-item date
   ranges) — none of these existed before, so they fell back to
   plain unstyled body text regardless of the class name's intent. */
.pmpro_font-x-large { font-size: 22px; }
.pmpro_font-large { font-size: 17px; }
.pmpro_font-small { font-size: 13px; color: var(--color-text-faint); }
.pmpro_card_title.pmpro_font-x-large { font-size: 22px; }
/* The "RM25 paid on ..." line sits directly above the order's line-
   item table — giving it the same serif/weight treatment as a card
   title (rather than just a slightly bigger body-text size) marks it
   as the moment's headline figure instead of another line of prose. */
#pmpro_order_single-items > h3.pmpro_font-large {
  font-family: var(--font-serif);
  font-size: 19px;
  font-weight: 700;
  color: var(--color-text);
  margin: 0 0 14px;
}

/* Empty-height dividers PMPro's templates sprinkle between sections
   in place of margin — with no rule at all these collapse to 0px and
   do nothing. */
.pmpro_spacer { height: 22px; }

/* Print-only receipt logo (inc/pmpro-invoice.php echoes this <img> on
   every view of the order page, screen included) — see §18 for why
   this needs to be a real <img> rather than a CSS background-image. */
.pmpro_print_logo { display: none; }

/* Order-meta list (order date / payment method / pay to / bill to) —
   .pmpro_cols-2 (§17 above) already puts this in a 2-column grid; what
   was missing is turning the plain "label" text into a small caption
   sitting above its value instead of running into it on one line. */
.pmpro_list.pmpro_list-with-labels .pmpro_list_item {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.pmpro_list_item_label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-faint);
  font-weight: 600;
}

/* Section headings PMPro renders on their own outside a .pmpro_card
   (e.g. "Order History" above the orders table) — previously fell
   back to the browser's plain default h2. */
.pmpro_section_title {
  font-family: var(--font-serif);
  font-size: 22px;
  font-weight: 700;
  color: var(--color-text);
  margin: 0 0 16px;
}

/* "查看我的会员账户 →" / "← 查看全部订单" footer links under the
   invoice/order card. */
.pmpro_actions_nav {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 24px;
  font-size: 14.5px;
  font-weight: 600;
}

/* Cancel-membership confirmation screen (pages/cancel.php's "confirm"
   branch, wrapped in <section id="pmpro_cancel">) — treated as its
   own deliberate moment, same reasoning as .pmpro_content_message:
   a centred card with a caution icon instead of a plain left-aligned
   paragraph, since this is a "are you sure" decision point too.
   PMPro's own class names are the reverse of what you'd guess —
   .pmpro_btn-submit is the "yes, cancel" button (it's the form's
   <input type=submit>) and .pmpro_btn-cancel is the "no, keep it"
   link (it "cancels" the cancel action) — the two blocks below use
   those real classes to swap which one LOOKS primary: keeping the
   membership becomes the solid gold button, cancelling becomes the
   quiet outline one, rather than the default PMPro styling making
   the destructive action the visually loudest thing on the page. */
#pmpro_cancel .pmpro_card_content { text-align: center; }
#pmpro_cancel .pmpro_card_content::before {
  content: '';
  display: block;
  width: 48px;
  height: 48px;
  margin: 4px auto 18px;
  border-radius: 50%;
  background-color: rgba(217, 164, 65, 0.12);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23C9A227' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z'%3E%3C/path%3E%3Cline x1='12' y1='9' x2='12' y2='13'%3E%3C/line%3E%3Cline x1='12' y1='17' x2='12.01' y2='17'%3E%3C/line%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 22px 22px;
}
#pmpro_cancel .pmpro_card_content > p:first-child {
  font-family: var(--font-serif);
  font-size: 20px;
  font-weight: 700;
  color: var(--color-text);
  max-width: 420px;
  margin: 0 auto 10px;
}
#pmpro_cancel .pmpro_card_content > p:nth-of-type(2) {
  font-size: 14.5px;
  color: var(--color-text-muted);
  max-width: 420px;
  margin: 0 auto;
}
#pmpro_cancel .pmpro_form_submit {
  max-width: 340px;
  margin: 26px auto 0;
  display: flex;
  flex-direction: column-reverse;
  gap: 12px;
}
#pmpro_cancel .pmpro_btn-cancel {
  display: block;
  width: 100%;
  text-align: center;
  background: var(--color-accent);
  color: var(--color-accent-ink);
  padding: 15px 26px;
  border-radius: 8px;
  font-weight: 700;
  box-shadow: 0 8px 20px -8px rgba(217, 164, 65, 0.55);
  transition: background-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
#pmpro_cancel .pmpro_btn-cancel:hover {
  background: var(--color-accent-strong);
  transform: translateY(-1px);
  box-shadow: 0 10px 24px -8px rgba(217, 164, 65, 0.65);
}
#pmpro_cancel input.pmpro_btn-submit[type="submit"] {
  width: 100%;
  background: transparent;
  color: var(--color-text-muted);
  border: 1px solid var(--color-border-strong);
  box-shadow: none;
  font-weight: 600;
}
#pmpro_cancel input.pmpro_btn-submit[type="submit"]:hover {
  background: rgba(217, 164, 65, 0.08);
  color: var(--color-accent-text);
  transform: none;
  box-shadow: none;
}

/* "You're not a member yet" empty state on the 会员账户 page (the
   #pmpro_account-membership-none div — PMPro's own id, shown in place
   of a level card when the logged-in visitor holds no membership).
   Same centred-icon-plus-CTA treatment as .pmpro_content_message and
   #pmpro_cancel, so every "become/stay a member" moment on the site
   reads as one consistent idiom rather than each being styled ad hoc.
   The plain sentence-with-inline-link PMPro renders here becomes a
   short line of muted text with a real button below it — CSS-only
   (no template override), so the link is what gets the button
   treatment even though it's still just one <p> in the markup. */
#pmpro_account-membership-none .pmpro_card_content { text-align: center; padding-top: 8px; }
#pmpro_account-membership-none .pmpro_card_content::before {
  content: '';
  display: block;
  width: 44px;
  height: 44px;
  margin: 4px auto 14px;
  border-radius: 50%;
  background-color: rgba(217, 164, 65, 0.12);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23C9A227' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolygon points='12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2'%3E%3C/polygon%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 20px 20px;
}
#pmpro_account-membership-none .pmpro_card_content p {
  color: var(--color-text-muted);
  font-size: 15px;
  margin-bottom: 0;
}
#pmpro_account-membership-none .pmpro_card_content p a {
  /* display:block (not inline-block) so it actually drops to its own
     line under the sentence instead of sitting beside it — inline-
     block still flows inline with the preceding text, it only adds
     box-model properties. width:fit-content + auto margins re-centres
     just the button itself once it's block-level, instead of letting
     it stretch to the paragraph's full width. */
  display: block;
  width: fit-content;
  margin: 14px auto 0;
  padding: 12px 26px;
  background: var(--color-accent);
  color: var(--color-accent-ink);
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-size: 14.5px;
  text-decoration: none;
  transition: background-color .15s ease;
}
#pmpro_account-membership-none .pmpro_card_content p a:hover { background: var(--color-accent-strong); }

/* Tables (Membership Levels comparison, Orders/Invoices, Billing history).
   Explicit table/table-row/table-cell display below is defensive: PMPro's
   own base.css (now dequeued) used to force these into a display:grid
   "stacked card" layout below a breakpoint via data-title attributes —
   without that stylesheet loaded these elements fall back to *whatever
   the browser's UA default is*, and that default can be surprisingly
   fragile once ancestors set unrelated display contexts. Pinning it
   here guarantees a normal table regardless. */
.pmpro_table,
.pmpro_billing_table,
.pmpro_invoices_table {
  display: table;
  width: 100%;
  border-collapse: collapse;
  font-size: 14.5px;
}
.pmpro_table thead,
.pmpro_billing_table thead,
.pmpro_invoices_table thead { display: table-header-group; }
.pmpro_table tbody,
.pmpro_billing_table tbody,
.pmpro_invoices_table tbody { display: table-row-group; }
.pmpro_table tr,
.pmpro_billing_table tr,
.pmpro_invoices_table tr { display: table-row; }
.pmpro_table th,
.pmpro_table td,
.pmpro_billing_table th,
.pmpro_billing_table td,
.pmpro_invoices_table th,
.pmpro_invoices_table td {
  display: table-cell;
  border: 1px solid var(--color-border);
  padding: 12px 14px;
  text-align: left;
  vertical-align: middle;
}
.pmpro_table thead th,
.pmpro_billing_table thead th,
.pmpro_invoices_table thead th {
  font-family: var(--font-serif);
  color: var(--color-accent-text);
  background: var(--color-surface-2);
}
/* The order/invoice table (pages/invoice.php) also uses a <th> for the
   row's own item-description cell in tbody (semantic "row header", not
   a column header) — without scoping the rule above to thead, that
   cell picked up the same gold/serif/shaded treatment as a real column
   heading, which read as a stray highlighted row above a plain one. */
.pmpro_table tbody th {
  font-family: inherit;
  font-weight: 600;
  color: var(--color-text);
  background: none;
}

/* Order/invoice line-items table (pages/invoice.php's "已于 ... 支付
   RM25" table) is always Description + Amount, so :last-child safely
   means "the Amount column" here — scoped to this one table by its
   wrapper id, since .pmpro_table is shared with the 4-column Order
   History table elsewhere, where last-child is Status, not Amount. */
#pmpro_order_single-items .pmpro_table th:last-child,
#pmpro_order_single-items .pmpro_table td:last-child {
  text-align: center;
}

/* Order History table (会员账户 page and "查看全部订单") — 等级/总计/状态
   columns read better centred; 日期 stays left-aligned since dates are
   naturally left-reading. Needs the ".pmpro_table th/td" element-type
   compound (not a bare class) to out-specificity the generic
   ".pmpro_table th, .pmpro_table td" left-align rule above (§17) — a
   plain single-class selector ties on specificity and loses to source
   order.
   PMPro is inconsistent about the amount column's class name between
   the two templates that render "the same" table: the account page's
   [pmpro_account] shortcode gives the header <th> class
   "pmpro_table_order-total" while its own data <td> (and the
   standalone "查看全部订单" page's th AND td) both use
   "pmpro_table_order-amount" — so without covering both class names,
   the 总计 header stayed left-aligned while the RM25 value under it
   was centred, reading as misaligned even though each rule was
   "working" on its own class. */
.pmpro_table th.pmpro_table_order-level,
.pmpro_table td.pmpro_table_order-level,
.pmpro_table th.pmpro_table_order-amount,
.pmpro_table td.pmpro_table_order-amount,
.pmpro_table th.pmpro_table_order-total,
.pmpro_table td.pmpro_table_order-total,
.pmpro_table th.pmpro_table_order-status,
.pmpro_table td.pmpro_table_order-status {
  text-align: center;
}

/* Order History table specifically (not the line-items receipt table,
   not any future comparison table using plain .pmpro_table) reads
   more like a clean list and less like a spreadsheet with just
   horizontal rules between rows instead of a full grid of borders.
   The .pmpro_table.pmpro_table_orders compound is needed to out-
   specificity the generic ".pmpro_table th/td { border: ... }" rule
   above (§17). */
.pmpro_table.pmpro_table_orders th,
.pmpro_table.pmpro_table_orders td {
  border-left: none;
  border-right: none;
  border-top: none;
  border-bottom: 1px solid var(--color-border);
}
.pmpro_table.pmpro_table_orders thead th {
  border-bottom: 1px solid var(--color-border-strong);
}
.pmpro_table.pmpro_table_orders tbody tr:last-child td,
.pmpro_table.pmpro_table_orders tbody tr:last-child th {
  border-bottom: none;
}

/* Status / alert banners (errors, success confirmations) */
.pmpro_message {
  padding: 14px 18px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  margin-bottom: 24px;
  font-size: 14.5px;
}
.pmpro_message.pmpro_error { border-color: #B3452F; color: #B3452F; background: rgba(179, 69, 47, 0.06); }
.pmpro_message.pmpro_success { border-color: #2F6B4F; color: #2F6B4F; background: rgba(47, 107, 79, 0.06); }
.pmpro_message.pmpro_alert { border-color: var(--color-border-strong); color: var(--color-accent-text); }

.pmpro_hidden { display: none !important; }

/* Account page: membership summary + nav */
.pmpro_account-invoices,
.pmpro_account-fields { margin-bottom: 32px; }
.pmpro_actions-links { display: flex; gap: 10px; flex-wrap: wrap; list-style: none; padding: 0; margin: 16px 0 0; }
.pmpro_actions-links li { margin: 0; }

/* =========================================================
   18. Misc / empty states
   ========================================================= */
.empty-state { padding-block: 60px; text-align: center; color: var(--color-text-muted); }

.notfound-hero { max-width: 640px; margin: 0 auto; padding-block: 100px; text-align: center; }
.notfound-hero .eyebrow { display: block; margin-bottom: 12px; }
.notfound-hero h1 { font-size: 64px; color: var(--color-text); } /* pure gold fails contrast at any size on the cream bg — keep the big numeral in ink, let the eyebrow carry the accent colour */

/* =========================================================
   19. Pricing (paid-memberships-pro/pages/levels.php override)
   ========================================================= */
/* There's only ever one membership level here, permanently — "创始会员"
   itself gets its price updated in place (RM25 -> RM45) once the first
   100 spots fill, rather than a second level being added alongside it.
   So this is just a single centered card, not a multi-column grid
   waiting to be filled in — no auto-fit/minmax needed. */
.pricing-grid {
  max-width: 640px;
  margin: 0 auto 64px;
}

.pricing-card {
  position: relative;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 40px 36px;
}

.pricing-card__badge {
  display: inline-block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent-ink);
  background: var(--color-accent);
  border-radius: 20px;
  padding: 6px 14px;
  margin-bottom: 20px;
}

.pricing-card__name { font-size: 24px; margin-bottom: 14px; }

.pricing-card__price {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 10px;
}
.pricing-card__amount { font-family: var(--font-serif); font-size: 44px; font-weight: 700; color: var(--color-text); }
.pricing-card__period { font-size: 15px; color: var(--color-text-faint); }

.pricing-card__note {
  font-size: 13.5px;
  color: var(--color-text-faint);
  border-left: 2px solid var(--color-border-strong);
  padding-left: 12px;
  margin-bottom: 28px;
}

.pricing-card__features { list-style: none; margin-bottom: 32px; }
/* Belt-and-suspenders against a stray native bullet stacking with the
   ::before checkmark (seen once when a nested context re-asserted
   list-style — explicit here regardless of where it's placed). */
.pricing-card__features li::marker { content: ""; }
.pricing-card__features li {
  position: relative;
  padding-left: 28px;
  margin-bottom: 20px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--color-text-muted);
}
.pricing-card__features li::before {
  content: "✓";
  position: absolute;
  left: 0;
  top: 1px;
  color: var(--color-accent-text);
  font-weight: 700;
}
.pricing-card__features li strong {
  display: block;
  color: var(--color-text);
  font-weight: 700;
  margin-bottom: 3px;
}
.pricing-card__features li span { display: block; }

.pricing-card__cta { width: 100%; justify-content: center; margin-bottom: 16px; }

.pricing-card__trust { font-size: 12.5px; color: var(--color-text-faint); text-align: center; margin: 0; }

.pricing-compare { max-width: 900px; margin: 0 auto 64px; }
.pricing-compare > h2 { font-size: 24px; text-align: center; margin-bottom: 28px; }

/* Wide tables must scroll inside their own container on small screens —
   never let the page itself gain horizontal scroll. */
.pricing-compare__scroll {
  overflow-x: auto;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}

.pricing-compare__table { width: 100%; min-width: 480px; border-collapse: collapse; font-size: 14.5px; }
.pricing-compare__table th,
.pricing-compare__table td { padding: 14px 16px; border-bottom: 1px solid var(--color-border); text-align: center; }
.pricing-compare__table thead th {
  font-family: var(--font-serif);
  color: var(--color-accent-text);
  background: var(--color-surface-2);
  border-bottom: 1px solid var(--color-border-strong);
}
.pricing-compare__table tbody tr:last-child td { border-bottom: none; }

.pricing-compare__row-label { text-align: left !important; color: var(--color-text); font-weight: 500; }

.pricing-compare__mark { font-size: 16px; }
.pricing-compare__mark.is-yes { color: var(--color-accent-text); font-weight: 700; }
.pricing-compare__mark.is-no { color: var(--color-text-faint); }

.pricing-compare__note { font-size: 12.5px; color: var(--color-text-faint); text-align: center; margin: 16px 0 0; }

.pricing-faq { max-width: 680px; margin: 0 auto; padding-bottom: 40px; }
.pricing-faq > h2 { font-size: 24px; text-align: center; margin-bottom: 32px; }
.pricing-faq__item { padding: 22px 0; border-top: 1px solid var(--color-border); }
.pricing-faq__item:last-child { border-bottom: 1px solid var(--color-border); }
.pricing-faq__item h3 { font-size: 17px; margin-bottom: 8px; }
.pricing-faq__item p { color: var(--color-text-muted); font-size: 15px; margin: 0; }

/* =========================================================
   20. Headline ticker (front-page.php, just under the header)
   ========================================================= */
/* Colour tokens come from the .headline-ticker dark-surface rescope in
   §1 — everything below just reads var(--color-*). */
.headline-ticker {
  /* .site-header doesn't actually use the flat var(--color-bg) its
     dark-surface rescope (§1) sets — it overrides with this translucent
     blurred navy instead (for the sticky-scroll glass effect). Sitting
     directly under it with a flat opaque background reads as a visibly
     different, slightly "flatter" shade even though both nominally
     start from the same #0D1730 — matching the exact background here
     removes that seam. */
  background: rgba(13, 23, 48, 0.92);
  backdrop-filter: saturate(140%) blur(8px);
  overflow: hidden;
  white-space: nowrap;
  padding-block: 12px;
  border-bottom: 1px solid var(--color-border);
}
.headline-ticker__track {
  display: flex;
  width: max-content;
  animation: bursa-ticker-scroll 46s linear infinite;
}
/* Pausing on hover is a cheap, practical concession to WCAG's "moving
   content" guidance — lets a reader stop the line they're on to
   actually click it, without needing a separate pause control. */
.headline-ticker:hover .headline-ticker__track { animation-play-state: paused; }

.headline-ticker__item {
  display: inline-flex;
  align-items: baseline;
  gap: 9px;
  margin-right: 48px;
  font-size: 14px;
  color: var(--color-text-muted);
}
.headline-ticker__item:hover { color: var(--color-text); }
.headline-ticker__cat {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 11.5px;
  letter-spacing: 0.04em;
  color: var(--color-accent);
}
.headline-ticker__title { font-family: var(--font-serif); }

/* Track is two back-to-back copies of the same list (rendered twice in
   PHP) — scrolling exactly half its total width loops with no seam,
   then the animation restarts at 0% onto an identical copy. */
@keyframes bursa-ticker-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  .headline-ticker__track { animation: none; }
  .headline-ticker { overflow-x: auto; }
}

/* =========================================================
   23. Responsive
   ========================================================= */
@media (max-width: 1100px) {
  .entry-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 900px) {
  .entry-grid { grid-template-columns: repeat(2, 1fr); }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .ebook-optin__panel { grid-template-columns: 220px 1fr; gap: 36px; padding: 36px; }
}

@media (max-width: 720px) {
  .primary-nav-wrap .primary-nav { display: none; }
  .primary-nav-wrap .primary-nav.is-open {
    display: block;
    position: absolute;
    top: 100%; left: 0; right: 0;
    background: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
    padding: 8px var(--container-pad) 20px;
  }
  .primary-nav-wrap .primary-nav.is-open .nav-menu {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
  }
  .primary-nav-wrap .primary-nav.is-open .nav-menu a { padding: 12px 4px; }
  .nav-toggle { display: inline-flex; }

  /* Hover doesn't exist on touch — the submenu just flows inline,
     always visible, indented under its parent link, instead of
     needing a tap-to-reveal interaction. */
  .nav-menu .sub-menu {
    position: static;
    opacity: 1;
    visibility: visible;
    transform: none;
    background: transparent;
    border: none;
    box-shadow: none;
    margin: 0 0 4px;
    padding: 0 0 0 16px;
  }
  .nav-menu .sub-menu a { padding: 10px 4px; font-size: 14px; }

  .entry-grid { grid-template-columns: 1fr; }
  .post-navigation .nav-links { grid-template-columns: 1fr; }
  .post-navigation .nav-next { text-align: left; }

  .footer-grid { grid-template-columns: 1fr; gap: 28px; }
  .hero { padding-block: 32px 48px; }
  .hero__inner { grid-template-columns: 1fr; gap: 0; }
  /* On a phone the illustration leads, with the eyebrow line directly
     under it, then subtitle/buttons/stats. .hero__copy is "unwrapped"
     (display: contents) so its children become items of the same grid
     as the illustration and can be interleaved with it. The big
     马股解码 / BURSA DECODE headline is dropped visually here (the same
     name is already in the header right above) but kept in the DOM,
     visually-hidden, so the page still has its h1 for search/screen
     readers. */
  .hero__copy { display: contents; }
  .hero__visual { order: 0; margin-bottom: -30px; }
  .hero__illustration { max-width: 320px; }
  .hero .eyebrow { order: 1; position: relative; z-index: 1; margin-bottom: 14px; }
  .hero__title {
    order: 1;
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
  .hero__subtitle,
  .hero__actions,
  .hero__stats { order: 2; }
  .section { padding-block: 44px; }

  .ebook-optin__panel { grid-template-columns: 1fr; padding: 28px; gap: 28px; }
  /* Keep the book's 3:4 proportions on a phone — dropping the ratio made
     it a short squat card with the title and byline jammed together. */
  .ebook-cover { width: 100%; max-width: 250px; margin: 0 auto; padding: 28px 24px; }
  .ebook-cover__title { margin: 26px 0 12px; }

  .mindset-about-layout { grid-template-columns: 1fr; }
  .mindset-about-layout__icon { position: static; max-width: 220px; margin: 32px auto 0; }
  .sector-split { grid-template-columns: 1fr; }
  .member-gate-banner { flex-direction: column; align-items: flex-start; }
  .member-gate-banner__actions { width: 100%; }
  /* Size each button to its label (not an equal 50/50 split, which made
     "了解创始会员" wrap onto two lines) and keep both on one row. */
  .member-gate-banner__actions .btn { flex: 1 1 auto; justify-content: center; white-space: nowrap; padding-inline: 16px; }
  .ebook-optin__row { flex-direction: column; align-items: stretch; }
  /* The desktop rule gives the input flex-basis 240px, which in a column
     layout becomes its HEIGHT — a 240px-tall email box. */
  .ebook-optin__row input[type="email"] { flex: 0 0 auto; width: 100%; }
  .ebook-optin__row .btn { justify-content: center; }

  /* Membership comparison table: fit the 3 columns to the screen instead
     of forcing a sideways scroll that hides 免费会员/创始会员. */
  .pricing-compare__table { min-width: 0; font-size: 13.5px; }
  .pricing-compare__table th,
  .pricing-compare__table td { padding: 12px 8px; }

  /* Article images/charts: browsers give <figure> a 40px side margin by
     default, which on a phone shaves ~80px off every chart. */
  .entry-content figure { margin-inline: 0; }

  /* Wide data tables inside articles scroll sideways inside their own
     box (at a readable size) rather than pushing the whole page wider. */
  .entry-content .wp-block-table { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .entry-content .wp-block-table table { min-width: 600px; table-layout: auto; font-size: 13.5px; }

  /* Account → 订单记录: keep the 已付款 pill and the date on one line
     (they were breaking one character per line in a 300px card). */
  .pmpro_tag { white-space: nowrap; }
  .pmpro_table.pmpro_table_orders th,
  .pmpro_table.pmpro_table_orders td { padding: 12px 6px; font-size: 13.5px; }
  .pmpro_table.pmpro_table_orders tbody th,
  .pmpro_table.pmpro_table_orders .pmpro_table_order-status { white-space: nowrap; }
  .pmpro_table.pmpro_table_orders .pmpro_tag { margin-left: 0; padding: 3px 9px; font-size: 12px; }

  .pmpro_cols-2,
  .pmpro_form_fields.pmpro_cols-2 { grid-template-columns: 1fr; }
  .pmpro_level_group { grid-template-columns: 1fr; }
  .pmpro_card_content, .pmpro_card_actions { padding: 22px 20px; }
  .pmpro_content_message .pmpro_card_title { padding: 28px 20px 6px; }
  .pmpro_content_message .pmpro_card_content { padding: 4px 20px 22px; }
}

/* Small phones: the header row (wordmark + search + 登录/会员账户 + menu
   button) doesn't fit at 390px with the English sub-mark beside the
   Chinese name, so stack it underneath instead of dropping it, and
   tighten the spacing rather than letting the button wrap. */
@media (max-width: 480px) {
  .site-header__inner { gap: 12px; padding-block: 14px; }
  .site-branding a { flex-direction: column; align-items: flex-start; gap: 3px; }
  .site-title-en { font-size: 10px; line-height: 1; letter-spacing: 0.14em; }
  .account-link a { padding: 6px 12px; font-size: 13px; }
}

/* =========================================================
   18. Print (Order/Invoice "打印或另存为PDF")
   ========================================================= */
/* The print button just calls window.print() on the live page — with
   no print stylesheet at all, that means printing/saving-as-PDF the
   dark navy nav bar, the footer, the "会员确认" page title and the
   thank-you prose along with it: everything a receipt shouldn't have.
   This section strips the page down to just the order card for print,
   and dresses it as its own small receipt document instead of a
   screenshot of the webpage. Scoped to #pmpro_order_single so it only
   changes anything on an actual order/invoice page — printing any
   other page on the site is unaffected. */
@media print {
  .site-header,
  .site-footer,
  .page-header,
  [id^="pmpro_confirmation-"],
  .pmpro_card_actions:first-child,
  .pmpro_actions_nav {
    display: none !important;
  }

  @page { margin: 1.6cm; }

  body,
  .site-content { background: #FFFFFF; }
  .container { max-width: none; padding: 0; margin: 0; }
  .page-content { max-width: none; padding-bottom: 0; }

  /* Colour is meaningful here (the status pill, the accent rules) and
     "Save as PDF" is the expected path far more than a physical B&W
     printer, so ask the browser to keep it rather than flatten
     everything to grayscale. */
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  #pmpro_order_single { margin: 0; }

  /* Letterhead — the site's circular badge mark (bursa_decode_invoice_
     print_logo() in inc/pmpro-invoice.php inserts the actual <img>; a
     resized 240x240 copy at assets/images/print-logo.png, not the 3MB
     media-library original) plus the text wordmark. This mark was
     deliberately rejected for the nav (see site-branding.php's note —
     too much fine detail, off-brand red at that size); a small badge
     alone on a receipt letterhead is a different context, and the
     user asked for it here specifically.

     The logo is a real <img>, not a background-image, on purpose: a
     CSS background is gated behind the print dialog's "Print
     backgrounds" toggle (off by default in most browsers), so it
     silently vanished for anyone who hadn't ticked that box — a real
     <img> always prints regardless. It's hidden on screen (PHP always
     outputs it; only this stylesheet reveals it) and, since PMPro
     gives no hook before the card starts, absolutely positioned back
     up over the top-left of the letterhead text below. */
  #pmpro_order_single { position: relative; }
  #pmpro_order_single::before {
    content: '马股解码 · 付款收据';
    display: block;
    padding-left: 42px;
    line-height: 34px;
    font-family: var(--font-serif);
    font-size: 20px;
    font-weight: 700;
    color: var(--color-text);
    margin-bottom: 18px;
  }
  .pmpro_print_logo {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    width: 34px;
    height: 34px;
  }

  .pmpro_card {
    border: 1px solid #000;
    box-shadow: none;
    border-radius: 0;
    break-inside: avoid;
  }
  .pmpro_card_title {
    padding: 18px 20px 12px;
    margin: 0 0 12px;
    border-bottom-color: #000;
  }
  .pmpro_card_content { padding: 0 20px 18px; }
  .pmpro_table th,
  .pmpro_table td { border-color: #999; }
  .pmpro_tag-success { background: none; border: 1px solid #2F6B4F; }
  .pmpro_tag-error { background: none; border: 1px solid #B3452F; }
  .pmpro_tag-alert { background: none; border: 1px solid var(--color-border-strong); }
}

/* Line chart — single-series time trend (e.g. a multi-year ratio),
   complementing the discrete-column .mini-bar-chart above. Geometry
   (points, gridline y's) is computed per use; this supplies chrome,
   stroke widths and text styling only, same split as every other
   in-article chart component. Single series needs no legend — the
   title already names what's plotted. Dashed vertical + two-line
   callout is for a one-off event annotation (not a second series,
   so no second axis). */
.line-chart { margin: 2em 0; padding: 24px 24px 18px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); box-shadow: var(--shadow-card); }
.line-chart__title { display: block; font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-accent-text); margin-bottom: 18px; }
.line-chart svg { display: block; width: 100%; height: auto; }
.line-chart .lc-grid { stroke: var(--color-border); stroke-width: 1; }
.line-chart .lc-tick { fill: var(--color-text-faint); font-size: 11px; font-family: var(--font-sans); }
.line-chart .lc-area { fill: var(--color-accent); opacity: 0.08; }
.line-chart .lc-line { fill: none; stroke: var(--color-accent-strong); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.line-chart .lc-dot { fill: var(--color-accent-strong); stroke: var(--color-surface); stroke-width: 2; }
.line-chart .lc-dot--current { fill: var(--color-accent); }
.line-chart .lc-value { fill: var(--color-text); font-family: var(--font-serif); font-weight: 700; font-size: 13px; }
.line-chart .lc-value--current { fill: var(--color-accent-text); }
.line-chart .lc-annotation-line { stroke: var(--color-border-strong); stroke-width: 1; stroke-dasharray: 4 4; }
.line-chart .lc-annotation-text { fill: var(--color-accent-text); font-family: var(--font-sans); font-weight: 700; font-size: 12px; }
.line-chart .lc-annotation-text--sub { fill: var(--color-text-muted); font-family: var(--font-sans); font-weight: 500; font-size: 11.5px; }
/* Per-member watermark (inc/member-watermark.php) — deliberately quiet,
   not a banner: the point is traceability if a page gets screenshotted
   and shared, not interrupting the member's own reading. Two spots:
   a band above the report-hero (own .container wrapper, since it
   renders before .container opens — same reason report-hero itself
   needs that), and one line appended inside .entry-content so a
   screenshot of just the article's end still carries it. */
.member-watermark {
  font-size: 12px;
  color: var(--color-text-faint);
  text-align: center;
}
.member-watermark-band {
  padding: 20px 0 0;
}
.member-watermark-band .member-watermark {
  margin: 0;
  padding-bottom: 16px;
  border-bottom: 1px dashed var(--color-border);
}
.entry-content > .member-watermark {
  margin: 2.4em 0 0;
  padding: 14px 0 0;
  border-top: 1px dashed var(--color-border);
}
