/* ==========================================================================
   FrontPage / Homepage
   All selectors scoped under .fp-page to avoid WP/Elementor cascade conflicts.
   Generous use of !important — site has 69+ active stylesheets.
   ========================================================================== */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800&display=swap');

/* ---------- Tokens ---------- */
.fp-page {
  --fp-gold:        #B89642;
  --fp-gold-soft:   #D4B84C;
  --fp-gold-dark:   #8B720A;
  --fp-gold-trans:  rgba(184, 150, 66, 0.85);
  --fp-navy:        #0A1F44;
  --fp-navy-deep:   #061635;
  --fp-navy-grad:   linear-gradient(135deg, #0F2D5C 0%, #061535 60%, #0A1F44 100%);
  --fp-ink:         #14171F;
  --fp-ink-soft:    #4A4E58;
  --fp-ink-mute:    #8A8F99;
  --fp-bg:          #E5E5E5;
  --fp-card:        #FFFFFF;
  --fp-line:        #E5E0D5;
  /* Square corners throughout, to match Front-page_UI_v3. Kept as variables
     rather than deleted so the scale can be restored in one place; the only
     curves left on the page are true circles (.fp-arrow, .fp-circle-arrow,
     .fp-footer__social a), which are shapes, not rounded rectangles. */
  --fp-radius-lg:   0;
  --fp-radius-md:   0;
  --fp-radius-sm:   0;
  --fp-shadow-sm:   0 2px 8px rgba(0,0,0,.06);
  --fp-shadow-md:   0 8px 24px rgba(0,0,0,.10);
  --fp-shadow-lg:   0 16px 40px rgba(0,0,0,.18);
  /* ---------- THREE TYPE TIERS, Chase 2026-09-16 ----------
     "现在有些字体大小不一的情况，我现在想设定三个不同的档次". Every piece of card
     text on this page is one of three things, and each had drifted to its own
     size: card headlines ran 30 / 31.76, article titles 17 / 20, blurbs and
     bylines 14 / 15 / 16 in three colours, one of them italic.

     T1  the card headline      — Highlights, Research Blog, Upcoming Events
     T2  the article title      — Research Headline, Apply the Insights
     T3  everything under it    — blurbs, excerpts, bylines: smallest, grey

     Change a tier here and every section moves together. The mobile block at
     the end of the file still overrides T1 per section, because a 32px
     headline in a 390px column is a different problem from a 32px headline in
     a 327px one. */
  --fp-t1-size:     clamp(22px, 2.1vw, 32px);
  --fp-t1-weight:   600;
  --fp-t2-size:     clamp(15px, 1.35vw, 20px);
  --fp-t2-weight:   700;
  --fp-t3-size:     15px;
  --fp-t3-weight:   500;
  --fp-t3-color:    #8A8A8A;
  --fp-ease:        cubic-bezier(.22,.9,.3,1);
  --fp-t-fast:      .25s var(--fp-ease);
  --fp-t-base:      .4s var(--fp-ease);
  --fp-t-slow:      .7s var(--fp-ease);
}

/* Bleed background to viewport edges so the page isn't on a narrow strip */
#fp-root {
  background: var(--fp-bg) !important;
  box-shadow:
    -9999px 0 0 9999px var(--fp-bg),
     9999px 0 0 9999px var(--fp-bg) !important;
}

/* ---------- Page base ---------- */
.fp-page {
  font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif !important;
  font-weight: 500;
  color: var(--fp-ink);
  line-height: 1.6;
  font-size: 16px;
  /* Break out of the boxed Elementor container to true full viewport width */
  width: 100vw !important;
  max-width: 100vw !important;
  margin-left: calc(-50vw + 50%) !important;
  margin-right: calc(-50vw + 50%) !important;
  /* Proportional layout: all horizontal metrics are % of viewport width */
  padding: 0 var(--fp-page-pad);
  box-sizing: border-box;
  --fp-page-pad: 14vw;  /* page side margins */
  --fp-side-w: 16vw;    /* left title column */
  --fp-side-gap: 4vw;   /* gap between title column and content */
  position: relative;
}
.fp-page * { font-family: inherit; }

.fp-page *,
.fp-page *::before,
.fp-page *::after {
  box-sizing: border-box;
}

.fp-page a { color: inherit; text-decoration: none; }
.fp-page button { font: inherit; cursor: pointer; }
.fp-page img { max-width: 100%; display: block; }

/* ---------- Section primitives ---------- */
.fp-section-eyebrow {
  font-size: 14px;
  font-weight: 600;
  color: var(--fp-ink);
  letter-spacing: .04em;
  margin: 0 0 8px;
}
.fp-section-eyebrow--light { color: rgba(255,255,255,.78); }

.fp-section-title {
  font-size: 44px;
  font-weight: 600 !important;
  line-height: 1.05;
  letter-spacing: -0.01em;
  margin: 0;
  color: var(--fp-ink);
}
.fp-section-title--light { color: #fff; }

/* ---------- Buttons ---------- */
.fp-page .fp-btn,
.fp-page a.fp-btn,
.fp-page button.fp-btn {
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  gap: 10px !important;
  height: 46px !important;
  min-height: 46px !important;
  max-height: 46px !important;
  padding: 0 28px !important;
  margin: 0 !important;
  border-radius: 0 !important;
  border: 2px solid transparent !important;
  font-size: 15px !important;
  font-weight: 700 !important;
  letter-spacing: .01em !important;
  line-height: 1 !important;
  white-space: nowrap !important;
  box-sizing: border-box !important;
  vertical-align: middle !important;
  text-decoration: none !important;
  cursor: pointer;
  transition: background var(--fp-t-fast), color var(--fp-t-fast),
              border-color var(--fp-t-fast), transform var(--fp-t-fast);
}
.fp-btn__icon {
  font-size: 14px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.fp-btn__arrow {
  display: inline-block;
  transition: transform var(--fp-t-fast);
}
.fp-btn:hover .fp-btn__arrow { transform: translateX(4px); }

.fp-btn--primary {
  background: #fff !important;
  color: var(--fp-ink) !important;
}
.fp-btn--primary:hover {
  background: var(--fp-gold) !important;
  color: #14171F !important;
}

.fp-btn--ghost {
  background: transparent !important;
  color: #fff !important;
  border-color: transparent !important;
}
.fp-btn--ghost:hover {
  border-color: #ffffff !important;
  background-color: rgba(255,255,255,.12) !important;
  box-shadow: 0 0 0 1px rgba(255,255,255,.35) !important;
}

.fp-page .fp-btn--gold-sm,
.fp-page a.fp-btn--gold-sm,
.fp-page button.fp-btn--gold-sm {
  background: var(--fp-gold) !important;
  color: #fff !important;
  height: 38px !important;
  min-height: 38px !important;
  max-height: 38px !important;
  padding: 0 22px !important;
  font-size: 14px !important;
}
.fp-page .fp-btn--gold-sm:hover {
  background: var(--fp-gold-dark) !important;
}

.fp-page .fp-btn--ink-sm,
.fp-page a.fp-btn--ink-sm,
.fp-page button.fp-btn--ink-sm {
  background: var(--fp-ink) !important;
  color: #fff !important;
  height: 38px !important;
  min-height: 38px !important;
  max-height: 38px !important;
  padding: 0 22px !important;
  font-size: 14px !important;
}
.fp-page .fp-btn--ink-sm:hover {
  background: var(--fp-gold) !important;
  color: #fff !important;
}

.fp-page .fp-btn--light-sm,
.fp-page a.fp-btn--light-sm {
  background: #fff !important;
  color: var(--fp-ink) !important;
  height: 38px !important;
  min-height: 38px !important;
  max-height: 38px !important;
  padding: 0 22px !important;
  font-size: 14px !important;
}
.fp-page .fp-btn--light-sm:hover {
  background: var(--fp-gold) !important;
  color: #fff !important;
}

.fp-btn--blue {
  background: #1B3F86 !important;
  color: #fff !important;
}
.fp-btn--blue:hover { background: #122F66 !important; }

.fp-btn--dark {
  background: var(--fp-ink) !important;
  color: #fff !important;
}
/* Gold on hover, like every other button on the page. The old #000 hover was
   nearly indistinguishable from the resting --fp-ink, so the button read as
   having no hover state at all. */
.fp-btn--dark:hover {
  background: var(--fp-gold) !important;
  color: #fff !important;
}
.fp-btn__icon { font-size: 14px; line-height: 1; }

/* ---------- Reveal animation ---------- */
.fp-reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity .8s var(--fp-ease), transform .8s var(--fp-ease);
}
.fp-reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* ==========================================================================
   1. HERO + NUMBERS
   ========================================================================== */
.fp-hero {
  position: relative;
  width: 100vw !important;
  margin-left: calc(-50vw + 50%) !important;
  margin-right: calc(-50vw + 50%) !important;
  margin-top: -50px !important;
  height: 96vh;
  min-height: 600px;
  overflow: hidden;
  color: #fff;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-top: 120px; /* matches header min-height; centers content below the header */
}
/* ---- hero bands -------------------------------------------------------
   Three videos, one diagonal cut, only the hovered one plays.

   clip-path clips HIT-TESTING as well as pixels, so "which band is the
   cursor over" needs no geometry: the band that receives pointerenter is
   the one under the cursor. The polygons leave a 0.6% gap between them and
   the black behind shows through — that gap is the seam, not an element.

   ⚠️ .fp-hero__overlay and .fp-hero__inner sit ABOVE these (z-index 1 and 2)
   and would swallow every hover, so both are made pointer-transparent below
   and the two CTA links are handed their events back. Remove one of those
   three rules and the hero stops responding to the mouse. */
.fp-hero__bands {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: #0B0E13;
}
.fp-band {
  position: absolute;
  inset: 0;
  cursor: pointer;
}
/* Equal area, not equal top edge. The cut leans 16.4% to the left over the
   full height, so a band's top width is not its size: measured on the first
   attempt the right band averaged 39.6% of the width against 29.8% and 29.4%
   for the other two, and it read as the big one.

   With a constant lean s, a band's average width is what counts:
     left   = t1 - s/2      middle = t2 - t1      right = 100 - t2 + s/2
   Setting all three to 33.3% with s = 16.4 gives t1 = 41.5, t2 = 74.9.
   Change the lean and these two numbers have to be recomputed, or the right
   band grows again. The 0.6% between the polygons is the seam. */
.fp-band:nth-child(1) { clip-path: polygon(0 0, 41.5% 0, 25.1% 100%, 0 100%); }
.fp-band:nth-child(2) { clip-path: polygon(42.1% 0, 74.9% 0, 58.5% 100%, 25.7% 100%); }
.fp-band:nth-child(3) { clip-path: polygon(75.5% 0, 100% 0, 100% 100%, 59.1% 100%); }
/* 🚨 SIZED BY HEIGHT, POSITIONED BY --x. NOT object-fit on a full-hero box.
   Each clip is a full-frame 1600x900 landscape and must appear at the same
   scale it would as an ordinary full-width hero — the bands are WINDOWS onto
   three full-size pictures, not three little pictures.

   height:100% + width:auto pins the scale to the hero's height and nothing
   else, so the picture is never blown up. The band then slides its own clip
   sideways with --x so the subject lands inside its own slice: the turtle sits
   near the middle of its frame, and the left band is the left third of the
   hero, so that clip is shifted left until the turtle is in view. Whatever
   falls outside the band is clipped away and costs nothing.

   min-width is the safety net for a very wide, short viewport where
   height x 16/9 no longer spans the hero; there object-fit crops instead of
   stretching. */
.fp-band__video {
  position: absolute;
  top: 0;
  left: var(--x, 50%);
  height: 100%;
  width: auto;
  min-width: 100%;
  object-fit: cover;
  /* Resting = a still frame, slightly pulled back so the live band reads as
     the live one. Not so far back that the other two look broken. */
  filter: saturate(.82) brightness(.88);
  /* No zoom at rest, and a small one live. Every scale step resamples: the
     clip is already drawn near 1:1 at 1920, and scale(1.06) was pushing it
     back through an upscale for no visible gain. */
  transform: translateX(-50%) scale(1);
  transition: filter .45s ease, transform .8s cubic-bezier(.2,.7,.3,1);
}
.fp-band:nth-child(1) .fp-band__video { --x: 16%; }   /* 龜 */
.fp-band:nth-child(2) .fp-band__video { --x: 50%; }   /* 人 */
.fp-band:nth-child(3) .fp-band__video { --x: 82%; }   /* 香港 */
.fp-band__tint {
  position: absolute;
  inset: 0;
  /* Lighter than it looks: .fp-hero__overlay adds its own gradient on top. */
  background: rgba(6, 9, 13, .42);
  transition: background .4s ease;
}
.fp-band.is-live .fp-band__video { filter: none; transform: translateX(-50%) scale(1.03); }
.fp-band.is-live .fp-band__tint { background: rgba(6, 9, 13, .22); }
.fp-band:focus-visible { outline: 2px solid #B89642; outline-offset: -6px; }

@media (prefers-reduced-motion: reduce) {
  .fp-band__video { transform: translateX(-50%) !important; transition: none; }
  .fp-band__tint { transition: none; }
}
/* Lightened 2026-08-05 on Chase's note that the video was reading too dark:
   .65/.45/.75 → .38/.22/.52. The gradient still has to earn its keep — it is
   what keeps the white headline legible over a moving picture — so it is not
   removed, only pulled back. The middle stop stays the lightest because that
   band sits behind the image, not the type; the top and bottom carry the
   contrast for the nav and the buttons. */
.fp-hero__overlay {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(0,0,0,.38) 0%, rgba(0,0,0,.22) 35%, rgba(0,0,0,.52) 100%);
  z-index: 1;
  pointer-events: none;   /* see .fp-hero__bands */
}
.fp-hero__inner {
  position: relative;
  z-index: 2;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 60px;
  text-align: center;
  pointer-events: none;   /* see .fp-hero__bands */
}
.fp-hero__inner a { pointer-events: auto; }
.fp-hero__title {
  /* !important is load-bearing: Phlox main.css has `.entry-content h1 {2.7em}`,
     specificity (0,1,1) vs this rule's (0,1,0), so without it the theme wins and
     the hero renders at 43.2px instead of 92px. The two @media overrides below
     already carried !important, which is why only desktop was affected. */
  font-size: clamp(44px, 6.4vw, 92px) !important;
  line-height: 1.02;
  font-weight: 800 !important;
  letter-spacing: -0.015em;
  margin: 0 0 24px;
  color: #fff;
  text-shadow: 0 2px 16px rgba(0,0,0,.25);
}
.fp-hero__subtitle {
  font-size: 17px;
  line-height: 1.55;
  margin: 0 auto 64px !important;
  color: rgba(255,255,255,.92);
}
.fp-hero__cta {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  justify-content: center;
}

/* Numbers strip */
.fp-numbers {
  position: relative;
  z-index: 3;
  width: 100vw !important;
  margin-left: calc(-50vw + 50%) !important;
  margin-right: calc(-50vw + 50%) !important;
  margin-top: -6vh !important;
  background: #0B0E14;
  color: #fff;
  padding: 38px 60px 52px;
  border-radius: var(--fp-radius-lg) !important;
}
.fp-numbers__caption {
  text-align: center;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: .06em;
  opacity: .9;
  margin: 0 0 36px !important;
}
.fp-numbers__grid {
  max-width: 1280px;
  margin: 0 auto;
  display: grid;
  /* 🚨 minmax(0, 1fr), NOT 1fr. `1fr` is `minmax(auto, 1fr)`, so a column whose
     content will not fit its share pushes past it and the four stop being equal.
     Rick, 2026-08-27: "Small alignment problem: 3,000,000+ Media Reach".
     Measured on the live page at 768 before this: the four columns came out
     153.8 / 156.3 / 133.0 / 133.0 — "Participants" and "Researchers" are the
     two long labels and they took the extra from the two on their right, so the
     row read as misaligned at exactly that width and nowhere else (1600 and
     1024 were 302 and 208 across, and 414 and below is a 2x2). The 0 floor lets
     a label wrap instead of widening its column. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 24px;
}
.fp-stat {
  text-align: center;
}
.fp-stat__icon {
  display: block !important;
  width: 130px;
  height: 130px;
  margin: 0 auto 12px;
  object-fit: contain;
}
/* 🚨 THE NUMBER MUST NEVER WRAP, AND ITS SIZE IS DERIVED FROM THE CELL, NOT THE
   VIEWPORT. Chase, 2026-08-28: "这个部分可以保持一行吗，而且四个图标的位置可以保持
   均匀吗？"

   Measured on the live page first: the icons were ALREADY exact — every one of
   the four sat 0px off its own cell's centre at 1920, 1600, 1440, 1280, 1100,
   1024, 900 and 768, with identical centre-to-centre gaps. What made the row
   read as lopsided was the fourth number, "3,000,000+", breaking onto a second
   line and dragging its label down with it. Fix the wrap and the unevenness
   goes with it; there was nothing wrong with the icon positions.

   It wrapped at EVERY desktop width, 768 through 1920, not just narrow ones,
   because `4.6vw` and the cell width do not scale together. The cell is
   (container - 3 x 24px gap) / 4 and the container is capped at 1280, so above
   ~1400 the cell stops growing at 302px while 4.6vw keeps going. Measured with
   a hidden probe in the page's own font: "3,000,000+" needs 5.572 x the
   font-size, so 302px of cell allows 54px of type and it was being given 64.

   The container-query form is exact rather than approximate — it IS the cell
   arithmetic — so it holds at every width instead of at the ones I happened to
   test: (100cqw - 72px) / 4 is the cell, / 5.572 is the type that fits in it,
   and 22.4 is those two divisors multiplied with a little margin.
   The plain clamp above it is the fallback for a browser without container
   queries; 3.3vw is the tightest ratio the eight measured widths needed, so it
   fits everywhere without the arithmetic.

   ⚠️ The 72px assumes a 24px gap. That holds from 721 up — both places the gap
   changes (22px and 16px) are inside @media (max-width: 720px), where the
   Global Impact mobile block further down sets its own size anyway. */
.fp-stat__num {
  display: block;
  font-size: clamp(28px, 3.3vw, 54px);
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
@supports (container-type: inline-size) {
  .fp-numbers__grid { container-type: inline-size; }
  .fp-stat__num { font-size: min(calc((100cqw - 72px) / 22.4), 64px); }
}
.fp-stat__label {
  display: block;
  font-size: 24px;
  letter-spacing: .02em;
  opacity: .82;
  margin-top: 4px;
}

/* ==========================================================================
   Shared side layout — big title + View More on the left, content right
   ========================================================================== */
.fp-side {
  display: grid;
  grid-template-columns: var(--fp-side-w) 1fr;
  gap: var(--fp-side-gap);
  align-items: center;
}
.fp-side__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
}
.fp-side__head .fp-section-title {
  font-size: clamp(34px, 3vw, 48px);
  font-weight: 600 !important;
  line-height: 1.12;
}

/* ==========================================================================
   2. ESGRI HIGHLIGHTS
   ========================================================================== */
.fp-highlights {
  margin: 80px auto 60px;
}
.fp-highlights__head {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 28px 36px 0 !important;
  margin-bottom: 0 !important;
}
.fp-highlights__nav {
  display: flex;
  align-items: center;
  gap: 14px;
}
.fp-highlights__counter {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--fp-ink-soft);
}
.fp-highlights__progress {
  display: flex;
  gap: 0;
  width: 280px;
  height: 3px;
  border-radius: 0;
  overflow: hidden;
  background: rgba(0,0,0,.14);
}
.fp-highlights__progress-seg {
  flex: 1;
  background: transparent;
  transition: background var(--fp-t-fast);
}
.fp-highlights__progress-seg.is-active {
  background: var(--fp-gold);
}
.fp-arrow {
  width: 36px; height: 36px;
  border-radius: 50% !important;
  border: none;
  background: var(--fp-ink);
  color: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  transition: background var(--fp-t-fast), transform var(--fp-t-fast), opacity var(--fp-t-fast);
  padding: 0;
  margin: 0 !important; /* theme buttons carry a bottom margin that breaks flex centering */
}
/* 🚨 HOVER STICKS ON A TOUCHSCREEN. A tap leaves :hover asserted on whatever
   was tapped until something else is tapped, so the arrow you just pressed
   keeps its hover colour — Chase, 2026-08-27, with the Insights next arrow
   sitting grey after a press: "为什么会变灰色？". Nothing was disabled and no
   --inactive class was set; #9aa0ac is simply the hover background, and it had
   nowhere to go. Every hover state on these arrows is gated on a device that
   can actually hover, and :active carries the press feedback for the ones that
   cannot — it lasts exactly as long as the finger is down. */
@media (hover: hover) {
  .fp-arrow:hover { background: var(--fp-gold); }
  .fp-arrow--next:hover { transform: translateX(2px); }
  .fp-arrow--prev:hover { transform: translateX(-2px); }
  .fp-arrow--inactive:hover { opacity: .55; }
}
.fp-arrow:active { background: var(--fp-gold); }
.fp-arrow--inactive { opacity: .35; }

.fp-highlights__viewport {
  background: #fff;
  border-radius: var(--fp-radius-lg) !important;
  box-shadow: var(--fp-shadow-md);
  overflow: hidden;
}
.fp-highlights__track {
  display: flex;
  transition: transform 1.1s var(--fp-ease);
  will-change: transform;
}
/* No `transform` in this transition on purpose. frontpage.js drives the carousel
   and always sets its own inline transition before animating; it then clears the
   inline value on reset. If a transform transition lived here it would take over
   at exactly that moment and animate the silent snap-back to 0, which is what
   made switching jitter. Duration also disagreed with the JS (.55s vs 500ms). */
.fp-scholars__track {
  transition: opacity .35s ease;
}
.fp-scholars__track.is-fading {
  opacity: 0;
}
.fp-highlight {
  flex: 0 0 100%;
  display: grid;
  grid-template-columns: 0.95fr 1fr;
  gap: 40px;
  padding: 24px 36px 36px;
  /* 🚨 `start`, not `center` — Chase, 2026-09-01, with a screenshot of the card
     floating in the middle of the panel.
     The two columns disagree about height and the disagreement grows as the
     window narrows: the picture is 4:3 of a shrinking column while the text
     needs MORE height as it wraps. Measured on the live page before the fix: at
     2000px the picture was 339 against a 304 body, at 1440 it was 233 against
     441, at 1280 it was 183 against 459. Centred, that left the picture stranded
     with a 128–162px gap ABOVE it, which is what the screenshot showed.

     Stretching the picture to the row height closed the gap perfectly but grew
     the image; Chase: "我不想改图片的size还是保持原来的". So the picture keeps its
     4:3 exactly as it was, and only the alignment changes — both columns start
     at the same line and the leftover room falls to the bottom of the card,
     where it reads as ordinary whitespace instead of a hole. The headline coming
     down a size in the same pass takes most of the difference out anyway. */
  align-items: start;
}
/* Bunny, 2026-09-04: "右邊那邊可以跟左邊置中嗎，現在是置頂".
   ⚠️ This centres the BODY only. `align-items: center` on the grid is what was
   removed above, and putting it back would strand the picture again whenever the
   text is the taller column. align-self on one child is safe in both directions:
   when the picture is taller (wide windows, short copy) the text centres against
   it; when the text is taller it already fills the row, so this is a no-op and
   the picture stays at the top where it belongs. */
.fp-highlight__body { align-self: center; }
.fp-highlight__media {
  display: block;
  border-radius: var(--fp-radius-md) !important;
  overflow: hidden;
  aspect-ratio: 4 / 3;
}
/* 🚨 DO NOT reintroduce a height on .fp-highlight__media to level the columns.
   It was tried on 2026-09-01 and reverted: it makes the picture taller than the
   design intends, and Chase asked for the original size. Two things were learned
   in that attempt and are worth not repeating —
     · `min-height: 100%` with the 4:3 ratio still in force drives the WIDTH off
       the new height: measured 588px inside a 310px column, the picture spilling
       across the text. Only clearing the ratio makes a height safe.
     · levelling the columns that way does close the gap, but it changes the
       crop at every width, which is a bigger change than the gap it fixes. */
.fp-highlight__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform .6s var(--fp-ease);
}
.fp-highlight__media:hover img { transform: scale(1.05); }
.fp-highlight__title {
  /* T1. Chase, 2026-09-01: "针对这类的尺寸的页面,标题大小可以小一些". The old
     clamp(28px, 3vw, 40px) barely moved across the whole desktop range — 40px
     at 1920 and still 38.4 at 1280 — while the column it sits in went from 455px
     to 297. So the headline wrapped to three or four lines exactly where the
     card was tightest, which is also what drove the body taller than the picture
     next to it. */
  font-size: var(--fp-t1-size);
  font-weight: var(--fp-t1-weight) !important;
  /* 🚨 1.2, NOT 1.1 — a clamped box is exactly line-height x lines tall, so a
     line-height tighter than the font's own line box crops the last line's
     descenders and nothing else. Measured on /home-2/ 2026-09-10 at 1600px:
     32px type, box 70px, content 74px, and the g of "Sustainable Change?" lost
     its tail. 1.2 gives 76px and the descender sits inside it. Poppins needs
     about 1.16; the same applies to any clamped heading on this page. */
  line-height: 1.2;
  margin: 0 0 18px;
  letter-spacing: -0.01em;
  /* Chase, 2026-09-01: "我想控制标题的行数,三行明显不合适". Two lines, and the
     type is sized so they FIT rather than get cut: the longest pre-broken
     segment on the current cards is 21 characters ("Professorships at HKU",
     "Perspectives & Voices"), which needs about 30px in the 327px column at
     1440 — hence the 32px ceiling and the gentler 2.1vw. The clamp below is the
     backstop for a title nobody measured, not the mechanism. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* 🚨 ONE CARD, ONE SIZE DOWN — because the rule above is two lines, not three.
   "4th Governance and Sustainability Conference" is 44 characters against the
   ~18 a 32px line holds in a 327px column, so at the shared size it either
   takes three lines (which Chase ruled out on 2026-09-01) or loses half the
   conference's name to an ellipsis. Shrinking this title alone keeps both the
   name and the two lines. Ceilings solved per width, not guessed: 24px holds
   "Sustainability Conference" in 300px at 1440, and 1.55vw keeps the same fit
   down to 1101, where the column is 236. Use it only for a title that has
   earned it — a card whose name simply cannot be shortened. */
.fp-highlight__title--three {
  /* 🚨 THE SIZE IS SHARED; ONLY THE LINE COUNT IS NOT. This was
     .fp-highlight__title--tight, one size down, so the conference's 44-character
     name fitted the two lines every card gets. Wendy, 2026-09-14: "the title
     font size for these two cards are different" — a card that is visibly
     smaller than its neighbour reads as a mistake, and the reason it was
     smaller is invisible.

     Measured at 1600px before changing it: at the shared 32px this title needs
     three lines (114px). Held to two it overflows by 41px, which loses a line
     of the conference's own name. So it keeps the size and takes the third
     line; the card body runs 38px taller than its neighbours, which is the
     cheaper of the two mismatches.

     The manual <br> went too: it forced "Sustainability Conference" onto one
     line, which at 32px is wider than the column and wrapped anyway. */
  -webkit-line-clamp: 3 !important;
}
.fp-highlight__excerpt {
  /* T3 */
  font-size: var(--fp-t3-size);
  font-weight: var(--fp-t3-weight);
  color: var(--fp-t3-color);
  line-height: 1.65;
  margin: 0 0 36px !important;
  /* Three lines, the same promise the research card next to it already makes.
     The blurbs arrive at 30-45 words and ran to seven lines at 1280, which is
     what made the text column so much taller than the picture. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* 🚨 ONE CARD, FIVE LINES — because the blurb is the client's, not ours.
   Wendy, 2026-09-11: "Contents: please refer to my original blurb." Hers runs
   to five lines in this column and the shared clamp is three, so the card was
   ending mid-sentence at "changemakers…". Raising the clamp for every card
   would make the row ragged; this is the one card whose text was written to be
   read in full. Remove the class and it goes back to three. */
.fp-highlight__excerpt--long {
  -webkit-line-clamp: 6 !important;
}

/* ==========================================================================
   3. UPCOMING EVENTS
   ========================================================================== */
.fp-events {
  display: grid;
  grid-template-columns: var(--fp-side-w) 1fr;
  gap: var(--fp-side-gap);
  margin: 72px auto;
  align-items: center;
}
.fp-events__head {
  display: flex;
  flex-direction: column;
  gap: 24px;
  align-items: flex-start;
}
.fp-events__head .fp-section-title {
  font-size: clamp(34px, 3vw, 48px);
  font-weight: 600 !important;
  line-height: 1.12;
}
/* 🚨 `list-style: none` on the UL is not enough. A list-style set on the <ul>
   only reaches the <li> by inheritance, and ANY rule that targets the li
   directly beats an inherited value no matter how specific the ul rule is —
   Phlox styles `.entry-content li`, so the bullets came back. Kill it on the
   item itself, and on ::marker, which is a separate pseudo-element that
   list-style: none does not always silence. */
/* 🚨 `.fp-page` IS LOAD-BEARING HERE, not decoration. Phlox's main.css carries
   `.entry-content ul { padding-left: 1.1em }` at (0,1,1), which outranks a plain
   `.fp-events__list` at (0,1,0) and was winning: every event row sat 17.6px to
   the right of the content column that highlights, research, insights and
   scholars all share, and the page read as misaligned at that one seam.
   Chase spotted it on 2026-09-09. The list markers were already off, so the
   indent bought nothing at all. */
.fp-page .fp-events__list {
  list-style: none !important;
  margin: 0;
  padding: 0;
}
.fp-event {
  list-style: none !important;
  border-bottom: 1px solid #c9c9c9;
  margin: 0 !important;
  padding: 10px 0 !important;
}
.fp-event:last-child {
  border-bottom: 0;
}
.fp-event__link {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "date    arrow"
    "title   arrow"
    "excerpt arrow";
  column-gap: 28px;
  row-gap: 0;
  padding: 6px 0 !important;
  transition: background var(--fp-t-fast);
}
.fp-event__link:hover { background: rgba(184,150,66,.04); }
.fp-event__date {
  grid-area: date;
  /* T3 size and weight, Chase 2026-09-16 — it keeps the gold, because the
     colour is what makes it read as the event's eyebrow; only the 14px/700 it
     had to itself is gone. */
  color: var(--fp-gold);
  font-size: var(--fp-t3-size);
  font-weight: var(--fp-t3-weight);
  letter-spacing: .02em;
  line-height: 1.55 !important;
  margin-bottom: 8px !important;
}
/* ── SIZED OFF THE COMP, not off 22px ─────────────────────────────────────────
   Chase, 2026-08-27: "活动标题也许可以大一些，参考当初的mockup". Measured the board
   (design/ESDR Highlights + Upcoming Events.jpg, 1920 wide) rather than eyeballed
   it: cap heights come out 54px for UPCOMING EVENTS and 34px for "Rethink -
   Asia's Hub for Climate Action &", so the comp sets the row title at 0.63 of
   its section title. The section title is clamp(34px, 3vw, 48px), and 0.63 of
   that is this line.

   ⚠️ It was a FLAT 22px, which is why the gap opened up as the window widened:
   at 390 and 768 the section title is 28 and 34, so 17 and 22 were already
   right at 0.63 — the ratio only broke above ~1100, where the section title
   climbs to 43.2 and then 48 and the row title stayed put. The floor is 22 so
   nothing below 1160 changes. */
.fp-event__title {
  grid-area: title;
  /* T1 — it was clamp(22px, 1.9vw, 30px), 2px short of the other two headlines
     at every width above 1160. Same tier, same curve. */
  font-size: var(--fp-t1-size);
  font-weight: var(--fp-t1-weight) !important;
  line-height: 1.3;
  margin: 0 0 8px !important;
  color: var(--fp-ink);
  transition: color var(--fp-t-fast);
}
.fp-event__link:hover .fp-event__title { color: var(--fp-gold); }
.fp-event__excerpt {
  grid-area: excerpt;
  /* T3 */
  font-size: var(--fp-t3-size);
  font-weight: var(--fp-t3-weight);
  color: var(--fp-t3-color);
  margin: 0 !important;
  line-height: 1.55;
}
/* 🚨 THE EVENT ROW SAYS WHAT THE ARROW DOES. Bunny and Lingzi, 14 Sep: "add CTA
   text, before ->, View more". A bare → in the right-hand column read as
   decoration rather than a link, and it is the only affordance on the row. The
   arrow keeps grid-area: arrow; the label rides in front of it inside
   .fp-event__cta so the three-row grid is untouched. */
.fp-event__cta {
  grid-area: arrow;
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  white-space: nowrap;
}
.fp-event__cta-text {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--fp-ink);
  transition: color var(--fp-t-fast);
}
.fp-event__link:hover .fp-event__cta-text { color: var(--fp-gold); }
.fp-event__arrow {
  align-self: center;
  font-size: 20px;
  color: var(--fp-ink);
  transition: transform var(--fp-t-fast), color var(--fp-t-fast);
}
.fp-event__link:hover .fp-event__arrow {
  transform: translateX(8px);
  color: var(--fp-gold);
}

/* ==========================================================================
   4. RESEARCH BLOG + AWARD (single carousel)
   ========================================================================== */
.fp-research {
  margin: 80px auto;
  width: 100vw !important;
  margin-left: calc(-50vw + 50%) !important;
  margin-right: calc(-50vw + 50%) !important;
}
.fp-research__container {
  /* Bunny's "expanded bg", 2026-09-07: the section now carries the blog block
     AND the headline block, so the old 1920x1080 art ran out halfway down.
     Same artwork, taller (the server re-encoded it to 1792x1440 on upload —
     it is a gradient with one light sweep, so the scaling does not show). */
  background: #0A1F44 url('https://hkujcesgri.hku.hk/wp-content/uploads/headline-bg-20260907.webp') center / cover no-repeat;
  border-radius: 0 !important;
  /* 36px at the bottom was the room the carousel's progress bar and arrows
     needed; they went with the award slide on 2026-09-07 and the cards were
     left sitting on the edge. 64px matches the top. */
  padding: 64px var(--fp-page-pad, 4vw) 64px;
  position: relative;
  overflow: hidden;
}
.fp-research__head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 24px;
  margin: 0;
}
.fp-research__head .fp-section-title {
  font-size: clamp(34px, 3vw, 48px);
  line-height: 1.12;
  margin: 0;
}

.fp-research__viewport {
  overflow: hidden;
}
.fp-research__track {
  display: flex;
  transition: transform .7s var(--fp-ease);
  will-change: transform;
}
.fp-research__group {
  flex: 0 0 100%;
  min-width: 0;
  /* 🚨 2px of padding, not decoration. The award group sits exactly at the
     viewport's right edge, and its View More button and active tab are white.
     At widths where 100% is fractional (911.6 at 1060) the clip edge snaps to
     a pixel and the group's left edge does not, so its first column bled
     through as two white slivers — one at button height, one at tab height
     (Chase, 2026-09-04: "这里的白边是啥"). Keeping the white 2px in from the
     seam is what stops it; box-sizing keeps the 100% translate maths intact. */
  box-sizing: border-box;
  padding-left: 2px;
  display: grid;
  grid-template-columns: var(--fp-side-w) 1fr;
  grid-template-rows: auto 1fr;
  column-gap: var(--fp-side-gap);
}
.fp-research__group > .fp-research__head {
  grid-column: 1;
  grid-row: 1 / 3;
  align-self: center;
}
.fp-research__group > .fp-tabs {
  grid-column: 2;
  grid-row: 1;
}
.fp-research__group > .fp-research__inner {
  grid-column: 2;
  grid-row: 2;
}
/* 🚨 aspect-ratio is the FALLBACK now, not the rule. frontpage.js measures the
   active slide and writes an explicit height here, which beats aspect-ratio; the
   ratio is what the panel falls back to if that script never runs. Do not tune
   the ratio or the min-heights further down to change the panel's height — they
   only decide what a visitor without JS sees. */
.fp-research__inner {
  position: relative;
  aspect-ratio: 2 / 1;
  overflow: hidden;
  min-height: 0;
  /* 🚨 SLOW ENOUGH TO READ AS A MOVE, NOT A JUMP. The four tabs differ by up to
     157px, and at .3s that landed as a snap — Wendy, 2026-09-14: "跳到research
     pillar的时候会突然缩短". Sizing every tab to the tallest was the other way to
     answer it and was worse: it parks the three shorter tabs above a hole. */
  transition: height .45s cubic-bezier(.4, 0, .2, 1);
}
/* The two frames of the outer carousel — the blog panel and the award panel —
   are flex items and were stretching each other to the taller one's height.
   Measured 2026-09-04: at 1366 the blog frame needed 539 and the award frame
   599, so the blog frame carried 60px it had no use for. They show one at a
   time; they do not have to agree. */
.fp-research__track { align-items: flex-start !important; }
.fp-research__inner-track {
  position: absolute !important;
  inset: 0 !important;
  height: auto !important;
}
/* Persistent white card bg — sits behind all slides so the bg never flashes during fade */
.fp-research__card-bg {
  position: absolute;
  inset: 0;
  background: #fff;
  border-radius: var(--fp-radius-md);
  transition: border-radius var(--fp-t-base);
  z-index: 0;
}
/* Stack all inner slides on top of each other; only .is-active is visible (faded in).
   Card body is anchored in place — only content fades, bg is provided by fp-research__card-bg. */
.fp-research__inner-track {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-areas: 'stack';
}
.fp-research__inner-track > * {
  position: absolute !important;
  inset: 0 !important;
  min-width: 0;
  background: transparent !important;
  overflow: hidden;
  opacity: 0;
  visibility: hidden;
  transition: opacity .35s ease, visibility 0s linear .35s;
}
.fp-research__inner-track > .is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity .35s ease, visibility 0s linear 0s;
}

