*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  -webkit-tap-highlight-color: transparent;
}

body {
  margin: 0;
  width: 100%;
  overflow-x: hidden;
  overflow-y: scroll;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-body);
  line-height: var(--t-body-lh);
  letter-spacing: var(--track);
}

a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }

/* the whole page fades up once, 300ms, like the reference's body.loaded */
.content { opacity: 0; }
body.is-loaded .content { opacity: 1; transition: opacity var(--t-mid) var(--ease); }

/* ---------------------------------------------------------------- nav ----
   One line of words separated by commas, pinned top left. The commas are
   generated, so the markup stays a plain list of links. Current section goes
   italic; the current sub-category greys out. */

#nav {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 12;
  width: 100%;
  padding: var(--nav-pad);
  background: var(--paper);
  transform: translateZ(0);
  transition: transform var(--t-mid) var(--ease), opacity var(--t-fast) var(--ease);
  will-change: transform;
}
body.nav-hidden #nav {
  transform: translateY(-100%);
  opacity: 0;
  pointer-events: none;
}

.nav-list {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  line-height: var(--nav-line);
}
.nav-list li { display: inline-block; }
.nav-list a,
.nav-list span {
  display: inline-block;
  font-size: var(--t-body);
  letter-spacing: var(--track);
  transition: opacity var(--t-fast) var(--ease);
}
/* the comma after every item but the last visible one */
.nav-list li:not(:last-child) > *::after {
  content: ',';
  padding-right: 0.28em;
}
.nav-list a.is-section { font-style: italic; }
.nav-list .is-current { color: var(--ink-dim); font-style: italic; }
html.can-hover .nav-list a:hover { opacity: 0.45; }

/* the ••• pill: the reference's read-more affordance */
.dots {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  height: 20px;
  padding: 0 8px;
  margin-left: 2px;
  border: 0;
  border-radius: 10px;
  background: var(--pill);
  cursor: pointer;
  vertical-align: middle;
  font: inherit;
}
.dots i {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--ink);
  transition: opacity var(--t-fast) var(--ease);
}
html.can-hover .dots:hover i { opacity: 0.45; }

/* the category list the ••• reveals, on its own line under the nav */
.nav-cats {
  display: none;
  padding-top: 2px;
  line-height: var(--nav-line);
}
.nav-cats.is-open { display: block; }
.nav-cats a {
  display: inline-block;
  font-size: var(--t-body);
  letter-spacing: var(--track);
}
.nav-cats a:not(:last-child)::after { content: ','; padding-right: 0.28em; }
.nav-cats a.is-active { color: var(--ink-dim); }
html.can-hover .nav-cats a:hover { opacity: 0.45; }

/* --------------------------------------------------------------- pages ---
   The nav is 48px tall in its resting state; the reference starts its first
   row right under it rather than reserving a hero. */
main { padding-top: 48px; }

.pagefoot {
  padding: 12% var(--page-margin) 6%;
  font-size: var(--t-caption);
  color: var(--ink-dim);
  font-family: var(--font-mono);
  letter-spacing: 0;
}

/* ------------------------------------------------------- text pages ------
   Information runs as one measure of body text at 21px, with small grey
   labels above each block. The reference sets its column at 15 of 25. */

.prose {
  padding: 0 var(--page-margin);
  max-width: calc(var(--page-margin) * 2 + (100% - var(--page-margin) * 2) / var(--cols) * 15);
}
.prose p {
  margin: 0 0 var(--para-gap);
  font-size: var(--t-big);
  line-height: 1.22;
  letter-spacing: var(--track);
}
.prose p.lede { margin-bottom: calc(var(--para-gap) * 1.6); }
.prose .label {
  margin: 0;
  font-size: var(--t-body);
  line-height: 1.1;
  color: var(--ink-dim);
}
.prose .block { margin-bottom: calc(var(--para-gap) * 1.7); }
.prose .block p { margin-bottom: 0; }
.prose a { text-decoration: none; }
html.can-hover .prose a:hover { opacity: 0.45; }
.prose em { font-style: italic; }

@media (max-width: 700px) {
  .prose { max-width: none; }
  .prose p { font-size: var(--t-body); }
}

/* ------------------------------------------------------------ index ------
   The typeset list of every project: an oversized name per row. */

.index-list { padding: 0 var(--page-margin); }

/* the filter, right-justified above the list, in the caption register */
.index-filter {
  display: flex;
  justify-content: flex-end;
  gap: 1.1em;
  padding: 6% var(--page-margin) 1.4em;
}
.index-filter button {
  border: 0;
  padding: 0;
  background: none;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: var(--t-caption);
  letter-spacing: 0;
  color: var(--ink-dim);
  transition: color var(--t-fast) var(--ease);
}
.index-filter button.is-active { color: var(--ink); }
html.can-hover .index-filter button:hover { color: var(--ink); }

/* name and number lead, disciplines and year sit opposite */
.index-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 2em;
}
.index-lead { flex: 1 1 auto; min-width: 0; }
.index-tail {
  flex: 0 0 auto;
  text-align: right;
  font-size: var(--t-caption);
  color: var(--ink-dim);
}
.index-tail .disc { display: block; font-style: italic; }
.index-tail .year { display: block; font-family: var(--font-mono); letter-spacing: 0; }
.index-lead .code {
  display: block;
  padding-top: 0.15em;
  font-family: var(--font-mono);
  font-size: var(--t-caption);
  letter-spacing: 0;
  color: var(--ink-dim);
}
.index-row[hidden] { display: none; }

.index-row {
  padding: 0.22em 0;
  border-top: 1px solid var(--ink);
}
.index-row:last-child { border-bottom: 1px solid var(--ink); }
.index-name {
  font-size: var(--t-index);
  line-height: 0.92;
  letter-spacing: var(--track-index);
  transition: opacity var(--t-fast) var(--ease);
}

/* Rollover: the whole span blacks out and inverts. The fill wipes up from the
   bottom rather than switching, so the row reads as a shutter closing. */
.index-row {
  /* the bar below is absolutely positioned against this, so the row has to be
     the containing block: without it the black fills the whole viewport */
  position: relative;
  isolation: isolate;
  padding-inline: 0.12em;
  margin-inline: -0.12em;
}
/* The bar belongs to this row only, and bleeds past the page margins so the
   invert reads full width. */
.index-row::before {
  content: '';
  position: absolute;
  inset: 0 calc(-1 * var(--page-margin));
  z-index: -1;
  background: var(--ink);
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform 260ms var(--ease);
}
.index-row > * { position: relative; }
html.can-hover .index-row:hover::before { transform: scaleY(1); }
html.can-hover .index-row:hover { color: var(--paper); }
html.can-hover .index-row:hover .index-tail,
html.can-hover .index-row:hover .index-tail *,
html.can-hover .index-row:hover .code { color: var(--paper); }
/* the scrambled name must not reflow the row while it cycles */
.index-name { display: block; font-variant-ligatures: none; }

/* the preview that rides with the cursor */
.index-preview {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 20;
  width: 22vw;
  max-width: 340px;
  aspect-ratio: var(--ratio, 1.5);
  pointer-events: none;
  opacity: 0;
  transition: opacity 180ms var(--ease);
  will-change: transform;
}
.index-preview.is-on { opacity: 1; }
.index-preview img { width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 700px) {
  .index-name { font-size: var(--t-index); }
  .index-preview { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .index-row::before { transition: none; }
}
