/* davidvinu.de — see design_handoff_personal_site/README.md for the design spec.
   Motion is tuned here, in the tokens below: quick arrival, long settle, and
   nothing animated that the compositor cannot hand to the GPU. */

@font-face {
  font-family: "Cormorant Garamond";
  src: url("/fonts/cormorant-garamond.woff2") format("woff2");
  font-weight: 300 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("/fonts/cormorant-garamond-italic.woff2") format("woff2");
  font-weight: 300 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/fonts/ibm-plex-mono.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ---- Tokens ---------------------------------------------------------- */

:root {
  --paper: #161514;
  --ink: #F5F2EC;
  --grey: #8A857D;
  --rule: #2C2A27;
  --gold: #C9A961;
  --gold-hi: #F1DFA6;
  --quote: #F1E9D6;

  --glow: 0.45;
  --glow-text: 0.35;

  --serif: "Cormorant Garamond", Georgia, serif;
  --mono: "IBM Plex Mono", monospace;

  /* Motion. Every entrance decelerates hard, so it reads as arriving rather
     than travelling; nothing waits on a long tail before the next thing moves. */
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-rule: cubic-bezier(.22, 1, .36, 1);
  --ease-leave: cubic-bezier(.55, 0, 1, .45);

  --t-rule: 1100ms;   /* the signature hairline growth */
  --t-enter: 620ms;
  --t-leave: 260ms;
  --t-color: 220ms;   /* hovers have to feel immediate */
  --t-nudge: 300ms;
  --t-dot: 420ms;
  --t-glow: 900ms;
  --t-chrome: 900ms;
  --stagger: 60ms;

  --gutter: 44px;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --paper: #F5F2EC;
    --ink: #161514;
    --rule: #DDD8CF;
    --quote: var(--ink);
  }
}
[data-theme="light"] {
  --paper: #F5F2EC;
  --ink: #161514;
  --rule: #DDD8CF;
  --quote: var(--ink);
}
[data-theme="dark"] {
  --paper: #161514;
  --ink: #F5F2EC;
  --rule: #2C2A27;
  --quote: #F1E9D6;
}

/* ---- Base ------------------------------------------------------------ */

html, body { margin: 0; padding: 0; height: 100%; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: 0.04em;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--ink); text-decoration: none; }
p { margin: 0; }

input, textarea, button {
  font: inherit;
  color: inherit;
  background: transparent;
  outline: none;
  letter-spacing: 0.04em;
}
input::placeholder, textarea::placeholder { color: var(--grey); opacity: 1; }

input:focus-visible,
textarea:focus-visible,
button:focus-visible,
a:focus-visible { outline: 1px solid var(--grey); outline-offset: 4px; }

::selection { background: var(--grey); color: var(--paper); }

[hidden] { display: none !important; }

/* Structure for search engines and screen readers, invisible on screen. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.screen:focus { outline: none; }
.screen:focus-visible { outline: none; }

.stage {
  position: relative;
  width: 100%;
  min-height: 100dvh;
  background: var(--paper);
  color: var(--ink);
  overflow: hidden;
  transition: background var(--t-enter) ease, color var(--t-enter) ease;
}

/* ---- Candlelight ----------------------------------------------------- */

.glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: var(--glow);
  transition: opacity var(--t-glow) ease;
}
.glow > * { position: absolute; }
.glow-outer, .glow-inner {
  left: 50%;
  top: 50%;
  border-radius: 50%;
  /* Promoted so the blur rasterises once and the drift is pure compositing. */
  will-change: transform;
}
.glow-outer {
  width: 90vmax;
  height: 90vmax;
  margin: -45vmax 0 0 -45vmax;
  filter: blur(30px);
  background: radial-gradient(circle at center,
    rgba(201, 169, 97, .42) 0%,
    rgba(201, 169, 97, .16) 26%,
    rgba(201, 169, 97, .04) 46%,
    rgba(0, 0, 0, 0) 62%);
  animation: dv-drift 32s ease-in-out infinite, dv-pulse 11s ease-in-out infinite;
}
.glow-inner {
  width: 50vmax;
  height: 50vmax;
  margin: -25vmax 0 0 -25vmax;
  filter: blur(24px);
  background: radial-gradient(circle at center,
    rgba(241, 223, 166, .28) 0%,
    rgba(241, 223, 166, .06) 40%,
    rgba(0, 0, 0, 0) 65%);
  animation: dv-drift2 23s ease-in-out infinite;
}
.glow-vignette {
  inset: 0;
  background: radial-gradient(ellipse at 50% 50%,
    rgba(0, 0, 0, 0) 40%,
    rgba(22, 21, 20, .55) 100%);
}

@keyframes dv-drift {
  0%   { transform: translate3d(-9%, -7%, 0) scale(1); }
  33%  { transform: translate3d(7%, 4%, 0) scale(1.15); }
  66%  { transform: translate3d(-3%, 9%, 0) scale(1.05); }
  100% { transform: translate3d(-9%, -7%, 0) scale(1); }
}
@keyframes dv-drift2 {
  0%   { transform: translate3d(12%, 10%, 0) scale(.9); }
  50%  { transform: translate3d(-10%, -8%, 0) scale(1.1); }
  100% { transform: translate3d(12%, 10%, 0) scale(.9); }
}
@keyframes dv-pulse {
  0%, 100% { opacity: .7; }
  50%      { opacity: 1; }
}

.stage:not([data-screen="home"]) .glow { --glow: 0.12; }

/* ---- Scrims ---------------------------------------------------------- */

/* The chrome floats over a scrolling column. Without these, lines of text run
   straight through the nav. Content dissolves into the ground instead. */
.scrim {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--t-enter) ease;
}
/* Solid out to just past the chrome, then a long fade, so the band itself is
   invisible on the ground but nothing ever reads through the nav. */
.scrim-top {
  top: 0;
  height: 112px;
  background: linear-gradient(to bottom,
    var(--paper) 0, var(--paper) 58px, transparent 112px);
}
.scrim-bottom {
  bottom: 0;
  height: 132px;
  background: linear-gradient(to top,
    var(--paper) 0, var(--paper) 64px, transparent 132px);
}
.stage:not([data-screen="home"]) .scrim { opacity: 1; }

/* ---- Chrome ---------------------------------------------------------- */

.header {
  position: absolute;
  top: 34px;
  left: var(--gutter);
  right: var(--gutter);
  z-index: 2;
  display: flex;
  justify-content: space-between;
  gap: 24px;
  pointer-events: none;
}
.header > * { pointer-events: auto; }
.header a { transition: color var(--t-color) ease; }
.header a:hover { color: var(--grey); }

.nav {
  position: absolute;
  right: var(--gutter);
  bottom: 36px;
  z-index: 2;
  display: flex;
  gap: 32px;
  align-items: center;
}
.nav > a {
  position: relative;
  color: var(--grey);
  transition: color var(--t-color) ease;
}
.nav > a:hover { color: var(--ink); }
.nav > a[data-current] { color: var(--ink); }

.dot {
  position: absolute;
  left: 50%;
  bottom: -11px;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--gold);
  opacity: 0;
  transform: translateX(-50%) scale(0);
  transition: transform var(--t-dot) var(--ease-out), opacity var(--t-dot) ease;
}
.nav > a[data-current] .dot { opacity: 1; transform: translateX(-50%) scale(1); }

.social { display: flex; gap: 18px; align-items: center; }
.social a {
  display: flex;
  width: 16px;
  height: 16px;
  color: var(--grey);
  transition: color var(--t-color) ease, transform var(--t-nudge) var(--ease-out);
}
.social a:hover { color: var(--ink); transform: translateY(-1px); }
.social svg { display: block; }

