/* ------------------------------------------------------------------
   Trey Woodard — Portfolio hero
   Timeline (seconds, after .is-ready is set on <main>):
     0.0  photo fades in from black
     0.6  name fades in ............ 2.6 name fades out → photo only
     3.3  frame draws itself in from top‑centre
     4.5  headline typewriter begins: "Thoughtful" types then backspaces,
          then cycles through role words (Secure, Resilient, Hardened, ...)
     4.9  nav / lede / status settle in
     5.0  frosted glass card slides up into frame
------------------------------------------------------------------ */

@property --draw {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}

:root {
  --ink: #f4f1ea;
  --ink-soft: rgba(244, 241, 234, 0.72);
  --ink-mute: rgba(244, 241, 234, 0.45);
  --moss: #b7c4a0;
  --frame: rgba(244, 241, 234, 0.55);
  --glass-bg: rgba(20, 28, 30, 0.42);
  --glass-line: rgba(244, 241, 234, 0.16);
  --serif: "Fraunces", "Iowan Old Style", Georgia, serif;
  --sans: "Manrope", system-ui, -apple-system, "Segoe UI", sans-serif;

  --t-photo: 0s;
  --t-name-in: 0.6s;
  --t-name-out: 2.6s;
  --t-frame: 3.3s;
  --t-text: 4.5s;
  --t-ui: 4.9s;
  --t-card: 5s;

  --ridge-h: clamp(150px, 18vw, 300px);
  --topo-tile: clamp(400px, 44vw, 640px);

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  background: #0e1416;
  color: var(--ink);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

html {
  scroll-behavior: smooth;
  scroll-padding-top: 40px;
}

a {
  color: inherit;
  text-decoration: none;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Stage ---------- */

.hero {
  position: relative;
  min-height: 100svh;
  overflow: hidden;
  isolation: isolate;
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(300px, 400px);
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "nav  nav"
    "head card"
    "foot card";
  gap: clamp(20px, 3vw, 40px);
  padding: clamp(84px, 9vw, 120px) clamp(48px, 8vw, 128px) clamp(64px, 7vw, 96px);
}

.hero__photo {
  position: absolute;
  inset: 0;
  z-index: -2;
  background: #0e1416 url("/assets/hero.webp") center / cover no-repeat;
  opacity: 0;
  transform: scale(1.06);
  filter: saturate(0.85) brightness(0.92);
}

.hero__veil {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(180deg, rgba(12, 20, 24, 0.55) 0%, rgba(12, 20, 24, 0.15) 35%, rgba(12, 20, 24, 0.35) 100%),
    linear-gradient(90deg, rgba(12, 20, 24, 0.45) 0%, rgba(12, 20, 24, 0) 55%);
  opacity: 0;
}

.is-ready .hero__photo {
  animation: photo-in 1.8s var(--ease-soft) var(--t-photo) both;
}

.is-ready .hero__veil {
  animation: fade-in 1.4s var(--ease-soft) calc(var(--t-photo) + 0.4s) both;
}

@keyframes photo-in {
  from {
    opacity: 0;
    transform: scale(1.06);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* ---------- Intro name ---------- */

.intro {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  pointer-events: none;
}

.intro__name {
  font-family: var(--serif);
  font-weight: 300;
  font-variation-settings: "SOFT" 80, "WONK" 1;
  font-size: clamp(2.6rem, 6.5vw, 6rem);
  letter-spacing: 0.04em;
  color: var(--ink);
  opacity: 0;
  filter: blur(10px);
  text-shadow: 0 2px 40px rgba(0, 0, 0, 0.4);
}

.is-ready .intro__name {
  animation:
    name-in 1.4s var(--ease-soft) var(--t-name-in) both,
    name-out 1s var(--ease-soft) var(--t-name-out) both;
}

@keyframes name-in {
  from {
    opacity: 0;
    filter: blur(10px);
    letter-spacing: 0.12em;
  }
  to {
    opacity: 1;
    filter: blur(0);
    letter-spacing: 0.04em;
  }
}

@keyframes name-out {
  from {
    opacity: 1;
    filter: blur(0);
  }
  to {
    opacity: 0;
    filter: blur(14px);
  }
}

/* ---------- Drawn frame ---------- */

.frame {
  position: absolute;
  inset: clamp(24px, 3.5vw, 52px) clamp(22px, 3.5vw, 60px);
  z-index: 1;
  pointer-events: none;
}

.frame__svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.frame__line {
  width: calc(100% - 2px);
  height: calc(100% - 2px);
  fill: none;
  stroke: var(--frame);
  stroke-width: 1.5;
  stroke-linecap: butt;
  /* pathLength="1" → dash units are fractions of the perimeter. The pattern
     period is exactly one perimeter (dash + gap = 1) so it wraps cleanly, and
     the dash grows outward from --start (0 = top-left corner, clockwise). */
  --draw: 0;
  --start: 0.15;
  stroke-dasharray: var(--draw) calc(1 - var(--draw));
  stroke-dashoffset: calc(var(--draw) / 2 - var(--start));
}

.is-ready .frame__line {
  animation: draw 1.6s var(--ease-out) var(--t-frame) both;
}

@keyframes draw {
  from { --draw: 0; }
  to { --draw: 1; }
}

/* ---------- Nav ---------- */

.nav {
  grid-area: nav;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
  font-size: 0.85rem;
  font-weight: 500;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
  font-weight: 600;
  letter-spacing: 0.01em;
}

.brand__mark {
  width: 22px;
  height: 22px;
  color: var(--moss);
}

.nav__links {
  display: flex;
  gap: clamp(20px, 2.6vw, 36px);
  color: var(--ink-soft);
}

.nav__links a {
  position: relative;
  transition: color 0.3s var(--ease-soft);
}

.nav__links a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -6px;
  height: 1px;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.4s var(--ease-out);
}

.nav__links a:hover {
  color: var(--ink);
}

.nav__links a:hover::after {
  transform: scaleX(1);
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.7em 1.35em;
  border-radius: 10px;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  transition: transform 0.35s var(--ease-out), box-shadow 0.35s var(--ease-out), background 0.3s;
}

.btn--light {
  background: var(--ink);
  color: #16201f;
  box-shadow: 0 8px 24px -12px rgba(0, 0, 0, 0.6);
}

.btn--light:hover {
  transform: translateY(-2px);
  background: #fff;
  box-shadow: 0 14px 30px -12px rgba(0, 0, 0, 0.7);
}

.btn--block {
  width: 100%;
  border-radius: 12px;
  padding: 0.95em 1.2em;
}

/* ---------- Headline (typewriter) ---------- */

.headline {
  grid-area: head;
  align-self: start;
  margin: 0;
  display: flex;
  flex-direction: column;
  font-family: var(--serif);
  font-weight: 300;
  font-variation-settings: "SOFT" 60, "WONK" 1;
  font-size: clamp(3rem, 8.4vw, 7.6rem);
  line-height: 0.98;
  letter-spacing: -0.015em;
  text-shadow: 0 2px 30px rgba(0, 0, 0, 0.25);
}

.headline__line {
  display: block;
}

.headline__line--muted {
  color: var(--ink-mute);
  font-style: italic;
  font-variation-settings: "SOFT" 100, "WONK" 1;
}

/* The typed line's text content is driven by main.js (intro word, then
   cycling role words). The markup already contains static fallback text
   so the headline reads correctly with JavaScript disabled. When JS is
   running, keep it hidden until main.js reveals it right as the
   typewriter sequence begins — otherwise the fallback text flashes on
   screen at the same time as the intro name. */
.headline__type {
  position: relative;
}

/* Zero‑width space keeps the line box alive while the word is backspaced
   away, so "Architecture" never shifts up between words. */
.headline__type::before {
  content: "\200B";
}

.js-enabled .headline__type {
  opacity: 0;
}

.js-enabled .headline__type.is-visible {
  opacity: 1;
}

.js-enabled .headline__type.is-caret::after {
  content: "";
  display: inline-block;
  width: 0.05em;
  height: 0.82em;
  margin-left: 0.09em;
  background: currentColor;
  vertical-align: -0.04em;
  animation: caret-blink 0.9s steps(1) infinite;
}

@keyframes caret-blink {
  50% { opacity: 0; }
}

/* "Architecture" stays static in markup, but once JS takes over it starts
   hidden and flickers in the moment "Thoughtful" begins typing. */
.js-enabled .headline__line--architecture {
  opacity: 0;
}

.js-enabled .headline__line--architecture.is-visible {
  animation: flicker 1.1s steps(1, end) both;
}

@keyframes flicker {
  0%   { opacity: 0; }
  8%   { opacity: 0.55; }
  12%  { opacity: 0; }
  20%  { opacity: 0.85; }
  26%  { opacity: 0.25; }
  32%  { opacity: 1; }
  40%  { opacity: 0.6; }
  46%  { opacity: 1; }
  58%  { opacity: 0.85; }
  64%  { opacity: 1; }
  100% { opacity: 1; }
}

/* ---------- Lede / status ---------- */

.foot {
  grid-area: foot;
  align-self: end;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 32px;
  padding-right: clamp(0px, 4vw, 48px);
}

.lede {
  max-width: 32ch;
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

.status {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 0.85rem;
  color: var(--ink-soft);
  white-space: nowrap;
}

.status strong {
  display: block;
  font-family: var(--serif);
  font-weight: 500;
  font-size: 1.15rem;
  color: var(--ink);
  letter-spacing: 0.01em;
}

.status__dot {
  position: relative;
  flex: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--moss);
}

/* The ring is a scaled pseudo-element rather than an animated box-shadow:
   box-shadow isn't composited, so animating it repainted the hero every
   frame, which re-rasterised the card's backdrop blur and made the glass
   shimmer. transform/opacity stay on the compositor. */
.status__dot::after {
  content: "";
  position: absolute;
  inset: -4px;
  z-index: -1;
  border-radius: 50%;
  background: rgba(183, 196, 160, 0.18);
  animation: pulse 2.4s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { transform: scale(1);   opacity: 1; }
  50%      { transform: scale(1.5); opacity: 0.3; }
}

/* ---------- Frosted glass card ---------- */

.glass {
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(22px) saturate(1.2);
  backdrop-filter: blur(22px) saturate(1.2);
  border: 1px solid var(--glass-line);
  border-radius: 22px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.12),
    0 30px 60px -30px rgba(0, 0, 0, 0.7);
}

.card {
  grid-area: card;
  align-self: end;
  justify-self: end;
  width: 100%;
  padding: 26px 24px 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.card__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}

.card__title {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 1.45rem;
  line-height: 1.15;
  letter-spacing: -0.01em;
}

.card__icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--glass-line);
  color: var(--ink-soft);
  transition: background 0.3s, color 0.3s;
}

