/* pane: one sheet of cold glass. Shared by every page (home, work, side, shelf). */

@font-face {
  font-family: "Inter";
  src: url("../fonts/inter-var.woff2") format("woff2-variations"), url("../fonts/inter-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face { font-family: "HanSans Subset"; src: url("../fonts/hansans-400.woff2") format("woff2"); font-weight: 400; font-display: swap; }
@font-face { font-family: "HanSans Subset"; src: url("../fonts/hansans-500.woff2") format("woff2"); font-weight: 500; font-display: swap; }
@font-face { font-family: "HanSans Subset"; src: url("../fonts/hansans-700.woff2") format("woff2"); font-weight: 600 700; font-display: swap; }

:root {
  --paper: #F6F9FB;
  --ice: #E6EEF3;
  --wall: #C9DBE6;
  --glacier: #9DBCCD;
  --blue: #3D6D8B;
  --deep: #22465C;
  --ink: #1C2832;
  --ink-2: rgba(28, 40, 50, .78);
  --hair: rgba(34, 70, 92, .16);
  /* (round 5) the blue and the grey for small type printed on the glass: a shade darker than on paper, so they keep
     4.5:1 on the lightest and the darkest glass (the paper's blue measured 4.35:1 on the old glass) */
  --blue-g: #2F5D79;
  --ink-2g: #4E5E69;
  --sans: "Inter", "PingFang SC", "HanSans Subset", "Hiragino Sans GB", "Microsoft YaHei", system-ui, -apple-system, sans-serif;
  --body: clamp(15px, calc(6.4px + .6vw), 16px);
  --ease: cubic-bezier(.22, .8, .22, 1);
  --out: cubic-bezier(.2, .75, .25, 1);        /* page content arriving: quick start, soft landing */
  font-synthesis: none;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--sans);
  font-size: var(--body);
  line-height: 1.7;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  font-feature-settings: "cv11", "ss03";
  font-style: normal;
}
:lang(zh-CN) { letter-spacing: .01em; }
a { color: inherit; text-decoration: none; }
b, strong { font-weight: 600; }
ul, ol { margin: 0; padding: 0; list-style: none; }
p, h1, h2, h3, figure { margin: 0; }
h1, h2, h3 { font-weight: 600; }
img { display: block; max-width: 100%; height: auto; }
svg { display: block; }
:focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; border-radius: 4px; }
.skip, .sr {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip:focus { position: fixed !important; left: 12px; top: 12px; width: auto; height: auto; clip: auto; z-index: 99; background: var(--paper); padding: 6px 12px; }

/* ================================================================== HOME */
/* the homepage scrolls: the cut sheet is the first screen (the hero box: sky, canvas, glass), the page below it
   follows (round 4, see the end of this file). The hero box is in the flow and leaves with a plain 1:1 scroll. */
/* (the About text box is laid out at 760 px before home.js measures it: never let that widen a phone's page) */
.pg-home { background: var(--ice); overflow-x: hidden; }
/* until home.js has laid the sheet out, nothing scrolls (an unmeasured text box would otherwise widen a phone's
   layout viewport for good) */
html.js:not(.is-laid) .pg-home { overflow: hidden; }
.sky {
  position: absolute; left: 0; top: 0; width: 100%; height: 100vh; z-index: 1;
  background: var(--ice) url("../assets/env/backdrop.webp") center / cover no-repeat;
}
/* (absolute, not fixed: the compositor scrolls the hero away with the page; a still canvas costs no GL frame) */
#gl { position: absolute; left: 0; top: 0; z-index: 2; width: 100vw; height: 100vh; display: block; }
/* the stacked sheet (phones, short screens) keeps its sky behind the whole scroll, as approved */
.is-scroll .sky { position: fixed; inset: 0; width: auto; height: auto; }
.sky, #gl, .glass { transition: opacity var(--home-t, .3s) linear; }
/* the printed text arrives with its glass, never before it */
.is-gl #gl, .is-gl .glass { opacity: 0; }
.is-gl.is-laid #gl, .is-gl.is-laid .glass { opacity: 1; }
.from-paper .glass, .from-paper #gl { transition: none; }
.from-paper .sky { opacity: 0; }
.from-paper body { background: var(--paper); }
.veil { position: fixed; inset: 0; background: var(--paper); opacity: 0; pointer-events: none; z-index: 50; }
/* the homepage while another page shows (app.js): laid out, never drawn, never in the way */
html.home-off .sky, html.home-off #gl, html.home-off #glass, html.home-off #glass .pane, html.home-off #glass .navstrip, html.home-off .veil { visibility: hidden !important; }
html.home-off #glass, html.home-off .sky, html.home-off #gl { position: fixed !important; }
/* leaving the homepage: in GL only the canvas fades (the printed text has already gone with its glass, and
   an opacity on the 3D text layer would flatten it); the CSS glass fades as a whole */
html.home-fade #gl, html.home-fade.is-css .sky, html.home-fade.is-css #glass { opacity: 0 !important; }

/* under an opaque canvas the CSS sky is never seen: keep it out of compositing */
.is-gl.is-laid:not(.from-paper) .sky { visibility: hidden; }
/* a page laid out once out of sight (app.js), so its first real layout finds warm caches */
.prelayout { position: absolute !important; left: 0; right: 0; top: 0; visibility: hidden !important; pointer-events: none !important; z-index: -1; }
html.home-snap .sky, html.home-snap #gl, html.home-snap #glass { transition: none !important; }

/* the DOM layer: every pane is a real element; in GL mode its 3D transform tracks the glass mesh exactly */
.glass {
  position: absolute; left: 0; top: 0; right: 0; height: 100vh; z-index: 3;
  perspective-origin: 50% 50%;
  transform-style: preserve-3d;
  pointer-events: none;
}
.sheet { position: absolute; inset: 0; transform-style: preserve-3d; margin: 0; }
.pane, .navstrip {
  position: absolute; left: 0; top: 0;
  transform-origin: 0 0;
  pointer-events: auto;
  visibility: hidden;          /* until laid out */
  -webkit-tap-highlight-color: transparent;
}
.is-laid .pane, .is-laid .navstrip { visibility: visible; }
.pane:focus-visible { outline: none; }
.edge { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }

/* --- nav strip */
.navstrip {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 22px 0 26px;
  font-size: 13px; line-height: 1; letter-spacing: .01em;
}
.nv-list { display: flex; gap: 28px; align-items: center; }
.nv { color: var(--deep); padding: 8px 0; position: relative; transition: color .2s; white-space: nowrap; }
.nv:hover, .nv[aria-current="page"] { color: var(--ink); }
.nv[aria-current="page"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 2px; height: 1px; background: currentColor; opacity: .5;
}
.lg { display: inline-flex; align-items: center; gap: 6px; color: var(--deep); }
.lg-a { padding: 8px 2px; opacity: .6; transition: opacity .2s; }
.lg-a[aria-current] { opacity: 1; color: var(--ink); font-weight: 500; }
.lg-a:hover { opacity: 1; }
.lg-s { opacity: .35; }

/* --- About pane */
/* --k scales the printed type a little on small or short screens (1, .95 or .9; set by home.js) */
.pane--about .ab { position: absolute; left: 0; top: 0; width: var(--abw, 300px); }
.ab { color: var(--ink); font-size: calc(var(--body) * var(--k, 1)); }
.ab-tag { font-size: calc(12px * var(--k, 1)); letter-spacing: .08em; color: var(--blue); line-height: 1.4; }
.ab-head > .ab-name:first-child { margin-top: 0; }
.ab-name {
  display: flex; align-items: baseline; flex-wrap: wrap; column-gap: 16px;
  margin-top: calc(18px * var(--k, 1));
  font-size: var(--name, 48px); line-height: 1.05; font-weight: 600; letter-spacing: -.02em;
}
.ab-name .n2 { font-size: max(15px, calc(var(--name, 48px) * .36)); font-weight: 450; letter-spacing: 0; color: var(--blue-g); }
.ab-role { margin-top: calc(12px * var(--k, 1)); font-size: calc(var(--body) * var(--k, 1)); font-weight: 500; color: var(--deep); }
.ab-cols { display: grid; grid-template-columns: 1fr 1fr; column-gap: calc(var(--colgap, 52px) * var(--k, 1)); margin-top: calc(var(--headgap, 40px) * var(--k, 1)); }
.is-narrow .ab-cols { grid-template-columns: 1fr; row-gap: 26px; }
.ab-intro li { position: relative; padding-left: 20px; line-height: 1.7; }
.ab-intro li + li { margin-top: calc(10px * var(--k, 1)); }
.ab-intro li::before { content: ""; position: absolute; left: 0; top: .85em; width: 9px; height: 1px; background: var(--blue); }
.ab-about { line-height: 1.7; }
.ab-blk { margin-top: calc(22px * var(--k, 1)); }
.ab-lbl { font-size: calc(12px * var(--k, 1)); font-weight: 600; letter-spacing: .12em; color: var(--blue-g); line-height: 1.4; margin-bottom: 6px; }
:lang(en) .ab-lbl { letter-spacing: .06em; text-transform: none; }
.ab-now { line-height: 1.65; font-feature-settings: "tnum"; }
.ab-hello { line-height: 1.6; }
.ab-links { display: flex; flex-wrap: wrap; column-gap: 18px; row-gap: 2px; margin-top: 8px; font-size: 14px; }
.ab-links a, .ab-wx { display: inline-flex; align-items: center; gap: 7px; color: var(--deep); padding: 2px 0; }
.ab-links a:hover { color: var(--ink); }
.ab-links svg, .ab-cv svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.2; stroke-linecap: round; stroke-linejoin: round; flex: none; }
.ab-cv { display: flex; align-items: center; flex-wrap: wrap; gap: 7px; margin-top: 6px; font-size: 14px; color: var(--deep); }
.ab-cv-l { margin-right: 4px; }
/* (round 5) laptops: the About block on the page's columns: the name on the margin column, the text (intro, about,
   contact) in one column from the page's text axis; the intro's dashes hang in the gutter */
