/* ============================================================
   YU LAB · DESIGN SYSTEM
   Colour tokens are named by USE, not by hue.
   ------------------------------------------------------------
   CONTRAST (measured, WCAG relative luminance)
     #94070A on rgb(var(--ground-2-rgb)) = 2.22:1   → fills & strokes ONLY
     #D13A35 on rgb(var(--ground-2-rgb)) = 4.24:1   → large text, labels, accents
     rgb(var(--ink-alt-rgb)) on rgb(var(--ground-2-rgb)) = high     → body text on dark
   Therefore --pku-red-fill is NEVER a text colour on dark.
   ============================================================ */

:root{
  /* ==========================================================================
     THEME LAYER
     The palette was written as literal colours in 522 places. Those 522 turned
     out to be only 76 base RGB triples wearing 319 different alphas, and the
     triples fall into five semantic families. Each family gets one token
     holding a bare "R,G,B" triple, so every existing rgba(...) keeps its own
     alpha untouched and only the base flips with the theme.

     Dark is the default and is declared here on :root, so a page with no
     data-theme attribute — or one whose JS never ran — renders exactly as it
     did before this layer existed.
     ========================================================================== */
  --ink-rgb:      240,238,233;   /* type and hairlines that sit ON the ground */
  --ink-alt-rgb:  242,239,232;   /* the warm-ivory variant of the same role   */
  --ground-rgb:     7,7,7;       /* the page ground itself                    */
  --ground-2-rgb:   5,5,5;       /* the deeper ground used behind media       */
  --ground-3-rgb:  10,9,9;       /* raised panels on the ground               */
  --ground-4-rgb:  12,10,10;
  --shadow-rgb:     0,0,0;       /* cast shadows: black in both themes        */
  /* The pale reds were picked to glow against black. On a warm light ground
     they drop to ~2.1:1 — visible, but not readable. One token, deepened on
     light, keeps the same role at a legible weight. */
  --accent-red:   #94070A;
  /* Plates that stay dark in BOTH themes: the red gradient panels. They were
     built from --carbon-black, which now flips, so on light their dark end
     turned pale and the light type pinned onto them vanished. A fixed value
     is the honest description of what these actually are. */
  --plate-dark:   #14100F;
  --shadow-k:       1;           /* …but scaled down on light, see below      */
}

/* ==========================================================================
   LIGHT THEME
   Only the family bases flip. Every alpha authored across the stylesheets is
   preserved exactly, so the whole tonal structure — which hairline is fainter
   than which, how far a hover lifts — survives the switch instead of being
   re-guessed 522 times.

   The light ground is --warm-ivory, the value this design system already used
   for its light surfaces, rather than pure white: it keeps the editorial
   warmth the dark side has, and stops the red from screaming.
   ========================================================================== */
:root[data-theme="light"]{
  /* Sampled from pku.edu.cn: its ground is rgb(239,235,234) and its red is
     #94070A — the same red this site already uses. The ivory here had been
     twice as warm as the University's (R-B of 10 against 5), which is what
     made it read as literary rather than as a research institution. These
     match the source, and the type is neutralised to sit with it. */
  --ink-rgb:      26,26,28;      /* was the near-white type; now near-black   */
  --ink-alt-rgb:  38,38,40;
  --ground-rgb:   239,235,234;   /* pku.edu.cn                                */
  --ground-2-rgb: 228,223,222;   /* recessed: darker than the ground          */
  --ground-3-rgb: 248,246,245;   /* raised: lighter than the ground           */
  --ground-4-rgb: 252,251,250;
  /* Shadows tuned against black read as grime on a warm light ground. The
     geometry stays; only the density comes down. */
  --shadow-k:     .38;
  --accent-red:   #94070A;

  /* The base palette is named after colours, not roles, but each name is used
     for exactly one role — verified: --carbon-black is a background 16 times
     and a text colour never; --ink-soft is a text colour 27 times and a
     background never. So the whole palette inverts together, and every
     ground/type pairing the design already had stays legible by construction.
     Islands that were light-on-dark become dark-on-light, and vice versa. */
  --carbon-black:  #EFEBEA;   /* deep ground        → paper            */
  --warm-black:    #E7E3E2;
  --warm-black-2:  #DAD5D4;
  --warm-ivory:    #1A1A1C;   /* type sitting on the ground → near-black */
  --paper-white:   #1E1E20;   /* inverted island background            */
  --ink:           #EFEBEA;   /* type on an inverted island            */
  --ink-soft:      #A6A3A2;
  --border-grey:   #3A3739;
  --line:          rgba(26,26,28,.15);
  --line-dark:     rgba(26,26,28,.13);
}

:root{
  /* brand — fill / stroke only */
  --pku-red-fill:  #94070A;
  --deep-red:      #2A0507;
  /* the only accent text colour usable on dark */
  --signal-red:    #94070A;
  /* dark ground */
  --carbon-black:  #050505;
  --warm-black:    #0D0909;
  --warm-black-2:  #171111;
  /* light ground */
  --warm-ivory:    #F2EFE8;
  --paper-white:   #FAF9F5;
  --mineral-grey:  #8D8984;
  --border-grey:   #D8D2C8;
  --ink:           #14100F;
  --ink-soft:      #5C5651;

  --line:          rgba(20,16,15,.13);
  --line-dark:     rgba(242,239,232,.15);
  --ok:            #94070A;
  --warn:          #8A5606;

  /* ---- type: English is the default and never falls through to a CJK face ---- */
  --sans:  -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Inter", "Segoe UI", Arial, sans-serif;
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --mono: "SF Mono", ui-monospace, "JetBrains Mono", Menlo, Consolas, monospace;
  /* ---- Chinese: only reached in zh mode or on an explicit .zh element ---- */
  --cjk:       "PingFang SC", "Hiragino Sans GB", "Source Han Sans SC", "Microsoft YaHei", sans-serif;
  --cjk-serif: "Songti SC", "Source Han Serif SC", "Noto Serif CJK SC", "STSong", serif;

  --gut: clamp(20px, 5vw, 96px);
  --maxw: 1440px;
  --measure: 72ch;          /* 680–760px body column */
  --sec-y: clamp(72px, 8.6vw, 150px);
  --ease: cubic-bezier(.22,.61,.36,1);
  --ease-out: cubic-bezier(.16,1,.3,1);
  --ease-soft: cubic-bezier(.33,1,.68,1);
  --nav-h: 70px;

  /* ---- radius scale ----
     Radii were ad hoc: 2, 3, 4, 5 and 6px scattered across components, which
     at those sizes just reads as "square with the corner shaved". One scale,
     with enough radius to actually register. */
  --r-xs: 7px;    /* chips, badges, tags        */
  --r-sm: 12px;   /* cells, list rows, buttons  */
  --r-md: 18px;   /* panels, cards              */
  --r-lg: 24px;   /* feature cards, rails       */

  /* ---- surface ----
     Glass without a frame. A drawn border is what made these read as boxes,
     so the edge is now carried the way real glass carries it: a bright lip
     along the top from the light above, and a shadow underneath that lifts
     the card off the ground. Everything derives from currentColor, so one
     definition serves both the light and the dark pages.
     Three dials, if this ever needs retuning: fill, blur, lip. */
  --surface-fill: color-mix(in srgb, currentColor 7%, transparent);
  --surface-fill-hover: color-mix(in srgb, currentColor 11%, transparent);
  --surface-blur: 22px;
  --surface-lip: color-mix(in srgb, currentColor 15%, transparent);
  --surface-lip-hover: color-mix(in srgb, currentColor 26%, transparent);
  --surface-shadow:
    inset 0 1px 0 var(--surface-lip),
    0 16px 38px -20px rgba(var(--shadow-rgb),calc(.62 * var(--shadow-k)));
  --surface-shadow-hover:
    inset 0 1px 0 var(--surface-lip-hover),
    0 22px 52px -20px rgba(var(--shadow-rgb),calc(.72 * var(--shadow-k)));
  /* kept so any rule still asking for a line gets nothing visible */
  --surface-line: transparent;
  --surface-line-hover: transparent;

  /* ---- one motion scale for the whole site ---- */
  --rv-dist: 30px;          /* how far a revealing block travels   */
  --rv-blur: 4px;           /* how soft it starts — kept low, a big
                               blurred layer is expensive to compose  */
  --rv-dur: .95s;           /* opacity / transform duration        */
  --rv-step: 72ms;          /* stagger between siblings            */
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:calc(var(--nav-h) + 16px)}
body{margin:0;background:var(--paper-white);color:var(--ink);font-family:var(--sans);font-size:16.5px;line-height:1.62;-webkit-font-smoothing:antialiased;overflow-x:clip}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;cursor:pointer;border:0;background:none;color:inherit;text-align:left}
::selection{background:var(--pku-red-fill);color:#fff}
[hidden]{display:none !important}

.wrap{max-width:var(--maxw);margin:0 auto;padding:0 var(--gut)}
.zh{font-family:var(--cjk)}
/* In Chinese the page title is Chinese and this line carries the English
   name underneath, so it keeps Latin metrics even in zh mode. */
.sub-latin{font-family:var(--sans);letter-spacing:.03em}
html[lang="zh"] .sub-latin{font-family:var(--sans)}
.sec{padding:var(--sec-y) 0;position:relative}
.sec-sm{padding:clamp(52px,7vw,110px) 0}

/* ---------- type ---------- */
.eyebrow{font-size:12.5px;letter-spacing:.22em;text-transform:uppercase;font-weight:600;color:var(--pku-red-fill)}
.display{font-family:var(--serif);font-weight:400;font-size:clamp(1.9rem,4.4vw,3.9rem);line-height:1.1;letter-spacing:-.022em;margin:0}
.h1{font-size:clamp(2rem,4.2vw,3.7rem);line-height:1.05;letter-spacing:-.032em;font-weight:660;margin:0}
.h2{font-size:clamp(1.5rem,2.5vw,2.3rem);line-height:1.14;letter-spacing:-.024em;font-weight:650;margin:0}
.h3{font-size:clamp(1.14rem,1.5vw,1.42rem);line-height:1.26;letter-spacing:-.016em;font-weight:640;margin:0}
.h4{font-size:15.5px;line-height:1.36;letter-spacing:-.012em;font-weight:650;margin:0}
.lede{font-size:clamp(1.02rem,1.32vw,1.28rem);line-height:1.56;color:var(--ink-soft);max-width:62ch;margin:0}
.body-txt{max-width:var(--measure);font-size:15.6px;line-height:1.72;color:var(--ink-soft);margin:0;white-space:pre-line}
.small{font-size:15px;line-height:1.65;color:var(--ink-soft);margin:0}
.tiny{font-size:12.5px;line-height:1.6;letter-spacing:.04em;color:var(--mineral-grey);margin:0}
.mono{font-family:var(--mono);font-size:12.5px;letter-spacing:.04em}
.serif-q{font-family:var(--serif);font-weight:400}

/* dark ground: body text is ivory, accent text is signal-red */
.dark{background:var(--carbon-black);color:var(--warm-ivory)}
.dark .lede,.dark .small,.dark .body-txt{color:rgba(var(--ink-alt-rgb),.66)}
.dark .tiny{color:rgba(var(--ink-alt-rgb),.45)}
.dark .eyebrow{color:var(--signal-red)}
.dark .h1,.dark .h2,.dark .h3,.dark .h4,.dark .display{color:var(--warm-ivory)}

/* ---------- section head ---------- */
.sec-head{display:grid;grid-template-columns:minmax(0,.38fr) minmax(0,.62fr);gap:clamp(22px,5vw,76px);align-items:start;margin-bottom:clamp(32px,4.4vw,64px)}
.sec-head .num{font-family:var(--mono);font-size:12.5px;color:var(--pku-red-fill);letter-spacing:.18em;margin-bottom:13px}
.dark .sec-head .num{color:var(--signal-red)}
/* A head with no body under it would otherwise stack its own bottom
   margin on top of the section padding. */
.sec-head:last-child{margin-bottom:0}
@media(max-width:900px){.sec-head{grid-template-columns:1fr;gap:16px}}

/* ---------- reveal ----------------------------------------------
   One reveal for the whole site. `.rv` (inner pages) and `.reveal`
   (home) are the same gesture: lift, sharpen, settle.
   Without JS every block is simply visible — the hidden state only
   exists once `.motion-ready` is on <html>.
   ---------------------------------------------------------------- */
.rv,.reveal{opacity:1;transform:none}
.motion-ready .rv,
.motion-ready .reveal{
  opacity:1;
  transform:none;
  filter:none;
  transition:none;
  transition-delay:0s;
}
.motion-ready .rv.in,
.motion-ready .reveal.is-visible,
.motion-ready .reveal.in{
  opacity:1;
  transform:none;
  filter:none;
}
/* authored stagger — kept for existing markup, expressed on the same scale */
.rv-1{--rv-i:1}.rv-2{--rv-i:2}.rv-3{--rv-i:3}
.rv-4{--rv-i:4}.rv-5{--rv-i:5}.rv-6{--rv-i:6}
.reveal-delay-1{--rv-i:1}.reveal-delay-2{--rv-i:2}.reveal-delay-3{--rv-i:3}

/* ---------- scroll progress --------------------------------------
   A single hairline under the fixed nav. It is the only chrome that
   reports position, so it stays 2px and brand-red.
   ---------------------------------------------------------------- */
.yl-progress{
  position:fixed;
  top:0;left:0;
  z-index:1200;
  width:100%;
  height:2px;
  pointer-events:none;
  transform-origin:0 50%;
  transform:scaleX(var(--yl-scroll,0));
  background:linear-gradient(90deg,var(--pku-red-fill),var(--signal-red));
  opacity:0;
  transition:opacity .4s var(--ease-out);
}
.yl-progress.is-on{opacity:.92}

/* ---------- scroll-linked media drift -----------------------------
   Progressive enhancement only: transform, never opacity, so a block
   can never be hidden by it. Ignored where view() is unsupported.
   ---------------------------------------------------------------- */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .motion-ready [data-drift]{
      animation:none;
      transform:none;
      will-change:auto;
    }

    /* ---------- leaving the top of the screen --------------------
       The counterpart to the reveal. It is only attached once a block
       has finished entering (`.rv-settled`, set from JS ~1.4s after the
       block first appears) so it can never interrupt the enter, and
       only to blocks shorter than the viewport so the whole block
       recedes together. `from` is the resting state and fill is both,
       so a block outside the range is always fully visible.
       ------------------------------------------------------------- */
    .motion-ready .rv-settled{
      animation:none;
      opacity:1;
      transform:none;
      filter:none;
    }
  }
}
@keyframes yl-drift{
  from{transform:translate3d(0,-2.1%,0) scale(1.045)}
  to  {transform:translate3d(0, 2.1%,0) scale(1.045)}
}
@keyframes yl-recede{
  from{opacity:1;transform:none;filter:none}
  to  {opacity:1;transform:none;filter:none}
}

