/* ========================================
   YAO Pinshuo — Personal HP
   B&W editorial + single accent
   light / dark theme via [data-theme]
   ======================================== */

:root {
  --bg: #fafaf7;
  --fg: #0a0a0a;
  --muted: #6b6b6b;
  --line: #1a1a1a;
  --line-soft: rgba(10, 10, 10, 0.12);
  --accent: #ff5722;
  /* text-sized links and labels: the accent itself is 3.15:1 on the paper,
     below AA for small text; this darker green is 4.8:1. Filled blocks and
     display type keep --accent. */
  --accent-ink: #15803d;
  --paper: #f1efe8;
  --grid: rgba(10, 10, 10, 0.04);
  --warn: #b5442a;

  --font-serif: "Source Serif 4", "Times New Roman", Georgia, serif;
  --font-sans: "Neue Haas Grotesk Display Pro", "Helvetica Neue", Helvetica, "Inter", system-ui, sans-serif;
  --font-mono: "JetBrains Mono", "IBM Plex Mono", "Courier New", monospace;
  --font-jp: "Noto Serif JP", "Yu Mincho", serif;
  /* Japanese running text: mincho is a print face and reads poorly on
     screen at body sizes, so long copy uses a gothic face instead. */
  --font-jp-sans: "Noto Sans JP", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic", "Meiryo", sans-serif;

  --radius: 0px;
  --pad: clamp(20px, 4vw, 56px);
  /* content is centred inside this, so the left and right margins always
     match no matter how wide the window gets */
  --maxw: 1180px;
  --gutter: max(var(--pad), calc((100% - var(--maxw)) / 2));
}

[data-theme="dark"] {
  --bg: #0a0a0a;
  --fg: #f5f5f0;
  --muted: #9a9a95;
  --line: #f5f5f0;
  --line-soft: rgba(245, 245, 240, 0.14);
  --paper: #141413;
  --grid: rgba(245, 245, 240, 0.05);
  --warn: #e8896a;
  --accent-ink: var(--accent);   /* 6:1 on the dark ground already */
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; background: var(--bg); }
body {
  font-family: var(--font-serif);
  background: var(--bg) !important;
  color: var(--fg);
  font-size: 17px;
  line-height: 1.55;
  overflow-x: hidden;
  font-feature-settings: "ss01", "kern", "liga";
  -webkit-font-smoothing: antialiased;
  transition: background 0.4s ease, color 0.4s ease;
}

a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
button { font: inherit; cursor: pointer; background: none; border: none; color: inherit; }

/* paper grain */
body::before {
  content: "";
  position: fixed; inset: 0;
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 40px 40px;
  pointer-events: none;
  z-index: 0;
  opacity: 0.5;
}

/* ========================================
   NAV
   ======================================== */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 18px var(--gutter);
  background: color-mix(in oklab, var(--bg) 78%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line-soft);
  font-family: var(--font-sans);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.nav-mark {
  display: flex; align-items: center; gap: 10px;
  font-weight: 700;
}
.nav-mark .dot { width: 8px; height: 8px; background: var(--accent); border-radius: 50%; }
.nav-links { display: flex; gap: 18px; justify-content: center; flex-wrap: wrap; }
.nav-links a { position: relative; padding: 4px 0; transition: color 0.2s; }
.nav-links a::after {
  content: ""; position: absolute; left: 0; bottom: -2px;
  width: 0; height: 1px; background: var(--accent);
  transition: width 0.3s;
}
.nav-links a:hover::after, .nav-links a.active::after { width: 100%; }
.nav-actions { display: flex; gap: 10px; justify-content: flex-end; align-items: center; }
.lang-pills { display: flex; gap: 1px; border: 1px solid var(--line-soft); }
.lang-pills button {
  padding: 5px 9px; font-size: 11px; letter-spacing: 0.06em;
  font-family: var(--font-mono); background: transparent;
  transition: background 0.2s, color 0.2s;
}
.lang-pills button.active { background: var(--fg); color: var(--bg); }
.theme-btn {
  width: 30px; height: 30px;
  border: 1px solid var(--line-soft);
  display: grid; place-items: center;
  font-size: 13px;
  transition: border-color 0.2s;
}
.theme-btn:hover { border-color: var(--fg); }

/* the ☾ / ☀ glyphs render as colour emoji on iOS without this */
.theme-btn {
  font-variant-emoji: text;
  font-feature-settings: "liga" 0;
}

/* hamburger: hidden until the links collapse. The bars are positioned
   absolutely so the open state lands on an exact X rather than whatever
   a flow layout happens to give. */
.nav-toggle {
  display: none;
  position: relative;
  width: 30px; height: 30px;
  border: 1px solid var(--line-soft);
  padding: 0;
  transition: border-color 0.2s;
}
.nav-toggle span {
  position: absolute;
  left: 7px; right: 7px; height: 1px;
  background: var(--fg);
  transition: transform 0.25s ease, opacity 0.18s ease;
}
.nav-toggle span:nth-child(1) { top: 10px; }
.nav-toggle span:nth-child(2) { top: 14px; }
.nav-toggle span:nth-child(3) { top: 18px; }
.nav-toggle:hover { border-color: var(--fg); }
.nav.menu-open .nav-toggle { border-color: var(--fg); }
.nav.menu-open .nav-toggle span:nth-child(1) { transform: translateY(4px) rotate(45deg); }
.nav.menu-open .nav-toggle span:nth-child(2) { opacity: 0; }
.nav.menu-open .nav-toggle span:nth-child(3) { transform: translateY(-4px) rotate(-45deg); }

@media (max-width: 1023px) {
  .nav { grid-template-columns: 1fr auto; }
  .nav-toggle { display: block; }
  /* the links become a panel that drops out of the bar */
  .nav-links {
    display: grid;
    position: absolute;
    top: 100%; left: 0; right: 0;
    grid-template-columns: 1fr 1fr;
    gap: 0;
    /* fully opaque: the hero showed straight through a translucent panel */
    background: var(--bg);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 12px 28px -18px rgba(0, 0, 0, 0.4);
    padding: 0 var(--gutter);
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    /* visibility keeps the eight collapsed links out of the tab order */
    visibility: hidden;
    transition: max-height 0.3s ease, opacity 0.22s ease, padding 0.3s ease, visibility 0.3s;
  }
  .nav.menu-open .nav-links {
    max-height: 60vh;
    opacity: 1;
    visibility: visible;
    padding: 6px var(--gutter) 14px;
  }
  .nav-links a {
    padding: 13px 0;
    border-bottom: 1px solid var(--line-soft);
    font-size: 12px;
  }
  .nav-links a:nth-last-child(-n+2) { border-bottom: none; }
  .nav-links a:nth-child(odd) { padding-right: 12px; }
  .nav-links a:nth-child(even) { padding-left: 12px; border-left: 1px solid var(--line-soft); }
  .nav-links a::after { display: none; }
}

/* ========================================
   GENERIC SECTION
   ======================================== */
section {
  position: relative;
  padding: clamp(64px, 9vh, 104px) var(--gutter);
  z-index: 1;
  /* anchor jumps land with the eyebrow clear of the fixed nav */
  scroll-margin-top: 48px;
}
.eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 24px;
}
.eyebrow::before {
  content: ""; width: 28px; height: 1px; background: var(--fg);
}
.section-title {
  font-family: var(--font-serif);
  font-size: clamp(34px, 4.4vw, 62px);
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.04;
  margin-bottom: 50px;
  font-style: italic;
}
.section-title em { font-style: normal; }
.divider {
  height: 1px; background: var(--line-soft);
  margin: 60px 0;
}

