/* R1 colours and font */
:root {
  --bg: #222124;
  --text: #f0f2f0;
  --muted: #8b8f8c;
}

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

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "Inter Tight", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-weight: 400;
  overflow-x: clip;
}

html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

h1, p { margin: 0; font-weight: 400; }
a { color: inherit; text-decoration: none; }

/* R2 type scale */
.t-s { font-size: 0.833333vw; line-height: 1.14583vw; }
.t-m { font-size: 1.04167vw; line-height: 1.30208vw; letter-spacing: 0.0260417vw; }
.t-l { font-size: 1.5625vw; line-height: 1.82292vw; }
.t-xl { font-size: 4.42708vw; line-height: 5.3125vw; }

@media (orientation: portrait) {
  .t-s { font-size: 1.62963vh; line-height: 2.24074vh; }
  .t-m { font-size: 2.03704vh; line-height: 2.5463vh; letter-spacing: 0.0509259vh; }
  .t-l { font-size: 3.05556vh; line-height: 3.56481vh; }
  .t-xl { font-size: 8.65741vh; line-height: 10.3889vh; }
}

/* R3 film grain */
.grain {
  position: fixed;
  width: 300%;
  height: 300%;
  top: -110%;
  left: -50%;
  opacity: 0.1;
  z-index: 10;
  pointer-events: none;
  background-image: url("../assets/noise.png");
  animation: grain 3s steps(5, end) infinite;
}

@keyframes grain {
  0%, 100% { transform: translate(0, 0); }
  10% { transform: translate(-5%, -10%); }
  20% { transform: translate(-15%, 5%); }
  30% { transform: translate(7%, -25%); }
  40% { transform: translate(-5%, 25%); }
  50% { transform: translate(-15%, 10%); }
  60% { transform: translate(15%, 0); }
  70% { transform: translate(0, 15%); }
  80% { transform: translate(3%, 35%); }
  90% { transform: translate(-10%, 10%); }
}

/* R4 layers; R12 canvas fade-in */
.particles {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  background: var(--bg);
  z-index: 0;
  opacity: 0;
  transition: opacity 1.2s ease-in-out;
}
.particles.is-ready { opacity: 1; }

.content { position: relative; z-index: 1; }

html.has-cursor, html.has-cursor * { cursor: none !important; }

/* R5 sections */
.section { position: relative; }
.hero, .statement, .globe, .map, .contact { height: 100svh; }
.helix { height: 210svh; }
.works { height: 220svh; }

/* R6 hero */
.hero { width: 90vw; margin: 0 auto; }
.hero-inner {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  height: 100%;
}
.hero-inner > :nth-child(2) { text-align: right; }

@media (orientation: portrait) {
  .hero-inner {
    flex-direction: column;
    justify-content: space-between;
    align-items: stretch;
    height: 100%;
    padding-block: 5vw;
  }
  .hero-inner > :first-child { text-align: left; align-self: flex-start; }
  .hero-inner > :last-child { text-align: right; align-self: flex-end; }
}

/* R7 character blur reveal */
.char {
  display: inline-block;
  white-space: pre;
  opacity: 0;
  filter: blur(18px) brightness(0);
  transition:
    filter 1.8s cubic-bezier(.16,1,.3,1) calc(var(--base-delay, 0.15s) + var(--i, 0) * var(--stagger, 0.05s)),
    opacity calc(1.8s * 0.6) ease-out calc(var(--base-delay, 0.15s) + var(--i, 0) * var(--stagger, 0.05s));
}
.is-revealed .char { opacity: 1; filter: blur(0) brightness(1); }

/* R8 word slide reveal */
.word-wrap {
  display: inline-block;
  overflow: hidden;
  padding: 7px 0;
  margin: -7px 0;
  vertical-align: top;
}
.word {
  display: inline-block;
  opacity: 0;
  transform: translateY(30%);
  transition:
    transform 0.9s cubic-bezier(.22,1,.36,1) calc(var(--base-delay, 0.15s) + var(--i, 0) * var(--stagger, 0.05s)),
    opacity 0.9s cubic-bezier(.22,1,.36,1) calc(var(--base-delay, 0.15s) + var(--i, 0) * var(--stagger, 0.05s));
}
.is-revealed .word { opacity: 1; transform: translateY(0); }

/* R10 custom cursor */
.cursor {
  position: fixed;
  top: 0;
  left: 0;
  pointer-events: none;
  z-index: 10;
  opacity: 0;
  transition: opacity 0.3s;
}
.cursor.is-visible { opacity: 1; }
.cursor:not(.is-enabled) { display: none; }
.cursor-trail { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; pointer-events: none; }
.cursor-head {
  position: absolute;
  top: 0;
  left: 0;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: var(--text);
  box-shadow: 0 0 10px 2px var(--text);
  transition: width 0.3s, height 0.3s, margin 0.3s, background 0.3s, box-shadow 0.3s;
  will-change: transform;
}
.cursor-head.is-target {
  width: 40px;
  height: 40px;
  margin: -20px 0 0 -20px;
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--text);
}

/* R19 globe ring */
.ring {
  position: fixed;
  top: 50%;
  left: 50%;
  width: 92vh;
  height: 92vh;
  margin: -46vh 0 0 -46vh;
  z-index: 1;
  pointer-events: none;
  overflow: visible;
  opacity: 0;
  animation: ring-spin 36s linear infinite;
}
.ring-text { font-size: 1.5625vw; fill: var(--text); }

@keyframes ring-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@media (orientation: portrait) {
  .ring { width: 55.2vh; height: 55.2vh; margin: -27.6vh 0 0 -27.6vh; }
  .ring-text { font-size: 4.6875vw; }
}