/* ---------- page enter / exit -------------------------------------
   Real cross-document transitions where the browser supports them;
   everywhere else the pages simply swap as before.
   ---------------------------------------------------------------- */
@view-transition{navigation:auto}
@media (prefers-reduced-motion:no-preference){
  ::view-transition-old(root){animation:yl-page-out .3s var(--ease-soft) both}
  ::view-transition-new(root){animation:yl-page-in .46s var(--ease-out) both}
}
@keyframes yl-page-out{
  to{opacity:0;transform:translate3d(0,-14px,0)}
}
@keyframes yl-page-in{
  from{opacity:0;transform:translate3d(0,20px,0)}
}

@media(prefers-reduced-motion:reduce){
  .rv,.reveal{opacity:1!important;transform:none!important;filter:none!important;transition:none!important}
  .nav-affiliations a{transition:none!important}
  .nav-affiliations a:hover,.nav-affiliations a:active{transform:none}
  html{scroll-behavior:auto}
}

/* An anchor jump aligns the target's top edge with the viewport's, which on
   this site is underneath a 70px sticky header. Every element that a fragment
   can address reserves that much plus a little air, so a link from the nav, the
   footer or another page lands where the reader can see it. */
[id]{ scroll-margin-top:88px }

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;gap:9px;padding:12px 22px;border-radius:100px;font-size:14.5px;font-weight:620;transition:transform .4s var(--ease),background .35s,color .35s,border-color .35s;white-space:nowrap}
.btn svg{width:14px;height:14px;flex:0 0 auto}
.btn-primary{background:var(--pku-red-fill);color:#fff}
.btn-primary:hover{background:var(--signal-red);transform:translateY(-2px)}
.btn-ghost{border:1px solid rgba(var(--ink-alt-rgb),.32);color:var(--warm-ivory)}
.btn-ghost:hover{background:var(--warm-ivory);color:var(--ink);border-color:var(--warm-ivory);transform:translateY(-2px)}
.btn-line{border:1px solid var(--border-grey);color:var(--ink)}
.btn-line:hover{border-color:var(--pku-red-fill);color:var(--pku-red-fill)}
.arrow-link{display:inline-flex;align-items:center;gap:8px;font-size:13.6px;font-weight:620;color:var(--pku-red-fill);transition:gap .35s var(--ease)}
.arrow-link:hover{gap:14px}
.dark .arrow-link{color:var(--signal-red)}

/* ============================================================
   NAV  (injected by render.js)
   ============================================================ */
.progress{position:fixed;top:0;left:0;height:2px;width:100%;background:var(--pku-red-fill);z-index:200;transform:scaleX(0);transform-origin:left}
@supports (animation-timeline:scroll()){
  .progress{animation:site-progress linear both;animation-timeline:scroll(root block)}
  @keyframes site-progress{to{transform:scaleX(1)}}
}
.nav{position:fixed;top:0;left:0;right:0;z-index:190;height:var(--nav-h);
  backdrop-filter:saturate(180%) blur(20px);-webkit-backdrop-filter:saturate(180%) blur(20px);
  border-bottom:1px solid rgba(242,239,232,.12);background:rgba(26,18,20,.9);
  transition:background .5s var(--ease),border-color .5s,color .5s;color:#F2EFE8}
.nav.solid,.nav.forced{background:rgba(26,18,20,.96);border-bottom-color:rgba(242,239,232,.14);color:#F2EFE8}
.nav-in{max-width:var(--maxw);margin:0 auto;padding:0 var(--gut);height:var(--nav-h);display:flex;align-items:center;gap:14px}
.nav-affiliations{display:flex;align-items:center;gap:9px;flex:0 0 auto}
.nav-affiliations a{display:flex;min-height:32px;align-items:center;opacity:.82;transition:opacity .3s,transform .3s}
.nav-affiliations a+a{padding-left:9px;border-left:1px solid rgba(255,255,255,.22)}
.nav-affiliations a:hover{opacity:1;transform:translateY(-1px)}
.nav-affiliations a:active{transform:translateY(1px)}
.nav-affiliations a:focus-visible{outline:2px solid currentColor;outline-offset:3px}
.nav-affiliations img{display:block;height:auto;object-fit:contain}
.nav-affiliation-ues{width:160px}
.nav-affiliation-cqb{width:106px;filter:brightness(0) invert(1)}
.nav-affiliation-ocean{display:flex;align-items:center;gap:5px;width:142px;color:#fff}
.nav-affiliation-ocean img{width:21px;height:21px;filter:brightness(0) invert(1);opacity:.92;flex:0 0 auto}
.nav-affiliation-ocean span{display:flex;min-width:0;flex-direction:column;justify-content:center;line-height:1.05}
.nav-affiliation-ocean b{display:block;font-size:10.5px;font-weight:650;letter-spacing:.04em;white-space:nowrap;color:currentColor}
.nav-affiliation-ocean small{display:block;margin-top:1px;font-size:5.6px;font-weight:520;letter-spacing:.01em;white-space:nowrap;color:rgba(255,255,255,.82)}
.brand{display:flex;align-items:center;gap:12px;flex:0 0 auto}
.brand-divider{display:inline-flex;align-items:center;height:36px;color:rgba(255,255,255,.42);font-family:var(--sans);font-size:25px;font-weight:300;line-height:1}
.mark{width:34px;height:34px;border-radius:50%;background:var(--pku-red-fill);color:#fff;display:grid;place-items:center;font-family:var(--serif);font-size:15px;font-weight:700;flex:0 0 auto;box-shadow:0 0 0 1px rgba(255,255,255,.22) inset}
.brand-txt{line-height:1.14}
.brand-txt b{display:block;font-size:15px;letter-spacing:-.018em;font-weight:680;color:#fff}
.brand-txt span{display:block;font-size:12.5px;letter-spacing:.15em;text-transform:uppercase;opacity:.62}
.nav-main{display:flex;align-items:center;gap:2px;margin-left:auto}
.nav-item{position:relative}
.nav-item > a,.nav-item > button{display:flex;align-items:center;gap:6px;padding:9px 13px;font-size:14.5px;font-weight:530;opacity:.76;border-radius:100px;transition:opacity .3s,background .3s;white-space:nowrap}
.nav-item > a:hover,.nav-item > button:hover{opacity:1;background:rgba(223,102,104,.12)}
.nav-item.active > a,.nav-item.active > button{opacity:1;color:#94070A;font-weight:640}
.nav .caret{width:8px;height:8px;opacity:.55;transition:transform .35s var(--ease)}
.nav-item.open .caret{transform:rotate(180deg)}
.nav-tools{display:flex;align-items:center;gap:4px;margin-left:14px;padding-left:14px;border-left:1px solid currentColor;border-left-color:rgba(141,137,132,.35)}
.icon-btn{width:34px;height:34px;border-radius:50%;display:grid;place-items:center;opacity:.72;transition:opacity .3s,background .3s}
.icon-btn:hover{opacity:1;background:rgba(148,7,10,.1)}
.lang{font-family:var(--mono);font-size:12.5px;letter-spacing:.06em;padding:7px 11px;border-radius:100px;border:1px solid rgba(141,137,132,.4);opacity:.8;transition:.3s;white-space:nowrap;flex:0 0 auto}
.lang:hover{opacity:1;border-color:#94070A;color:#94070A}
.lang b{font-weight:700}
.burger{display:none;width:38px;height:38px;place-items:center}
.burger i{display:block;width:19px;height:1.5px;background:currentColor;position:relative;transition:.3s}
.burger i::before,.burger i::after{content:'';position:absolute;left:0;width:19px;height:1.5px;background:currentColor;transition:.3s}
.burger i::before{top:-6px}.burger i::after{top:6px}
body.menu-open .burger i{background:transparent}
body.menu-open .burger i::before{top:0;transform:rotate(45deg)}
body.menu-open .burger i::after{top:0;transform:rotate(-45deg)}

/* mega menu */
.mega{position:absolute;top:calc(100% + 8px);left:50%;transform:translate(-50%,-8px);
  background:var(--paper-white);border:1px solid var(--line);border-radius:var(--r-md);
  box-shadow:0 24px 70px rgba(20,16,15,.16);padding:26px 28px 22px;
  max-width:calc(100vw - 40px);
  opacity:0;pointer-events:none;transition:.35s var(--ease);color:var(--ink)}
.nav-item.open .mega{opacity:1;pointer-events:auto;transform:translate(-50%,0)}
.mega-grid{display:grid;gap:24px 32px}
.mega[data-cols="1"]{min-width:270px}
.mega[data-cols="1"] .mega-grid{grid-template-columns:1fr}
.mega[data-cols="2"]{min-width:520px}
.mega[data-cols="2"] .mega-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
.mega[data-cols="3"]{min-width:690px}
.mega[data-cols="3"] .mega-grid{grid-template-columns:repeat(3,minmax(0,1fr))}
.mega[data-cols="4"]{min-width:880px}
.mega[data-cols="4"] .mega-grid{grid-template-columns:repeat(4,minmax(0,1fr))}
/* Mega menus only exist ≥1241px (below that the drawer takes over), so a
   centred 880px panel always fits; max-width above is the safety net. */
.mega-col h6{margin:0 0 11px;font-family:var(--mono);font-size:12.5px;letter-spacing:.15em;text-transform:uppercase;color:var(--mineral-grey);font-weight:600}
.mega-col a{display:block;padding:5px 0;font-size:13.6px;font-weight:540;transition:color .28s,padding-left .28s}
.mega-col a:hover{color:var(--pku-red-fill);padding-left:5px}
.mega-col a .sub{display:block;font-size:12.5px;color:var(--mineral-grey);font-weight:400;font-family:var(--cjk)}
/* What is actually behind the link. These entries lead to charts and real
   numbers, and a bare label gives the reader no reason to expect that. */
.mega-meta{
  display:block;
  margin-top:2px;
  font-family:var(--mono);
  font-size:12.5px;
  font-weight:500;
  letter-spacing:.06em;
  color:var(--mineral-grey);
  opacity:.82;
  transition:color .28s,opacity .28s;
}
.mega-col a:hover .mega-meta{color:var(--pku-red-fill);opacity:1}
.dark .mega-col a:hover .mega-meta{color:var(--signal-red)}
/* ---- destination cards ----------------------------------------------
   Used where a section is a small number of whole pages rather than a
   list of anchors: one target per card, contents described in text.
   Borders derive from the panel's own colour so the same rule reads on
   the light inner-page panel and the near-black home panel.
   --------------------------------------------------------------------- */
.mega-cards[data-cols="2"]{min-width:640px}
.mega-cards .mega-grid{gap:14px}
/* The card is a container now, not a link: it holds its own second level,
   and an <a> cannot be nested inside an <a>. `.mega-card-main` inherits the
   whole-card click target the card itself used to be. */
.mega-card{
  position:relative;
  display:flex;
  flex-direction:column;
  border-radius:var(--r-sm);
  transition:background .3s var(--ease),box-shadow .3s var(--ease),transform .3s var(--ease);
}
.mega-card-main{
  position:relative;
  display:block;
  /* grows to fill, so two cards whose descriptions differ in length still
     drop their hairline — and the second level below it — on the same line */
  flex:1 1 auto;
  padding:18px 40px 17px 19px;
}
.mega-card b{display:block;font-size:15px;font-weight:660;letter-spacing:-.01em}
.mega-card b .sub{display:block;margin-top:2px;font-size:12.5px;font-weight:400;color:var(--mineral-grey);font-family:var(--cjk)}
.mega-card p{margin:9px 0 0;font-size:12.6px;line-height:1.55;color:var(--mineral-grey)}
.mega-card .mega-meta{margin-top:11px}
.mega-card-main > i{
  position:absolute;
  top:18px;right:17px;
  font-style:normal;
  color:var(--pku-red-fill);
  transition:transform .32s var(--ease-out);
}
.mega-card:has(> .mega-card-main:hover),
.mega-card:not(.has-subs):hover{transform:translateY(-1px)}
.mega-card-main:hover > i{transform:translateX(4px)}
/* A card with a second level needs less room above it — the sub-links carry
   the weight, so the headline block tightens up rather than the panel growing. */
.mega-card.has-subs .mega-card-main{padding-bottom:13px}
.mega-card.has-subs p{margin-top:7px}

/* ---- second level -------------------------------------------------------
   Deliberately subordinate: smaller, indented, separated by one hairline.
   These read as "inside this destination", never as peers of the two cards. */
.mega-subs{
  display:flex;
  flex-direction:column;
  margin:auto 11px 11px;
  padding-top:5px;
  border-top:1px solid color-mix(in srgb, currentColor 11%, transparent);
}
.mega-sub{
  position:relative;
  display:flex;
  align-items:baseline;
  gap:9px;
  padding:8px 26px 8px 8px;
  border-radius:var(--r-xs);
  transition:background .26s var(--ease),padding-left .26s var(--ease-out);
}
.mega-sub b{
  font-size:12.6px;
  font-weight:620;
  letter-spacing:-.005em;
  white-space:nowrap;
}
.mega-sub > span{
  font-size:12.5px;
  line-height:1.3;
  color:var(--mineral-grey);
  font-family:var(--mono);
  letter-spacing:.01em;
}
.mega-sub > i{
  position:absolute;
  top:50%;right:9px;
  margin-top:-.55em;
  font-style:normal;
  font-size:12.5px;
  opacity:0;
  color:var(--pku-red-fill);
  transition:opacity .26s var(--ease),transform .26s var(--ease-out);
  transform:translateX(-3px);
}
.mega-sub:hover{
  background:color-mix(in srgb, currentColor 9%, transparent);
  padding-left:12px;
}
.mega-sub:hover > i{opacity:1;transform:translateX(0)}
html[lang="zh"] .mega-sub > span{font-family:var(--cjk);letter-spacing:.02em}
/* --pku-red-fill is a fill, never text on dark (2.2:1). Every panel that
   sits on a dark ground takes the accent text colour instead. */
.dark .mega-card i,
body[data-page="home"] .mega-card i,
body[data-inner-hero] .mega-card i{color:var(--signal-red)}
.mega-foot{margin-top:20px;padding-top:16px;border-top:1px solid var(--line);display:flex;flex-wrap:wrap;gap:20px}
@media(max-width:1180px){.nav-main{display:none}.burger{display:grid;margin-left:auto}.nav-tools{margin-left:0}}
@media(max-width:720px){
  .nav-in{gap:8px}
  .nav-affiliations{gap:5px}
  .nav-affiliations a+a{padding-left:5px}
  .nav-affiliation-ues{width:116px}
  .nav-affiliation-cqb{width:76px}
  .nav-affiliation-ocean{width:106px;gap:4px}
  .nav-affiliation-ocean img{width:17px;height:17px}
  .nav-affiliation-ocean b{font-size:8px}
  .nav-affiliation-ocean small{font-size:4.4px}
  .brand{gap:7px}
  .brand-divider{height:30px;font-size:21px}
  .mark{width:30px;height:30px}
  .brand-txt span{display:none}
}
@media(max-width:520px){
  .nav-in{gap:8px}
  .nav-affiliations a:nth-child(n+2){display:none}
  .nav-tools{margin-left:0;padding-left:8px;gap:2px}
  .lang{font-size:12.5px;padding:6px 8px;letter-spacing:.02em}
  .brand-txt span{display:none}
}
@media(max-width:440px){
  .brand-txt b{font-size:14px}
}

/* mobile drawer */
.drawer{position:fixed;inset:var(--nav-h) 0 0 0;z-index:185;background:var(--paper-white);color:var(--ink);
  padding:12px var(--gut) 40px;overflow-y:auto;transform:translateY(-12px);opacity:0;pointer-events:none;transition:.4s var(--ease)}
body.menu-open .drawer{transform:none;opacity:1;pointer-events:auto}
.drawer-item{border-bottom:1px solid var(--line)}
.drawer-item > a,.drawer-item > button{width:100%;display:flex;align-items:center;justify-content:space-between;padding:15px 0;font-size:16.5px;font-weight:600}
.drawer-item .plus{font-family:var(--mono);font-size:17px;color:var(--pku-red-fill);transition:transform .4s var(--ease)}
.drawer-item.open .plus{transform:rotate(135deg)}
.drawer-sub{max-height:0;overflow:hidden;transition:max-height .5s var(--ease)}
.drawer-sub-in{padding:0 0 14px 2px}
.drawer-sub a{display:block;padding:8px 0;font-size:14.4px;color:var(--ink-soft)}
.drawer-sub a:hover{color:var(--pku-red-fill)}
.drawer-sub a .mega-meta{margin-top:1px;font-size:12.5px}
.drawer-sub h6{margin:12px 0 4px;font-family:var(--mono);font-size:12.5px;letter-spacing:.15em;text-transform:uppercase;color:var(--mineral-grey)}

/* ============================================================
   HERO
   ============================================================ */
.hero{position:relative;min-height:100svh;display:flex;align-items:flex-end;background:var(--carbon-black);color:var(--warm-ivory);overflow:hidden;isolation:isolate}
#hero-canvas{position:absolute;inset:0;width:100%;height:100%;z-index:0}
.hero::after{content:'';position:absolute;inset:0;z-index:1;pointer-events:none;
  background:radial-gradient(115% 82% at 76% 10%,rgba(148,7,10,.40),rgba(148,7,10,0) 58%),
             radial-gradient(90% 68% at 6% 100%,rgba(209,58,53,.16),rgba(var(--shadow-rgb),calc(0 * var(--shadow-k))) 62%),
             linear-gradient(180deg,rgba(var(--ground-2-rgb),.74) 0%,rgba(var(--ground-2-rgb),.16) 34%,rgba(var(--ground-2-rgb),.88) 100%)}
.hero-in{position:relative;z-index:2;width:100%;max-width:var(--maxw);margin:0 auto;padding:calc(var(--nav-h) + clamp(28px,7vh,58px)) var(--gut) 34px}
.hero-brandline{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px 14px;margin-bottom:clamp(14px,3vh,30px)}
.hero-brandline b{font-size:clamp(1rem,1.3vw,1.2rem);letter-spacing:.16em;text-transform:uppercase;font-weight:680}
.hero-brandline span{font-size:12.5px;letter-spacing:.2em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.55)}
.hero h1{font-family:var(--serif);font-weight:400;font-size:clamp(1.56rem,min(2.9vw,4.8vh),3.25rem);line-height:1.15;letter-spacing:-.02em;max-width:46ch;margin:0 0 clamp(14px,2.4vh,28px)}
.hero h1 em{font-style:italic}
.hero h1 .hl{background:linear-gradient(180deg,transparent 62%,rgba(209,58,53,.55) 62%,rgba(209,58,53,.55) 92%,transparent 92%)}
.hero-kicker{font-family:var(--mono);font-size:12.5px;letter-spacing:.13em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.52);margin:0 0 clamp(20px,3.4vh,38px)}
.hero-cta{display:flex;flex-wrap:wrap;gap:11px;margin-bottom:clamp(22px,4.4vh,50px)}
.hero-strip{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid rgba(var(--ink-alt-rgb),.2)}
.hero-strip div{padding:17px 20px 4px;border-left:1px solid rgba(var(--ink-alt-rgb),.12)}
.hero-strip div:first-child{border-left:0;padding-left:0}
.hero-strip .k{font-family:var(--mono);font-size:clamp(1.1rem,1.9vw,1.75rem);font-weight:500;letter-spacing:-.03em;display:block;line-height:1}
.hero-strip .v{display:block;font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.5);margin-top:8px}
@media(max-width:860px){.hero-strip{grid-template-columns:repeat(2,1fr)}.hero-strip div{padding:14px 14px 4px}.hero-strip div:nth-child(odd){border-left:0;padding-left:0}}
.scrollcue{position:absolute;right:calc(var(--gut)/2.6);top:50%;transform:translateY(-50%);z-index:3;display:flex;flex-direction:column;align-items:center;gap:14px;font-size:12.5px;letter-spacing:.22em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.42)}
.scrollcue span:first-child{writing-mode:vertical-rl}
.scrollcue .bar{width:1px;height:54px;background:linear-gradient(180deg,rgba(var(--ink-alt-rgb),.06),rgba(var(--ink-alt-rgb),.6));animation:cue 2.4s var(--ease) infinite;transform-origin:top}
@keyframes cue{0%,100%{transform:scaleY(.35);opacity:.4}50%{transform:scaleY(1);opacity:1}}
@media(max-width:1240px){.scrollcue{display:none}}

/* page hero (inner pages) */
.phero{background:var(--carbon-black);color:var(--warm-ivory);padding:calc(var(--nav-h) + clamp(48px,8vw,104px)) 0 clamp(44px,6vw,86px);position:relative;overflow:hidden}
.phero::after{content:'';position:absolute;inset:0;background:radial-gradient(100% 90% at 82% 0%,rgba(148,7,10,.34),rgba(148,7,10,0) 60%);pointer-events:none}
.phero-in{position:relative;z-index:1}
.crumb{display:flex;flex-wrap:wrap;gap:8px;align-items:center;font-family:var(--mono);font-size:12.5px;letter-spacing:.12em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.48);margin-bottom:20px}
.crumb a:hover{color:var(--signal-red)}
.crumb i{font-style:normal;opacity:.5}
.phero h1{margin:0 0 14px}
.phero .sub{font-family:var(--cjk);font-size:15px;color:rgba(var(--ink-alt-rgb),.6);margin:0 0 18px}

/* ============================================================
   QUESTIONS (homepage 02)
   ============================================================ */
.q-stage{display:grid;grid-template-columns:minmax(0,.34fr) minmax(0,.66fr);gap:clamp(24px,5vw,72px);align-items:start}
@media(max-width:900px){.q-stage{grid-template-columns:1fr}}
.q-rail{display:flex;flex-direction:column;gap:2px;position:sticky;top:calc(var(--nav-h) + 28px)}
@media(max-width:900px){.q-rail{position:static;flex-direction:row;overflow-x:auto}}
.q-rail button{display:flex;gap:14px;padding:16px 18px;border-radius:4px;transition:background .4s var(--ease);position:relative;min-width:0}
@media(max-width:900px){.q-rail button{min-width:190px;flex:0 0 auto}}
.q-rail button::before{content:'';position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--pku-red-fill);transform:scaleY(0);transition:transform .4s var(--ease)}
.q-rail button.on{background:var(--warm-ivory)}
.q-rail button.on::before{transform:scaleY(1)}
.q-rail .qn{font-family:var(--mono);font-size:12.5px;color:var(--pku-red-fill);letter-spacing:.1em;padding-top:3px}
.q-rail .qt{font-size:14px;font-weight:600;line-height:1.4}
.q-panel{display:none}
.q-panel.on{display:block;animation:fadeup .55s var(--ease-out)}
@keyframes fadeup{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
.q-panel .qq{font-family:var(--serif);font-size:clamp(1.4rem,2.9vw,2.6rem);line-height:1.24;letter-spacing:-.02em;margin:0 0 18px}
.q-panel .qzh{font-family:var(--cjk);font-size:15px;color:var(--ink-soft);margin:0 0 22px}
.q-visual{aspect-ratio:16/9;border-radius:5px;background:linear-gradient(150deg,var(--plate-dark),var(--deep-red) 70%,var(--pku-red-fill));position:relative;overflow:hidden;display:grid;place-items:center;margin-bottom:22px}
.q-visual canvas{position:absolute;inset:0;width:100%;height:100%}
.q-visual .vlabel{position:relative;z-index:1;font-family:var(--mono);font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.6);padding:0 24px;text-align:center}

/* ============================================================
   RESEARCH FOCUS — editorial rows
   ============================================================ */
.focus-row{display:grid;grid-template-columns:minmax(0,.46fr) minmax(0,.54fr);gap:clamp(26px,4.4vw,70px);
  padding:clamp(34px,4.6vw,70px) 0;border-top:1px solid var(--line);align-items:start}
.focus-row:last-child{border-bottom:1px solid var(--line)}
@media(max-width:900px){.focus-row{grid-template-columns:1fr;gap:20px}}
.focus-row .fnum{font-family:var(--mono);font-size:12.5px;color:var(--pku-red-fill);letter-spacing:.16em;margin-bottom:12px}
.focus-row h3{margin:0 0 8px;font-size:clamp(1.4rem,2.2vw,2rem);letter-spacing:-.026em;font-weight:650}
.focus-row .ftag{font-family:var(--cjk);font-size:14px;color:var(--mineral-grey);margin:0 0 18px}
.focus-row .fjudge{font-family:var(--serif);font-size:clamp(1.06rem,1.5vw,1.34rem);line-height:1.44;margin:0 0 18px;max-width:40ch}
.focus-media{aspect-ratio:16/10;border-radius:5px;overflow:hidden;background:linear-gradient(150deg,var(--plate-dark),var(--deep-red) 72%,var(--pku-red-fill));position:relative;display:grid;place-items:center}
.focus-media .ph{font-family:var(--mono);font-size:12.5px;letter-spacing:.15em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.45);text-align:center;padding:0 20px}
.chips{display:flex;flex-wrap:wrap;gap:7px;margin-top:16px}
.chip{font-size:12.5px;letter-spacing:.03em;padding:5px 11px;border-radius:100px;border:1px solid var(--border-grey);color:var(--ink-soft);background:#fff}
.chip.red{border-color:rgba(148,7,10,.3);color:var(--pku-red-fill);background:rgba(148,7,10,.05)}
.dark .chip{border-color:var(--line-dark);color:rgba(var(--ink-alt-rgb),.7);background:rgba(var(--ink-alt-rgb),.04)}

/* ============================================================
   GRID PRIMITIVES
   ============================================================ */
.grid2,.grid3,.grid4{display:grid;gap:1px;background:var(--line);border:1px solid var(--line);border-radius:4px;overflow:hidden}
.grid2{grid-template-columns:repeat(2,minmax(0,1fr))}
.grid3{grid-template-columns:repeat(3,minmax(0,1fr))}
.grid4{grid-template-columns:repeat(4,minmax(0,1fr))}
@media(max-width:1000px){.grid4{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:900px){.grid3{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:620px){.grid2,.grid3,.grid4{grid-template-columns:1fr}}
/* These grids are drawn as hairlines showing through a 1px gap, so a
   short row leaves the backdrop exposed as a blank tile. Collapse the
   track count to whatever the data actually filled. Each rule sits
   inside the breakpoint it belongs to so it never re-widens a grid the
   responsive rules above have already stacked. */
.grid2:has(> :only-child),
.grid3:has(> :only-child),
.grid4:has(> :only-child){grid-template-columns:minmax(0,1fr)}
@media(min-width:621px){
  .grid3:has(> :nth-child(2):last-child),
  .grid4:has(> :nth-child(2):last-child){grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media(min-width:1001px){
  .grid4:has(> :nth-child(3):last-child){grid-template-columns:repeat(3,minmax(0,1fr))}
}
.dark .grid2,.dark .grid3,.dark .grid4{background:var(--line-dark);border-color:var(--line-dark)}
.cell{background:var(--paper-white);padding:20px 19px;display:flex;flex-direction:column;gap:9px}
.dark .cell{background:var(--carbon-black)}
.cell .cn{font-family:var(--mono);font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--pku-red-fill)}
.dark .cell .cn{color:var(--signal-red)}
.cell h5{margin:0;font-size:14px;font-weight:640;letter-spacing:-.01em;line-height:1.32}
.cell p{margin:0;font-size:12.9px;line-height:1.58;color:var(--ink-soft)}
.dark .cell p{color:rgba(var(--ink-alt-rgb),.62)}
.cell .big{font-family:var(--mono);font-size:1.6rem;letter-spacing:-.035em;line-height:1}
.cell .zh-t{font-family:var(--cjk);font-size:12.5px;color:var(--mineral-grey)}

/* flow / pill rows */
.flow{display:flex;flex-wrap:wrap;align-items:center;gap:7px}
.flow span{font-size:12.6px;padding:8px 13px;border-radius:3px;background:var(--warm-ivory);border:1px solid var(--line);line-height:1.3}
.flow .arrow{background:none;border:0;color:var(--pku-red-fill);padding:8px 2px;font-family:var(--mono)}
.dark .flow span{background:rgba(var(--ink-alt-rgb),.05);border-color:var(--line-dark);color:var(--warm-ivory)}
.dark .flow .arrow{background:none;border:0;color:var(--signal-red)}
.pillrow{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:18px 20px;border:1px dashed var(--border-grey);border-radius:4px;background:var(--paper-white)}
.pillrow b{font-family:var(--mono);font-size:12.5px;font-weight:500;letter-spacing:.03em;padding:7px 12px;background:var(--warm-ivory);border-radius:3px}
.pillrow b.hot{background:var(--pku-red-fill);color:#fff}
.pillrow s{text-decoration:none;color:var(--pku-red-fill);font-family:var(--mono)}
.pillrow .lbl{font-family:var(--mono);font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--mineral-grey);margin-right:4px}
.dark .pillrow{border-color:var(--line-dark);background:rgba(var(--ink-alt-rgb),.03)}
.dark .pillrow b{background:rgba(var(--ink-alt-rgb),.08);color:var(--warm-ivory)}
.dark .pillrow s{color:var(--signal-red)}
.kpis{display:flex;flex-wrap:wrap;gap:8px}
.kpis b{font-family:var(--mono);font-size:12.5px;font-weight:500;padding:6px 11px;border-radius:3px;background:rgba(148,7,10,.07);color:var(--pku-red-fill)}
.dark .kpis b{background:rgba(209,58,53,.14);color:#f2b8b6}
.bullets{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:8px}
.bullets li{font-size:13.8px;line-height:1.58;color:var(--ink-soft);padding-left:15px;position:relative}
.bullets li::before{content:'';position:absolute;left:0;top:8px;width:5px;height:5px;border-radius:50%;background:var(--pku-red-fill);opacity:.55}
.dark .bullets li{color:rgba(var(--ink-alt-rgb),.66)}
.dark .bullets li::before{background:var(--signal-red)}

/* status / disclosure badges */
.badge{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);font-size:12.5px;letter-spacing:.13em;text-transform:uppercase;padding:4px 9px;border-radius:3px;border:1px solid}
.badge-review{background:rgba(138,86,6,.1);color:var(--warn);border-color:rgba(138,86,6,.3)}
.badge-pending{background:rgba(141,137,132,.12);color:var(--mineral-grey);border-color:rgba(141,137,132,.3)}
.badge-role{background:rgba(148,7,10,.07);color:var(--pku-red-fill);border-color:rgba(148,7,10,.26)}
.dark .badge-review{background:rgba(209,58,53,.14);color:#f0c07a;border-color:rgba(240,192,122,.32)}
.dark .badge-role{background:rgba(var(--ink-alt-rgb),.1);color:var(--warm-ivory);border-color:var(--line-dark)}
.note{margin-top:18px;padding-top:14px;border-top:1px solid var(--line);font-size:12.5px;line-height:1.62;color:var(--mineral-grey)}
.dark .note{border-top-color:var(--line-dark);color:rgba(var(--ink-alt-rgb),.45)}

/* ============================================================
   INFRASTRUCTURE LOOP — 200vh pin + click-step
   ============================================================ */
.loop-sec{background:var(--carbon-black);color:var(--warm-ivory)}
.loop-scroll{height:200vh;position:relative}
.loop-pin{position:sticky;top:0;height:100svh;display:flex;align-items:center;overflow:hidden}
.loop-inner{width:100%;max-width:var(--maxw);margin:0 auto;padding:0 var(--gut)}
.loop-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:end;gap:18px;margin-bottom:clamp(20px,3.4vh,40px)}
.loop-head h2{max-width:22ch}
.loop-head .sub{font-size:13.6px;color:rgba(var(--ink-alt-rgb),.58);max-width:44ch;margin:0}
.loop-steps{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line-dark);border:1px solid var(--line-dark);border-radius:5px;overflow:hidden}
@media(max-width:900px){.loop-steps{grid-template-columns:repeat(2,1fr)}}
@media(max-width:560px){.loop-steps{grid-template-columns:1fr}}
.loop-step{background:var(--carbon-black);padding:clamp(18px,2.6vh,30px) 20px;display:flex;flex-direction:column;gap:11px;transition:background .6s var(--ease);position:relative;min-height:0;cursor:pointer}
.loop-step .ln{font-family:var(--mono);font-size:12.5px;letter-spacing:.15em;color:rgba(var(--ink-alt-rgb),.4);transition:color .5s}
.loop-step h4{margin:0;font-size:clamp(1.02rem,1.5vw,1.32rem);letter-spacing:-.02em;font-weight:660}
.loop-step .lc{margin:0;font-size:12.6px;line-height:1.55;color:rgba(var(--ink-alt-rgb),.5);transition:color .5s}
.loop-step .lmods{display:flex;flex-wrap:wrap;gap:5px;margin-top:auto;padding-top:8px}
.loop-step .lmods a{font-family:var(--mono);font-size:12.5px;letter-spacing:.03em;padding:4px 8px;border-radius:3px;background:rgba(var(--ink-alt-rgb),.06);color:rgba(var(--ink-alt-rgb),.62);transition:.3s}
.loop-step .lmods a:hover{background:var(--pku-red-fill);color:#fff}
.loop-step.on{background:var(--warm-black-2)}
.loop-step.on .ln{color:var(--signal-red)}
.loop-step.on .lc{color:rgba(var(--ink-alt-rgb),.78)}
.loop-step::after{content:'';position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--pku-red-fill);transform:scaleX(0);transform-origin:left;transition:transform .7s var(--ease-out)}
.loop-step.on::after{transform:scaleX(1)}
.loop-nav{display:flex;align-items:center;gap:10px;margin-top:22px}
.loop-dots{display:flex;gap:6px}
.loop-dots button{width:26px;height:4px;border-radius:2px;background:rgba(var(--ink-alt-rgb),.2);transition:.4s var(--ease)}
.loop-dots button.on{background:var(--signal-red);width:40px}
.loop-cycle{font-family:var(--mono);font-size:12.5px;letter-spacing:.13em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.42);margin-left:auto}
@media(prefers-reduced-motion:reduce){.loop-scroll{height:auto}.loop-pin{position:static;height:auto;padding:var(--sec-y) 0}}

/* ============================================================
   PLATFORM ECOSYSTEM — capability map
   ============================================================ */
.filterbar{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:26px}
.filterbar button{font-family:var(--mono);font-size:12.5px;letter-spacing:.07em;padding:8px 14px;border-radius:100px;border:1px solid var(--border-grey);color:var(--ink-soft);background:var(--paper-white);transition:.32s var(--ease)}
.filterbar button:hover{border-color:var(--pku-red-fill);color:var(--pku-red-fill)}
.filterbar button.on{background:var(--ink);border-color:var(--ink);color:var(--warm-ivory)}
.filterbar .count{opacity:.55;margin-left:5px}

.tier-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:12px;margin:clamp(32px,4vw,56px) 0 16px}
.tier-head h3{margin:0}
.tier-head .tn{font-family:var(--mono);font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--pku-red-fill)}
.tier-head .zh-t{font-family:var(--cjk);font-size:13px;color:var(--mineral-grey)}

/* tier 1 — large cards */
.t1{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
@media(max-width:1000px){.t1{grid-template-columns:1fr}}
.pcard1{border:1px solid var(--line);border-radius:5px;overflow:hidden;background:var(--paper-white);display:flex;flex-direction:column;transition:transform .5s var(--ease),box-shadow .5s var(--ease)}
.pcard1:hover{transform:translateY(-4px);box-shadow:0 20px 50px rgba(20,16,15,.12)}
.pcard1 .media{aspect-ratio:16/9;background:linear-gradient(150deg,var(--plate-dark),var(--deep-red) 72%,var(--pku-red-fill));display:grid;place-items:center;position:relative}
.pcard1 .media .ph{font-family:var(--mono);font-size:12.5px;letter-spacing:.15em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.42)}
.pcard1 .body{padding:22px 21px;display:flex;flex-direction:column;gap:10px;flex:1}
.pcard1 h4{margin:0;font-size:1.16rem;letter-spacing:-.022em;font-weight:660}
.pcard1 .zh-t{font-family:var(--cjk);font-size:12.5px;color:var(--mineral-grey)}
.pcard1 .claim{margin:0;font-family:var(--serif);font-size:15px;line-height:1.46}
.pcard1 .foot{margin-top:auto;padding-top:12px;display:flex;align-items:center;justify-content:space-between;gap:10px}

/* tier 2 — medium cards */
.t2{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--line);border:1px solid var(--line);border-radius:4px;overflow:hidden}
@media(max-width:960px){.t2{grid-template-columns:repeat(2,1fr)}}
@media(max-width:620px){.t2{grid-template-columns:1fr}}
.pcard2{background:var(--paper-white);padding:24px 22px;display:flex;flex-direction:column;gap:10px;min-height:210px;transition:background .45s}
.pcard2:hover{background:var(--warm-ivory)}
.pcard2 .grp{font-family:var(--mono);font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--pku-red-fill)}
.pcard2 h4{margin:0;font-size:1.06rem;letter-spacing:-.02em;font-weight:650}
.pcard2 .zh-t{font-family:var(--cjk);font-size:12.5px;color:var(--mineral-grey)}
.pcard2 p{margin:0;font-size:13.2px;line-height:1.58;color:var(--ink-soft);flex:1}
.pcard2 .foot{margin-top:auto;font-family:var(--mono);font-size:12.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--pku-red-fill)}