.card__icon svg {
  width: 14px;
  height: 14px;
}

.card__icon:hover {
  background: rgba(255, 255, 255, 0.1);
  color: var(--ink);
}

.card__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.chip {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--glass-line);
  font-size: 0.82rem;
  font-weight: 500;
}

.chip svg {
  width: 14px;
  height: 14px;
  color: var(--ink-soft);
}

.card__meta {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.18);
  border: 1px solid var(--glass-line);
}

.card__meta > div {
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 0.82rem;
  font-weight: 500;
}

.card__meta > div + div {
  border-left: 1px solid var(--glass-line);
}

.card__meta small {
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.card__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 4px;
}

.card__price {
  margin: 0;
  font-family: var(--serif);
  font-size: 1.5rem;
  font-weight: 400;
  letter-spacing: -0.01em;
}

.card__price em {
  font-style: italic;
  color: var(--moss);
  font-variation-settings: "SOFT" 100, "WONK" 1;
}

.card__note {
  font-size: 0.75rem;
  color: var(--ink-mute);
}

/* ---------- Reveal choreography ---------- */

.reveal {
  opacity: 0;
}

.is-ready .reveal--nav {
  animation: rise 1s var(--ease-out) var(--t-ui) both;
}

.is-ready .reveal--lede {
  animation: rise 1.1s var(--ease-out) calc(var(--t-ui) + 0.15s) both;
}

