@font-face {
  font-family: 'Lastoria';
  src: url('fonts/LastoriaBoldRegular.otf') format('opentype');
  font-weight: 700;
  font-display: swap;
}

@font-face {
  font-family: 'Chillax';
  src: url('fonts/Chillax_Complete/Fonts/WEB/fonts/Chillax-Regular.woff2') format('woff2'),
       url('fonts/Chillax_Complete/Fonts/WEB/fonts/Chillax-Regular.woff') format('woff');
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: 'Chillax';
  src: url('fonts/Chillax_Complete/Fonts/WEB/fonts/Chillax-Light.woff2') format('woff2'),
       url('fonts/Chillax_Complete/Fonts/WEB/fonts/Chillax-Light.woff') format('woff');
  font-weight: 300;
  font-display: swap;
}

@font-face {
  font-family: 'Chillax';
  src: url('fonts/Chillax_Complete/Fonts/WEB/fonts/Chillax-Medium.woff2') format('woff2'),
       url('fonts/Chillax_Complete/Fonts/WEB/fonts/Chillax-Medium.woff') format('woff');
  font-weight: 500;
  font-display: swap;
}

@font-face {
  font-family: 'Nohemi';
  src: url('fonts/Nohemi/Web-TT/Nohemi-Thin.woff2') format('woff2'),
       url('fonts/Nohemi/Web-TT/Nohemi-Thin.woff') format('woff');
  font-weight: 100;
  font-display: block;
}

@font-face {
  font-family: 'Origin';
  src: url('fonts/Origin/Web-TT/Origin-BlackDisplay.woff2') format('woff2'),
       url('fonts/Origin/Web-TT/Origin-BlackDisplay.woff') format('woff');
  font-weight: 900;
  font-display: block;
}

@property --p {
  syntax: '<percentage>';
  inherits: false;
  initial-value: -10%;
}

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

html, body { margin: 0; }

:root {
  --paper: #f4f1ea;
  --ink: #1a1f2b;
  --dur: 3400ms;
  --par: 0;

  /* Final sky frame: off-white border --m on the left, right and top; the
     bottom edge is hidden under the page. Sizes below are the 16:9 sky's
     "cover" width at the start (full screen) and in the final frame. */
  --m: 4vw;
  --sw0: max(100vw, calc(100vh * 16 / 9));
  --sw1: max(calc(100vw - 2 * var(--m)), calc((100vh - var(--m)) * 16 / 9));
  --sh1: calc(var(--sw1) * 9 / 16);

  /* Planets, placed relative to the final frame */
  --pd: calc(var(--sw1) * 0.26);
  --px: calc(var(--sw1) * 0.18);
  --py: calc(50vh + var(--m) / 2 + var(--sh1) * 0.1);
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: system-ui, sans-serif;
}

/* Lenis */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

/* Layers: hero (auto) < page (1) < planets (2) < title + note (3) */
.hero {
  position: relative;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
}

/* The frame starts full-bleed and shrinks linearly, so the off-white border
   grows around the sky while the images shuffle. */
.frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: #0b1220;
  transition: top var(--dur) linear, left var(--dur) linear, right var(--dur) linear;
}
.flipping .frame {
  top: var(--m);
  left: var(--m);
  right: var(--m);
}

/* Zoom-out: the image's width eases from full-bleed to the framed size.
   It is 12% oversized so the scroll parallax never exposes an edge. */
.bg {
  position: absolute;
  left: 50%;
  top: 50%;
  width: calc(var(--sw0) * 1.12);
  max-width: none;
  height: auto;
  transform: translate(-50%, calc(-50% + var(--par) * 1px));
  transition: width var(--dur) linear;
}
.flipping .bg {
  width: calc(var(--sw1) * 1.12);
}

/* One big word per flash frame, inverted against the image underneath. */
.flash {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  font-family: 'Origin', system-ui, sans-serif;
  font-weight: 900;
  font-size: var(--fs, 24vw);
  line-height: 1;
  white-space: nowrap;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  color: #fff;
  mix-blend-mode: difference;
  pointer-events: none;
}
.flash.on {
  display: flex;
}
/* Second copy added on top of the inverted one, to lift the negative's brightness. */
.flash.glow {
  color: #777;
  mix-blend-mode: plus-lighter;
}

