/* Layout and components for the home pages. Colors come only from the tokens
   in theme.css. The look follows LifeRPG (docs/design.md §7). */
:root {
  --radius: 20px;
  /* Type (docs/home-redesign.md §5): the stacks are in theme.css, shared with
     LifeRPG. Monospace only for code-like marks ($ whoami, BibTeX). Chinese
     text is 仿宋 with Latin in Helvetica Neue Light, a little larger, since
     仿宋 is thin. */
  --body: var(--font-body);
  --gutter: max(16px, env(safe-area-inset-left));
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-padding-top: 80px; }
body { min-height: 100vh; display: flex; flex-direction: column; background: var(--page-bg); background-attachment: fixed; color: var(--ink); font: 15px/1.6 var(--font-body); -webkit-font-smoothing: antialiased; }
/* ?zhbody=sans compares a sans serif for the Chinese text (app.js). */
:root[lang^="zh"][data-zh-body="sans"] { --font-body: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", "Microsoft YaHei", sans-serif; }
:root[lang^="zh"] body { font-size: 16px; font-weight: 300; line-height: 1.75; }
main { flex: 1; }
* { -webkit-tap-highlight-color: transparent; }
button { font-family: inherit; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; border-radius: 6px; }
::selection { background: var(--accent); color: var(--panel); }
[hidden] { display: none !important; }
h1, h2, h3 { font-weight: 650; letter-spacing: -.025em; line-height: 1.25; }
h1, h2 { font-family: var(--font-head); }
:root[lang^="zh"] h1, :root[lang^="zh"] h2 { font-weight: 900; letter-spacing: 0; }
:root[lang^="zh"] .work-card h3 { font-family: var(--font-head); font-weight: 700; }

/* Both languages are in the page; show the one that agrees with <html lang>. */
:root:not([lang^="zh"]) .t[lang^="zh"],
:root[lang^="zh"] .t[lang="en"] { display: none !important; }

.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip-link { position: fixed; z-index: 110; top: -80px; left: 20px; padding: 10px 18px; border-radius: 10px; background: var(--panel); color: var(--ink); }
.skip-link:focus { top: 10px; }
.wrap { width: min(1120px, 100% - 2 * var(--gutter)); margin-inline: auto; }
.wrap.narrow { width: min(980px, 100% - 2 * var(--gutter)); }

.icon { flex: none; width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.icon.small { width: 14px; height: 14px; }

/* ---- Header: "[avatar] Tong Xiao · Publications Lab · CV ⚙" */
/* The banner is full width on every page; it stays at the top and turns into a
   translucent bar once the page scrolls (app.js sets .scrolled). */
.site-header { position: sticky; top: 0; z-index: 50; transition: background-color .25s, box-shadow .25s; }
.site-header.scrolled { background: var(--bar, color-mix(in srgb, var(--surface) 78%, transparent)); box-shadow: 0 1px 0 var(--pane-line); -webkit-backdrop-filter: blur(16px) saturate(1.3); backdrop-filter: blur(16px) saturate(1.3); }
.header-inner { padding: 0 max(clamp(20px, 3vw, 44px), env(safe-area-inset-right)) 0 max(clamp(20px, 3vw, 44px), env(safe-area-inset-left)); min-height: 64px; display: flex; align-items: center; gap: 20px; }
.owner { display: flex; align-items: center; gap: 10px; color: var(--ink); font-size: 18px; font-weight: 650; letter-spacing: -.035em; white-space: nowrap; }
.owner:hover { text-decoration: none; color: var(--accent); }
.owner img { display: block; border-radius: 50%; }
.site-nav { display: flex; gap: 2px; margin-right: auto; }
.site-nav a { padding: 6px 12px; border-radius: 9px; color: var(--ink-dim); font-size: 14.5px; font-weight: 550; }
.site-nav a:hover { color: var(--ink); background: var(--surface-hover); text-decoration: none; }
.site-nav a[aria-current="page"] { color: var(--ink); background: var(--pane); box-shadow: inset 0 0 0 1px var(--pane-line); }
.header-actions { display: flex; align-items: center; gap: 8px; }
.ds-menu {
  --ds-trigger-size: 34px; --ds-trigger-bg: var(--panel); --ds-trigger-border: var(--line-faint);
  --ds-hover: var(--surface-hover); --ds-surface: var(--panel-solid); --ds-border: var(--line);
  --ds-track: var(--surface-raised); --ds-selected: var(--control-selected);
  --ds-ink: var(--ink); --ds-ink-dim: var(--ink-dim); --ds-ink-faint: var(--ink-faint);
  --ds-focus: var(--accent); --ds-shadow: var(--shadow-float);
}

/* ---- Buttons and small controls */
.button { display: inline-flex; align-items: center; gap: 8px; height: 38px; padding: 0 15px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel); color: var(--ink); font-size: 14px; font-weight: 600; white-space: nowrap; }
.button:hover { background: var(--surface-hover); text-decoration: none; }
.button.primary { border-color: transparent; background: var(--ink); color: var(--panel); }
.button.primary:hover { background: var(--ink-dim); }
.button.small { height: 34px; padding: 0 12px; gap: 6px; font-size: 13.5px; }
.button.small .icon { width: 16px; height: 16px; }
.icon-link { display: inline-grid; place-items: center; width: 36px; height: 36px; border-radius: 9px; color: var(--ink-dim); }
.icon-link:hover { color: var(--ink); background: var(--surface-hover); }
.icon-link .icon { width: 20px; height: 20px; }
.pill { display: inline-flex; align-items: center; height: 26px; padding: 0 10px; border: 1px solid var(--line); border-radius: 7px; background: var(--panel); color: var(--ink-dim); font-size: 12.5px; font-weight: 600; cursor: pointer; list-style: none; user-select: none; }
.pill:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.chip { height: 30px; padding: 0 13px; border: 1px solid var(--line); border-radius: 999px; background: var(--pane); color: var(--ink-dim); font-size: 13px; font-weight: 600; cursor: pointer; }
.chip:hover { color: var(--ink); }
.chip[aria-pressed="true"] { border-color: transparent; background: var(--ink); color: var(--panel); }
.badge { display: inline-block; margin-left: 8px; padding: 0 8px; border-radius: 6px; background: var(--milestone-soft); color: var(--milestone); font-size: 11.5px; font-weight: 650; line-height: 20px; vertical-align: 1px; }
.ring-dot { flex: none; display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--ring, var(--accent)); }