.is-ready .reveal--status {
  animation: rise 1.1s var(--ease-out) calc(var(--t-ui) + 0.3s) both;
}

/* Frosted glass sliding up into frame */
.is-ready .reveal--card {
  animation: glass-up 1.2s var(--ease-out) var(--t-card) both;
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes glass-up {
  0% {
    opacity: 0;
    transform: translateY(72px);
  }
  45% {
    opacity: 1;
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ---------- Scroll cue ---------- */

.scroll-cue {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: 14px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-mute);
  transition: color 0.35s var(--ease-soft);
}

.scroll-cue svg {
  width: 9px;
  height: 15px;
  animation: cue-drift 2.6s var(--ease-soft) infinite;
}

.scroll-cue:hover {
  color: var(--ink);
}

.is-ready .reveal--cue {
  animation: cue-in 1.2s var(--ease-out) calc(var(--t-card) + 0.4s) both;
}

@keyframes cue-in {
  from {
    opacity: 0;
    transform: translate(-50%, 10px);
  }
  to {
    opacity: 1;
    transform: translate(-50%, 0);
  }
}

@keyframes cue-drift {
  0%, 100% { transform: translateY(-2px); opacity: 0.5; }
  50%      { transform: translateY(2px);  opacity: 1; }
}

/* ---------- Ridge divider ---------- */

/* The hero doesn't end on a rule — it ends on a skyline. Three ranges with
   aerial perspective (far = lighter and hazier) give depth, and the near
   range is filled with the page colour so the topographic section below
   reads as the ground the peaks sit on. Sits at z-index -1 (just above the
   veil) so hero content stays clear of it. */
.ridge {
  position: absolute;
  z-index: -1;
  left: 0;
  right: 0;
  bottom: -1px;
  height: var(--ridge-h);
  pointer-events: none;
}

.ridge__svg {
  display: block;
  width: 100%;
  height: 100%;
}

.ridge__layer {
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.ridge__layer--far {
  fill: #2a3436;
  fill-opacity: 0.5;
  stroke: rgba(244, 241, 234, 0.16);
  stroke-width: 0.9;
}

.ridge__layer--mid {
  fill: #1a2426;
  fill-opacity: 0.88;
  stroke: rgba(244, 241, 234, 0.28);
  stroke-width: 1;
}

.ridge__layer--near {
  fill: #0e1416;
  stroke: var(--frame);
  stroke-width: 1.2;
  stroke-opacity: 0.7;
}

/* Same tile and horizontal origin as the section below; the vertical offset
   equals the ridge height, which puts the tile grid's origin exactly on the
   section's top edge so contour lines run unbroken across the seam. */
.ridge__topo {
  position: absolute;
  inset: 0;
  background: url("/assets/topo.svg") repeat 50% var(--ridge-h) / var(--topo-tile);
  opacity: 0.55;
  clip-path: url(#ridgeNearClip);
}

.ridge__defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.is-ready .ridge {
  animation: ridge-in 1.6s var(--ease-out) calc(var(--t-frame) + 0.5s) both;
}

@keyframes ridge-in {
  from {
    opacity: 0;
    transform: translateY(34px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ---------- Scroll reveal ---------- */

/* Sections below the fold settle in as they enter the viewport, echoing the
   hero's `rise`. `--d` staggers siblings. Only applied when JS can flip
   `.is-inview`, so the content stays visible without JavaScript. */
.js-enabled [data-reveal] {
  opacity: 0;
  transform: translateY(28px);
  filter: blur(7px);
  transition:
    opacity 1.05s var(--ease-out) var(--d, 0s),
    transform 1.15s var(--ease-out) var(--d, 0s),
    filter 1.05s var(--ease-out) var(--d, 0s);
}

.js-enabled [data-reveal].is-inview {
  opacity: 1;
  transform: none;
  filter: blur(0);
}

/* Directional variants — paired elements enter from opposite edges. */
.js-enabled [data-reveal="left"] {
  transform: translate3d(-56px, 14px, 0);
}

.js-enabled [data-reveal="right"] {
  transform: translate3d(56px, 14px, 0);
}

/* ---------- Sections ---------- */

.section {
  position: relative;
  isolation: isolate;
  padding: clamp(96px, 14vh, 180px) 0 clamp(80px, 12vh, 150px);
}

/* Seamless contour tile, continuing straight out of the ridge above and
   fading out before the section ends so it never fights the text. */
.section--topo::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: url("/assets/topo.svg") repeat 50% 0 / var(--topo-tile);
  opacity: 0.55;
  -webkit-mask-image: linear-gradient(180deg, #000 0 68%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 0 68%, transparent 100%);
}

.section__inner {
  position: relative;
  width: min(100%, 1180px);
  margin: 0 auto;
  padding: 0 clamp(28px, 8vw, 128px);
}

.section__head {
  max-width: 44ch;
  margin-bottom: clamp(48px, 7vh, 88px);
}

.eyebrow {
  margin: 0 0 18px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--moss);
}

.section__title {
  margin: 0;
  font-family: var(--serif);
  font-weight: 300;
  font-variation-settings: "SOFT" 60, "WONK" 1;
  font-size: clamp(2.2rem, 5.4vw, 4.2rem);
  line-height: 1.02;
  letter-spacing: -0.015em;
}

.section__title em {
  font-style: italic;
  color: var(--ink-mute);
  font-variation-settings: "SOFT" 100, "WONK" 1;
}

.section__lede {
  max-width: 46ch;
  margin: 22px 0 0;
  font-size: 0.95rem;
  line-height: 1.65;
  color: var(--ink-soft);
}

/* ---------- Education timeline ---------- */

.timeline {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0 0 0 clamp(28px, 4vw, 52px);
  display: flex;
  flex-direction: column;
  gap: clamp(24px, 4vh, 44px);
}

/* Rail draws downward once the list enters view, echoing the hero frame. */
.timeline::before {
  content: "";
  position: absolute;
  left: 0;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: linear-gradient(180deg, var(--frame), rgba(244, 241, 234, 0.08));
  transform: scaleY(0);
  transform-origin: top;
  transition: transform 1.6s var(--ease-out);
}

.js-enabled .timeline:has(.timeline__item.is-inview)::before {
  transform: scaleY(1);
}

.timeline__item {
  position: relative;
}

.timeline__marker {
  position: absolute;
  top: 34px;
  left: calc(clamp(28px, 4vw, 52px) * -1);
  width: 9px;
  height: 9px;
  margin-left: -4.5px;
  border-radius: 50%;
  background: var(--moss);
  box-shadow: 0 0 0 4px rgba(183, 196, 160, 0.14);
}

.timeline__marker::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 9px;
  width: calc(clamp(28px, 4vw, 52px) - 13px);
  height: 1px;
  background: var(--frame);
}

.edu {
  padding: clamp(22px, 2.6vw, 30px) clamp(22px, 2.8vw, 34px);
  transition: transform 0.5s var(--ease-out), border-color 0.5s var(--ease-soft);
}

.edu:hover {
  transform: translateY(-4px);
  border-color: rgba(244, 241, 234, 0.3);
}

.edu__years {
  margin: 0 0 10px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.edu__school {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.45rem, 2.6vw, 2rem);
  line-height: 1.12;
  letter-spacing: -0.01em;
}

.edu__degree {
  margin: 6px 0 0;
  font-size: 0.92rem;
  font-weight: 500;
  color: var(--moss);
}

.edu__honors {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 0 0;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

.edu__honors::before {
  content: "";
  width: 5px;
  height: 5px;
  flex: none;
  border-radius: 50%;
  background: var(--moss);
  box-shadow: 0 0 0 3px rgba(183, 196, 160, 0.14);
}

.edu__note {
  max-width: 58ch;
  margin: 14px 0 0;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--ink-soft);
}

.edu__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  list-style: none;
  margin: 18px 0 0;
  padding: 0;
}

.edu__tags li {
  padding: 6px 11px;
  border-radius: 999px;
  border: 1px solid var(--glass-line);
  background: rgba(255, 255, 255, 0.05);
  font-size: 0.74rem;
  font-weight: 500;
  color: var(--ink-soft);
}

/* ---------- Experience ---------- */

/* ---------- Experience (inverted map sheet) ---------- */

/* This section flips to light: a printed trail map dropped into the middle of
   a dark page. The palette variables are redefined locally, so every child
   component re-themes itself without needing its own overrides. */
.section--map {
  --ink: #1c2a20;
  --ink-soft: rgba(28, 42, 32, 0.78);
  --ink-mute: rgba(28, 42, 32, 0.56);
  --moss: #3d5c2c;
  --frame: rgba(28, 42, 32, 0.4);
  --glass-bg: rgba(252, 251, 243, 0.58);
  --glass-line: rgba(28, 42, 32, 0.18);
  color: var(--ink);
  background: #c3d0ad;
  box-shadow:
    inset 0 14px 26px -18px rgba(10, 16, 12, 0.85),
    inset 0 -14px 26px -18px rgba(10, 16, 12, 0.85);
}

/* Real terrain: Castle Lake and Heart Lake, south-west of Mount Shasta.
   Rendered from OpenStreetMap data (ODbL) — one non-repeating sheet, so the
   eye never catches a tiling seam. */
.section--map::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: url("/assets/shasta.svg") no-repeat 50% 0 / cover;
  opacity: 0.66;
}

/* Paper grain, and a warm wash so the sheet isn't flat. */
.section--map::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(70% 50% at 24% 30%, rgba(255, 255, 255, 0.35), transparent 70%),
    radial-gradient(60% 45% at 82% 78%, rgba(154, 176, 128, 0.4), transparent 72%);
}

.section--map .glass {
  -webkit-backdrop-filter: blur(6px) saturate(1.05);
  backdrop-filter: blur(6px) saturate(1.05);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.65),
    0 20px 40px -26px rgba(22, 34, 24, 0.65);
}

.section--map .role__tags li {
  background: rgba(255, 255, 255, 0.55);
  border-color: rgba(28, 42, 32, 0.18);
}

.section--map .role__body:hover {
  border-color: rgba(28, 42, 32, 0.42);
}

.section--map .eyebrow,
.section--map .role__org {
  color: #3d5c2c;
}

/* Extra breathing room between cards so the trail's wide swings land in open
   map rather than disappearing behind a card. */
.section--map .roles {
  gap: clamp(96px, 13vh, 190px);
}

.map-credit {
  display: none;
}

.site-foot__credit {
  margin: 0;
  color: var(--ink-mute);
}

.site-foot__credit a {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.site-foot__credit a:hover {
  color: var(--ink);
}


.roles-wrap {
  position: relative;
}

.roles {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(40px, 6vh, 76px);
}

/* ---------- Experience trail ---------- */

.trail {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.trail__svg {
  display: block;
  overflow: hidden;
}

/* Faint full-length route, so the trail reads as a path not yet walked. */
.trail__ghost {
  stroke: rgba(28, 42, 32, 0.3);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-dasharray: 2 9;
}

.trail__line {
  stroke: #8f3d1f;
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(0 1px 2px rgba(255, 255, 255, 0.7));
  /* --dash and --len are written by main.js as the section scrolls. */
  stroke-dasharray: var(--len, 0);
  stroke-dashoffset: calc(var(--len, 0) - var(--dash, 0));
}

.trail__mark {
  fill: #fbfaf2;
  stroke: rgba(28, 42, 32, 0.5);
  stroke-width: 2.4;
  transition: stroke 0.45s var(--ease-soft), fill 0.45s var(--ease-soft);
}

.trail__mark.is-reached {
  fill: #8f3d1f;
  stroke: #fbfaf2;
}

.trail__compass {
  opacity: 0;
  transition: opacity 0.5s var(--ease-soft);
}

.trail__compass.is-live {
  opacity: 1;
}

.trail__compass-disc {
  fill: #fbfaf2;
  stroke: rgba(28, 42, 32, 0.55);
  stroke-width: 2;
}

.trail__compass-ring {
  stroke: #8f3d1f;
  stroke-width: 2;
}

.trail__needle {
  fill: rgba(28, 42, 32, 0.35);
}

.trail__needle-n {
  fill: #8f3d1f;
}

.role {
  --when-col: clamp(140px, 16vw, 200px);
  --role-gap: clamp(24px, 3.4vw, 48px);
  position: relative;
  display: grid;
  grid-template-columns: var(--when-col) minmax(0, 1fr);
  gap: var(--role-gap);
}

.role__when {
  position: sticky;
  top: 40px;
  align-self: start;
  padding-top: 4px;
}

.role__period {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.1rem, 1.8vw, 1.45rem);
  line-height: 1.15;
  letter-spacing: -0.01em;
}

.role__org {
  margin: 8px 0 0;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--moss);
}

.role__kind {
  margin: 6px 0 0;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.role__body {
  padding: clamp(24px, 2.8vw, 36px) clamp(24px, 3vw, 40px);
  transition: transform 0.5s var(--ease-out), border-color 0.5s var(--ease-soft);
}

.role__body:hover {
  transform: translateY(-4px);
  border-color: rgba(183, 196, 160, 0.42);
}

.role__title {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.35rem, 2.4vw, 1.9rem);
  line-height: 1.14;
  letter-spacing: -0.01em;
}

.role__summary {
  max-width: 62ch;
  margin: 14px 0 0;
  font-size: 0.92rem;
  line-height: 1.7;
  color: var(--ink-soft);
}

.role__points {
  list-style: none;
  margin: 20px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.role__points li {
  position: relative;
  max-width: 66ch;
  padding-left: 22px;
  font-size: 0.88rem;
  line-height: 1.65;
  color: var(--ink-soft);
}

.role__points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.66em;
  width: 10px;
  height: 1px;
  background: var(--moss);
}

.role__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  list-style: none;
  margin: 22px 0 0;
  padding: 0;
}