.fp-page .fp-tabs {
  display: flex !important;
  gap: 0 !important;
  margin: 0 !important;
  padding: 0 !important;
  position: relative !important;
  z-index: 2;
}
.fp-page .fp-tabs[hidden],
.fp-page .fp-research__head[hidden] { display: none !important; }
.fp-page .fp-research__viewport {
  margin: -1px 0 0 !important;
  padding: 0 !important;
  overflow: hidden !important;
  background: transparent !important;
}
.fp-page .fp-tab,
.fp-page button.fp-tab {
  position: relative !important;
  background: transparent !important;
  color: rgba(255,255,255,.78) !important;
  border: 0 !important;
  border-radius: 0 !important;
  padding: 14px 28px !important;
  margin: 0 !important;
  font-size: 15px !important;
  font-weight: 700 !important;
  letter-spacing: .01em !important;
  cursor: pointer;
  z-index: 1;
  transition: color var(--fp-t-base);
}
.fp-page .fp-tab:hover { color: #fff !important; }
.fp-page .fp-tab.is-active {
  color: var(--fp-ink) !important;
}

/* Sliding white indicator under the active tab. JS positions it via left/width. */
.fp-page .fp-tab-indicator {
  position: absolute;
  /* Overlap the panel below by 1px. The two white boxes meet on a fractional
     pixel boundary (y = …·53), so each edge antialiases against the dark blue
     behind and a faint hairline shows through. Geometry reports a 0px gap —
     the seam is a rendering artifact, not a layout one. */
  bottom: -1px;
  left: 0;
  width: 0;
  height: calc(100% + 1px);
  background: #fff;
  border-radius: 0;
  z-index: 0;
  pointer-events: none;
  transition: left .5s var(--fp-ease), width .5s var(--fp-ease);
}
/* The two ::before/::after fillets that used to curve the active tab into the
   panel below are gone — they drew their arc with a radial-gradient mask, not
   border-radius, so they survived the square-corner pass and read as inner
   rounded corners. The tab is now a plain rectangle on all four sides.
   (`.is-at-edge-left` is still toggled by the JS; it simply has nothing to do.) */

.fp-research-card {
  flex: 0 0 100%;
  min-width: 0;
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: 40px;
  padding: 36px;
  background: #fff;
  border-radius: var(--fp-radius-md) var(--fp-radius-md) var(--fp-radius-md) var(--fp-radius-md);
  align-items: stretch;
  transition: border-radius var(--fp-t-base);
}
.fp-research-card__body { align-self: center; }
.fp-research-card__media {
  display: block;
  border-radius: var(--fp-radius-sm) !important;
  overflow: hidden;
  aspect-ratio: 1 / 1;
  align-self: center;
}
.fp-research-card__media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform .6s var(--fp-ease);
}
.fp-research-card__media:hover img { transform: scale(1.05); }
/* 🚨 CLAMPED TO THREE LINES, and it has to be. The card is a fixed-height slide
   in a carousel — the JS pins .fp-research__inner-track and the card inherits it
   — and the card itself is overflow: hidden. A long title does not push the card
   taller, it pushes the excerpt and the Read More button out through the bottom
   edge, silently. "The Real Effect of Sociopolitical Racial/Ethnic Animus:
   Mutual Fund Manager Performance during AAPI Hate" set SEVEN lines at 1920 and
   clipped 199px off the card, cutting the excerpt mid-sentence and taking the
   buttons with it (Chase, 2026-08-20).

   Three, not "however many fit": the excerpt below is already clamped to 4, so
   a title free to grow just eats its neighbour. The ellipsis is the point — the
   full title is one click away and the card keeps its shape whatever lands in
   the Latest slot. */
/* 🚨 THE SAME SCALE AS .fp-highlight__title, DELIBERATELY — Chase, 2026-08-27:
   "博客卡标题高亮卡标题一样大吧". They are the two card headlines on the page and
   they sat on different curves: 34.6 vs 40 at 1440, 46 vs 40 at 1920. Every
   range below now carries the highlight card's numbers, so if one moves the
   other has to move with it — which is why 2026-09-01's reduction to
   clamp(22px, 2.1vw, 32px) was made in all three places at once. */
