/* ════════════════════════════════════════════════════════════
   SCAFFOLD — motion and detail
   Layered on top of style.css. Everything here serves one idea:
   a structure that is built, that responds, and that steps back.
   ════════════════════════════════════════════════════════════ */

:root {
  --ease-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-expo: cubic-bezier(0.7, 0, 0.84, 0);
  --ease-settle: cubic-bezier(0.22, 1.2, 0.36, 1);
}

/* ── paper ──────────────────────────────────────────────────
   A faint tooth, so the page reads as a sheet, not a screen. */
html::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 95;
  pointer-events: none;
  opacity: 0.045;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.9 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

html.dark::after { opacity: 0.07; mix-blend-mode: screen; filter: invert(1); }

/* ── typography refinements ─────────────────────────────── */
h1, h2, .tile-title, .index-title { text-wrap: balance; }
p, li { hanging-punctuation: first; }
.hero-meta, .page-kicker, .tower-readout, .title-block { font-feature-settings: 'tnum' 1, 'zero' 1; }

/* ════════════════════════════════════════════════════════════
   1. CONSTRUCTION — letters are poured thin and wide, then
      compressed into their final heavy, condensed form
   ════════════════════════════════════════════════════════════ */

.word { display: inline-block; white-space: nowrap; }
/* Chinese may still break between characters */
.word:has(.cjk) { white-space: normal; display: inline; }

.ch {
  display: inline-block;
  font-variation-settings: 'wght' 800, 'wdth' 75;
  transition: font-variation-settings 0.5s var(--ease-expo), color 0.4s;
  will-change: font-variation-settings;
}

/* the poured state, before construction */
html.js .build .ch {
  animation: pour 1.25s var(--ease-expo) both;
  animation-delay: calc(var(--i) * 55ms + var(--d0, 120ms));
}

@keyframes pour {
  0%   { font-variation-settings: 'wght' 200, 'wdth' 100; opacity: 0; transform: translateY(0.35em); color: var(--accent); }
  45%  { opacity: 1; }
  70%  { color: var(--accent); }
  100% { font-variation-settings: 'wght' 800, 'wdth' 75; opacity: 1; transform: none; color: var(--ink); }
}

/* CJK has no variable axes: rise and settle instead */
html.js .build .ch.cjk {
  animation-name: rise-cjk;
}

@keyframes rise-cjk {
  from { opacity: 0; transform: translateY(0.25em) scale(0.96); filter: blur(6px); }
  to   { opacity: 1; transform: none; filter: none; }
}

/* once built, the letters answer the cursor (set inline by demo.js) */
.built .ch { animation: none; }

/* ════════════════════════════════════════════════════════════
   2. THE DRAWING — couplers, crosshair and a title block
   ════════════════════════════════════════════════════════════ */

.hero-stage { --mx: -100px; --my: -100px; }

/* falling pieces may leave the drawing sideways, never the page */
.hero { overflow-x: clip; }

.couplers {
  position: absolute;
  inset: 0 -1% 0 -1%;
  pointer-events: none;
  z-index: 2;
}

.coupler {
  --t: clamp(0, (var(--s) - var(--k)) * 2.2, 1);
  position: absolute;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  background: var(--bg);
  border: 1.5px solid var(--accent);
  transform:
    translate(calc(var(--t) * var(--dx) * 1px), calc(var(--t) * var(--dy) * 1px))
    rotate(calc(var(--t) * var(--r) * 1deg));
  opacity: calc(1 - var(--t));
  transition: background 0.25s, box-shadow 0.35s var(--ease-expo);
  animation: coupler-in 0.6s var(--ease-settle) both;
  animation-delay: calc(var(--k) * 1.2s + 1.15s);
}

.coupler.near {
  background: var(--accent);
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 18%, transparent);
}

@keyframes coupler-in {
  from { opacity: 0; scale: 0; }
}

/* drafting-table crosshair */
.crosshair {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
  opacity: 0;
  transition: opacity 0.3s;
}

.hero-stage.tracking .crosshair { opacity: 1; }

.crosshair::before,
.crosshair::after {
  content: '';
  position: absolute;
  background-image: linear-gradient(90deg, var(--accent) 50%, transparent 50%);
  opacity: 0.55;
}