/* tier 3 — compact list */
.t3{border:1px solid var(--line);border-radius:4px;overflow:hidden}
.prow{display:grid;grid-template-columns:minmax(0,.26fr) minmax(0,.5fr) minmax(0,.24fr);gap:18px;
  padding:16px 20px;border-bottom:1px solid var(--line);align-items:center;background:var(--paper-white);transition:background .4s}
.prow:last-child{border-bottom:0}
.prow:hover{background:var(--warm-ivory)}
.prow .nm{display:flex;flex-direction:column;gap:2px}
.prow .nm b{font-size:14.6px;font-weight:650;letter-spacing:-.015em}
.prow .nm span{font-family:var(--cjk);font-size:12.5px;color:var(--mineral-grey)}
.prow .cl{font-size:13px;line-height:1.5;color:var(--ink-soft)}
.prow .meta{display:flex;align-items:center;justify-content:flex-end;gap:8px}
@media(max-width:820px){.prow{grid-template-columns:1fr;gap:8px}.prow .meta{justify-content:flex-start}}

/* ============================================================
   PLATFORM DETAIL
   ============================================================ */
.pdetail{display:grid;grid-template-columns:minmax(0,.64fr) minmax(0,.36fr);gap:clamp(26px,4.6vw,72px);align-items:start}
@media(max-width:980px){.pdetail{grid-template-columns:1fr}}
.pd-side{position:sticky;top:calc(var(--nav-h) + 26px);display:flex;flex-direction:column;gap:1px;background:var(--line);border:1px solid var(--line);border-radius:4px;overflow:hidden}
@media(max-width:980px){.pd-side{position:static}}
.pd-side .sblock{background:var(--paper-white);padding:18px 19px}
.pd-side h6{margin:0 0 10px;font-family:var(--mono);font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--mineral-grey)}
.pd-side a{display:block;padding:5px 0;font-size:13.4px;font-weight:540;transition:color .3s}
.pd-side a:hover{color:var(--pku-red-fill)}
.pd-side .muted{font-size:12.6px;color:var(--mineral-grey)}
/* Contents of the article beside it, built at runtime from the section
   labels — see railContents() in render.js. A plain list read as filler, so
   it is a rail: one hairline behind the items, a marker that travels to
   whichever section you are in, and rows that step aside on hover. */