/* ---- Hero */
.hero { display: flex; align-items: center; gap: 34px; padding: 52px 0 18px; }
.hero-avatar { flex: none; width: 128px; height: 128px; border-radius: 50%; border: 4px solid var(--panel); box-shadow: 0 0 0 1px var(--pane-line); object-fit: cover; }
.hero h1 { font-size: clamp(34px, 6vw, 46px); line-height: 1.1; letter-spacing: -.04em; font-weight: 700; }
.headline { margin-top: 8px; font-size: 19px; font-weight: 550; letter-spacing: -.01em; }
.focus { margin-top: 2px; color: var(--ink-dim); }
.open-to { margin-top: 10px; color: var(--milestone); font-weight: 600; }
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 20px; }
.hero-icons { display: flex; gap: 2px; }

/* ---- Sections */
.section { margin-top: 44px; }
.section > h2 { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; font-size: 22px; }
.section-lead { margin: -6px 0 16px; color: var(--ink-dim); }
.pane { background: var(--pane); border: 1px solid var(--pane-line); border-radius: var(--radius); }
.prose { padding: 24px 30px; font-size: 16px; }
.prose p { max-width: 74ch; }
.prose p + p { margin-top: 12px; }
.grid { display: grid; gap: 14px; }
.more { padding: 14px 0 12px; border-top: 1px solid var(--pane-line); font-size: 14px; font-weight: 600; }

/* ---- Selected work */
.work-grid { grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); }
.work-card { display: flex; flex-direction: column; gap: 8px; padding: 20px 22px; }
.work-meta { display: flex; justify-content: space-between; gap: 12px; color: var(--ink-faint); font-size: 12px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase; }
.work-card h3 { font-size: 17px; }
.work-card p { color: var(--ink-dim); font-size: 14.5px; }
.work-card .work-meta { color: var(--ink-faint); }
.work-links { display: flex; flex-wrap: wrap; gap: 14px; margin-top: auto; padding-top: 4px; font-size: 14px; font-weight: 600; }
.work-links a { display: inline-flex; align-items: center; gap: 4px; }