.pane--about .ab.is-ruled { display: grid; grid-template-columns: var(--c1, 311px) var(--c2, 444px); grid-template-rows: auto auto 1fr; min-height: var(--abh, 0px); column-gap: 28px; align-items: start; }
.ab.is-ruled .ab-head { grid-column: 1; grid-row: 1 / span 3; }
.ab.is-ruled .ab-cols, .ab.is-ruled .ab-side { display: contents; }
.ab.is-ruled .ab-intro { grid-column: 2; grid-row: 1; }
.ab.is-ruled .ab-intro li { padding-left: 0; }
.ab.is-ruled .ab-intro li::before { left: -19px; }
.ab.is-ruled .ab-about { grid-column: 2; grid-row: 2; margin-top: calc(14px * var(--k, 1)); }
.ab.is-ruled .ab-contact { grid-column: 2; grid-row: 3; align-self: end; }
.ab.is-ruled .ab-intro li, .ab.is-ruled .ab-about, .ab.is-ruled .ab-hello { text-wrap: pretty; }
.ab.is-ruled .ab-name { margin-top: 0; padding-top: 2px; }
/* short laptop screens (home.js 'tight' step): same type sizes, tighter leading and spacing */
.is-tight .ab-name { margin-top: 12px; }
.is-tight .ab-role { margin-top: 8px; }
.is-tight .ab-cols { margin-top: 22px; }
.is-tight .ab-intro li, .is-tight .ab-about, .is-tight .ab-now { line-height: 1.58; }
.is-tight .ab-intro li + li { margin-top: 6px; }
.is-tight .ab-blk { margin-top: 14px; }
.is-tight .ab-lbl { margin-bottom: 3px; }
.is-tight .ab-links { margin-top: 4px; }
.ab-cv a { color: var(--ink); border-bottom: 1px solid var(--hair); line-height: 1.3; }
.ab-cv a:hover { border-color: var(--blue); }
.ab-sep { opacity: .4; }
/* the two domains named in the intro sentences link to their sites (the sentence itself is unchanged) */
.ab-intro .ab-dom { white-space: nowrap; color: inherit; border-bottom: 1px solid var(--hair); transition: border-color .2s, color .2s; }
.ab-intro .ab-dom:hover { color: var(--deep); border-color: var(--blue); }
#contact.is-ping { animation: ping 1.4s var(--ease); }
@keyframes ping { 0%, 100% { background: transparent; } 25% { background: rgba(157, 188, 205, .28); } }

/* --- the work panes (round 5): each printed with the index's own content at the page's sizes: number and dates
   (13), title (16), subtitle (16, grey). The printed text stays on hover; a hover adds an image beside it (GL plate /
   CSS window) or, for GEO and the game, the 负责 line (.ph) */
.pl, .ph { position: absolute; left: 0; top: 0; }
.pl {
  font-size: 16px; font-weight: 400; color: var(--ink); line-height: 24px; white-space: nowrap; letter-spacing: 0;
  transition: opacity .18s var(--ease), transform .3s var(--ease);
}
.pl-k { display: flex; gap: 14px; margin-bottom: 7px; font-size: 13px; line-height: 21px; color: var(--ink-2g); font-variant-numeric: tabular-nums; }
.pl-no { color: var(--blue-g); }
.pl-t, .pl-s { display: block; }
.pl-t { transition: color .15s; }
.pl-s { color: var(--ink-2g); }
.pl-s .nw { white-space: nowrap; }
.pane.is-hot .pl-t { color: var(--deep); }
.ph {
  width: var(--phw, 300px);
  opacity: 0; transform: translate3d(0, 8px, 0);
  transition: opacity .2s var(--ease), transform .3s var(--ease);
}
.ph-t { display: block; }
.ph-t1, .ph-t2, .ph-own { text-wrap: pretty; }
.ph-t1 { display: block; font-size: calc(21px * var(--k, 1)); font-weight: 600; line-height: 1.25; letter-spacing: -.01em; color: var(--ink); }
.ph-t2 { display: block; margin-top: 6px; font-size: calc(15px * var(--k, 1)); font-weight: 500; line-height: 1.45; color: var(--blue); }
.ph-own { display: block; margin-top: 12px; font-size: 13px; line-height: 1.65; color: var(--deep); }
.pane .ph-own { margin-top: 0; line-height: 21px; color: var(--ink-2g); }
/* hover text never below 13 px (the compact step tightens the title and the spacing, not the small text) */
.is-compact .ph-t1 { font-size: calc(18px * var(--k, 1)); }
.is-compact .ph-t2 { font-size: max(13px, calc(13.5px * var(--k, 1))); margin-top: 4px; }
.is-compact .ph-own { margin-top: 8px; line-height: 1.55; }
.pane.is-hot .ph { opacity: 1; transform: none; transition-delay: .03s; }
.pane .ph { transform: translate3d(0, 4px, 0); }
.pane.no-ph .ph { display: none; }