.crosshair::before {
  left: calc(-1 * var(--gutter));
  right: calc(-1 * var(--gutter));
  top: var(--my);
  height: 1px;
  background-size: 6px 1px;
}

.crosshair::after {
  top: -3.2rem;
  bottom: 0;
  left: var(--mx);
  width: 1px;
  background-image: linear-gradient(180deg, var(--accent) 50%, transparent 50%);
  background-size: 1px 6px;
}

.crosshair .readout {
  position: absolute;
  left: var(--mx);
  top: var(--my);
  transform: translate(10px, 10px);
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--mono);
  font-size: 0.62rem;
  font-variant-numeric: tabular-nums;
  padding: 0.15rem 0.35rem;
  white-space: nowrap;
}

/* architectural title block, bottom-right of the drawing */
.title-block {
  position: absolute;
  right: 0;
  top: clamp(2.6rem, 6vw, 4.6rem);
  z-index: 4;
  width: min(22rem, 30%);
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  background: var(--bg);
  border: 1.5px solid var(--ink);
  font-family: var(--mono);
  font-size: 0.62rem;
  line-height: 1.25;
  text-transform: uppercase;
  animation: block-in 0.9s var(--ease-expo) both;
  animation-delay: 1.35s;
}

@keyframes block-in {
  from { opacity: 0; transform: translateY(14px); clip-path: inset(100% 0 0 0); }
  to   { opacity: 1; transform: none; clip-path: inset(0 0 0 0); }
}

.title-block > div {
  border-right: 1px solid var(--border-strong);
  border-bottom: 1px solid var(--border-strong);
  padding: 0.4rem 0.5rem 0.45rem;
  min-width: 0;
}

.title-block > div:nth-last-child(-n+2) { border-bottom: 0; }
.title-block .w2 { grid-column: span 2; }
.title-block .w3 { grid-column: span 3; }
.title-block .w4 { grid-column: span 4; }
.title-block .w6 { grid-column: span 6; }
.title-block .end { border-right: 0; }

.title-block small {
  display: block;
  color: var(--ink-faint);
  font-size: 0.55rem;
  margin-bottom: 0.2rem;
}

.title-block b { color: var(--ink); font-weight: 600; }
.title-block .accent b { color: var(--accent); }

.title-block .project b {
  font-family: var(--display);
  font-size: 1.05rem;
  font-stretch: 75%;
  font-weight: 800;
  letter-spacing: 0;
  line-height: 1;
}

.title-block .meter {
  display: block;
  height: 5px;
  margin-top: 0.3rem;
  background: var(--surface-soft);
  position: relative;
}

.title-block .meter i {
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform-origin: left;
  transform: scaleX(var(--support, 1));
  transition: transform 0.2s linear;
}

/* the tower is built after the drawing, bottom bay first */
.scaffold-tower .bay { animation: bay-up 0.5s var(--ease-settle) both; }

.scaffold-tower .bay:nth-child(1) { animation-delay: 2.06s; }
.scaffold-tower .bay:nth-child(2) { animation-delay: 2.00s; }
.scaffold-tower .bay:nth-child(3) { animation-delay: 1.94s; }
.scaffold-tower .bay:nth-child(4) { animation-delay: 1.88s; }
.scaffold-tower .bay:nth-child(5) { animation-delay: 1.82s; }
.scaffold-tower .bay:nth-child(6) { animation-delay: 1.76s; }
.scaffold-tower .bay:nth-child(7) { animation-delay: 1.70s; }
.scaffold-tower .bay:nth-child(8) { animation-delay: 1.64s; }
.scaffold-tower .bay:nth-child(9) { animation-delay: 1.58s; }
.scaffold-tower .bay:nth-child(10) { animation-delay: 1.52s; }
.scaffold-tower .bay:nth-child(11) { animation-delay: 1.46s; }
.scaffold-tower .bay:nth-child(12) { animation-delay: 1.40s; }

@keyframes bay-up {
  from { opacity: 0; transform: translateY(-14px); }
}

/* ════════════════════════════════════════════════════════════
   3. REVEALS — headings rise through a mask
   ════════════════════════════════════════════════════════════ */

.mask-line {
  display: block;
  overflow: hidden;
  padding-bottom: 0.08em;
  margin-bottom: -0.08em;
}

