/* ==========================================================================
   Base — fonts, tokens, reset, typography, shared components
   ========================================================================== */

/* Variable font: one file covers weights 300–700. */
@font-face {
  font-family: "Overpass Mono";
  src: url("../fonts/OverpassMono-VariableFont_wght.ttf") format("truetype-variations");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

/* Static fallbacks for engines that ignore variable TTFs. */
@font-face {
  font-family: "Overpass Mono Static";
  src: url("../fonts/OverpassMono-Light.ttf") format("truetype");
  font-weight: 300;
  font-display: swap;
}
@font-face {
  font-family: "Overpass Mono Static";
  src: url("../fonts/OverpassMono-Regular.ttf") format("truetype");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Overpass Mono Static";
  src: url("../fonts/OverpassMono-Medium.ttf") format("truetype");
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: "Overpass Mono Static";
  src: url("../fonts/OverpassMono-SemiBold.ttf") format("truetype");
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Overpass Mono Static";
  src: url("../fonts/OverpassMono-Bold.ttf") format("truetype");
  font-weight: 700;
  font-display: swap;
}

@font-face {
  font-family: "Overpass";
  src: url("../fonts/Overpass-VariableFont_wght.ttf") format("truetype-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* Static fallbacks for engines that ignore variable TTFs. */
@font-face {
  font-family: "Overpass Static";
  src: url("../fonts/Overpass-Light.ttf") format("truetype");
  font-weight: 300;
  font-display: swap;
}
@font-face {
  font-family: "Overpass Static";
  src: url("../fonts/Overpass-Regular.ttf") format("truetype");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Overpass Static";
  src: url("../fonts/Overpass-Medium.ttf") format("truetype");
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: "Overpass Static";
  src: url("../fonts/Overpass-SemiBold.ttf") format("truetype");
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Overpass Static";
  src: url("../fonts/Overpass-Bold.ttf") format("truetype");
  font-weight: 700;
  font-display: swap;
}

/* Overpass Mono, digits only — first in the body stack so 0–9 always set in mono. */
@font-face {
  font-family: "Overpass Mono Digits";
  src: url("../fonts/OverpassMono-VariableFont_wght.ttf") format("truetype-variations");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0030-0039;
}
@font-face {
  font-family: "Overpass Mono Digits";
  src: url("../fonts/OverpassMono-Regular.ttf") format("truetype");
  font-weight: 400;
  font-display: swap;
  unicode-range: U+0030-0039;
}
@font-face {
  font-family: "Overpass Mono Digits";
  src: url("../fonts/OverpassMono-Medium.ttf") format("truetype");
  font-weight: 500;
  font-display: swap;
  unicode-range: U+0030-0039;
}

:root {
  --font-display: "Overpass Mono", "Overpass Mono Static", "Courier New", monospace;
  --font-body: "Overpass Mono Digits", "Overpass", "Overpass Static", "Helvetica Neue", Arial, sans-serif;
  --font: var(--font-display);

  --paper: #f4f4f4;
  --ink: #111111;
  --ink-70: rgba(17, 17, 17, 0.7);
  --ink-50: rgba(17, 17, 17, 0.5);
  --ink-30: rgba(17, 17, 17, 0.3);
  --line: rgba(17, 17, 17, 0.13);
  --wash: #f4f3f1;
  --accent: #e0432b;

  --edge: clamp(1.25rem, 3.6vw, 3.25rem);
  --bar: clamp(3.75rem, 6vw, 5.25rem);

  /* Proportional layout unit. The art-directed offsets on the project pages
     were composed against a 1512px-wide viewport, so --u resolves to exactly
     1px there and tracks the viewport elsewhere; write those offsets as
     calc(<design px> * var(--u)) and the compositions hold their proportions.
     Clamped so small windows keep the panels legible and wide screens stay
     sane. Phones drop the floor entirely — see the override below.
     Mirrored by Site.unit() in js/site.js; keep the two in step. */
  --u: clamp(0.7px, 100vw / 1512, 1.25px);

  --step--1: clamp(0.688rem, 0.66rem + 0.14vw, 0.78rem);
  --step-0: clamp(0.875rem, 0.85rem + 0.15vw, 1rem);
  --step-1: clamp(1.125rem, 1rem + 0.6vw, 1.5rem);
  --step-2: clamp(1.5rem, 1.2rem + 1.5vw, 2.5rem);
  --step-3: clamp(2.25rem, 1.5rem + 3.6vw, 4.5rem);
  --step-4: clamp(3rem, 1rem + 9vw, 9rem);

  /* Display type — fluid only (no transform: scale on a page wrapper). */
  --display-sm: clamp(1.35rem, 0.85rem + 2.2vw, 2.333rem); /* ~28pt */
  --display-md: clamp(1.5rem, 0.75rem + 3.5vw, 3rem); /* ~36pt */
  /* 6.35vw reaches the 6rem cap at the 1512px reference width, so the corner
     labels stay 96px there and scale below it. The previous ramp capped out at
     ~884px, which left them pinned at full size on narrow screens where they
     collided with the opening panel's copy. */
  --display-lg: clamp(2rem, 6.35vw, 6rem); /* ~72pt */
  --text-note: clamp(0.688rem, 0.65rem + 0.25vw, 0.917rem); /* ~11pt */

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-io: cubic-bezier(0.76, 0, 0.24, 1);

  /* Lateral Continuity — shared direction for text, media, and pages */
  --motion-x: 1rem;
  --motion-x-media: 1.5rem;
  --motion-page: 1.25rem;
  --motion-dur: 1s;
  --motion-dur-media: 1.2s;
  --motion-dur-page: 0.6s;
  --motion-stagger: 80ms;
  --motion-text-delay: 1s;
}

/* Phones: drop the 0.7 floor and scale the offsets in true proportion to the
   viewport. Held at the floor, an 840px gallery offset still measured ~590px on
   a 390px screen — one and a half screens of empty space to swipe past. Pure
   proportion reproduces the composition as designed, only smaller. */
@media (max-width: 767.98px) {
  :root {
    --u: calc(100vw / 1512);
  }
}

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

html {
  -webkit-text-size-adjust: 100%;
  cursor: url("../assets/cursor.svg?v=9") 25 25, auto;
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: 0.03em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  cursor: inherit;
}

a,
button,
[role="button"],
label,
summary,
.select,
.menu-toggle {
  cursor: url("../assets/cursor.svg?v=9") 25 25, pointer;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-display);
}

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

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

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
}

ul,
ol {
  list-style: none;
}

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

:focus-visible {
  outline: 1px solid var(--ink);
  outline-offset: 4px;
}

/* --- Typography helpers -------------------------------------------------- */

.label {
  font-family: var(--font-display);
  font-size: var(--step--1);
  font-weight: 400;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.display {
  font-family: var(--font-display);
  font-size: var(--step-4);
  font-weight: 400;
  line-height: 0.88;
  letter-spacing: -0.035em;
}

.heading {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.02em;
}

.lede {
  font-size: var(--step-1);
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.muted {
  color: var(--ink-50);
}

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

/* Underline that draws in on hover. */
.link {
  position: relative;
  display: inline-block;
  padding-bottom: 0.1em;
}

.link::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.5s var(--ease);
}

.link:hover::after,
.link:focus-visible::after {
  transform: scaleX(1);
}

.link--drawn::after {
  transform: scaleX(1);
}

.link--drawn:hover::after {
  transform-origin: right;
  transform: scaleX(0);
}

/* --- Site chrome -------------------------------------------------------- */

.masthead {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  height: var(--bar);
  padding: 0 var(--edge);
  mix-blend-mode: difference;
  color: #fff;
}

.masthead a,
.masthead button {
  color: inherit;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  line-height: 0;
}

.brand-mark {
  display: block;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  object-fit: cover;
}

/* Keep the gradient mark readable — difference blend would invert it. */
.masthead .wordmark {
  mix-blend-mode: normal;
}

/* Menu trigger: label + two rules that collapse into a cross when open. */
.menu-toggle {
  font-family: var(--font-display);
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  font-size: var(--step--1);
  font-weight: 400;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}

.menu-toggle__glyph {
  position: relative;
  width: 1.5rem;
  height: 0.5rem;
}

.menu-toggle__glyph i {
  position: absolute;
  left: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transition: transform 0.5s var(--ease-io);
}

.menu-toggle__glyph i:first-child {
  top: 0;
}
.menu-toggle__glyph i:last-child {
  bottom: 0;
}

.menu-toggle:hover .menu-toggle__glyph i:first-child {
  transform: translateX(0.25rem);
}
.menu-toggle:hover .menu-toggle__glyph i:last-child {
  transform: translateX(-0.25rem);
}

body.nav-open .menu-toggle__glyph i:first-child {
  transform: translateY(0.25rem) rotate(15deg);
}
body.nav-open .menu-toggle__glyph i:last-child {
  transform: translateY(-0.25rem) rotate(-15deg);
}

/* While the corner nav is open, park Close in the top centre
   so it does not sit on top of the Works button. */
body.nav-open .masthead {
  justify-content: center;
  pointer-events: none;
}

body.nav-open .menu-toggle {
  pointer-events: auto;
  z-index: 70;
}

/* --- Page load / exit veil ---------------------------------------------- */

/* Same-document and cross-document: keep the corner nav continuous when
   both pages share it (no disappear / reappear). */
@view-transition {
  navigation: auto;
}

::view-transition-group(site-nav) {
  animation-duration: 0s;
}

::view-transition-old(site-nav),
::view-transition-new(site-nav) {
  animation: none;
  mix-blend-mode: normal;
  height: 100%;
  overflow: clip;
}

.veil {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--paper);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--motion-dur-page) var(--ease);
  view-transition-name: none;
}