/* ========================================
   HERO
   ======================================== */
.hero {
  min-height: 100vh;
  min-height: 100svh;
  padding-top: 108px;
  display: grid;
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.9fr);
  gap: clamp(20px, 4vw, 80px);
  align-items: center;
  position: relative;
}
.hero-left { position: relative; z-index: 2; }
.hero-meta {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 30px;
  display: flex; flex-wrap: wrap; gap: 20px; align-items: center;
}
.hero-meta > span { white-space: nowrap; }
.hero-meta .live {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--accent);
  animation: pulse 1.6s infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 var(--accent); }
  50% { opacity: 0.6; box-shadow: 0 0 0 6px transparent; }
}

.hero-name {
  font-family: var(--font-serif);
  font-size: clamp(46px, 7vw, 104px);
  font-weight: 400;
  letter-spacing: -0.035em;
  line-height: 0.9;
  margin-bottom: 8px;
}
.hero-name .ital { font-style: italic; color: var(--accent); }
.hero-name-cn {
  font-family: var(--font-jp);
  font-size: clamp(17px, 1.9vw, 24px);
  letter-spacing: 0.36em;
  margin-top: 14px;
  margin-bottom: 22px;
  color: var(--muted);
  font-weight: 400;
}

.hero-typer {
  font-family: var(--font-mono);
  font-size: clamp(14px, 1.4vw, 17px);
  letter-spacing: 0.02em;
  margin-bottom: 36px;
  min-height: 1.6em;
}
.hero-typer .prompt { color: var(--accent); margin-right: 8px; }
.hero-typer .caret {
  display: inline-block; width: 8px; height: 1.1em;
  background: var(--fg); margin-left: 3px;
  vertical-align: -2px;
  animation: blink 1s infinite;
}
@keyframes blink { 50% { opacity: 0; } }

.hero-desc {
  font-family: var(--font-serif);
  font-size: clamp(16px, 1.4vw, 19px);
  line-height: 1.7;
  max-width: none;
  color: var(--fg);
  margin-bottom: 36px;
  text-wrap: pretty;
}
.hero-desc .key {
  background: linear-gradient(transparent 65%, color-mix(in oklab, var(--accent) 35%, transparent) 65%);
  padding: 0 2px;
}

.hero-cta {
  display: flex; gap: 14px; flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.btn {
  display: inline-flex; align-items: center; gap: 12px;
  padding: 14px 22px;
  border: 1px solid var(--fg);
  transition: all 0.25s;
  position: relative;
  overflow: hidden;
}
.btn::before {
  content: ""; position: absolute; inset: 0;
  background: var(--fg);
  transform: translateX(-101%);
  transition: transform 0.35s cubic-bezier(0.65,0,0.35,1);
  z-index: -1;
}
.btn:hover { color: var(--bg); }
.btn:hover::before { transform: translateX(0); }
.btn.primary { background: var(--fg); color: var(--bg); }
.btn.primary::before { background: var(--accent); }
.btn.primary:hover { color: var(--bg); border-color: var(--accent); }
.btn .arrow { transition: transform 0.25s; }
.btn:hover .arrow { transform: translateX(4px); }

/* hero right — protein + scientific overlay */
.hero-right {
  position: relative;
  aspect-ratio: 1;
  max-width: 520px;
  justify-self: end;
  width: 100%;
}
.protein-stage {
  position: absolute; inset: 0;
  display: grid; place-items: center;
}
.protein-stage canvas { width: 100%; height: 100%; }
.protein-frame {
  position: absolute; inset: 0;
  border: 1px solid var(--line-soft);
  pointer-events: none;
}
.protein-frame::before, .protein-frame::after {
  content: "";
  position: absolute;
  background: var(--accent);
}
.protein-frame::before {
  top: -1px; left: -1px;
  width: 28px; height: 1px;
}
.protein-frame::after {
  top: -1px; left: -1px;
  width: 1px; height: 28px;
}
.protein-corner-tr, .protein-corner-bl, .protein-corner-br {
  position: absolute;
  background: var(--accent);
  pointer-events: none;
}
.protein-corner-tr { top: -1px; right: -1px; width: 28px; height: 1px; }
.protein-corner-tr::after { content:""; position:absolute; right:0; top:0; width:1px; height:28px; background:var(--accent); }
.protein-corner-bl { bottom: -1px; left: -1px; width: 28px; height: 1px; }
.protein-corner-bl::after { content:""; position:absolute; left:0; bottom:0; width:1px; height:28px; background:var(--accent); }
.protein-corner-br { bottom: -1px; right: -1px; width: 28px; height: 1px; }
.protein-corner-br::after { content:""; position:absolute; right:0; bottom:0; width:1px; height:28px; background:var(--accent); }

.protein-readout {
  position: absolute;
  bottom: 12px; left: 12px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--muted);
  pointer-events: none;
  text-transform: uppercase;
}
.protein-readout .lbl { color: var(--accent); }

.protein-tag {
  position: absolute;
  top: 12px; right: 12px;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--muted);
  text-transform: uppercase;
  pointer-events: none;
}

@media (max-width: 900px) {
  .hero { grid-template-columns: 1fr; padding-top: 100px; gap: 40px; }
  .hero-right { max-width: 380px; justify-self: start; }
}
@media (max-width: 640px) {
  :root { --pad: 20px; }
  .hero { padding-top: 90px; min-height: auto; padding-bottom: 60px; }
  .hero-name { font-size: clamp(40px, 12vw, 60px) !important; }
  .hero-meta { flex-wrap: wrap; gap: 8px; font-size: 10px; }
  .hero-typer { font-size: 13px; }
  .hero-desc { font-size: 15px; }
  .hero-cta { width: 100%; }
  .hero-cta .btn { flex: 1; justify-content: center; padding: 12px 16px; }
  .hero-right { max-width: 280px; margin: 0 auto; }
  section { padding: 60px var(--pad); }
  .section-title { font-size: clamp(28px, 7vw, 38px) !important; margin-bottom: 32px; }
  .nav { padding: 14px var(--pad); }
  .nav-mark { font-size: 11px; }
  .nav-actions { gap: 6px; }
  .lang-pills button { padding: 4px 6px; font-size: 10px; }
  .theme-btn { width: 26px; height: 26px; font-size: 12px; }
}

/* ========================================
   ABOUT
   ======================================== */
.about-text { max-width: none; }
.about-text p {
  font-size: clamp(16px, 1.35vw, 19.5px);
  line-height: 1.72;
  margin-bottom: 20px;
}
.about-text p:first-of-type::first-letter {
  font-family: var(--font-serif);
  font-size: 5em;
  float: left;
  line-height: 0.85;
  padding-right: 12px;
  padding-top: 4px;
  color: var(--accent);
  font-style: italic;
}


@media (max-width: 800px) {
  .about-text p:first-of-type::first-letter { font-size: 4em; }
}

/* ========================================
   RESEARCH
   ======================================== */
.lede {
  font-family: var(--font-serif);
  font-size: clamp(18px, 1.6vw, 23px);
  line-height: 1.52;
  margin-bottom: 56px;
  text-wrap: pretty;
}