.role__tags li {
  padding: 6px 11px;
  border-radius: 999px;
  border: 1px solid var(--glass-line);
  background: rgba(255, 255, 255, 0.05);
  font-size: 0.74rem;
  font-weight: 500;
  color: var(--ink-soft);
}

/* ---------- Stack dashboard ---------- */

/* Terminal rather than graph paper: seamless tile of code lines, shell
   prompts and tree connectors, over faint CRT scanlines. */
.section--dash::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    url("/assets/code.svg"),
    repeating-linear-gradient(
      180deg,
      rgba(244, 241, 234, 0.028) 0 1px,
      transparent 1px 3px
    );
  background-size: clamp(520px, 46vw, 760px) auto, auto;
  background-repeat: repeat, repeat;
  opacity: 0.85;
  -webkit-mask-image: radial-gradient(125% 85% at 50% 40%, #000 32%, transparent 100%);
  mask-image: radial-gradient(125% 85% at 50% 40%, #000 32%, transparent 100%);
}

.section--dash::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image: linear-gradient(90deg, transparent, rgba(183, 196, 160, 0.09), transparent);
  background-size: 26% 100%;
  background-repeat: no-repeat;
  animation: track 21s linear infinite;
}

@keyframes track {
  from { background-position: -30% 0; }
  to   { background-position: 130% 0; }
}