body.is-loading .veil,
body.is-leaving .veil {
  opacity: 1;
}

/* Page content shares the lateral language with reveals (veil = fade). */
main {
  transition: transform var(--motion-dur-page) var(--ease);
}

body.is-loading main {
  transform: translateX(var(--motion-page));
  transition: none;
}

body.is-loading.nav-back main {
  transform: translateX(calc(var(--motion-page) * -1));
}

body.is-leaving main {
  transform: translateX(calc(var(--motion-page) * -1));
}

body.is-leaving.nav-back main {
  transform: translateX(var(--motion-page));
}

/* Home opening: Quiet Fade — no lateral page shift on the hero. */
body.home.is-loading main,
body.home.is-leaving main,
body.home.is-loading.nav-back main,
body.home.is-leaving.nav-back main {
  transform: none;
}

/* --- Spline scenes ------------------------------------------------------- */

/* Paper fallback so the area reads as part of the page while the WebGL scene
   downloads and compiles, instead of flashing an empty white gap. */
.scene {
  background: var(--paper);
}

/* Give vertical panning and zoom back to the page. Without this the canvas
   swallows the gesture and the page cannot be scrolled by touching the scene.
   Taps and horizontal drags still reach the scene. */
.scene spline-viewer {
  touch-action: pan-y pinch-zoom;
}

