/* ==========================================================================
   About: the four chambers.
   One heart, one camera, behind the words. Each beat (a section) reserves a
   square slot for the heart; the camera frames the heart to that slot, so the
   layout lives here and the 3D follows it. Without JavaScript, with reduced
   motion and until the scene is ready, each slot shows a still of the same
   scene, so every state is a finished composition.

   Classes on <html>: no-js | js still | js motion (+ live once the scene runs)
   ========================================================================== */

:root {
  --cols: 12;
  --col-gap: 24px;
  /* The heart's square, and where it rests: centred in the space under the
     header. Every desktop beat shares it, so the heart never wanders. */
  --slot: min(780px, calc(100dvh - var(--chrome-h) - 104px), 50vw);
  --slot-top: calc(var(--chrome-h) + (100dvh - var(--chrome-h) - var(--slot)) / 2);
  /* Where a beat takes over, as a share of the viewport: the next chamber is
     fully lit when its section's top reaches --beat-in from the top, and the
     current one holds until its bottom reaches --beat-out. (Read by page.js.) */
  --beat-in: .36;
  --beat-out: .86;
}

/* ---------------------------------------------------------------- stage */
/* The stage: the scene's ground colour (page.js paints it) with the canvas on
   it. Hit-testable, so the shared header can read the ground; the page above
   it takes every click. */
.stage { position: fixed; inset: 0 0 auto 0; height: 100lvh; z-index: 0; background: var(--paper); }
.stage__canvas { position: absolute; top: 0; left: 0; display: block; width: 100%; height: 100%; opacity: 0; pointer-events: none; transition: opacity 600ms var(--ease-out); }
html.live .stage__canvas { opacity: 1; }
main { position: relative; z-index: 1; }
.foot { z-index: 2; }

/* ---------------------------------------------------------------- beats */
/* A still's transparent margin can reach past a narrow screen's edge: clip it
   at the section (clip, not hidden, so the slots still stick). */
.beat { background: var(--beat-bg, var(--paper)); overflow-x: clip; contain: layout paint; }
.chamber { --beat-bg: var(--hue-tint); }
.coda { --beat-bg: var(--coral-tint); }
/* Live, the ground is the scene's own (page.js paints the strip under the
   header to match it), so the sections let it through. */
html.live .beat { background: transparent; }

.beat__grid {
  display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); column-gap: var(--col-gap);
  padding: var(--slot-top) var(--gutter) 0;
}
.beat__text { grid-column: 1 / span 5; grid-row: 1; min-width: 0; }
.beat__slot {
  grid-column: 6 / -1; grid-row: 1; justify-self: center; align-self: start;
  position: sticky; top: var(--slot-top);
  width: min(100%, var(--slot));
}

/* A slot is the square the heart is framed to. Its still overflows it a
   little, because a chamber lifted out of the heart is wider than the heart. */
.slot { aspect-ratio: 1; }
.poster {
  position: absolute; left: -12.5%; top: -12.5%; width: 125%; height: 125%; max-width: none;
  object-fit: contain; pointer-events: none; user-select: none;
  transform: scale(calc(1 / var(--fit-scale, 1)));   /* the same framing as the live heart */
  transition: opacity 600ms var(--ease-out);
}
html.live .poster { opacity: 0; }
/* once the scene is up, the stills never load */
html.live.settled .poster { display: none; }

/* ---------------------------------------------------------------- opening */
.beat--hero .beat__grid { min-height: 100dvh; padding-bottom: 0; }
.hero__text { align-self: center; padding-bottom: calc(var(--slot-top) - var(--chrome-h)); }
.hero__title {
  font: 700 clamp(56px, 6.6vw, 124px)/.9 var(--font-display); letter-spacing: -.04em;
  max-width: 7.4em; text-wrap: balance;
}

