@charset "utf-8";

/* Fonts. All self-hosted in assets/fonts/, licences alongside.

   Basteleur Moonlight and Basteleur Bold are separate designs, not two weights,
   so each is its own family used at normal weight. Don't set font-weight on
   them or the browser fakes a bold.

   Basteleur: Keussel, via Velvetyne (OFL)
   Newsreader: Production Type (OFL), variable 200-800
   Commit Mono: Eigil Nikolajsen (OFL) */

@font-face {
  font-family: "Basteleur Moonlight";
  src: url("../fonts/Basteleur-Moonlight.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Basteleur Bold";
  src: url("../fonts/Basteleur-Bold.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Newsreader";
  src: url("../fonts/Newsreader-normal-latin.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  src: url("../fonts/Newsreader-normal-latin-ext.woff2") format("woff2");
  font-weight: 200 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Newsreader";
  src: url("../fonts/Newsreader-italic-latin.woff2") format("woff2");
  font-weight: 200 800;
  font-style: italic;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Commit Mono";
  src: url("../fonts/CommitMono-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Commit Mono";
  src: url("../fonts/CommitMono-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* Golden Hour: personal site. Plain CSS, no framework, no build step. */


/* Tokens */

:root {
  /* Palette.
     --sun is for decoration and big display type only. It's too light for body
     text on the cream background, so use --bark-deep or --dusk-deep for links
     and coloured text on light grounds. */
  --sun:        #F5B841;
  --sun-hot:    #FFD98A;
  --sun-lit:    #FFE7A8;  /* link colour on the dark sections, about 5:1 contrast */
  --moss:       #2F5D3A;
  --moss-lit:   #4E8A5C;
  --bark-deep:  #A8492C;
  --paper:      #FDF6E3;
  --dusk-deep:  #6B4E92;
  --ink:        #1B1A17;

  /* What the components actually use. Dark mode only reassigns these. */
  --bg:         var(--paper);
  --fg:         var(--ink);
  --fg-soft:    #55524A;
  --rule:       var(--ink);
  --accent:     var(--bark-deep);
  --accent-2:   var(--dusk-deep);


  /* Type. All serif, on purpose. The fallbacks are serifs too, so the page still
     looks like itself before the webfonts load. */
  --font-display: "Basteleur Moonlight", "Hoefler Text",
                  "Baskerville", "Iowan Old Style", "Palatino Linotype",
                  Palatino, Georgia, serif;
  --font-body:    "Newsreader", "Charter", "Bitstream Charter",
                  "Iowan Old Style", "Source Serif 4", Georgia,
                  "Times New Roman", serif;
  --font-mono:    "Commit Mono", ui-monospace, SFMono-Regular, "SF Mono",
                  Menlo, Consolas, "Liberation Mono", monospace;

  /* Section colours. The page gets darker as you scroll down, and the jump at
     --st3 (into the trees) is meant to be noticeable. */
  --st1: #F7EDD6;  /* grove   - warm paper */
  --st2: #DCEBF5;  /* sky     - about */
  --st3: #3E6B4A;  /* trees   - research */
  --st4: #E8C24A;  /* field   - computation */
  --st5: #4A3520;  /* flowers - teaching */
  --st6: #2A1E12;  /* footer */

  /* Space & structure */
  --wrap:       74rem;
  --gutter:     clamp(1.25rem, 5vw, 4rem);
  --section-y:  clamp(4.5rem, 11vh, 8rem);
  --rule-w:     4px;

  /* Motion */
  --ease:       cubic-bezier(0.22, 1, 0.36, 1);
  --dur:        0.6s;
}

/* Dark mode. Follows the system setting unless the header toggle has set
   data-theme on <html>, in which case that wins. The values are repeated
   because a media query can't be combined with an attribute selector. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg:       #14180F;
    --fg:       #F3EEDD;
    --fg-soft:  #B3AE9C;
    --rule:     #F3EEDD;
    --accent:   #E58A66;
    --accent-2: #B79BE0;

    /* Same descent, just starting darker. */
    --st1: #171E15;
    --st2: #131C24;
    --st3: #16281B;
    --st4: #2E2513;
    --st5: #1C140B;
    --st6: #100B06;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg:       #14180F;
  --fg:       #F3EEDD;
  --fg-soft:  #B3AE9C;
  --rule:     #F3EEDD;
  --accent:   #E58A66;
  --accent-2: #B79BE0;

  /* Same descent, just starting darker. */
  --st1: #171E15;
  --st2: #131C24;
  --st3: #16281B;
  --st4: #2E2513;
  --st5: #1C140B;
  --st6: #100B06;
}


/* Reset and base */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* keeps anchor jumps from landing under the sticky top bar */
  scroll-padding-top: 5rem;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: clamp(1.05rem, 0.95rem + 0.35vw, 1.2rem);
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { max-width: 100%; height: auto; display: block; }

ul, ol { margin: 0; padding: 0; list-style: none; }

a { color: var(--accent); text-decoration-thickness: 2px; text-underline-offset: 3px; }
a:hover { color: var(--accent-2); }

:focus-visible {
  outline: 3px solid var(--sun);
  outline-offset: 3px;
  border-radius: 2px;
}

::selection { background: var(--sun); color: var(--ink); }


/* Typography */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 400;
  line-height: 0.95;
  letter-spacing: -0.02em;
  margin: 0;
  text-wrap: balance;
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--fg-soft);
  margin: 0 0 1.25rem;
}

.section-h {
  font-size: clamp(2.75rem, 9vw, 6.5rem);
  margin-bottom: 0.4em;
}

.side-h {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--fg-soft);
  padding-bottom: 0.75rem;
  margin: 4rem 0 1.75rem;
  border-bottom: 2px solid currentColor;
}

.lede {
  font-size: clamp(0.7rem, 0.7rem + 0.9vw, 1.7rem);
  line-height: 1.4;
  max-width: 70ch;
  margin: 0 0 2.5rem;
  text-wrap: pretty;
}

.prose { max-width: 62ch; }
.prose p { margin: 0 0 1.35em; text-wrap: pretty; }
.prose__lead {
  font-size: 1.25em;
  line-height: 1.45;
  font-weight: 500;
}


/* Layout */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.stratum {
  position: relative;
  padding-block: var(--section-y);
  border-bottom: var(--rule-w) solid var(--rule);
}

.split {
  display: grid;
  gap: clamp(2rem, 5vw, 4.5rem);
  grid-template-columns: 1fr;
}
@media (min-width: 62rem) {
  .split { grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); }
  .split__side { border-left: 2px solid currentColor; padding-left: clamp(1.5rem, 3vw, 3rem); }
  .split__side .side-h { margin-top: 0; }
}

/* A figure in the side column carries its own frame, so it skips the rule. */
.split__side--figure { border-left: 0; padding-left: 0; }

.portrait { margin: 0; }
/* Stacked on narrow screens a 4:5 frame would fill the whole viewport. */
@media (max-width: 61.999rem) {
  .portrait { max-width: 22rem; margin-inline: auto; }
}
.portrait__cap {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--fg-soft);
  margin-top: 0.85rem;
}