.pd-toc{position:relative;padding-left:16px}
.pd-toc::before{
  content:"";
  position:absolute;
  left:0;top:26px;bottom:6px;
  width:1px;
  background:color-mix(in srgb, currentColor 14%, transparent);
}
.pd-toc a{
  position:relative;
  display:flex;
  gap:11px;
  align-items:baseline;
  padding:7px 0;
  color:var(--ink-soft);
  transition:color .32s var(--ease-out),transform .32s var(--ease-out);
}
/* the travelling marker */
.pd-toc a::before{
  content:"";
  position:absolute;
  left:-16px;
  top:50%;
  width:1px;
  height:0;
  margin-top:0;
  background:var(--signal-red);
  transition:height .38s var(--ease-out),margin-top .38s var(--ease-out);
}
.pd-toc a i{
  flex:none;
  width:1.5em;
  font-family:var(--mono);
  font-style:normal;
  font-size:12.5px;
  letter-spacing:.06em;
  color:var(--mineral-grey);
  transition:color .32s var(--ease-out);
}
.pd-toc a:hover{transform:translateX(3px)}
.pd-toc a:hover i{color:var(--signal-red)}
.pd-toc a.is-current{color:var(--pku-red-fill);font-weight:640}
.pd-toc a.is-current i{color:var(--pku-red-fill)}
.pd-toc a.is-current::before{height:16px;margin-top:-8px}
.dark .pd-toc a.is-current,.dark .pd-toc a.is-current i{color:var(--signal-red)}
@media(prefers-reduced-motion:reduce){
  .pd-toc a,.pd-toc a::before,.pd-toc a i{transition:none}
}
.pd-block{margin-bottom:clamp(28px,3.6vw,48px)}
.pd-block > h3{margin:0 0 14px}
.pd-block .lbl{font-family:var(--mono);font-size:12.5px;letter-spacing:.15em;text-transform:uppercase;color:var(--pku-red-fill);margin-bottom:12px}