.projects { display: flex; flex-direction: column; }
.project {
  border-top: 1px solid var(--line-soft);
  padding: 30px 0;
  display: grid;
  grid-template-columns: 80px minmax(0, 1fr);
  gap: 30px;
  align-items: start;
  position: relative;
}
.project:last-child { border-bottom: 1px solid var(--line-soft); }
.project-num {
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  color: var(--muted);
  padding-top: 6px;
}
.project-num .accent { color: var(--accent); }
.project-main h3 {
  font-family: var(--font-serif);
  font-size: clamp(22px, 2.4vw, 32px);
  font-weight: 400;
  line-height: 1.15;
  margin-bottom: 10px;
  letter-spacing: -0.01em;
}
.project-main h3 em { color: var(--accent); }
.project-tags {
  display: flex; gap: 10px; flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--muted);
  text-transform: uppercase;
  margin-top: 8px;
}
.project-tags span { padding-right: 10px; border-right: 1px solid var(--line-soft); }
.project-tags span:last-child { border-right: none; }

.project-detail {
  grid-column: 2 / -1;
  margin-top: 22px;
}
.project-detail-inner {
  padding: 24px;
  background: var(--paper);
  border-left: 2px solid var(--accent);
}
.project-detail p {
  font-size: 15.5px; line-height: 1.72;
  margin-bottom: 14px;
}


/* technique cloud */
.tech-section { margin-top: 80px; }
.tech-section h3 {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 24px;
  font-weight: 500;
}
.tech-cloud {
  display: flex; flex-wrap: wrap; gap: 8px 12px;
  font-family: var(--font-mono);
  font-size: 13px;
}
/* Direct children only: the popover inside a chip is also a span. */
.tech-cloud > span,
.tech-cloud > a {
  position: relative;
  padding: 6px 14px;
  border: 1px solid var(--line-soft);
  transition: border-color 0.2s, background 0.2s, color 0.2s;
  cursor: default;
  color: inherit;
}
.tech-cloud > a { cursor: pointer; }
.tech-cloud > span:hover,
.tech-cloud > a:hover,
.tech-cloud > .has-term:focus-visible,
.tech-cloud > .has-term.open {
  border-color: var(--fg);
  background: var(--fg);
  color: var(--bg);
  outline: none;
}
.tech-cloud > span.org { font-style: italic; }
.tech-cloud > span.org:hover { background: var(--accent); border-color: var(--accent); color: var(--bg); }
/* model chips are links: the arrow says so, and turns accent on hover */
.tech-cloud > .model .arrow-glyph { font-size: 10px; color: var(--muted); }
.tech-cloud > .model:hover .arrow-glyph,
.tech-cloud > .model.open .arrow-glyph { color: var(--accent); }

@media (max-width: 800px) {
  .project { grid-template-columns: 56px minmax(0, 1fr); gap: 14px; padding: 26px 0; }
  .project-num { font-size: 10px; }
}
@media (max-width: 480px) {
  .project { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .project-num { display: none; }
}

/* ========================================
   TIMELINE (Education)
   ======================================== */
.timeline {
  position: relative;
  padding-left: 80px;
}
.timeline::before {
  content: ""; position: absolute;
  left: 24px; top: 12px; bottom: 12px;
  width: 1px; background: var(--line-soft);
}
.timeline-item {
  position: relative;
  padding: 30px 0;
  opacity: 0;
  transform: translateX(-20px);
  transition: all 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.timeline-item.visible { opacity: 1; transform: translateX(0); }
.timeline-item::before {
  content: ""; position: absolute;
  left: -64px; top: 38px;
  width: 13px; height: 13px;
  background: var(--bg);
  border: 2px solid var(--fg);
  border-radius: 50%;
  transition: all 0.3s;
}
.timeline-item.current::before {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--accent) 25%, transparent);
}
.timeline-item:hover::before {
  background: var(--accent); border-color: var(--accent);
}
.timeline-date {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--muted);
  text-transform: uppercase;
  margin-bottom: 6px;
  display: flex; gap: 10px; align-items: center;
}
.timeline-item.current .timeline-date .badge {
  background: var(--accent); color: white;
  padding: 2px 8px; font-size: 9px; letter-spacing: 0.1em;
}
.timeline-item h4 {
  font-family: var(--font-serif);
  font-size: clamp(20px, 2vw, 26px);
  font-weight: 400;
  letter-spacing: -0.01em;
  margin-bottom: 6px;
}
.timeline-item .institution {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--fg);
  margin-bottom: 8px;
  font-weight: 500;
}
.timeline-item .detail {
  font-size: 14px;
  color: var(--muted);
  line-height: 1.6;
}

@media (max-width: 600px) {
  .timeline { padding-left: 40px; }
  .timeline::before { left: 8px; }
  .timeline-item::before { left: -36px; }
}

/* ========================================
   PUBLICATIONS
   ======================================== */
.pub-list { display: flex; flex-direction: column; }
.pub-item {
  display: grid;
  grid-template-columns: 110px 1fr auto;
  gap: 30px;
  padding: 22px 0;
  border-top: 1px solid var(--line-soft);
  align-items: start;
  transition: padding 0.25s;
}
.pub-item:last-child { border-bottom: 1px solid var(--line-soft); }
.pub-item:hover { padding-left: 12px; }
.pub-date {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
  letter-spacing: 0.05em;
  padding-top: 4px;
}
.pub-content h4 {
  font-family: var(--font-serif);
  font-size: 19px;
  font-weight: 400;
  letter-spacing: -0.01em;
  line-height: 1.3;
  margin-bottom: 6px;
}
.pub-content .authors {
  font-size: 14px;
  color: var(--muted);
  margin-bottom: 4px;
}
.pub-content .venue {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
  letter-spacing: 0.04em;
}
.pub-types { display: flex; flex-direction: column; gap: 4px; align-items: flex-end; }
.pub-tag {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 3px 8px;
  border: 1px solid var(--line-soft);
}
.pub-tag.intl { background: var(--accent); color: white; border-color: var(--accent); }
.pub-tag.poster { color: var(--muted); }
/* Not yet presented. Outlined rather than filled so it reads as a state and
   does not compete with the filled "International" tag beside it; dashed for
   the same reason the pending grant pill is dashed. */
.pub-tag.upcoming {
  color: var(--accent);
  border-style: dashed;
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
}

.pub-empty {
  padding: 40px;
  border: 1px dashed var(--line-soft);
  text-align: center;
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--muted);
  letter-spacing: 0.04em;
}

.award-row {
  display: grid;
  grid-template-columns: 110px 1fr;
  gap: 30px;
  padding: 22px 0;
  border-top: 1px solid var(--line-soft);
}
.award-row:last-child { border-bottom: 1px solid var(--line-soft); }
.award-row .year {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--accent);
  letter-spacing: 0.05em;
}
.award-row h4 {
  font-family: var(--font-serif);
  font-size: 19px;
  font-weight: 400;
  margin-bottom: 4px;
  letter-spacing: -0.01em;
}
.award-row p { font-size: 14px; color: var(--muted); }

@media (max-width: 700px) {
  .pub-item { grid-template-columns: 1fr; gap: 8px; padding: 18px 0; }
  .award-row { grid-template-columns: 1fr; gap: 6px; }
  .pub-types { flex-direction: row; align-items: flex-start; }
  .pub-content h4 { font-size: 16px; }
}

