/* ============================================================
   MAIN
   1  Reset and base        7  About and spec list
   2  Type utilities        8  Figures
   3  Layout                9  Contact and footer
   4  Header               10  Project detail page
   5  Masthead             11  Motion
   6  Project index        12  Responsive
   ============================================================ */

/* --- 1  Reset and base -------------------------------------- */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  background-color: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--t-body);
  font-stretch: var(--w-text);
  font-weight: 400;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

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

a { color: inherit; text-decoration: none; }

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

::selection {
  background-color: var(--c-amber);
  color: var(--c-forest);
}

/* Skip link, visible only on focus */
.skip {
  position: absolute;
  left: var(--s-4);
  top: -100%;
  z-index: 100;
  padding: var(--s-3) var(--s-4);
  background-color: var(--c-amber);
  color: var(--c-forest);
  font-family: var(--font-mono);
  font-size: var(--t-meta);
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
}
.skip:focus { top: var(--s-4); }

/* --- 2  Type utilities -------------------------------------- */

.mono {
  font-family: var(--font-mono);
  font-size: var(--t-meta);
  font-weight: 500;
  letter-spacing: var(--track-mono);
  line-height: 1.4;
  text-transform: uppercase;
}

.micro {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-weight: 400;
  letter-spacing: var(--track-micro);
  line-height: 1.4;
  text-transform: uppercase;
}

.kicker {
  color: var(--ink-dim);
  margin-bottom: var(--s-5);
}

.h2 {
  font-size: var(--t-h2);
  font-stretch: var(--w-title);
  font-weight: 600;
  letter-spacing: var(--track-snug);
  line-height: 1.05;
}

.h3 {
  font-size: var(--t-h3);
  font-stretch: var(--w-subtitle);
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.15;
}

.lead {
  font-size: var(--t-lead);
  line-height: 1.55;
  max-width: 52ch;
}

.prose { max-width: var(--measure); }
.prose p + p { margin-top: var(--s-5); }

.dim { color: var(--ink-dim); }

/* Short accent rule. The one place the livery stripe is quoted,
   sized as an editorial rule rather than a graphic band. */
.rule-accent {
  width: 96px;
  height: 2px;
  background-color: var(--accent-graphic);
  border: 0;
}

/* Status pill, for work that is still open. Filled rather than
   outlined, using --on-accent for its text so contrast holds even
   though --accent itself flips from a deep, dark amber on paper to
   a pure, light amber on the forest ground. */
.tag {
  display: inline-block;
  padding: 0.3em 0.65em;
  background-color: var(--accent);
  color: var(--on-accent);
  line-height: 1.3;
  white-space: nowrap;
}

/* Inline text link. Rule wipes in from the left, like a plotter. */
.link {
  position: relative;
  color: inherit;
  transition: color var(--dur-fast) var(--ease);
}
.link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 1px;
  background-color: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
}
.link:hover { color: var(--accent); }
.link:hover::after { transform: scaleX(1); }

/* --- 3  Layout ---------------------------------------------- */

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

.section {
  padding-block: var(--section-y);
  scroll-margin-top: var(--header-h);
}

.band {
  background-color: var(--bg);
  color: var(--ink);
}

.hr {
  border: 0;
  border-top: 1px solid var(--rule);
}

/* --- 4  Header ---------------------------------------------- */

/* Forest green at all times, with the amber livery line on its
   baseline throughout: seamless on the masthead, a solid rail over
   the paper below. */
.header {
  position: sticky;
  top: 0;
  z-index: 50;
  background-color: var(--c-forest);
  color: var(--c-cream);
  border-bottom: 1px solid var(--c-amber);
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-5);
  height: var(--header-h);
}

.header__name {
  font-family: var(--font-mono);
  font-size: var(--t-meta);
  font-weight: 500;
  letter-spacing: var(--track-mono);
  text-transform: uppercase;
}

.nav {
  display: flex;
  gap: var(--s-5);
}

.nav__link {
  position: relative;
  padding-block: var(--s-2);
  color: var(--c-peach);
  transition: color var(--dur-fast) var(--ease);
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 2px;
  background-color: var(--c-amber);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
}
.nav__link:hover,
.nav__link.is-current { color: var(--c-cream); }
.nav__link:hover::after,
.nav__link.is-current::after { transform: scaleX(1); }

/* --- 5  Masthead -------------------------------------------- */