/* nested tabs (evidence chain) */
.tabs{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:16px}
.tabs button{font-family:var(--mono);font-size:12.5px;letter-spacing:.06em;padding:8px 13px;border-radius:100px;border:1px solid var(--border-grey);color:var(--ink-soft);background:var(--paper-white);transition:.32s var(--ease)}
.tabs button:hover{border-color:var(--pku-red-fill);color:var(--pku-red-fill)}
.tabs button.on{background:var(--ink);border-color:var(--ink);color:var(--warm-ivory)}
.tabpane{display:none}
.tabpane.on{display:block;animation:fadeup .45s var(--ease-out)}
.out{display:flex;align-items:baseline;gap:12px;padding:14px 17px;border-radius:3px;background:rgba(148,7,10,.055);border:1px solid rgba(148,7,10,.18);margin-top:14px}
.out b{font-family:var(--mono);font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--pku-red-fill);flex:0 0 auto}
.out span{font-size:13.4px;font-weight:560}
.srcs{display:flex;flex-wrap:wrap;gap:6px}
.srcs b{font-size:12.5px;font-weight:550;padding:5px 10px;border-radius:3px;background:var(--warm-ivory);border:1px solid var(--line)}
.codeblk{border:1px solid var(--line);border-radius:4px;overflow:hidden;background:var(--carbon-black);margin-top:14px}
.codeblk .cl{display:block;padding:9px 14px;font-family:var(--mono);font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.45);border-bottom:1px solid var(--line-dark)}
.codeblk pre{margin:0;padding:14px;font-family:var(--mono);font-size:12.5px;line-height:1.7;color:#f0c9c7;overflow-x:auto;white-space:pre}
.fmflow{display:flex;flex-wrap:wrap;align-items:stretch;gap:8px}
.fmstep{flex:1 1 165px;padding:14px 15px;border:1px solid var(--line);border-radius:3px;background:var(--warm-ivory);display:flex;flex-direction:column;gap:5px}
.fmstep b{font-size:13.2px;font-weight:645}
.fmstep span{font-size:12.5px;color:var(--ink-soft);line-height:1.42}
.fmstep.hot{background:var(--pku-red-fill);border-color:var(--pku-red-fill);color:#fff}
.fmstep.hot span{color:rgba(255,255,255,.84)}
.fmarrow{display:flex;align-items:center;color:var(--pku-red-fill);font-family:var(--mono);font-size:14px}
@media(max-width:700px){.fmarrow{display:none}}

/* taxonomy ladder */
.tax{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1px;background:var(--line);border:1px solid var(--line);border-radius:4px;overflow:hidden}
@media(max-width:1000px){.tax{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:620px){.tax{grid-template-columns:1fr}}
.taxcol{background:var(--paper-white);padding:19px 18px;display:flex;flex-direction:column;gap:10px}
.taxcol .hd{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.taxcol h5{margin:0;font-size:13.4px;font-weight:645;line-height:1.3}
.taxcol .cnt{font-family:var(--mono);font-size:12.5px;color:var(--pku-red-fill);flex:0 0 auto}
.taxcol .zh-t{font-family:var(--cjk);font-size:12.5px;color:var(--mineral-grey);margin-top:-5px}
.taxcol ul{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:4px}
.taxcol li{font-size:12.5px;line-height:1.45;color:var(--ink-soft);padding-left:12px;position:relative}
.taxcol li::before{content:'';position:absolute;left:0;top:6px;width:4px;height:4px;border-radius:50%;background:var(--pku-red-fill);opacity:.5}
.taxcol .diff{margin-top:auto;height:3px;border-radius:2px;background:var(--border-grey);overflow:hidden}
.taxcol .diff i{display:block;height:100%;width:0;background:var(--pku-red-fill);transition:width 1.2s var(--ease-out)}

/* funnel + deltas */
.funnel{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1px;background:var(--line);border:1px solid var(--line);border-radius:4px 4px 0 0;overflow:hidden}
@media(max-width:760px){.funnel{grid-template-columns:1fr}}
.fstage{background:var(--paper-white);padding:19px 18px;display:flex;flex-direction:column;gap:7px}
.fstage .s{font-family:var(--mono);font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--pku-red-fill)}
.fstage .who{font-family:var(--mono);font-size:1.55rem;letter-spacing:-.04em;line-height:1}
.fstage h5{margin:0;font-size:13.4px;font-weight:640}
.fstage p{margin:0;font-size:12.5px;line-height:1.5;color:var(--ink-soft)}
.deltas{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1px;background:var(--line);border:1px solid var(--line);border-top:0;overflow:hidden}
@media(max-width:760px){.deltas{grid-template-columns:1fr}}
.delta{background:var(--warm-ivory);padding:17px 18px;display:flex;flex-direction:column;gap:5px}
.delta .k{font-family:var(--mono);font-size:1.1rem;letter-spacing:-.02em}
.delta .v{font-size:12.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--mineral-grey)}

/* residue resolution */
.resz{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1px;background:var(--line);border:1px solid var(--line);border-radius:4px;overflow:hidden}
@media(max-width:880px){.resz{grid-template-columns:1fr}}
.rz{background:var(--paper-white);padding:19px 18px}
.rz .s{font-family:var(--mono);font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--pku-red-fill);display:block;margin-bottom:4px}
.rz h5{margin:0 0 2px;font-size:13.4px;font-weight:640}
.rz .sub{font-size:12.5px;color:var(--mineral-grey);margin-bottom:13px}
.aa{display:grid;grid-template-columns:80px minmax(0,1fr) 42px;gap:9px;align-items:center;margin-bottom:7px;font-family:var(--mono);font-size:12.5px}
.aa .lb{color:var(--ink)}
.aa .tr{height:8px;background:var(--warm-ivory);border-radius:2px;position:relative;overflow:hidden}
.aa .tr::before{content:'';position:absolute;left:50%;top:0;bottom:0;width:1px;background:var(--border-grey)}
.aa .tr i{position:absolute;top:0;bottom:0;width:0;transition:width 1.1s var(--ease-out)}
.aa .tr i.neg{right:50%;background:var(--pku-red-fill)}
.aa .tr i.pos{left:50%;background:var(--ok)}
.aa .vv{text-align:right;color:var(--ink-soft)}
.rz .zoom{margin-top:11px;font-family:var(--mono);font-size:12.5px;letter-spacing:.06em;color:var(--pku-red-fill)}
.rz .concl{margin-top:11px;font-size:12.5px;line-height:1.5;color:var(--ink-soft)}

/* domains / market */
.mkt-row{display:grid;grid-template-columns:auto 1fr auto;gap:16px;align-items:center;padding:15px 0;border-bottom:1px solid var(--line)}
.mkt-row:first-child{border-top:1px solid var(--line)}
.mkt-row .n{font-weight:630;font-size:14.6px}
.mkt-row .d{font-size:12.5px;color:var(--ink-soft);text-align:right}
.mkt-bar{height:3px;background:var(--border-grey);position:relative;border-radius:2px;overflow:hidden}
.mkt-bar i{position:absolute;inset:0 auto 0 0;width:0;background:var(--pku-red-fill);border-radius:2px;transition:width 1.5s var(--ease-out)}

/* ============================================================
   PROJECTS
   ============================================================ */
.proj-lead{border:1px solid var(--line);border-radius:5px;overflow:hidden;background:var(--paper-white);display:grid;grid-template-columns:minmax(0,.42fr) minmax(0,.58fr)}
@media(max-width:900px){.proj-lead{grid-template-columns:1fr}}
.proj-lead .side{background:linear-gradient(155deg,var(--plate-dark) 0%,var(--deep-red) 58%,var(--pku-red-fill) 100%);color:var(--warm-ivory);padding:clamp(24px,3.2vw,42px);display:flex;flex-direction:column;justify-content:space-between;gap:24px}
.proj-lead .crest{font-family:var(--mono);font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.62)}
.proj-lead h3{font-family:var(--cjk);font-size:clamp(1.24rem,2.1vw,1.72rem);line-height:1.36;margin:13px 0 0;font-weight:600}
.proj-lead .en{font-size:12.5px;line-height:1.56;color:rgba(var(--ink-alt-rgb),.6);margin:10px 0 0}
.proj-lead .meta{padding:clamp(24px,3.2vw,42px)}
.mrows{border-top:1px solid var(--line)}
.mrow{display:grid;grid-template-columns:128px minmax(0,1fr);gap:16px;padding:12px 0;border-bottom:1px solid var(--line);align-items:baseline}
.mrow b{font-family:var(--mono);font-size:12.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--mineral-grey);font-weight:500}
.mrow span{font-size:13.8px;line-height:1.5}
@media(max-width:600px){.mrow{grid-template-columns:1fr;gap:3px}}
.proj-band{display:grid;grid-template-columns:minmax(0,.4fr) minmax(0,.6fr);gap:clamp(20px,4vw,56px);align-items:center;
  padding:clamp(26px,3.4vw,44px);border-radius:5px;background:var(--carbon-black);color:var(--warm-ivory);position:relative;overflow:hidden}
@media(max-width:900px){.proj-band{grid-template-columns:1fr}}
.proj-band::after{content:'';position:absolute;inset:0;background:radial-gradient(90% 130% at 90% 10%,rgba(148,7,10,.42),rgba(148,7,10,0) 62%);pointer-events:none}
.proj-band > *{position:relative;z-index:1}

/* ============================================================
   PUBLICATIONS
   ============================================================ */
.featpub{display:grid;grid-template-columns:repeat(2,1fr);gap:1px;background:var(--line-dark);border-top:1px solid var(--line-dark);border-bottom:1px solid var(--line-dark)}
@media(max-width:900px){.featpub{grid-template-columns:1fr}}
.fp{background:var(--carbon-black);color:var(--warm-ivory);padding:clamp(26px,3.6vw,50px);position:relative;overflow:hidden;transition:background .6s var(--ease)}
.fp:hover{background:var(--warm-black-2)}
.fp::before{content:'';position:absolute;left:0;top:0;width:100%;height:2px;background:linear-gradient(90deg,var(--pku-red-fill),var(--signal-red));transform:scaleX(0);transform-origin:left;transition:transform .8s var(--ease-out)}
.fp:hover::before{transform:scaleX(1)}
.fp .vbadge{display:inline-flex;align-items:center;gap:8px;font-family:var(--mono);font-size:12.5px;letter-spacing:.12em;color:var(--signal-red);margin-bottom:18px;text-transform:uppercase}
.fp .vbadge .dot{width:6px;height:6px;border-radius:50%;background:var(--signal-red);animation:pulse 2.2s var(--ease) infinite}
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(1.5)}}
.fp h3{font-family:var(--serif);font-weight:400;font-size:clamp(1.14rem,1.8vw,1.6rem);line-height:1.26;margin:0 0 14px}
.fp p{font-size:14px;line-height:1.68;color:rgba(var(--ink-alt-rgb),.62);margin:0 0 18px}
.fp .authors{font-family:var(--mono);font-size:12.5px;color:rgba(var(--ink-alt-rgb),.45);line-height:1.5}
.fp .q{margin-top:22px;padding:17px 19px;border-left:2px solid var(--signal-red);background:rgba(var(--ink-alt-rgb),.04);font-family:var(--serif);font-size:14.4px;line-height:1.6;color:rgba(var(--ink-alt-rgb),.84)}
.fp .q cite{display:block;margin-top:10px;font-family:var(--mono);font-size:12.5px;font-style:normal;letter-spacing:.07em;color:rgba(var(--ink-alt-rgb),.42)}

.publist{border-top:0}
.pub-head{display:grid;grid-template-columns:64px minmax(0,1fr) 128px 72px;gap:clamp(12px,2.2vw,30px);padding:0 0 10px;border-bottom:1px solid var(--line);color:var(--mineral-grey);font-family:var(--mono);font-size:12.5px;letter-spacing:.08em;line-height:1.3;text-transform:uppercase}
.pub-head-citations{text-align:right}
.pub{display:grid;grid-template-columns:64px minmax(0,1fr) 128px 72px;gap:clamp(12px,2.2vw,30px);
  padding:19px 0;border-bottom:1px solid var(--line);align-items:baseline;transition:background .4s}
.pub:hover{background:var(--warm-ivory)}
.pub .y{font-family:var(--mono);font-size:12.5px;color:var(--pku-red-fill);letter-spacing:.05em}
.pub .t{font-size:15.2px;line-height:1.46;font-weight:545;letter-spacing:-.012em;margin:0 0 6px}
.pub .a{font-size:12.5px;color:var(--mineral-grey);line-height:1.5}
.pub .a u{text-decoration:none;color:var(--ink);font-weight:645;border-bottom:1.5px solid var(--pku-red-fill)}
.pub .j{font-size:12.5px;font-style:italic;color:var(--ink-soft);line-height:1.4}
.pub .c{font-family:var(--mono);font-size:12.5px;color:var(--mineral-grey);text-align:right;white-space:nowrap}
.pub .rel{display:flex;flex-wrap:wrap;gap:5px;margin-top:7px}
.pub .rel a{font-family:var(--mono);font-size:12.5px;letter-spacing:.08em;text-transform:uppercase;padding:4px 9px;border-radius:var(--r-xs);background:var(--warm-ivory);color:var(--ink-soft);transition:.3s}
.pub .rel a:hover{background:var(--pku-red-fill);color:#fff}
@media(max-width:900px){.pub{grid-template-columns:54px minmax(0,1fr)}.pub .j,.pub .c{grid-column:2;text-align:left}.pub .c{color:var(--pku-red-fill)}.pub-head{grid-template-columns:54px minmax(0,1fr)}.pub-head-journal,.pub-head-citations{display:none}}
.statbar{display:flex;flex-wrap:wrap;gap:22px;align-items:center;margin-top:20px}
.statbar .st b{font-family:var(--mono);font-size:19px;letter-spacing:-.03em;display:block;line-height:1}
.statbar .st span{font-size:12.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--mineral-grey);display:block;margin-top:5px}