/* The dashboard reads better edge to edge than boxed into the text column. */
.section--dash .section__inner {
  width: 100%;
}

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 128px), 1fr));
  gap: clamp(12px, 1.4vw, 18px);
  margin-bottom: clamp(24px, 3.4vh, 40px);
}

.stat {
  padding: clamp(18px, 2vw, 24px);
}

.stat__value {
  display: flex;
  align-items: baseline;
  gap: 4px;
  margin: 0;
  font-family: var(--serif);
  font-weight: 300;
  font-variation-settings: "SOFT" 60, "WONK" 1;
  font-size: clamp(1.9rem, 3.4vw, 2.7rem);
  line-height: 1;
  letter-spacing: -0.02em;
}

.stat__unit {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--moss);
}

.stat__label {
  margin: 12px 0 0;
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.panels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr));
  align-items: start;
  gap: clamp(14px, 1.8vw, 22px);
}

.panel {
  padding: clamp(22px, 2.4vw, 30px);
  transition: border-color 0.5s var(--ease-soft);
}

.panel:hover {
  border-color: rgba(244, 241, 234, 0.28);
}

.panel__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  padding-bottom: 14px;
  margin-bottom: 22px;
  border-bottom: 1px solid var(--glass-line);
}

.panel__title {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.1rem, 1.8vw, 1.35rem);
  letter-spacing: -0.01em;
}