/* fly-away: the DOM text fades while the glass leaves */
/* in GL mode the text flies with its glass (opacity is driven per pane); the CSS fallback just fades */
.is-css.is-leaving .pane > *, .is-css.is-leaving .navstrip > * { opacity: 0 !important; transition: opacity .18s linear !important; }
.is-gl.is-leaving .pane .ph, .is-gl.is-leaving .pane .pl { transition: none !important; }
.is-leaving .glass { pointer-events: none; }

/* ================================================================== CSS MODE (no WebGL / phones / reduced motion) */
.is-css .pane, .is-css .navstrip {
  background: linear-gradient(160deg, rgba(255, 255, 255, .56), rgba(240, 246, 250, .34) 55%, rgba(225, 236, 243, .42));
  -webkit-backdrop-filter: blur(16px) saturate(1.08);
  backdrop-filter: blur(16px) saturate(1.08);
}
.is-css .pane.is-hot {
  -webkit-backdrop-filter: saturate(1.05); backdrop-filter: saturate(1.05);
  /* etched only around the text; the image behind the rest of the pane stays clear */
  background: radial-gradient(ellipse calc(var(--phw, 260px) * .78 + 40px) 130px at var(--tx, 30%) var(--ty, 70%), rgba(246, 249, 251, .9), rgba(246, 249, 251, .55) 55%, rgba(240, 246, 250, 0) 100%);
}
.is-css .pane--plain.is-hot {
  background: radial-gradient(ellipse 170px 56px at var(--tx, 30%) var(--ty, 70%), rgba(246, 249, 251, .85), rgba(246, 249, 251, 0) 100%);
}
.is-css .pane, .is-css .navstrip { transition: transform .3s var(--ease), background .25s; }
.is-css .edge path.e1 { fill: none; stroke: rgba(255, 255, 255, .95); stroke-width: 2; }
.is-css .edge path.e2 { fill: none; stroke: rgba(61, 109, 139, .22); stroke-width: 1; }
.is-css .ph { background: none; }
.shadows { position: absolute; inset: 0; pointer-events: none; }
.shadows svg { position: absolute; left: 0; top: 0; overflow: visible; }
.css-shots { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
/* overflow hidden as well as the clip path: images larger than their pane must never widen the page */
.css-win { position: absolute; left: 0; top: 0; overflow: hidden; }
.css-shots img {
  position: absolute; left: 0; top: 0; max-width: none; border-radius: 0; box-shadow: none; outline: 1px solid rgba(34, 70, 92, .14); outline-offset: -1px;
  opacity: 0; transition: opacity .22s var(--ease), transform .34s var(--ease);
}
.css-shots img.on { opacity: 1; }
/* CSS glass on a laptop: the sheet is the first screen and scrolls away with the page; stacked (phones): the
   sheet is in the flow and the story follows it */
.is-css .glass { position: absolute; left: 0; top: 0; right: 0; height: 100vh; overflow-x: clip; }
.is-css.is-scroll .glass { position: relative; height: auto; }
.is-css.is-scroll .sky { background-image: url("../assets/env/backdrop-mobile.webp"); }
@media (max-width: 759px) {
  .navstrip { padding: 7px 14px 7px 16px; flex-wrap: wrap; row-gap: 2px; font-size: 12.5px; }
  .nv-list { gap: 4px 14px; flex-wrap: wrap; }
  .navstrip .nv { padding: 4px 0; }
  .navstrip .lg-a { padding: 4px 2px; }
  .lg { margin-left: auto; }
  .pane--about .ab { font-size: 15px; }
}
@media (prefers-reduced-motion: reduce) {
  /* 0s, not .01s: a non-zero duration turns every element's default "all" into a transition, and the layout
     would then measure the type before --k takes effect (reduced motion stacked on 1280x800) */
  *, *::before, *::after { transition-duration: 0s !important; animation-duration: .01s !important; }
}

/* ================================================================== INNER PAGES (clean paper) */
.pg-work, .pg-side, .pg-shelf { background: var(--paper); }
.top { position: relative; z-index: 10; height: 108px; }
/* the header of clean pages while the homepage shows (kept, with its shard, for the next page) */
.top.top-off { position: fixed; left: 0; top: 0; width: 100%; visibility: hidden; }
.shard {
  position: fixed; left: 20px; top: 14px; width: 168px; height: 96px; z-index: 20;
  display: block; color: var(--ink);
}
.shard-gl { position: absolute; inset: 0; width: 100%; height: 100%; }
.shard-t {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  font-size: 18px; font-weight: 500; line-height: 1; pointer-events: none;
}
.no-shardgl .shard-gl { display: none; }
.no-shardgl .shard::before {
  content: ""; position: absolute; left: 26px; top: 22px; right: 34px; bottom: 26px;
  clip-path: polygon(6% 30%, 86% 4%, 100% 92%, 10% 100%);
  background: linear-gradient(150deg, rgba(255, 255, 255, .9), rgba(201, 219, 230, .7));
  box-shadow: inset 0 0 0 1px rgba(61, 109, 139, .3);
}
.navline {
  position: absolute; right: 32px; top: 30px;
  display: flex; align-items: center; gap: 30px; font-size: 13px; line-height: 1;
}
.navline .nv-list { gap: 26px; }

.plainpg { max-width: 1120px; margin: 0 auto; padding: 56px 40px 140px; }
.ip:focus, .plainpg:focus, .sheet:focus { outline: none; }

/* page content arriving (app.js adds .v-enter for one play): 0.34 s, the first blocks 30 ms apart */
.v-enter > * { animation: v-in .34s var(--out) both; }
.v-enter > :nth-child(2) { animation-delay: .03s; }
.v-enter > :nth-child(3) { animation-delay: .06s; }
.v-enter > :nth-child(n+4) { animation-delay: .09s; }
.v-enter > .shard, .v-enter-r > .shard, .v-enter > .ibar, .v-enter-r > .ibar { animation: none; }
@keyframes v-in { from { opacity: 0; transform: translate3d(0, 14px, 0); } to { opacity: 1; transform: none; } }
/* reduced motion: a plain fade */
.v-enter-r > * { animation: v-fade .12s linear both; }
@keyframes v-fade { from { opacity: 0; } to { opacity: 1; } }
/* page content leaving: lifted over whatever comes next (no layout change), fading */
.v-leave { opacity: 0 !important; transition: opacity .16s linear !important; pointer-events: none; z-index: 15; }
.ip.v-leave, .plainpg.v-leave { position: relative; }
.sh-gl .shelfpg.v-leave { position: fixed; }
/* a stacked homepage coming back under a page that is still scrolled */
html.home-pin #glass { position: fixed !important; top: var(--home-pin, 0) !important; }
@media (prefers-reduced-motion: reduce) {
  .v-leave { transition-duration: .12s !important; }
  .v-enter-r > * { animation: v-fade .12s linear both !important; }
  .sky, #gl, .glass { transition: opacity .12s linear !important; }
}

/* (fix 1: the project pages and 业余项目 are set on the round-4 page; their rules are at the end of this file) */
.pg-title { font-size: clamp(32px, 2.9vw, 42px); line-height: 1.15; letter-spacing: -.02em; }
.pg-lede { margin-top: 22px; max-width: 680px; color: var(--ink-2); }
.pg-foot { margin-top: 64px; max-width: 680px; color: var(--ink-2); font-size: 14px; }
/* page veil for leaving inner pages */
.pg-work .veil, .pg-side .veil, .pg-shelf .veil { background: var(--ice); }

@media (max-width: 860px) {
  /* phones: the shard top left, the language switch top right, the five links in one quiet row below */
  .top { height: 126px; }
  .shard { left: 8px; top: 8px; width: 132px; height: 76px; }
  .navline { position: absolute; left: 22px; right: 22px; top: 92px; gap: 0; justify-content: flex-start; flex-wrap: nowrap; font-size: 12.5px; }
  .navline .nv-list { gap: 0 15px; flex-wrap: nowrap; white-space: nowrap; overflow-x: auto; scrollbar-width: none; }
  .navline .nv-list::-webkit-scrollbar { display: none; }
  .navline .lg { position: absolute; right: 0; top: -62px; }
  .navline .nv { padding: 5px 0; }
  .navline .nv[aria-current="page"]::after { bottom: 1px; }
  .plainpg { padding: 28px 22px 100px; }
}


/* ================================================================== SHELF
   static (no WebGL / reduced motion): a calm cover grid with every line visible.
   .sh-gl (WebGL): the covers float in 3D; the page keeps only tiny corner text and a glass card. */
.sh-stage, .sh-layer, .sh-arr { display: none; }
.sh-filter { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-top: 40px; }
.sf {
  appearance: none; border: 0; background: none; padding: 4px 0; margin: 0; cursor: pointer;
  font: inherit; font-size: 13px; line-height: 1.3; color: var(--deep); letter-spacing: .02em;
  display: inline-flex; align-items: center; gap: 9px; transition: color .25s;
}
.sf-d { width: 6px; height: 6px; border-radius: 50%; border: 1px solid var(--blue); transition: background .25s, transform .4s var(--ease); flex: none; }
.sf:hover { color: var(--ink); }
.sf[aria-pressed="true"] { color: var(--ink); }
.sf[aria-pressed="true"] .sf-d { background: var(--blue); transform: scale(1.15); }
.bk-g { margin-top: 48px; transition: opacity .4s var(--ease); }
.is-filter .bk-g:not(.is-on) { opacity: .32; }
.bk-gn { font-size: 13px; font-weight: 600; letter-spacing: .1em; color: var(--blue); }
:lang(en) .bk-gn { letter-spacing: .04em; }
.bk-list { margin-top: 18px; display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 32px 24px; }
.bk-b { appearance: none; border: 0; background: none; padding: 0; margin: 0; font: inherit; color: inherit; text-align: left; display: block; width: 100%; cursor: default; }
.bk-cv { display: block; aspect-ratio: 2 / 3; border-radius: 3px; overflow: hidden; background: var(--ice); box-shadow: 0 0 0 1px var(--hair), 0 18px 34px -24px rgba(34, 70, 92, .45); }
.bk-cv img { width: 100%; height: 100%; object-fit: cover; filter: saturate(.55) brightness(1.04) hue-rotate(-4deg); transition: filter .45s var(--ease); }
.bk-b:hover .bk-cv img, .bk-b:focus-visible .bk-cv img { filter: none; }
.bk-cv--none {
  display: flex; align-items: center; justify-content: center; padding: 14px; text-align: center;
  background: linear-gradient(160deg, rgba(255, 255, 255, .85), rgba(230, 238, 243, .7) 60%, rgba(201, 219, 230, .6));
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .9), 0 0 0 1px var(--hair);
}
.bk-cvt { font-size: 13px; font-weight: 500; color: var(--deep); line-height: 1.5; }
.bk-tx { display: block; }
.bk-t { display: block; margin-top: 12px; font-size: 14px; font-weight: 600; line-height: 1.4; }
.bk-a { display: block; font-size: 12.5px; color: var(--deep); margin-top: 2px; }
.bk-st { display: inline-block; margin-top: 6px; font-size: 11.5px; line-height: 1; padding: 3px 8px; border-radius: 999px; border: 1px solid var(--hair); color: var(--blue); }
.bk-n { display: block; margin-top: 8px; font-size: 13px; line-height: 1.65; color: var(--ink-2); text-wrap: pretty; }