/* ============================================================
   PEOPLE
   ============================================================ */
.pi-grid{display:grid;grid-template-columns:minmax(0,.4fr) minmax(0,.6fr);gap:clamp(26px,4.6vw,70px)}
@media(max-width:900px){.pi-grid{grid-template-columns:1fr}}
.portrait{aspect-ratio:4/5;border-radius:5px;position:relative;overflow:hidden;background:linear-gradient(155deg,var(--plate-dark) 0%,var(--deep-red) 56%,var(--pku-red-fill) 100%);display:grid;place-items:center;color:var(--warm-ivory)}
.portrait canvas{position:absolute;inset:0;width:100%;height:100%;opacity:.5}
.portrait .ini{position:relative;font-family:var(--serif);font-size:clamp(3rem,6.4vw,5.4rem);letter-spacing:-.04em;opacity:.95}
.portrait .cap{position:absolute;left:0;right:0;bottom:0;padding:16px 19px;background:linear-gradient(0deg,rgba(var(--shadow-rgb),calc(.72 * var(--shadow-k))),transparent);font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.8)}
.pi-name{display:flex;align-items:baseline;gap:13px;flex-wrap:wrap;margin-bottom:8px}
.pi-name h1,.pi-name h3{font-size:clamp(1.5rem,2.5vw,2.2rem);letter-spacing:-.03em;margin:0;font-weight:660}
.pi-name .zh{font-family:var(--cjk);font-size:clamp(1.2rem,1.9vw,1.6rem);color:var(--pku-red-fill);font-weight:600}
.profile-role{margin:5px 0 0;font-size:14px;line-height:1.45;color:var(--pku-red-fill);letter-spacing:.02em}
.tl{margin:0;padding:0;list-style:none;position:relative}
.tl::before{content:'';position:absolute;left:5px;top:6px;bottom:6px;width:1px;background:var(--border-grey)}
.tl li{position:relative;padding:0 0 17px 26px}
.tl li::before{content:'';position:absolute;left:0;top:6px;width:11px;height:11px;border-radius:50%;background:var(--paper-white);border:1.5px solid var(--mineral-grey)}
.tl li.now::before{background:var(--pku-red-fill);border-color:var(--pku-red-fill);box-shadow:0 0 0 4px rgba(148,7,10,.14)}
.tl .when{font-family:var(--mono);font-size:12.5px;color:var(--pku-red-fill);letter-spacing:.05em;display:block;margin-bottom:3px}
.tl .what{font-size:14.2px;font-weight:560;line-height:1.4}
.tl .where{font-size:12.8px;color:var(--ink-soft)}
.rows{border-top:1px solid var(--line)}
.rows a,.rows div{display:flex;gap:16px;padding:12px 0;border-bottom:1px solid var(--line);font-size:13.8px;align-items:baseline;transition:color .3s}
.rows a:hover{color:var(--pku-red-fill)}
.rows b{font-family:var(--mono);font-size:12.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--mineral-grey);flex:0 0 92px;font-weight:500}
.member-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line);border:1px solid var(--line);border-radius:4px;overflow:hidden}
@media(max-width:1000px){.member-grid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:560px){.member-grid{grid-template-columns:1fr}}
.mcard{background:var(--paper-white);padding:0;display:flex;flex-direction:column;transition:background .45s}
.mcard:hover{background:var(--warm-ivory)}
.mcard .ph{aspect-ratio:1;background:var(--warm-ivory);display:grid;place-items:center;font-family:var(--serif);font-size:2rem;color:var(--mineral-grey)}
.mcard .b{padding:16px 17px;display:flex;flex-direction:column;gap:5px}
.mcard h5{margin:0;font-size:14.6px;font-weight:645}
.mcard .r{display:block;font-size:12.5px;color:var(--pku-red-fill);font-family:var(--mono);letter-spacing:.06em;line-height:1.45;overflow-wrap:anywhere}
.mcard p{margin:0;font-size:12.5px;color:var(--ink-soft);line-height:1.5;white-space:pre-line}
.roles{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line);border:1px solid var(--line);border-radius:4px;overflow:hidden}
@media(max-width:1000px){.roles{grid-template-columns:repeat(2,1fr)}}
@media(max-width:560px){.roles{grid-template-columns:1fr}}
.rcard{background:var(--paper-white);padding:23px 21px;min-height:172px;display:flex;flex-direction:column;gap:9px;transition:background .45s}
.rcard:hover{background:var(--warm-ivory)}
.rcard .cnt{font-family:var(--mono);font-size:1.8rem;letter-spacing:-.04em;line-height:1;color:var(--pku-red-fill)}
.rcard h4{margin:0;font-size:14.4px}
.rcard .zh-t{font-family:var(--cjk);font-size:12.5px;color:var(--mineral-grey)}
.rcard p{margin:0;font-size:12.5px;color:var(--ink-soft);line-height:1.54}
.rcard .open{margin-top:auto;font-family:var(--mono);font-size:12.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--pku-red-fill)}

/* empty-state (only where a page must exist) */
.empty{border:1px dashed var(--border-grey);border-radius:5px;padding:clamp(30px,5vw,60px);text-align:center;background:var(--paper-white)}
.empty .ei{font-family:var(--mono);font-size:12.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--mineral-grey);margin-bottom:12px}
.empty h3{margin:0 0 10px}
.empty p{max-width:52ch;margin:0 auto;font-size:13.8px;color:var(--ink-soft)}
.dark .empty{border-color:var(--line-dark);background:rgba(var(--ink-alt-rgb),.03)}

/* ============================================================
   NEWS
   ============================================================ */
.newsgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:var(--line);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
@media(max-width:960px){.newsgrid{grid-template-columns:1fr}}
.ncard{background:var(--paper-white);padding:clamp(22px,2.8vw,34px);display:flex;flex-direction:column;gap:12px;min-height:216px;transition:background .45s}
.ncard:hover{background:var(--warm-ivory)}
.ncard .meta{display:flex;align-items:center;gap:10px}
.ncard .type{font-family:var(--mono);font-size:12.5px;letter-spacing:.13em;text-transform:uppercase;padding:3px 8px;border-radius:2px;background:rgba(148,7,10,.08);color:var(--pku-red-fill)}
.ncard .d{font-family:var(--mono);font-size:12.5px;color:var(--mineral-grey);letter-spacing:.06em}
.ncard h4{margin:0;font-size:clamp(1rem,1.2vw,1.14rem);line-height:1.36;letter-spacing:-.015em;font-weight:620}
.ncard p{margin:0;font-size:13.2px;line-height:1.6;color:var(--ink-soft);flex:1}
.ncard .src{font-family:var(--mono);font-size:12.5px;letter-spacing:.06em;color:var(--mineral-grey)}
.ncard:hover .src{color:var(--pku-red-fill)}
.newslist{border-top:1px solid var(--line)}
.nrow{display:grid;grid-template-columns:104px 108px minmax(0,1fr);gap:20px;padding:20px 0;border-bottom:1px solid var(--line);align-items:baseline;transition:background .4s}
.nrow:hover{background:var(--warm-ivory)}
.nrow h4{margin:0 0 6px;font-size:15.4px;font-weight:600;line-height:1.4}
.nrow p{margin:0;font-size:13.2px;line-height:1.58;color:var(--ink-soft);max-width:70ch}
@media(max-width:820px){.nrow{grid-template-columns:1fr;gap:7px}}

/* ============================================================
   JOIN
   ============================================================ */
.join-grid{display:grid;grid-template-columns:minmax(0,.4fr) minmax(0,.6fr);gap:clamp(26px,4.6vw,76px)}
@media(max-width:900px){.join-grid{grid-template-columns:1fr}}
.acc{border:1px solid var(--surface-line);border-radius:var(--r-sm);overflow:hidden;margin-bottom:clamp(8px,.9vw,12px);background:var(--surface-fill);-webkit-backdrop-filter:blur(var(--surface-blur)) saturate(1.06);backdrop-filter:blur(var(--surface-blur)) saturate(1.06)}
.acc > button{width:100%;display:flex;align-items:center;gap:14px;padding:18px 21px;font-size:15.4px;font-weight:620;letter-spacing:-.015em}
.acc .plus{margin-left:auto;font-family:var(--mono);font-size:18px;color:var(--signal-red);transition:transform .45s var(--ease)}
.acc.open .plus{transform:rotate(135deg)}
.acc .zh-t{font-family:var(--cjk);font-size:12.5px;font-weight:400;color:rgba(var(--ink-alt-rgb),.45)}
.acc .st{font-family:var(--mono);font-size:12.5px;letter-spacing:.12em;text-transform:uppercase;padding:4px 10px;border-radius:var(--r-xs);background:rgba(148,7,10,.24);color:var(--accent-red)}
.accbody{max-height:0;overflow:hidden;transition:max-height .55s var(--ease)}
.accbody-in{padding:0 21px 22px;font-size:13.8px;line-height:1.75;color:rgba(var(--ink-alt-rgb),.66)}
.accbody-in h6{margin:15px 0 7px;font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--signal-red);font-weight:600}
.accbody-in ul{margin:0;padding-left:17px}
.accbody-in li{margin-bottom:5px}
.accbody-in p{margin:0}

/* ============================================================
   FOOTER
   ============================================================ */
footer{background:var(--carbon-black);color:var(--warm-ivory);padding:clamp(50px,6.4vw,86px) 0 30px;border-top:1px solid var(--line-dark)}
.f-top{display:grid;grid-template-columns:minmax(0,1.35fr) repeat(2,minmax(0,.7fr));gap:clamp(24px,4vw,54px);padding-bottom:clamp(32px,4.4vw,56px)}
@media(max-width:960px){.f-top{grid-template-columns:1fr 1fr;gap:30px}}
@media(max-width:560px){.f-top{grid-template-columns:1fr}}
.f-brand .mark{margin-bottom:16px}
.f-brand p{font-size:14.5px;line-height:1.72;color:rgba(var(--ink-alt-rgb),.56);max-width:36ch;margin:0}
.f-col h6{margin:0 0 14px;font-family:var(--mono);font-size:13.5px;letter-spacing:.16em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.4);font-weight:600}
.f-col a,.f-col span{display:block;font-size:14.5px;color:rgba(var(--ink-alt-rgb),.72);padding:5px 0;transition:color .3s}
.email-link{cursor:default}
[data-email-link]{cursor:pointer}
.f-col .email-link{white-space:nowrap}
.f-col .email-link .email-at{display:inline;padding:0;color:inherit;font-size:inherit}
.f-col a:hover{color:var(--signal-red)}
.f-bot{display:flex;flex-wrap:wrap;gap:11px 24px;align-items:center;padding-top:23px;border-top:1px solid var(--line-dark);font-size:13.5px;color:rgba(var(--ink-alt-rgb),.72);letter-spacing:.04em}
.f-bot .sep{margin-left:auto}
.footer-contact-link{display:inline-flex;color:var(--accent-red);font-weight:650;white-space:nowrap;transition:color .3s,transform .3s var(--ease)}
.footer-contact-link:hover{color:var(--signal-red);transform:translateX(2px)}
.totop{position:fixed;right:20px;bottom:20px;z-index:150;width:44px;height:44px;border-radius:50%;background:var(--pku-red-fill);color:#fff;display:grid;place-items:center;opacity:0;pointer-events:none;transform:translateY(12px);transition:.45s var(--ease);box-shadow:0 8px 26px rgba(148,7,10,.34)}
.totop.on{opacity:1;pointer-events:auto;transform:none}
.totop:hover{background:var(--signal-red)}


/* ============================================================
   LANGUAGE
   English is the default. Chinese is not shown alongside it —
   the reader switches, and the whole page switches with them.
     .zh-only  visible only in Chinese
     .en-only  visible only in English
     .zh       an inline Chinese run inside English text
               (proper nouns: 喻航 / 北京大学城市与环境学院 / 待遇条款)
   Elements carrying data-en + data-zh are swapped in place by
   render.js — never stacked.
   ============================================================ */
/* The gate must beat component styling. `.zh-only` is one class, so any
   component rule like `.mega-col a .sub{display:block}` used to out-specify
   it and print Chinese into the English page. Hiding is therefore forced;
   showing stays at normal weight so components keep control of block vs
   inline. */
.zh-only{display:none}
html:not([lang="zh"]) .zh-only{display:none !important}
html[lang="zh"] .zh-only{display:block}
html[lang="zh"] span.zh-only,html[lang="zh"] .zh-only.inline{display:inline}
html[lang="zh"] .en-only{display:none !important}

/* In Chinese the whole type system changes face, and metrics tuned
   for Latin are relaxed for CJK. */
html[lang="zh"] body{font-family:var(--cjk);letter-spacing:.01em}
html[lang="zh"] .display,
html[lang="zh"] .hero h1,
html[lang="zh"] .q-panel .qq,
html[lang="zh"] .fp h3,
html[lang="zh"] .fjudge,
html[lang="zh"] .mission .q,
html[lang="zh"] .pane .claim,
html[lang="zh"] .pcard1 .claim,
html[lang="zh"] .proj-lead h3{font-family:var(--cjk-serif);letter-spacing:0;line-height:1.44}
html[lang="zh"] .h1,html[lang="zh"] .h2,html[lang="zh"] .h3,html[lang="zh"] .h4{
  font-family:var(--cjk);letter-spacing:-.004em;line-height:1.26;font-weight:650}
html[lang="zh"] .hero h1{font-size:clamp(1.36rem,min(2.42vw,4vh),2.5rem);max-width:26em}
html[lang="zh"] .h1{font-size:clamp(1.7rem,3.4vw,2.9rem)}
html[lang="zh"] .lede,html[lang="zh"] .body-txt{line-height:1.88;letter-spacing:.012em}
html[lang="zh"] .body-txt{max-width:37em}
html[lang="zh"] .bullets li,html[lang="zh"] .cell p,html[lang="zh"] .small{line-height:1.82}
html[lang="zh"] .eyebrow,html[lang="zh"] .crumb,html[lang="zh"] .tier-head .tn,
html[lang="zh"] .cell .cn,html[lang="zh"] .pd-block .lbl,html[lang="zh"] .f-col h6{letter-spacing:.1em}
html[lang="zh"] .hero-kicker{letter-spacing:.08em}
/* numerals and identifiers stay Latin in both languages */
.mono,.hero-strip .k,.cell .big,.delta .k,.fstage .who,.aa,
.statbar .st b,.rcard .cnt,.pub .y,.pub .c,.tl .when{font-family:var(--mono)}

/* ---------- real product recordings ---------- */
.pmedia{position:relative;border-radius:5px;overflow:hidden;background:var(--carbon-black);aspect-ratio:16/9;border:1px solid var(--line)}
.pmedia video,.pmedia img{width:100%;height:100%;object-fit:cover;display:block}
.pmedia figcaption{position:absolute;left:0;right:0;bottom:0;padding:12px 15px;margin:0;
  background:linear-gradient(0deg,rgba(var(--ground-2-rgb),.84),rgba(var(--ground-2-rgb),0));
  font-family:var(--mono);font-size:12.5px;letter-spacing:.13em;text-transform:uppercase;color:rgba(var(--ink-alt-rgb),.82)}
.pmedia .vtoggle{position:absolute;top:11px;right:11px;width:30px;height:30px;border-radius:50%;
  background:rgba(var(--ground-2-rgb),.6);color:var(--warm-ivory);display:grid;place-items:center;
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);opacity:.85;transition:.3s}
.pmedia .vtoggle:hover{opacity:1;background:var(--pku-red-fill)}
.pcard1 .media video,.pcard1 .media img{width:100%;height:100%;object-fit:cover}