/* Quiet loading tell — one pulsing dot in the scene's own moss tone. */
.scene__loader {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 1;
  width: 0.5rem;
  height: 0.5rem;
  margin: -0.25rem 0 0 -0.25rem;
  border-radius: 50%;
  background: var(--nav-pink, #5a6c68);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.6s var(--ease);
}

.scene.is-scene-loading .scene__loader {
  animation: scene-pulse 1.6s var(--ease) infinite;
}

.scene.is-scene-ready .scene__loader,
.scene.is-scene-failed .scene__loader {
  opacity: 0;
  animation: none;
}

@keyframes scene-pulse {
  0%,
  100% {
    opacity: 0.18;
    transform: scale(0.85);
  }
  50% {
    opacity: 0.55;
    transform: scale(1.15);
  }
}

@media (prefers-reduced-motion: reduce) {
  .scene.is-scene-loading .scene__loader {
    opacity: 0.35;
    animation: none;
  }
}

/* --- Footer -------------------------------------------------------------- */

.footer {
  display: flex;
  flex-direction: column;
  min-height: 100svh;
  padding: calc(var(--bar) + clamp(1.5rem, 5vh, 3rem)) var(--edge) clamp(1.5rem, 4vh, 2.5rem);
  border-top: 1px solid var(--ink);
}

/* The corner labels are fixed over the page, so the closing row lifts clear of
   the bottom pair (about / credits). Same treatment as .feature__body. */
body.has-corners .footer {
  padding-bottom: calc(var(--nav-inset, 2rem) + clamp(6.5rem, 14vh, 9rem));
}

.footer__intro {
  display: flex;
  flex: 1;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: clamp(1.25rem, 4vh, 2.5rem);
}

.footer__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
}

.footer__email {
  font-family: var(--font-display);
  font-size: clamp(2rem, 7vw, 6rem);
  font-weight: 400;
  letter-spacing: -0.04em;
  line-height: 0.95;
}

.footer__email:hover {
  font-style: italic;
}

.footer__row--meta {
  padding-top: clamp(1.5rem, 4vh, 2.5rem);
  border-top: 1px solid var(--line);
}

.footer__socials {
  font-family: var(--font-display);
  display: flex;
  font-weight: 400;
  gap: 1.5rem;
  font-size: var(--step--1);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

@media (max-width: 700px) {
  .footer__row--meta {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.75rem;
  }
}

/* --- Scroll reveal — Lateral Continuity ---------------------------------- */

.reveal {
  opacity: 0;
  transform: translateX(var(--motion-x));
  transition:
    opacity var(--motion-dur) var(--ease),
    transform var(--motion-dur) var(--ease);
}

.reveal.is-in {
  opacity: 1;
  transform: none;
}

/* Media enters a touch farther / longer — same direction */
.reveal--media,
.slide .reveal:not(.reveal--text) {
  transform: translateX(var(--motion-x-media));
  transition:
    opacity var(--motion-dur-media) var(--ease),
    transform var(--motion-dur-media) var(--ease);
}

.reveal--media.is-in,
.slide .reveal:not(.reveal--text).is-in {
  transform: none;
}

/* Text stays hidden until armed — beat project-specific opacity rules. */
.reveal.reveal--text {
  transform: translateX(var(--motion-x, 1rem));
  transition:
    opacity var(--motion-dur) var(--ease),
    transform var(--motion-dur) var(--ease);
}

.reveal.reveal--text:not(.is-in) {
  opacity: 0 !important;
  transform: translateX(var(--motion-x, 1rem)) !important;
}

.reveal.reveal--text.is-in {
  opacity: 1;
  transform: none;
}

/* Lens bridge title settles to its softer designed opacity after reveal. */
body.project--lens .slide--lens-bridge .slide__blurb-title.reveal.is-in {
  opacity: 0.5;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .reveal,
  .reveal--media,
  .reveal.reveal--text,
  .slide .reveal:not(.reveal--text):not(.is-in) {
    opacity: 1 !important;
    transform: none !important;
  }

  body.is-loading main,
  body.is-leaving main,
  body.is-loading.nav-back main,
  body.is-leaving.nav-back main,
  body.home.is-loading main,
  body.home.is-leaving main {
    transform: none;
  }
}