.mask-line > span {
  display: inline-block;
  transform: translateY(105%);
  transition: transform 1s var(--ease-expo);
  transition-delay: calc(var(--i, 0) * 90ms);
}

.revealed .mask-line > span,
html:not(.js) .mask-line > span { transform: none; }

.sec-label b { display: inline-block; min-width: 2.6ch; }
.hello .arr { margin-left: 0.12em; }

/* ════════════════════════════════════════════════════════════
   4. CONTENTS TILES — each carries a small working model
   ════════════════════════════════════════════════════════════ */

.tile { --h: 0; }

.tile-viz {
  position: absolute;
  right: 1.2rem;
  bottom: 1.2rem;
  pointer-events: none;
}

/* podcast: a live level meter */
.viz-wave {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 3.2rem;
}

.viz-wave i {
  width: 4px;
  height: 100%;
  background: var(--accent);
  transform-origin: bottom;
  transform: scaleY(0.12);
  transition: background 0.25s;
}

.tile:hover .viz-wave i,
.tile:focus-visible .viz-wave i {
  background: var(--accent-ink);
  animation: level 0.9s ease-in-out infinite alternate;
  animation-delay: calc(var(--i) * -137ms);
}

@keyframes level {
  0%   { transform: scaleY(0.15); }
  35%  { transform: scaleY(0.9); }
  60%  { transform: scaleY(0.4); }
  100% { transform: scaleY(0.75); }
}

/* cv: a ruler of years with "now" marked, beside the title */
.viz-ruler {
  bottom: auto;
  top: 44%;
  width: clamp(9rem, 16vw, 13rem);
  font-family: var(--mono);
  font-size: 0.58rem;
  color: var(--ink-faint);
}

.viz-ruler,
.viz-ruler .track { display: block; }

.viz-ruler .track {
  position: relative;
  height: 1.1rem;
  border-bottom: 1.5px solid currentColor;
  background-image: linear-gradient(90deg, currentColor 1px, transparent 1px);
  background-size: 25% 100%;
  background-position: -0.5px 0;
}

.viz-ruler .fill {
  display: block;
  position: absolute;
  left: 0;
  bottom: -1.5px;
  height: 4px;
  width: var(--now);
  background: var(--accent);
  transform-origin: left;
  transform: scaleX(0.04);
  transition: transform 0.9s var(--ease-expo), background 0.25s;
}

.viz-ruler .years { display: flex; justify-content: space-between; margin-top: 0.3rem; }

.tile:hover .viz-ruler { color: var(--accent-ink); }
.tile:hover .viz-ruler .fill { transform: scaleX(1); background: var(--accent-ink); }

/* writing: a caret after the latest title */
.tile-preview .caret {
  display: inline-block;
  width: 0.5em;
  height: 1em;
  margin-left: 0.15em;
  vertical-align: -0.15em;
  background: var(--accent);
  animation: caret 1.05s steps(2) infinite;
}

.tile:hover .tile-preview .caret { background: var(--accent-ink); }

@keyframes caret { 50% { opacity: 0; } }

/* the tile number ticks like a counter */
.tile-top > span:first-child { font-variant-numeric: tabular-nums; }

/* ════════════════════════════════════════════════════════════
   5. PAGE TRANSITION — the twelve columns come down and go up
   ════════════════════════════════════════════════════════════ */

.wipe {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  pointer-events: none;
}

.wipe i {
  background: var(--accent);
  transform: scaleY(0);
  transform-origin: top;
  box-shadow: 1px 0 0 var(--accent);
}

.wipe.cover i {
  transform: scaleY(1);
  transition: transform 0.42s var(--ease-in-expo);
  transition-delay: calc(var(--i) * 22ms);
}

.wipe.covered i { transform: scaleY(1); transition: none; }

.wipe.reveal i {
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform 0.62s var(--ease-expo);
  transition-delay: calc(var(--i) * 26ms);
}

/* the destination's name, set large while the columns are down */
.wipe .wipe-label {
  position: absolute;
  left: var(--gutter);
  right: var(--gutter);
  bottom: clamp(1.5rem, 5vw, 3.5rem);
  color: var(--accent-ink);
  font-family: var(--display);
  font-size: clamp(3.5rem, 12vw, 11rem);
  font-stretch: 75%;
  font-weight: 800;
  line-height: 0.85;
  overflow: hidden;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(0.3em);
  transition: opacity 0.25s, transform 0.5s var(--ease-expo);
}