/* Placeholder stand-in for an image that does not exist yet. Delete .ph
   along with its markup once a real <img> goes in. */
.ph {
  aspect-ratio: 4 / 5;
  display: grid;
  place-items: center;
  border: 3px dashed currentColor;
  color: var(--fg-soft);
  background-image: repeating-linear-gradient(
    45deg,
    transparent 0 10px,
    color-mix(in srgb, currentColor 12%, transparent) 10px 11px
  );
}
.ph__label {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-align: center;
  line-height: 1.8;
}

.btn {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 0.9em 1.5em;
  border: 3px solid var(--ink);
  transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.btn--sun {
  background: var(--sun);
  color: var(--ink);
  box-shadow: 6px 6px 0 var(--ink);
}
.btn--outline {
  background: transparent;
  color: var(--fg);
  border-color: currentColor;
  box-shadow: 6px 6px 0 currentColor;
}
.btn:hover, .btn:focus-visible {
  transform: translate(3px, 3px);
  box-shadow: 3px 3px 0 currentColor;
  color: var(--ink);
}
.btn--sun:hover, .btn--sun:focus-visible { box-shadow: 3px 3px 0 var(--ink); }


/* Skip link, top bar, sun cursor */

.skip-link {
  position: absolute;
  left: 1rem;
  top: -100%;
  z-index: 999;
  background: var(--sun);
  color: var(--ink);
  font-weight: 700;
  padding: 0.75rem 1.25rem;
  border: 3px solid var(--ink);
  text-decoration: none;
}
.skip-link:focus { top: 1rem; }

.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.75rem var(--gutter);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 2px solid var(--rule);
}