/* ========================================
   HOBBIES
   ======================================== */
.hobby-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
}
.hobby-cell {
  padding: 36px 24px;
  border-right: 1px solid var(--line-soft);
  position: relative;
  overflow: hidden;
  transition: background 0.3s;
}
.hobby-cell:last-child { border-right: none; }
.hobby-cell:hover { background: var(--paper); }
.hobby-cell .glyph {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: 56px;
  color: var(--accent);
  line-height: 1;
  margin-bottom: 18px;
  transition: transform 0.4s;
}
.hobby-cell:hover .glyph { transform: rotate(-8deg) scale(1.05); }
.hobby-cell h4 {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin-bottom: 8px;
}
.hobby-cell p {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.5;
}

@media (max-width: 900px) {
  .hobby-strip { grid-template-columns: repeat(2, 1fr); }
  .hobby-cell { border-bottom: 1px solid var(--line-soft); }
  .hobby-cell:nth-child(even) { border-right: none; }
}

/* ========================================
   NEWS
   ======================================== */
.news-flow {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
}
.news-card {
  padding: 28px 24px 28px 0;
  border-top: 1px solid var(--line-soft);
  position: relative;
}
.news-card:nth-child(even) { padding-left: 30px; padding-right: 0; border-left: 1px solid var(--line-soft); }
.news-card .when {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--accent);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 8px;
}
.news-card h4 {
  font-family: var(--font-serif);
  font-size: 20px;
  font-weight: 400;
  margin-bottom: 6px;
  letter-spacing: -0.01em;
}
.news-card p { font-size: 14px; color: var(--muted); line-height: 1.6; }

@media (max-width: 700px) {
  .news-flow { grid-template-columns: 1fr; }
  .news-card:nth-child(even) { padding-left: 0; border-left: none; }
}

/* ========================================
   CONTACT
   ======================================== */
.contact-block {
  display: grid;
  /* minmax(0, …) so a long email or headline cannot push the column past the
     viewport: a bare 1fr has an automatic min-content floor. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(40px, 6vw, 80px);
  align-items: center;
  padding: clamp(60px, 10vh, 100px) 0;
}
.contact-headline {
  font-family: var(--font-serif);
  font-size: clamp(38px, 5vw, 68px);
  font-weight: 400;
  line-height: 0.95;
  letter-spacing: -0.03em;
}
.contact-headline em { color: var(--accent); font-style: italic; }
.contact-sub {
  font-size: 17px; line-height: 1.7;
  color: var(--muted);
  margin-top: 24px;
  max-width: 460px;
}
.contact-list {
  display: flex; flex-direction: column;
  border-top: 1px solid var(--line-soft);
}
.contact-row {
  display: grid;
  grid-template-columns: 110px minmax(0, 1fr) auto;
  gap: 20px;
  padding: 16px 0;
  border-bottom: 1px solid var(--line-soft);
  font-family: var(--font-mono);
  font-size: 13px;
  align-items: center;
  transition: padding 0.25s;
}
.contact-row:hover { padding-left: 8px; }
.contact-row .label {
  color: var(--muted);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.contact-row .value {
  font-family: var(--font-mono);
  font-size: 13px;
  overflow-wrap: anywhere;
}
.contact-row .arrow {
  font-family: var(--font-serif);
  font-size: 18px;
  color: var(--accent);
  transition: transform 0.25s;
  font-style: italic;
}
.contact-row:hover .arrow { transform: translate(4px, -4px); }

.socials {
  display: flex; gap: 16px; margin-top: 30px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.socials a {
  padding: 8px 14px;
  border: 1px solid var(--line-soft);
  transition: all 0.2s;
  /* the row already wraps — never break a label mid-word */
  white-space: nowrap;
}
.socials a:hover {
  background: var(--fg); color: var(--bg); border-color: var(--fg);
}

@media (max-width: 800px) {
  .contact-block { grid-template-columns: 1fr; gap: 40px; padding: 40px 0; }
  .contact-headline { font-size: clamp(36px, 9vw, 56px) !important; }
  .contact-row { grid-template-columns: 90px 1fr auto; gap: 12px; font-size: 12px; }
  .contact-row .value { font-size: 12px; word-break: break-word; }
  .socials { flex-wrap: wrap; gap: 8px; }
}

/* ========================================
   FOOTER
   ======================================== */
footer {
  position: relative;
  padding: 32px var(--gutter);
  border-top: 1px solid var(--line-soft);
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 16px;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--muted);
  text-transform: uppercase;
  z-index: 1;
}
footer .clock { color: var(--accent); }

/* visible / scroll fade */
.reveal {
  opacity: 0; transform: translateY(20px);
  transition: opacity 0.8s, transform 0.8s;
}
.reveal.visible { opacity: 1; transform: translateY(0); }

/* ========================================
   ARROW glyphs — force text presentation, never emoji
   ======================================== */
.contact-row .arrow,
.arrow-glyph,
.btn .arrow {
  font-variant-emoji: text;
  font-feature-settings: "liga" 0;
  text-rendering: optimizeLegibility;
}

/* ========================================
   CJK typography fixes — applies to zh / ja
   ======================================== */
/* CJK line breaking, applied to text blocks only (never to layout containers).
   `word-break: normal` lets Chinese and Japanese break between any two
   characters, which justified text needs (pitfall 18: keep-all left gappy
   lines); `overflow-wrap: anywhere` still wraps a long Latin token or URL;
   `line-break: strict` keeps punctuation off the line start. */
[data-lang="zh"] .hero-desc,
[data-lang="zh"] .about-text p,
[data-lang="zh"] .project-detail p,
[data-lang="zh"] .timeline-item .detail,
[data-lang="zh"] .timeline-item h4,
[data-lang="zh"] .timeline-item .institution,
[data-lang="zh"] .news-card p,
[data-lang="zh"] .news-card h4,
[data-lang="zh"] .pub-content h4,
[data-lang="zh"] .pub-content .venue,
[data-lang="zh"] .pub-content .authors,
[data-lang="zh"] .award-row p,
[data-lang="zh"] .award-row h4,
[data-lang="zh"] .hobby-cell p,
[data-lang="zh"] .hobby-cell h4,
[data-lang="zh"] .contact-sub,
[data-lang="zh"] .project-main h3,
[data-lang="zh"] .section-title,
[data-lang="zh"] .contact-headline,
[data-lang="ja"] .hero-desc,
[data-lang="ja"] .about-text p,
[data-lang="ja"] .project-detail p,
[data-lang="ja"] .timeline-item .detail,
[data-lang="ja"] .timeline-item h4,
[data-lang="ja"] .timeline-item .institution,
[data-lang="ja"] .news-card p,
[data-lang="ja"] .news-card h4,
[data-lang="ja"] .pub-content h4,
[data-lang="ja"] .pub-content .venue,
[data-lang="ja"] .pub-content .authors,
[data-lang="ja"] .award-row p,
[data-lang="ja"] .award-row h4,
[data-lang="ja"] .hobby-cell p,
[data-lang="ja"] .hobby-cell h4,
[data-lang="ja"] .contact-sub,
[data-lang="ja"] .project-main h3,
[data-lang="ja"] .section-title,
[data-lang="ja"] .contact-headline {
  /* keep-all stops Chinese and Japanese breaking between characters, which
     leaves short lines that justification then stretches into gappy rows.
     normal is the correct value for zh/ja; strict still protects punctuation. */
  word-break: normal;
  overflow-wrap: anywhere;
  line-break: strict;
}