/* Heading: two offset words. "project" (script, larger) sits up and to the
   left and overlaps the top of "deimos" (Origin) slightly. "deimos" is below
   the planets (z-index 1 < .planets 2) so the right planet covers part of it. */
.title {
  position: absolute;
  inset: 0;
  margin: 0;
  pointer-events: none;
  --pref: clamp(20px, 2.88vw, 50px);
  --pfs: calc(var(--pref) * var(--pk, 1.95));
  --dfs: clamp(84px, 20vw, 320px);
  /* top edge of the right-hand planet */
  --ptop: calc(var(--py) - var(--pd) * 0.4875);
  /* visible glyphs end ~0.62em into the line box; sink them ~0.35em under the planet's top */
  --dtop: calc(var(--ptop) - var(--dfs) * 0.3);
  color: var(--paper);
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
}

.title span,
.deimos-inv .t-deimos {
  position: absolute;
  display: block;
  /* padding keeps swashes inside the reveal mask */
  padding: 0.6em 0.6em 1.4em;
  /* handwriting reveal: a soft slanted wipe driven by --p */
  --p: -10%;
  -webkit-mask-image: linear-gradient(100deg, #000 calc(var(--p) - 6%), transparent var(--p));
  mask-image: linear-gradient(100deg, #000 calc(var(--p) - 6%), transparent var(--p));
}

.t-project {
  z-index: 3;
  font-family: 'Chillax', system-ui, sans-serif;
  font-weight: 300;
  font-size: var(--pfs);
  letter-spacing: 0.14em;
  /* top-left corner is fixed (--pref sets where); a larger --pfs grows it down and right */
  left: calc(12vw - 0.6em);
  top: calc(var(--dtop) + var(--dfs) * 0.1 - var(--pref) * 1.2 - 0.6em);
}

.t-deimos {
  z-index: 1;
  font-family: 'Nohemi', system-ui, sans-serif;
  font-weight: 100;
  font-size: var(--dfs);
  letter-spacing: -0.02em;
  left: calc(50vw + var(--px) * 0.4);
  transform: translateX(-50%);
  top: calc(var(--dtop) - 0.6em);
}

/* "deimos" scrolls with the hero, as do the planets, so they stay registered. A second copy
   sits above the planets, masked to the right planet's shape, and inverts
   whatever is under it: the colour-negative only shows where they overlap. */
.deimos-inv {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  mix-blend-mode: difference;
  color: #fff;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  --pref: clamp(20px, 2.88vw, 50px);
  --pfs: calc(var(--pref) * var(--pk, 1.95));
  --dfs: clamp(84px, 20vw, 320px);
  --ptop: calc(var(--py) - var(--pd) * 0.4875);
  --dtop: calc(var(--ptop) - var(--dfs) * 0.3);
  --pw: calc(var(--pd) * 0.975);
  -webkit-mask-image: url('7920.webp');
  mask-image: url('7920.webp');
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: var(--pw) var(--pw);
  mask-size: var(--pw) var(--pw);
  -webkit-mask-position: calc(50vw + var(--px) - var(--pw) / 2) calc(var(--py) - var(--pw) / 2);
  mask-position: calc(50vw + var(--px) - var(--pw) / 2) calc(var(--py) - var(--pw) / 2);
}
.deimos-inv .t-deimos {
  position: absolute;
  z-index: auto;
  color: #fff;
}

/* Phones: the 16:9 sky is cropped to a narrow slice and --px (a share of its
   width) is far wider than the screen, so "deimos" would sit off the right
   edge. Centre it on the frame instead; the planets stay where they are. */
@media (max-width: 899px) and (orientation: portrait) {
  .title, .deimos-inv { --dfs: clamp(84px, 22vw, 150px); }
  .t-deimos { left: 50vw; }
  /* a little higher, so its descenders clear the tops of the d and e */
  .t-project { top: calc(var(--dtop) + var(--dfs) * 0.1 - var(--pref) * 1.2 - 0.9em); }
}

/* Subheading: centred between and over the planets, its top on the line where
   their bottom third begins. */
.sub {
  position: absolute;
  z-index: 3;
  top: calc(var(--py) + var(--pd) / 6);
  left: 0;
  right: 0;
  margin: 0;
  text-align: center;
  font-family: 'Chillax', system-ui, sans-serif;
  font-weight: 400;
  font-size: clamp(16px, 2vw, 30px);
  line-height: 1;
  letter-spacing: 0.08em;
  color: var(--paper);
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.2s ease;
}
.settled .sub { opacity: 0.9; }

/* Quick write-on: "project", then "deimos" starting before it finishes. */
.titled .t-project {
  animation: write 650ms cubic-bezier(0.3, 0, 0.3, 1) forwards;
}
.titled .t-deimos {
  animation: write 650ms cubic-bezier(0.3, 0, 0.3, 1) 450ms forwards;
}
@keyframes write {
  from { --p: -10%; }
  to   { --p: 112%; }
}


/* The page itself slides up over the hero during the shuffle, linearly, until
   its top edge covers the lower third of the planets' height. Being real
   content, it then scrolls with the user. */
.page {
  position: relative;
  z-index: 1;
  min-height: 200vh;
  /* flow-root stops the divider's 70vh top margin collapsing through the page,
     which was shoving the page's top edge (the bottom border) off screen. */
  display: flow-root;
  background: var(--paper);
  margin-top: 0;
  transition: margin-top var(--dur) linear;
}
.flipping .page {
  margin-top: calc(-1 * (100vh - (var(--py) + var(--pd) * 0.62)));
}

/* Section divider: asymmetric, bleeding off the right edge. */
.divider {
  display: block;
  width: 64vw;
  height: auto;
  aspect-ratio: 1320 / 300;
  margin: 9vh 0 0 auto;
  object-fit: cover;
}

.page { overflow-x: clip; }

/* Planets are anchored to the hero (they scroll away with it) so the heading
   overlap stays registered. */
.planets {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100vh;
  height: 100svh;
  z-index: 2;
  pointer-events: none;
  /* The right planet is wider than a phone screen; without this the page scrolls sideways. */
  overflow-x: clip;
}

.planet {
  position: absolute;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  opacity: 0;
  transition: opacity 1.6s ease;
}

.p1 {
  width: var(--pd);
  left: calc(50vw - var(--px));
  top: var(--py);
}

/* p2 is scaled slightly so both drawn circles have equal diameter. */
.p2 {
  width: calc(var(--pd) * 0.975);
  left: calc(50vw + var(--px));
  top: var(--py);
}

.revealing .planet { opacity: 1; }

/* Shader layers replace the plain images while the blur-fade runs. */
.planet.gl {
  opacity: 1;
  transition: none;
}
.sky-gl {
  visibility: hidden;
}


/* Loading screen: the monogram on paper until every hero asset is ready, then
   a cross-fade into the intro. */
html.loading { overflow: hidden; }
#loader {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  background: var(--paper);
  color: var(--ink);
  transition: opacity 800ms ease, visibility 0s linear 800ms;
}
#loader.done {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
.loader-mark {
  width: auto;
  height: clamp(88px, 16vh, 150px);
  opacity: 0;
  animation: mark-in 900ms ease 100ms forwards, mark-breathe 2200ms ease-in-out 1000ms infinite;
  transition: transform 800ms ease;
}
#loader.done .loader-mark { transform: scale(0.97); }
@keyframes mark-in {
  from { opacity: 0; filter: blur(8px); }
  to   { opacity: 0.9; filter: blur(0); }
}
@keyframes mark-breathe {
  0%, 100% { opacity: 0.9; }
  50%      { opacity: 0.45; }
}
@media (prefers-reduced-motion: reduce) {
  .loader-mark { animation: mark-in 400ms ease forwards; }
}

/* Inverted-colour overlay for the planets, shown over the last image before the
   sky. Above the planets and the page, like the heading's inverted copy, so
   the whole group inverts whatever is beneath it. */
.planets-inv {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  mix-blend-mode: difference;
}

/* Starfield over the sky: fades in once the title starts, drifts very slowly,
   and thins out toward the lighter horizon. */
.starfield {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 4s ease 600ms;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 35%, transparent 85%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 35%, transparent 85%);
}
.titled .starfield { opacity: 1; }
.stars {
  position: absolute;
  left: 0;
  border-radius: 50%;
  background: transparent;
  will-change: transform;
  animation: star-drift linear infinite;
}
@keyframes star-drift {
  to { transform: translateY(var(--tile)); }
}
@media (prefers-reduced-motion: reduce) {
  .stars { animation: none; }
}


/* Star chart revealed under the cursor (starmap.js). A canvas the size of the frame;
   the script draws only the faded patch under the pointer. */
.starmap {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  pointer-events: none;
}