.topbar__mark {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.1rem;
  letter-spacing: -0.01em;
  color: var(--fg);
  text-decoration: none;
}
.topbar__sun {
  width: 1.1rem;
  height: 1.1rem;
  border-radius: 50%;
  background: var(--sun);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--sun) 35%, transparent);
  animation: pulse 4s var(--ease) infinite;
}
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--sun) 35%, transparent); }
  50%      { box-shadow: 0 0 0 8px color-mix(in srgb, var(--sun) 0%, transparent); }
}

.topbar__nav { margin-left: auto; }
.topbar__nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem clamp(0.7rem, 2.5vw, 2rem);
}
.topbar__nav a {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--fg);
  text-decoration: none;
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
}
.topbar__nav a:hover,
.topbar__nav a[aria-current="true"] { border-bottom-color: var(--sun); }

/* Light/dark toggle, far right of the top bar. Shows the moon in light mode
   and the sun in dark mode, i.e. the mode it will switch to. */
.theme-toggle {
  display: inline-grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 2px solid var(--rule);
  border-radius: 50%;
  background: transparent;
  color: var(--fg);
  cursor: pointer;
}
.theme-toggle:hover { background: color-mix(in srgb, var(--sun) 30%, transparent); }
.theme-toggle:focus-visible { outline: 3px solid var(--sun); outline-offset: 2px; }
.theme-toggle svg { width: 1.1rem; height: 1.1rem; }
/* Once the nav no longer fits beside the name, give it its own row so the
   toggle stays in the top-right corner instead of wrapping under the links. */
@media (max-width: 61.999rem) {
  .topbar__nav { order: 3; flex-basis: 100%; margin-left: 0; }
}
.theme-toggle__sun { display: none; }
:root[data-theme="dark"] .theme-toggle__sun { display: block; }
:root[data-theme="dark"] .theme-toggle__moon { display: none; }

/* The sun cursor replaces the arrow instead of trailing it, so the disc has to
   sit exactly under the pointer. JS un-hides it and adds .js-suncursor to <html>,
   but only for fine pointers when motion is allowed. Without JS you keep the
   normal arrow. */
.sun-cursor {
  position: fixed;
  top: 0; left: 0;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;      /* centre the disc on the pointer */
  border-radius: 50%;
  z-index: 60;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.3s linear;
  background: radial-gradient(circle, var(--sun-hot) 0 36%, var(--sun) 37% 100%);
  /* The dark outline keeps the sun visible over the yellow flowers, where a
     yellow disc would disappear. */
  box-shadow:
    0 0 0 1.5px rgba(27, 26, 23, 0.35),
    0 0 12px 3px rgba(245, 184, 65, 0.55);
}
.sun-cursor.is-live { opacity: 1; }