/* Big CJK headings: open tracking and slightly smaller body size for breathability */
[data-lang="zh"] .section-title,
[data-lang="ja"] .section-title {
  letter-spacing: 0.06em;
  font-size: clamp(28px, 3.6vw, 48px);
  line-height: 1.28;
}
/* Japanese display type is mincho (the loaded Noto Serif JP), not whatever
   the OS picks for a generic serif */
[data-lang="ja"] .section-title,
[data-lang="ja"] .contact-headline,
[data-lang="ja"] .post-title {
  font-family: var(--font-jp);
}
[data-lang="zh"] .contact-headline,
[data-lang="ja"] .contact-headline {
  letter-spacing: 0.05em;
  font-size: clamp(32px, 4.4vw, 58px);
  line-height: 1.2;
}
[data-lang="zh"] .hero-name,
[data-lang="ja"] .hero-name {
  letter-spacing: -0.01em;
}
[data-lang="zh"] .hero-name-cn,
[data-lang="ja"] .hero-name-cn {
  letter-spacing: 0.18em;
}
@media (max-width: 800px) {
  [data-lang="zh"] .section-title,
  [data-lang="ja"] .section-title {
    font-size: clamp(22px, 5.6vw, 32px) !important;
    letter-spacing: 0.04em;
    line-height: 1.36;
  }
  [data-lang="zh"] .contact-headline,
  [data-lang="ja"] .contact-headline {
    font-size: clamp(28px, 7vw, 40px) !important;
    letter-spacing: 0.04em;
    line-height: 1.22;
  }
}

/* Hobby cell glyphs already serif/italic — for CJK, italic looks cleaner with a slight skew override */
[data-lang="zh"] .hero-desc .key,
[data-lang="ja"] .hero-desc .key,
[data-lang="zh"] .tech-cloud > .org,
[data-lang="ja"] .tech-cloud > .org {
  font-style: normal;
}

/* ========================================
   Inline hyperlinks inside body text
   ======================================== */
.about-text a,
.project-detail a,
.timeline-item .institution a,
.timeline-item .detail a,
.pub-content a,
.venue a,
.award-row a,
.news-card a {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: color-mix(in srgb, var(--accent) 35%, transparent);
  text-underline-offset: 3px;
  transition: text-decoration-color 0.2s ease, color 0.2s ease;
}
.about-text a:hover,
.project-detail a:hover,
.timeline-item .institution a:hover,
.timeline-item .detail a:hover,
.pub-content a:hover,
.venue a:hover,
.award-row a:hover,
.news-card a:hover {
  text-decoration-color: var(--accent);
}

/* a quiet pointer at the foot of a section (Blog → Collection) */
.section-more {
  margin-top: 36px;
  font-size: 15px;
  color: var(--muted);
}
.section-more a {
  color: var(--fg);
  border-bottom: 1px solid var(--line-soft);
  padding-bottom: 2px;
  transition: color 0.2s, border-color 0.2s;
}
.section-more a:hover { color: var(--accent-ink); border-color: var(--accent-ink); }
.section-more .arrow-glyph { color: var(--accent); }

/* ========================================
   Seeking-postdoc callout in Contact section
   ======================================== */
.seeking-card {
  margin-top: 28px;
  padding: 18px 22px;
  border: 1px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  position: relative;
}
.seeking-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 8px;
  font-weight: 600;
}
.seeking-body {
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.55;
  color: var(--fg);
  margin: 0;
}

/* CV download button, sits inline with social pills */
.socials-cv {
  background: var(--fg);
  color: var(--bg) !important;
  border-color: var(--fg) !important;
}
.socials-cv:hover {
  background: var(--accent);
  border-color: var(--accent) !important;
  color: var(--bg) !important;
}


/* ========================================
   Glossary terms — hover / focus definition
   ======================================== */
.term,
.tech-cloud > .has-term {
  position: relative;
  cursor: help;
}
.term {
  border-bottom: 1px dotted color-mix(in srgb, var(--fg) 45%, transparent);
}
.term:focus-visible { outline: 1px solid var(--accent); outline-offset: 2px; }
.term-pop {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 10px);
  transform: translateX(calc(-50% + var(--pop-shift, 0px))) translateY(5px);
  width: max-content;
  max-width: min(300px, 74vw);
  padding: 11px 13px;
  background: var(--fg);
  color: var(--bg);
  font-family: var(--font-sans);
  font-size: 12px;
  font-style: normal;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0;
  text-transform: none;
  text-align: left;
  white-space: normal;
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.22);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s;
  z-index: 60;
}
.term-pop::after {
  content: "";
  position: absolute;
  top: 100%;
  /* counter-shift so the arrow keeps pointing at the trigger */
  left: calc(50% - var(--pop-shift, 0px));
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: var(--fg);
}
.term:hover > .term-pop,
.term:focus-within > .term-pop,
.tech-cloud > .has-term:hover > .term-pop,
.tech-cloud > .has-term:focus > .term-pop,
.tech-cloud > .has-term:focus-within > .term-pop,
.has-term.open > .term-pop {
  opacity: 1;
  visibility: visible;
  transform: translateX(calc(-50% + var(--pop-shift, 0px))) translateY(0);
}
/* the chip itself inverts on hover; keep the popover readable */
.tech-cloud > .has-term:hover > .term-pop,
.tech-cloud > .has-term.open > .term-pop {
  background: var(--fg);
  color: var(--bg);
}

/* ========================================
   Section sub-labels + notes
   ======================================== */
.block-label {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 16px;
}
.tech-note,
.funding-note {
  font-family: var(--font-sans);
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--muted);
  max-width: 92ch;
  margin: -8px 0 26px;
}

/* ========================================
   Models & tools: grouped chip clouds. Each chip is a link with the
   description in its popover (ModelChip in app.jsx). Two columns of groups
   on a wide screen, one below 900px.
   ======================================== */
.model-groups {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 28px 56px;
}
@media (min-width: 900px) {
  .model-groups { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.model-group-label {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg);
  padding-bottom: 8px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--line-soft);
}
/* the host line and the touch hint under a model description */
.term-pop .pop-meta {
  display: block;
  margin-top: 7px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.04em;
  color: color-mix(in srgb, var(--bg) 72%, transparent);
}

/* ========================================
   Grants & fellowships table
   ======================================== */