/* ---------- benchmark bars ---------- */
.bbar{display:grid;grid-template-columns:minmax(0,1fr) 54px;gap:12px;align-items:center;margin-bottom:10px}
.bbar .bn{font-size:12.8px;line-height:1.3}
.bbar .bn em{font-style:normal;font-family:var(--mono);font-size:12.5px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--mineral-grey);margin-left:7px}
.bbar .bv{font-family:var(--mono);font-size:12.5px;text-align:right}
.bbar .btr{grid-column:1/-1;height:5px;border-radius:3px;background:var(--border-grey);overflow:hidden;margin-top:-3px}
.bbar .btr i{display:block;height:100%;width:0;background:var(--pku-red-fill);transition:width 1.2s var(--ease-out)}
.bbar.me .btr i{background:var(--signal-red)}
.bbar.me .bn,.bbar.me .bv{font-weight:660}
.cov{display:grid;grid-template-columns:minmax(0,1fr) 36px;gap:10px;align-items:center;margin-bottom:8px}
.cov .cn2{font-size:12.5px}
.cov .cv{font-family:var(--mono);font-size:12.5px;text-align:right;color:var(--mineral-grey)}
.cov .ctr{grid-column:1/-1;height:4px;border-radius:2px;background:var(--border-grey);overflow:hidden;margin-top:-3px}
.cov .ctr i{display:block;height:100%;width:0;background:var(--mineral-grey);transition:width 1.1s var(--ease-out)}
.cov.me .ctr i{background:var(--pku-red-fill)}
.cov.me .cn2{font-weight:660}
.cov.me .cv{color:var(--pku-red-fill)}

/* ============================================================
   PINNED RAILS
   Two pages put a short column beside a very long one: Join Us
   (a 4-line intro next to ~3,200px of open positions) and a
   person profile (portrait + contact next to a full CV). Left
   static, that column simply ran out and left thousands of
   pixels of empty ground scrolling past. Pinning turns the gap
   into structure — the rail stays with the reader instead.
   ============================================================ */
.join-grid > *:first-child{
  position:sticky;
  top:calc(var(--nav-h) + 30px);
  align-self:start;
}
/* The profile rail is taller than a laptop viewport, so the portrait
   scrolls away naturally and only the compact contact block pins.
   The column keeps its default stretch so the sticky range covers the
   whole CV. */
.pi-grid > *:first-child{align-self:stretch}
.pi-grid .rows{
  position:sticky;
  top:calc(var(--nav-h) + 30px);
}
@media(max-width:900px){
  .join-grid > *:first-child,
  .pi-grid .rows{position:static;top:auto}
}
@media(prefers-reduced-motion:reduce){
  .join-grid > *:first-child,
  .pi-grid .rows{position:static;top:auto}
}

/* Positions index inside the pinned Join Us rail. */
.join-toc{margin-top:clamp(28px,3.4vw,44px);padding-top:20px;border-top:1px solid var(--line)}
.join-toc h6{margin:0 0 10px;font-family:var(--mono);font-size:12.5px;letter-spacing:.15em;text-transform:uppercase;color:var(--mineral-grey);font-weight:600}

/* ============================================================
   SURFACES
   The site drew almost every group as a 1px gap over a coloured
   backdrop — twelve of them — so a page of cards read as a wire
   diagram. Groups are now real gaps between real surfaces, each
   with its own radius, and the hairline is on the card rather
   than showing through it.
   ============================================================ */
.surface,
.grid2 > *, .grid3 > *, .grid4 > *,
.t2 > *, .tax > *, .resz > *,
.member-grid > *, .roles > *, .newsgrid > *,
.pd-side > .sblock{
  border-radius:var(--r-sm);
  border:1px solid var(--surface-line);
  background:var(--surface-fill);
  -webkit-backdrop-filter:blur(var(--surface-blur)) saturate(1.06);
  backdrop-filter:blur(var(--surface-blur)) saturate(1.06);
  transition:background .4s var(--ease-out),border-color .4s var(--ease-out),transform .4s var(--ease-out);
}
a.surface:hover,
.grid2 > a:hover, .grid3 > a:hover, .grid4 > a:hover,
.t2 > a:hover, .member-grid > a:hover, .roles > a:hover, .newsgrid > a:hover{
  background:var(--surface-fill-hover);
  border-color:var(--surface-line-hover);
  transform:translateY(-2px);
}

/* the groups themselves become plain gapped grids */
.grid2,.grid3,.grid4,
.t2,.tax,.resz,.member-grid,.roles,.newsgrid{
  gap:clamp(10px,1.1vw,16px);
  background:none;
  border:0;
  border-radius:0;
  overflow:visible;
}
.funnel,.deltas{
  gap:clamp(10px,1.1vw,16px);
  background:none;
  border:0;
  overflow:visible;
}
.funnel > *,.deltas > *{
  border-radius:var(--r-sm);
  border:1px solid var(--surface-line);
  background:var(--surface-fill);
}
.deltas{border-top:0}

/* cells keep their own padding but lose the flat fill they no longer need */
.cell,.pcard2{background:transparent;padding:clamp(18px,1.7vw,24px) clamp(17px,1.6vw,22px)}
.dark .cell{background:transparent}
.pcard2:hover{background:var(--surface-fill-hover)}

/* the sticky rail: separate panels rather than one boxed stack */
.pd-side{
  gap:clamp(8px,.9vw,12px);
  background:none;
  border:0;
  border-radius:0;
  overflow:visible;
}
.pd-side .sblock{background:var(--surface-fill);padding:18px 19px}

/* the loop strip keeps its single frame but gains a real radius */
.loop-steps{gap:clamp(8px,.9vw,14px);background:none;border:0;overflow:visible}
.loop-steps > *{border-radius:var(--r-sm);border:1px solid var(--surface-line);background:var(--surface-fill)}

/* featured publications: two glass cards, and one card when there is one */
.featpub{
  gap:clamp(12px,1.3vw,18px);
  background:none;
  border:0;
}
.fp{
  border-radius:var(--r-md);
  border:1px solid var(--surface-line);
  background:var(--surface-fill);
  -webkit-backdrop-filter:blur(var(--surface-blur)) saturate(1.06);
  backdrop-filter:blur(var(--surface-blur)) saturate(1.06);
}
.fp:hover{background:var(--surface-fill-hover);border-color:var(--surface-line-hover)}
/* A single featured paper used to leave half the row as exposed backdrop —
   the "empty preview panel". It now runs the full measure and reads across.

   The switch is a class the renderer sets, not `:has(> :only-child)`. The
   selector was correct and fragile: it asks the DOM how many cards there are,
   and any injected sibling answers for it. A reader with a DOI-rewriting
   extension got exactly that — one element appended, `:only-child` stops
   matching, and the layout collapses back to two columns with the second one
   empty. The renderer already knows the count; it says so. */
.featpub-solo{grid-template-columns:minmax(0,1fr)}
@media(min-width:901px){
  .featpub-solo .fp{
    display:grid;
    grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
    gap:clamp(26px,3vw,54px);
    align-items:start;
    align-content:start;
  }
  /* Rows are stated rather than left to auto-placement. With only the columns
     assigned, the left column ran out after the title while the right column
     kept going — on a direction whose single featured paper carries no figure,
     that left most of the card empty. The pull quote moves under the title,
     which is where it belongs anyway, and the two columns end together. */
  .featpub-solo .fp > .vbadge{grid-column:1;grid-row:1}
  .featpub-solo .fp > h3{grid-column:1;grid-row:2}
  .featpub-solo .fp > .q{grid-column:1;grid-row:3;margin-top:clamp(24px,2.4vw,38px)}
  .featpub-solo .fp > p{grid-column:2;grid-row:2}
  .featpub-solo .fp > .authors{grid-column:2;grid-row:3;margin-top:clamp(24px,2.4vw,38px)}
  /* With a figure the left column is the figure, so the original split stands:
     everything written goes right, in the order it was written. */
  .featpub-solo .fp.has-fig > .vbadge{grid-row:auto}
  .featpub-solo .fp.has-fig > h3,
  .featpub-solo .fp.has-fig > p,
  .featpub-solo .fp.has-fig > .authors,
  .featpub-solo .fp.has-fig > .q{grid-row:auto;margin-top:0}
}

/* buttons: the outline button becomes glass, both get a real radius */
.btn{border-radius:var(--r-sm);padding:13px 24px}
.btn-line{
  border:1px solid var(--surface-line);
  background:var(--surface-fill);
  -webkit-backdrop-filter:blur(var(--surface-blur)) saturate(1.06);
  backdrop-filter:blur(var(--surface-blur)) saturate(1.06);
}
.btn-line:hover{
  border-color:var(--surface-line-hover);
  background:var(--surface-fill-hover);
  color:inherit;
  transform:translateY(-2px);
}
.chip,.chip.inline{border-radius:var(--r-xs)}

/* fewer rules in long lists: one hairline, and only between items */
.bullets li{padding-left:18px}
.publist .pub{border-radius:var(--r-sm)}
.publist .pub:hover{background:var(--surface-fill)}

@media(prefers-reduced-motion:reduce){
  .surface,.grid2 > *,.grid3 > *,.grid4 > *,.t2 > *,.member-grid > *,.roles > *,.newsgrid > *,.btn,.fp{
    transition:none;
  }
  a.surface:hover,.grid2 > a:hover,.grid3 > a:hover,.grid4 > a:hover,.btn:hover{transform:none}
}

/* ============================================================
   GLASS, FRAMELESS
   Every surface drops its drawn border. The edge comes from the
   top lip and the shadow instead, which is what stops a page of
   cards reading as a page of boxes.
   ============================================================ */
.surface,
.grid2 > *, .grid3 > *, .grid4 > *,
.t2 > *, .tax > *, .resz > *,
.funnel > *, .deltas > *,
.member-grid > *, .roles > *, .newsgrid > *,
.loop-steps > *,
.pd-side > .sblock,
.fp, .acc, .btn-line, .pcard1{
  border:0;
  background:var(--surface-fill);
  box-shadow:var(--surface-shadow);
  -webkit-backdrop-filter:blur(var(--surface-blur)) saturate(1.12);
  backdrop-filter:blur(var(--surface-blur)) saturate(1.12);
}
a.surface:hover,
.grid2 > a:hover, .grid3 > a:hover, .grid4 > a:hover,
.t2 > a:hover, .member-grid > a:hover, .roles > a:hover, .newsgrid > a:hover,
.fp:hover, .btn-line:hover, .pcard1:hover{
  background:var(--surface-fill-hover);
  box-shadow:var(--surface-shadow-hover);
}

/* ---- navigation dropdown ----
   It was a fully opaque panel with a hairline. It is the one surface that
   always sits over content, so it is the one that most wanted to be glass. */
.mega{
  border:0;
  /* Opaque enough to read against a bright hero. At 58% the molecule showed
     straight through and the items inside stopped being legible; the blur
     still does the work, the fill just has to hold the text. */
  background:color-mix(in srgb, var(--paper-white) 88%, transparent);
  -webkit-backdrop-filter:blur(34px) saturate(1.3);
  backdrop-filter:blur(34px) saturate(1.3);
  box-shadow:
    inset 0 1px 0 var(--surface-lip),
    0 30px 80px -28px rgba(var(--shadow-rgb),calc(.75 * var(--shadow-k)));
}
/* A card sitting on the panel is glass on glass, so the shared 7% fill left
   it indistinguishable from its own background. These get their own, firmer
   values rather than inheriting the page-level surface. */
.mega-card{
  border:0;
  background:color-mix(in srgb, currentColor 9%, transparent);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, currentColor 22%, transparent),
    0 10px 26px -16px rgba(var(--shadow-rgb),calc(.6 * var(--shadow-k)));
}
/* Scoped to the headline: with a second level inside, a plain `:hover` lit the
   whole card whenever the pointer merely passed over a sub-link, so the two
   levels fought each other. The card responds to its own headline; the
   sub-links respond to themselves. */
.mega-card:has(> .mega-card-main:hover),
.mega-card:not(.has-subs):hover{
  background:color-mix(in srgb, currentColor 15%, transparent);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, currentColor 34%, transparent),
    0 16px 34px -16px rgba(var(--shadow-rgb),calc(.7 * var(--shadow-k)));
}
.drawer{
  -webkit-backdrop-filter:blur(30px) saturate(1.3);
  backdrop-filter:blur(30px) saturate(1.3);
}