/* ---------------------------------------------------------------- GL shelf */
html.sh-gl, .sh-gl body { height: 100%; overflow: hidden; }
.sh-gl body { overscroll-behavior: none; }
.sh-gl .sh-stage { display: block; position: fixed; inset: 0; z-index: 0; touch-action: none; }
.sh-gl .sh-stage canvas { display: block; width: 100%; height: 100%; opacity: 0; transition: opacity .25s linear; }
.sh-gl.sh-on .sh-stage canvas { opacity: 1; }
/* the shelf's stage, prepared out of sight on another page: in the document (so its canvas already has
   its compositor resources), invisible, never in the way */
.sh-stage.sh-parked { display: block; position: fixed; inset: 0; z-index: -1; opacity: 0; pointer-events: none; }
.sh-stage.sh-parked canvas { display: block; width: 100%; height: 100%; }
/* leaving the shelf: the books are thrown while the next page comes over them; the card goes at once */
.sh-exit .sh-stage { pointer-events: none; }
.sh-gl.sh-exit .sh-stage canvas { opacity: 0; transition: opacity .15s linear; }
.sh-exit .sh-layer { opacity: 0; transition: opacity .12s linear; }
.shelfpg.v-leave, .sh-exit .shelfpg { opacity: 0; transition: opacity .12s linear; }
.sh-gl .sh-stage.is-grab { cursor: grabbing; }
.sh-gl .sh-stage.is-point { cursor: pointer; }
.sh-gl .sh-layer { display: block; position: fixed; inset: 0; z-index: 6; pointer-events: none; transform-style: preserve-3d; perspective-origin: 50% 50%; overflow: hidden; }
.sh-card {
  position: absolute; left: 0; top: 0; transform-origin: 0 0; width: var(--cw, 268px);
  padding: 20px 22px 22px; opacity: 0; visibility: hidden;
}
.sh-card.on { visibility: visible; }
.sh-card .c-t { display: block; font-size: 15px; font-weight: 600; line-height: 1.45; color: var(--ink); text-wrap: pretty; }
.sh-card .c-a { display: block; margin-top: 3px; font-size: 12.5px; line-height: 1.5; color: var(--deep); }
.sh-card .c-st { display: inline-block; margin-top: 9px; font-size: 11.5px; line-height: 1; padding: 3px 8px; border-radius: 999px; border: 1px solid rgba(61, 109, 139, .28); color: var(--blue); }
.sh-card .c-st:empty { display: none; }
.sh-card .c-n { display: block; margin-top: 11px; font-size: 13px; line-height: 1.7; color: var(--ink-2); text-wrap: pretty; }

.sh-gl .shelfpg {
  position: fixed; inset: 0; max-width: none; margin: 0; padding: 0; z-index: 4; pointer-events: none;
}
.sh-gl .shelfpg > * { pointer-events: auto; }
.sh-gl .sh-head { position: absolute; left: 36px; bottom: 32px; width: min(410px, 32vw); }
.sh-gl .sh-head .pg-title { font-size: 32px; letter-spacing: -.01em; }
.sh-gl .sh-head .pg-lede { margin-top: 12px; font-size: 13px; line-height: 1.75; max-width: none; }
.sh-gl .sh-filter { position: absolute; left: 38px; top: 138px; margin: 0; flex-direction: column; gap: 2px; }
.sh-gl .sf { font-size: 12.5px; }
.sh-gl .pg-foot { position: absolute; right: 36px; bottom: 32px; margin: 0; width: min(330px, 26vw); font-size: 12px; line-height: 1.7; text-align: right; }
.sh-gl .sh-head, .sh-gl .sh-filter, .sh-gl .pg-foot { text-shadow: 0 0 2px var(--paper), 0 0 14px var(--paper), 0 0 28px var(--paper); }
.sh-gl .sh-arr {
  display: flex; gap: 6px; position: absolute; left: 0; right: 0; top: 30px; margin: 0 auto; width: max-content;
  padding: 3px; border-radius: 999px;
}
.sh-arr button {
  appearance: none; border: 0; background: none; margin: 0; padding: 7px; cursor: pointer; border-radius: 50%;
  color: var(--glacier); transition: color .25s, background .25s;
}
.sh-arr button:hover { color: var(--deep); }
.sh-arr button.on { color: var(--ink); background: rgba(201, 219, 230, .38); }
.sh-arr svg { width: 14px; height: 14px; fill: currentColor; }
.sh-arr svg .o { fill: none; stroke: currentColor; stroke-width: 1.6; }
/* the accessible list stays in the page (focusing a book selects it in 3D) but is not drawn */
.sh-gl #shelf-root {
  position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; margin: 0; padding: 0;
}
.sh-gl.sh-sel .sh-head, .sh-gl.sh-sel .pg-foot { opacity: .35; transition: opacity .4s var(--ease); }