.wipe.cover .wipe-label { opacity: 1; transform: none; transition-delay: 0.22s; }
.wipe.covered .wipe-label { opacity: 1; transform: none; transition: none; }
.wipe.reveal .wipe-label { opacity: 0; transform: translateY(-0.2em); transition: opacity 0.2s, transform 0.4s var(--ease-expo); }

/* cover the page before first paint when arriving through a wipe */
html.wipe-in body { visibility: hidden; }

/* ════════════════════════════════════════════════════════════
   6. FOOTER — the closing sheet
   ════════════════════════════════════════════════════════════ */

.end-sheet {
  margin: clamp(5rem, 11vw, 9rem) auto 0;
  max-width: var(--wrap);
  padding: 0 var(--gutter);
  overflow: hidden;
}

.end-row {
  align-items: center;
  border-top: 1px solid var(--ink);
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem 0;
  font-family: var(--mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.end-row a { color: var(--ink); text-decoration: none; white-space: nowrap; }
.end-row a:hover { color: var(--accent); }

.wordmark {
  color: var(--ink);
  display: inline-block;
  font-family: var(--display);
  font-size: 15.6vw;
  font-stretch: 75%;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 0.78;
  margin: 0 0 -0.06em -0.03em;
  text-transform: uppercase;
  white-space: nowrap;
  user-select: none;
}

@media (min-width: 1400px) { .wordmark { font-size: calc(var(--wrap) * 0.156); } }

/* an underdrawing in pencil tone; letters near the cursor are inked in */
.wordmark .ch {
  color: color-mix(in srgb, var(--ink) 9%, var(--bg));
  transition: font-variation-settings 0.6s var(--ease-expo), color 0.35s;
}

.wordmark .ch.lit { color: var(--accent); }

/* the wordmark already names the site; keep only the copyright line */
.end-sheet ~ footer .footer-name { visibility: hidden; }

html[lang="zh"] .wordmark { letter-spacing: 0.04em; font-size: 22vw; line-height: 0.95; }

/* old footer stays for the copyright line only */
.end-sheet + hr.divider { display: none; }

/* ════════════════════════════════════════════════════════════
   7. PHOTOS — the lightbox grows out of the frame you clicked
   ════════════════════════════════════════════════════════════ */

.flip-ghost {
  position: fixed;
  z-index: 301;
  object-fit: cover;
  pointer-events: none;
  transition: all 0.55s var(--ease-expo);
}

/* ════════════════════════════════════════════════════════════
   8. MOTION SAFETY + SMALL SCREENS
   ════════════════════════════════════════════════════════════ */

@media (prefers-reduced-motion: reduce) {
  html.js .build .ch { animation: none; }
  .mask-line > span { transform: none !important; transition: none; }
  .coupler, .title-block, .scaffold-tower .bay { animation: none; }
  .wipe { display: none; }
  html.wipe-in body { visibility: visible; }
}

@media (hover: none), (pointer: coarse) {
  .crosshair { display: none; }

  /* nothing hovers on a touch screen, so the tile models show their
     finished state instead of waiting for a hover that never comes */
  .viz-ruler .fill { transform: scaleX(1); }
  .viz-ruler { color: var(--ink-soft); }
  .viz-wave i { animation: level 0.9s ease-in-out infinite alternate; animation-delay: calc(var(--i) * -137ms); }
}

@media (hover: none) and (prefers-reduced-motion: reduce) {
  .viz-wave i { animation: none; transform: scaleY(0.5); }
}

@media (max-width: 960px) {
  .title-block { position: relative; top: auto; width: 100%; margin-top: 1.5rem; }
}

@media (max-width: 640px) {
  /* on a phone the title block only repeats the line above it and the
     tower beside it, and it pushes the introduction off the first screen */
  .title-block { display: none; }
  .couplers { display: none; }
  .viz-ruler { width: 8rem; }
  .tile-viz { right: 1rem; bottom: 1rem; }
  /* text never runs under the thumbnail or the level meter */
  .tile:has(.tile-img:not([hidden])) .tile-preview,
  .tile:has(.viz-wave) .tile-preview { padding-right: 6.5rem; }
  .to-top { display: inline-flex; align-items: center; min-height: 2.75rem; padding-left: 0.75rem; }
}