.panel__meta {
  margin: 0;
  flex: none;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
}

.metrics {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 22px;
}

/* Levels live here rather than in inline style attributes — the site's CSP
   sets `style-src 'self'` with no 'unsafe-inline', which strips them. */
.metric--45 { --level: 45; }
.metric--50 { --level: 50; }
.metric--55 { --level: 55; }
.metric--60 { --level: 60; }
.metric--65 { --level: 65; }
.metric--70 { --level: 70; }
.metric--75 { --level: 75; }
.metric--80 { --level: 80; }
.metric--85 { --level: 85; }

.metric__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.metric__name {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.005em;
}

.metric__value {
  margin: 0;
  flex: none;
  font-size: 0.8rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--moss);
}

.metric__unit {
  font-size: 0.66rem;
  opacity: 0.6;
  margin-left: 1px;
}

.metric__bar {
  position: relative;
  height: 4px;
  margin-top: 9px;
  border-radius: 2px;
  background: rgba(244, 241, 234, 0.09);
  overflow: hidden;
}

/* Decile ticks, so the bar reads as a gauge rather than a progress bar. */
.metric__bar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    90deg,
    transparent 0 calc(10% - 1px),
    rgba(14, 20, 22, 0.85) calc(10% - 1px) 10%
  );
}

.metric__fill {
  display: block;
  height: 100%;
  width: calc(var(--level) * 1%);
  border-radius: 2px;
  background: linear-gradient(90deg, rgba(183, 196, 160, 0.45), var(--moss));
}