@media (max-width: 860px) {
  .bk-list { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 26px 16px; }
  .sh-gl .shelfpg { display: flex; flex-direction: column; justify-content: flex-end; padding: 0 18px calc(16px + env(safe-area-inset-bottom)); }
  .sh-gl .sh-head { position: static; width: auto; }
  .sh-gl .sh-head .pg-title { font-size: 24px; }
  .sh-gl .sh-head .pg-lede { margin-top: 8px; font-size: 12px; line-height: 1.7; }
  .sh-gl .pg-foot { position: static; width: auto; text-align: left; margin-top: 8px; font-size: 11.5px; line-height: 1.6; }
  .sh-gl .sh-filter { top: 134px; left: 22px; right: 58px; flex-direction: row; flex-wrap: wrap; gap: 0 14px; }
  .sh-gl .sf { font-size: 12px; gap: 7px; }
  .sh-gl .sh-arr { left: auto; right: 10px; top: 128px; margin: 0; flex-direction: column; gap: 0; }
  .sh-gl.sh-sel .sh-head, .sh-gl.sh-sel .pg-foot { opacity: 0; }
}


/* ================================================================== THE PAGE BELOW THE GLASS (round 4)
   One object (the glass sheet), then paper. The page hangs from the hero's own lines: --m (the About text edge, 84 px
   at 1440) and --tx (the About second column, 423 zh / 443 en at 1440), written by js/story.js from the hero layout.
   Three sizes (13 / 16 / 32; phones 13 / 16 / 24), two weights (400 / 600), ink on paper, one blue, one rule.
   No glass, blur, shadow, radius, gradient or scroll-linked motion below the hero (the seam is the hero's own sky).
   Spec: site-v7/round4/DESIGN-SPEC.md. Everything is scoped to .page (the story container), .bar and .seam. */
.page, .bar {
  --ink-2: #5B6C78;          /* 5.14:1 on paper, 4.63:1 on ice: meta, subtitles, figure numbers */
  --hair: rgba(34, 70, 92, .2);
  --keyline: rgba(34, 70, 92, .14);
  --fs: 16px; --lh: 28px;    /* text (zh 16/28 = the module); en 16/26 */
  --fs-s: 13px; --lh-s: 21px;
  --fs-t: 32px; --lh-t: 40px;
  --m: 5.86vw;               /* fallbacks until story.js writes the hero's own values */
  --tx: 29.36vw;
  --g: 28px;                 /* gutter = one line of text */
  --tw: 512px;               /* measure: 32 em = 32 Han characters a line */
  --gap: calc(var(--lh) * 5);
  --bar: 48px;
  --e4: cubic-bezier(.4, 0, .2, 1);
}
.page.l-en, .bar.l-en { --lh: 26px; --lh-s: 20px; }

.story { position: relative; z-index: 4; overflow-x: clip; }
.page {
  display: flow-root;
  font-size: var(--fs); line-height: var(--lh); font-weight: 400; color: var(--ink); background: var(--paper);
  font-feature-settings: "cv11", "ss03"; font-optical-sizing: auto;
}
.page, .page *, .bar, .bar * { letter-spacing: 0; }
.page h1, .page h2, .page h3 { font-size: inherit; font-weight: inherit; }
.page b, .page strong { font-weight: 600; }
.page.l-zh b, .page.l-zh strong, .page.l-zh .hd, .page.l-zh .ap-h, .page.l-zh .sp-n { font-variation-settings: "wght" 495; }  /* Inter's stem = PingFang Semibold */
.page :focus-visible, .bar :focus-visible { outline: 2px solid var(--blue); outline-offset: 3px; border-radius: 0; }
.page .nw, .bar .nw { white-space: nowrap; }
.page .no, .page .nu, .page .ix-no, .page .ix-d, .page .sn, .page .plate-no, .bar .rh-no { font-variant-numeric: tabular-nums; }

/* the homepage while another page shows: no height, never drawn; leaving / coming back: fades with the hero */
.story, .seam { transition: opacity var(--home-t, .3s) linear; }
html.home-off .story, html.home-off .seam, html.home-off .bar { position: fixed !important; left: 0; top: 0; width: 100%; height: 0; margin: 0; overflow: hidden; visibility: hidden !important; }
html.home-fade .story, html.home-fade .seam, html.home-fade .bar { opacity: 0 !important; }
html.home-snap .story, html.home-snap .seam, html.home-snap .bar { transition: none !important; }

/* ---------------------------------------------------------------- text setting */
.page .tx { text-wrap: pretty; }
/* (fix 1) tags and subtitles balanced: no single word or tag alone on a line (python-pptx, MIDI, weeks, references);
   (fix 2) the 独立负责 line pretty: the longer line first, its 「、」 items bound by build.py (never 推广 alone) */
.page .mt-c, .page .t2 { text-wrap: balance; }
.page .own { text-wrap: pretty; }
.page.l-zh .tx {                               /* Chinese: set solid, justified on whole characters (W3C clreq) */
  text-align: justify; text-justify: auto;
  text-spacing-trim: trim-start; text-autospace: normal;
}
.page.l-en .tx { text-align: start; hyphens: manual; }

/* ---------------------------------------------------------------- the seam: the hero's sky pales into paper over 140 px
   (its own bottom row of pixels, blurred along the row, (1 - t)^2.3; drawn once per layout, static) */
.seam { position: relative; z-index: 4; height: 140px; margin-top: 100vh; background: var(--paper); }
.is-scroll .seam { margin-top: 0; }
.seam--veil { background: transparent; }            /* phones: paper fading in over the stacked sheet's fixed sky */
.seam canvas { display: block; width: 100%; height: 100%; opacity: 0; }
.seam.on canvas { opacity: 1; }

/* ---------------------------------------------------------------- the one grid every block hangs from */
.page .cols {
  display: grid;
  grid-template-columns: [m-start] calc(var(--tx) - var(--m) - var(--g)) [m-end t-start] var(--tw) [t-end e-start] minmax(0, 1fr) [e-end];
  column-gap: var(--g);
}
.page .ix, .page .wk, .page .ap { padding-inline: var(--m); }

/* ---------------------------------------------------------------- the bar: running head + the strip's words (the only
   fixed element below the hero; flat paper; hidden over the hero, back on scroll-up, away on scroll-down) */