.grant-table {
  border-top: 1px solid var(--line);
}
.grant-head {
  display: grid;
  grid-template-columns: 168px 150px 1fr 150px;
  gap: 24px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line-soft);
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}
.grant-row {
  display: grid;
  grid-template-columns: 168px 150px 1fr 150px;
  gap: 24px;
  padding: 20px 0;
  border-bottom: 1px solid var(--line-soft);
  align-items: start;
  transition: padding 0.25s, background 0.25s;
}
.grant-row:hover { padding-left: 10px; background: var(--paper); }
.grant-year {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
  letter-spacing: 0.04em;
  padding-top: 2px;
}
.grant-program {
  font-family: var(--font-sans);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.4;
}
.grant-program a {
  border-bottom: 1px solid transparent;
  transition: color 0.2s, border-color 0.2s;
}
.grant-program a:hover { color: var(--accent); border-bottom-color: var(--accent); }
.grant-title {
  font-family: var(--font-serif);
  font-size: 15px;
  line-height: 1.55;
  color: var(--muted);
}
.grant-title a { color: var(--accent-ink); }
.grant-status { display: flex; justify-content: flex-end; }
.grant-pill {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 5px 10px;
  border: 1px solid var(--line-soft);
  color: var(--muted);
  white-space: nowrap;
  text-align: center;
  line-height: 1.4;
}
.tone-yes .grant-pill {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.tone-pending .grant-pill {
  border-color: var(--accent);
  color: var(--accent);
}
.tone-lottery .grant-pill {
  border-style: dashed;
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  color: color-mix(in srgb, var(--accent) 85%, var(--fg));
  white-space: normal;
  min-width: 128px;
}
.tone-yes .grant-program,
.tone-pending .grant-program { color: var(--fg); }
.tone-no .grant-year,
.tone-no .grant-program,
.tone-no .grant-title { opacity: 0.72; }

@media (max-width: 860px) {
  .grant-head { display: none; }
  .grant-row {
    grid-template-columns: 1fr auto;
    gap: 6px 14px;
    padding: 18px 0;
  }
  .grant-year { grid-column: 1; order: 1; }
  .grant-status { grid-column: 2; grid-row: 1 / span 2; order: 2; align-items: start; }
  .grant-program { grid-column: 1; order: 3; }
  .grant-title { grid-column: 1 / -1; order: 4; font-size: 14px; }
  .grant-pill { font-size: 9px; }
}

/* ========================================
   Thesis rows + paper note
   ======================================== */
.thesis-orig {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--muted);
  margin-bottom: 6px;
  line-height: 1.5;
}
.pub-note {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
  margin-top: 8px;
  letter-spacing: 0.03em;
}

/* ========================================
   Inline highlight for a word inside running text
   ======================================== */
/* ========================================
   Figures at night
   ========================================
   Every figure on this site sits on a white plate, and most of the artwork has
   a light ground baked into the pixels: the paper figures are white-backed,
   and the py2Dmol and Biotite images were flattened onto the paper colour
   because their sources were transparent. On a dark page that is a glaring
   slab, and changing `background` alone cannot fix it, because the light is
   in the image data, not in the CSS.

   `filter` is the only lever that reaches both. It applies to the element as a
   whole, so filtering the <img> dims its own white padding box at the same
   time and no bright halo is left around the edge. 0.72 was picked by putting
   several values side by side on a dark page: 0.78 still reads as bright,
   0.62 starts muddying the colours. The small contrast bump compensates for
   the flattening that dimming causes.

   No hover-to-restore: a figure that flashes back to full brightness as the
   cursor crosses it reintroduces exactly the glare this removes. The theme
   toggle is the way back to the undimmed figure. */
[data-theme="dark"] figure.post-fig img,
[data-theme="dark"] figure.post-fig video,
[data-theme="dark"] .project-fig img {
  filter: brightness(0.72) contrast(1.05);
}

.hl {
  background: linear-gradient(transparent 64%, color-mix(in oklab, var(--accent) 34%, transparent) 64%);
  padding: 0 2px;
  font-style: normal;
  font-weight: inherit;
}
/* the lighter partner to .hl, for runs too long to sit under a marker band */
.ul {
  border-bottom: 1.5px solid color-mix(in oklab, var(--accent) 60%, transparent);
  padding-bottom: 1px;
  font-style: normal;
  font-weight: inherit;
}

/* ========================================
   Project status + outcome
   ======================================== */
.project-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.project-head h3 { margin-bottom: 0; }
.project-status {
  font-family: var(--font-mono);
  font-size: 9.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 4px 9px;
  border: 1px solid var(--line-soft);
  white-space: nowrap;
  flex-shrink: 0;
}
.project-status.tone-failed {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 55%, transparent);
  background: color-mix(in srgb, var(--warn) 8%, transparent);
}
.project-status.tone-done {
  color: #fff;
  background: var(--accent);
  border-color: var(--accent);
}
.project-status.tone-active {
  color: var(--accent);
  border-color: var(--accent);
  border-style: dashed;
}

.project-outcome {
  margin-top: 18px;
  padding: 16px 20px;
  border-left: 2px solid var(--warn);
  background: color-mix(in srgb, var(--warn) 5%, transparent);
}
.outcome-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--warn);
  margin-bottom: 8px;
  font-weight: 600;
}
.project-outcome p { margin-bottom: 0; }

.project-paper {
  margin-top: 16px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
}
.project-paper a { color: var(--accent-ink); }
.project-paper a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* ========================================
   Theses — long form
   ======================================== */
.thesis {
  border-top: 1px solid var(--line-soft);
  padding: 28px 0;
}
.thesis:last-child { border-bottom: 1px solid var(--line-soft); }
.thesis-head {
  display: grid;
  grid-template-columns: 110px minmax(0, 1fr);
  gap: 30px;
  margin-bottom: 20px;
}
.thesis-year {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--accent);
  letter-spacing: 0.05em;
  padding-top: 5px;
}
.thesis-head h4 {
  font-family: var(--font-serif);
  font-size: clamp(19px, 1.7vw, 23px);
  font-weight: 400;
  line-height: 1.35;
  letter-spacing: -0.01em;
  margin-bottom: 6px;
}
.thesis-orig {
  font-family: var(--font-sans);
  font-size: 13px;
  color: var(--muted);
  line-height: 1.5;
  margin-bottom: 8px;
}
.thesis-where {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
  letter-spacing: 0.04em;
  line-height: 1.6;
}
/* one flowing paragraph, indented to line up with the thesis title */
.thesis-body {
  font-size: 16px;
  line-height: 1.8;
  color: var(--fg);
  text-wrap: pretty;
  /* line up with the thesis title, which sits after the 110px year column */
  margin-left: 140px;
  max-width: none;
}
.thesis-body p { margin-bottom: 1.05em; }
.thesis-body p:last-child { margin-bottom: 0; }

@media (max-width: 700px) {
  .thesis-head { grid-template-columns: 1fr; gap: 8px; }
  .thesis-body { margin-left: 0; }
}

/* ========================================
   Blog post list
   ======================================== */
.post-list { display: flex; flex-direction: column; }
.post-item {
  display: grid;
  grid-template-columns: 110px minmax(0, 1fr) auto;
  gap: 30px;
  padding: 22px 0;
  border-top: 1px solid var(--line-soft);
  align-items: start;
  transition: padding 0.25s, background 0.25s;
}
.post-item:last-child { border-bottom: 1px solid var(--line-soft); }
a.post-item:hover { padding-left: 12px; background: var(--paper); }
.post-date {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
  letter-spacing: 0.05em;
  padding-top: 4px;
}
.post-main h4 {
  font-family: var(--font-serif);
  font-size: 19px;
  font-weight: 400;
  line-height: 1.35;
  margin-bottom: 6px;
}
.post-main p { font-size: 14px; color: var(--muted); line-height: 1.6; }
.post-item .arrow-glyph { color: var(--accent); font-size: 15px; }

@media (max-width: 700px) {
  .post-item { grid-template-columns: minmax(0, 1fr) auto; gap: 8px 12px; }
  .post-date { grid-column: 1 / -1; }
}

/* ========================================
   Click-to-copy contact rows
   ======================================== */