.js .header, .js .nav { opacity: 0; }
.js .header { transition: opacity var(--t-chrome) ease 200ms; }
.js .nav { transition: opacity var(--t-chrome) ease 320ms; }
.js .stage[data-chrome] .header,
.js .stage[data-chrome] .nav { opacity: 1; }

/* The dot stays a 5px dot. The padding and the matching negative margin grow
   the hit area around it without moving it a pixel, and the label is taken out
   of flow so the reachable area stays the dot, not the sentence. */
.egg {
  position: absolute;
  left: var(--gutter);
  bottom: 40px;
  z-index: 2;
  display: block;
  padding: 20px;
  margin: -20px;
}
.egg-dot {
  display: block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--gold);
  opacity: 0.5;
  transition: opacity var(--t-color) ease, transform var(--t-nudge) var(--ease-out);
}
.egg:hover .egg-dot,
.egg:focus-visible .egg-dot { opacity: 1; transform: scale(1.6); }

.egg-label {
  position: absolute;
  left: 100%;
  top: 50%;
  transform: translateY(-50%);
  white-space: nowrap;
  color: var(--ink);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--t-color) ease;
}
.egg:hover .egg-label,
.egg:focus-visible .egg-label { opacity: 1; }

/* With JS the characters carry the reveal, so the line itself stays put and
   the letters arrive one after another, out of the dot. */
.js .egg-label { opacity: 1; }
.js .egg-label .c {
  display: inline-block;
  white-space: pre;
  opacity: 0;
  transform: translate3d(-5px, 0, 0);
  transition: opacity 200ms ease, transform 200ms var(--ease-leave);
}
.js .egg:hover .egg-label .c,
.js .egg:focus-visible .egg-label .c {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  transition: opacity 320ms ease var(--d), transform 460ms var(--ease-out) var(--d);
}

/* Measured: beside the dot the line reaches x=236, and the nav comes to meet
   it below 660px. A phone has no hover to begin with, so on both the dot just
   stays a dot. */
@media (hover: none), (max-width: 680px) {
  .egg-label { display: none; }
}

/* ---- Screens --------------------------------------------------------- */

.screen {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  box-sizing: border-box;
}
.screen-home { align-items: center; padding: 0 var(--gutter); }
.screen-scroll {
  align-items: safe center;
  padding: 90px var(--gutter) 116px;
  overflow-y: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.screen-scroll::-webkit-scrollbar { display: none; }

.column { width: min(100%, 700px); display: flex; flex-direction: column; }

.js .screen-scroll {
  opacity: 0;
  transform: translate3d(0, 12px, 0);
  transition: opacity var(--t-enter) ease, transform var(--t-enter) var(--ease-out);
}
.js .stage[data-page] .screen-scroll {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}
.js .stage[data-leaving] .screen-scroll {
  opacity: 0;
  transform: translate3d(0, -6px, 0);
  transition: opacity var(--t-leave) var(--ease-leave), transform var(--t-leave) var(--ease-leave);
}

/* ---- Start ----------------------------------------------------------- */

.quote {
  border: 0;
  padding: 0;
  margin: 0;
  text-align: left;
  cursor: pointer;
  width: min(100%, 700px);
  color: var(--quote);
  transition: color var(--t-color) ease;
}
.quote:hover { color: var(--grey); }
.quote-line {
  display: block;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(34px, 4.6vw, 64px);
  line-height: 1.15;
  text-wrap: pretty;
  text-shadow: 0 0 42px rgba(201, 169, 97, var(--glow-text));
}
.w { display: inline-block; }

/* The line arrives word by word and leaves in one movement. Opacity and
   transform only: blur here was the single biggest source of stutter. */
.js .w {
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  transition: opacity var(--t-leave) var(--ease-leave),
              transform var(--t-leave) var(--ease-leave);
}
.js .stage[data-quote] .w {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  transition: opacity var(--t-enter) ease var(--d),
              transform var(--t-enter) var(--ease-out) var(--d);
}

/* ---- Projects -------------------------------------------------------- */

.row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 24px;
  align-items: start;
  padding: 0 0 20px;
  color: var(--ink);
  transition: color var(--t-color) ease;
}
.row-name {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(28px, 2.6vw, 36px);
  line-height: 1.1;
  letter-spacing: 0;
}
.row-link {
  display: flex;
  gap: 14px;
  align-items: center;
  color: var(--grey);
  padding-top: 8px;
  white-space: nowrap;
}
.arrow {
  display: inline-block;
  font-size: 14px;
  color: var(--grey);
  transition: transform var(--t-nudge) var(--ease-out), color var(--t-color) ease;
}
.row:hover { color: var(--grey); }
.row:hover .arrow { transform: translate3d(3px, -3px, 0); color: var(--gold); }