.bar {
  position: fixed; z-index: 10; left: 0; right: 0; top: 0; height: var(--bar);
  background: var(--paper); border-bottom: 1px solid var(--hair); color: var(--ink);
  transform: translateY(-100%); visibility: hidden;
  transition: transform .3s var(--e4), visibility 0s linear .3s, opacity var(--home-t, .3s) linear;
}
.bar.on { transform: none; visibility: visible; transition: transform .3s var(--e4), visibility 0s, opacity var(--home-t, .3s) linear; }
.bar-in {
  height: 100%; padding-inline: var(--m);
  display: grid; align-items: center; column-gap: var(--g);
  grid-template-columns: [m-start] calc(var(--tx) - var(--m) - var(--g)) [m-end t-start] minmax(0, 1fr) [t-end] auto auto;
  font-size: var(--fs-s); line-height: var(--lh-s);
}
.bar .rh { grid-column: m-start / t-end; display: grid; grid-template-columns: subgrid; white-space: nowrap; }
.bar .rh-no { color: var(--blue); }
.bar .rh-t { color: var(--ink); overflow: hidden; }
.bar-nav .nw, .page .ft-nav .nw { display: flex; gap: 24px; }
.bar-nav .nv, .page .ft-nav .nv, .bar .lg-a, .page .ft .lg-a { display: block; padding: 6px 0; opacity: 1; font-weight: 400; color: var(--ink-2); transition: color .15s; white-space: nowrap; }
.bar-nav .nv:hover, .page .ft-nav .nv:hover, .bar .lg-a:hover, .page .ft .lg-a:hover { color: var(--ink); }
.bar-nav .nv[aria-current="page"], .page .ft-nav .nv[aria-current="page"] { color: var(--ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 6px; }
.bar .nv::after, .page .nv::after { content: none; }
.bar .lg, .page .ft .lg { display: inline-flex; align-items: center; gap: 6px; margin-left: 0; color: var(--ink-2); }
.bar .lg-a[aria-current], .page .ft .lg-a[aria-current] { color: var(--ink); }
.bar .lg-s, .page .ft .lg-s { opacity: 1; color: var(--ink-2); }

/* ---------------------------------------------------------------- index of the four works (on the hero's two axes) */
.page .ix { margin-top: var(--lh); }
.page .ix-a { align-items: baseline; padding: 14px 0 13px; border-bottom: 1px solid var(--hair); }
.page .ix li:first-child .ix-a { border-top: 1px solid var(--deep); }
.page .ix-m { grid-column: m-start / m-end; display: flex; gap: 24px; font-size: var(--fs-s); line-height: var(--lh-s); }
.page .ix-no { color: var(--blue); }
.page .ix-d { color: var(--ink-2); }
.page .ix-t { grid-column: t-start / e-end; display: flex; flex-wrap: wrap; column-gap: .75em; }  /* the subtitle moves whole to the next line */
.page .ix-t .t1 { color: var(--ink); transition: color .15s; }
.page .ix-t .t2 { color: var(--ink-2); }
.page .ix-a:hover .t1 { color: var(--blue); }

/* ---------------------------------------------------------------- a work: one constant template (the Canon page) */
.page .wk { margin-top: var(--gap); scroll-margin-top: var(--bar); }
.page > .wk:first-child { margin-top: var(--lh); }       /* (round 5: the four-row index is gone; the panes print it) */
.page .wk-h { border-top: 1px solid var(--deep); padding-top: var(--lh); }
.page .wk-h > .no { grid-column: m-start / m-end; grid-row: 1; font-size: var(--fs-t); line-height: var(--lh-t); color: var(--blue); }
.page .wk-t { grid-column: t-start / e-end; grid-row: 1; font-size: var(--fs-t); line-height: var(--lh-t); }
.page .wk-t:focus { outline: none; }
.page .wk-t .t1, .page .wk-t .t2 { display: block; }
.page .wk-t .t1 { color: var(--ink); }
.page .wk-t .t2 { color: var(--ink-2); }
.page .meta { grid-column: m-start / m-end; grid-row: 2; align-self: baseline; margin-top: 14px; font-size: var(--fs-s); line-height: var(--lh-s); color: var(--ink-2); }
.page .meta p + p { margin-top: 7px; }
.page .own { grid-column: t-start / t-end; grid-row: 2; align-self: baseline; margin-top: 14px; }

.page .wk-b { margin-top: calc(var(--lh) * 1.5); align-items: start; }
.page .wk-case { grid-column: m-start / t-end; grid-row: 1; display: grid; grid-template-columns: subgrid; }
.page .sec { grid-column: m-start / t-end; display: grid; grid-template-columns: subgrid; }
.page .wk-case .sec { grid-column: 1 / -1; }
.page .sec + .sec { margin-top: var(--lh); }
.page .hd { grid-column: 1; font-weight: 600; color: var(--ink); }      /* head at text size, on the first line of its text */
.page .sec .tx { grid-column: 2; }
.page .steps li { position: relative; }
.page .sn { position: absolute; right: calc(100% + 12px); top: 1px; font-size: var(--fs-s); line-height: var(--lh); color: var(--blue); }

/* plates: real screenshots at their own proportions, flat, square, numbered; in the evidence column */
.page .plates { grid-column: e-start / e-end; grid-row: 1; max-width: var(--tw); }   /* never wider than the text column */
.page .plate + .plate { margin-top: var(--lh); }
.page .pl-a { display: block; cursor: zoom-in; }      /* a plate opens its 1200 px file (js/app.js: over the page) */
.page .plate img { width: 100%; outline: 1px solid var(--keyline); outline-offset: -1px; }
.page .plate-no { margin-top: 7px; font-size: var(--fs-s); line-height: var(--lh-s); color: var(--ink-2); }

.page .go { margin-top: var(--lh); }
.page .wk > .go { padding-left: calc(var(--tx) - var(--m)); }
.page .go a { display: inline-flex; align-items: center; gap: 8px; color: var(--blue); transition: color .15s; }
.page .go .ar { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.3; transition: transform .15s var(--e4); }
.page .go a:hover { color: var(--deep); }
.page .go a:hover .ar { transform: translateX(3px); }

/* ---------------------------------------------------------------- 业余项目, 书架, 联系 */
.page .ap { margin-top: var(--gap); scroll-margin-top: var(--bar); }
.page .ap-in { border-top: 1px solid var(--deep); padding-top: var(--lh); }
.page .ap-h { grid-column: m-start / m-end; grid-row: 1; font-weight: 600; color: var(--ink); }
.page .ap-h:focus { outline: none; }
.page .ap-in > .tx, .page .ap-in > .go { grid-column: t-start / t-end; }

/* 业余项目: a catalogue of seven, left-aligned, the approved images at one ratio */
.page .sp-grid { grid-column: t-start / e-end; grid-row: 1; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); column-gap: var(--g); row-gap: calc(var(--lh) * 1.5); }
.page .spi { display: block; }
.page .sp-im { display: block; aspect-ratio: 16 / 10; background: var(--ice); }
.page .sp-im img { width: 100%; height: 100%; object-fit: cover; outline: 1px solid var(--keyline); outline-offset: -1px; }   /* flat, as the plates */
.page .sp-tx { display: block; }
.page .sp-n { display: block; margin-top: 14px; font-weight: 600; transition: color .15s; }
.page .sp-n .ext { display: inline-block; width: 12px; height: 12px; margin-left: 4px; vertical-align: -1px; fill: none; stroke: var(--blue); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; transition: transform .15s var(--e4); }
.page .sp-d { display: block; font-size: var(--fs-s); line-height: var(--lh-s); color: var(--ink-2); overflow-wrap: anywhere; }
.page a.spi:hover .sp-n { color: var(--blue); }
.page a.spi:hover .ext { transform: translate(2px, -2px); }
.page .ap-in > .sp-grid + .go { margin-top: calc(var(--lh) * 2); }