.fp-research-card__title {
  /* T1 */
  font-size: var(--fp-t1-size);
  font-weight: var(--fp-t1-weight) !important;
  line-height: 1.15;
  margin: 0 0 16px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.fp-research-card__authors {
  /* T3. The names sit in an <em> the feed does not control, and Chase asked for
     them upright and comma-separated on 2026-09-16 — frontpage.js turns the
     feed's pipes into commas, this un-italicises what is already there. */
  font-size: var(--fp-t3-size);
  font-weight: var(--fp-t3-weight);
  color: var(--fp-t3-color);
  margin: 0 0 24px !important;
}
.fp-research-card__authors em { font-style: normal !important; }
.fp-research-card__excerpt {
  /* T3 */
  font-size: var(--fp-t3-size);
  font-weight: var(--fp-t3-weight);
  line-height: 1.6;
  color: var(--fp-t3-color);
  margin: 0 0 26px !important;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.fp-research-card__actions {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: center;
}
.fp-research-card__actions .fp-btn {
  line-height: 1;
}
/* 🚨 THE RESEARCH CARD'S BUTTONS ARE THE SMALL SIZE, LIKE EVERY OTHER SECTION'S.
   Bunny and Lingzi, 14 Sep: "Read More & Podcast button are too big, should be
   same size as others e.g. view more". They were the only two buttons on the
   page still on the 46px default — "Read More" is .fp-btn--blue and "Podcast"
   is .fp-btn--dark, and neither carries a --*-sm modifier, so they kept the base
   geometry while the View More beside them is 38px. Sized here rather than by
   swapping their classes, because the blue and the dark are the card's own
   colours and only the size was wrong. */
.fp-page .fp-research-card__actions .fp-btn,
.fp-page .fp-research-card__actions a.fp-btn,
.fp-page .fp-research-card__actions button.fp-btn {
  height: 38px !important;
  min-height: 38px !important;
  max-height: 38px !important;
  padding: 0 22px !important;
  font-size: 14px !important;
}

/* ---------- Podcast popup (rendered on body, outside .fp-page scope) ---------- */
.fp-podcast-popup {
  position: fixed !important;
  bottom: 24px !important;
  right: 24px !important;
  width: 380px !important;
  max-width: calc(100vw - 48px) !important;
  background: #193b65 !important;
  border-radius: 0 !important;
  box-shadow: 0 8px 32px rgba(0,0,0,.18) !important;
  z-index: 99999 !important;
  overflow: hidden !important;
  animation: fp-popup-in .3s cubic-bezier(.22,.9,.3,1);
}
.fp-podcast-popup__header {
  display: flex !important;
  align-items: center !important;
  justify-content: space-between !important;
  padding: 12px 16px !important;
  background: #193b65 !important;
  color: #fff !important;
}
.fp-podcast-popup__title { font-size: 14px; font-weight: 700; }
.fp-podcast-popup__close {
  background: none;
  border: 0;
  color: #fff;
  font-size: 22px;
  line-height: 1;
  padding: 0 4px;
  cursor: pointer;
  opacity: .7;
  transition: opacity var(--fp-t-fast);
}
.fp-podcast-popup__close:hover { opacity: 1; }
/* Spotify's 12px corner radius is drawn inside the iframe. Trimming 4px off
   every side is enough to lose it: the farthest corner pixel left, (4,4), is
   sqrt(2)*8 = 11.3px from the arc's centre, inside the 12px radius — so no
   transparent corner survives, and the player keeps 12 of its 16px padding. */
.fp-podcast-popup__player { height: 144px !important; overflow: hidden !important; }
.fp-podcast-popup__player iframe {
  display: block !important; width: calc(100% + 8px) !important; max-width: none !important;
  height: 152px !important; margin: -4px 0 0 -4px !important; border: 0 !important;
}
@keyframes fp-popup-in {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}

/* Award frame variant */
/* ⚠️ THIS CARD SETS THE HEIGHT OF THE WHOLE RESEARCH SECTION, so its spacing is
   not a local decision. .fp-research__track is a flex row and the Research Blog
   group is the slide beside it, so whichever of the two is taller stretches the
   other. The second award card — the one with the extra paragraph — needed 557px
   at 1920 while the four blog tabs needed 475, and that 82px landed as white
   space under every tab. Chase, 2026-08-21: "中间间隔啥的可以给少一些，行距可以
   少一些". Padding, logo, leading and the gaps between blocks all came down;
   nothing lost any words. Check this card before blaming the blog tabs for the
   section's height. */
.fp-award-card {
  flex: 0 0 100%;
  min-width: 0;
  background: #fff;
  border-radius: var(--fp-radius-md) var(--fp-radius-md) var(--fp-radius-md) var(--fp-radius-md);
  padding: 28px 56px;
  transition: border-radius var(--fp-t-base);
}

/* Research Pillar slide — 3×2 grid of pillar cards inside a white container */
.fp-research-pillars {
  flex: 0 0 100%;
  min-width: 0;
  background: #fff;
  border-radius: var(--fp-radius-md);
  padding: 28px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
  transition: border-radius var(--fp-t-base);
}
.fp-pillar {
  display: flex !important;
  flex-direction: column;
  text-decoration: none !important;
  color: var(--fp-ink) !important;
  background: var(--fp-card);
  border-radius: var(--fp-radius-sm);
  overflow: hidden;
  box-shadow: var(--fp-shadow-sm);
  transition: box-shadow var(--fp-t-fast), transform var(--fp-t-fast);
}
.fp-pillar:hover {
  box-shadow: var(--fp-shadow-md);
  transform: translateY(-3px);
}
/* 🚨 ONE HEIGHT FOR ALL SIX, FIXED. Letting the picture absorb each card's
   slack (flex:1 1 auto) did end the white band under the shorter cards, but it
   gave the row three different picture heights — 106 / 90 / 76 at 1600 — and
   that reads worse than the space did. Chase, 2026-09-14: "一行的图的尺寸保持
   最小值 … 六个都一样吧". So the pictures agree and the leftover goes back under
   the text, where it is even across a row. */
.fp-pillar__img-wrap {
  /* The var is set by fitInner() when the fixed panel leaves the pillar grid
     with slack under it: all six pictures grow by the same amount, so they stay
     identical and the white strip goes. 76px is the floor, used whenever there
     is no slack and whenever the JS never runs. */
  height: var(--fp-pillar-img-h, 76px);
  overflow: hidden;
  flex-shrink: 0;
}
.fp-pillar__img {
  width: 100%;
  height: 100%;
  background-size: cover;
  background-position: center;
  transition: transform var(--fp-t-slow);
}
.fp-pillar:hover .fp-pillar__img {
  transform: scale(1.05);
}
.fp-pillar__body {
  padding: 11px 14px 12px;
  flex: 1;
  display: flex;
  flex-direction: column;
}
.fp-pillar__title {
  font-size: 13.5px;
  font-weight: 600 !important;
  color: var(--fp-navy);
  margin: 0 0 6px;
  line-height: 1.2;
  letter-spacing: -0.005em;
}
.fp-pillar__topics {
  list-style: none !important;
  margin: 0 !important;
  padding: 0 !important;
  font-size: 11.5px;
  color: var(--fp-ink-soft);
}
/* 🚨 line-height NEEDS !important HERE, AND THAT IS NOT DECORATION.
   Phlox ships `.entry-content ul li { line-height: 190% }` at (0,1,2); this
   selector is (0,1,1), so the 1.45 that stood here was never once applied — the
   subtopics rendered at 23.75px, not 18.1px. The whole tab was 60px taller than
   its own stylesheet said, and every attempt to tune the spacing did nothing.
   Same trap as .esgri-rb-pillar__topics on /research-blog-2/. If a line-height
   in this file appears to have no effect, check the computed value before
   changing the number. */
.fp-pillar__topics li {
  display: flex;
  align-items: baseline;
  gap: 6px;
  line-height: 1.25 !important;
  margin-bottom: 1px !important;
  font-weight: 600;
}
.fp-pillar__topics li::before {
  content: '＞';
  color: var(--fp-navy);
  font-size: 11px;
  font-weight: 900;
  flex-shrink: 0;
  line-height: 1;
}
.fp-award-card__brand {
  text-align: center;
  margin: 0 !important;
  padding: 0 !important;
}
.fp-award-card__brand img {
  height: 168px;
  width: auto;
  margin: 0 auto !important;
  padding: 0 !important;
  display: block;
}
.fp-award-card__heading {
  font-size: 22px;
  font-weight: 600 !important;
  line-height: 1.25;
  margin: 0 0 8px;
}
.fp-award-card p {
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--fp-ink-soft);
  margin: 0 0 8px;
}
.fp-page .fp-award-card__cta,
.fp-page a.fp-award-card__cta {
  display: flex !important;
  width: -moz-fit-content !important;
  width: fit-content !important;
  margin: 16px auto 0 !important;
}

/* Footer of research carousel */
.fp-research__foot {
  display: flex;
  align-items: center;
  gap: 24px;
  padding-top: 28px;
  margin-left: calc(var(--fp-side-w) + var(--fp-side-gap));  /* align under the card column */
  position: relative;
  z-index: 1;
}
.fp-progress {
  flex: 1;
  display: flex;
  gap: 0;
  height: 3px;
  border-radius: 0;
  overflow: hidden;
  background: rgba(255,255,255,.18);
}
.fp-progress__seg {
  flex: 1;
  background: transparent;
  transition: background var(--fp-t-fast);
}
.fp-progress__seg.is-active {
  background: #5BB5FF;
}
.fp-research__arrows { display: flex; gap: 10px; }
/* ⚠️ .fp-research__foot and .fp-research__arrows above style nothing since
   2026-09-07 — the outer carousel became a single frame when RESEARCH HEADLINE
   replaced the award slide, so the progress bar and its arrows went with it.
   .fp-progress itself is still live: HIGHLIGHTS uses it. */

/* ==========================================================================
   RESEARCH HEADLINE                                     (Bunny's v5, 7 Sep)
   ==========================================================================
   Three cards on the same navy ground as the research blog, one per kind of
   recognition: a top-journal paper, the Best Working Paper Prize, an award.
   Shape is the INSIGHTS card — outlined tag, title, grey meta line, picture
   underneath — at a larger size, because it has a third of a 1037px column
   here rather than a third of 666.

   🚨 THE PICTURE IS BOTTOM-ALIGNED ACROSS THE ROW, and that is what
   `.fp-hl-card__top { flex: 1 1 auto }` buys. The cards are flex items in one
   row, so they are all as tall as the tallest; the media has a fixed ratio, so
   every picture is the same height; letting the white top absorb the leftover
   puts all three pictures on the same line whatever the meta line does. Give
   the top a fixed height instead and a two-line byline (card 2) overflows it.
   ========================================================================== */
.fp-headline {
  display: grid;
  grid-template-columns: var(--fp-side-w) 1fr;
  column-gap: var(--fp-side-gap);
  align-items: center;
  /* The headline row leads the section (Chase, 2026-09-07: "RESEARCH HEADLINE
     放到research blog上面"), so the gap that used to sit above it now sits
     between the two blocks.

     🚨 144px, and the history here is misleading. It was 72, then 40 on
     2026-09-07 on the strength of a "空隙有些大" attributed to Chase. Bunny and
     Lingzi asked for the opposite on 14 Sep ("more space between 2 section"),
     and when that was put to Chase on 16 Sep he said the earlier remark was
     probably not about this seam at all, and set it to 144 himself. Do not
     "restore" 40 from the git history on the strength of that old quote.

     The blog block's own tab row adds its height on top of this, so the seam
     looks wider than the number. Desktop only — the below-1100 rule is still
     32px, because 144 on a phone is most of a screen of empty ground. */
  margin-bottom: 144px;
}
/* 🚨 THE SIZE HAS TO BE SET HERE, and the reason is the same trap the events
   list fell into. Phlox has `.entry-content h2 { font-size: 2.1em }` at (0,1,1),
   which beats the base `.fp-section-title` at (0,1,0): this heading rendered
   33.6px at every width above 720 while HIGHLIGHTS, RESEARCH BLOG, INSIGHTS,
   UPCOMING EVENTS and SCHOLARS all sat at 43-48, because each of those has a
   head-scoped rule at (0,2,0) and this one did not. Chase caught the pair.
   Same clamp as .fp-research__head so the two research headings match exactly. */
.fp-headline__head { min-width: 0; }
.fp-headline__head .fp-section-title {
  font-size: clamp(34px, 3vw, 48px);
  line-height: 1.12;
}
/* 🚨 THE ARROWS HANG IN THE PAGE PADDING, they do not take a share of the row.
   Inline they cost 84px of arrow and 36px of gap out of a 749px column, which
   took the cards from 234 to 194 — narrower than the INSIGHTS cards below,
   when the mockup has them half again as wide. The page keeps 202px of margin
   at 1440 (14vw) and the arrows only need 60 of it. Negative outer margins,
   not `position: absolute`: the arrows still centre themselves on the row's
   height, and hover keeps its 2px nudge (an absolute arrow would need a
   translate of its own, and the hover rule would overwrite it). */
.fp-headline__viewport {
  display: flex;
  align-items: center;
  gap: 18px;
  min-width: 0;
}
/* 🚨 THREE CLASSES, AND !important. `.fp-page button.fp-circle-arrow` sets
   `margin: 0 !important` at (0,2,1) further down this file, so the obvious
   `.fp-page .fp-headline__arrow--prev` (0,2,0) loses however loud it is and
   the cards stay at the 194px this block exists to fix. Measured, not read:
   the arrow's computed margin-left was 0px with the rule live. */
.fp-page .fp-headline .fp-headline__arrow--prev { margin-left: -60px !important; }
.fp-page .fp-headline .fp-headline__arrow--next { margin-right: -60px !important; }
.fp-headline__track-wrap {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  overflow: clip;
}
.fp-headline__track {
  display: flex;
  gap: 24px;
  will-change: transform;
}
.fp-hl-card {
  flex: 0 0 calc((100% - 2 * 24px) / 3);
  min-width: 0;
  container-type: inline-size;
  background: var(--fp-card, #fff);
  border-radius: 0 !important;
  overflow: hidden;
  display: flex;
  transition: transform var(--fp-t-fast), box-shadow var(--fp-t-fast);
}
@media (hover: hover) {
  .fp-hl-card:hover { transform: translateY(-3px); box-shadow: 0 4px 14px rgba(0,0,0,.28); }
}
.fp-page .fp-hl-card__link {
  display: flex;
  flex-direction: column;
  width: 100%;
  color: var(--fp-ink) !important;
  text-decoration: none !important;
}
.fp-hl-card__top {
  flex: 1 1 auto;
  padding: 24px 22px 20px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.fp-hl-tag {
  display: inline-block;
  padding: 4px 9px;
  border: 1px solid var(--fp-ink);
  background: transparent;
  color: var(--fp-ink);
  border-radius: 0 !important;
  font-size: clamp(9px, .78vw, 11.5px);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  line-height: 1.4;
  margin-bottom: 16px;
}
.fp-page .fp-hl-card__title {
  /* T2 */
  font-family: 'Poppins', 'Noto Sans', sans-serif;
  font-size: var(--fp-t2-size);
  font-weight: var(--fp-t2-weight) !important;
  line-height: 1.25;
  margin: 0 0 10px !important;
  color: var(--fp-ink);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.fp-page .fp-hl-card__meta {
  /* T3 */
  font-size: var(--fp-t3-size);
  font-weight: var(--fp-t3-weight);
  line-height: 1.35;
  color: var(--fp-t3-color);
  margin: 0 !important;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.fp-hl-card__media {
  position: relative;
  aspect-ratio: 500 / 528;
  flex: 0 0 auto;
  overflow: hidden;
}
.fp-hl-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* The journal plate. Bunny gave the colour and the face in her markup on the
   mockup: "Semni Bold Italic / blue color code: 0088ff". Right-aligned and
   pinned to the top-right corner of the picture, as on the mockup. */
.fp-hl-card__journal {
  position: absolute;
  top: 0;
  right: 0;
  /* 🚨 SIZED FROM THE CARD (cqw), NOT THE VIEWPORT (vw). Against the viewport
     the plate kept its 16px while the card shrank, so it grew from the 41% of
     the card width Bunny drew to 66% at 1440 and past 60% again on a phone,
     where one card fills the row — a slab of blue over the picture (Chase,
     2026-09-07: "蓝色色块有些问题"). 5.2cqw holds it near 41% at every card
     size, and the max-width is the backstop for a long journal name.

     4.6cqw, not the 5.25 the mockup measures: her card is 533px wide against
     our 234 at 1440 (she drew wider than the page's 14vw measure), and the
     plate cannot be both her 41% of the card and legible at our size. 4.6
     is the largest that keeps "Development" — the longest word, and what
     actually sets the width — inside half the card. */
  /* 🚨 `width: min-content`, or the plate is wider than the words in it. An
     absolutely positioned box shrink-to-fits to min(available, preferred),
     and "preferred" is the whole name on ONE line — far past the cap. So it
     took the cap, 50% of the card, and the right-aligned text left a slab of
     bare blue down its left side (Chase, 2026-09-07: "这里为什么要多出一块蓝").
     min-content is the longest WORD instead, which is the width the wrapped
     lines actually need; max-width stays as a backstop for a card so narrow
     that even one word will not fit. */
  width: min-content;
  max-width: 60%;
  padding: 7px 11px 9px;
  background: #0088FF;
  color: #fff;
  font-family: 'Poppins', 'Noto Sans', sans-serif;
  font-size: clamp(10px, 4.6cqw, 15px);
  font-style: italic;
  font-weight: 600;
  line-height: 1.25;
  text-align: right;
}
.fp-page .fp-headline__arrow { flex: 0 0 auto; }


.fp-page .fp-circle-arrow,
.fp-page button.fp-circle-arrow {
  width: 42px !important;
  height: 42px !important;
  min-width: 42px !important;
  min-height: 42px !important;
  max-width: 42px !important;
  max-height: 42px !important;
  border-radius: 50% !important;
  border: 0 !important;
  padding: 0 !important;
  margin: 0 !important;
  background: rgba(255,255,255,.16) !important;
  color: #fff !important;
  display: inline-flex !important;
  align-items: center !important;
  justify-content: center !important;
  font-size: 17px !important;
  line-height: 1 !important;
  box-sizing: border-box !important;
  cursor: pointer;
  transition: background var(--fp-t-fast), transform var(--fp-t-fast), opacity var(--fp-t-fast);
}
@media (hover: hover) {
  .fp-page .fp-circle-arrow:hover { background: var(--fp-gold) !important; transform: translateX(2px); }
}
.fp-page .fp-circle-arrow:active { background: var(--fp-gold) !important; }
/* White circle, BLUE arrow — Bunny, 2026-09-09: "please also change the arrow
   to white circle with blue arrow", with the RESEARCH HEADLINE prev arrow
   circled. It was already a white circle; the glyph was --fp-navy (#0A1F44),
   which against a navy band reads as black rather than blue. This is the same
   #0088FF as the journal plate on the cards beside it, so the section carries
   one blue. */
/* 🚨 `button` IS LOAD-BEARING. The base rule above is written
   `.fp-page .fp-circle-arrow, .fp-page button.fp-circle-arrow`, and that second
   selector is (0,2,1) — it outranks a plain `.fp-page .fp-circle-arrow--light`
   at (0,2,0) no matter how many !importants either carries. So this modifier
   had never applied to a single arrow on the live page: the headline pair have
   been translucent-white pills since they were built, which is what Bunny
   circled. The same trap cost a round trip on the arrow margins in this file
   already. */
.fp-page button.fp-circle-arrow--light {
  background: #fff !important;
  color: #0088FF !important;
}
@media (hover: hover) {
  .fp-page button.fp-circle-arrow--light:hover { background: var(--fp-gold) !important; color: #fff !important; }
}
.fp-page button.fp-circle-arrow--light:active { background: var(--fp-gold) !important; color: #fff !important; }
.fp-page .fp-circle-arrow--inactive { opacity: .4; }
@media (hover: hover) {
  .fp-page .fp-circle-arrow--inactive:hover { opacity: .7; }
}

/* Scholars + News carousel arrows — light-bg variant: black default, lighter gray hover */
.fp-page .fp-scholars .fp-circle-arrow,
.fp-page .fp-scholars button.fp-circle-arrow,
.fp-page .fp-news .fp-circle-arrow,
.fp-page .fp-news button.fp-circle-arrow {
  background-color: #14171F !important;
  background-image: none !important;
  color: #fff !important;
  font-size: 24px !important;
  line-height: 1 !important;
}
@media (hover: hover) {
  .fp-page .fp-scholars .fp-circle-arrow:hover,
  .fp-page .fp-news .fp-circle-arrow:hover {
    background-color: #9aa0ac !important;
    transform: none;
  }
}
.fp-page .fp-scholars .fp-circle-arrow:active,
.fp-page .fp-news .fp-circle-arrow:active {
  background-color: #9aa0ac !important;
  transform: none;
}

/* ==========================================================================
   5. SCHOLARS
   ========================================================================== */
.fp-scholars {
  margin: 96px auto 60px;
}
.fp-scholars__body {
  min-width: 0;
}
/* 🚨 THE ARROWS DO NOT GET COLUMNS OF THEIR OWN.

   Chase, 2026-08-21: "学者头像的最右边和下面insights的左右边对齐". This used to be
   a `50px 1fr 50px` grid, so the arrows ate 62px (50 + the 12px gap) off EACH
   side of the content column and the portraits were the only thing on the page
   that did not span it. Measured at 1920: Highlights, Upcoming Events, the
   Insights row and this section's own filter pills all run 652.8 → 1651.2,
   while the portraits ran 714.8 → 1589.2.

   The arrows are absolute now, in the gutter, and the track owns the whole
   column — the same shape `.fp-news__viewport` already has. Verified 652.8 →
   1651.2 at 1920, 489.6 → 1238.4 at 1440, 435.2 → 1100.8 at 1280, matching the
   Insights row to the pixel at each.

   🚨 THE OFFSET NEVER LANDS TANGENT TO A PORTRAIT, AND THAT IS THE WHOLE RULE.
   The gutter is 4vw and the arrow is 42px, so the arrow only fits inside it
   with air on both sides down to about 1375 (4vw = 55). A constant 54px past
   that walks the left arrow into the SCHOLARS title — 2.1px of air at 1280, a
   3.3px collision at 1150 — and sliding it with the gutter only moves the
   collision to the other side: at 1280 `4vw - 8` leaves it 1.2px off the first
   portrait, which reads as a mistake rather than as a control.

   So the arrow is either clear of the portraits or frankly on top of them.
   Above 1375 it rides the gutter, 12px off the photos where there is room and
   never closer than 5. Below, it straddles the edge at 21px — half over the
   portrait, the way the Insights arrows sit over their cards. */
.fp-scholars__viewport {
  position: relative;
  display: block;
  min-width: 0;
}
/* 🚨 ONE OFFSET, TWO CAROUSELS — Chase, 2026-08-21: "这两个部分的箭头也可以对齐".
   The Scholars and Insights rows sit in the same `.fp-side` content column, so
   feeding both arrow pairs the same number is what puts them on the same two
   vertical lines. Insights used to hard-code `left/right: 18px`, i.e. INSIDE the
   frame and on top of the first and last card, which is why the two sections'
   arrows sat ~72px apart at 1920. Change this token, not one section. */
.fp-scholars__viewport,
.fp-news__viewport {
  --fp-carousel-arrow-out: 21px;
}
/* `!important` on the transform is load-bearing: `.fp-page .fp-scholars
   .fp-circle-arrow:hover` sets `transform: none`, and without it the arrow
   would drop half its own height the moment the cursor touched it. The Insights
   arrows carry it for the same reason. */
.fp-page .fp-scholars button.fp-scholars__arrow {
  position: absolute !important;
  top: 50% !important;
  transform: translateY(-50%) !important;
  z-index: 5;
}
.fp-page .fp-scholars button.fp-scholars__arrow--prev {
  left: calc(var(--fp-carousel-arrow-out) * -1) !important;
  right: auto !important;
}
.fp-page .fp-scholars button.fp-scholars__arrow--next {
  right: calc(var(--fp-carousel-arrow-out) * -1) !important;
  left: auto !important;
}
/* 1375 is where 4vw reaches 55px — the arrow plus 5px of air off the portraits
   and 8px off the title column, the tightest that still reads as "outside".
   Above ~1550 the `min()` settles at 54 and the gap opens to a full 12, which
   is the layout Vera drew. */
@media (min-width: 1375px) {
  .fp-scholars__viewport,
  .fp-news__viewport {
    --fp-carousel-arrow-out: min(54px, calc(var(--fp-side-gap) - 8px));
  }
}
.fp-scholars__track-wrap {
  overflow: hidden;
  min-width: 0;
}
.fp-scholars__track {
  display: flex;
  gap: 18px;
  transition: opacity .35s ease;   /* transform stays JS-owned — see note above */
  will-change: transform, opacity;
}
.fp-scholars__track.is-fading { opacity: 0; }
/* 🚨 FEWER PORTRAITS, NOT SMALLER ONES. Bunny and Lingzi, 14 Sep: "if the
   browser is small/narrow, should be reduce the numbers of displaying
   professors, not smaller their size, like the insights part". Four across was
   the only rule above 1100, so the row just squeezed: measured 174px wide at
   1440, 143 at 1200 and 130 at 1101, where a head is barely legible.

   Three is the base now and four is the exception, because four only has room
   above ~1440: the track is about 0.52 of the window there, and 4 x 174 + 54 of
   gaps needs 750px of track. Below that, three keeps every portrait at or above
   the 174px the design was drawn at — 237 at 1440 down to 179 at 1101.

   Chase, 2026-09-17, on the band below 1100: "现在似乎是到了一个阈值之后会直接变成
   两个，现在改成三个可以吗". That rule is in the max-width:1100 block further down;
   it was two and is now three, so the count now steps 4, 3, 3, 2 instead of
   jumping 4 to 2. The phone rule (max-width:720) stays at two. */
.fp-scholar {
  flex: 0 0 calc((100% - 36px) / 3);
}
@media (min-width: 1441px) {
  .fp-scholar { flex: 0 0 calc((100% - 54px) / 4); }
}
.fp-scholar {
  position: relative;
  aspect-ratio: 3 / 4;
  border-radius: var(--fp-radius-md) !important;
  overflow: hidden;
  background: #ddd;
  cursor: pointer;
  box-shadow: var(--fp-shadow-sm);
  text-align: left;
}
.fp-page .fp-scholar__photo,
.fp-page img.fp-scholar__photo {
  position: absolute !important;
  inset: 0 !important;
  top: 0 !important;
  left: 0 !important;
  width: 100% !important;
  height: 100% !important;
  max-width: none !important;
  max-height: none !important;
  min-width: 0 !important;
  min-height: 0 !important;
  object-fit: cover !important;
  display: block !important;
  transition: transform var(--fp-t-base);
}
.fp-scholar__base {
  position: absolute;
  bottom: 0; left: 0; right: 0;
  padding: 18px 22px 22px;
  background: transparent !important;
  color: #fff;
  text-align: right;
  text-shadow: 0 1px 6px rgba(0,0,0,.65);
  transition: opacity var(--fp-t-fast);
}
.fp-scholar__base .fp-scholar__name {
  font-size: 16px;
  font-weight: 600 !important;
  /* Bunny and Lingzi, 14 Sep: "delete the space between name and school".
     The two lines are one label, not two paragraphs. */
  margin: 0 !important;
  white-space: nowrap;
  letter-spacing: -0.01em;
}
.fp-scholar__affil {
  font-size: 12.5px;
  margin: 0;
  line-height: 1.35;
  opacity: .92;
}
.fp-scholar__bio {
  position: absolute;
  inset: 0;
  background: var(--fp-gold-trans);
  color: #fff;
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  opacity: 0;
  transition: opacity var(--fp-t-base);
  overflow-y: auto;
}
.fp-scholar:hover .fp-scholar__bio { opacity: 1; }
.fp-scholar:hover .fp-scholar__base { opacity: 0; }
.fp-scholar__bio .fp-scholar__name {
  font-size: 19px;
  font-weight: 600 !important;
  margin: 0 0 6px !important;
  line-height: 1.15;
}
.fp-scholar__role {
  font-size: 12.5px;
  margin: 0 !important;
  line-height: 1.4;
}
/* .fp-scholar__intro is gone from the markup — Chase, 2026-09-16: only the
   four HKU leaders carried a paragraph and the eighteen external scholars did
   not, so the hover panel read as two different designs. The rule is kept, and
   only the rule, so that restoring a paragraph is an HTML edit rather than a
   redesign:
     font-size: 10.5px; line-height: 1.45; margin: auto 0 0 0; opacity: .94;
   Nothing renders it today. */

.fp-scholars__pills {
  display: flex;
  justify-content: center;
  gap: 18px;
  margin-top: 36px;
  flex-wrap: wrap;
}
.fp-page .fp-pill,
.fp-page button.fp-pill,
.fp-page button[type="button"].fp-pill,
.fp-page button[type="submit"].fp-pill {
  display: inline-flex !important;
  align-items: center !important;
  gap: 10px !important;
  padding: 14px 30px !important;
  background-color: #14171F !important;
  background-image: none !important;
  color: #fff !important;
  border: 0 !important;
  border-radius: 0 !important;
  font-size: 15px !important;
  font-weight: 700 !important;
  -webkit-appearance: none !important;
  appearance: none !important;
  cursor: pointer;
  transition: background-color var(--fp-t-fast), color var(--fp-t-fast);
}
/* 🚨 THE AFFILIATE PILLS ARE BUTTONS LIKE THE REST. Bunny and Lingzi, 14 Sep,
   twice: "these buttons are too big, should be same as view more", and "mouse
   over should be gold color background, black color text, same as others".
   The grey #4A4E58 hover was the only hover on the page that was not gold, and
   the 14px/30px padding made them taller than every neighbouring button.
   Black text on gold is what .fp-btn--primary:hover does; the -sm buttons use
   white on gold, but Bunny asked for black and these sit on white. */
.fp-page .fp-pill,
.fp-page a.fp-pill,
.fp-page button.fp-pill {
  height: 38px !important;
  min-height: 38px !important;
  max-height: 38px !important;
  padding: 0 22px !important;
  font-size: 14px !important;
  box-sizing: border-box !important;
}
.fp-page .fp-pill:hover,
.fp-page a.fp-pill:hover,
.fp-page button.fp-pill:hover {
  background-color: var(--fp-gold) !important;
  color: #14171F !important;
}
.fp-page .fp-pill--gold,
.fp-page .fp-pill.is-active,
.fp-page button.fp-pill.is-active,
.fp-page button.fp-pill--gold {
  background-color: #B89642 !important;
}
.fp-page .fp-pill--gold:hover,
.fp-page .fp-pill.is-active:hover,
.fp-page button.fp-pill--gold:hover,
.fp-page button.fp-pill.is-active:hover {
  background-color: #8B720A !important;
}

/* ==========================================================================
   6. IN MEDIA
   ========================================================================== */
.fp-news {
  margin-top: 96px !important;
  margin-bottom: 80px !important;
  padding: 0;
  box-sizing: border-box;
}
.fp-news__viewport {
  position: relative;
  display: block;
  min-width: 0;
  /* 🚨 NO RIGHT BLEED. This used to cancel the page's 14vw right padding so the
     strip ran to the window edge; measured at 1440 it ended at x=1440 while
     Highlights, Upcoming Events and Scholars — the other three `fp-side`
     sections — all end at 1238. It was the only one out of line, which only
     became obvious once the row held three wide cards instead of six narrow
     ones (Chase, 2026-08-12: "不要全部延伸到最右了，保持设定的72vw").
     72vw is the page's own measure: 100 - 2 x --fp-page-pad. */
}

/* Set by frontpage.js while a section is fetching its data, cleared when the
   data settles either way. `visibility`, not `display`: the hand-written
   fallback still occupies its height, so nothing below it jumps — the visitor
   sees the section's own space, then its real contents, and never the stale
   cards being swapped out from under them. Applied from JS, so a visitor with
   no JavaScript hides nothing and reads the fallback. */
.fp-hydrating,
.fp-prehydrate { visibility: hidden !important; }
.fp-prehydrate > .is-active { visibility: hidden !important; animation: fp-force-reveal 0s linear 10s forwards !important; }

/* 🚨 `fp-prehydrate` is written in frontpage.html, NOT added by the script, and
   that is the whole point. Adding it from JS left a window between the fragment
   being injected and the script running in which the stale cards were painted —
   long enough to catch: the "Awards" card Chase screenshotted was still
   reachable after the first attempt at this.

   The animation is a safety net, not decoration. If frontpage.js never loads,
   nothing would remove the class and these blocks would stay invisible for
   good; a zero-length animation delayed 10s reveals them anyway. The script
   normally wins that race by eight seconds. */
@keyframes fp-force-reveal { to { visibility: visible; } }
.fp-prehydrate { animation: fp-force-reveal 0s linear 10s forwards !important; }

.fp-news__arrow {
  position: absolute !important;
  top: 50% !important;
  transform: translateY(-50%) !important;
  z-index: 5;
}
.fp-page .fp-news button.fp-news__arrow--prev {
  left: calc(var(--fp-carousel-arrow-out) * -1) !important;
  right: auto !important;
}
.fp-page .fp-news button.fp-news__arrow--next {
  right: calc(var(--fp-carousel-arrow-out) * -1) !important;
  left: auto !important;
}
/* 🚨 THE CLIP IS FLUSH WITH THE CARDS, SO IT EATS THEIR SHADOWS.
   The row spans the content column exactly and the first and last cards sit on
   its edges, so `overflow: hidden` cut the outer shadow off at the very two
   sides Chase asked to see it on ("给insights的卡片外围都加上一圈shadow",
   2026-08-21) — the shadow was there all along and only rendered between cards.

   `overflow: clip` clips identically but honours `overflow-clip-margin`, which
   lets a fixed band of paint escape without making the box scrollable and,
   unlike padding + a negative margin, without moving anything: the viewport has
   no padding or border, so a negative top margin here would collapse through it
   and drag the whole row up the page.

   🚨 THE BAND AND THE CARD'S BLUR HAVE TO BE SIZED TOGETHER: y + blur ≤ 18px.
   The band lets out whatever is painted in it, including the shadow of the card
   waiting off-frame — that one starts 18px out (the track gap) and reaches
   `blur / 2` back toward us. Too wide a band and its tail prints as a soft
   vertical bar at the right edge of the row with no card attached to it: I
   shipped 16px against an 18px blur and called the tail invisible, and Chase
   saw it at once ("右侧还有未出现的下一个卡片的阴影，这个不对"). Too narrow and
   the cut lands on our own shadow while it is still ~2% dark, which is the same
   artefact one card to the left.

   So the band must sit above our reach and below the neighbour's:
     ours (sides)  = blur/2      = 7      ours (bottom) = y + blur/2 = 10
     neighbour     = 18 - blur/2 = 11
   10px satisfies both, which is why the card's blur is 14 and not more — raise
   one and you must lower the other. Measured at the row's right edge: 240 at
   the card, pure white from +8, cut at +10 with nothing to cut.

   `hidden` stays above as the fallback for Safari before 16, which keeps the
   clipped look rather than losing the clip and printing all ten cards. */
.fp-news__track-wrap {
  overflow: hidden;
  overflow: clip;
  overflow-clip-margin: 9px;
  min-width: 0;
}
.fp-news__track {
  display: flex;
  gap: 18px;
  width: 100%;
  margin-left: 0;
}
/* 🚨 A FIXED BASIS, NOT `flex: 1 1 0`. Every card used to take an equal share
   of the row, so both widths depended on HOW MANY cards were in the track —
   the note below still says the ratio was tuned for the count of the day. That
   stopped being workable when the section went live and started carrying the
   ten newest items: ten equal shares came out 76px wide and the row was
   unreadable.

   The basis is now solved for THREE cards whatever the count (Chase,
   2026-08-12: "一下子还是同时展示3个") and `.fp-news__track-wrap` (already
   `overflow: hidden`) clips the rest. The arrows rotate the whole list through
   those three places.

   3.4 = two ordinary cards plus the feature at 1.4; 2 x 18px is the gap
   between three of them. Three is also what the 1.4 ratio was originally
   tuned against — see the note below, which says the ordinary cards should
   come out around 300px, and at three they do. */
.fp-news__track > .fp-news-card {
  flex: 0 0 calc((100% - 2 * 18px) / 3.4);
  min-width: 0;
  transition: flex-basis .7s var(--fp-ease);
}
/* The "featured" card (the FIRST one) gets wider — class is set by JS so the
   width change animates concurrently with the carousel slide instead of
   snapping after it. */
/* 1.4, not 2. The ratio is what sets the collapsed card's width, and the ask
   was for those to be half again as wide: at 2 they came out ~200px, at 1.4
   they are ~300px while the feature stays about where it was. Because every
   card is `flex: 1 1 0`, both widths depend on how MANY cards are in the
   track — this number is tuned for the three that are there now, so re-check
   it if a card is added or dropped. */
.fp-news__track > .fp-news-card.is-feature {
  flex-basis: calc(1.4 * (100% - 2 * 18px) / 3.4);
}
/* A white card on a white page: the shadow is not decoration here, it is the
   only thing that draws the card's edge — `--fp-radius-*` are all 0, so there
   are no corners to read either. `--fp-shadow-sm` (0 2px 8px / .06) was too
   little to see at all, which is what "加上一圈shadow" was about.

   Two layers on purpose: the 4px one puts a definite edge right at the card,
   the 14px one gives the ambient falloff. The offsets stay small so the shadow
   reads on all four sides — a big `y` would light the top edge out completely
   and leave a pool under the card instead of a ring. 🚨 `3px 14px` is not a
   free choice: it is bounded by the 18px track gap, because the wrap's clip
   margin has to clear this shadow without also clearing the next card's. See
   the note on `.fp-news__track-wrap` before changing either number. */
.fp-news-card {
  background: var(--fp-card);
  border-radius: var(--fp-radius-md) !important;
  overflow: hidden;
  box-shadow: 0 1px 4px rgba(0,0,0,.05), 0 2px 10px rgba(0,0,0,.13);
  transition: transform var(--fp-t-fast), box-shadow var(--fp-t-fast);
  display: flex;
  flex-direction: column;
}
/* Same two layers, lifted — `--fp-shadow-md` alone (0 8px 24px / .10) now reads
   as a sideways step rather than up, because the resting state is no longer the
   near-invisible sm. */
.fp-news-card:hover {
  box-shadow: 0 2px 6px rgba(0,0,0,.06), 0 4px 12px rgba(0,0,0,.16);
  transform: translateY(-3px);
}
/* While the row is rotating the cards travel under a stationary cursor, so
   each one in turn takes the -3px lift and drops it again — a vertical wobble
   riding on a horizontal slide. Set by frontpage.js for the length of the
   animation only; `pointer-events` is what actually stops :hover matching. */
.fp-news__track.is-rotating > .fp-news-card {
  pointer-events: none;
}
.fp-news-card__link {
  display: flex;
  flex-direction: column;
  height: 100%;
  color: inherit;
}
.fp-news-card__top {
  padding: 18px 20px 16px;
}
/* ── OUTLINED, LIKE THE ARCHIVE PAGES ─────────────────────────────────────────
   Chase, 2026-08-27: "insights里面那些标签改成和reports页面里面差不多的，黑线外围
   黑字". /reports/ (and /columns/, /perspectives/, /voices/, /news/ — they all
   run archive.css) marks a card with `.arc-kind`: a 1px ink rule, ink text on
   nothing, uppercase at .1em. Same idea here, one step up in size because these
   cards are 210-358px wide against the archive's tighter rows — 9.5px against
   its 8, everything else copied off it.

   🚨 THE PER-KIND FILLS ARE GONE, AND THAT REMOVES A TRAP. The modifier is
   built in newsCard() from the kind string, lowercased and hyphenated, so every
   new row in INSIGHT_REST needed a matching rule added here or its pill
   rendered as bare text on white with no background — a data change breaking a
   stylesheet at a distance. One style for every kind cannot fail that way.
   (--press/--media/--awards/--report/--column/--perspective all deleted; the
   class is still emitted, it simply has nothing to match.) */
.fp-news-tag {
  display: inline-block;
  padding: 3px 7px;
  border: 1px solid var(--fp-ink);
  background: transparent;
  color: var(--fp-ink);
  border-radius: 0 !important;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  line-height: 1.4;
  margin-bottom: 12px;
}

/* 🚨 CLAMPED, AND THE CLAMP IS LOAD-BEARING. Unclamped, the two English press
   releases run 126 and 112 characters — ten and eight lines in a 210px column,
   against one to four for everything else. Every card stretches to the tallest,
   so those two titles alone set the row to 701px and the other nine paid for it
   as white space between the excerpt and the picture: 16px on the tall card,
   235px on the short ones (Chase, 2026-08-12: "简介和图片之间的space有些多").
   The void was slack, not padding — there was nothing to tighten.

   4 is where the curve bends, measured at 1440/1100/900 — going 3 -> 4 takes
   the truncated cards from 4 to 2 (7 to 3 at 900px) for 22px of height, and
   4 -> 5 buys no further truncation at all for another 22px. The two that stay
   truncated are the 126- and 112-character press-release headlines, which no
   clamp fits in a 210px column; they show ~40% and the full title is one click
   away. */
.fp-news-card__title {
  /* T2 — it was a flat 17px/600 against the headline card's 20px/700, which is
     the size AND weight mismatch Chase called out on 2026-09-16: the two are
     the same kind of thing and have to look it. */
  font-size: var(--fp-t2-size);
  font-weight: var(--fp-t2-weight) !important;
  line-height: 1.3;
  margin: 0 0 6px;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 5.2em;   /* 4 lines x 1.3 */
}
.fp-news-card__date {
  /* T3 — Chase, 2026-09-16: the date sits under the Insights title exactly
     where the byline sits under a Research Headline title, so it is the same
     tier and was the last thing still at 12.5px on its own grey. */
  font-size: var(--fp-t3-size);
  font-weight: var(--fp-t3-weight);
  color: var(--fp-t3-color);
  margin: 0 0 10px;
}
.fp-news-card__excerpt {
  font-size: 13px;
  line-height: 1.55;
  color: var(--fp-ink-soft);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 7.75em;  /* 5 lines x 1.55 */
}
/* 🚨 5, THE SAME AS EVERY OTHER CARD — was 9, and putting it back reintroduces
   a row-height jump on every arrow click. All ten cards sit in the track at all
   times, so the tallest of them fixes the row height no matter how it is
   rotated — UNLESS the feature is allowed to be the tallest, because then the
   height depends on which card currently holds that slot, and the excerpts run
   anywhere from 4 to 20 lines. The feature still reads as the feature: 1.4x the
   width, the wide image instead of the tall one, and more words per line at the
   same five lines. */
.fp-news-card.is-feature .fp-news-card__excerpt {
  -webkit-line-clamp: 5;
}
/* Grows into whatever slack a short title leaves rather than sitting at the
   bottom with the slack above it — `margin-top: auto` put every spare pixel in
   one place, directly under the excerpt, which is exactly where it showed. The
   picture absorbing it instead means the gap under the text is a constant 16px
   (the padding) on every card; the cost is that image heights vary by up to
   one title's worth, which is a ragged top edge rather than a white hole.
   `flex-basis` stays `auto` so `height` is the floor, including the 200px
   mobile override below. */
.fp-news-card__media {
  flex: 1 0 auto;
  height: 280px;
  overflow: hidden;
  position: relative;
}
.fp-news-card__media img,
.fp-news-card__img {
  position: absolute !important;
  inset: 0 !important;
  width: 100% !important;
  height: 100% !important;
  max-width: none !important;
  max-height: none !important;
  object-fit: cover !important;
  display: block !important;
  transition: transform .6s var(--fp-ease), opacity .5s ease;
}
/* Default: show the tall "thumbnail" image; hide the wide "full" image */
.fp-news-card__img--full { opacity: 0; }
.fp-news-card__img--thumb { opacity: 1; }
/* Featured card: show the wide "full" image instead */
.fp-news-card.is-feature .fp-news-card__img--thumb { opacity: 0; }
.fp-news-card.is-feature .fp-news-card__img--full { opacity: 1; }

.fp-news-card:hover .fp-news-card__media img { transform: scale(1.06); }

/* ==========================================================================
   FOOTER (custom — replaces Phlox footer on pages that load this stylesheet)
   ========================================================================== */
.aux-elementor-footer {
  display: none !important;
}
.fp-footer {
  width: 100vw !important;
  margin-left: calc(-50vw + 50%) !important;
  margin-right: calc(-50vw + 50%) !important;
  margin-top: 80px !important;
  margin-bottom: 0 !important;
  background: #000 !important;
  color: #fff;
  padding: 64px 48px 32px;
  border-radius: 0 !important;
  box-sizing: border-box;
  font-family: 'Poppins', -apple-system, sans-serif;
}
.fp-footer__cols {
  max-width: 1280px;
  margin: 0 auto 56px;
  display: grid;
  grid-template-columns: 1fr 1.3fr 1fr;
  gap: 48px;
  align-items: start;
}
.fp-footer__col--logo img {
  max-width: 180px;
  width: 100%;
  height: auto;
  display: block;
}
.fp-footer__col--partners {
  display: flex;
  flex-direction: column;
  gap: 28px;
}
.fp-footer__partner-label {
  font-size: 14px;
  font-weight: 300 !important;
  margin: 0 0 8px !important;
  color: #fff;
}
.fp-footer__partner-logo {
  display: inline-block;
  text-decoration: none;
}
/* 🚨 SIZED BY WIDTH, NOT HEIGHT, AND THAT IS THE WHOLE POINT.

   These carried `height: 84px / 140px` with `width: auto`, which looks safe and
   is not: the theme also puts `max-width: 100%` on footer images. On a desktop
   the column is wide enough that the cap never engages. On a phone it does — and
   when a fixed height meets a capped width, the image does not scale down, it
   SQUASHES.

   Measured on an iPhone-width viewport (393px): the Jockey Club lockup is a
   3.176:1 file rendering at 345x140 = 2.464:1, i.e. 22% narrower than it should
   be. That is the "JC logo seems squeezed" that Rick logged on roughly twenty
   devices, and the reason "JC logo" is the one entry against nearly every page
   in his sheet — it is one rule, not twenty-eight page bugs.

   Driving the WIDTH instead makes max-width a proportional clamp rather than a
   distortion: the height follows. The widths below are the old heights times
   each file's own aspect, so nothing moves on a desktop:

       HKU Business School  680x360  = 1.889 : 84px tall -> 159px wide
       HKJC Charities Trust 2560x806 = 3.176 : 140px tall -> 445px wide

   ⚠️ If either logo file is ever replaced, recompute the width from the new
   file's aspect. Leaving a stale number here re-creates exactly this bug. */
.fp-footer__partner-logo img {
  height: auto;
  width: 159px;
  max-width: 100%;
  display: block;
}
.fp-footer__partner:nth-child(2) .fp-footer__partner-logo img {
  width: 445px;
  margin-left: -24px;
  /* The Jockey Club file carries 176 of its 806 rows as empty space above the
     artwork, so "Funded by" sat 41px above it against 21px for "Established
     by" (Bunny, 2026-10-02: "這個空位要跟上面business school的一樣"). Crop
     122 of those rows: the box keeps the bottom 684, and the gap matches —
     21/21 at 1440, 21/19 at 390, measured on screen pixels. */
  aspect-ratio: 2560 / 684;
  object-fit: cover;
  object-position: center bottom;
}
.fp-footer__col--nav {
  display: flex;
  flex-direction: column;
  gap: 32px;
}
.fp-footer__nav {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.fp-footer__nav a {
  color: #fff !important;
  font-size: 15px;
  font-weight: 300 !important;
  letter-spacing: .06em;
  text-decoration: none !important;
  transition: opacity var(--fp-t-fast);
}
.fp-footer__nav a:hover { opacity: .65; }
.fp-footer__social {
  display: flex;
  gap: 12px;
  align-items: center;
}
.fp-footer__social a {
  display: inline-flex !important;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  background: #000 !important;
  color: #fff !important;
  border-radius: 50% !important;
  border: 0 !important;
  transition: background var(--fp-t-fast);
  text-decoration: none !important;
}
.fp-footer__social a:hover {
  background: #1f2533 !important;
}
.fp-footer__social svg {
  width: 18px;
  height: 18px;
  fill: currentColor;
  display: block;
}
.fp-footer__social-text span {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: #fff;
  line-height: 1;
}
.fp-footer__bottom {
  max-width: 1280px;
  margin: 0 auto;
  padding-top: 28px;
  border-top: 1px solid rgba(255,255,255,.12);
  text-align: center;
}
.fp-footer__disclaimer {
  font-size: 12px;
  font-weight: 300 !important;
  line-height: 1.6;
  color: rgba(255,255,255,.7);
  margin: 0 0 14px !important;
}
.fp-footer__copyright {
  font-size: 12px;
  font-weight: 300 !important;
  color: rgba(255,255,255,.7);
  margin: 0 !important;
}

@media (max-width: 900px) {
  .fp-footer__cols { grid-template-columns: 1fr; gap: 36px; }
  .fp-footer { padding: 48px 24px 24px; }
}

/* ==========================================================================
   Rounded floating site header (Phlox / Elementor) — applies only on pages
   that load this stylesheet (i.e., our FrontPage). Other pages unaffected.
   ========================================================================== */
/* Outer header: fixed white pill bg, min-height matches visual header height.
   Inside, the real content (#sticky-header) is also fixed-positioned independently. */
#site-elementor-header,
.aux-elementor-header {
  position: fixed !important;
  top: 0 !important;
  left: 0 !important;
  right: 0 !important;
  width: 100% !important;
  min-height: 120px !important;
  margin: 0 !important;
  padding: 0 !important;
  border-radius: 0 !important;
  background: transparent !important;
  overflow: visible !important;
  z-index: 1400 !important;
  filter: drop-shadow(0 8px 20px rgba(0,0,0,0)) !important;
  transition: filter .35s ease !important;
}
/* The pill/scrim layer. Default = header-height pill, expands to a mega-menu scrim on hover. */
.aux-elementor-header::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 100%;
  background: transparent;
  border-radius: inherit;
  z-index: -1;
  pointer-events: none;
  transition: background .35s ease, height .35s ease, border-radius .35s ease;
}

/* Sticky state: only color the pill + drop shadow, no geometry change */
#site-elementor-header.aux-sticky,
.aux-elementor-header.aux-sticky {
  filter: drop-shadow(0 8px 20px rgba(0,0,0,.18)) !important;
}
.aux-elementor-header.aux-sticky::before {
  background: #fff;
}

/* Hover (mega-menu): the pill expands downward to a full-width dark scrim covering the
   top half of the viewport. Dropdown items inside appear floated on top of the scrim. */
.aux-elementor-header:not(.aux-sticky):hover::before {
  background: rgba(0, 0, 0, 0.6);
  height: var(--fp-mega-h, 100%);  /* JS sets --fp-mega-h based on hovered dropdown height */
  border-radius: 0;
  pointer-events: auto;  /* capture hover so moving into the dark zone keeps menu open */
}

/* Dropdown sub-menu: transparent in mega-menu mode — the scrim provides the dark bg */
.aux-elementor-header:not(.aux-sticky) .premium-sub-menu {
  background: transparent !important;
  background-color: transparent !important;
  box-shadow: none !important;
  border: 0 !important;
}
.aux-elementor-header:not(.aux-sticky) .premium-sub-menu .premium-menu-link,
.aux-elementor-header:not(.aux-sticky) .premium-sub-menu .premium-sub-menu-link {
  color: rgba(255,255,255,.88) !important;
  background: transparent !important;
}
.aux-elementor-header:not(.aux-sticky) .premium-sub-menu .premium-menu-link:hover,
.aux-elementor-header:not(.aux-sticky) .premium-sub-menu .premium-sub-menu-link:hover {
  color: #fff !important;
  background: transparent !important;
  text-decoration: none !important;
}

/* Sub-menu hover underline: line is on the text-wrapping span so it hugs the text exactly */
.aux-elementor-header .premium-sub-menu .premium-sub-menu-link {
  text-decoration: none !important;
}
.aux-elementor-header .premium-sub-menu .fp-link-text {
  position: relative;
  display: inline-block;
}
.aux-elementor-header .premium-sub-menu .fp-link-text::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -4px;
  height: 1px;
  background: #fff;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .35s ease, background-color .35s ease;
  pointer-events: none;
}
/* 🚨 The gold warm belongs here too. Bunny, 2026-09-02: "top bar 的底線會劃完變
   金色,但下面的sub-item沒有,只有白色,要統一一下". The top-level row has drawn white
   and then warmed to gold since 2026-08-28; this rule only ever set the width,
   so the panel underneath stayed white and the two halves of the same menu
   behaved differently. Same pair of transitions as the row above: the draw, then
   the colour delayed by exactly the length of the draw. The sticky state already
   did this (see the block below) — it was state A that was out of step. */
.aux-elementor-header .premium-sub-menu .premium-sub-menu-link:hover .fp-link-text::after,
.aux-elementor-header .premium-sub-menu .premium-menu-link:hover .fp-link-text::after {
  transform: scaleX(1);
  background: #B89642;
  transition: transform .35s ease, background-color .3s ease .35s;
}
/* ── The dropdown in the sticky state ──────────────────────────────────────
   Chase, 2026-08-28, with a screenshot of the open Insights menu on the white
   bar: "我看到的依旧是这个啊". The TOP-LEVEL row was brought into line earlier the
   same day; the panel under it was not, and it was still the 2024 palette —
   measured live, every sub-menu link computed rgb(56,90,72), the same green the
   old /staff/ section heads used, because nothing here ever set a colour for
   the sticky state and Premium Addons' own default showed through.

   So the panel now follows the row above it: ink at rest, gold on hover, and an
   underline that draws in ink and then warms to gold on the same delay. State A
   is unchanged — white at .88 on the dark scrim, white underline.

   🚨 #14171F written out rather than var(--fp-ink), for the third time in this
   file and for the same reason: --fp-ink is declared on .fp-page and the header
   is outside it, so the variable never arrives. This underline was
   rgba(0,0,0,0) — invisible — until this change, exactly like the top-level one. */
.aux-elementor-header.aux-sticky .premium-sub-menu .premium-menu-link,
.aux-elementor-header.aux-sticky .premium-sub-menu .premium-sub-menu-link {
  color: #14171F !important;
}
.aux-elementor-header.aux-sticky .premium-sub-menu .premium-menu-link:hover,
.aux-elementor-header.aux-sticky .premium-sub-menu .premium-sub-menu-link:hover {
  color: #B89642 !important;
}
.aux-elementor-header.aux-sticky .premium-sub-menu .fp-link-text::after {
  background: #14171F;
}
.aux-elementor-header.aux-sticky .premium-sub-menu .premium-sub-menu-link:hover .fp-link-text::after,
.aux-elementor-header.aux-sticky .premium-sub-menu .premium-menu-link:hover .fp-link-text::after {
  background: #B89642;
  transition: transform .35s ease, background-color .3s ease .35s;
}
/* the nested-menu chevrons, which had a rule for state A only */
.aux-elementor-header.aux-sticky .premium-dropdown-icon,
.aux-elementor-header.aux-sticky .premium-sub-menu .fas {
  color: #14171F !important;
}

/* Dropdown chevron icons (e.g. arrow next to "Policy Research" indicating a nested submenu) */
.aux-elementor-header:not(.aux-sticky) .premium-dropdown-icon,
.aux-elementor-header:not(.aux-sticky) .premium-menu-link i,
.aux-elementor-header:not(.aux-sticky) .premium-sub-menu .fas {
  color: #fff !important;
}
/* Strip all square-bg/border from every Phlox inner layer so the rounded ::before shows through */
.aux-elementor-header #sticky-header,
.aux-elementor-header .e-con-parent,
.aux-elementor-header .e-con-inner,
.aux-elementor-header .e-con,
.aux-elementor-header .aux-wrapper,
.aux-elementor-header .aux-header,
.aux-elementor-header .aux-header-elements-wrapper {
  background: transparent !important;
  background-color: transparent !important;
  background-image: none !important;
  border: 0 !important;
  box-shadow: none !important;
}

/* Force #sticky-header into a fixed, constant viewport position (top:18) in BOTH states.
   Phlox normally toggles it between relative<->fixed which causes the logo to jump.
   By pinning it ourselves, layout never shifts; only the outer header's bg/shadow animates. */
#site-elementor-header #sticky-header,
.aux-elementor-header #sticky-header,
.aux-elementor-header.aux-sticky #sticky-header {
  position: fixed !important;
  top: 30px !important;
  left: 0 !important;
  right: 0 !important;
  width: 100% !important;
  transform: none !important;
  z-index: 1401 !important;
}

/* --- Logo swap: hide Phlox img + duplicate text, render our horizontal logo via bg-image --- */
.aux-elementor-header .aux-logo-anchor img { display: none !important; }
.aux-elementor-header .aux-logo-text { display: none !important; }
.aux-elementor-header .aux-logo-anchor {
  display: block !important;
  width: 260px !important;
  height: 52px !important;
  background-image: url('https://hkujcesgri.hku.hk/wp-content/uploads/2026/05/esgri-logo-white-horizontal-scaled.png') !important;
  background-position: center left !important;
  background-repeat: no-repeat !important;
  background-size: contain !important;
  transition: opacity .25s ease;
}
.aux-elementor-header.aux-sticky .aux-logo-anchor {
  background-image: url('https://hkujcesgri.hku.hk/wp-content/uploads/2026/05/esgri-logo-color-horizontal-scaled.png') !important;
}

/* --- Menu text color: white over video, dark when sticky --- */
.aux-elementor-header .premium-nav-menu .premium-menu-link-parent,
.aux-elementor-header .premium-nav-menu .premium-menu-link-parent i {
  color: #fff !important;
  transition: color .35s ease;
}
/* 🚨 Written out for the same reason as the underline above: --fp-ink lives on
   .fp-page, the header is outside it, so this said `color: var(--fp-ink)` and
   resolved to nothing. The menu text was falling back to the theme's inherited
   #444 and only LOOKED right. #14171F is what was meant. */
.aux-elementor-header.aux-sticky .premium-nav-menu .premium-menu-link-parent,
.aux-elementor-header.aux-sticky .premium-nav-menu .premium-menu-link-parent i {
  color: #14171F !important;
}

/* Hide the "Home" nav item on this page (we're already on home, so it's redundant) */
.aux-elementor-header .menu-item-home {
  display: none !important;
}

/* Remove Premium Addons' default green underline (Premium uses ::before for the bar) */
.aux-elementor-header .premium-nav-menu .premium-menu-link-parent::before {
  display: none !important;
  content: none !important;
}
.aux-elementor-header .premium-nav-menu .premium-menu-link-parent,
.aux-elementor-header .premium-nav-menu .premium-menu-link-parent:hover {
  border-bottom: 0 !important;
  box-shadow: none !important;
  background: transparent !important;
  background-image: none !important;
  text-decoration: none !important;
  position: relative;
}

/* Our custom hover underline: animates width 0 → 100% (left to right), in the
   colour of the state's own menu text, and THEN warms to gold.

   Chase, 2026-08-28: "形态b我想让其朝着a对齐，悬浮下划线，一开始是黑色，然后hover
   上去变金色". Aligning B to A means the rule is the same in both states and only
   the starting colour differs — white over the video, ink once the bar is white
   — with the same #B89642 at the end of both. That gold is the site's, declared
   with that exact value in in-the-media.css, archive.css and voices.css.

   🚨 THE COLOUR CHANGE IS DELAYED BY EXACTLY THE DRAW. `background-color .3s
   ease .35s` in the hover rule starts the warm only once the 0.35s width
   animation has finished, which is what makes it read as "黑色，然后变金色"
   rather than as a gold line appearing. The base rule keeps an UNDELAYED
   background transition so the colour drops back promptly on mouse-out instead
   of staying gold while the line retracts. Two transitions on purpose. */
.aux-elementor-header .premium-nav-menu .premium-menu-link-parent::after {
  content: '' !important;
  display: block !important;
  position: absolute !important;
  left: 0 !important;
  right: auto !important;
  bottom: 12px !important;
  width: 0 !important;
  height: 1px !important;
  background: #fff !important;
  background-color: #fff !important;
  background-image: none !important;
  border: 0 !important;
  transition: width .35s ease, background-color .2s ease !important;
  pointer-events: none;
  transform: none !important;
}
/* 🚨 #14171F WRITTEN OUT, NOT var(--fp-ink). --fp-ink is declared on .fp-page
   and the site header is NOT inside .fp-page — custom properties only inherit
   down the tree, so the variable never reached here. The declaration was
   invalid at computed-value time and dropped, which left this underline
   TRANSPARENT: measured on the live page 2026-08-28, hovering a scrolled menu
   item animated the width to 130px in rgba(0,0,0,0). It had been invisible in
   the sticky state since the day it was written. */
.aux-elementor-header.aux-sticky .premium-nav-menu .premium-menu-link-parent::after {
  background: #14171F !important;
  background-color: #14171F !important;
}
.aux-elementor-header .premium-nav-menu .premium-menu-link-parent:hover::after {
  width: 100% !important;
  background: #B89642 !important;
  background-color: #B89642 !important;
  transition: width .35s ease, background-color .3s ease .35s !important;
}

/* Hero — header is now fixed (out of flow), no need for the -50px overlap */
.fp-page .fp-hero { margin-top: 0 !important; }

/* ==========================================================================
   Sticky nav scroll state — DELETED 2026-08-28, it never did anything
   ==========================================================================
   There was a `body.fp-scrolled` block here painting the bar
   rgba(245,245,245,.96) with a blur and a shadow. Its selector list was
   "generous so the correct element gets the change regardless of theme
   version" — and it listed .elementor-location-header, #site-header,
   .site-header, .main-header-container and #masthead. Counted in the live HTML
   of /home-2/: every one of those appears ZERO times. The header on this site
   is `.aux-elementor-header`, which the list does not contain, so the rule has
   matched nothing since it was written.

   What actually paints the bar white when scrolled is
   `.aux-elementor-header.aux-sticky::before` further up this file, keyed to the
   class PHLOX adds, not to fp-scrolled. Re-pointing the dead rule at
   .aux-elementor-header would have layered a grey-blue blur under that white
   and changed the design; deleting it leaves the bar exactly as it looks today.

   ⚠️ frontpage.js still adds and removes `fp-scrolled` on every scroll event.
   Nothing styles it now. Left alone deliberately — another session was in that
   file the same day — but it is dead weight and can go with the listener. */

/* ==========================================================================
   Responsive
   ========================================================================== */

/* INSIGHTS shows TWO cards, not three, below 1400 (Chase, 2026-09-03:
   "insights部分改成在较小宽度下为两个，而不是三个"; the threshold moved from 1100
   to 1400 on 09-09 after Bunny photographed three cramped cards on her laptop —
   "can we keep 2 only if the browser is narrow"). 1400 is where three stops
   working: the carousel viewport is 749px at 1440 and 666px at 1280, so three
   cards go from 236px each to 207px.

   It lives in its own query rather than in the ≤1100 block below, because that
   block also unstacks the whole page and none of the rest of it should start at
   1400. The old line here set grid-template-columns on a flex row and did
   nothing; the width is the flex-basis, so it is the basis that changes. Same
   shape as the desktop rule — one plain card plus the feature at 1.4, one 18px
   gap. The carousel is unaffected: slideDistance() measures the plain card, it
   does not assume three. */
@media (max-width: 1400px) {
  .fp-news__track > .fp-news-card { flex-basis: calc((100% - 18px) / 2.4); }
  .fp-news__track > .fp-news-card.is-feature { flex-basis: calc(1.4 * (100% - 18px) / 2.4); }
}

@media (max-width: 1100px) {
  .fp-side { grid-template-columns: 1fr; gap: 28px; }
  .fp-events { grid-template-columns: 1fr; }
  .fp-highlight { grid-template-columns: 1fr; }
  .fp-research-card { grid-template-columns: 1fr; }
  .fp-research__group { grid-template-columns: 1fr; grid-template-rows: auto auto auto; }
  .fp-research__group > .fp-research__head { grid-column: 1; grid-row: 1; margin-bottom: 20px; }
  .fp-research__group > .fp-tabs { grid-column: 1; grid-row: 2; }
  .fp-research__group > .fp-research__inner { grid-column: 1; grid-row: 3; aspect-ratio: auto; }
  .fp-research__foot { margin-left: 0; }
  .fp-research__inner { aspect-ratio: auto; }
  .fp-scholar { flex: 0 0 calc((100% - 36px) / 3); }   /* three, not two — Chase 2026-09-17 */
  /* The title stops being a column here, so the arrows sit in the page's own
     14vw padding (154px at 1100) and there is nothing left to crowd — the 4vw
     ceiling above is measuring a gutter that no longer exists. */
  .fp-scholars__viewport,
  .fp-news__viewport { --fp-carousel-arrow-out: 54px; }
  /* HEADLINE: the title stops being a column, and two cards fit where three
     did. Three items against two places is also the point at which the arrows
     start doing something — setupHeadlineCarousel() enables them by counting. */
  /* 🚨 72px between the two blocks inside the navy band, not 32. Chase,
     2026-09-18: "蓝色空间的内部还是有些挤，各个板块之间". Research Headline and
     Research Blog are two separate sections sharing one band, and at 32px the
     seam between them was TIGHTER than the 40px the band keeps at its own top
     and bottom — so the band read as one long run rather than two blocks. The
     desktop sets 144 here (Bunny item 1, Chase's own number on 2026-09-16); on
     a phone that is a sixth of the screen, so this is set against the band's own
     edge padding rather than copied from the desktop.

     🚨 IT MOVES WITH THAT PADDING. When the edge went 40 -> 64 (Chase, same day,
     "这个部分的padding可以大一些，现在只有40px") a seam left at 72 would have been
     only 8px more than the edge, and the band would have gone back to reading as
     one long run — the thing the 72 was for. 96 keeps the seam clearly the widest
     gap in the band without spending a sixth of the screen on it. */
  .fp-headline { grid-template-columns: 1fr; margin-bottom: 96px; }
  .fp-headline__head { margin-bottom: 24px; }
  .fp-hl-card { flex-basis: calc((100% - 24px) / 2); }
}

/* ----- Mobile (≤720px) ----- */
@media (max-width: 720px) {
  .fp-page { padding: 0 16px; }

  /* Header: shorter pill, smaller logo */
  #site-elementor-header,
  .aux-elementor-header {
    min-height: 72px !important;
  }
  #site-elementor-header #sticky-header,
  .aux-elementor-header #sticky-header,
  .aux-elementor-header.aux-sticky #sticky-header {
    top: 12px !important;
  }
  .aux-elementor-header .aux-logo-anchor {
    width: 170px !important;
    height: 34px !important;
  }

  /* Hero: smaller min-height, less padding under header */
  .fp-hero {
    height: 88vh;
    min-height: 480px;
    padding-top: 72px;
  }
  .fp-hero__inner { padding: 0 20px; }
  .fp-hero__title { font-size: clamp(34px, 9vw, 56px) !important; }
  .fp-hero__subtitle { font-size: 15px; margin-bottom: 40px !important; }
  .fp-hero__cta { gap: 12px; }

  /* Numbers: 2 cols, tighter */
  .fp-numbers { padding: 28px 16px !important; margin-top: -4vh !important; }
  .fp-numbers__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; }
  /* 🚨 `.fp-numbers__value` DOES NOT EXIST. The markup calls them .fp-stat__num
     / .fp-stat__label / .fp-stat__icon, so this whole "Numbers: tighter" block
     only ever reached the section and the grid — every stat kept its desktop
     type on a phone. See the Global Impact block at the end of this file for
     what that did and what replaced it. The dead selector is kept here, struck
     out, so the next person does not re-add it (Chase, 2026-08-26). */

  /* Section titles overall */
  .fp-section-title { font-size: clamp(28px, 7vw, 38px) !important; }
  .fp-section-eyebrow { font-size: 12px !important; }

  /* Highlights — stack img + content */
  .fp-highlight { grid-template-columns: 1fr !important; gap: 16px; }
  .fp-highlight__img { aspect-ratio: 16/9; height: auto; }
  .fp-highlights__head { flex-wrap: wrap; gap: 12px; }

  /* Events: simpler list */
  .fp-events { padding: 36px 0; gap: 16px; }
  .fp-events__list { border-top: 0; }
  .fp-event__link {
    grid-template-columns: 1fr 24px;
    grid-template-areas:
      'date    arrow'
      'title   arrow'
      'excerpt arrow';
    gap: 4px 12px;
    padding: 12px 0 !important;
  }
  /* the same 0.63-of-the-section-title ratio the base rule now follows. Flat
     17px was right at 390, where the section title is 28 — but that title is on
     `clamp(28px, 7vw, 38px)` and reaches its 38px ceiling by ~543, so from
     there to 720 the row title was sitting at 0.45 of it. 4.4vw keeps 390
     unchanged and closes that band. */
  /* T1 on mobile is the highlight/research pair's curve — see
     .fp-research-card__title below, which carries the same numbers. */
  .fp-event__title { font-size: clamp(21px, 5.6vw, 27px) !important; line-height: 1.3; }

  /* Research carousel: tighter container, smaller text, single-col pillars */
  /* 🚨 LONGHANDS, NOT THE SHORTHAND. This section is full-bleed: the base rule
     pairs `width: 100vw` with `margin-left/right: calc(-50vw + 50%)` to pull it
     back out of .fp-page's 16px padding. `margin: 40px auto` here reset both of
     those to `auto`, so the 100vw box started at the padding edge and ran 16px
     off the right of the screen — the tab strip and the carousel's next arrow
     were sliced in half (Chase, 2026-08-26). Set only the vertical margins. */
  .fp-research { margin-top: 40px !important; margin-bottom: 40px !important; }
  /* 🚨 40px top AND bottom. Bunny and Lingzi, 14 Sep, item 16: "The space
     between the heading and the ending of research section should be wider" —
     two screenshots, one of the navy band starting straight into RESEARCH
     HEADLINE, one of the last card almost touching the band's bottom edge.
     Measured at 390px: 20px above the heading and 14px below the last thing,
     against 64/64 on the desktop, which is why it only reads as cramped on a
     phone. The old values were also lopsided — less air at the foot than the
     head, so the band looked like it had been cut off rather than ended. */
  .fp-research__container {
    padding: 64px 16px !important;
    border-radius: 0 !important;
  }
  .fp-research__head { margin-bottom: 18px !important; }
  .fp-research__head .fp-section-title { font-size: clamp(26px, 7vw, 36px) !important; }
  .fp-research-card {
    grid-template-columns: 1fr !important;
    gap: 16px !important;
    padding: 18px !important;
  }
  .fp-research-card__media { aspect-ratio: 4/3; }
  .fp-research-card__title { font-size: clamp(21px, 5.6vw, 27px) !important; margin-bottom: 8px !important; }  /* = .fp-highlight__title on mobile */
  .fp-research-card__excerpt { font-size: var(--fp-t3-size) !important; -webkit-line-clamp: 4; margin-bottom: 20px !important; }  /* T3 */
  .fp-award-card { padding: 22px 16px !important; }
  .fp-award-card__brand img { max-height: 90px !important; width: auto; }
  .fp-research-pillars {
    grid-template-columns: 1fr !important;
    padding: 16px !important;
    gap: 12px !important;
  }
  /* Tabs: scroll horizontally if they overflow */
  .fp-page .fp-tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .fp-page .fp-tabs::-webkit-scrollbar { display: none; }
  .fp-page .fp-tab,
  .fp-page button.fp-tab {
    padding: 10px 16px !important;
    font-size: 13px !important;
    white-space: nowrap;
    flex-shrink: 0;
  }

  /* Scholars: single col + stacked filters */
  .fp-scholars { padding: 36px 0; }
  .fp-scholar { flex: 0 0 100% !important; }
  /* The page padding is 16px on a phone, so an arrow 54px outside the track
     would hang off the viewport and give the whole page a horizontal scrollbar.
     They move onto the portrait instead. (Written when this was one card wide;
     it is two from 2026-08-27 — see H at the end of this file — and the arrows
     now sit over the OUTER edge of the pair, clear of both faces.) */
  .fp-scholars__viewport,
  .fp-news__viewport { --fp-carousel-arrow-out: -10px; }
  .fp-scholars__pills {
    flex-direction: column !important;
    gap: 8px !important;
    align-items: stretch;
  }
  .fp-scholars__pills .fp-pill {
    width: 100% !important;
    justify-content: center;
  }

  /* HEADLINE stays a carousel on the phone — one card at a time, arrows live.
     The insights row below stacks instead, because it carries ten items and a
     ten-tap carousel is worse than a scroll. Three headline cards are not. */
  .fp-hl-card { flex-basis: 100%; }
  /* 🚨 THE ARROWS SIT ON THE CARD, SO THE CARD GETS THE WHOLE STRIP.
     Bunny and Lingzi, 14 Sep, item 14: "The box should be larger and the arrow
     button for mobile should be overlay the box, like the insights". They are
     right that Insights is the model — the two carousels have the SAME markup,
     `viewport > [prev, track-wrap, next]`, and differ only here in CSS.

     Measured at 390px before this block: the strip is 358px wide and the
     headline card got 254 of it (71%), because each arrow is a 42px flex item
     in the flow with a gap beside it. Insights' arrows are absolute and inset
     10px INSIDE its card, so its card takes all 358. Same treatment here gives
     the headline card 358 as well — 104px back, 41% wider.

     🚨 And they have to change colour. `fp-circle-arrow--light` is a WHITE disc
     with a blue glyph, which reads because it floats on the section's navy
     ground. The card is white too, and the card's vertical middle (y=2211) lands
     ABOVE the photo (which starts at 2226) — so a light arrow at top:50% would
     be a white disc on the white text area, i.e. invisible. Take Insights'
     colours instead: #14171F disc, white glyph, legible on the copy and on the
     picture both. Beating `.fp-page button.fp-circle-arrow--light` (0,2,1)
     needs the three-class form below. */
  .fp-page .fp-headline__viewport {
    position: relative;
    display: block;
    gap: 0;
  }
  .fp-page .fp-headline button.fp-headline__arrow {
    position: absolute !important;
    /* 🚨 ON THE PHOTO'S MIDLINE, NOT THE CARD'S. Insights can sit at top:50%
       because its card is nearly all picture. This card is copy on top and
       photo below, so 50% lands on the seam — and the first cut of this put the
       next arrow straight through the blue "Journal of Financial Economics"
       tag in the photo's top-right corner. Measured at 390px: the tag's bottom
       is 151px up from the strip's foot and the arrow ran 133..175.

       The photo is always the bottom band and always 16/10, so anchor from the
       foot instead of the head: half the photo, less half an arrow. Its width
       is the page less 2x16px of padding, so its height is 62.5vw - 20px, and
       the offset is 31.25vw - 31px. Checked against the live boxes at 390, 360
       and 720 — 90.9/81.5/194 against photo midpoints of 112/103/215, exact at
       all three, and 18px clear of the tag at 390. */
    top: auto !important;
    /* the photo is 500/528 now (see .fp-hl-card__media below): its height is
       (100vw - 32px) x 1.056, so half of it less half an arrow is 52.8vw - 38px */
    bottom: calc(52.8vw - 38px) !important;
    transform: none !important;
    z-index: 5;
    margin: 0 !important;
    background: #14171F !important;
    color: #fff !important;
    border-color: #fff !important;
  }
  .fp-page .fp-headline button.fp-headline__arrow--prev { left: 10px !important; right: auto !important; }
  .fp-page .fp-headline button.fp-headline__arrow--next { right: 10px !important; left: auto !important; }
  /* Chase, 2026-10-02: "这个似乎高度被压了一下，可以给多一些，手机版". The phone
     cropped every picture to 16/10, which for the 500x528 headline art took off
     nearly half of it (the Best Working Paper wordmark, Thomas Schmid's chin).
     The phone now shows the desktop's own 500/528 — the whole picture. */
  .fp-hl-card__media { aspect-ratio: 500 / 528; }

  /* News: vertical stack, no arrows, no featured-card width swap */
  .fp-news { padding: 32px 0; }
  .fp-news__head { padding: 0 4px; flex-wrap: wrap; gap: 8px; }
  .fp-news__title { font-size: clamp(30px, 8vw, 42px) !important; }
  /* 🚨 `.fp-page` and `button` are both load-bearing. This used to read
     `.fp-news__viewport > .fp-news__arrow`, which is (0,2,0) and loses to the
     base `.fp-page button.fp-circle-arrow { display: inline-flex !important }`
     at (0,2,1) — two !important declarations, so specificity decides. The
     arrows stayed on the stacked mobile column, absolutely positioned over the
     middle card, doing nothing. (They read as `flex`, not `inline-flex`,
     because `position: absolute` blockifies the value — which is why the
     computed style doesn't obviously point at that rule.) */
  /* ── 2026-08-27: INSIGHTS BECOMES A CAROUSEL HERE TOO ──────────────────────
     Chase: "现在insights那边没有切换的额，怎么处理？" — it was the one carousel on
     the page with no way to switch on a phone. This block used to turn the row
     into a stacked column, hide the arrows, and cap the list at three with
     `:nth-child(n+4) { display: none }` — the reasoning was that a column does
     not clip, so all ten would print in full and run for pages, and the rest
     were "one tap away under View More". That is defensible on its own, but not
     next to Highlights and Scholars, which both keep their arrows on a phone:
     seven of the ten items had no route to them from this module at all.

     One card per view, not two. These are content cards — tag, title, date and
     a 200px picture — and at two across they would be 173px wide, which is the
     size Scholars' portraits take because a face survives it and a headline
     does not.

     🚨 EVERY LINE BELOW IS LOAD-BEARING FOR THE ROTATION, not styling:
       - the track has to stay a ROW. setupNewsCarousel()'s rotate() reads
         `flexDirection === 'column'` and returns early — that guard was how the
         stacked column stayed still.
       - the wrap has to CLIP again. `overflow: visible` was right for a column;
         in a row it would print all ten side by side, off the screen.
       - the n+4 cap has to go. Cards rotate through the DOM, so a hidden 4th
         child is not "the ones you can't reach yet" — it is a blank that
         rotates INTO the visible slot on the third press.
       - .is-feature has to be neutralised. It is a flex-basis (1.4x) that JS
         toggles as the row turns; at one-per-view every card is the visible
         card, so the width must not move. Its other two effects are already
         moot here — the wide image is forced on every card below, and the
         5-line excerpt loses to the 3-line clamp further down. */
  .fp-page .fp-news__viewport > button.fp-news__arrow { display: inline-flex !important; }
  /* the same progressive pair the desktop wrap uses: `clip` with a 9px margin
     so the card's own shadow is not sliced off at the left and right edges,
     `hidden` first for anything that does not know `clip`. 9 < the 12px gap, so
     the next card still cannot peek through. */
  .fp-news__track-wrap {
    overflow: hidden !important;
    overflow: clip !important;
    overflow-clip-margin: 9px !important;
  }
  .fp-news__track {
    flex-direction: row !important;
    gap: 12px !important;
    width: 100% !important;
    margin-left: 0 !important;
  }
  .fp-news__track > .fp-news-card,
  .fp-news__track > .fp-news-card.is-feature {
    flex: 0 0 100% !important;
    min-width: 0;
  }
  .fp-news-card__media { height: 200px !important; }
  /* On mobile every card shows the wide "full" image since there's no featured-position concept */
  .fp-news-card .fp-news-card__img--thumb { opacity: 0 !important; }
  .fp-news-card .fp-news-card__img--full { opacity: 1 !important; }
  /* stacked one per row on a phone — nothing has to line up, and
     reserving would just pad every card out */
  /* T2 — the flat 16px here was the last place the two article titles
     disagreed (16 against the headline card's 15 at 390). */
  .fp-news-card__title { font-size: var(--fp-t2-size) !important; min-height: 0 !important; }
  .fp-news-card__excerpt { min-height: 0 !important; }
  .fp-news-card__excerpt { -webkit-line-clamp: 3 !important; font-size: 13px !important; }

  /* Footer: stack columns.
     🚨 STACKED, NOT CENTRED. Bunny and Lingzi, 14 Sep, item 18: "Align the text
     to the left like below", with a mock-up of the footer menu run down the
     left edge. It is not a new design — it is what every OTHER page already
     does. Measured at 390px: on /insights/ and /staff/ the whole footer column
     computes text-align: start with the logo at x=24, while /home-2/ computed
     center with the logo pushed to x=105. Those pages load esgri-chrome.css and
     this one does not, so the five centring declarations that used to live here
     made the home page the only footer out of line. Removed rather than
     replaced: without them the desktop rules apply unchanged, which is the
     alignment the rest of the site has. */
  .fp-footer__cols {
    grid-template-columns: 1fr !important;
    gap: 32px !important;
  }

  /* 🚨 A HAIRLINE, BECAUSE THE SHADOW ALONE DOES NOT READ ON A PHONE.
     Bunny and Lingzi, 14 Sep, item 19: "The box for the report is disappear,
     may because the shadow is not obvious?" — and the guess is right. The
     shadow is there and it is not clipped: sampled a pixel out from the card's
     own edge on the live page, 235 at the left, 239 at the right, 233 below,
     fading to pure white by 12px, identical at 390 and 1440. 235 against 255 is
     about 8% at its darkest and gone within a few pixels; on a phone held in
     daylight that is nothing, and the white card on the white page simply has
     no edge.

     A border rather than a stronger shadow. The shadow's reach is deliberately
     sized against the clip band above (y + blur/2 = 7 against a 9px band, with
     the off-frame neighbour's tail starting at 18 - blur/2 = 11) — raising the
     blur means re-deriving all of that, and the last time it was got wrong the
     next card's shadow printed as a bar with no card attached. A 1px inset
     border costs nothing outside the box, so none of it moves. box-sizing is
     border-box, so the card stays 358px wide.

     Phone only: the desktop card was signed off as it is. */
  .fp-page .fp-news-card { border: 1px solid #E3E3E3; }
}

.fp-btn__more { display: none; }   /* shown only in the stacked layout — see the <=1100 block */

/* ----- Extra-small phones (≤420px) ----- */
@media (max-width: 420px) {
  .fp-hero__title { font-size: clamp(28px, 9vw, 42px) !important; }
  .fp-numbers__grid { gap: 16px; }
  .fp-research__container { padding: 64px 12px !important; }   /* see item 16 above */
  .fp-research-card { padding: 14px !important; }
  .fp-page .fp-tab { padding: 9px 12px !important; font-size: 12px !important; }
}

.fp-events__list > li::marker,
.fp-event::marker { content: none !important; }

/* ==========================================================================
   White strip under the footer                        (2026-08-05)
   ==========================================================================
   The footer is the last thing on the page, but the THEME's article wrapper
   keeps its own bottom margin below it: `.entry-main` carries 70px and
   `.entry-content` 50px. Nothing renders in that band, so it shows the white
   #inner-body background as a strip under the black footer — measured at
   exactly 70px between the footer's bottom edge and the document height.

   Zeroed rather than covered with a background: painting it black would leave
   70px of dead page still there and would move the problem to any page that
   ends on a different colour. Unscoped is safe — this stylesheet is only ever
   fetched by /home-2/. */
.entry-main,
.entry-content {
  margin-bottom: 0 !important;
}

/* ==========================================================================
   One arrow, every circular control                   (Chase, 2026-08-08)
   ==========================================================================
   The site had THREE arrow languages: '\u2039 \u203A' on /insights/ and /voices/,
   '\u2190 \u2192' on the hero slider and the awards row here, and an SVG chevron on the
   scholars and news rows here. Chase: "很多的左右切换的按钮其实都是用<>之类的
   标志，这个真的很丑，很不齐". The chevron won — it was already the best of the
   three when measured, and it is the only one that is geometry rather than
   typography.

   Measured on the live pages at 8x, ink inside the button:

                              ink        offset from centre
     \u2039 \u203A  (38px button)     4.0 x 7.1     y +2.00   <- 5% of the diameter
     \u2190 \u2192  (36px button)    14.9 x 5.8     y +1.44
     chevron (42px button)   5.8 x 10.1    y +0.50

   🚨 THE BOXES WERE ALWAYS CENTRED. Measured geometrically, every one of these
   buttons centres its child at exactly (0.00, 0.00) — flex was never wrong.
   What is off-centre is the INK INSIDE the glyph's own box: '\u2190' draws
   14.9 x 5.8px of ink inside a 16 x 25.6px line box, low. No amount of
   align-items fixes that, and it moves with every font fallback. An SVG's ink
   sits where the viewBox puts it, on every machine.

   34% rather than a px size, so one rule serves 36px, 38px and 42px buttons and
   follows them through the breakpoints. It overrides the width/height
   attributes on the scholars and news chevrons, which were 14px in a 42px
   button — 33.3%, so those do not move. */
.fp-page button.fp-arrow > svg,
.fp-page button.fp-circle-arrow > svg {
  width: 34% !important;
  height: 34% !important;
  display: block !important;
  flex: 0 0 auto !important;
  overflow: visible !important;
}

/* ─────────────────────────────────────────────────────────────────────────────
   🚨 THE RESEARCH CARD IS A FIXED-HEIGHT BOX AND ITS CONTENT DID NOT FIT.

   .fp-research__inner is `aspect-ratio: 2 / 1`, so the panel's height is half
   its width and nothing else — while the body inside it barely scaled at all,
   because .fp-research-card__title's clamp() hits its 46px ceiling at 1437px
   and stays there. Wide viewport, tall box, everything fits; narrow the window
   and the box shrinks past the type, which does not.

   The card is `overflow: hidden`, so nothing warns you: the excerpt and both
   buttons simply leave through the bottom edge. Measured before this block, on
   the Aug-20 Latest post, the tallest of the three slides overflowed at EVERY
   desktop width — 13px at 1920, 138 at 1440, 220 at 1280, 61 at 1024 — and
   Chase saw it as the card being cut in half (2026-08-20).

   The fix is in three parts, in this order:

     1. the title clamps to 3 lines (see .fp-research-card__title above). That
        alone took 199px off at 1920 and is what makes 1920 and 1700 fit.
     2. between 1101 and 1750 the body is sized to the PANEL, not the viewport:
        the title's ceiling comes down to 40px and picks up a 2.4vw term that
        actually engages in this range, and the excerpt drops to 3 lines.
     3. where even that is not enough — 1280 to 1500, the worst of the squeeze,
        and 768 to 1100 where the card is single-column with a full-width square
        image — a min-height floors the panel. `aspect-ratio` yields to
        min-height, so the box grows only where it was too short: +11px at 1440,
        +52 at 1280, nothing at 1600 and above.

   Verified 0px overflow at 1920/1700/1600/1440/1366/1280/1100/1024/900/768/600/
   390 after. If you change any type size or margin in the card, re-run that
   sweep — the margins here are not decoration, they are what buys the fit.
   ───────────────────────────────────────────────────────────────────────────── */
@media (min-width: 1101px) and (max-width: 1750px) {
  .fp-research-card { padding: 28px !important; gap: 28px !important; }
  /* 🚨 A STRAIGHT vw CURVE CANNOT FIT TWO LINES ACROSS THIS RANGE.
     Chase, 2026-09-01: "我想控制标题的行数,三行明显不合适". The text column here is
     327px at 1440 but only 256px at 1101, and the longest headline segment on
     the current cards is 21 characters. Measured live by stepping the size until
     every one of the five fitted two lines: 28px at 1440, 24px at 1280, 20px at
     1101 — a line through those is 2.36vw − 6px, which no plain `Xvw` matches.
     It is deliberately confined to this range: below 1101 the card goes single
     column and the same formula would give 20px in a 720px-wide column. */
  .fp-highlight__title,
  .fp-research-card__title,
  /* T1, 2026-09-16: the event row was left off this curve, so it was the only
     headline still at the plain clamp — 32px against the other two's 31.76 at
     1600, 26.88 against 24.2 at 1280. Same tier, same line. */
  .fp-event__title {
    font-size: clamp(20px, calc(2.36vw - 6px), 32px) !important;
  }
  /* The conference title used to be shrunk again here, because a rule inside a
     media query beats a plain one at the top of the file however loud either
     is. It no longer needs to be: every highlight title now takes the size
     above, and the long one takes a third line instead (2026-09-14). */
  .fp-research-card__title {
    margin-bottom: 10px !important;
  }
  .fp-research-card__authors { font-size: 15px !important; margin-bottom: 14px !important; }
  .fp-research-card__excerpt {
    font-size: 15px !important;
    -webkit-line-clamp: 3 !important;
    margin-bottom: 16px !important;
  }
}

/* min-height: 385px lived here until 2026-09-04. frontpage.js now measures the
   active slide and sets the panel's height, so a floor can only ADD dead space —
   it was leaving 13-40px in this range. */

/* 🚨 Do NOT reduce .fp-research-card's padding in this range to buy height — it
   backfires. Below 1101px the card is single-column and the media is a
   full-width SQUARE, so narrower padding makes the image wider, which makes it
   taller, which makes the card taller. Tried it: the 1100 deficit went from 18
   to 30. */
@media (min-width: 601px) and (max-width: 1100px) {
  .fp-research-card__title { margin-bottom: 10px !important; }
  .fp-research-card__authors { margin-bottom: 12px !important; }
  .fp-research-card__excerpt {
    -webkit-line-clamp: 3 !important;
    margin-bottom: 14px !important;
  }
  /* min-height: 455px removed 2026-09-04 — see the note in the 1101-1500 block. */
}

/* ─────────────────────────────────────────────────────────────────────────────
   🚨 THE RESEARCH PILLAR TAB WAS EATING ITS OWN SUBTOPICS.

   Same fixed box as the blog card, different content and a far worse fit. The
   six pillar cards sit in a 3x2 grid inside .fp-research__inner, which is
   `aspect-ratio: 2 / 1`, so the grid gets half the panel's width to work in and
   splits it into two equal rows — 213px each at 1920. The tallest card needs
   297px there, and more as the window narrows and the titles wrap. Everything
   past the first line of the topic list fell out through `overflow: hidden`.

   Measured before this block, subtopics visible out of the 17 in the markup:

       1920  7 of 17     Chase saw this one and asked about it
       1700  1 of 17
       1600 .. 768   ZERO
        600, 390     ZERO — the grid goes single-column, needs 1874px, gets 420

   So the tab was only ever nearly-working at one width. It is not a truncation
   to tune; the box is the wrong shape for this slide.

   The fix is to let the panel take the pillar grid's real height, but ONLY while
   that slide is the active one — the blog slides fit their 2:1 box (see the
   block above, verified 0px at twelve widths) and there is no reason to move
   them. `:has()` is what makes that scopeable in CSS: the panel reads which of
   its own slides is active and unpins itself for that one. The inactive slides
   stay absolutely positioned, so they contribute no height and the panel is
   exactly as tall as what you are looking at.

   Measured after: all 17 subtopics visible at 1920/1600/1440/1280/1100/1024/
   900/768/600/390, and the blog tab's panel height is unchanged to the pixel at
   every one of them.

   ⚠️ If `:has()` is ever unavailable the tab degrades to what it does today —
   clipped, not broken — so there is no fallback here on purpose.
   ───────────────────────────────────────────────────────────────────────────── */
.fp-research__inner:has(> .fp-research__inner-track > .fp-research-pillars.is-active) {
  aspect-ratio: auto !important;
  min-height: 0 !important;
}

.fp-research__inner:has(> .fp-research__inner-track > .fp-research-pillars.is-active) > .fp-research__inner-track {
  position: relative !important;
  inset: auto !important;
  height: auto !important;
}

/* The active pillar slide joins the flow so the track has something to measure;
   its siblings keep the absolute positioning the track's own rule gives them. */
.fp-research__inner-track > .fp-research-pillars.is-active {
  position: relative !important;
  inset: auto !important;
}

/* ─────────────────────────────────────────────────────────────────────────────
   ONE HEIGHT FOR EVERY SLIDE, AND IT IS THE HEIGHT THE TALLEST ONE NEEDS.

   Chase, 2026-08-21: switching to Research Pillar shoved the page down —
   385 -> 726 at 1440. The slides are stacked in one grid cell but every one of
   them is `position: absolute`, so NONE of them sizes the track; the panel got
   its height from `aspect-ratio: 2 / 1` and whatever did not fit was cut off by
   `overflow: hidden`.

   🚨 MY FIRST FIX PUT ONLY THE PILLAR SLIDE BACK IN FLOW, so the panel became
   exactly the pillar grid's height — and once the pillar grid was tightened, that
   was SHORTER than the blog cards needed. Chase's own screen is 3840x2160 at
   200%, i.e. 1920 CSS px, where the cards need up to 496px and the panel had
   become 455: the Editor's Pick image and both buttons were sliced off. Sizing a
   stack to one of its members is the bug, not the fix.

   So every slide joins the flow and shares one grid area. The track is then as
   tall as its tallest slide, always — no aspect ratio, no per-breakpoint
   min-heights, nothing to update when the copy changes. Inactive slides keep the
   opacity 0 / visibility hidden the track already gives them, so they are
   invisible and out of the accessibility tree; they only contribute height.

   This also fixes something nobody had reported: the AWARD panel's cards were
   being clipped at every width from 1101 to 2560, for the same reason.

   Measured, tallest slide vs the panel it was given, before -> after:

     2560   424 -> 626   three research cards and both award cards were clipped
     1920   455 -> 557   two research cards and both award cards  (Chase's screen)
     1600   455 -> 605   both award cards
     1440   472 -> 629   both award cards
     1280   516 -> 653   both award cards  (the 4K meeting room, 300%)
     1101   589 -> 677   both award cards

   Nothing is clipped at any of those now, and the panel height is identical on
   every tab, so switching moves nothing.

   ⚠️ ONLY ABOVE 1100px. At 1100 and below .fp-research-card goes single-column
   (image above the text) and wants ~1138px, and the pillar grid drops to one
   column. Stacking there would make the panel 1138px tall on a phone. That range
   keeps the old behaviour — including the clipping it already had before today,
   which is a separate thing to fix.
   ───────────────────────────────────────────────────────────────────────────── */
@media (min-width: 1101px) {
  .fp-research__inner { aspect-ratio: auto !important; }
  .fp-research__inner > .fp-research__inner-track {
    position: relative !important;
    inset: auto !important;
    height: auto !important;
  }
  .fp-research__inner-track > * {
    position: relative !important;
    inset: auto !important;
    grid-area: stack !important;
    /* 🚨 START, NOT STRETCH. All six slides share one grid area, so the track
       is as tall as the tallest of them (the pillar grid, 486 at 1366) and a
       stretched slide is that tall too — its body sat centred in the stretch
       while the box was sized to the content, so the card showed a white
       void on top and lost its bottom (Chase, 2026-09-04: "依旧存在这个问题").
       Each slide keeps its own height; the box, sized by fitInner(), clips
       the taller inactive ones, which are invisible anyway. */
    align-self: start !important;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   INSIGHTS cards: one picture size (Chase, 2026-08-24: "home-2这里存在图片
   大小不一的情况")
   ══════════════════════════════════════════════════════════════════════════
   Measured on the live row: every card is 569px tall and every title 88px, so
   the cards were already square with each other. The pictures were not — 381px
   on a Report and 280px on a Column, because `.fp-news-card__media` carried
   `flex: 1 0 auto` and grew into whatever the text left behind. A Column has an
   excerpt (min-height 7.75em) and a Report has none: Reports come from
   /insights/'s own data file, which has no summary field at all, so `sum` is
   hard-coded empty and there is no copy that could fill the gap.

   ⚠️ THE OLD BEHAVIOUR WAS DELIBERATE — see the note above .fp-news-card__media:
   the growth was chosen over `margin-top: auto` precisely to avoid a white hole
   under the excerpt, accepting "image heights vary by up to one title's worth"
   as the cost. That cost is the thing being reported now, so the trade is being
   made the other way, not discovered fresh.

   Since every card is the same height and some carry more words, SOMETHING has
   to absorb the difference — the only question is whether it is the picture or
   the space under the text. The picture is the part a visitor compares across
   cards, so it is now constant and the text block takes the slack: same shape
   on every card, some filled with words, some not. */
.fp-news-card__top { flex: 1 0 auto; }
.fp-news-card__media { flex: 0 0 auto; }

/* ══════════════════════════════════════════════════════════════════════════
   One rhythm between the modules (Chase, 2026-08-24: "每个模块之间的垂直距离
   可以大一些")
   ══════════════════════════════════════════════════════════════════════════
   Measured on the live page first, and the gaps were not one figure at all —
   108, 72, 80, 96, 95 between the five joins. Each section had picked its own
   margin over time (80/60, 72, 80, 96/60, 96/80), and because adjacent margins
   COLLAPSE the gap is whichever of the pair is larger, so no single number was
   ever in charge of it.

   --fp-band is that number now: one token, set on both sides of every join, so
   the rhythm is even by construction and "bigger" is one edit. 140 first, then 200 the same
   day: "每个模块之间现在距离太近，没有间隔感". 140 was already up from the 72-108
   it replaced, and still read as one continuous page rather than a stack of
   separate blocks — the number that stops modules touching is not the average
   of what they had, it is whatever finally reads as a break.

   ⚠️ .fp-hero -> .fp-numbers is deliberately NOT in here. Its gap is -65px:
   the numbers band is pulled UP over the hero on purpose, which is a composition
   and not a rhythm. Give it 140 and the overlap disappears. */
.fp-page { --fp-band: 200px; }

.fp-highlights,
.fp-events,
.fp-research,
.fp-scholars,
.fp-news {
  margin-top: var(--fp-band) !important;
  margin-bottom: var(--fp-band) !important;
}

/* the two that centre themselves horizontally have to keep doing it — their
   shorthand carried `auto`, and setting margin-top/bottom alone would leave it */
.fp-highlights, .fp-events, .fp-scholars, .fp-news {
  margin-left: auto !important;
  margin-right: auto !important;
}

@media (max-width: 900px) {
  /* the same proportion, at a phone's scale */
  .fp-page { --fp-band: 120px; }
}

/* ── The Upcoming Events arrow, to the comp ───────────────────────────────
   Chase, 2026-08-24: "这里右侧的箭头可以更加醒目一些". It was a 20px ink '→'
   that only became gold on hover; the comp draws it long, thin and gold from
   the start, which is what makes it read at the end of a 900px-wide row.
   frontpage.js now builds it as an SVG so its length is a viewBox and not a
   font's opinion — see the note there. */
.fp-event__arrow {
  width: 40px !important;
  height: 12px !important;
  display: block !important;
  color: var(--fp-gold) !important;
  overflow: visible !important;
}
.fp-event__link:hover .fp-event__arrow {
  color: var(--fp-gold) !important;   /* already gold; the shift is the feedback */
}

/* ═════════════════════════════════════════════════════════════════════════════
   MOBILE PASS — Chase, 2026-08-26: "home-2的手机端存在很多问题，请你做一个彻底
   的测试，还有修复". Audited at 320 / 360 / 390 / 414 against the live page.
   Four fixes here; the fifth (the full-bleed shorthand) is up in the ≤720 block
   where the damage was done, and the sixth is in frontpage.js.
   ═════════════════════════════════════════════════════════════════════════════ */

/* ── 1. The research panel was a fixed 420px box holding ~600px of content ────
   Above 1100 every slide joins the flow and the panel is as tall as the tallest
   (see the long note above @media (min-width: 1101px)). That block ends with
   "⚠️ ONLY ABOVE 1100px … keeps the old behaviour — including the clipping it
   already had before today, which is a separate thing to fix." This is that fix.

   Below 1100 the card goes single-column — image ABOVE the text instead of
   beside it — so it needs roughly 595px at 390 and gets 420. What that looked
   like, measured at 390:

     blog card   media row squeezed to 87px for a 254px image, which then
                 overflowed its own row and printed UNDER the title: the words
                 "ESG and the Stock Market: Is ESG Exposure Systematic?" sat on
                 top of the wind turbines. This is the screenshot Chase sent.
     award card  content 662px in the same 420px box, `overflow: hidden` — the
                 "Learn More" button was cut through the middle.

   Stacking every slide the way >1100 does is not an option here: the pillar
   grid is single-column on a phone and wants ~1145px, so all four tabs would
   get a 1145px panel. So the panel follows the ACTIVE slide instead — exactly
   what the `:has()` rule above already does for the pillar tab, generalised to
   every slide. Inactive slides stay absolute, so they still paint through the
   0.35s cross-fade but contribute no height. */
@media (max-width: 1100px) {
  /* 🚨 AND THE ROWS MUST NOT STRETCH, or this fix trades one bug for another.
     The two groups sit in one flex track, so both are as tall as the taller of
     them. Once each panel takes its own content height they stop matching (746
     vs 502 at 390), and a grid with `align-content: normal` stretches its auto
     rows to fill the leftover — which fell on the TAB row: the award group's
     white indicator grew from 41px to 72px, and to 266px once the pillar tab
     had made group 1 tall. Nothing showed while both panels were pinned to
     420px, because then the groups were the same height. */
  .fp-research__group { align-content: start; }
  .fp-research__inner {
    aspect-ratio: auto !important;
    min-height: 0 !important;
  }
  .fp-research__inner > .fp-research__inner-track {
    position: relative !important;
    inset: auto !important;
    height: auto !important;
  }
  .fp-research__inner-track > .is-active {
    position: relative !important;
    inset: auto !important;
  }

  /* ── 3. The events rows sat 18px right of their own heading ────────────────
     Phlox styles `.entry-content ul` with `padding-left: 1.1em` and beats the
     `padding: 0` on .fp-events__list. Above 1100 the heading is a separate
     column, so nobody could see it. At 1100 and below the two stack, and the
     offset shows at every width in that range — measured head/row: 101/119 at
     721, 126/144 at 900, 154/172 at 1100, 16/34 at 390. Desktop keeps the
     indent it has always had. */
  .fp-events__list { padding-left: 0 !important; }
}

@media (max-width: 720px) {
  /* ── 2. The highlights progress bar ran off the LEFT edge ──────────────────
     .fp-highlights__head is `justify-content: flex-end`, so when its one child
     is wider than the box the overflow goes left, not right — and there is no
     scrollbar to reveal it. The counter (43px) was at x = -99 at 390: "01 / 06"
     was not clipped, it was entirely off the screen, and the bar was cut too.
     The bar is the only flexible part of that row, so let it be flexible — and
     the nav has to flex too, or it stays shrink-to-fit and the bar has no free
     space to grow into (it collapsed to its 40px floor when only the bar was
     made flexible). */
  .fp-highlights__nav { flex: 1 1 auto; min-width: 0; }
  .fp-highlights__progress { width: auto; flex: 1 1 auto; min-width: 40px; }

  /* ── 4. The gold arrow was 40px wide in a 24px column ──────────────────────
     It became an SVG on 2026-08-24 (see the note above .fp-event__arrow) and
     grew from a 20px glyph to a 40px viewBox, but the mobile grid still gave it
     the old 24px track. It overflowed to x=390 — flush with the screen edge,
     no gutter, looking sheared off. */
  .fp-event__link { grid-template-columns: 1fr 40px; }
}

/* ── 5. Global Impact kept its desktop type on a phone ────────────────────────
   The mobile block up in @media (max-width: 720px) tries to shrink these with
   `.fp-numbers__value`, a class that appears nowhere in frontpage.html — the
   markup is .fp-stat__num / .fp-stat__label / .fp-stat__icon. So the numbers
   stayed at the 36px floor of `clamp(36px, 4.6vw, 64px)`, the labels at 24px
   and the icons at 130px square, inside a column 133px wide at 320.

   `1fr` is `minmax(auto, 1fr)`, and auto's floor is min-content — "2,375,646+"
   has no break opportunity in it, so the column could not shrink to its share
   and the grid pushed its right edge to x=387 whatever the screen was: 13px
   past the gutter at 390, 67px past it at 320. The section clips, so there was
   no scrollbar to show it — the widest number just ran under the edge of the
   screen. Only ≤720, where the grid is two columns; the four-column desktop
   layout measures inside its section at 721 through 1920.

   min-width: 0 lets the columns be equal again, and the type comes down to
   something that fits inside one. */
@media (max-width: 720px) {
  .fp-stat { min-width: 0; }
  .fp-stat__icon { width: 92px; height: 92px; margin-bottom: 8px; }
  .fp-stat__num { font-size: clamp(20px, 6.2vw, 34px); }
  .fp-stat__label { font-size: 15px; }
}

/* ═════════════════════════════════════════════════════════════════════════════
   MOBILE DESIGN PASS — Chase, 2026-08-27: "我觉得现在home-2的手机端设计还是一团糟".

   The pass before this one fixed six things that were broken. He is right that
   the result still does not look designed, and the reason is structural: there
   has never been a mobile comp. design/ and design_v2/ hold eleven desktop
   boards and not one phone screen, so every mobile rule in this file was written
   to stop the desktop layout from falling over at 390 rather than to lay
   anything out. Nothing below changes what a module is or the order they come
   in — it gives the phone the four things the desktop version has and it did
   not: one left rail, one vertical rhythm, line breaks that suit the measure,
   and captions you can read.
   ═════════════════════════════════════════════════════════════════════════════ */

/* ── A. ONE VERTICAL RHYTHM, IN PROPORTION ────────────────────────────────────
   The band is 200px at desktop and 120 below 900, which the note above calls
   "the same proportion, at a phone's scale". It is not: 200 of 1440 is 14% of
   the screen's width, 120 of 390 is 31%. The joins read as holes — a third of a
   phone screen of nothing between every pair of modules, five times over. 88 is
   22%, still an unmistakable break, and it takes ~160px of dead scroll out.
   ⚠️ Do not take this below ~72: 140 was rejected on the desktop side for
   exactly this reason ("没有间隔感"), and the break has to survive the phone. */
@media (max-width: 720px) {
  .fp-page { --fp-band: 88px; }
}

@media (max-width: 720px) {
  /* ── B. ONE LEFT RAIL ───────────────────────────────────────────────────────
     Four modules, four different insets: section titles at the 16px page
     gutter, the highlights card's text at 52 (36px of card padding), the
     research card's at 34, the insights card's at 36 — and the insights image
     flush at 16 while its own text is at 36. Nothing lines up with anything, so
     the eye finds no edge to follow down the page. Every card takes the same
     20px now, which puts ALL card text on one rail at x=36 against a section
     title rail at 16. Two rails, deliberately, instead of five by accident. */
  .fp-highlight { padding: 20px !important; }
  .fp-highlights__head { padding: 20px 20px 0 !important; }
  .fp-research-card { padding: 20px !important; }
  .fp-news-card__top { padding: 20px !important; }
  /* the research band is the one card that sits inside another box, and the
     ≤420 block narrows that box to 12px — which would leave its card text on
     32 while every other card sat on 36. Horizontal only: the smaller vertical
     padding there is still right on a small screen. */
  .fp-research__container { padding-left: 16px !important; padding-right: 16px !important; }

  /* ── C. THE HEADS STOP COSTING A THIRD OF A SCREEN ─────────────────────────
     Title, then a 24px gap, then a View More button, then the content: 190px
     before a module says anything, on a 844px screen, five times. The gap is
     the cheap half of that. */
  .fp-side__head,
  .fp-events__head { gap: 14px !important; }

  /* ── D. THE HERO'S LINE BREAKS ARE DESKTOP'S, NOT THE PHONE'S ──────────────
     The markup hard-breaks the headline after "HUB" and the standfirst after
     "research and". At 1440 those are the designer's line endings. At 390 the
     headline becomes WORLD CLASS HUB / FOR / SUSTAINABILITY — an orphaned
     three-letter line — and the standfirst breaks mid-clause with a short line
     hanging under a full one. Let the measure choose; it has a different one. */
  .fp-hero__title br,
  .fp-hero__subtitle br,
  .fp-highlight__title br { display: none; }
  /* Dropping the break alone is not enough: the browser fills greedily, so
     WORLD CLASS HUB takes the first line and FOR is left on its own above
     SUSTAINABILITY, which at 35px is 340px wide and can never share a line
     anyway. `balance` spends the same three lines evenly instead, and does the
     same for the standfirst's one-word last line. Unsupported browsers get the
     greedy wrap, which is what they have today. */
  .fp-hero__title,
  .fp-hero__subtitle { text-wrap: balance; }

  /* The two buttons were a white block on the right and, next to it, what looks
     like loose text: .fp-btn--ghost's border is `2px solid transparent`, which
     is invisible against a photograph. They are a pair, so they are now the
     same width and both have an edge. */
  /* 🚨 `.fp-page a.fp-btn` — the element selector in that base rule makes it
     (0,2,1), so a plain `.fp-hero__cta .fp-btn` at (0,2,0) loses to it however
     late it comes in the file. Anything overriding the button's own box has to
     out-specify it; `flex` is the exception, because the base rule never sets
     it and a first pass here that only appeared to work was doing just that. */
  .fp-hero__cta { gap: 10px; flex-wrap: nowrap; }
  .fp-page .fp-hero__cta a.fp-btn {
    flex: 1 1 0;
    min-width: 0;
    padding-left: 12px !important;
    padding-right: 12px !important;
  }
  .fp-page .fp-hero__cta a.fp-btn--ghost { border-color: rgba(255,255,255,.55) !important; }

  /* 96vh of hero leaves ~270px of empty water under the buttons once the
     content is centred. */
  .fp-hero { height: 78vh; min-height: 460px; }
  .fp-hero__subtitle { margin-bottom: 40px !important; }

  /* ── E. THE HIGHLIGHT CARD ─────────────────────────────────────────────────
     A 30px headline and a seven-line unclamped excerpt, in a card that is one
     of five in a carousel — the card was 667px tall and the excerpt trails off
     in an ellipsis anyway. Three lines is the same promise in a third of the
     room, and it matches what the research card next to it already does. */
  .fp-highlight__title {
    font-size: clamp(21px, 5.6vw, 27px) !important;
    margin-bottom: 10px !important;
  }
  .fp-highlight__excerpt {
    font-size: var(--fp-t3-size);   /* T3 */
    line-height: 1.6;
    margin-bottom: 18px !important;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* ── F. THE SCHOLAR'S NAME WAS UNREADABLE ──────────────────────────────────
     .fp-scholar__base carries no background — it leans on `text-shadow` alone,
     which works at 1440 where the card is a quarter of a row and the name sits
     over a dark suit. Full-width on a phone the same caption lands on the
     photographer's light grey backdrop, and "HKU Business School" at 12.5px in
     white on #a8a8a8 is not text any more. A scrim, and 13px. */
  .fp-scholar__base {
    padding: 48px 20px 18px !important;
    background: linear-gradient(to top,
                rgba(0,0,0,.72) 0%, rgba(0,0,0,.34) 55%, rgba(0,0,0,0) 100%) !important;
  }
  .fp-scholar__base .fp-scholar__name { font-size: 18px; }
  .fp-scholar__affil { font-size: 13px; opacity: 1; }
}

/* Two 15px buttons side by side need 278px of a 320px screen's 280px of inner
   width, and `white-space: nowrap` means they cannot give any of it back — at
   320 the hero's inner box took its own min-content width, 322, and hung 2px
   off the edge. Below 360 they stack instead, full width each. */
@media (max-width: 359px) {
  .fp-hero__cta { flex-wrap: wrap; }
  .fp-page .fp-hero__cta a.fp-btn { flex: 1 1 100%; }
}

/* ── G. THE TAB LABELS WRAP, AND THE PILLAR GRID KEEPS ITS 3x2 ───────────────
   Chase, 2026-08-27: "这个标签在手机端换下行" — and then, when I wrapped the
   strip instead: "事实上，我的意思是Latest 换行 Blog". The break he wants is
   INSIDE the tab, not between tabs: Latest / Blog, Editor's / Pick, Most /
   Popular, Research / Pillar, four of them still on one line.

   That is the better answer and it is worth writing down why. Wrapping the strip
   put two tabs on each of two rows, which meant an active tab on the top row no
   longer touched the panel it opens, and it cost the sliding white indicator —
   one absolutely positioned box whose height is the whole strip, so over two
   rows it paints behind both lines at once. Breaking the labels keeps one row:
   the tabs become four equal columns, the indicator still has one line to slide
   along, and the phone runs exactly the code the desktop does.

   The strip was `overflow-x: auto` before either attempt — four nowrap tabs need
   507px of a 358px card, so Research Pillar sat off the right edge behind no
   scrollbar and no fade. A horizontal scroller nobody can see is a hidden tab. */
@media (max-width: 720px) {
  .fp-page .fp-tabs {
    flex-wrap: nowrap !important;
    overflow-x: visible !important;
  }
  /* equal columns, and `min-width: 0` so a flex item may go narrower than its
     own longest word — without it the strip would still overflow, silently */
  .fp-page .fp-tab,
  .fp-page button.fp-tab {
    flex: 1 1 0 !important;
    min-width: 0 !important;
    white-space: normal !important;
    text-align: center !important;
    line-height: 1.25 !important;
    padding: 9px 6px !important;
    font-size: 12.5px !important;
  }

  /* ── THE PILLAR GRID: 2 COLUMNS, 3 ROWS, AT EVERY PHONE WIDTH ─────────────
     Chase asked for the desktop board's 3x2 first, then "三行两列吧都设置成".
     Three columns is the wrong count for a handset and the measuring says so:
     at 360 the cards are 95px, which leaves a 79px line, and "Environmental" —
     the longest word in this copy — sets 79px at the size the titles want. Every
     tile was one word away from breaking, and at 320 (81px cards) they broke
     outright: Leadershi/p, Regulatio/n, and subtopics cut off by the card.

     Two columns give 161px at 390 and 126px even at 320, so the ＞ markers stay,
     the type stays at a size worth reading, and nothing is one character from
     falling apart. It is 3 rows instead of 2, which on a page you scroll costs
     nothing — 615px against the 1145px the single column used to take.

     🚨 `min-width: 0` on the subtopic row is still load-bearing. That row is
     `display: flex`, so its text is an anonymous flex item with `min-width:
     auto` — a floor of its longest word. A word wider than the row does not
     wrap, it overflows, and the card's `overflow: hidden` cuts it. That was
     already happening at ONE column: "Board & Executive Performanc", "Corporate
     Control & Restructuri", invisible as an error, just the end of the line
     gone. `overflow-wrap: anywhere` is the backstop for whatever copy lands in
     this list later. */
  .fp-research-pillars {
    grid-template-columns: repeat(2, 1fr) !important;
    padding: 14px !important;
    gap: 10px !important;
  }
  .fp-pillar__img-wrap { height: var(--fp-pillar-img-h, 62px); }
  .fp-pillar__body { padding: 9px 10px 10px; }
  /* 12.5/11 were sized for the two-column card, which was 161px wide at 390.
     One per row gives the card the full 358px, so the type goes back up past
     the desktop sizes (13.5/11.5) rather than staying shrunk to fit a column
     that no longer exists. Chase, 2026-09-21: "字可以大一些". */
  .fp-pillar__title { font-size: 15.5px; margin-bottom: 6px; letter-spacing: -0.01em; }
  .fp-pillar__topics { font-size: 13px; }
  .fp-pillar__topics li { gap: 5px; min-width: 0; margin-bottom: 3px !important; }
  .fp-pillar__title,
  .fp-pillar__topics { overflow-wrap: anywhere; }

  /* ── ONE PILLAR PER ROW, PANEL HEIGHT UNCHANGED, SCROLL INSIDE ─────────────
     Bunny, 2026-09-21: one per row on mobile, keep the section the height it is
     now, and let the pillar list scroll within it.

     One per row gives the card the full 358px at 390, so the titles that used
     to break at three columns — Leadershi/p, Regulatio/n — have room and the
     subtopics sit one to a line. It costs height: six cards want about 1030px
     against the 675 the blog slides take.

     🚨 THE PANEL'S HEIGHT IS AN INLINE STYLE WRITTEN BY frontpage.js, and
     `fitInner` only ever GROWS it. So capping has to happen on the slide, not
     on the panel: a first attempt reverted the `:has()` unpin and made the
     slide `position:absolute; inset:0`, which fed the panel the pillar grid's
     1029 and pinned every tab at 969 — worse than before, and still clipped.
     Cap the slide and the panel simply never learns the bigger number.

     The cap is NOT here. Two CSS attempts failed: `max-height: 78vh` left a
     322px hole under the list, and `position:absolute; inset:0` measured 1029
     anyway — a stylesheet cannot name the height the blog slides happen to
     have. frontpage.js knows that number, so it writes maxHeight and overflow
     onto this slide directly (search: ONE PILLAR PER ROW). All that is left
     here is the column count.

     Scroll chaining is left at the browser default on purpose. `contain` would
     stop the page moving once the list hits its end, which is exactly what
     makes a nested scroller feel broken on a phone. */
  .fp-research-pillars {
    grid-template-columns: 1fr !important;
  }
}

/* ── H. TWO SCHOLARS AT A TIME ────────────────────────────────────────────────
   Chase, 2026-08-27: "这个改成两个人试试看". One card filled the whole width, so a
   single headshot took a phone screen on its own — 358x477 of one face, with no
   sense that it is a carousel of many people until you press an arrow.

   Two per view is what 1100-and-below already does; the phone was the only
   range that dropped to one. The rotation JS is card-count agnostic — it steps
   by `cards[0].width + gap` and moves one card from front to back — so nothing
   there changes.

   The caption has to come down with the card: at 173px wide the name had 133px
   to sit in, and .fp-scholar__name is `white-space: nowrap`, so anything it
   could not fit would have hung out of the card rather than wrapping. Longest
   in the deck is "Thomas SCHMID" at 13 characters and "HKU Business School" at
   19; both clear their line at 14/11px with room to spare. */
@media (max-width: 720px) {
  .fp-scholars__track { gap: 12px; }
  .fp-scholar { flex: 0 0 calc((100% - 12px) / 2) !important; }
  .fp-scholar__base { padding: 34px 12px 12px !important; }
  .fp-scholar__base .fp-scholar__name { font-size: 14px; }
  .fp-scholar__affil { font-size: 11px; }
}


/* ---- hero bands, narrow screens ---------------------------------------
   A diagonal band is about 70px wide on a phone — unusable, and there is no
   hover to drive it. Below 900px the cut turns horizontal and a tap plays a
   band; the JS already treats click as the toggle. */
@media (max-width: 900px) {
  /* ⚠️ SPECIFICITY, not the media query, decides this. A media query adds no
     weight, so `.fp-band .fp-band__video` (0,2,0) loses to the desktop
     `.fp-band:nth-child(1) .fp-band__video` (0,3,0) and the clips stayed
     shoved to 16% / 82% — two thirds of the top band and of the bottom band
     were black. Match the (0,3,0) and win on order instead. */
  .fp-band:nth-child(1) .fp-band__video,
  .fp-band:nth-child(2) .fp-band__video,
  .fp-band:nth-child(3) .fp-band__video {
    --x: 50%;
    width: 100%;
    height: 100%;
  }
  .fp-band:nth-child(1) { clip-path: polygon(0 0, 100% 0, 100% 33.5%, 0 39.5%); }
  .fp-band:nth-child(2) { clip-path: polygon(0 40.5%, 100% 34.5%, 100% 66.5%, 0 72.5%); }
  .fp-band:nth-child(3) { clip-path: polygon(0 73.5%, 100% 67.5%, 100% 100%, 0 100%); }
}

/* ==========================================================================
   🚨 THE CARD PICTURES KEEP THEIR HEIGHT WHEN THE WINDOW NARROWS
   Bunny and Lingzi, 14 Sep: "the height of the image size should be large
   enough no matter the browser size is large or small", against a 1200px-wide
   window, and "same as about" for the Research Blog card beside it.

   The cause was a locked ratio. .fp-highlight__media was aspect-ratio 4/3 and
   .fp-research-card__media 1/1, both in a two-column grid, so a narrower window
   took the width down and the height followed it: the Highlights picture went
   310x233 at 1440 to 249x187 at 1200 to 224x168 at 1101.

   The house pattern for this is .fp-news-card__media, which sets a flat
   height: 280px and lets only the width flex — and /upcoming-events/ does the
   same thing by stretching the picture to the row. Chase picked the Insights
   version on 16 Sep ("先试试b") over stretching, because stretching also
   enlarges the Highlights picture at 1440 (233 to 274) and that size was
   restored on Chase's own instruction on 2026-09-01. These numbers ARE the
   current 1440 heights, so the widest window is unchanged to the pixel.

   🚨 aspect-ratio has to be cleared, not overridden. With the ratio still in
   force a height drives the WIDTH off it — the 2026-09-01 attempt measured a
   588px picture inside a 310px column. The note above .fp-highlight__media
   records that; this is the safe form of the same idea.

   🚨 Desktop only. Below 1100 the layout is one column and the picture is
   ~665px wide; pinning it to 233px there would turn it into a letterbox strip.
   ========================================================================== */
@media (min-width: 1101px) {
  .fp-page .fp-highlight__media {
    aspect-ratio: auto !important;
    /* 🚨 233px flat was the first cut of this fix and it had a blind side.
       Chase, 2026-09-17: "为啥我感觉highlight的高度变小了". 233 was simply the
       height at 1440, which I took as the anchor and never looked above — so
       everything wider LOST height where the old 4:3 ratio had been giving it
       away for free. Measured against the ratio it replaced: 1920 was 324px and
       became 233 (91px gone), 1600 was 263. Below 1440 it gains, which was the
       point: 1200 was 187.

       So: grow with the window above 1440, never shrink below it. The line
       passes through 233px @ 1440 and 324px @ 1920 — the two heights the old
       ratio gave — and stops there, so an ultrawide does not get a mural. */
    height: clamp(233px, calc(18.96vw - 40px), 324px) !important;
  }
  /* 🚨 THE RESEARCH BLOG PICTURE FILLS THE PANEL. Chase, 2026-09-16: "图片的
     高度能不能高一些，我记得之前有一个版本，图片高度是占满整个内部的". It was
     301px here for a day, which fixed Bunny's shrinking but left it shorter than
     the box. The panel is already pinned to a fixed height (the tallest tab), so
     stretching the picture to it gives a CONSTANT height at every desktop width
     — which is what Bunny asked for — and fills the 115px that was being split
     above and below the card.

     The card has to stretch too: it is a grid item in the slide stack and the
     centring rule at the end of this file holds every slide at align-self:
     center. This overrides it for the article card only; the pillar slide still
     fills the box on its own. .fp-research-card__body keeps align-self: center,
     so the text stays optically centred against the taller picture. */
  .fp-page .fp-research__inner-track > .fp-research-card {
    align-self: stretch !important;
  }
  .fp-page .fp-research-card__media {
    aspect-ratio: auto !important;
    height: auto !important;
    align-self: stretch !important;
  }
}

/* ==========================================================================
   🚨 THE HIGHLIGHT TITLES DROP THEIR HAND-PLACED BREAK WHEN THE CARD STACKS
   Chase, 16 Sep, looking at "2026 Sustainability / Forum" broken across two
   lines in a window with room to spare: "这情况下就不要标题换行了".

   Five of the seven highlight titles carry a literal <br> in frontpage.html —
   "2026 Sustainability <br>Forum", "NGO Empowerment <br>Initiative" and so on.
   They were placed to shape the heading inside the NARROW right-hand column of
   the two-column desktop card, where the title really does need two lines.
   Below 1100 that card becomes one column and the body goes full width — 720px
   at 1100, 665 at 1024 — so the forced break fires in a box three times wider
   than the one it was drawn for, and the title breaks while the paragraph under
   it runs the full width.

   display:none on a <br> removes the break without touching the markup, so the
   desktop shape those breaks were written for is untouched. Scoped to the
   stacked layout only.

   ⚠️ Only .fp-highlight__title. The <br> in .fp-scholar__role separates a job
   title from an institution and is content, not shaping; the hero and the
   section labels are drawn to their breaks at every width.
   ========================================================================== */
@media (max-width: 1100px) {
  .fp-page .fp-highlight__title br { display: none !important; }
}

/* ==========================================================================
   🚨 THE CARD SITS IN THE MIDDLE OF THE PANEL, NOT AT ITS TOP
   Chase, 2026-09-16: "不是水平上的居中，而是垂直居中，你下面留那么大块的白边干嘛".

   The panel is pinned to the tallest of the four tabs — his own call on
   2026-09-14, "你为什么不能设置research blog的高度始终不变", so that switching tabs
   never moves the tab strip's footing. Research Pillar is the tallest at 472px
   and the three article tabs are 357, so those three carried 115px of white.
   The slide is a grid item in a one-area stack and was align-self: start, which
   put every one of those 115px underneath the card and none above it.

   Centring splits it, 57 above and 57 below, which reads as the panel's padding
   instead of a hole. The pillar slide is the full 472 so it is unaffected.

   ⚠️ The other way to spend the slack is the one the pillar pictures already
   use — grow the picture until the card fills the box (see fitInner() in
   frontpage.js). That removes the white entirely but makes the article
   thumbnail noticeably taller than the 301px just agreed with Bunny, so it is
   a separate decision, not a tidy-up.
   ========================================================================== */
.fp-page .fp-research__inner-track > * {
  align-self: center !important;
}

/* ==========================================================================
   🚨 THE SCHOLAR CARD IS A LINK NOW
   Bunny and Lingzi, 14 Sep: "should direct to their personal page when click".
   Each card was an <article> with cursor:pointer and nothing behind it — the
   cursor promised a destination the card did not have.

   All 22 are <a> now, pointing at the faculty page the site already publishes
   for that person: HKU Business School profiles for the HKU names, and for the
   global affiliates the same external URLs that /global-affiliates/ has been
   serving. The map was assembled from Scholars/scholars.html,
   affiliates_data.json and hku_researchers_data.json; four HKU names that were
   in none of them (Hongbin CAI, David BISHOP, Junhong CHU, Thomas SCHMID) were
   resolved by the hkubs.hku.hk/people/<first>-<last>/ pattern and each page was
   opened and checked to carry that person's name in its title.

   ⚠️ Four of the external links answer 403 to a script — Harvard and the two
   Columbia pages sit behind a bot challenge. They are the institute's own
   published links, live on /global-affiliates/ today, so a 403 from curl is not
   a dead link. Do not "fix" them by deleting them.

   The anchor reset matters: an <a> brings its own colour and underline, and the
   name and affiliation inside the card set their own colour only on some
   states. */
.fp-page a.fp-scholar,
.fp-page a.fp-scholar:hover,
.fp-page a.fp-scholar:focus {
  display: block;
  color: inherit !important;
  text-decoration: none !important;
}
.fp-page a.fp-scholar:focus-visible {
  outline: 3px solid var(--fp-gold);
  outline-offset: 2px;
}

/* ==========================================================================
   🚨 ON A PHONE THE SECTION BUTTON GOES UNDER THE CONTENT, NOT ABOVE IT
   Bunny and Lingzi, 14 Sep, with the only phone mockup in the whole report:
   "make View more button smaller and put under the content on mobile version,
   and put the topic in one line, like the mockup below. Apply to all sections,
   including other pages."

   Every one of these sections puts its label and its View More in one head
   block beside the content on desktop, and below 1100 that head simply stacks
   ABOVE the content — so the phone reads title, button, and only then the thing
   the button is about. Her mockup reads title, content, button.

   🚨 display:contents ON THE HEAD IS WHAT MAKES THIS CSS-ONLY. The button is
   nested inside the head next to the title, so `order` alone cannot lift it out
   — order only sorts siblings. Dropping the head's own box promotes the title
   and the button to children of the section, and then order can put one first
   and the other last with the content between them. No markup moves, so the
   desktop layout is untouched.

   Three shapes, same idea: .fp-side covers Insights, Scholars and Highlights;
   .fp-events and .fp-research__group have their own heads.
   ========================================================================== */
@media (max-width: 1100px) {
  .fp-page .fp-side,
  .fp-page .fp-events,
  .fp-page .fp-research__group {
    display: flex !important;
    flex-direction: column !important;
    gap: 20px !important;
  }
  /* 🚨 EXCEPT UNDER THE TABS. Chase, 2026-09-18: "现在为什么这里浮空了一段?" —
     a 20px navy strip between the tab strip and the white panel. My own doing,
     from the item-15 reorder above: turning the group into a flex column put a
     20px gap between EVERY pair of children, and one of those pairs is the tabs
     and the panel they open. A folder tab that floats above its folder stops
     reading as a tab at all — on the desktop grid they are adjacent rows with
     no gap, which is why it only showed on the phone. flex `gap` cannot be set
     per pair, so the panel pulls itself back up by exactly that 20px. */
  .fp-page .fp-research__group > .fp-research__inner {
    margin-top: -20px !important;
  }

  /* 🚨 THE CARD CENTRES IN THE PANEL ON A PHONE TOO. Chase, 2026-09-21:
     "我觉得你可以在垂直上居中", on the Most Popular tab.

     The panel is pinned by fitInner to the tallest tab, which is Research
     Pillar; the three article tabs are shorter and the leftover sat entirely
     under the card. Measured at 390px: panel 675, track 578, 0 above and 97
     below on Latest Blog, Editor's Pick and Most Popular alike.

     The desktop already does this — `.fp-page .fp-research__inner-track > *
     { align-self: center }`, from the 2026-09-16 fix to the same complaint —
     but it cannot bite here: on a phone the track's own grid row is sized to
     the active card, so the card fills its row exactly and there is nothing for
     align-self to distribute. The slack is one level up, between the track and
     the panel. So centre the track instead. The card background is absolute
     with inset 0, so it stays the full panel and is not a flex item. */
  .fp-page .fp-research__inner {
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
  }

  /* 🚨 AND THEN THE PICTURE TAKES THE SLACK INSTEAD. Chase, 2026-09-21, right
     after the centring above: "图片高度是否可以延申?". Yes — and it is what the
     desktop already does, from 2026-09-16: the research picture fills the panel
     rather than floating in it with white above and below.

     So the track fills the panel, the card fills the track, and the card's
     media row takes whatever the body does not. Measured at 390px before:
     panel 675, card 578 or 553, media locked to 237 by aspect-ratio 4/3, with
     97 or 122px of white left over.

     🚨 aspect-ratio has to be CLEARED, not overridden — the same trap as the
     desktop fix. With the ratio still in force a height drives the WIDTH off
     it; the 2026-09-01 attempt on the highlight card measured a 588px picture
     inside a 310px column.

     The centring above stays as the fallback: if the track ever fails to fill,
     the card is still centred rather than pinned to the top. */
  .fp-page .fp-research__inner-track {
    flex: 1 1 auto !important;
    grid-template-rows: 1fr !important;
  }
  .fp-page .fp-research__inner-track > .fp-research-card {
    align-self: stretch !important;
    grid-template-rows: 1fr auto !important;
  }
  .fp-page .fp-research-card__media {
    aspect-ratio: auto !important;
    height: auto !important;
    align-self: stretch !important;
  }
  .fp-page .fp-side > .fp-side__head,
  .fp-page .fp-events > .fp-events__head,
  .fp-page .fp-research__group > .fp-research__head {
    display: contents !important;
  }
  .fp-page .fp-side__head > .fp-section-title,
  .fp-page .fp-events__head > .fp-section-title,
  .fp-page .fp-research__head > .fp-section-title {
    order: 1;
  }
  /* 🚨 width:100% AND min-width:0, BOTH. These were grid items in a `1fr`
     column, which made them fill the column and shrink inside it for free. As
     flex items they size to their CONTENT instead: measured right after the
     switch, .fp-news__viewport came out 1432px wide in a 358px section — the
     card ran off both edges — while .fp-scholars__body shrank to 252 and sat
     narrower than everything above it. width pins them to the column;
     min-width:0 is what lets a flex item be narrower than its longest child,
     without which the carousel would push the section wide again. */
  .fp-page .fp-side > *:not(.fp-side__head),
  .fp-page .fp-events > *:not(.fp-events__head),
  .fp-page .fp-research__group > *:not(.fp-research__head) {
    order: 2;
    width: 100% !important;
    min-width: 0 !important;
  }
  .fp-page .fp-side__head > .fp-btn,
  .fp-page .fp-events__head > .fp-btn,
  .fp-page .fp-research__head > .fp-btn {
    order: 3;
    align-self: flex-start;
    /* "smaller": 38px is the desktop small button. A phone does not need the
       same target as a desktop hover, and this one now sits under a card rather
       than beside a heading. */
    height: 34px !important;
    min-height: 34px !important;
    max-height: 34px !important;
    padding: 0 18px !important;
    font-size: 13px !important;
    /* 🚨 34px PAINTED, 44px TAPPABLE. The comment that used to sit above these
       numbers said a phone "does not need the same target as a desktop hover",
       which is backwards — a finger needs more room than a cursor, not less.
       Bunny did ask for smaller, and against the desktop's oversized buttons she
       was right, so the button still LOOKS 34px; the hit area is grown to the
       44px floor with a pseudo-element instead. Chase, 2026-09-18, on the
       Research Blog one: it is the loneliest control on the phone page, sitting
       under a 675px card with nothing else near it to catch a near miss. */
    position: relative;
  }
  .fp-page .fp-side__head > .fp-btn::after,
  .fp-page .fp-events__head > .fp-btn::after,
  .fp-page .fp-research__head > .fp-btn::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    /* 🚨 All 10px UPWARD, not 5 above and 5 below. The button is the last child
       of .fp-research__group inside .fp-research__viewport, which clips — a
       bottom: -5px was silently cut off and a tap 4px under the button still
       missed. Measured on the live page both ways. There is a 20px gap above
       the button and nothing in it, so the whole extension goes there. */
    top: -10px;
    bottom: 0;
  }

  /* 🚨 "View More" alone is ambiguous down here. Chase, 2026-09-18: on the phone
     the button ends up 815px below its own RESEARCH BLOG heading and directly on
     top of the band's bottom padding, with a RESEARCH HEADLINE block above it in
     the same navy — so nothing on screen says what it will show more OF. The
     desktop keeps it beside the heading and needs no help, so this rides the
     same <=1100 block as the reorder that put the button down there. */
  /* 🚨 .fp-btn is a flex row with gap:10px, so this span becomes a third flex
     item and the 10px lands between "More" and "Blogs" — it read as a double
     space. Cancel the gap and spell the single space here. */
  .fp-btn__more {
    display: inline;
    margin-left: -10px;
  }
  .fp-btn__more::before { content: '\00a0'; }   /* a plain space here collapses */

  /* "put the topic in one line". The titles hard-break for the narrow desktop
     side column — APPLY THE / INSIGHTS — and that break has nothing to do with
     a phone, where the title spans the full width. Dropping it lets each one
     take the room it has: at 390 "APPLY THE INSIGHTS" and "RESEARCH BLOG" come
     back to one line, and "RESEARCH HEADLINE", which cannot fit, wraps on its
     own.
     🚨 The space lives in the MARKUP, before the <br>. display:none on a <br>
     removes the line break AND the whitespace it stood for, which is how the
     2026-08-27 pass produced "HUBFOR" in the hero. On desktop that space
     collapses at the break and nothing changes. */
  .fp-page .fp-section-title br { display: none !important; }
}

/* ==========================================================================
   🚨 ON A PHONE THE EVENT ROW'S "View more" GETS ITS OWN LINE
   Chase, 2026-09-17, on a 390px screenshot: "view more在upcoming里面不对".

   The label was added to the row on 14 Sep for Bunny ("add CTA text, before ->,
   View more") and that was a DESKTOP note, on a row 1100px wide with an `auto`
   column beside it. On a phone the same row is a two-column grid whose right
   track is a fixed 24px (below 1100) or 40px (below 720) — both sized years ago
   for a bare arrow. "View more →" needs about 100px, so it was clipped to the
   arrow's width and painted flush against the screen edge: measured 40px wide
   with its right edge at x=374, the section's own right edge.

   Stacking the row is the fix rather than hiding the label, because the label
   is the thing Bunny asked for. Four rows now — date, title, excerpt, cta — and
   the cta sits left-aligned under the excerpt where it has the full width.
   ========================================================================== */
@media (max-width: 1100px) {
  .fp-page .fp-event__link {
    grid-template-columns: 1fr !important;
    grid-template-areas:
      'date'
      'title'
      'excerpt'
      'arrow' !important;
    gap: 4px 0 !important;
  }
  .fp-page .fp-event__cta {
    justify-self: start;
    margin-top: 10px;
  }
}

/* ==========================================================================
   🚨 THE HIGHLIGHTS CARD KEEPS ONE GAP UNDER THE PICTURE, ON EVERY SLIDE.
   Bunny and Lingzi, 14 Sep, item 13: "A Space between the photo and the title,
   it should be all like voice" — two screenshots of the same phone carousel,
   slides 02/07 and 06/07, with visibly different air under the picture.

   Measured on /home-2/ at 390px, photo bottom to title top across all seven:
     50, 70, 50, 70, 50, 50, 16
   The 16 is "Voices is live!", the one she pointed at as right.

   It was never a spacing decision — it is `.fp-highlight__body { align-self:
   center }` (line 651) read in the wrong axis. On the desktop card that is two
   COLUMNS and the line centres the text beside the picture, which is what Chase
   asked for on 2026-09-16 ("我说的不是水平上的居中，而是垂直居中"). On a phone the
   same card is two ROWS, so it centres the text inside its own row instead, and
   every slide with a short excerpt gets half its leftover space parked between
   the picture and the title. The arithmetic, at 390px: row 2 runs 274.5..510.7,
   and slide 1's 190px body lands at 274.5 + (236.2-190)/2 = 297.6 — the 297 that
   was measured. Voices fills its row, has no leftover, and sits at the bare 16.

   So `align-self: start` below 1100 and the gap is the row-gap on every slide.
   `auto 1fr` goes with it so the slack collects in the body's row rather than
   growing the picture's. The desktop rule is untouched.
   ========================================================================== */
@media (max-width: 1100px) {
  .fp-page .fp-highlight {
    position: relative;
    grid-template-rows: auto 1fr !important;
  }
  .fp-page .fp-highlight__body {
    align-self: start !important;
  }
}

@media (max-width: 767px) {
  .fp-page .fp-highlight { grid-template-rows: auto auto 1fr !important; }
  .fp-page .fp-highlight > .fp-highlights__head { padding: 0 !important; }
}

/* ── Whole words, at every width (site audit, 2026-09-23) ─────────────────────
   A word-by-word check at every 20px from 320 to 1940 found five places
   where a word was split across two lines — the text boxes carry
   `overflow-wrap: break-word`, so a word wider than its box breaks inside
   itself rather than overflowing:

     Participants / Researchers   Global Impact labels, 740-800
     Research                     the Research Pillar tab, 320-340
     MICHAELY, Management         a scholar card's name and role, 320

   The labels are a fixed 24px in a cell that is (grid - 3 x 24px) / 4; at a
   640px grid that cell is 142px and "Researchers" needs 156. Sized off the
   cell the way .fp-stat__num already is: the longest label is 6.5 x the
   font-size, so cell / 6.9 always fits, capped at the old 24px. */
@supports (container-type: inline-size) {
  @media (min-width: 721px) {
    .fp-stat__label { font-size: min(24px, calc((100cqw - 72px) / 27.6)); }
  }
}
@media (max-width: 360px) {
  .fp-page button.fp-tab { padding-left: 3px !important; padding-right: 3px !important; letter-spacing: 0 !important; font-size: 11.5px !important; }
  .fp-scholar__name { font-size: 13px !important; }
  .fp-scholar__role { font-size: 11.5px !important; }
}

/* ═════════════════════════════════════════════════════════════════════════════
   RESEARCH BLOG, BUNNY'S 2026-09-24 MOCKUP ("website revamp", four screenshots)

   Blog tabs: two posts side by side in the white card, a hairline between —
   wide banner picture, the title, the authors as round portraits with the name
   under each, Read More (ink) and Podcast (navy, waveform). No excerpt. On a
   phone the two stack under a horizontal rule and the pictures go.
   Only Most Popular uses it now: Latest Blog and Editor's Pick went back to
   the pre-mockup card with portraits for the byline (Bunny, same day).

   Research Pillar: six picture tiles with the title laid over a dark wash —
   3 x 2 on desktop, 2 x 3 on a phone. The topic lists are not in the mockup.

   Sizes are the mockup's own, scaled by its card (1090 wide) to ours (~850 at
   1440): title 24px / 3 lines, portraits 46px, names 10.5px, banner 3:1.
   Markup: .fp-rb-slide > .fp-rb2 > .fp-rb2__post (frontpage.html, rebuilt by
   hydrateResearch() in frontpage.js).
   ═════════════════════════════════════════════════════════════════════════════ */
.fp-rb-slide {
  padding: 34px 36px 36px;
  box-sizing: border-box;
}
.fp-rb2 {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: 0;
}
.fp-rb2__post {
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 0 32px 0 0;
}
.fp-rb2__post + .fp-rb2__post {
  padding: 0 0 0 32px;
  border-left: 1px solid rgba(20, 23, 31, .35);
}
.fp-rb2__media {
  display: block;
  aspect-ratio: 3 / 1;
  overflow: hidden;
  background: #E9ECF1;
  margin-bottom: 20px;
}
.fp-rb2__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .6s var(--fp-ease);
}
.fp-rb2__post:hover .fp-rb2__media img { transform: scale(1.04); }
.fp-page .fp-rb2__title {
  font-family: "Poppins", "Noto Sans", sans-serif !important;
  font-size: clamp(18px, 1.7vw, 24px) !important;
  font-weight: 600 !important;
  line-height: 1.22 !important;
  letter-spacing: -0.005em;
  margin: 0 0 18px !important;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  min-height: 3.66em;           /* three lines, so the two posts' portraits line up */
}
.fp-page .fp-rb2__title a {
  color: #000 !important;
  text-decoration: none !important;
  transition: color .2s ease;
}
.fp-page .fp-rb2__title a:hover { color: var(--fp-gold) !important; }
.fp-page ul.fp-rb2__authors {
  list-style: none !important;
  margin: 0 0 20px !important;
  padding: 0 !important;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 8px;
}
.fp-rb2__author {
  width: 58px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  margin: 0 !important;
}
.fp-rb2__face {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  flex-shrink: 0;
}
.fp-rb2__face--initials {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #1B3F86;
  color: #fff;
  font-family: "Poppins", "Noto Sans", sans-serif;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: .02em;
}
.fp-rb2__name {
  margin-top: 5px;
  font-size: 10.5px;
  line-height: 1.25;
  color: #8A8F98;
}
.fp-rb2__actions {
  margin-top: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.fp-page .fp-rb2__actions .fp-btn {
  height: 38px !important;
  min-height: 38px !important;
  padding: 0 16px !important;
  font-size: 14px !important;
  gap: 8px;
  border-radius: 0 !important;
}
.fp-rb2__read { background: #000 !important; }
.fp-rb2__wave {
  width: 20px;
  height: 16px;
  flex-shrink: 0;
}

/* Latest Blog and Editor's Pick are NOT .fp-rb2 cards: Bunny, 2026-09-24,
   "keep these 2 unchanged and just change their author part". They are the
   pre-mockup .fp-research-card (styled far above), and only their author line
   is the portrait row — which sits in that card's body, here. */
.fp-page .fp-research-card__authors ul.fp-rb2__authors { margin: 4px 0 14px !important; }
.fp-research-card__authors .fp-rb2__name { font-style: normal; }

/* ---- Research Pillar: tiles with the title on the picture ---------------- */
.fp-page .fp-research-pillars {
  padding: 34px 40px 36px !important;
  grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
  gap: 18px !important;
  align-content: start;
}
.fp-page .fp-research-pillars .fp-pillar {
  position: relative !important;
  display: block !important;
  aspect-ratio: 16 / 10;
  border-radius: 0 !important;
  box-shadow: none !important;
  background: #14171F !important;
  overflow: hidden;
  transform: none !important;
}
.fp-page .fp-research-pillars .fp-pillar__img-wrap {
  position: absolute !important;
  inset: 0 !important;
  height: auto !important;
}
.fp-page .fp-research-pillars .fp-pillar__img-wrap::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(14, 22, 38, .52);
  transition: background-color .3s ease;
}
.fp-page .fp-research-pillars .fp-pillar:hover .fp-pillar__img-wrap::after { background: rgba(14, 22, 38, .38); }
.fp-page .fp-research-pillars .fp-pillar__body {
  position: absolute !important;
  inset: 0 !important;
  z-index: 1;
  display: flex !important;
  flex-direction: column !important;    /* the base rule's; centring is on the main axis */
  justify-content: center !important;
  align-items: flex-start !important;
  padding: 0 12% !important;
  background: transparent !important;
}
.fp-page .fp-research-pillars .fp-pillar__title {
  color: #fff !important;
  font-size: clamp(14px, 1.25vw, 18px) !important;
  font-weight: 600 !important;
  line-height: 1.25 !important;
  margin: 0 !important;
  text-shadow: 0 1px 8px rgba(0, 0, 0, .35);
}
.fp-page .fp-research-pillars .fp-pillar__topics { display: none !important; }

/* ---- 721-1100: still two across, tighter ----------------------------------- */
@media (max-width: 1100px) {
  .fp-rb-slide,
  .fp-page .fp-research-pillars { padding: 26px 26px 28px !important; }
  .fp-rb2__post { padding-right: 26px; }
  .fp-rb2__post + .fp-rb2__post { padding-left: 26px; }
}

/* ---- Phones: the two posts stack, no pictures; pillars 2 x 3 ---------------- */
@media (max-width: 720px) {
  .fp-rb-slide { padding: 26px 22px 28px !important; }
  .fp-rb2 { display: block; }
  .fp-rb2__post,
  .fp-rb2__post + .fp-rb2__post { padding: 0; border-left: 0; }
  .fp-rb2__post + .fp-rb2__post {
    margin-top: 24px;
    padding-top: 24px;
    border-top: 1px solid rgba(20, 23, 31, .35);
  }
  .fp-rb2__media { display: none !important; }
  .fp-page .fp-rb2__title {
    font-size: 21px !important;
    min-height: 0;
    margin-bottom: 16px !important;
  }
  .fp-page ul.fp-rb2__authors { gap: 8px 10px; margin-bottom: 18px !important; }
  .fp-rb2__author { width: 58px; }
  .fp-rb2__face { width: 44px; height: 44px; }
  .fp-rb2__face--initials { font-size: 14px; }
  .fp-rb2__name { font-size: 10px; }
  .fp-page .fp-research-pillars {
    padding: 22px !important;
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: 12px !important;
    max-height: none !important;
    overflow: visible !important;
  }
  /* near-square at 390 as in the mockup (161 x 148), but the height is capped
     rather than tied to the width: at 480 a 1:1.1 tile made the pillar tab
     712px tall against the blog tabs' 497 */
  .fp-page .fp-research-pillars .fp-pillar { aspect-ratio: auto; height: clamp(120px, 38vw, 160px); }
  .fp-page .fp-research-pillars .fp-pillar__body { padding: 0 11% !important; }
  .fp-page .fp-research-pillars .fp-pillar__title { font-size: 13.5px !important; }
}
/* 481-720: two tall tiles across a 690px card are 330 x 363 each and made the
   panel 1108px high at 720. Three across at the desktop's 16:10 until the card
   is phone-narrow; the mockup's 2 x 3 is a phone layout. */
@media (min-width: 481px) and (max-width: 720px) {
  .fp-page .fp-research-pillars { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; }
  .fp-page .fp-research-pillars .fp-pillar { aspect-ratio: 16 / 10; }
  .fp-page .fp-research-pillars .fp-pillar__body { padding: 0 9% !important; }
  .fp-page .fp-research-pillars .fp-pillar__title { font-size: 13.5px !important; }
}

/* ── Phones, to Bunny's mobile mockups (2026-09-24) ────────────────────────────
   Chase: "你手机版的似乎有点问题啊，已经有了mockup了，前两个不用咋改，后面的照着
   mockup做就行". Measured off her two phone screenshots, scaled to a 390 screen:
     tab strip    one line, ~11px, regular weight — ours was two lines of 12.5 bold
     Most Popular title 24px (ours 21), portraits 38 (44), names 9, buttons 32
                  high at 12px (38 / 14), 18px inside the card
     pillars      tiles 158 x 141, 9px apart, 16px inside the card, 12.5px titles
   Latest Blog and Editor's Pick (the single-post cards) are left as they were. */
@media (max-width: 720px) {
  .fp-page .fp-tab,
  .fp-page button.fp-tab {
    flex: 1 1 auto !important;
    white-space: nowrap !important;
    font-size: 11px !important;
    font-weight: 500 !important;
    letter-spacing: 0 !important;
    line-height: 1.2 !important;
    padding: 11px 6px !important;
  }
  .fp-page .fp-tab.is-active,
  .fp-page button.fp-tab.is-active { font-weight: 600 !important; }

  .fp-rb-slide:has(> .fp-rb2:not(.fp-rb2--single)) { padding: 22px 18px 24px !important; }
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__title { font-size: 24px !important; line-height: 1.2 !important; margin-bottom: 14px !important; }
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__author { width: 52px; }
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__face { width: 38px; height: 38px; }
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__face--initials { font-size: 12px; }
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__name { font-size: 9px; }
  .fp-page .fp-rb2:not(.fp-rb2--single) ul.fp-rb2__authors { margin-bottom: 16px !important; }
  .fp-page .fp-rb2:not(.fp-rb2--single) .fp-rb2__actions .fp-btn {
    height: 32px !important;
    min-height: 32px !important;
    padding: 0 14px !important;
    font-size: 12px !important;
  }
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__wave { width: 16px; height: 13px; }
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__post + .fp-rb2__post { margin-top: 20px; padding-top: 20px; }

}
@media (max-width: 480px) {   /* the 2 x 3 phone grid; 481-720 keeps three across */
  .fp-page .fp-research-pillars { padding: 16px !important; gap: 9px !important; }
  .fp-page .fp-research-pillars .fp-pillar { height: auto !important; aspect-ratio: 158 / 141; }
  .fp-page .fp-research-pillars .fp-pillar__body { padding: 0 11% !important; }
  .fp-page .fp-research-pillars .fp-pillar__title { font-size: 12.5px !important; line-height: 1.25 !important; }
}
/* the narrowest phones: the four tabs need ~308px at 11px and a 320 screen
   gives them 288 */
@media (max-width: 374px) {
  .fp-page .fp-tab,
  .fp-page button.fp-tab { font-size: 10px !important; padding: 10px 3px !important; }
}

/* ==========================================================================
   Research Blog authors who are affiliates                    (2026-09-30)
   ==========================================================================
   PersonCard/esgri-person.js marks an affiliate's portrait+name item with
   .esg-person; the whole item opens their card. Chase: bolder name, no dotted
   underline (the card's default marker elsewhere), and the face clickable too. */
.fp-page .fp-rb2__author.esg-person { border-bottom: 0 !important; cursor: pointer; }
/* Chase, 2026-10-02: not bold — first an underline ("限制改成下划线吧"), then
   the card's original fine gold dots ("改成以前那样的细点"), on the name only */
.fp-page .fp-rb2__author.esg-person .fp-rb2__name {
  /* as a dotted text-decoration, not the original border-bottom: the name is
     a block that wraps ("Xinming / Du"), and a border ran the block's full
     width under the last line only */
  /* 2026-10-02, last word: a thin solid line in the same gold ("还是改成细线") */
  text-decoration: underline solid rgba(184, 150, 66, .7) !important;
  text-decoration-thickness: 1px; text-underline-offset: 3px;
  transition: color .15s ease, text-decoration-color .15s ease;
}
.fp-page .fp-rb2__author.esg-person:hover .fp-rb2__name { text-decoration-color: #B89642 !important; }
.fp-page .fp-rb2__author.esg-person:hover .fp-rb2__name,
.fp-page .fp-rb2__author.esg-person:focus-visible .fp-rb2__name { color: #B89642 !important; }
.fp-page .fp-rb2__author.esg-person:focus-visible { outline: 2px solid #B89642; outline-offset: 2px; }

/* ==========================================================================
   Phones: Most Popular and Research Pillar breathe a little      (2026-10-01)
   ==========================================================================
   Chase: "这个可以稍微松散一些" (both tabs, at 415px). The panel's height is
   set by Latest / Editor's Pick (597px there); Most Popular's content was 440
   and the pillar grid 505, so both sat packed in the middle of a taller box.
   More air between the parts, slightly bigger faces and tiles — still inside
   the same box, so the section does not grow. */
@media (max-width: 480px) {
  .fp-page .fp-rb2:not(.fp-rb2--single) .fp-rb2__title { margin-bottom: 20px !important; }
  .fp-page .fp-rb2:not(.fp-rb2--single) ul.fp-rb2__authors { gap: 10px 14px !important; margin-bottom: 24px !important; }
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__author { width: 56px; }
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__face { width: 42px; height: 42px; }
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__name { font-size: 9.5px; margin-top: 2px; }
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__post + .fp-rb2__post { margin-top: 32px; padding-top: 32px; }

  .fp-page .fp-research-pillars { padding: 20px !important; gap: 14px !important; }
  .fp-page .fp-research-pillars .fp-pillar { aspect-ratio: 158 / 150; }
}

/* ==========================================================================
   Phones: section titles sit LEFT, under the same edge as their buttons
   ==========================================================================
   Chase, 2026-10-01: HIGHLIGHTS, APPLY THE INSIGHTS, UPCOMING EVENTS and
   SCHOLARS were centred on mobile. Their wrapper is display:contents there, so
   the title is a flex item of a section with align-items:center; the button
   beside it already had align-self:flex-start, the title did not. The
   breakpoint is the display:contents block's own (1100px). */
@media (max-width: 1100px) {
  .fp-page .fp-side__head > .fp-section-title,
  .fp-page .fp-events__head > .fp-section-title,
  .fp-page .fp-research__head > .fp-section-title {
    align-self: flex-start !important;
    text-align: left !important;
  }
}

/* ==========================================================================
   481-720: pillar tiles fit their column again                   (2026-10-02)
   ==========================================================================
   Chase's screenshot (~517px): the six tiles overlapped and ran off the card.
   The <=720 rule fixes each tile's HEIGHT (clamp to 160px) and the 481-720
   rule gives it aspect-ratio 16/10 — with a fixed height the ratio computes
   the WIDTH, 160 x 1.6 = 256px, in a column only 126-206px wide. Here the
   height must come from the width instead. */
@media (min-width: 481px) and (max-width: 720px) {
  /* and two across, as on phones: three across left 126px tiles at 481 with
     four-line titles in them and the panel half empty */
  .fp-page .fp-research-pillars {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: 14px !important;
    padding: 22px !important;
  }
  .fp-page .fp-research-pillars .fp-pillar {
    height: auto !important;
    width: 100% !important;
    min-width: 0 !important;
    aspect-ratio: 16 / 10 !important;
  }
  .fp-page .fp-research-pillars .fp-pillar__body { padding: 0 9% !important; }
  .fp-page .fp-research-pillars .fp-pillar__title { font-size: 14px !important; line-height: 1.3 !important; }
}

/* ==========================================================================
   Tablets (721-1100px): Most Popular and Research Pillar fill their panel
   ==========================================================================
   Chase, 2026-10-02, on an iPad mini (768): "布局似乎太松散了". The panel is
   682px tall there (set by Latest / Editor's Pick), but Most Popular filled
   416px — its pictures keep the desktop's 3:1 and were 80px strips in a 240px
   column — and the pillar grid 265px: six 154 x 96 tiles three across.
   Pictures go to 4:3 and the pillars two across at 3:2, as on phones but larger. */
@media (min-width: 721px) and (max-width: 1100px) {
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__media { aspect-ratio: 4 / 3 !important; margin-bottom: 22px !important; }
  .fp-page .fp-rb2:not(.fp-rb2--single) .fp-rb2__title { margin-bottom: 18px !important; }
  .fp-page .fp-rb2:not(.fp-rb2--single) ul.fp-rb2__authors { margin-bottom: 22px !important; }

  .fp-page .fp-research-pillars {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: 16px !important;
    padding: 28px !important;
  }
  .fp-page .fp-research-pillars .fp-pillar {
    height: auto !important;
    width: 100% !important;
    min-width: 0 !important;
    aspect-ratio: 3 / 2 !important;
  }
  .fp-page .fp-research-pillars .fp-pillar__body { padding: 0 9% !important; }
  .fp-page .fp-research-pillars .fp-pillar__title { font-size: 15px !important; line-height: 1.3 !important; }
}

/* ==========================================================================
   Research Blog: one button pair in every tab                  (2026-10-02)
   ==========================================================================
   Chase: "这里的按钮要统一一下，mobile上大小尺寸还有点不一样". Latest Blog and
   Editor's Pick now carry Most Popular's classes (frontpage.js unifyActions),
   and this sets one size for both kinds of card: 38px on a desktop, 34px on a
   phone. Before it, a phone showed Most Popular at 32px / 12px beside the
   other two tabs at 38px / 14px. */
.fp-page .fp-research-card__actions,
.fp-page .fp-rb2__actions { gap: 10px !important; }
.fp-page .fp-research-card__actions .fp-btn,
.fp-page .fp-research-card__actions a.fp-btn,
.fp-page .fp-research-card__actions button.fp-btn,
.fp-page .fp-rb2 .fp-rb2__actions .fp-btn,
.fp-page .fp-rb2:not(.fp-rb2--single) .fp-rb2__actions .fp-btn {
  height: 38px !important; min-height: 38px !important; max-height: 38px !important;
  padding: 0 16px !important; font-size: 14px !important; gap: 8px; border-radius: 0 !important;
}
.fp-research-card__actions .fp-rb2__wave,
.fp-rb2 .fp-rb2__wave,
.fp-rb2:not(.fp-rb2--single) .fp-rb2__wave { width: 20px; height: 16px; }
@media (max-width: 720px) {
  .fp-page .fp-research-card__actions .fp-btn,
  .fp-page .fp-research-card__actions a.fp-btn,
  .fp-page .fp-research-card__actions button.fp-btn,
  .fp-page .fp-rb2 .fp-rb2__actions .fp-btn,
  .fp-page .fp-rb2:not(.fp-rb2--single) .fp-rb2__actions .fp-btn {
    height: 34px !important; min-height: 34px !important; max-height: 34px !important;
    padding: 0 14px !important; font-size: 13px !important;
  }
  .fp-research-card__actions .fp-rb2__wave,
  .fp-rb2 .fp-rb2__wave,
  .fp-rb2:not(.fp-rb2--single) .fp-rb2__wave { width: 17px; height: 14px; }
}

/* ==========================================================================
   Scholars: the three affiliate buttons outlined                (2026-10-02)
   ==========================================================================
   Chase: "这里改成黑框白底黑字，mobile也是" — black border, white ground, black
   text, at every width. Hover keeps the page's gold (border with it). */
.fp-page .fp-scholars__pills .fp-pill,
.fp-page .fp-scholars__pills a.fp-pill {
  background-color: #fff !important;
  color: #14171F !important;
  border: 1px solid #14171F !important;
}
.fp-page .fp-scholars__pills .fp-pill:hover,
.fp-page .fp-scholars__pills a.fp-pill:hover,
.fp-page .fp-scholars__pills a.fp-pill:focus-visible {
  background-color: var(--fp-gold) !important;
  border-color: var(--fp-gold) !important;
  color: #14171F !important;
}

/* Apply the Insights on a phone: a taller picture (Chase, 2026-10-02: "这里高度
   也稍微给多一些吧"). 200px → 4:3 of the card (268px at 390), the shape the
   other phone carousels' pictures read at. */
@media (max-width: 720px) {
  .fp-news-card__media { height: min(calc((100vw - 32px) * .75), 420px) !important; }
  /* the arrows on the picture's midline, as Research Headline's are: the
     picture is the card's last 1px-inset band, so anchor from the foot */
  .fp-page .fp-news button.fp-news__arrow {
    top: auto !important;
    bottom: calc(min(calc((100vw - 32px) * .75), 420px) / 2 - 20px) !important;
    transform: none !important;
  }
}

/* ==========================================================================
   One measure with the rest of the site                       (2026-10-02)
   ==========================================================================
   The page's sides were 14vw (a 72vw body). Chase chose the hybrid for the
   whole site, front page included: max(72vw, min(1200px, 100vw - 70px)) — a
   1200px box on laptops, 72% on big screens — so this page's content now sits
   on the same edges as the new site header above it and every other page.
   Unchanged at >=1667px (72vw is the larger there); wider below that (1200
   instead of 1037 at 1440). Phones keep their 16px (the 720px rule above). */
@media (min-width: 721px) {
  .fp-page {
    --fp-page-pad: calc((100vw - var(--esgri-measure, max(72vw, min(1200px, calc(100vw - 70px))))) / 2);
  }
}
/* …and the carousel arrows. From 721 to 1100 the title stops being a column
   and the arrows hung 54px (Headline: 60px) out into the page's side padding —
   which was 14vw (154px at 1100) and is now the measure's 35px, so they were
   cut off at the window's edge. They take whatever that padding allows, less
   4px of air, and overlap the cards by the rest. */
@media (min-width: 721px) and (max-width: 1100px) {
  .fp-scholars__viewport,
  .fp-news__viewport { --fp-carousel-arrow-out: min(54px, calc(var(--fp-page-pad) - 4px)) !important; }
  .fp-page .fp-headline .fp-headline__arrow--prev { margin-left: calc(min(60px, var(--fp-page-pad) - 4px) * -1) !important; }
}
/* Headline's NEXT arrow hangs into the right-hand page padding at every width
   with a title column too, so it needs the same cap until that padding is
   64px again (1340) */
@media (min-width: 721px) and (max-width: 1340px) {
  .fp-page .fp-headline .fp-headline__arrow--next { margin-right: calc(min(60px, var(--fp-page-pad) - 4px) * -1) !important; }
}

/* Homepage Voices player: portrait video, matching Voices' on-page lightbox. */
.fp-voice-dialog {
  position: fixed !important;
  inset: 0 !important;
  margin: auto !important;
  height: fit-content !important;
  z-index: 2147483647 !important;
  padding: 36px 16px 16px !important;
  border: 0 !important;
  background: #111 !important;
  color: #fff !important;
  width: min(440px, calc(100vw - 32px));
  max-height: calc(100dvh - 32px);
  border-radius: 8px;
}
.fp-voice-dialog::backdrop { background: rgba(0,0,0,.85); }
.fp-voice-dialog video { display: block; width: 100%; max-height: calc(100dvh - 150px); object-fit: contain; background: #000; }
.fp-voice-dialog p { color: #fff !important; margin: 12px 0 0 !important; font-size: 14px; }
.fp-voice-dialog__close { position: absolute; right: 8px; top: 4px; width: 32px; height: 32px; border: 0; background: transparent; color: #fff; font-size: 28px; cursor: pointer; }

/* 2026-10-09: the image occupies the left column from the top of the card;
   controls and copy share the right column. Clear the old fixed image height
   before stretching, so the image cannot expand the grid column width. */
@media (min-width: 768px) {
  .fp-page .fp-highlights__viewport { position: relative; }
  .fp-page .fp-highlights__head {
    position: absolute; top: 28px; right: 36px; z-index: 2;
    width: calc((100% - 76px) / 1.95);
    padding: 0 !important;
  }
  .fp-page .fp-highlights__nav { width: 100%; min-width: 0; }
  .fp-page .fp-highlights__progress { flex: 1; width: auto; min-width: 20px; }
  .fp-page .fp-highlights__nav .fp-arrow { flex-shrink: 0; }
  .fp-page .fp-highlight {
    grid-template-columns: minmax(0, .95fr) minmax(0, 1fr) !important;
    position: relative;
    grid-template-rows: 1fr !important;
    padding: 0 36px 0 0 !important;
    align-items: stretch !important;
  }
  .fp-page .fp-highlight__media {
    position: relative; aspect-ratio: auto !important;
    height: auto !important; min-height: 300px; align-self: stretch;
  }
  .fp-page .fp-highlight__media img {
    position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  }
  .fp-page .fp-highlight__body { padding: 104px 0 36px; align-self: center !important; min-width: 0; }
}

/* Home KV promoted from kv-test-1, with half-strength black overlays. */
.fp-hero--kvwind .fp-band__tint { background: rgba(6,9,13,.21); }
.fp-hero--kvwind .fp-band.is-live .fp-band__tint { background: rgba(6,9,13,.11); }
.fp-hero--kvwind .fp-hero__overlay {
  background: linear-gradient(180deg,rgba(0,0,0,.19) 0%,rgba(0,0,0,.11) 35%,rgba(0,0,0,.26) 100%);
}
@media (max-width: 900px) {
  .fp-hero--kvwind .fp-band:nth-child(1) { clip-path: polygon(0 0,100% 0,100% 28.6%,0 34.6%) !important; }
  .fp-hero--kvwind .fp-band:nth-child(2) { clip-path: polygon(0 35.2%,100% 29.2%,100% 60.3%,0 66.3%) !important; }
  .fp-hero--kvwind .fp-band:nth-child(3) { clip-path: polygon(0 66.9%,100% 60.9%,100% 100%,0 100%) !important; }
  .fp-hero--kvwind .fp-band:nth-child(1) .fp-band__video,
  .fp-hero--kvwind .fp-band:nth-child(1).is-live .fp-band__video { transform: translateX(-50%) translateY(-34%) !important; }
  .fp-hero--kvwind .fp-band:nth-child(3) .fp-band__video,
  .fp-hero--kvwind .fp-band:nth-child(3).is-live .fp-band__video { transform: translateX(-50%) translateY(27%) !important; }
  .fp-hero--kvwind .fp-hero__subtitle { text-shadow: 0 1px 12px rgba(0,0,0,.6),0 0 2px rgba(0,0,0,.35); }
  .fp-hero--kvwind .fp-hero__cta .fp-btn--ghost { text-shadow: 0 1px 10px rgba(0,0,0,.6); }
}

/* Phone Insights hierarchy: article title above the quieter date. */
@media (max-width: 767px) {
  .fp-page .fp-news-card__title { font-size: 20px !important; line-height: 1.4 !important; }
  .fp-page .fp-news-card__date { font-size: 14px !important; line-height: 1.5 !important; }
}