.contact-row.copyable {
  /* it is a <button>: undo the UA styling, keep the row grid */
  appearance: none;
  width: 100%;
  margin: 0;
  border: none;
  border-bottom: 1px solid var(--line-soft);
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.contact-row.copyable .value {
  border-bottom: 1px dotted color-mix(in srgb, var(--fg) 40%, transparent);
  padding-bottom: 1px;
  transition: border-color 0.2s;
}
.copy-hint {
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  border: 1px solid var(--line-soft);
  padding: 4px 8px;
  white-space: nowrap;
  transition: all 0.2s;
}
.contact-row.copyable:hover { padding-left: 8px; }
.contact-row.copyable:hover .value { border-bottom-color: var(--accent); }
.contact-row.copyable:hover .copy-hint {
  border-color: var(--accent);
  color: var(--accent);
}
.contact-row.copyable:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 2px;
}
.contact-row.copyable.copied .copy-hint,
.contact-row.copyable.copied:hover .copy-hint {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bg);
}
.contact-row.copyable.copied .value { border-bottom-color: var(--accent); }

@media (max-width: 800px) {
  .copy-hint { font-size: 8.5px; padding: 3px 6px; letter-spacing: 0.1em; }
}

/* ========================================
   Phone: tighter type and spacing.
   The desktop rhythm is right; on a small screen the same rhythm turns
   every section into a long scroll, so step the whole scale down.
   ======================================== */
@media (max-width: 640px) {
  body { font-size: 15px; line-height: 1.5; }
  section { padding: 46px var(--pad); }
  .eyebrow { margin-bottom: 16px; font-size: 10px; }
  .divider { margin: 38px 0; }

  .about-text p { font-size: 15px; line-height: 1.65; margin-bottom: 16px; }
  .about-text p:first-of-type::first-letter { font-size: 3.4em; padding-right: 9px; }

  .lede { font-size: 17px; line-height: 1.5; margin-bottom: 34px; }

  .project { padding: 22px 0; }
  .project-main h3 { font-size: 19px; line-height: 1.25; }
  .project-head { gap: 8px; margin-bottom: 8px; }
  .project-status { font-size: 8.5px; padding: 3px 7px; letter-spacing: 0.1em; }
  .project-tags { font-size: 9px; gap: 7px; margin-top: 6px; }
  .project-detail { margin-top: 14px; }
  .project-detail-inner { padding: 14px 14px 14px 16px; }
  .project-detail p { font-size: 13.5px; line-height: 1.65; margin-bottom: 10px; }
  .project-outcome { margin-top: 12px; padding: 12px 14px; }
  .outcome-label { font-size: 9px; margin-bottom: 6px; }

  .tech-section { margin-top: 44px; }
  .tech-section h3 { margin-bottom: 12px; font-size: 10px; }
  .tech-note, .funding-note { font-size: 12.5px; margin: -4px 0 16px; line-height: 1.55; }
  .tech-cloud { gap: 6px 7px; font-size: 11px; }
  .tech-cloud > span, .tech-cloud > a { padding: 4px 9px; }
  .model-groups { gap: 18px; }
  .model-group-label { font-size: 9.5px; padding-bottom: 5px; margin-bottom: 10px; letter-spacing: 0.12em; }

  .timeline-item { padding: 20px 0; }
  .timeline-item h4 { font-size: 18px; }
  .timeline-item .institution { font-size: 13px; margin-bottom: 5px; }
  .timeline-item .detail { font-size: 13px; line-height: 1.55; }
  .timeline-date { font-size: 10px; margin-bottom: 4px; }

  .block-label { font-size: 10px; margin-bottom: 12px; }
  .pub-item { padding: 14px 0; }
  .pub-content h4 { font-size: 15.5px; line-height: 1.3; }
  .pub-content .authors { font-size: 12.5px; }
  .pub-content .venue { font-size: 10.5px; }
  .pub-note { font-size: 10px; margin-top: 5px; }
  .pub-empty { padding: 22px; font-size: 12px; }

  .thesis { padding: 20px 0; }
  .thesis-head { margin-bottom: 12px; }
  .thesis-head h4 { font-size: 17px; }
  .thesis-orig { font-size: 12px; margin-bottom: 5px; }
  .thesis-where { font-size: 10.5px; }
  .thesis-body { font-size: 13.5px; line-height: 1.68; }

  .grant-row { padding: 14px 0; }
  .grant-year { font-size: 10.5px; }
  .grant-program { font-size: 13px; }
  .grant-title { font-size: 13px; line-height: 1.5; }

  .award-row { padding: 16px 0; }
  .award-row h4 { font-size: 16px; }
  .award-row p { font-size: 13px; }

  .hobby-cell { padding: 22px 16px; }
  .hobby-cell .glyph { font-size: 40px; margin-bottom: 10px; }
  .hobby-cell h4 { font-size: 13px; }
  .hobby-cell p { font-size: 12.5px; }

  .news-card { padding: 18px 16px 18px 0; }
  .news-card h4 { font-size: 17px; }
  .news-card p { font-size: 13px; line-height: 1.55; }
  .news-card .when { font-size: 10px; margin-bottom: 5px; }

  .post-item { padding: 16px 0; }
  .post-main h4 { font-size: 16.5px; }
  .post-main p { font-size: 13px; }

  .contact-sub { font-size: 15px; margin-top: 16px; }
  .seeking-card { margin-top: 20px; padding: 14px 16px; }
  .seeking-body { font-size: 13px; }
  .socials { margin-top: 22px; gap: 6px; }
  .socials a { padding: 7px 11px; font-size: 10px; }
  .contact-row { padding: 12px 0; }

  footer { padding: 24px var(--gutter); font-size: 10px; gap: 10px; }
}

/* ========================================
   Section header
   ======================================== */
.section-head { margin-bottom: 50px; }
.section-head .section-title { margin-bottom: 20px; }
.section-head .lede { margin-bottom: 0; }
@media (max-width: 640px) {
  .section-head { margin-bottom: 28px; }
  .section-head .section-title { margin-bottom: 14px; }
}


/* ========================================
   ALIGNMENT — the single rule for the whole site, including blog posts.

   Three questions, in order:

   1. Running prose, or display / label / metadata?
      Headings, labels, metadata, captions, list items, table cells, pills
      and buttons are ALWAYS ragged-right. Only multi-line running prose is
      a candidate for justification. The list below is exhaustive; if a new
      block is not in it, it stays ragged.

   2. Which script?
      CJK justifies natively — Chinese and Japanese sit on a full-width grid
      and break between almost any two characters, so every line already
      fills and a ragged edge reads as unfinished. Justify at every width.
      Latin only justifies when the measure is wide enough.

   3. How wide is the measure?
      Hyphenation is off by choice (it was breaking "de-sign", "rec-ognize"),
      so Latin justification depends on having enough word spaces per line to
      absorb the slack. The working threshold is ~65 characters. Measured on
      this site: 73–135 ch on the desktop, 40–45 ch on a phone. So Latin is
      justified above 700px and ragged below it.

   Centring is reserved for closed symmetric objects — a status pill, a boxed
   notice. Never running text, never a heading: the layout hangs off a left
   axis and centred text fights it.
   ======================================== */
.hero-desc,
.about-text p,
.thesis-body,
.thesis-body p,
.project-detail p,
.post-standfirst,
.post-body p {
  text-align: justify;
  text-wrap: pretty;
}