.rule {
  display: block;
  height: 1px;
  background: var(--rule);
  transform-origin: left;
}
.row .rule { grid-column: 1 / -1; margin-bottom: 22px; }
.rule-closing { margin-bottom: 0; }

/* Authors and venue: a citation is incomplete without them, which is why this
   survives where the project descriptions did not. */
.row-meta {
  grid-column: 1 / -1;
  margin-top: 12px;
  color: var(--grey);
  line-height: 1.7;
}

.mystery { padding: 26px 0 0; color: var(--grey); line-height: 1.7; }

.js .rule {
  transform: scaleX(0);
  transition: transform var(--t-rule) var(--ease-rule) var(--d, 0ms);
}
.js .row .rule { --d: calc(var(--i) * var(--stagger)); }
.js .stage[data-page] .rule { transform: scaleX(1); }

.js .row, .js .mystery {
  opacity: 0;
  transform: translate3d(0, 12px, 0);
}
.js .row {
  transition: color var(--t-color) ease,
              opacity var(--t-enter) ease calc(var(--i) * var(--stagger)),
              transform var(--t-enter) var(--ease-out) calc(var(--i) * var(--stagger));
}
.js .mystery {
  transition: opacity var(--t-enter) ease var(--d),
              transform var(--t-enter) var(--ease-out) var(--d);
}
.js .stage[data-page] .row,
.js .stage[data-page] .mystery {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* Leaving, everything drops its stagger and goes at once. */
.js .stage[data-leaving] .rule {
  transition-duration: var(--t-leave);
  transition-delay: 0ms;
}
.js .stage[data-leaving] .row,
.js .stage[data-leaving] .mystery { transition-delay: 0ms; }

/* ---- Contact --------------------------------------------------------- */

.form { gap: 40px; }
.block { display: flex; flex-direction: column; }
.ask { gap: 14px; }

.ask h2 {
  margin: 0;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(30px, 3.6vw, 48px);
  line-height: 1.18;
}
.ask p { color: var(--grey); line-height: 1.7; max-width: 48ch; }

.mail-row {
  display: flex;
  justify-content: space-between;
  gap: 24px;
  padding: 16px 0;
  color: var(--ink);
  transition: color var(--t-color) ease;
}
.mail-row:hover { color: var(--grey); }

.field {
  display: grid;
  grid-template-columns: 110px minmax(0, 1fr);
  align-items: baseline;
  padding: 14px 0;
  border-bottom: 1px solid var(--rule);
}
.field-wish { align-items: start; }
.field-label { color: var(--grey); transition: color var(--t-color) ease; }
.field-wish .field-label { padding-top: 2px; }
.field:focus-within .field-label { color: var(--ink); }

.field input, .field textarea {
  border: 0;
  padding: 2px 0;
  width: 100%;
  box-sizing: border-box;
}
.field textarea { resize: none; line-height: 1.7; }

.honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.send-block { gap: 14px; align-items: flex-start; }
.send {
  align-self: flex-start;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--grey);
  transition: color var(--t-color) ease;
}
.send[data-armed] { color: var(--ink); }
.send[data-armed] .arrow { transform: translate3d(3px, -3px, 0); }
.send:hover { color: var(--grey); }
.send .arrow { color: inherit; }
.send[disabled] { cursor: default; color: var(--grey); }