/* 书架: the real covers standing on two hairline shelves; the idle cold tint is baked into the files */
.page .sh-in { grid-row: 1; }
.page .shelves { grid-column: t-start / e-end; margin-top: calc(var(--lh) * 2); container-type: inline-size; }
.page .shelf { display: flex; align-items: flex-end; gap: 10px; border-bottom: 1px solid var(--hair); }
.page .shelf + .shelf { margin-top: var(--lh); }
.page .shelf li { flex: none; }
.page .cv { position: relative; display: block; height: min(120px, calc((100cqw - 90px) / var(--sum))); aspect-ratio: var(--r); }
.page .cv img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.page .cv .cv-o { opacity: 0; transition: opacity .3s var(--e4); }
.page .cv:hover .cv-o, .page .cv:focus-visible .cv-o { opacity: 1; }
.page .sh-bl { margin-top: calc(var(--lh) * 2); color: var(--ink-2); }

/* 联系: the colophon on the page's one ice field, closed by the strip's own words */
.page .co { margin-top: var(--gap); background: var(--ice); padding: calc(var(--lh) * 3) var(--m) 0; scroll-margin-top: var(--bar); }
.page .co-b { grid-column: t-start / e-end; }
.page .co-b > .tx { max-width: var(--tw); }
.page .co-l { margin-top: var(--lh); }
.page .co-l li { display: flex; align-items: center; gap: 10px; padding: 2px 0; }
.page .co-l svg { width: 16px; height: 16px; flex: none; fill: none; stroke: var(--deep); stroke-width: 1.2; stroke-linecap: round; stroke-linejoin: round; }
.page .co-l a, .page .co-wx { display: inline-flex; align-items: center; gap: 10px; }
.page .u, .page .co-cv a { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .28em; text-decoration-color: rgba(34, 70, 92, .3); transition: text-decoration-color .15s, color .15s; }
.page .co-l a:hover .u, .page .co-cv a:hover { color: var(--deep); text-decoration-color: var(--deep); }
.page .co-p { margin-left: 4px; color: var(--ink-2); }
.page.l-zh .co-p { font-feature-settings: "cv11", "ss03", "halt"; }   /* 「PDF 版：」: the colon's empty half (8 px) goes */
.page .co-s { color: var(--ink-2); }
.page .ft {
  margin-top: calc(var(--lh) * 4); border-top: 1px solid var(--hair); padding: 14px 0 calc(var(--lh) * 2);
  display: flex; justify-content: space-between; align-items: center; font-size: var(--fs-s); line-height: var(--lh-s);
}

/* ---------------------------------------------------------------- once-only fade of images that arrive late */
.page .fi { transition: opacity .24s ease-out; }
.page.motion .fi.ld { opacity: 0; }

/* ---------------------------------------------------------------- narrower laptops: no evidence column, plates after 方案 */
@media (max-width: 1319px) {
  .page .wk-b .wk-case { display: contents; }
  .page .wk-b .sec { grid-column: m-start / t-end; }
  .page .sec--bg { order: 1; } .page .sec--ch { order: 2; } .page .sec--so { order: 3; } .page .plates { order: 4; } .page .sec--va { order: 5; }
  .page .plates { grid-column: t-start / e-end; grid-row: auto; max-width: none; margin-top: var(--lh); display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: var(--g); align-items: end; }
  /* (fix 2) a pair stands on one line (figure numbers level); a single plate, or the last of an odd number, stands in
     the text column (never alone in half a row) */
  .page .plates > .plate:last-child:nth-child(odd) { grid-column: 1 / -1; width: 100%; max-width: var(--tw); }
  .page .plate + .plate { margin-top: 0; }
  .page .plates + .sec--va { margin-top: var(--lh); }
  .page .sp-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------------------------------------------------------------- phones and small tablets: one column on the hero's edge */
@media (max-width: 959px) {
  .page, .bar { --fs-t: 24px; --lh-t: 32px; --gap: calc(var(--lh) * 4); --bar: 44px; }
  .seam { height: 96px; }

  .bar-in { display: flex; justify-content: space-between; }
  .bar .rh, .bar .lg { display: none; }
  .bar-nav { flex: 1; }
  .bar-nav .nw { justify-content: space-between; gap: 0; }

  .page .cols { display: block; }
  /* (fix 1) the stacked hero's panes are the contents here (each glides to its work): no index on phones */
  .page .ix-a { display: block; padding: 12px 0 13px; }
  .page .ix-m { gap: 12px; }
  .page .ix-t .t2 { flex-basis: 100%; }      /* the subtitle always on its own line */

  /* numeral and employer · dates as one small kicker line, then the title, the own line, the tools */
  .page .wk-h { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 12px; padding-top: 20px; }
  .page .wk-h > .no { grid-column: 1; grid-row: 1; font-size: var(--fs-s); line-height: var(--lh-s); }
  .page .meta { display: contents; }
  .page .mt-w { grid-column: 2; grid-row: 1; font-size: var(--fs-s); line-height: var(--lh-s); color: var(--ink-2); }
  .page .wk-t { grid-column: 1 / -1; grid-row: 2; margin-top: 8px; }
  .page .own { grid-column: 1 / -1; grid-row: 3; margin-top: 14px; }
  .page .mt-c { grid-column: 1 / -1; grid-row: 4; margin-top: 7px; font-size: var(--fs-s); line-height: var(--lh-s); color: var(--ink-2); }
  .page .wk-b { display: flex; flex-direction: column; margin-top: var(--lh); }
  .page .wk-b .wk-case { display: contents; }
  .page .sec { display: block; }
  .page .hd { display: block; }
  .page .tx { max-width: 32em; }
  .page.l-zh .tx { text-align: start; }
  .page .sn { right: calc(100% + 9px); }
  .page .plates { display: block; align-self: stretch; margin-top: var(--lh); }   /* (full width before a lazy plate has loaded) */
  .page .plate + .plate { margin-top: var(--lh); }
  .page .wk > .go { padding-left: 0; }

  .page .ap-in { display: block; padding-top: 20px; }
  .page .ap-h { margin-bottom: 14px; }
  .page .sp-grid { display: block; }
  .page .sp-grid li + li { margin-top: 24px; }
  .page .spi { display: grid; grid-template-columns: 112px minmax(0, 1fr); column-gap: 16px; align-items: start; }
  .page .sp-n { margin-top: -4px; }
  .page .shelves { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px 8px; align-items: end; margin-top: var(--lh); }
  .page .shelf { display: contents; }
  .page .cv { height: auto; width: 100%; }
  .page .sh-bl { margin-top: var(--lh); }

  .page .co { padding-top: calc(var(--lh) * 2); }
  .page .co-in { display: block; }
  .page .co-l li { flex-wrap: wrap; }
  .page .ft { flex-direction: column; align-items: stretch; gap: 6px; }
  .page .ft-nav .nw { justify-content: space-between; gap: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .bar, .bar.on { transition: none; }
  .page .fi, .page .cv .cv-o, .page .go .ar, .page .sp-n .ext { transition: none; }
}

/* ================================================================== THE PROJECT PAGES AND 业余项目 (fix 1)
   Set on the same page as the homepage below the glass: .page gives the sizes (13 / 16 / 32, phones 13 / 16 / 24), the
   weights (400 / 600), the colours (ink / ink-2 / blue), the grid, the Chinese (16/28 justified, 32 em, trim-start) and
   English (16/26 flush left) text setting, the plates and the index rows; .ip adds what differs. These pages have no hero
   to measure, so the hero's own layout rules give its two lines (home.js layoutOnce / axes()): the outer axis m = the
   sheet margin + the About pane's padding (GL: 5.86 % of the width, at most 108 px), the text axis tx = m + the About
   pane's first column and gap (its text block is 45 % (zh) / 48 % (en) of the sheet, 560 to 800 px); stacked (below
   1100 px) m = 2 % + 4.2 % of the width and tx = m + 240; below 760 px m = 34. Within 3 px of the live homepage from 1280
   to 1920 (1440: m 84, tx 422 zh / 443 en). */
.page.ip {
  --m: min(5.86vw, 108px);
  --tx: calc(var(--m) + clamp(306px, 21.6vw + 26px, 426px));
  padding: 0 0 calc(var(--lh) * 5);
}
.page.ip.l-en { --tx: calc(var(--m) + clamp(306px, 23.04vw + 26px, 426px)); }
.page.ip .wk, .page.ip .ap { margin-top: 0; }
.page.ip .pg-title { font-size: inherit; line-height: inherit; letter-spacing: 0; }
/* the plates the homepage shows stand beside the text (as there); the others follow the case two-up, top-aligned, never
   wider than the text column */
.page .ip-pl { margin-top: calc(var(--lh) * 2); }
.page .ip-pl .plates {
  grid-column: t-start / e-end; grid-row: auto; max-width: none; margin-top: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, var(--tw))); column-gap: var(--g); row-gap: var(--lh); align-items: end;
}
.page .ip-pl .plate + .plate { margin-top: 0; }
.page .ip-pl .plate:last-child:nth-child(odd) { grid-column: 1; }
/* the other three works close the page, as rows of the index */
.page.ip .more > .ix { margin-top: var(--gap); }
/* 业余项目: the homepage's catalogue at a page's scale (two columns), each with the address it links to */
.page.ip .sp-grid--page { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: calc(var(--lh) * 2); }
.page .sp-u { display: block; font-size: var(--fs-s); line-height: var(--lh-s); color: var(--blue); overflow-wrap: anywhere; }