.masthead {
  position: relative;
  padding-top: var(--s-8);
  padding-bottom: var(--section-y);
}

/* .shell here becomes a positioned sibling so it stacks above the
   track without a z-index: it comes second in DOM order among
   positioned elements, so it paints on top regardless. */
.masthead > .shell { position: relative; }

.masthead__track {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.masthead__track svg {
  position: absolute;
  left: -1%;
  bottom: -6%;
  width: clamp(320px, 78vw, 980px);
  height: auto;
  opacity: 0.16;
}

/* Undertray project page only: sits in the empty space beside the
   title, not spanning the whole section the way .masthead__track
   does on the homepage. */
.masthead__chassis {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.masthead__chassis svg {
  position: absolute;
  right: 4%;
  top: 28%;
  width: clamp(304px, 40vw, 576px);
  height: auto;
  opacity: 1;
}

/* Same idea as .masthead__chassis, sized for a tall narrow subject
   (rocket airframe) instead of a wide one. */
.masthead__rocket {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.masthead__rocket svg {
  position: absolute;
  right: calc(10% + 142px);
  top: 14%;
  width: clamp(120px, 15vw, 220px);
  height: auto;
  opacity: 0.55;
}

/* Same idea again, this time for a wide side-view diagram instead of
   a tall or portrait one. Sits low in the band as a strip. */
.masthead__stage {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.masthead__stage svg {
  position: absolute;
  right: calc(2% + 76px);
  top: calc(2% + 303px);
  width: clamp(427px, 45.5vw, 739px);
  height: auto;
  opacity: 0.55;
  transform: translateY(5mm) rotate(45deg);
}

/* A small nudge in the corner of a masthead, hinting there's more
   below and linking straight to it. Sits after .shell in DOM order
   so it paints on top of the decorative track/chassis art without
   needing z-index, same trick as .masthead > .shell itself. */
.scroll-cue {
  position: absolute;
  right: var(--gutter);
  bottom: calc(var(--s-6) + 76px);
  display: flex;
  align-items: center;
  gap: var(--s-4);
  color: var(--ink-dim);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease);
}
.scroll-cue:hover { color: var(--accent); }
.scroll-cue .micro {
  font-size: 0.85rem;
  letter-spacing: var(--track-micro);
}
.scroll-cue__arrow {
  width: 20px;
  height: 32px;
  flex-shrink: 0;
  animation: scroll-cue-bounce 1.8s ease-in-out infinite;
}
@keyframes scroll-cue-bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(4px); }
}

/* Same idea as .masthead__chassis: sits beside the title, not
   spanning the whole section. */
.masthead__coilover {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.masthead__coilover svg {
  position: absolute;
  right: 6%;
  top: calc(8% + 113px);
  width: clamp(230px, 27.6vw, 437px);
  height: auto;
  opacity: 0.55;
}

/* Same idea again, tall portrait subject (pressure-vessel tank). */
.masthead__daf {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.masthead__daf svg {
  position: absolute;
  right: 7%;
  top: calc(6% + 96px);
  width: clamp(180px, 22.8vw, 336px);
  height: auto;
  opacity: 0.5;
  transform: translateX(-15mm);
}

/* Same idea again, tall nose-up subject (L1 rocket airframe drawing).
   Stands on the right of the band, clear of the title and lead. */
.masthead__l1 {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.masthead__l1 svg {
  position: absolute;
  right: 8%;
  top: 9%;
  width: clamp(66px, 8.5vw, 108px);
  height: auto;
  opacity: 0.55;
  transform: translate(-40mm, 20mm);
}

/* Document control line. One only, or it becomes cosplay. */
.docline {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  padding-block: var(--s-3);
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  color: var(--ink-dim);
}

/* Set in Space Grotesk rather than Archivo, scoped to this one
   element, so the name reads as a distinct, considered choice
   rather than another huge condensed-grotesque wordmark. */
.masthead__name {
  margin-top: var(--s-7);
  font-family: var(--font-name);
  font-size: var(--t-name);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.08;
}

.masthead__rule { margin-top: var(--s-6); }

.masthead__lead { margin-top: var(--s-6); }

/* Hero layout: name and lead on the left, headshot on the right.
   Stacks on narrow viewports, portrait falling below the copy. */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-8);
  align-items: start;
}

@media (min-width: 56rem) {
  .hero {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--s-9);
  }
}

.hero__portrait {
  width: 100%;
  max-width: 240px;
}

/* Spec strip. Enlarged so Discipline, Focus, Based, and Status carry
   real visual weight now that they are the masthead's only facts. */
.strip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--s-6) var(--s-6);
  margin-top: var(--s-9);
  padding-top: var(--s-5);
  border-top: 1px solid var(--rule);
}