/* R20 visibility, D19 placement: label, line and dot follow the projected city (set per frame in px). */
.map-label,
.map-line,
.map-dot {
  position: fixed;
  left: 0;
  top: 0;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s, filter 0.3s, visibility 0.3s;
}
.map-label { white-space: nowrap; }
.map-label.is-wrapped { white-space: normal; }
.map-label.is-left { text-align: right; }
.map-line { width: 64px; height: 1px; background: var(--text); }
.map-dot { width: 6px; height: 6px; }
.map-dot::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--text);
  box-shadow: 0 0 10px 2px var(--text);
  animation: map-dot-pulse 2s ease-in-out infinite;
}
@keyframes map-dot-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.6); }
}

/* D19 portrait: below the point, size M, exactly two centred lines that never wrap further */
@media (orientation: portrait) {
  .map-label { font-size: 2.03704vh; line-height: 2.5463vh; letter-spacing: 0.0509259vh; }
  .map-label, .map-label.is-wrapped, .map-label.is-left { white-space: nowrap; text-align: center; }
  .map-label-line { display: block; }
}

/* R22 statement and helix line */
.statement {
  width: 90vw;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: flex-start;
}
.statement-text { max-width: 41.6667vw; }

.line-stage {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 0 5vw;
  pointer-events: none;
}
.helix-line {
  max-width: 41.6667vw;
  text-align: right;
  opacity: 0;
  transition: opacity 0.3s, filter 0.3s;
}

@media (orientation: portrait) {
  .statement-text { max-width: 100%; }
  .helix-line { max-width: 100%; }
}

/* R23 works gallery */
.works-stage {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  overflow: hidden;
  pointer-events: none;
  visibility: hidden;
}
.works-stage.is-active { visibility: visible; pointer-events: auto; }
.works-track {
  display: flex;
  flex-direction: row;
  gap: 12vw;
  margin-top: 4.62963vh;
  will-change: transform;
}
.tile { flex: 0 0 50vw; width: 50vw; }
.tile-link { display: block; color: inherit; }
.tile-media {
  position: relative;
  aspect-ratio: 16 / 9;
  width: 100%;
}
.tile-media::before {
  content: "";
  position: absolute;
  inset: -10%;
  background: radial-gradient(closest-side, rgb(240 242 240 / 14%), transparent);
  opacity: 0;
  transition: opacity 0.4s;
  pointer-events: none;
}
.tile.is-loaded .tile-media::before { opacity: 1; }
.tile-canvas {
  position: absolute;
  inset: -10%;
  width: 120%;
  height: 120%;
  display: block;
  transition: filter 0.4s;
}
.tile-link:hover .tile-canvas { filter: drop-shadow(0 1.48148vh 2.59259vh #ffffff38); }
/* D18: plain image with the same cover crop while the tile has no WebGL context. */
.tile-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: 50% 50%;
  transition: filter 0.4s;
}
.tile.has-gl .tile-img { visibility: hidden; }
.tile-link:hover .tile-img { filter: drop-shadow(0 1.48148vh 2.59259vh #ffffff38); }
.tile-caption {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.25vw;
  margin-top: 2.77778vh;
}
.tile-title { text-align: left; }
.tile-meta {
  display: flex;
  gap: 0.833333vw;
  text-align: right;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.works-kicker {
  position: fixed;
  top: 5.92593vh;
  left: 4.16667vw;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.works-closing-stage {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 90vw;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  justify-content: flex-start;
  pointer-events: none;
}
.works-closing {
  max-width: 33.3333vw;
  text-align: left;
}
.fade {
  opacity: 0;
  filter: blur(10px);
  transition: opacity 0.4s, filter 0.4s;
  pointer-events: none;
}
.fade.is-visible { opacity: 1; filter: blur(0); }

@media (orientation: portrait) {
  .tile { flex-basis: 85vw; width: 85vw; }
}

/* R26 contact */
.contact {
  width: 90vw;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2vw;
  text-align: center;
}
.contact-kicker { color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; }
.contact-email {
  opacity: 0;
  filter: blur(10px);
  transition: opacity 0.6s, filter 0.6s;
}
.contact-email.is-visible { opacity: 1; filter: blur(0); }
.contact-social { color: var(--muted); }
.contact-social a { color: var(--text); }

@media (orientation: portrait) {
  .contact-email { font-size: 8vw; line-height: 1.5; }
}

/* R27 footer */
.footer {
  width: 90vw;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  flex-direction: row;
  justify-content: space-between;
  padding: 2.22222vh 0;
}
.footer-line {
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  transition: color 0.3s;
}
.footer-line:has(a:hover) { color: var(--text); }

@media (orientation: portrait) {
  .footer {
    justify-content: center;
    text-align: center;
    row-gap: 6vw;
    padding: 10vw;
  }
  .footer-line { font-size: 3vw; letter-spacing: 0.04em; }
}

/* R28 preloader; D13 layer and counter */
body.is-preloading .content {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
.preloader {
  position: fixed;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  transition: opacity 0.7s, filter 0.7s;
}
.preloader.is-dismissing { opacity: 0; filter: blur(10px); }
.preloader-count {
  font-variant-numeric: tabular-nums;
  opacity: 0;
  filter: blur(10px);
  transition: opacity 0.6s, filter 0.6s;
}
.preloader-count.is-visible { opacity: 1; filter: blur(0); }

/* R30 text dissolve */
.dissolve {
  position: relative;
  display: inline-block;
}
.dissolve-wrap {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  transition: opacity 0.4s, filter 0.4s;
}
.dissolve-text { display: inline-block; }
.dissolve-canvas {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
}

/* D7 reduced motion */
@media (prefers-reduced-motion: reduce) {
  .grain { animation: none; }
  .char, .word, .contact-email { opacity: 1; filter: none; transform: none; transition: none; }
  .map-dot::before { animation: none; }
}