@media (max-width: 700px) {
  [data-lang="en"] .hero-desc,
  [data-lang="en"] .about-text p,
  [data-lang="en"] .thesis-body,
  [data-lang="en"] .thesis-body p,
  [data-lang="en"] .project-detail p,
  [data-lang="en"] .post-standfirst,
  [data-lang="en"] .post-body p {
    text-align: start;
  }
}

body { hyphens: none; -webkit-hyphens: none; }

/* ========================================
   Contact fixes
   ======================================== */
/* the social row had no flex-wrap outside the mobile query, so on a wide
   screen it ran straight through the address column next to it */
.socials { flex-wrap: wrap; }
/* "Email · UTokyo (ECC)" does not fit 110px, and wrapping a label mid-phrase
   reads as a mistake */
/* the label column is as wide as its longest label, up to 230px: "Email ·
   Institute of Industrial Science" written in full needs the room, the
   shorter labels should not push the values right */
.contact-row { grid-template-columns: fit-content(230px) minmax(0, 1fr) auto; }
@media (max-width: 800px) {
  .contact-row { grid-template-columns: 120px minmax(0, 1fr) auto; }
}
@media (max-width: 640px) {
  .contact-row { grid-template-columns: minmax(0, 1fr) auto; gap: 2px 12px; }
  .contact-row .label { grid-column: 1 / -1; }
}

/* ========================================
   Japanese: gothic for running text, mincho stays for display headings.
   ======================================== */
[data-lang="ja"] .hero-desc,
[data-lang="ja"] .lede,
[data-lang="ja"] .about-text p,
[data-lang="ja"] .thesis-body,
[data-lang="ja"] .thesis-body p,
[data-lang="ja"] .thesis-orig,
[data-lang="ja"] .project-detail p,
[data-lang="ja"] .project-outcome p,
[data-lang="ja"] .timeline-item .detail,
[data-lang="ja"] .news-card p,
[data-lang="ja"] .grant-title,
[data-lang="ja"] .award-row p,
[data-lang="ja"] .hobby-cell p,
[data-lang="ja"] .contact-sub,
[data-lang="ja"] .post-main p,
[data-lang="ja"] .pub-empty,
[data-lang="ja"] .tech-note,
[data-lang="ja"] .funding-note {
  font-family: var(--font-jp-sans);
  font-style: normal;
  line-height: 1.85;
  letter-spacing: 0.01em;
}
/* CJK has no true italic: the browser synthesises a slant, which looks wrong
   at display sizes. Applies to Chinese as well as Japanese. */
[data-lang="ja"] .section-title,
[data-lang="ja"] .contact-headline,
[data-lang="ja"] .contact-headline em,
[data-lang="ja"] .lede,
[data-lang="ja"] .post-title,
[data-lang="ja"] .hobby-cell .glyph,
[data-lang="zh"] .section-title,
[data-lang="zh"] .contact-headline,
[data-lang="zh"] .contact-headline em,
[data-lang="zh"] .lede,
[data-lang="zh"] .post-title,
[data-lang="zh"] .post-body h2,
[data-lang="ja"] .post-body h2,
[data-lang="zh"] .hobby-cell .glyph,
[data-lang="zh"] .about-text p:first-of-type::first-letter,
[data-lang="ja"] .about-text p:first-of-type::first-letter {
  font-style: normal;
}
[data-lang="ja"] .thesis-head h4,
[data-lang="ja"] .project-main h3,
[data-lang="ja"] .timeline-item h4,
[data-lang="ja"] .news-card h4,
[data-lang="ja"] .pub-content h4,
[data-lang="ja"] .award-row h4,
[data-lang="ja"] .post-main h4 {
  font-family: var(--font-jp-sans);
  font-weight: 500;
  letter-spacing: 0;
}

/* Blog post pages. Gothic is for running text only: the post title and the
   section headings stay mincho, same rule as the site. */
[data-lang="ja"] .post-standfirst,
[data-lang="ja"] .post-body p,
[data-lang="ja"] .post-body li,
[data-lang="ja"] .pull,
[data-lang="ja"] figure.post-fig figcaption,
[data-lang="ja"] .post-foot p {
  font-family: var(--font-jp-sans);
  font-style: normal;
}
/* 1.9 with extra tracking read as sparse: tightened to the density of the
   surrounding Latin text */
[data-lang="ja"] .post-body p,
[data-lang="ja"] .post-body li,
[data-lang="ja"] .post-standfirst,
[data-lang="ja"] .post-foot p {
  font-size: 16px;
  line-height: 1.75;
  letter-spacing: 0;
}
[data-lang="ja"] .post-title,
[data-lang="ja"] .post-body h2 { font-family: var(--font-jp); }
/* the Latin headings use negative tracking, which squeezes CJK glyphs */
[data-lang="ja"] .post-title,
[data-lang="ja"] .post-body h2,
[data-lang="zh"] .post-title,
[data-lang="zh"] .post-body h2 { letter-spacing: 0; }
@media (max-width: 700px) {
  [data-lang="ja"] .post-body p,
  [data-lang="ja"] .post-body li,
  [data-lang="ja"] .post-standfirst { font-size: 15px; line-height: 1.72; }
}

/* ========================================
   Portrait beside the About heading.
   Deliberately not a column of text next to the photo: the prose still runs
   the full width underneath, the portrait only shares the heading row.
   ======================================== */
.about-head { margin-bottom: 40px; }
.portrait {
  border: 1px solid var(--line-soft);
  background: var(--paper);
  padding: 8px;
  width: 100%;
  max-width: 220px;
  align-self: start;
}
.portrait img {
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
}
@media (min-width: 780px) {
  .about-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: clamp(32px, 5vw, 72px);
    align-items: center;
  }
  .about-head .section-title { margin-bottom: 0; }
  .portrait { width: clamp(180px, 17vw, 230px); max-width: none; }
}
@media (max-width: 779px) {
  .about-head { margin-bottom: 28px; }
  .portrait { max-width: 132px; margin-top: 4px; }
}

/* ========================================
   Figure inside a project panel
   ======================================== */
.project-fig {
  margin: 20px 0 4px;
  border-top: 1px solid var(--line-soft);
  padding-top: 18px;
  /* Prose runs the full container width, but a figure that does the same
     dominates the panel. The cap is set by the smallest label baked into the
     TOC graphic: at 620px the "(nm)" axis labels land near 13px, which is the
     floor for legibility. Do not raise it without re-checking the artwork. */
  max-width: 620px;
}
.project-fig img {
  width: 100%;
  height: auto;
  display: block;
  border: 1px solid var(--line-soft);
  background: #fff;
  padding: 10px;
}
.project-fig figcaption {
  font-family: var(--font-sans);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted);
  margin-top: 10px;
}
@media (max-width: 640px) {
  .project-fig { margin-top: 14px; padding-top: 14px; }
  .project-fig img { padding: 6px; }
  .project-fig figcaption { font-size: 11.5px; margin-top: 8px; }
}

/* ========================================
   Reduced motion: the reveals, the caret, the live dot and the page
   transitions stop; the typewriter and the hero canvas are handled in JS.
   ======================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  body { transition: none; }
  .reveal, .timeline-item { opacity: 1; transform: none; transition: none; }
  .live, .caret { animation: none !important; }
  .term-pop, .tech-cloud > span, .tech-cloud > a, .btn, .contact-row, .section-more a { transition: none; }
}