.form-note { color: var(--grey); line-height: 1.7; }
.form-note:empty { display: none; }

.received {
  width: min(100%, 700px);
  font-family: var(--serif);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(30px, 3.6vw, 48px);
  line-height: 1.18;
}

/* ---- Paper ----------------------------------------------------------- */

.paper > .rule { margin: 0; }

.paper-title {
  margin: 26px 0 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(28px, 3vw, 40px);
  line-height: 1.15;
  letter-spacing: 0;
  text-wrap: pretty;
}
.paper-authors { margin: 16px 0 0; color: var(--grey); line-height: 1.7; }
a.author { color: var(--grey); border-bottom: 1px solid var(--rule); transition: color var(--t-color) ease; }
a.author:hover { color: var(--ink); }
.paper-venue { margin: 4px 0 26px; color: var(--grey); line-height: 1.7; }
.sep { padding: 0 .5em; opacity: .6; }

/* Serif, so the one-line summary reads as a voice rather than as more data. */
.paper-lead {
  margin: 26px 0 0;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(19px, 2vw, 24px);
  line-height: 1.45;
  color: var(--ink);
  max-width: 44ch;
  text-wrap: pretty;
}
.paper-abstract {
  margin: 22px 0 26px;
  color: var(--grey);
  line-height: 1.8;
  max-width: 66ch;
}
/* The column is a flex container, so margins do not collapse: without this,
   two paragraphs sit 48px apart instead of one line. */
.paper-abstract + .paper-abstract { margin-top: 0; }
.paper-abstract a { color: var(--grey); border-bottom: 1px solid var(--rule); transition: color var(--t-color) ease; }
.paper-abstract a:hover { color: var(--ink); }

/* Same label column as the contact form, so the page feels native. */
.paper-facts { margin: 26px 0; }
.fact {
  display: grid;
  grid-template-columns: 110px minmax(0, 1fr);
  align-items: baseline;
  padding: 10px 0;
}
.fact dt { color: var(--grey); }
.fact dd { margin: 0; color: var(--grey); line-height: 1.7; }
.fact-links { display: flex; flex-wrap: wrap; gap: 24px; }
.fact a { color: var(--ink); transition: color var(--t-color) ease; }
.fact a:hover { color: var(--grey); }
.fact a:hover .arrow { transform: translate3d(3px, -3px, 0); color: var(--gold); }
.fact .arrow { margin-left: 10px; }

/* A project page can gather several things under one name. They keep the
   rhythm of the index: a hairline, a name, a line, a link. */
.group-label {
  margin: 26px 0 0;
  padding: 0;
  font: inherit;
  font-weight: 400;
  color: var(--grey);
}
.subproject { display: block; margin: 22px 0 0; }
.subproject > .rule { margin: 0 0 20px; }
.subproject-name {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(21px, 2.2vw, 26px);
  line-height: 1.2;
  letter-spacing: 0;
  color: var(--ink);
}
.subproject-lead { margin: 10px 0 0; color: var(--grey); line-height: 1.8; max-width: 62ch; }
.subproject-links { display: flex; flex-wrap: wrap; gap: 24px; margin: 12px 0 0; }
.subproject-links a { color: var(--grey); transition: color var(--t-color) ease; }
.subproject-links a:hover { color: var(--ink); }
.subproject-links a:hover .arrow { transform: translate3d(3px, -3px, 0); color: var(--gold); }
.subproject-links .arrow { margin-left: 10px; }

/* A project's lead is a statement, not a summary of a paper: it sits closer. */
.project-lead { max-width: 38ch; }