@media (max-width: 1099px) {
  .page.ip, .page.ip.l-en { --m: calc(clamp(18px, 2vw, 40px) + clamp(40px, 4.03vw, 72px)); --tx: calc(var(--m) + 240px); }
}
@media (max-width: 959px) {
  .page .ip-pl { margin-top: var(--lh); }
  .page .ip-pl .plates { display: block; margin-top: 0; }
  .page .ip-pl .plate + .plate { margin-top: var(--lh); }
  .page.ip .sp-grid li + li { margin-top: calc(var(--lh) * 1.5); }
  .page.ip .spi { display: block; }
  .page.ip .sp-n { margin-top: 14px; }
}
@media (max-width: 759px) {
  .page.ip, .page.ip.l-en { --m: 34px; --tx: var(--m); }
}

/* ---------------------------------------------------------------- a plate opened from its figure (js/app.js): the picture
   at its own size over the page on paper, its figure number under it; a click, Esc or any scroll closes it */
.lb {
  --lbm: max(24px, min(5.86vw, 84px));
  position: fixed; inset: 0; width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 0; border: 0;
  background: var(--paper); color: var(--ink); cursor: zoom-out; overflow: hidden;
  font-family: var(--sans); letter-spacing: 0; animation: lb-in .15s ease-out both;
}
.lb[open] { display: flex; align-items: center; justify-content: center; }
.lb::backdrop { background: transparent; }
.lb:focus { outline: none; }
.lb figure { display: flex; flex-direction: column; align-items: flex-start; gap: 7px; }
.lb img {
  max-width: calc(100vw - 2 * var(--lbm)); max-height: calc(100vh - 2 * var(--lbm) - 28px); width: auto; height: auto;
  outline: 1px solid rgba(34, 70, 92, .14); outline-offset: -1px;
}
.lb figcaption { font-size: 13px; line-height: 21px; color: #5B6C78; font-variant-numeric: tabular-nums; }
@keyframes lb-in { from { opacity: 0; } to { opacity: 1; } }

/* ================================================================== THE HEADER OF THE PROJECT PAGES AND 业余项目 (fix 2)
   The ← shard (header.top, shared with the shelf and contact pages, which keep theirs as it was) is absolute here: it
   scrolls away with the page and never covers its text. The round-3 nav line gives way to the page's own first row,
   flat as the homepage's bar (.ip-top: 13 px, ink-2, the current page underlined, weights 400 only; right edge at 100 %
   minus the margin, on phones 中 / EN beside the shard and the words from the margin). After it has scrolled away, the
   homepage's bar returns on scroll-up (.ibar, js/app.js): 「← 关于我」 on the margin, the other words and 中 / EN on the
   right edge; away on scroll-down. */
.pg-work .top, .pg-side .top { height: 0; }
.pg-work .shard, .pg-side .shard { position: absolute; }
.pg-work .top .navline, .pg-side .top .navline { display: none; }
.page.ip > .ibar + * { margin-top: calc(var(--lh) * 2); }

.page .ip-top {
  height: 108px; padding: 14px var(--m) 0;
  display: flex; align-items: center; justify-content: flex-end; gap: var(--g);
  font-size: var(--fs-s); line-height: var(--lh-s);
}
.page .ip-nav .nw, .page .ib-nav .nw { display: flex; gap: 24px; }
.page .ip-top .nv, .page .ibar .nv, .page .ib-back, .page .ip-top .lg-a, .page .ibar .lg-a {
  display: block; padding: 6px 0; opacity: 1; font-weight: 400; color: var(--ink-2); white-space: nowrap; transition: color .15s;
}
.page .ib-back { display: inline-flex; gap: 6px; }
.page .ip-top .nv:hover, .page .ibar .nv:hover, .page .ib-back:hover, .page .ip-top .lg-a:hover, .page .ibar .lg-a:hover { color: var(--ink); }
.page .ip-top .nv[aria-current="page"], .page .ibar .nv[aria-current="page"] { color: var(--ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 6px; }
.page .ip-top .lg, .page .ibar .lg { display: inline-flex; align-items: center; gap: 6px; margin-left: 0; color: var(--ink-2); }
.page .ip-top .lg-a[aria-current], .page .ibar .lg-a[aria-current] { color: var(--ink); }
.page .ip-top .lg-s, .page .ibar .lg-s { opacity: 1; color: var(--ink-2); }

.page .ibar {
  position: fixed; z-index: 10; left: 0; right: 0; top: 0; height: var(--bar);
  background: var(--paper); border-bottom: 1px solid var(--hair);
  transform: translateY(-100%); visibility: hidden;
  transition: transform .3s var(--e4), visibility 0s linear .3s;
}
.page .ibar.on { transform: none; visibility: visible; transition: transform .3s var(--e4), visibility 0s; }
.page .ibar-in { height: 100%; padding-inline: var(--m); display: flex; align-items: center; gap: var(--g); font-size: var(--fs-s); line-height: var(--lh-s); }
.page .ib-back { margin-right: auto; }

@media (max-width: 959px) {
  /* the bar on phones: 「← 关于我」 and the four other words spread across the field, as the homepage's (no 中 / EN) */
  .page .ibar .lg { display: none; }
  .page .ibar-in { justify-content: space-between; gap: 0; }
  .page .ib-back { margin-right: 0; }
  .page .ib-nav, .page .ib-nav .nw { display: contents; }
}
@media (max-width: 759px) {
  .page .ip-top {
    height: 126px; padding: 0 var(--m);
    display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: 84px auto; align-items: center; gap: 0;
  }
  .page .ip-top .lg { grid-column: 2; grid-row: 1; }
  .page .ip-nav { grid-column: 1 / -1; grid-row: 2; }
  .page .ip-nav .nw { justify-content: space-between; gap: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .page .ibar, .page .ibar.on { transition: none; }
}