/* ---------------------------------------------------------------- chambers */
.chamber .beat__grid { padding-bottom: 34dvh; }
.chamber__name {
  font: 700 clamp(64px, 7vw, 124px)/.86 var(--font-display); letter-spacing: -.045em;
  color: var(--hue-deep); margin-left: -.03em;
}
.chamber__part { margin-top: 14px; font: 500 15px/1.4 var(--font-body); color: var(--ink-muted); }
.chamber__words {
  margin-top: clamp(28px, 3.4dvh, 40px);
  font: 500 clamp(22px, 1.8vw, 30px)/1.24 var(--font-display); letter-spacing: -.016em;
  max-width: 25ch; text-wrap: pretty;
}

.entries { margin-top: clamp(40px, 6dvh, 64px); max-width: 600px; }
/* An entry: a gutter for its marks (the organisation's, a school's or an
   app's), then the words. Entries without a mark keep the gutter, so every
   title starts on the same line. */
.entry {
  display: grid; grid-template-columns: 40px minmax(0, 1fr); column-gap: 18px;
  padding: 20px 0 24px; border-top: 1.5px solid color-mix(in srgb, var(--hue) 60%, transparent);
}
.entry__body { min-width: 0; }
.entry__marks { display: grid; align-content: start; gap: 10px; padding-top: 1px; }
.entry__marks img { width: 40px; height: 40px; object-fit: contain; }
.entry__marks img[src*="org-preply"] { border-radius: 10px; }
.entry--app .entry__marks img { border-radius: 11px; }
.entry__head { display: flex; align-items: baseline; justify-content: space-between; gap: 6px 20px; flex-wrap: wrap; }
.entry__role { font: 650 22px/1.2 var(--font-display); letter-spacing: -.012em; }
.entry__role a { position: relative; text-decoration: none; }
.entry__role a::after { content: ""; position: absolute; inset: -11px -8px; }   /* a 44 px tap target */
.entry__role a:hover { text-decoration: underline; text-decoration-thickness: 2px; text-underline-offset: .2em; }
.entry__when { font: 500 15px/1.3 var(--font-body); color: var(--ink-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.entry__place { margin-top: 4px; font: 500 15.5px/1.45 var(--font-body); color: var(--ink-muted); }
.entry__facts { margin-top: 12px; display: grid; gap: 6px; }
.entry__facts li { position: relative; padding-left: 20px; font-size: 16.5px; line-height: 1.5; text-wrap: pretty; }
.entry__facts li::before {
  content: ""; position: absolute; left: 1px; top: .62em; width: 7px; height: 7px; border-radius: 50%;
  background: var(--hue);
}

/* Photographs of his life, as prints: a mat in a stronger tint of the
   chamber's colour, a slight tilt, a soft lift in the chamber's deep shade. */
.print {
  position: relative; margin: 0; padding: 9px; border-radius: 7px;
  background: color-mix(in srgb, var(--hue) 36%, #ffffff);
  box-shadow: 0 1px 2px rgba(28, 24, 48, .08), 0 18px 28px -18px color-mix(in srgb, var(--hue-deep) 60%, transparent);
  transform: rotate(var(--tilt, 0deg));
  will-change: transform;   /* rasterised once; scrolling and the hover lift stay on the compositor */
  transition: transform var(--dur-ui) var(--ease-out);
}
.print img { display: block; width: 100%; height: auto; border-radius: 3px; background: color-mix(in srgb, var(--hue) 12%, #ffffff); }
.print:hover { transform: rotate(var(--tilt, 0deg)) translateY(-3px); }
/* while the page scrolls, nothing under the pointer starts a hover */
html.is-scrolling .print, html.is-scrolling .chamber__link, html.is-scrolling .entry__role a, html.is-scrolling .skills__row { pointer-events: none; }
.print--wide { width: min(100%, 540px); margin-top: 24px; }
/* two prints, the second overlapping the first and set lower */
.prints { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); max-width: 560px; margin-top: 24px; }
.prints .print { grid-row: 1; align-self: start; }
.prints .print:first-child { grid-column: 1 / span 8; }
.prints .print:last-child { grid-column: 4 / span 9; margin-top: calc(min(100vw, 560px) * .22); }
.prints--tall { max-width: 520px; margin-top: 30px; }
.prints--tall .print:first-child { grid-column: 1 / span 6; }
.prints--tall .print:last-child { grid-column: 6 / span 7; margin-top: calc(min(100vw, 520px) * .1); }

.chamber__total { margin-top: 8px; padding-top: 18px; border-top: 1.5px solid color-mix(in srgb, var(--hue) 60%, transparent); font: 500 16.5px/1.5 var(--font-body); max-width: 600px; }
.chamber__link {
  display: inline-flex; align-items: center; gap: 10px; min-height: 48px; margin-top: 20px; padding: 0 22px 0 24px;
  border-radius: var(--pill); background: var(--ink); color: var(--hue-tint);
  font: 600 16px/1 var(--font-body); text-decoration: none;
  transition: transform var(--dur-micro) var(--ease-out), background-color var(--dur-micro) ease-out;
}
.chamber__link svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: transform var(--dur-ui) var(--ease-out); }
.chamber__link:hover { transform: translateY(-2px); }
.chamber__link:hover svg { transform: translateX(3px); }
.chamber__total + .chamber__link { margin-top: 28px; }

/* ---------------------------------------------------------------- the vessels */
.vessels .beat__grid { padding-bottom: 30dvh; }
.vessels__title { font: 650 clamp(30px, 2.6vw, 42px)/1 var(--font-display); letter-spacing: -.025em; }
.vessels__title--next { margin-top: clamp(56px, 8dvh, 88px); }
.school { margin-top: 26px; }
.school + .school { margin-top: 34px; padding-top: 26px; border-top: 1.5px solid var(--line); }
.school__name { font: 700 clamp(72px, 8.4vw, 150px)/.84 var(--font-display); letter-spacing: -.05em; margin-left: -.03em; }
.school__name--small { font: 650 clamp(24px, 2vw, 30px)/1.1 var(--font-display); letter-spacing: -.015em; margin-left: 0; }
.school__deg { margin-top: 14px; font: 500 17px/1.4 var(--font-body); color: var(--ink-muted); }
.school--ucla .school__deg, .school__name--small { display: flex; align-items: center; gap: 12px; }
.school__mark { width: 40px; height: 40px; flex: none; object-fit: contain; }
.school--ucla .school__deg { font-size: 19px; color: var(--ink); }
.school__facts { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }
.school__facts li {
  display: inline-flex; align-items: baseline; gap: 8px; min-height: 40px; padding: 9px 16px 8px;
  border-radius: var(--pill); background: var(--card); box-shadow: 0 1px 2px rgba(28, 24, 48, .07);
  font: 500 15.5px/1.3 var(--font-body);
}
.school__facts b { font: 700 19px/1 var(--font-display); letter-spacing: -.01em; }
.school__more { margin-top: 8px; font-size: 16.5px; line-height: 1.5; max-width: 40ch; }

.langs { display: grid; grid-template-columns: repeat(2, max-content); gap: 22px 48px; margin-top: 24px; }
.langs li { display: grid; gap: 6px; align-content: start; }
.langs span { font: 650 clamp(30px, 2.8vw, 46px)/1 var(--font-display); letter-spacing: -.02em; }
.langs span:lang(ja), .langs span:lang(ko) { letter-spacing: 0; font-weight: 600; font-family: var(--font-display), "Hiragino Sans", "Yu Gothic UI", "Yu Gothic", "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans CJK JP", "Noto Sans KR", sans-serif; }
.langs small { font: 500 13.5px/1.2 var(--font-body); color: var(--ink-muted); }

.skills { margin-top: 18px; max-width: 600px; }
.skills__row { display: grid; grid-template-columns: 128px minmax(0, 1fr); gap: 4px 20px; padding: 14px 0 15px; border-top: 1.5px solid var(--line); }
.skills__row dt { display: flex; align-items: center; gap: 12px; font: 600 15.5px/1.5 var(--font-body); }
.skills__row .sticker { width: 30px; height: 30px; margin-block: -6px; }
.skills__row dd { margin: 0; font-size: 16px; line-height: 1.5; text-wrap: pretty; }

/* ---------------------------------------------------------------- coda */
.coda .beat__grid { min-height: 100dvh; padding-bottom: 0; }
.coda__text { align-self: center; padding-bottom: calc(var(--slot-top) - var(--chrome-h)); }
.coda__words {
  font: 700 clamp(40px, 4.2vw, 76px)/.98 var(--font-display); letter-spacing: -.035em;
  max-width: 13ch; text-wrap: balance;
}
.coda__more {
  margin-top: clamp(22px, 3dvh, 34px); max-width: 26ch;
  font: 500 clamp(21px, 1.7vw, 28px)/1.25 var(--font-display); letter-spacing: -.016em; text-wrap: pretty;
}

/* ---------------------------------------------------------------- narrower desktops */
@media (max-width: 1180px) {
  .beat__text { grid-column: 1 / span 6; }
  .beat__slot { grid-column: 7 / -1; }
  :root { --slot: min(780px, calc(100dvh - var(--chrome-h) - 104px), 46vw); }
}

/* ---------------------------------------------------------------- phone and tablet
   The heart rests at the top; each chamber's words arrive on a sheet that
   slides up over it, and the next chamber turns into view between sheets. */
@media (max-width: 820px) {
  :root {
    --slot: min(100vw, calc(50dvh - 16px), 560px);
    --slot-top: calc(var(--chrome-h) - 6px);
    --slot-follow: 1;
    --fit-scale: 1.05;
    --beat-in: .3;
    --beat-out: .92;
  }
  .beat__grid { display: flex; flex-direction: column; padding: 0; }
  .beat__slot { width: var(--slot); margin: 0 auto; }
  .beat__text { position: relative; z-index: 1; }
  /* the heart's window comes first, then the words */
  .chamber .beat__slot, .vessels .beat__slot, .coda .beat__slot { order: -1; }

  .beat--hero .beat__grid { min-height: 100dvh; padding: calc(var(--chrome-h) + 12px) 0 0; }
  .hero__text { padding: 0 var(--gutter); align-self: auto; }
  .hero__title { font-size: clamp(44px, 12.4vw, 76px); max-width: none; }
  .beat--hero .beat__slot { position: relative; top: auto; width: min(100vw, calc(100dvh - 300px)); margin: auto; }

  .chamber .beat__grid, .vessels .beat__grid { padding: 0; }
  .chamber__text, .vessels__text {
    /* at least a screen tall, so the window above is covered before the next one shows */
    min-height: calc(100dvh - var(--slot-top) + 16px);
    margin: 2dvh 0 0; padding: 30px var(--gutter) 64px;
    background: var(--paper); border-radius: 28px;
    box-shadow: 0 -10px 34px -18px rgba(28, 24, 48, .28), 0 14px 30px -20px rgba(28, 24, 48, .3);
  }
  .chamber__name { font-size: clamp(56px, 16vw, 92px); }
  .chamber__words { font-size: 22px; max-width: none; }
  .entries { margin-top: 32px; }
  .entry__role { font-size: 20px; }
  .entry__facts li { font-size: 16px; }
  .entry { grid-template-columns: 36px minmax(0, 1fr); column-gap: 14px; }
  .entry__marks img { width: 36px; height: 36px; }
  .prints .print:last-child { margin-top: 24vw; }
  .prints--tall .print:last-child { margin-top: 10vw; }
  .chamber + .chamber .beat__slot, .vessels .beat__slot, .coda .beat__slot { margin-top: 0; }

  .school--ucla .school__name { font-size: clamp(76px, 24vw, 120px); }
  .skills__row { grid-template-columns: minmax(0, 1fr); gap: 4px; }

  .coda .beat__grid { min-height: 100dvh; padding: 0 0 8dvh; }
  .coda .beat__slot { position: relative; top: auto; margin-top: calc(var(--chrome-h) - 6px); }
  .coda__text { padding: 2dvh var(--gutter) 0; align-self: auto; }
  .coda__words { font-size: clamp(34px, 9.6vw, 52px); max-width: 15ch; }
  .coda__more { font-size: 21px; }
}

@media (prefers-reduced-motion: reduce) {
  .stage__canvas, .poster { transition: none; }
}