.js .subproject {
  opacity: 0;
  transform: translate3d(0, 12px, 0);
  transition: opacity var(--t-enter) ease calc(var(--i) * var(--stagger)),
              transform var(--t-enter) var(--ease-out) calc(var(--i) * var(--stagger));
}
.js .stage[data-page] .subproject { opacity: 1; transform: translate3d(0, 0, 0); }
.js .stage[data-leaving] .subproject { transition-delay: 0ms; }

.cite { margin: 26px 0 0; }
.cite-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 24px;
  margin: 0;
  font: inherit;
  font-weight: 400;
  color: var(--grey);
}
.cite-copy {
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--grey);
  transition: color var(--t-color) ease;
}
.cite-copy:hover { color: var(--ink); }
.cite-body {
  margin: 18px 0 0;
  font-family: var(--mono);
  font-size: 12px;
  line-height: 1.8;
  color: var(--grey);
  /* Soft-wrapped so a long title does not run off the edge. The copied text
     is textContent, so it keeps the real line breaks either way. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  letter-spacing: 0.02em;
}

/* ---- Prose ------------------------------------------------------------ */

.prose-title {
  margin: 26px 0 0;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(30px, 3.6vw, 48px);
  line-height: 1.18;
}
.prose-section { margin: 38px 0 0; }
.prose-section h2 { margin: 0 0 14px; font: inherit; font-weight: 400; color: var(--ink); }
.prose-section p { margin: 0 0 14px; color: var(--grey); line-height: 1.8; max-width: 66ch; }
.prose-section p:last-child { margin-bottom: 0; }
.prose .rule-closing { margin-top: 44px; }
.prose-note { margin: 22px 0 0; color: var(--grey); }

.js .prose-section {
  opacity: 0;
  transform: translate3d(0, 12px, 0);
  transition: opacity var(--t-enter) ease calc(var(--i) * var(--stagger)),
              transform var(--t-enter) var(--ease-out) calc(var(--i) * var(--stagger));
}
.js .stage[data-page] .prose-section { opacity: 1; transform: translate3d(0, 0, 0); }
.js .stage[data-leaving] .prose-section { transition-delay: 0ms; }

.form-privacy { margin: 0; }
.form-privacy a { color: var(--grey); transition: color var(--t-color) ease; }
.form-privacy a:hover { color: var(--ink); }

/* ---- Narrow viewports ------------------------------------------------ */


/* Five social icons plus two links is a wide row. It tightens, then wraps,
   rather than ever reaching across to the easter-egg dot. */
@media (max-width: 520px) {
  .nav { gap: 18px 22px; }
  .social { gap: 14px; }

  /* Bounded on both sides so the row has something to wrap inside, reversed so
     the links keep the bottom edge and the icons move above them. Flex only
     wraps when it must, so this is a valve, not a layout change. Three links
     and five icons reach the easter-egg dot at about 490px. */
  .nav {
    /* Starts clear of the easter-egg dot and its hit area, so "fits the row"
       and "does not reach the dot" become the same question. */
    left: calc(var(--gutter) + 48px);
    flex-wrap: wrap-reverse;
    justify-content: flex-end;
    pointer-events: none;
  }
  .nav > * { pointer-events: auto; }
  .scrim-bottom {
    height: 150px;
    background: linear-gradient(to top, var(--paper) 0, var(--paper) 84px, transparent 150px);
  }

  /* A project name shares its line with the link comfortably. A paper title
     does not: side by side it is squeezed into a column six words deep. */
  .row-long { grid-template-columns: minmax(0, 1fr); }
  .row-long .row-link { padding-top: 14px; }
}

@media (max-width: 420px) {
  :root { --gutter: 24px; }
  .screen-scroll { padding-bottom: 128px; }
  .field, .fact { grid-template-columns: 90px minmax(0, 1fr); }
}

@media (max-width: 360px) {
  .nav { gap: 16px 18px; }
  .social { gap: 11px; }
}

/* ---- Reduced motion -------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  * {
    transition-duration: 1ms !important;
    transition-delay: 0ms !important;
    animation: none !important;
  }
}
