/* ── othertime ─────────────────────────────────────────────────────────── */

/* one variable file covers the whole weight range */
@font-face {
  font-family: 'Playfair Display';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/assets/fonts/playfair-display-latin.woff2') format('woff2');
  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;
}

:root {
  /* the reveal — long, unhurried, decelerating almost to a stop */
  --reveal-ease: cubic-bezier(.15, .78, .18, 1);
  --reveal-move: 3400ms;   /* scale + tilt settling */
  --reveal-light: 2600ms;  /* darkness lifting */
  --reveal-focus: 2000ms;  /* blur pulling into focus */

  /* how much of the dark film stays behind once we've arrived */
  --film: .30;

  /* the reveal's starting pose (see .bg-layer for the covering maths) */
  --bg-overscan: -4.5%;
  --bg-start-scale: .958;
  --bg-tilt: 1deg;

  --ink: 255, 248, 242;
}

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

html, body {
  height: 100%;
  background: #06101f;
  overflow: hidden;
  overscroll-behavior: none;
}

body {
  font-family: 'Playfair Display', Georgia, 'Times New Roman', serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* ── stage ─────────────────────────────────────────────────────────────── */

.stage {
  position: fixed;
  inset: 0;
  overflow: hidden;
  isolation: isolate;   /* keep blend modes inside the stage */
  z-index: 0;
}

/* the layer the mouse drifts, so it can't fight the reveal transform */
.drift {
  position: absolute;
  inset: 0;
  transform: translate3d(var(--dx, 0px), var(--dy, 0px), 0);
  will-change: transform;
}

/* Overscanned so the tilt + zoom-out can never let an edge into frame.
   A rect rotated by θ only still covers the viewport if it is scaled by at
   least cos θ + (w/h)·sin θ. At 1° that is 1.031 at 16:9 and 1.041 at 21:9;
   9% overscan against a 0.958 start scale gives 1.044 — clear of both.
   The overscan is also a permanent crop, which is why it is kept this tight:
   any more and the base of the doorway falls out of frame. Portrait is far
   cheaper geometrically (1.008), so it overscans less again. */
.bg-layer {
  position: absolute;
  inset: var(--bg-overscan);
  opacity: 0;
  transform: scale(var(--bg-start-scale)) rotate(var(--bg-tilt));
  filter: brightness(.30) saturate(.52) contrast(1.10) blur(9px);
  will-change: transform, filter, opacity;
  transition:
    transform var(--reveal-move) var(--reveal-ease),
    filter var(--reveal-focus) var(--reveal-ease),
    opacity 1700ms ease-out;
}

.is-revealed .bg-layer {
  opacity: 1;
  transform: scale(1) rotate(0deg);
  filter: brightness(1) saturate(1) contrast(1) blur(0px);
}

@media (orientation: portrait) {
  :root {
    --bg-overscan: -2.5%;
    --bg-start-scale: .975;
  }
}

.bg {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* sit the frame on the image's bottom edge — the doorway's base and the
     walkway it stands on are the last thing in the source, and they should be
     the thing that survives the crop */
  object-position: center 100%;
  display: block;
}

/* ── the dark film ─────────────────────────────────────────────────────── */

.tint {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(130% 105% at 50% 58%,
      rgba(6, 16, 36, .06) 0%,
      rgba(4, 12, 30, .42) 55%,
      rgba(2, 8, 22, .86) 100%),
    linear-gradient(180deg, rgba(3, 10, 26, .56) 0%, rgba(3, 10, 26, .28) 24%, rgba(3, 10, 26, 0) 48%);
  opacity: 1;
  transition: opacity var(--reveal-light) var(--reveal-ease);
  pointer-events: none;
}
.is-revealed .tint { opacity: var(--film); }

.grain {
  position: absolute;
  inset: -8%;
  pointer-events: none;
  opacity: 0;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='260'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.92' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 230px 230px;
  animation: grain-drift 1.1s steps(1, end) infinite;
  transition: opacity 2600ms ease-out;
  will-change: transform;
}
.is-revealed .grain { opacity: .26; }

@keyframes grain-drift {
  0%   { transform: translate3d(0, 0, 0); }
  12%  { transform: translate3d(-3%, -2%, 0); }
  25%  { transform: translate3d(2%, -4%, 0); }
  37%  { transform: translate3d(-4%, 3%, 0); }
  50%  { transform: translate3d(3%, 2%, 0); }
  62%  { transform: translate3d(-2%, -3%, 0); }
  75%  { transform: translate3d(4%, 1%, 0); }
  87%  { transform: translate3d(-1%, 4%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

.vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(120% 92% at 50% 50%,
    rgba(0, 0, 0, 0) 46%,
    rgba(3, 8, 20, .30) 82%,
    rgba(2, 6, 16, .58) 100%);
  opacity: 0;
  transition: opacity 3000ms ease-out;
}
.is-revealed .vignette { opacity: 1; }

/* ── the light the cursor carries ──────────────────────────────────────── */

.glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0;
  transition: opacity 1100ms ease-out;
  contain: paint;
}
.is-revealed.has-pointer .glow { opacity: 1; }

.glow::before,
.glow::after {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  border-radius: 50%;
  transform: translate3d(var(--gx, -50vw), var(--gy, -50vh), 0) scale(var(--gs, 1));
  will-change: transform;
}

/* wide, soft wash — lifts the film over a big area */
.glow::before {
  width: 780px;
  height: 780px;
  margin: -390px 0 0 -390px;
  background: radial-gradient(circle closest-side,
    rgba(255, 236, 214, .17) 0%,
    rgba(255, 214, 226, .10) 34%,
    rgba(168, 208, 255, .045) 62%,
    rgba(120, 170, 255, 0) 78%);
  filter: blur(26px);
}

/* tighter warm core, so the effect has a centre you can feel */
.glow::after {
  width: 300px;
  height: 300px;
  margin: -150px 0 0 -150px;
  background: radial-gradient(circle closest-side,
    rgba(255, 244, 228, .20) 0%,
    rgba(255, 222, 232, .10) 44%,
    rgba(255, 220, 235, 0) 74%);
  filter: blur(14px);
}

/* ── wordmark ──────────────────────────────────────────────────────────── */

.masthead {
  position: fixed;
  top: clamp(22px, 3.6vh, 44px);
  left: 0;
  right: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(18px, 3.4vh, 40px);
  pointer-events: none;
  padding: 0 24px;
  padding-top: env(safe-area-inset-top, 0px);
  text-align: center;
}

.wordmark,
.tagline {
  font-family: 'Playfair Display', Georgia, serif;
  color: rgb(var(--ink));
  /* the scrim in .tint carries the contrast now, so the type needs only the
     faintest lift off the sky rather than a shadow of its own */
  text-shadow: 0 1px 18px rgba(8, 18, 42, .30);
  opacity: 0;
  transform: translate3d(0, 10px, 0);
  filter: blur(6px);
  transition:
    opacity 2000ms ease-out var(--in, 900ms),
    transform 2400ms var(--reveal-ease) var(--in, 900ms),
    filter 1800ms ease-out var(--in, 900ms);
}

.wordmark {
  font-weight: 600;
  font-size: clamp(20px, 1.6vw, 26px);
  line-height: 1;
  letter-spacing: .16em;
  text-indent: .16em;          /* re-centre against the trailing track */
}

/* the statement — the thing the page is actually saying, so it carries the
   weight. Set optically tight, the way a display serif wants to be. */
.tagline {
  --in: 1500ms;
  font-weight: 500;
  font-size: clamp(20px, 2.9vw, 44px);
  line-height: 1.16;
  letter-spacing: -.005em;
  max-width: 17em;
  text-wrap: balance;
}

.is-revealed .wordmark,
.is-revealed .tagline {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  filter: blur(0);
}

/* ── cursor ────────────────────────────────────────────────────────────── */

.cursor {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 50;
  pointer-events: none;
  opacity: 0;
  transition: opacity 700ms ease;
}
.has-pointer.is-revealed .cursor { opacity: 1; }

.cursor > * {
  position: absolute;
  left: 0;
  top: 0;
  border-radius: 50%;
}

/* soft iridescent bloom trailing furthest behind */
.cursor__halo {
  width: 86px;
  height: 86px;
  margin: -43px 0 0 -43px;
  transform: translate3d(var(--hx, -200px), var(--hy, -200px), 0) scale(var(--hs, 1));
  background: radial-gradient(circle closest-side,
    rgba(255, 232, 220, .16) 0%,
    rgba(255, 190, 205, .09) 46%,
    rgba(150, 200, 255, 0) 76%);
  filter: blur(9px);
  mix-blend-mode: screen;
  will-change: transform;
}

/* the wisp — no edge to it, just a soft body of light that lags behind the
   core and leans into the direction of travel */
.cursor__wisp {
  width: 30px;
  height: 30px;
  margin: -15px 0 0 -15px;
  transform:
    translate3d(var(--rx, -200px), var(--ry, -200px), 0)
    rotate(var(--rr, 0deg))
    scale(var(--rsx, 1), var(--rsy, 1));
  background: radial-gradient(circle closest-side,
    rgba(255, 250, 244, .58) 0%,
    rgba(255, 234, 226, .32) 42%,
    rgba(255, 216, 222, .11) 72%,
    rgba(212, 228, 255, 0) 100%);
  filter: blur(5px);
  mix-blend-mode: screen;
  will-change: transform;
}

/* the core — keeps closest to the true pointer */
.cursor__dot {
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  transform: translate3d(var(--dxp, -200px), var(--dyp, -200px), 0) scale(var(--ds, 1));
  background: radial-gradient(circle,
    rgba(255, 253, 250, .98) 0%,
    rgba(255, 247, 240, .86) 36%,
    rgba(255, 228, 218, .32) 66%,
    rgba(255, 212, 206, 0) 100%);
  box-shadow:
    0 0 10px rgba(255, 240, 228, .68),
    0 0 26px rgba(255, 218, 220, .32);
  will-change: transform;
}

@media (pointer: fine) {
  html.has-pointer,
  html.has-pointer body,
  html.has-pointer a,
  html.has-pointer button { cursor: none; }
}

/* ── reduced motion ────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  .bg-layer {
    transform: none;
    filter: brightness(.55) saturate(.8);
    transition: opacity 900ms ease-out, filter 900ms ease-out;
  }
  .is-revealed .bg-layer { transform: none; }
  .grain { animation: none; }
  .wordmark, .tagline {
    transform: none;
    filter: none;
    transition: opacity 900ms ease-out var(--in, 250ms);
  }
  .tagline { --in: 500ms; }
  .is-revealed .wordmark,
  .is-revealed .tagline { transform: none; }
  .cursor, .glow { transition: opacity 300ms linear; }
}