.strip__key {
  display: block;
  color: var(--ink-dim);
  margin-bottom: var(--s-2);
}

/* Archivo, not mono: the label above stays mono/micro as a proper
   spec tag, but a value this large in monospace reads as a code
   block rather than a spec-sheet entry. This matches how the About
   section's spec__row already pairs a mono label with a sans value. */
.strip__val {
  font-family: var(--font-sans);
  font-stretch: var(--w-title);
  font-size: var(--t-strip-val);
  font-weight: 700;
  letter-spacing: var(--track-snug);
  line-height: 1.25;
}

.strip__val--accent { color: var(--accent); }

/* --- 6  Project index --------------------------------------- */

.index {
  border-top: 1px solid var(--rule-hi);
  margin-top: var(--s-8);
}

.project {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) minmax(0, auto);
  align-items: start;
  gap: var(--s-5) var(--s-7);
  padding-block: var(--s-7);
  border-bottom: 1px solid var(--rule);
  transition: background-color var(--dur) var(--ease);
}

/* Hover ground bleeds slightly past the text column. Kept
   narrower than --gutter so it never overflows the viewport. */
.project::before {
  content: "";
  position: absolute;
  inset: 0 calc(var(--bleed) * -1);
  background-color: var(--bg-raise);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.project:hover::before { opacity: 1; }

/* Lifts content above ::before without negative z-index, which
   an ancestor band's background would otherwise paint over. */
.project > * { position: relative; }

/* Accent rule overlays the hairline and wipes in from the left */
.project::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -1px;
  width: 100%;
  height: 1px;
  background-color: var(--accent-graphic);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
}
.project:hover::after { transform: scaleX(1); }

/* Numerals are drawn as outlines, which reads as a technical
   drawing, then fill solid on hover. */
.project__num {
  font-size: var(--t-numeral);
  font-stretch: var(--w-numeral);
  font-weight: 700;
  letter-spacing: var(--track-snug);
  line-height: 0.8;
  font-variant-numeric: tabular-nums;
  color: transparent;
  -webkit-text-stroke: 1.5px var(--ink-faint);
  transition: color var(--dur) var(--ease),
              -webkit-text-stroke-color var(--dur) var(--ease);
}
.project:hover .project__num {
  color: var(--accent-graphic);
  -webkit-text-stroke-color: var(--accent-graphic);
}

/* Solid fallback where text-stroke is unsupported */
@supports not (-webkit-text-stroke: 1px currentColor) {
  .project__num { color: var(--ink-faint); }
  .project:hover .project__num { color: var(--accent-graphic); }
}

.project__body { padding-top: var(--s-2); }

/* Team or organization, above the title. Cheap credibility. */
.project__org {
  display: block;
  margin-bottom: var(--s-3);
  color: var(--ink-dim);
}

.project__desc {
  margin-top: var(--s-4);
  max-width: 58ch;
  color: var(--ink-dim);
}

/* Fields are separated by space rather than punctuation, the way a
   spec sheet does it. Needs the wider gap to read unambiguously. */
.project__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3) var(--s-5);
  margin-top: var(--s-5);
  color: var(--ink-dim);
}

.project__go {
  padding-top: var(--s-3);
  color: var(--ink-dim);
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease);
}
.project:hover .project__go { color: var(--accent); }

.index__foot {
  display: flex;
  justify-content: space-between;
  gap: var(--s-4);
  margin-top: var(--s-5);
  color: var(--ink-dim);
}

/* --- 7  About and spec list --------------------------------- */

.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-9) var(--s-8);
}

@media (min-width: 62rem) {
  .split { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

.spec { border-top: 1px solid var(--rule-hi); }

.spec__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-1) var(--s-5);
  padding-block: var(--s-4);
  border-bottom: 1px solid var(--rule);
}

@media (min-width: 40rem) {
  .spec__row {
    grid-template-columns: minmax(0, var(--label-col)) minmax(0, 1fr);
    align-items: baseline;
  }
}

.spec__row dt {
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-weight: 400;
  letter-spacing: var(--track-micro);
  line-height: 1.4;
  text-transform: uppercase;
  color: var(--ink-dim);
  padding-top: 0.35em;
}