/* ---- Publications */
.pub { display: grid; grid-template-columns: 120px 1fr; gap: 18px; padding: 18px 0; border-top: 1px solid var(--pane-line); scroll-margin-top: 80px; }
.pub:target { margin-inline: -14px; padding-inline: 14px; border-radius: 14px; background: var(--accent-soft); }
.pub-thumb { width: 120px; height: 90px; display: grid; place-items: center; overflow: hidden; border: 1px solid var(--pane-line); border-radius: 10px; background: #fff; }
.pub-thumb img { display: block; max-width: 100%; max-height: 100%; object-fit: contain; }
.pub-thumb.empty { background: var(--surface-raised); color: var(--ink-faint); }
.pub-thumb.empty .icon { width: 28px; height: 28px; }
.pub.no-thumb { grid-template-columns: 1fr; }
.pub-title { font-size: 16px; letter-spacing: -.01em; line-height: 1.35; font-weight: 620; }
.pub-authors { margin-top: 3px; color: var(--ink-dim); font-size: 14px; line-height: 1.5; }
.pub-authors strong { color: var(--ink); font-weight: 650; }
.pub-authors sup { font-size: 10px; }
.pub-venue { margin-top: 2px; color: var(--ink-dim); font-size: 13.5px; }
.venue { color: var(--ink); font-weight: 650; }
.pub-links { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.bib-toggle[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.bib-box { position: relative; margin-top: 10px; }
.bib-box pre { overflow-x: auto; padding: 12px 14px; border-radius: 10px; background: var(--surface-raised); color: var(--ink); font: 12px/1.55 var(--mono); }
.bib-box .copy { position: absolute; top: 8px; right: 8px; }
.copy[data-done]::after { content: " ✓"; margin-left: 4px; }
.compact-list { padding: 2px 24px 0; }
.compact-list > .pub:first-child { border-top: 0; }
.compact-list .pub-links { margin-top: 8px; }

.page-head { padding: 44px 0 8px; }
.page-head h1 { font-size: 34px; letter-spacing: -.035em; }
.lead { margin-top: 6px; color: var(--ink-dim); font-size: 16px; }
.pub-list { margin-top: 16px; }
.pub-year { display: grid; grid-template-columns: 88px 1fr; column-gap: 20px; }
.pub-year .year { grid-column: 1; grid-row: 1 / span 40; align-self: start; position: sticky; top: 72px; padding-top: 18px; color: var(--ink-faint); font-size: 20px; font-variant-numeric: tabular-nums; }
.pub-year .pub { grid-column: 2; }

/* ---- Lab */
.app-grid { grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); }
/* A Lab app on the Lab page: logo, name and Open; what it is for beside its
   intro video (one column on narrow screens). */
.app-card { display: flex; flex-direction: column; gap: 18px; padding: 24px 28px; }
.app-head { display: flex; align-items: center; gap: 12px; }
.app-head .button { flex: none; margin-left: auto; }
.app-main { display: flex; align-items: center; gap: 14px; color: var(--ink); }
.app-main:hover { text-decoration: none; }
.app-main:hover .app-name { color: var(--accent); }
.app-logo img { display: block; width: 52px; height: 52px; border-radius: 13px; }
.app-name { font-size: 20px; font-weight: 650; letter-spacing: -.02em; }
.app-body.with-video { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: start; gap: 28px; }
.app-tagline { color: var(--ink-dim); font-size: 15.5px; line-height: 1.6; }
.app-video { display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; border-radius: 12px; background: #0b1424; box-shadow: 0 0 0 1px var(--pane-line); }
/* An app's text (apps.yaml tagline) may have paragraphs and a list. */
:is(.app-tagline, .lab-text) :is(p + p, ul) { margin-top: 8px; }
:is(.app-tagline, .lab-text) ul { padding-left: 1.25em; }
:is(.app-tagline, .lab-text) li + li { margin-top: 3px; }
:is(.app-tagline, .lab-text) li::marker { color: var(--accent); }
.more-link { margin-top: 12px; font-size: 14px; font-weight: 600; }
.page-head h1 { display: flex; align-items: center; gap: 12px; }
.lab-list { display: grid; gap: 16px; margin-top: 18px; }

/* ---- News */
.news-page { margin-top: 18px; }
.news-list { padding: 2px 24px; list-style: none; }
.news-item { display: grid; grid-template-columns: 8px 76px 1fr; gap: 14px; align-items: baseline; padding: 12px 0; border-top: 1px solid var(--pane-line); }
.news-item:first-child { border-top: 0; }
.news-item time { color: var(--ink-faint); font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.news-text { font-size: 14.5px; }

/* ---- Footer */
.site-footer { margin-top: 64px; padding: 26px 0 36px; border-top: 1px solid var(--pane-line); color: var(--ink-faint); font-size: 13.5px; }
.footer-inner { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding: 0 max(clamp(20px, 3vw, 44px), env(safe-area-inset-right)) 0 max(clamp(20px, 3vw, 44px), env(safe-area-inset-left)); }
.site-footer a.email { color: var(--ink-dim); }
.footer-links { display: flex; gap: 2px; }

.not-found { padding-bottom: 40px; }

/* ================================================================ the scene
   The home page (a full-width cover, three screens tall in all) and the News,
   Publications and Lab pages float over an illustrated scene that changes
   with the language and the theme (docs/home-redesign.md): body.scenic. */

/* The scenes (sky, pictures, ink, accent and glass) are in scene.css, shared
   with LifeRPG. The robot's own colors, the same on every scene: */
body.scenic {
  --robot-shell: #f2ece1; --robot-panel: #4d8785; --robot-face: #252a2d;
  --robot-eye: #65e7e8; --robot-joint: #343a3d; --robot-light: #e5a13a;
}

/* Everything else floats over the scene. */
.scenic .site-header { position: fixed; left: 0; right: 0; }
/* The banner (once scrolled) and the footer share one translucent color,
   --chrome (scene.css). */
.scenic .site-header.scrolled { background: var(--chrome); box-shadow: 0 1px 0 var(--chrome-line); -webkit-backdrop-filter: none; backdrop-filter: none; }
.scenic main, .scenic .site-footer { position: relative; z-index: 1; }
/* The other pages start below the banner, which floats over the scene. */
.scenic.sub main { padding-top: 64px; }
.scenic .glass, .scenic .pane {
  background: var(--glass); border: 1px solid var(--glass-line); border-radius: var(--radius);
  box-shadow: 0 12px 40px rgba(10, 20, 40, .06);
}
.scenic .button { background: var(--glass); border-color: var(--glass-line); color: var(--ink); }
.scenic .button:hover { background: color-mix(in srgb, var(--glass) 70%, var(--ink) 8%); }
.scenic .button.primary { background: var(--ink); border-color: transparent; color: var(--glass-solid, #fff); }
:root[data-theme="dark"] .scenic .button.primary { color: #0b0f1e; }
.scenic .site-footer { margin-top: 0; padding: 22px 0 26px; background: var(--chrome); border-top: 1px solid var(--chrome-line); color: var(--ink-dim); }

/* Cover: the first screen. */
/* The cover leads one continuous page: 40 px below the banner, and the next
   section close behind it. Its large type makes it stand out. */
.cover { position: relative; padding: 104px 0 36px; }
.kicker { display: inline-block; color: var(--kicker); font: 600 15px/1 ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: .02em; }
.cover h1 { margin-top: 18px; font-size: clamp(50px, 5.6vw, 80px); line-height: 1.02; letter-spacing: -.035em; }
:root[lang^="zh"] .cover h1 { letter-spacing: -.01em; }
.cover .headline { margin-top: 18px; font-size: 22px; font-weight: 600; letter-spacing: -.01em; }
/* A link to an app (LifeRPG) shows the app's logo, a small ad for it. */
.headline .app-link { white-space: nowrap; }
.headline .app-link img { display: inline-block; width: 1.15em; height: 1.15em; margin-right: .3em; border-radius: .28em; vertical-align: -.22em; box-shadow: 0 0 0 1px var(--glass-line); }
/* The lead is as wide as its quote's line (paragraphs, at width 0, do not
   widen it; they fill it), at least 62ch, and it keeps clear of the robot
   game in the corner (210 px). */
.cover .lead { width: fit-content; min-width: min(62ch, 100%); max-width: calc(100% - 240px); margin-top: 16px; color: var(--ink-dim); font-size: 18px; line-height: 1.6; }
.cover .lead > p { width: 0; min-width: 100%; }
.cover .lead p + p { margin-top: 10px; }
/* A quote in the lead (a paragraph that starts with ">"): the owner's own
   motto, set as an epigraph, in a serif italic under a large opening mark. */
.cover .lead blockquote { position: relative; margin-top: 22px; padding-left: 40px; color: var(--ink); text-wrap: balance; font: italic 400 21px/1.45 "STIX Two Text", Georgia, "Times New Roman", serif; letter-spacing: 0; }
.cover .lead blockquote::before { content: "\201C"; position: absolute; left: -2px; top: -.12em; color: var(--kicker); font: 400 64px/1 "STIX Two Text", Georgia, "Times New Roman", serif; }
/* Math in a quote: the same STIX design as the text, so no scaling; \text{…}
   is italic too, as it would be in TeX, and "log" stays upright. */
.cover .lead blockquote math { font-size: 1em; }
.cover .lead blockquote mtext { font-family: "STIX Two Text", Georgia, serif; font-style: italic; }
:root[lang^="zh"] .cover .lead blockquote { font-family: "STIX Two Text", "Kaiti SC", STKaiti, KaiTi, var(--font-body); font-style: normal; font-size: 22px; }
/* Where the Chinese quote needs two lines (phones), break it only at its
   spaces, between whole phrases: balanced, it otherwise split 正相关 and
   left half of each line empty. A phrase too long for the screen still breaks. */
:root[lang^="zh"] .cover .lead blockquote p { word-break: keep-all; overflow-wrap: anywhere; }
:root[lang^="zh"] .cover .lead blockquote mtext { font-family: "Kaiti SC", STKaiti, KaiTi, var(--font-body); font-style: normal; }
/* $math$ in the content (build.mjs): set in a math font, as TeX would set it;
   Chinese words in it take the page's Chinese font. */
math { font-family: "STIX Two Math", "Cambria Math", "Latin Modern Math", var(--font-body); font-size: 1.12em; }
.cover .edu { margin-top: 12px; color: var(--ink-faint); font-size: 13.5px; letter-spacing: .01em; }
.cover-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 28px; }
.cover-icons { display: flex; gap: 2px; margin-left: 4px; }
.cover .icon-link { color: var(--ink-dim); }
.cover-email { display: inline-flex; align-items: center; gap: 8px; margin-left: 8px; color: var(--ink-dim); font-size: 14px; }
.cover-email .icon { width: 16px; height: 16px; }

/* The robot game's placeholder. */
/* The robot game (robotGame in build.mjs, "The robot game" in app.js): small,
   in the cover's bottom-right corner, plain lines on a floor line, no box and
   no words. The robot keeps its own colors in both themes (cream shell, dark
   lines and visor, cyan eyes), so the eyes stay clear on the visor; the floor
   follows the page's ink. */
.cover-inner { position: relative; display: block; }
/* The name and the robot share a row on phones; elsewhere the row is not a
   box, and the robot sits in the cover's corner. */
.name-row { display: contents; }
.robot-game {
  position: absolute; right: 0; bottom: -6px; width: 210px;
  user-select: none; -webkit-user-select: none;
  --rg-shell: #f4efe6; --rg-ink: #2b3136; --rg-visor: #22272b; --rg-glow: #45e0e8; --rg-ear: #4d8785; --rg-light: #f0a83c; --rg-low: #e5484d;
}
.robot-game svg { display: block; width: 100%; height: auto; overflow: visible; touch-action: none; }
.rg-floor { fill: none; stroke: var(--ink); stroke-width: 1.5; stroke-linecap: round; opacity: .45; }
.rg-cable { fill: none; stroke: var(--rg-ink); stroke-width: 2; stroke-linecap: round; }
:root[data-theme="dark"] .rg-cable { stroke: var(--rg-shell); }
.rg-shell { fill: var(--rg-shell); stroke: var(--rg-ink); stroke-width: 1.8; stroke-linejoin: round; }
.rg-mark { fill: none; stroke: var(--rg-ink); stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
.rg-ear { fill: var(--rg-ear); stroke: var(--rg-ink); stroke-width: 1.6; }
.rg-visor { fill: var(--rg-visor); stroke: var(--rg-ink); stroke-width: 1.6; }
.rg-eyes { fill: none; stroke: var(--rg-glow); stroke-width: 2.4; stroke-linecap: round; }
.rg-eyes-open { fill: var(--rg-glow); }
.rg-eyes-tired { fill: var(--rg-glow); opacity: 0; }
.rg-charge { fill: var(--rg-glow); transform-box: fill-box; transform-origin: left center; transition: transform 1.6s ease-in-out, fill .3s; }
.rg-plug { cursor: grab; outline: none; }
.rg-plug:focus-visible .rg-shell { stroke: var(--accent); stroke-width: 2.6; }
.robot-game.dragging .rg-plug { cursor: grabbing; }
.rg-hit { fill: transparent; stroke: none; }
/* Metal prongs: gray enough to show on light and dark scenes. */
.rg-prongs { fill: none; stroke: #8d959c; stroke-width: 2; stroke-linecap: round; }
:root[data-theme="dark"] .rg-prongs { stroke: #c4cbd1; }
.rg-socket .rg-shell { transition: stroke .2s, stroke-width .2s; }
.robot-game.near .rg-socket .rg-shell { stroke: var(--rg-glow); stroke-width: 2.6; }
/* Poses on transforms, so each change animates. */
.rg-upper, .rg-head, .rg-arm, .rg-legs, .rg-eyes, .rg-eyes-open, .rg-eyes-tired, .rg-antenna { transform-box: fill-box; transition: transform .6s cubic-bezier(.3, 1.4, .5, 1), opacity .3s, fill .3s; }
.rg-legs { transform-origin: center bottom; }
.rg-head { transform-origin: center bottom; }
.rg-arm-l { transform-origin: right top; }
.rg-arm-r { transform-origin: left top; }
.rg-eyes-happy { opacity: 0; }
/* Out of battery: sitting on its feet, head down, arms hanging, eyes half shut. */
.robot-game[data-state="empty"] :is(.rg-upper) { transform: translateY(15px); }
.robot-game[data-state="empty"] .rg-legs { transform: scaleY(.12); }
.robot-game[data-state="empty"] .rg-head { transform: translateY(2px) rotate(-10deg); }
.robot-game[data-state="empty"] .rg-arm-l { transform: rotate(12deg); }
.robot-game[data-state="empty"] .rg-arm-r { transform: rotate(-12deg); }
.robot-game[data-state="empty"] .rg-eyes-open { opacity: 0; }
.robot-game[data-state="empty"] .rg-eyes-tired { opacity: .75; }
.robot-game[data-state="empty"] .rg-charge { transform: scaleX(.15); fill: var(--rg-low); animation: rg-blink 1.2s steps(2, jump-none) infinite; }
/* Charging: still sitting, eyes open, the battery fills. */
.robot-game[data-state="charging"] .rg-upper { transform: translateY(15px); }
.robot-game[data-state="charging"] .rg-legs { transform: scaleY(.12); }
/* Charged: it stands, smiles, its antenna lights up and it waves. */
.robot-game[data-state="happy"] .rg-eyes-open { opacity: 0; }
.robot-game[data-state="happy"] .rg-eyes-happy { opacity: 1; }
.robot-game[data-state="happy"] .rg-antenna { fill: var(--rg-light); }
.robot-game[data-state="happy"] .rg-arm-r { animation: rg-wave .45s ease-in-out .5s 4 alternate; }
@keyframes rg-blink { 50% { opacity: .2; } }
@keyframes rg-wave { from { transform: rotate(0); } to { transform: rotate(-140deg); } }
@media (prefers-reduced-motion: reduce) {
  .robot-game * { transition: none !important; animation: none !important; }
}

/* The two content screens. */
.band { position: relative; padding: 28px 0 8px; }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin: 30px 0 16px; }
.section-title { font-size: 26px; letter-spacing: -.02em; }
.head-link { flex: none; font-size: 14px; font-weight: 600; }
:root[lang^="zh"] .section-title { font-size: 27px; letter-spacing: .04em; }
/* Selected work. Each media box has the shape of its picture (--r, width /
   height, from the build), so a picture loses at most a thin edge. The first
   card is wide: picture left, text right. The others share one row, and each
   grows in proportion to its --r, so all their pictures are the same height
   (the flex basis is the card's padding and border, which do not grow). */
.home .work-card { --pad: 16px; gap: 14px; padding: var(--pad); }
.home .work-card.feature { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; gap: 28px; margin-bottom: 16px; }
.work-card.feature .work-body { padding: 8px 12px 8px 0; }
.home .work-card.feature h3 { font-size: 20px; }
.work-row { display: flex; gap: 16px; }
.work-row .work-card { flex: var(--r) 1 calc(2 * var(--pad) + 2px); min-width: 0; }
.work-media { position: relative; aspect-ratio: var(--r, 4 / 3); overflow: hidden; border-radius: 14px; }
/* A hover preview (app.js) covers the card's picture or placeholder. */
:is(.work-media, .lab-media) > .preview { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.work-media img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* Touch screens with reduced motion or Data Saver, or a browser that will not
   play by itself: the whole picture is a button that plays or pauses the
   preview (app.js), with a small mark in its corner. */
.preview-toggle { position: absolute; z-index: 1; inset: 0; width: 100%; padding: 0; border: 0; background: none; cursor: pointer; }
.preview-toggle:focus-visible { outline-offset: -3px; border-radius: 14px; }
.pt-mark { position: absolute; right: 10px; bottom: 10px; width: 34px; height: 34px; display: grid; place-items: center; border-radius: 50%; background: rgba(10, 14, 24, .55); color: #fff; }
.pt-mark svg { width: 18px; height: 18px; }
.pt-play { fill: currentColor; }
.pt-pause { fill: none; stroke: currentColor; stroke-width: 2.6; stroke-linecap: round; }
.playing .pt-play, .work-media:not(.playing) .pt-pause { display: none; }
/* Until the owner adds a picture: the organization's name on a soft wash. */
.work-media.placeholder { display: grid; place-items: center; background:
  radial-gradient(120% 90% at 0% 0%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 70%),
  radial-gradient(120% 90% at 100% 100%, color-mix(in srgb, var(--kicker) 20%, transparent), transparent 70%),
  color-mix(in srgb, var(--glass) 80%, var(--ink) 4%); }
.work-media.placeholder span { color: color-mix(in srgb, var(--ink) 45%, transparent); font: 600 20px/1 var(--font-head); letter-spacing: .02em; }
.work-body { display: flex; flex: 1; flex-direction: column; gap: 6px; min-width: 0; padding: 0 4px 2px; }
.home .work-card h3 { font-size: 17px; }
.work-text { color: var(--ink-dim); font-size: 14px; line-height: 1.55; }
.home-pubs { margin-top: 0; padding: 2px 24px; }
.home-pubs > .pub:first-child { border-top: 0; }
.scenic .pub { border-top-color: var(--glass-line); }
.scenic .pub-venue .venue-full { color: var(--ink-faint); }
/* Lab: a picture, the logo and name, one line. A card with a preview plays it on hover. */
.lab-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.lab-card { display: flex; flex-direction: column; gap: 12px; padding: 14px 14px 18px; color: var(--ink); transition: transform .25s, box-shadow .25s; }
.lab-card:hover { text-decoration: none; transform: translateY(-3px); box-shadow: 0 18px 50px rgba(10, 20, 40, .12); }
.lab-media { position: relative; display: block; aspect-ratio: 16 / 10; overflow: hidden; border-radius: 14px; background: color-mix(in srgb, var(--glass) 70%, var(--ink) 6%); }
.lab-media img, .lab-media video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .5s; }
.lab-card:hover .lab-media > img:first-child { transform: scale(1.03); }
.lab-title { display: flex; align-items: center; gap: 10px; padding: 0 4px; font-size: 18px; letter-spacing: -.02em; }
.lab-title img { border-radius: 9px; }
.lab-card:hover .lab-title strong { color: var(--accent); }
.lab-text { display: block; padding: 0 4px; color: var(--ink-dim); font-size: 14.5px; line-height: 1.55; }
/* Life: LifeRPG's skill net, embedded (/liferpg/?embed=skills). */
.life-embed { overflow: hidden; padding: 0; }
.life-embed iframe { display: block; width: 100%; height: 660px; border: 0; background: transparent; }
.home .news-list { padding: 2px 22px; }
.scenic .news-item { border-top-color: var(--glass-line); }
/* Open sky at the end of each page over the scene, so its last picture shows
   before the footer instead of behind the content. */
.scene-window { position: relative; height: 26vh; min-height: 180px; }
/* The publications page's list floats over the scene in one pane. */
.sub .pub-list { padding: 4px 28px 8px; }
.sub .pub-list > .pub-year:first-child > .pub:first-of-type { border-top: 0; }

/* Chinese text is 仿宋, which is thin: one or two px larger than English. */
:root[lang^="zh"] .cover .headline { font-family: var(--font-head); font-weight: 700; font-size: 24px; }
:root[lang^="zh"] .cover .lead { font-size: 20px; }
:root[lang^="zh"] .cover .edu, :root[lang^="zh"] .cover-email { font-size: 15px; }
:root[lang^="zh"] :is(.work-text, .lab-text, .news-text, .app-tagline, .section-lead, .page-head .lead) { font-size: 16px; }
:root[lang^="zh"] :is(.site-nav a, .button, .head-link, .more-link, .pub-venue, .pub-authors) { font-size: 15.5px; }
:root[lang^="zh"] .site-footer { font-size: 15px; }

/* Narrower laptops and tablets: too narrow for three cards in a row. Every
   work card has its picture left and its text right; a tall picture is
   narrower, so that no card is much taller than the others. */
@media (max-width: 1000px) {
  .app-body.with-video { grid-template-columns: 1fr; gap: 18px; }
  .work-row { flex-direction: column; }
  .home .work-card, .home .work-card.feature { display: grid; grid-template-columns: min(44%, calc(var(--r) * 210px)) minmax(0, 1fr); align-items: center; gap: 22px; margin-bottom: 0; }
  .work-row .work-card { flex: none; }
  .home .work-card.feature { margin-bottom: 16px; }
  .home .work-card.feature h3 { font-size: 17px; }
  .home .work-card .work-body { padding: 4px 4px 4px 0; }
}

/* ================================================================ phones
   A phone app, not a small laptop page (owner, 2026-10-10). The banner is one
   row: the owner, CV and the settings. The sections are tabs along the bottom
   (tabBar in build.mjs). While the page scrolls down, banner and tabs slide
   away; they come back when it scrolls up, near the top and at the end
   (app.js: body.bars-hidden), as in Robot Overflow. No panes: the content
   lies on the scene. No footer: the cover has the links. The home page shows
   only the cover and Selected work; the tabs lead to the rest. */
.tab-bar { display: none; }
@media (max-width: 760px) {
  :root { --bar-h: 56px; --tab-h: 54px; }
  body { background: var(--surface); }
  /* Separator lines that show on every scene. */
  .scenic { --sep: color-mix(in srgb, var(--ink) 13%, transparent); }

  /* The banner. */
  .site-header { padding-top: env(safe-area-inset-top); transition: transform .25s ease, opacity .25s ease, background-color .25s, box-shadow .25s; }
  .header-inner { min-height: var(--bar-h); gap: 10px; padding: 0 max(16px, env(safe-area-inset-right)) 0 max(16px, env(safe-area-inset-left)); }
  .owner { margin-right: auto; gap: 9px; font-size: 17px; }
  .owner img { width: 32px; height: 32px; }
  .site-nav { display: none; }
  /* The bars are opaque (the bar color over the top of the sky), so text
     that scrolls under them does not show through; a blur over the moving
     scene would cost too much. */
  .scenic { --bar-solid: linear-gradient(var(--bar), var(--bar)) var(--sky-top); }
  .scenic .site-header.scrolled { background: var(--bar-solid); }

  /* The tab bar. */
  .tab-bar {
    position: fixed; z-index: 50; left: 0; right: 0; bottom: 0; display: flex; justify-content: space-around;
    padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
    background: color-mix(in srgb, var(--surface) 92%, transparent); box-shadow: 0 -1px 0 var(--line-faint);
    transition: transform .25s ease, opacity .25s ease;
  }
  /* The tab bar lets the scene show through, softly (owner, 2026-10-10):
     its bar color at four fifths over a blur of what is under it, so
     the labels stay clear over a dark picture and text under it stays quiet. */
  .scenic .tab-bar { background: color-mix(in srgb, var(--bar) 80%, transparent); box-shadow: 0 -1px 0 var(--chrome-line); -webkit-backdrop-filter: blur(20px) saturate(1.5); backdrop-filter: blur(20px) saturate(1.5); }
  .scenic .tab-bar a:not([aria-current]) { color: var(--ink-dim); }
  .tab-bar a { flex: 1; max-width: 120px; min-height: var(--tab-h); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; padding: 6px 2px 5px; color: var(--ink-faint); font-size: 10.5px; font-weight: 550; line-height: 1.2; white-space: nowrap; }
  .tab-bar a:hover { color: var(--ink-dim); text-decoration: none; }
  .tab-bar a:focus-visible { outline-offset: -4px; border-radius: 12px; }
  .tab-bar a[aria-current="page"] { color: var(--accent); font-weight: 650; }
  .tab-bar .icon { width: 24px; height: 24px; }
  .tab-bar a[aria-current="page"] .icon { stroke-width: 1.9; }
  :root[lang^="zh"] .tab-bar a { font-size: 11.5px; font-weight: 400; }
  :root[lang^="zh"] .tab-bar a[aria-current="page"] { font-weight: 600; }

  /* Both slide away while the page scrolls down. */
  .bars-hidden .site-header { transform: translateY(-100%); opacity: 0; }
  .bars-hidden .tab-bar { transform: translateY(100%); opacity: 0; }

  .site-footer { display: none; }
  body:not(.scenic) main { padding-bottom: calc(var(--tab-h) + env(safe-area-inset-bottom) + 32px); }
  .scenic.sub main { padding-top: calc(var(--bar-h) + env(safe-area-inset-top)); }

  /* No panes: lists and cards lie on the scene, with lines between items. */
  .scenic :is(.glass, .pane) { background: none; border: 0; border-radius: 0; box-shadow: none; }
  .scenic :is(.pub, .news-item) { border-top-color: var(--sep); }

  /* The scene is larger on phones (scene.css). */
  /* Open sky for the figures; the tab bar covers its ground. */
  .scene-window { height: calc(var(--tab-h) + env(safe-area-inset-bottom) + min(24vh, 52vw)); }

  /* Cover: no CV button (the banner has it); Google Scholar is an icon like
     the others; the robot is small, in the top-right corner. */
  .cover { padding: calc(var(--bar-h) + env(safe-area-inset-top) + 30px) 0 8px; }
  .cover h1 { font-size: 44px; }
  .cover .headline { margin-top: 14px; font-size: 20px; line-height: 1.35; }
  .cover .lead { max-width: none; font-size: 16.5px; }
  .cover .lead blockquote { margin-top: 18px; padding-left: 34px; font-size: 19px; }
  .cover .lead blockquote::before { font-size: 54px; }
  :root[lang^="zh"] .cover .headline { font-size: 21px; }
  :root[lang^="zh"] .cover .lead { font-size: 18px; }
  /* Smaller on the narrowest phones, so that its longer phrase still fits a line. */
  :root[lang^="zh"] .cover .lead blockquote { font-size: min(20px, 5.4vw); }
  .cover-actions { gap: 2px; margin-top: 20px; margin-left: -8px; }
  .cover-cv { display: none !important; }
  .cover .cover-scholar { width: 36px; height: 36px; padding: 0; justify-content: center; border: 0; border-radius: 9px; background: none; color: var(--ink-dim); }
  .cover .cover-scholar:hover { background: var(--surface-hover); }
  .cover-scholar .icon { width: 20px; height: 20px; }
  .cover-scholar .label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  /* The icons and the address in one row. */
  .cover-actions { flex-wrap: wrap; }
  .cover .icon-link, .cover .cover-scholar { width: 34px; }
  .cover-icons { margin-left: 0; }
  .cover-email, :root[lang^="zh"] .cover-email { gap: 6px; margin-left: 6px; font-size: 13.5px; white-space: nowrap; }
  /* The robot stands at the end of the name's row, its floor line on the
     name's baseline (the box's bottom edge is on the baseline; the floor is
     10 of the drawing's 150 units above it). Standing up, it is as tall as
     the name's font size. A tap anywhere on it plugs it in or out (app.js). */
  .name-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
  .robot-game { position: static; flex: none; width: 86px; translate: 0 calc(86px * 10 / 220); }

  /* Home: the cover and Selected work only. */
  .home :is(#news, #publications, #lab, #life) { display: none; }
  .band { padding: 8px 0 0; }
  .section-head { margin: 24px 0 14px; }
  .section-title { font-size: 24px; }

  /* Selected work: a column of pictures, each with its text below. */
  .home .work-card, .home .work-card.feature { display: flex; flex-direction: column; gap: 12px; margin: 0; padding: 0; }
  .work-row { flex-direction: column; gap: 0; }
  .work-row .work-card { flex: none; margin-top: 52px; }
  .home .work-card .work-body { padding: 0 2px; }
  .home .work-card.feature h3 { font-size: 18px; }
  .home .work-card h3 { font-size: 18px; }
  .work-text { font-size: 15px; }
  .work-links { padding-top: 2px; }

  /* The other pages. The tab bar names the page, so its title is only for
     screen readers. */
  .page-head { padding: 20px 0 4px; }
  .page-head h1 { font-size: 32px; }
  .sub .page-head { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .news-page { margin-top: 8px; }
  .news-list, .home .news-list { padding: 0; }
  .news-item { grid-template-columns: 8px 1fr; row-gap: 2px; padding: 14px 0; }
  .news-item time, .news-item .news-text { grid-column: 2; }
  .news-text { font-size: 15.5px; }
  .pub-list, .sub .pub-list { margin-top: 4px; padding: 0; }
  .pub { grid-template-columns: 1fr; gap: 0; padding: 18px 0; }
  .pub-thumb, .venue-full { display: none; }
  .pub-year { grid-template-columns: 1fr; }
  .pub-year .year { position: static; grid-row: auto; padding-top: 22px; }
  .pub-year .pub { grid-column: 1; }
  .sub .pub-list > .pub-year .pub:first-of-type { border-top: 0; }
  .pub-links .pill { height: 32px; padding: 0 12px; border-radius: 9px; font-size: 13px; }
  .lab-list { gap: 0; margin-top: 8px; }
  .app-card { gap: 14px; padding: 0; }
  .app-card + .app-card { margin-top: 26px; padding-top: 26px; border-top: 1px solid var(--sep); }
  .app-body.with-video { grid-template-columns: 1fr; gap: 16px; }
  .app-main { gap: 12px; }
  .app-logo img { width: 44px; height: 44px; border-radius: 11px; }
  .app-name { font-size: 19px; }
  .lab-row { grid-template-columns: 1fr; }
  .life-embed iframe { height: 540px; }
}