/* the rail's dividing rule is redundant once the panels are separate cards */
.mega[data-cols="3"] .mega-col:last-child::before{display:none}

@media(prefers-reduced-motion:reduce){
  .surface,.fp,.acc,.btn-line,.mega-card{transition:none}
}

/* ============================================================
   FEATURED PUBLICATION FIGURE
   A figure lifted from the paper, used as the card's anchor.
   In the single-card layout it takes the left column outright;
   in the two-up layout it sits as a band above the text.
   ============================================================ */
.fp{position:relative;overflow:hidden}
.fp-fig{
  display:block;
  position:relative;
  margin:calc(-1 * clamp(26px,3.6vw,50px)) calc(-1 * clamp(26px,3.6vw,50px)) clamp(22px,2.4vw,32px);
  overflow:hidden;
  /* The placeholder is what the reader sees while a lazy figure is still on its
     way, and it used to be --ground-3, which on the dark theme is rgb(10,9,9) —
     darker than the card it sits in and nearly the page itself. A 400px band of
     that reads as a hole in the layout, not as a figure loading. A faint plate,
     lighter than its card, reads as a frame waiting to be filled. */
  background:
    linear-gradient(150deg, rgba(var(--ink-rgb),.11), rgba(var(--ink-rgb),.045) 62%),
    rgb(var(--ground-rgb));
}
.fp-fig img{
  width:100%;
  height:100%;
  object-fit:cover;
  aspect-ratio:16/10;
  transition:transform .8s var(--ease-out),filter .8s var(--ease-out),opacity .45s var(--ease-out);
  filter:saturate(.88) contrast(1.03);
}
/* Arrives rather than appears — and while it has not arrived, the plate above
   is what shows, instead of a half-painted image. Gated on a class the script
   adds, so that if the script never runs the figure is simply visible: a fade
   whose "before" state is invisible must never be the default. */
:root.js-fig-fade .fp-fig img:not([data-loaded]){opacity:0}
.fp:hover .fp-fig img{transform:scale(1.03);filter:saturate(1) contrast(1.05)}
.fp-credit{
  position:absolute;
  left:0;right:0;bottom:0;
  padding:22px 18px 10px;
  background:linear-gradient(0deg,rgba(6,6,6,.86),transparent);
  font-family:var(--mono);
  font-size:12.5px;
  letter-spacing:.05em;
  color:rgba(var(--ink-alt-rgb),.62);
}

/* Single featured paper: figure left, the record right. */
@media(min-width:901px){
  .featpub-solo .fp.has-fig{
    grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr);
    align-items:start;
  }
  .featpub-solo .fp.has-fig .fp-fig{
    grid-column:1;
    grid-row:1 / span 6;
    margin:calc(-1 * clamp(26px,3.6vw,50px)) 0 calc(-1 * clamp(26px,3.6vw,50px)) calc(-1 * clamp(26px,3.6vw,50px));
    align-self:stretch;
    border-radius:0;
  }
  .featpub-solo .fp.has-fig .fp-fig img{
    aspect-ratio:auto;
    min-height:100%;
  }
  .featpub-solo .fp.has-fig .vbadge,
  .featpub-solo .fp.has-fig > h3,
  .featpub-solo .fp.has-fig > p,
  .featpub-solo .fp.has-fig > .authors,
  .featpub-solo .fp.has-fig > .q{grid-column:2}
}
@media(max-width:900px){
  .fp-fig img{aspect-ratio:16/9}
}

/* Mobile drawer: the second level indents under its parent card, one step in,
   so the hierarchy survives the collapse to a single column. */
.drawer-sub-deep{
  display:flex;
  align-items:baseline;
  gap:8px;
  padding-left:20px !important;
  font-size:13px;
  opacity:.82;
}
.drawer-sub-deep::before{
  content:"";
  flex:0 0 auto;
  width:9px;height:1px;
  margin-right:2px;
  background:color-mix(in srgb, currentColor 34%, transparent);
}
.drawer-sub-deep small{
  font-family:var(--mono);
  font-size:12.5px;
  color:var(--mineral-grey);
}
html[lang="zh"] .drawer-sub-deep small{font-family:var(--cjk)}

/* ==========================================================================
   THEME TOGGLE
   Same footprint and same quiet weight as the language switch it sits beside.
   Only one icon is ever shown, and it reflects the theme currently on screen.
   The accessible label still states the action the button will perform.
   ========================================================================== */
.theme-toggle{
  display:grid;
  place-items:center;
  width:34px;
  height:34px;
  padding:0;
  border:0;
  border-radius:100px;
  background:transparent;
  color:inherit;
  cursor:pointer;
  transition:background .28s var(--ease),color .28s var(--ease),transform .28s var(--ease-out);
}
.theme-toggle:hover{
  background:color-mix(in srgb, currentColor 11%, transparent);
  transform:translateY(-1px);
}
.theme-toggle:active{transform:translateY(0)}
.theme-toggle svg{display:block}
.theme-ico{display:none;line-height:0}
.theme-ico-sun{color:#F2EFE8}
.theme-ico-moon{color:#14100F}
/* Light theme → sun. Dark theme → black moon on a pale disc. */
:root[data-theme="light"] .theme-ico-sun{display:block}
:root:not([data-theme="light"]) .theme-ico-moon{display:block}
:root:not([data-theme="light"]) .theme-toggle{background:#F2EFE8}
:root:not([data-theme="light"]) .theme-toggle:hover{background:#fff}

/* The switch itself is a deliberate, visible change of state, so the grounds
   and the type cross over together rather than each component animating on its
   own schedule. Anything already mid-transition is left alone. */
:root.theme-shifting *,
:root.theme-shifting *::before,
:root.theme-shifting *::after{
  transition:background-color .34s var(--ease-soft),
             color .34s var(--ease-soft),
             border-color .34s var(--ease-soft),
             box-shadow .34s var(--ease-soft) !important;
}
@media(prefers-reduced-motion:reduce){
  :root.theme-shifting *,
  :root.theme-shifting *::before,
  :root.theme-shifting *::after{transition:none !important}
}

/* ==========================================================================
   FIXED-GROUND COMPONENTS
   A handful of surfaces keep their colour in both themes: the brand red fills,
   and the gradient plates built on --deep-red. Their type was inheriting the
   themed ink, so on light it turned near-black on a dark red plate. These pin
   the type to the light side regardless of theme, which is the only correct
   answer for a ground that does not move.
   ========================================================================== */
.home-bridge-primary,
.proj-lead .side,
.pcard1 .media,
.q-visual,
.focus-media,
.home-button-solid,
.btn-primary,
.mark,
.totop{
  color:#F5F1EA;
}
.home-bridge-primary h2,
.home-bridge-primary p,
.home-bridge-primary span,
.home-bridge-primary i,
.proj-lead .side *,
.pcard1 .media *,
.q-visual *,
.focus-media *{
  color:inherit;
}
.proj-lead .side p,
.proj-lead .side .crest,
.proj-lead .side .badge{
  color:rgba(245,241,234,.78);
}

/* The rail and node glyphs are accent-red on a red tint. On dark the tint sits
   over near-black so they separate; on light both sit over ivory and the two
   reds collapse into each other. Deepening the glyph restores the step. */
:root[data-theme="light"] .mx-rail-symbol,
:root[data-theme="light"] .mx-node-symbol{
  color:#94070A;
}
/* The foundation mark sits on a fixed dark-red plate in both themes, so it
   takes light type in both — deepening it for light was the wrong call. */
.mx-foundation-mark{color:#F1DCDD}
/* A count on a red fill inherits the themed ink, which on light is near-black. */
.count-hot,
.newsgrid .count,
.filterbar .count.on{color:#F5F1EA}

/* ==========================================================================
   RED AS TYPE
   --pku-red-fill is a fill. Used as a text colour it lands at 2.2:1 on the
   dark ground and 2.1:1 as dark type on a red chip — the stylesheet already
   carried a comment saying as much, but 48 declarations were still doing it.
   --accent-red is the readable member of the family in whichever theme is
   active, so every one of them points there instead.
   ========================================================================== */
.fnum,
.filterbar .count,
.acc .st,
span.y,
.pub .y,
.tl .when,
.newsgrid time,
.newsgrid .when,
.drawer-item .plus,
.mega-foot .arrow-link,
.arrow-link{
  color:var(--accent-red);
}
/* Chips that carry the red fill keep light type in both themes. */
.filterbar button.on,
.filterbar button[aria-pressed="true"],
.pillrow b.hot,
.newsgrid .count.on{
  color:#F5F1EA;
}
/* Status pills use the same PKU-red accent as the rest of the site. */
:root[data-theme="light"] .acc .st{
  background:rgba(148,7,10,.12);
  color:var(--accent-red);
}

/* Last of the red-as-type sites: these were reached through --pku-red-fill on
   page-level overrides rather than the shared rules above. */
.focus-row .fnum,
.tl .when,
.newsgrid .meta span,
.acc .when,
.pd-block .when{color:var(--accent-red)}

/* A chip filled with the brand red carries light type in both themes; the
   count inside it inherits, so it must not fall back to the accent. */
.filterbar button.on,
.filterbar button.on *,
.filterbar button[aria-pressed="true"],
.filterbar button[aria-pressed="true"] *{color:#F5F1EA !important}

/* inner-v2 declares its own filled chip and its own foundation mark later in
   the cascade, so these need the page scope to land. */
body[data-inner-hero] .filterbar button.on,
body[data-inner-hero] .filterbar button.on *{color:#F5F1EA !important}
body[data-inner-hero] .mx-foundation-mark{color:#F1DCDD}

/* ==========================================================================
   .dark SECTIONS ON THE LIGHT THEME
   `.dark` marks a section that inverts against a light page. On the light
   theme that inversion has nowhere to point: keeping it would drop a black
   slab into an ivory page, and letting it half-apply produced light type on a
   light ground. The section simply rejoins the page instead, which is what a
   reader expects when they have asked for a light site.
   ========================================================================== */
:root[data-theme="light"] .dark{
  background:transparent !important;
  color:inherit !important;
}
:root[data-theme="light"] .dark .h1,
:root[data-theme="light"] .dark .h2,
:root[data-theme="light"] .dark .h3,
:root[data-theme="light"] .dark .h4,
:root[data-theme="light"] .dark .display{color:inherit !important}
:root[data-theme="light"] .dark .lede,
:root[data-theme="light"] .dark .small,
:root[data-theme="light"] .dark .body-txt{color:rgba(var(--ink-rgb),.66) !important}
:root[data-theme="light"] .dark .tiny{color:rgba(var(--ink-rgb),.45) !important}
:root[data-theme="light"] .dark .chip{
  color:rgba(var(--ink-rgb),.7) !important;
  background:rgba(var(--ink-rgb),.04) !important;
}
:root[data-theme="light"] .dark .cell{background:transparent !important}

/* ==========================================================================
   DARK PLATES ON THE LIGHT THEME
   Nine of the site's figures are dark-field scientific imagery — average
   luminance 0.02 to 0.24. On the dark theme they sit flush with the page and
   are the reason it looks like a research site. Dropped onto ivory unchanged
   they read as holes punched in the page.

   Regenerating them in a light key was the obvious answer and is the wrong
   one: the darkness is the subject, not the styling — a dark-field micrograph
   lit brightly is no longer a dark-field micrograph. So each figure becomes a
   PLATE instead: a deliberate dark ground, inset from the page, with a hairline
   and a shadow that lifts it. This is how a journal sets the same image on a
   white page, and it reads as intent rather than as an accident.
   ========================================================================== */
:root[data-theme="light"] .focus-media,
:root[data-theme="light"] .fp-fig{
  background:#14100F;
  box-shadow:
    inset 0 0 0 1px rgba(20,16,15,.06),
    0 2px 4px rgba(20,16,15,.05),
    0 18px 44px -22px rgba(20,16,15,.35);
}
/* .home-result is NOT one of these. The other two are figures — an image and
   nothing else — so a dark ground under them is the whole point. This one is a
   card: a micrograph on top, a caption underneath. Painting the card dark took
   the caption with it, and the caption's ink stayed dark, so the title read at
   a contrast ratio of 1.09 — invisible. The plate belongs to the image cell,
   the caption belongs on paper. */
:root[data-theme="light"] .home-result{
  background:var(--home-bg-raised);
  box-shadow:
    inset 0 0 0 1px rgba(20,16,15,.06),
    0 2px 4px rgba(20,16,15,.05),
    0 18px 44px -22px rgba(20,16,15,.35);
}
:root[data-theme="light"] .home-result img{background:#14100F}
/* A hair of breathing room inside the plate, so the image is mounted on it
   rather than bleeding to its edge — the difference between a figure and a
   background photograph. */
:root[data-theme="light"] .focus-media{padding:10px;border-radius:var(--r-sm)}
:root[data-theme="light"] .focus-media img{border-radius:calc(var(--r-sm) - 6px)}
:root[data-theme="light"] .home-result{border-radius:var(--r-sm);overflow:hidden}

/* The featured-publication figure runs to the card edge by design, so it takes
   the plate colour without the inset. */
:root[data-theme="light"] .fp-fig{border-radius:0}
:root[data-theme="light"] .fp.has-fig{
  background:#FFFFFF;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.6),
    0 2px 4px rgba(20,16,15,.04),
    0 18px 44px -24px rgba(20,16,15,.28);
}

/* ========================================================================== 
   LARGE-DISPLAY TYPE SCALING
   The desktop layout is comfortable at the working viewport but becomes too
   delicate on wide monitors. Scale the complete page proportionally at larger
   CSS viewports so type, spacing and controls retain the same hierarchy.
   Mobile and ordinary laptop widths keep their existing proportions.
   ========================================================================== */
@media screen and (min-width:1440px){body{zoom:1.06}}
@media screen and (min-width:1680px){body{zoom:1.11}}
@media screen and (min-width:1920px){body{zoom:1.17}}
@media screen and (min-width:2400px){body{zoom:1.24}}