/* Rays: eight spokes around the disc, turning slowly. */
.sun-cursor::after {
  content: "";
  position: absolute;
  inset: -10px;
  background: repeating-conic-gradient(from 0deg,
    var(--sun) 0deg 6deg, transparent 6deg 45deg);
  -webkit-mask: radial-gradient(circle, transparent 0 12px, #000 13px 19px, transparent 19px);
          mask: radial-gradient(circle, transparent 0 12px, #000 13px 19px, transparent 19px);
  animation: sun-turn 26s linear infinite;
}

/* Soft glow underneath so the sun lights up whatever it passes over. Purely
   decoration. */
.sun-cursor::before {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 22rem; height: 22rem;
  margin: -11rem 0 0 -11rem;
  mix-blend-mode: soft-light;
  background:
    radial-gradient(circle, rgba(255, 240, 190, 0.75) 0%,
                            rgba(245, 184,  65, 0.40) 8%,
                            rgba(245, 184,  65, 0.16) 26%,
                            rgba(245, 184,  65, 0.00) 64%);
}

@keyframes sun-turn { to { transform: rotate(360deg); } }

/* Only hide the arrow once JS has confirmed the sun is drawing. Links and
   buttons set their own cursor, so they need overriding as well. */
.js-suncursor,
.js-suncursor a,
.js-suncursor button,
.js-suncursor [role="button"] { cursor: none; }


/* Hero: the full-width photograph */

.stratum--hero {
  padding-top: 0;
  padding-bottom: 0;
  background: var(--st1);
  overflow: hidden;
}

/* Full width, no gutter or frame. Don't crop the image any other way or the
   hotspot percentages stop lining up. */
/* The photo runs straight into the About section, and its bottom border is the
   divider. */
.grove.grove--bleed { margin: 0; padding-inline: 0; }
.grove.grove--bleed .grove-fig {
  max-width: none;
  border: 0;
  border-bottom: var(--rule-w) solid var(--rule);

  /* Crops some sky off the top. The source is 3:2, so in a 16:9 box `cover` shows
     the bottom 84.4% of it. Both ratios are fixed, so the crop is the same at
     every width and the hotspot percentages stay accurate. If you change this
     ratio, retune the hotspots. */
  aspect-ratio: 16 / 9;
}
.grove.grove--bleed .grove-img {
  height: 100%;
  object-fit: cover;
  object-position: 50% 100%;
}

/* The name sits over the open sky on the left. It's dark rather than white
   because the clouds are white and would wash it out. */
.hero {
  position: absolute;
  left: clamp(1.25rem, 5vw, 4rem);
  top: clamp(1.25rem, 5%, 4.5rem);
  z-index: 2;
  max-width: min(56%, 44rem);
  line-height: 1.5;
  will-change: transform;
  /* Click-through. The name and thesis sit on top of the sky band, and when they
     caught clicks they blocked the link to About, which is the most likely
     click. Now the whole top band is the About link. The catch is you can't
     select the hero text. */
  pointer-events: none;
}
@media (max-width: 60rem) {
  .hero { max-width: 82%; }
}
@media (max-width: 47.99rem) {
  .grove.grove--bleed .grove-fig { aspect-ratio: 5 / 6; }
  .grove.grove--bleed .grove-img { object-position: 50% 78%; }
  .hero { top: clamp(0.75rem, 3%, 2rem); max-width: 92%; }
  .hero__name { font-size: clamp(2.1rem, 10vw, 3.4rem); }
  .hero__thesis { font-size: clamp(0.85rem, 3.5vw, 1.1rem); max-width: 26ch; margin-bottom: 0.6rem; }
  .hero__meta { font-size: 0.6rem; padding: 0.35em 0.6em; }
}

.hero__name {
  font-size: clamp(2.5rem, 8.5vw, 8rem);
  line-height: 0.9;
  letter-spacing: -0.03em;
  margin: 0 0 clamp(0.75rem, 2vw, 1.5rem);
  color: var(--ink);
}
.hero__name span { display: block; }
.hero__name span:last-child { color: var(--bark-deep); margin-left: 0.06em; }

.hero__thesis {
  font-family: var(--font-body);
  font-size: clamp(0.95rem, 0.6rem + 1.05vw, 1.6rem);
  line-height: 1.35;
  font-weight: 450;
  color: var(--ink);
  
  max-width: none;          /* as wide as the name above it */
  margin: 0 0 0.9rem;
  text-wrap: pretty;
}

/* This line can land on the treeline, where a soft scrim isn't enough, so it
   gets a solid background. */
.hero__meta {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: clamp(0.62rem, 0.54rem + 0.22vw, 0.8rem);
  letter-spacing: 0.06em;
  line-height: 1.5;
  color: var(--ink);
  background: color-mix(in srgb, var(--paper) 90%, transparent);
  padding: 0.4em 0.75em;
  margin: 0;
}

/* Very faint scrim so the text stays readable whatever the sky looks like. */
.hero::before {
  content: "";
  position: absolute;
  inset: -12% -30% -14% -12%;
  z-index: -1;
  background: linear-gradient(105deg,
              rgba(253, 246, 227, 0.80) 0%,
              rgba(253, 246, 227, 0.62) 38%,
              rgba(253, 246, 227, 0.22) 68%,
              rgba(253, 246, 227, 0) 88%);
  /* Fade the bottom as well, otherwise the scrim leaves a visible line across
     the treeline on narrow screens. */
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 58%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 58%, transparent 100%);
}

/* Grove */

.stratum--grove { background: var(--st1); }

.grove {
  margin: clamp(1.5rem, 4vw, 3rem) 0;
  padding-inline: var(--gutter);
}
/* The photo has four clickable regions laid over it. Their positions are
   percentages, so they stay aligned at any width as long as the image keeps
   its aspect ratio (no object-fit cropping). */
.grove-fig {
  position: relative;
  margin: 0;
  max-width: 78rem;
  margin-inline: auto;
  border: 3px solid var(--rule);
  background: #1B2018;          /* placeholder colour until the photo loads */
  line-height: 0;
}
.grove-img { width: 100%; height: auto; display: block; }

.spot {
  position: absolute;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;   /* labels go on the right, away from the name */
  padding: clamp(0.5rem, 1.5vw, 1rem);
  text-decoration: none;
  border: 2px solid transparent;
  border-radius: 3px;
  transition: border-color 0.35s var(--ease), background-color 0.35s var(--ease);
}
.spot:hover, .spot:focus-visible {
  border-color: var(--sun);
  background: color-mix(in srgb, var(--sun) 14%, transparent);
}
.spot:focus { outline: none; }

.spot__label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  line-height: 1.3;
  padding: 0.5em 0.8em;
  color: var(--ink);
  background: var(--sun);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.spot:hover .spot__label,
.spot:focus-visible .spot__label { opacity: 1; transform: none; }

/* the section the region links to */
.spot__label b {
  display: block;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 400;
  letter-spacing: -0.01em;
  text-transform: none;
}

.spot        { left: 0; width: 100%; }
.spot--sky   { top:  0%; height: 40%; }   /* sky and cloud */
.spot--trees { top: 40%; height: 19%; }   /* treeline */
.spot--field { top: 59%; height: 23%; }   /* flowering field */
.spot--flowers { top: 82%; height: 18%; }  /* closest flowers */

/* Touch screens have no hover, so always show the labels. */
@media (hover: none) and (min-width: 48rem) {
  .spot__label { opacity: 1; transform: none; }
  .spot { border-color: color-mix(in srgb, var(--sun) 55%, transparent); }
}


@media (max-width: 47.99rem) {
  .spot { display: none; }
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}


.stratum--sky { background: var(--st2); }

.facts { display: grid; gap: 1.15rem; }
.facts li { display: grid; gap: 0.15rem; }
.facts__k {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
}
.facts__v { font-size: 0.98rem; line-height: 1.45; }


.stratum--trees {
  background: var(--st3);
  color: var(--paper);
  --fg: var(--paper);
  --fg-soft: #DCEBDF;
  --rule: var(--paper);
  --accent: var(--sun-lit);
  --accent-2: var(--paper);
}
.stratum--trees a { color: var(--sun-lit); }
.stratum--trees a:hover { color: var(--paper); }

.threads { display: grid; gap: 0; border-top: 2px solid currentColor; }
.threads li { padding: 1.75rem 0; border-bottom: 2px solid currentColor; }
.threads h4 { font-size: clamp(1.3rem, 3vw, 2rem); margin-bottom: 0.5rem; }
.threads p { margin: 0; max-width: 70ch; color: var(--fg-soft); }

.pubs { display: grid; gap: 0; border-top: 2px solid currentColor; counter-reset: pub; }

.pub {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5rem 2rem;
  padding: 1.75rem 0;
  border-bottom: 2px solid currentColor;
  align-items: start;
}
@media (min-width: 48rem) {
  .pub { grid-template-columns: 5rem minmax(0, 1fr) auto; }
}
.pub__year {
  font-family: var(--font-mono);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--sun-lit);
  margin: 0;
}
.pub__title { font-size: clamp(1.15rem, 2.2vw, 1.6rem); margin-bottom: 0.4rem; }
.pub__authors, .pub__venue { margin: 0 0 0.25rem; font-size: 0.95rem; color: var(--fg-soft); }
.pub__links { margin: 0.65rem 0 0; display: flex; flex-wrap: wrap; gap: 1rem; }
.pub__links a {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  border-bottom: 2px solid currentColor;
  padding-bottom: 1px;
}
.pubs__tag {
  justify-self: start;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.3em 0.7em;
  border: 2px solid currentColor;
  white-space: nowrap;
}