.spec__row dd {
  font-size: 1rem;
  line-height: 1.5;
}

/* Stacks a secondary credential under a primary one within a single
   row, e.g. a prior degree under the current one, instead of a
   second dt/dd pair repeating the row label. */
.spec__line { display: block; }
.spec__line + .spec__line {
  margin-top: 0.35em;
  color: var(--ink-dim);
}

/* --- 8  Figures --------------------------------------------- */

.figure { display: block; }

.figure__box {
  position: relative;
  aspect-ratio: 4 / 3;
  background-color: var(--bg-raise);
  border: 1px solid var(--rule);
}

/* Static, not absolutely positioned, so the corner crop marks
   (below) keep painting above it rather than under it. */
.figure__box img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Corner crop marks, a drawing-frame convention */
.figure__box::before,
.figure__box::after {
  content: "";
  position: absolute;
  width: 14px;
  height: 14px;
  border-color: var(--rule-hi);
  border-style: solid;
  border-width: 0;
}
.figure__box::before {
  top: 8px; left: 8px;
  border-top-width: 1px;
  border-left-width: 1px;
}
.figure__box::after {
  bottom: 8px; right: 8px;
  border-bottom-width: 1px;
  border-right-width: 1px;
}

.figure__box--portrait { aspect-ratio: 4 / 5; }

/* Let the image set its own height so nothing is cropped: the full
   frame shows at the image's native aspect ratio. */
.figure__box--natural { aspect-ratio: auto; }
.figure__box--natural img {
  height: auto;
  object-fit: contain;
}

/* An image slot deliberately withheld, rather than one simply not
   filled in yet. The label sits inside the frame itself, at the
   exact point of absence, not just in the caption below it. */
.figure__box--withheld {
  display: flex;
  align-items: center;
  justify-content: center;
}
.figure__box--withheld span { color: var(--ink-dim); }

.figure figcaption {
  margin-top: var(--s-3);
  color: var(--ink-dim);
}

.figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: var(--s-6);
  align-items: start;
}

/* --- 9  Contact and footer ---------------------------------- */

/* #contact needs its own positioning context: it's a plain .section,
   not .masthead, so it doesn't inherit that rule. */
#contact { position: relative; }
#contact > .shell { position: relative; }

.contact__track {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
}
.contact__track svg {
  width: clamp(480px, 120vw, 1350px);
  height: auto;
  opacity: 0.16;
}

.contact__email {
  display: inline-block;
  margin-top: var(--s-6);
  font-size: clamp(1.4rem, 2.8vw, 2.2rem);
  font-stretch: var(--w-title);
  font-weight: 600;
  letter-spacing: var(--track-snug);
  line-height: 1.1;
  word-break: break-word;
  color: var(--accent);
}

.contact__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-4) var(--s-6);
  margin-top: var(--s-7);
  font-size: 1.125rem;
}

.footer {
  padding-block: var(--s-6);
  border-top: 1px solid var(--rule);
  color: var(--ink-dim);
}

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3) var(--s-5);
}

/* --- 10  Project detail page -------------------------------- */

.title-lg {
  font-size: clamp(2.5rem, 6vw, 5rem);
  font-stretch: var(--w-display);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  line-height: 0.98;
  text-transform: uppercase;
}

.block { margin-top: var(--s-9); scroll-margin-top: var(--header-h); }

.block__head {
  padding-bottom: var(--s-4);
  margin-bottom: var(--s-5);
  border-bottom: 1px solid var(--rule);
  color: var(--ink-dim);
}

.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4) var(--s-5);
  padding-top: var(--s-5);
  border-top: 1px solid var(--rule-hi);
}

/* --- 11  Motion --------------------------------------------- */

/* Hidden state applies only when JS is running, so the page is
   fully readable if main.js never loads. */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(8px);
  transition:
    opacity var(--dur-slow) var(--ease),
    transform var(--dur-slow) var(--ease);
  transition-delay: calc(var(--i, 0) * 40ms);
}

.js [data-reveal].is-in {
  opacity: 1;
  transform: none;
}

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

  .js [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }

  *, *::before, *::after {
    transition-duration: 1ms !important;
    animation-duration: 1ms !important;
  }
}

/* --- 12  Responsive ----------------------------------------- */

@media (max-width: 52rem) {
  .project {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-3);
  }
  .project__num { line-height: 1; }
  .project__go { padding-top: 0; }
}