.metric__note {
  margin: 11px 0 0;
  font-size: 0.82rem;
  line-height: 1.62;
  color: var(--ink-mute);
}

/* Gauges sweep out to their value once the panel lands. */
.js-enabled [data-reveal] .metric__fill {
  width: 0;
  transition: width 1.25s var(--ease-out);
}

.js-enabled [data-reveal].is-inview .metric__fill {
  width: calc(var(--level) * 1%);
}

.metric:nth-child(1) .metric__fill { transition-delay: 0.2s; }
.metric:nth-child(2) .metric__fill { transition-delay: 0.31s; }
.metric:nth-child(3) .metric__fill { transition-delay: 0.42s; }
.metric:nth-child(4) .metric__fill { transition-delay: 0.53s; }
.metric:nth-child(5) .metric__fill { transition-delay: 0.64s; }
.metric:nth-child(6) .metric__fill { transition-delay: 0.75s; }

/* ---------- Standalone pages ---------- */

/* Pages without the hero carry their own top bar instead of the grid nav. */
.topbar {
  position: relative;
  z-index: 2;
  padding-top: clamp(26px, 4vh, 42px);
  font-size: 0.85rem;
  font-weight: 500;
}

.topbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 32px;
}

.nav__links a[aria-current="page"] {
  color: var(--ink);
}

.nav__links a[aria-current="page"]::after {
  transform: scaleX(1);
}

.section--page {
  padding-top: clamp(52px, 9vh, 110px);
  min-height: 72vh;
}