.stratum--field {
  background: var(--st4);
  color: var(--ink);
  --fg: var(--ink);
  --fg-soft: #5A4A1E;
  --rule: var(--ink);
  --accent: #1F4A2C;      /* deep green, 5.9:1 on the gold */
  --accent-2: var(--ink);
  --bg: var(--paper);     /* pale cards on the gold background */
}
/* In dark mode the gold turns deep brown, so the ink text above would vanish.
   Flip to light text and dark cards, keeping the gold for borders. Repeated
   for the same reason as the dark tokens at the top. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .stratum--field {
    color: #F3EEDD;
    --fg: #F3EEDD;
    --fg-soft: #D6C9A4;
    --rule: #E8C24A;
    --accent: #9BD3A8;
    --accent-2: #F3EEDD;
    --bg: #1E1709;
  }
}
:root[data-theme="dark"] .stratum--field {
  color: #F3EEDD;
  --fg: #F3EEDD;
  --fg-soft: #D6C9A4;
  --rule: #E8C24A;
  --accent: #9BD3A8;
  --accent-2: #F3EEDD;
  --bg: #1E1709;
}

.projects {
  display: grid;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}
.project {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  padding: clamp(1.5rem, 3vw, 2.25rem);
  background: var(--bg);
  border: 3px solid var(--rule);
  box-shadow: 8px 8px 0 var(--rule);
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.project:hover { transform: translate(4px, 4px); box-shadow: 4px 4px 0 var(--accent); }
.project__title { font-size: clamp(1.4rem, 3vw, 2rem); }
.project__title a { color: inherit; text-decoration: none; }
.project__title a:hover { color: var(--accent); }
.project__blurb { margin: 0; flex: 1; color: var(--fg-soft); font-size: 0.98rem; line-height: 1.55; }
.project__tags { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.project__tags li {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0.3em 0.65em;
  border: 2px solid currentColor;
}


.stratum--flowers {
  position: relative;
  overflow: hidden;
  background: var(--st5);
  color: var(--paper);
  --fg: var(--paper);
  --fg-soft: #C8B79C;
  --rule: var(--paper);
  --accent: var(--sun-lit);
  --accent-2: var(--paper);
  border-bottom: 0;
}
.stratum--flowers a { color: var(--sun-lit); }


.contact {
  display: grid;
  gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: 1fr;
  position: relative;
  z-index: 1;
}
@media (min-width: 52rem) {
  .contact { grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); }
  .contact__links .side-h { margin-top: 0; }
}

.bigmail {
  display: inline-block;
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 3.0vw, 3rem);
  font-weight: 400;
  letter-spacing: -0.03em;
  line-height: 1.1;
  word-break: break-word;
  text-decoration: none;
  border-bottom: 4px solid var(--sun);
  margin-bottom: 2rem;
  transition: background-color 0.3s var(--ease), color 0.3s var(--ease);
}
.bigmail:hover, .bigmail:focus-visible { background: var(--sun); color: var(--ink); }

.contact__links ul { display: grid; border-top: 2px solid currentColor; }
.contact__links a {
  display: block;
  padding: 0.8rem 0.4rem;
  border-bottom: 2px solid currentColor;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background-color 0.25s var(--ease), color 0.25s var(--ease), padding-left 0.25s var(--ease);
}
.contact__links a:hover, .contact__links a:focus-visible {
  background: var(--sun);
  color: var(--ink);
  padding-left: 1.1rem;
}


/* Footer */

.colophon {
  background: var(--st6);
  color: #B9B4A6;
  padding-block: 2.5rem;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.05em;
}
.colophon .wrap { display: flex; flex-wrap: wrap; gap: 0.75rem 2rem; justify-content: space-between; }
.colophon p { margin: 0; }
.colophon a { color: var(--sun); }


/* Motion and reveal states */

/* Elements start hidden only after JS confirms it's running. Otherwise, if JS
   failed, the page would be blank. */
.js-motion [data-reveal] {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.js-motion [data-reveal].is-visible { opacity: 1; transform: none; }

/* children of a group appear one after another */
.js-motion [data-reveal-group] > * {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.js-motion [data-reveal-group].is-visible > * { opacity: 1; transform: none; }
.js-motion [data-reveal-group].is-visible > *:nth-child(1) { transition-delay: 0.00s; }
.js-motion [data-reveal-group].is-visible > *:nth-child(2) { transition-delay: 0.08s; }
.js-motion [data-reveal-group].is-visible > *:nth-child(3) { transition-delay: 0.16s; }
.js-motion [data-reveal-group].is-visible > *:nth-child(4) { transition-delay: 0.24s; }
.js-motion [data-reveal-group].is-visible > *:nth-child(5) { transition-delay: 0.32s; }
.js-motion [data-reveal-group].is-visible > *:nth-child(6) { transition-delay: 0.40s; }

/* The photo fades in rather than sliding or scaling. It runs edge to edge, so
   scaling would make it wider than the screen mid-transition. */
.js-motion .grove-fig {
  opacity: 0;
  transition: opacity 1s var(--ease);
}
.js-motion .grove.is-visible .grove-fig { opacity: 1; }


/* Reduced motion */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* don't leave anything hidden */
  .js-motion [data-reveal],
  .js-motion [data-reveal-group] > * {
    opacity: 1 !important;
    transform: none !important;
  }
  .js-motion .grove-fig {
    opacity: 1 !important;
    transform: none !important;
  }

  .sun-cursor { display: none !important; }
}


/* The longer-bio toggle is a button styled to look like a link. */
.linkbtn {
  color: var(--accent);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  font-size: clamp(1.05rem, 0.95rem + 0.35vw, 1.2rem);
  font-family: 'Newsreader', serif;
  text-underline-offset: 3px;
}
.linkbtn:hover { color: var(--accent-2); }
.linkbtn::after { content: " +"; text-decoration: none; display: inline-block; }
.linkbtn[aria-expanded="true"]::after { content: " \2212"; }
.bio-more[hidden] { display: none; }