.projects {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr));
  align-items: start;
  gap: clamp(16px, 2vw, 26px);
}

.project__body {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: clamp(24px, 2.8vw, 34px);
  transition: transform 0.5s var(--ease-out), border-color 0.5s var(--ease-soft);
}

.project__body:hover {
  transform: translateY(-4px);
  border-color: rgba(244, 241, 234, 0.3);
}

.project__meta {
  margin: 0 0 12px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--moss);
}

.project__title {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(1.35rem, 2.4vw, 1.9rem);
  line-height: 1.14;
  letter-spacing: -0.01em;
}

.project__note {
  max-width: 52ch;
  margin: 14px 0 0;
  font-size: 0.9rem;
  line-height: 1.68;
  color: var(--ink-soft);
}

.project__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  list-style: none;
  margin: 20px 0 0;
  padding: 0;
}

.project__tags li {
  padding: 6px 11px;
  border-radius: 999px;
  border: 1px solid var(--glass-line);
  background: rgba(255, 255, 255, 0.05);
  font-size: 0.74rem;
  font-weight: 500;
  color: var(--ink-soft);
}

.project__link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: auto;
  padding-top: 22px;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  transition: color 0.3s var(--ease-soft);
}

.project__link svg {
  width: 13px;
  height: 13px;
}

.project__link:hover {
  color: var(--ink);
}

.project--empty {
  grid-column: 1 / -1;
  max-width: 620px;
}

.project--empty .btn {
  margin-top: 26px;
}

/* ---------- Site footer ---------- */

.site-foot {
  border-top: 1px solid rgba(244, 241, 234, 0.08);
  padding: 30px 0 34px;
  font-size: 0.78rem;
  color: var(--ink-mute);
}

.site-foot__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
}

.site-foot p {
  margin: 0;
}

.site-foot__top {
  transition: color 0.3s var(--ease-soft);
}

.site-foot__top:hover {
  color: var(--ink);
}

/* ---------- Responsive ---------- */

@media (max-width: 900px) {
  .hero {
    grid-template-columns: 1fr;
    grid-template-areas:
      "nav"
      "head"
      "foot"
      "card";
    padding: 80px 28px 56px;
  }

  .nav__links {
    display: none;
  }

  .headline {
    font-size: clamp(2.8rem, 13vw, 5rem);
  }

  .foot {
    flex-direction: column;
    align-items: flex-start;
    gap: 20px;
    padding-right: 0;
  }

  .card {
    justify-self: stretch;
    width: 100%;
    max-width: none;
  }

  .frame {
    inset: 14px;
  }

  .scroll-cue {
    display: none;
  }

  .topbar__inner {
    gap: 16px;
  }

  .role {
    grid-template-columns: minmax(0, 1fr);
    gap: 18px;
  }

  .trail {
    display: none;
  }

  .role__when {
    position: static;
    display: flex;
    align-items: baseline;
    gap: 14px;
    flex-wrap: wrap;
  }

  .role__org,
  .role__kind {
    margin: 0;
  }

  .js-enabled [data-reveal="left"],
  .js-enabled [data-reveal="right"] {
    transform: translate3d(0, 24px, 0);
  }

  .site-foot__inner {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }
}

/* ---------- Motion preferences ---------- */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .intro {
    display: none;
  }

  .is-ready .hero__photo,
  .is-ready .hero__veil,
  .is-ready .frame__line,
  .is-ready .reveal,
  .is-ready .ridge,
  .scroll-cue svg,
  .js-enabled .headline__line--architecture.is-visible {
    animation: none;
  }

  .hero__photo,
  .hero__veil,
  .headline__line,
  .ridge,
  .js-enabled .headline__line--architecture,
  .reveal {
    opacity: 1;
    transform: none;
  }

  .scroll-cue {
    transform: translateX(-50%);
  }

  .js-enabled [data-reveal] {
    opacity: 1;
    transform: none;
    filter: none;
    transition: none;
  }

  .timeline::before {
    transform: scaleY(1);
    transition: none;
  }

  .trail__line,
  .trail__mark,
  .trail__compass {
    transition: none;
  }

  .section--map::before {
    animation: none;
  }

  .section--dash::before {
    animation: none;
  }

  .js-enabled [data-reveal] .metric__fill {
    width: calc(var(--level) * 1%);
    transition: none;
  }

  .edu:hover,
  .role__body:hover,
  .project__body:hover {
    transform: none;
  }

  .js-enabled .headline__type.is-caret::after {
    display: none;
  }

  .frame__line {
    --draw: 1;
  }

  .status__dot::after {
    animation: none;
  }
}
