@font-face {
  font-family: "Space Grotesk";
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  src: url("../assets/fonts/SpaceGrotesk-Medium.woff2") format("woff2");
}

@font-face {
  font-family: "Space Grotesk";
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url("../assets/fonts/SpaceGrotesk-Regular.woff2") format("woff2");
}

@font-face {
  font-family: "Space Grotesk";
  font-weight: 300;
  font-style: normal;
  font-display: swap;
  src: url("../assets/fonts/SpaceGrotesk-Light.woff2") format("woff2");
}

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

html,
body {
  height: 100%;
  background: #000;
  font-family: "Space Grotesk", sans-serif;
  font-weight: 500;
}

/* No transition on the way in -- it's the first thing in <body>, so it
   simply paints as soon as the browser gets there, covering the page
   before anything underneath has a chance to be seen unstyled. Only the
   way out (see js/preloader.js) fades. */
.preloader {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: #000;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 1;
  transition: opacity 0.3s ease;
}

.preloader.is-hidden {
  opacity: 0;
  pointer-events: none;
}

.preloader[hidden] {
  display: none;
}

.preloader__gif {
  height: 56pt;
  width: auto;
}

.hero {
  min-height: 100svh;
  align-items: center;
}

/* left of the globe, same row: spans 4 columns wide overall, but the
   items themselves hug their own text (flex, not equal grid columns) --
   2026/social/brand sit with equal gaps between them, and ABOUT is
   pushed out to the far right edge via its own auto margin.
   Stacked above the globe (see .sphere-wrap) so that if the globe's
   aspect-ratio-driven height ever overflows its row on narrow/short
   viewports, it passes behind the menu instead of covering the labels. */
.menu {
  grid-column: 1 / 5;
  grid-row: 1;
  position: relative;
  z-index: 2;
  display: flex;
  align-items: baseline;
  gap: var(--gutter);
}

.menu-item {
  font-size: 10pt;
  color: #fff;
  white-space: nowrap;
}

.menu-item--year {
  font-weight: 500;
  letter-spacing: -0.06em;
  opacity: 0.48;
}

.menu-item--social {
  font-weight: 500;
  letter-spacing: 0.04em;
}

.menu-item--social .dim {
  opacity: 0.48;
}

.menu-item--social .menu-slash {
  opacity: 0.48;
}

.menu-item--social .menu-link {
  color: inherit;
  text-decoration: none;
  opacity: 0.75;
}

.menu-item--social .menu-link:hover {
  text-decoration: underline;
}

.menu-item--brand {
  font-weight: 400;
  opacity: 0.48;
}

.menu-item--brand .brand-mmm {
  letter-spacing: 0.08em;
}

.menu-item--brand .brand-dotworks {
  letter-spacing: -0.04em;
}

.menu-item--about {
  position: relative;
  font-weight: 400;
  letter-spacing: 0.08em;
  margin-left: auto;
}

.about-label {
  /* reset <button> chrome so it reads exactly like the other plain-text labels */
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  opacity: 0.75;
  transition: opacity 0.3s ease;
}

.menu-item--about:hover .about-label {
  opacity: 1;
}

/* Both start scaled to nothing, pinned at the ABOUT text's own center
   point, then grow to their real size while sliding out to their
   designated offset -- same transform property on both ends (translate
   then scale) so the browser can interpolate it smoothly. */
.abt-hover-img {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.4s ease, opacity 0.4s ease;
}

.abt-hover-img--a {
  width: 93px;
  height: 87px;
}

.abt-hover-img--b {
  width: 52px;
  height: 65px;
}

.menu-item--about:hover .abt-hover-img {
  opacity: 1;
}

.menu-item--about:hover .abt-hover-img--a {
  transform: translate(calc(-50% + 58pt), calc(-50% - 58pt)) scale(1);
}

.menu-item--about:hover .abt-hover-img--b {
  transform: translate(calc(-50% - 40pt), calc(-50% + 40pt)) scale(1);
}

/* While the modal is open (see about-modal.js), the hover reveal is
   pointless -- ABOUT's only job at that point is to close it -- so
   these override the :hover rules above back to their rest state. */
.menu-item--about.about-active:hover .about-label {
  opacity: 0.75;
}

.menu-item--about.about-active:hover .abt-hover-img--a,
.menu-item--about.about-active:hover .abt-hover-img--b {
  opacity: 0;
  transform: translate(-50%, -50%) scale(0);
}

/* Mirrors .menu on the opposite side of the globe (cols 9/13 vs 1/5), but
   the 4 labels sit in equal-width containers instead of hugging their own
   text -- the long services list needs the fixed width to actually wrap.
   Its box is taller than .menu's single line (services text wraps to
   several lines), so centering it the same way .menu is would leave its
   top edge sitting higher than .menu's -- align-self:start instead, and
   js/menu-align.js nudges it down via margin-top to match .menu's actual
   top exactly, since that offset depends on the globe's rendered height
   and can't be known in advance from CSS alone. */
.menu--right {
  grid-column: 9 / 13;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  align-self: start;
  /* overrides .menu's inherited baseline alignment -- baseline alignment
     across 4 columns of mismatched line count (the chip has no text
     baseline at all) produces unpredictable per-item offsets. Flush
     top-alignment instead, with js/menu-align.js correcting the
     remaining line-height leading gap precisely. */
  align-items: start;
}

.menu--right .menu-item {
  opacity: 0.75;
  white-space: normal;
}

.menu-item--availability {
  font-weight: 500;
  letter-spacing: -0.02em;
}

/* Each chip sets its own opacity individually, so the blanket rule above
   would compound with it (e.g. 0.75 * 0.35) -- opt this item back out. */
.menu-item--chips {
  opacity: 1;
}

/* Fixed 2 columns so 6 chips always land 2-per-row across 3 rows,
   regardless of how much room this column actually has. */
.chip-list {
  display: grid;
  grid-template-columns: repeat(2, max-content);
  gap: 0;
}

.menu-item--services {
  font-weight: 200;
  letter-spacing: 0;
  text-align: justify;
}

.menu-item--inquiries {
  font-weight: 500;
  letter-spacing: 0.02em;
  text-align: right;
}

/* All chips share one fixed width instead of hugging their own text --
   sized in js/menu-align.js to fit "SEPT.", the longest label, so the
   rest sit centered inside the same-size pill. */
.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 2pt 6pt;
  font-size: 8pt;
  letter-spacing: 0.01em;
  border: 1px solid currentColor;
  border-radius: 999px;
  opacity: 0.75;
  white-space: nowrap;
}

.chip--dim {
  opacity: 0.35;
}

/* 4 columns wide, centered: cols 5/9 of 12 on desktop, full 1/5 of 4 on mobile.
   Sized to 110% of that and re-centered via justify-self, so it overflows
   its own column area symmetrically rather than being constrained to it --
   nothing up the ancestor chain clips overflow, so this stays fully visible. */
.sphere-wrap {
  grid-column: 5 / 9;
  grid-row: 1;
  position: relative;
  z-index: 1;
  width: 110%;
  justify-self: center;
  aspect-ratio: 1 / 1;
  /* The decal is drawn from an opaque video frame, so its black areas
     are fully opaque canvas pixels even with the internal lighten trick
     (that only affects blending against the wireframe drawn earlier in
     the same canvas, not against whatever's behind the element itself).
     This makes the whole globe blend against its real backdrop too, so
     .profile-pic can show through -- against the page's plain black
     background this is a no-op (lighten against black == source-over). */
  mix-blend-mode: lighten;
}

/* Same grid area as .sphere-wrap, so its center point matches the
   globe's exactly. Lower z-index puts it behind the globe. Only ever
   shown while the about modal is open (toggled in about-modal.js).
   Scales in from 80% to 100% on open and reverses on close -- see
   about-modal.js for why this can't just be the [hidden] attribute
   alone (transitions can't run across a display:none boundary). */
.profile-pic {
  grid-column: 5 / 9;
  grid-row: 1;
  position: relative;
  z-index: 0;
  justify-self: center;
  align-self: center;
  width: calc((var(--col) * 4 + var(--gutter) * 3) * 0.448); /* 4 columns, reduced by 80%, doubled, +12% */
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: 32pt;
  corner-shape: superellipse(2); /* 100% corner smoothing; ignored where unsupported, falls back to plain rounding */
  opacity: 0;
  transform: scale(0.8);
  transition: transform 0.2s ease, opacity 0.2s ease;
  pointer-events: none;
}

.profile-pic.is-visible {
  opacity: 1;
  transform: scale(1);
}

.profile-pic[hidden] {
  display: none;
}

/* Positioned in JS (see about-modal.js) to align exactly with "2026©"'s
   own top-left corner -- the hero row is vertically centered, so a
   grid-based position would land at the top of the viewport instead.
   Width uses --col (defined globally in grid.css) since this element
   lives outside the grid (position: fixed) and can't size itself
   against the real columns via grid-column. */
.about-modal {
  position: fixed;
  z-index: 10;
  width: calc(var(--col) * 3 + var(--gutter) * 2);
  background: #000;
  color: #fff;
  padding: 0; /* no top/bottom padding -- height hugs the paragraph text exactly */
  font-family: "Space Grotesk", sans-serif;
  font-weight: 300;
  letter-spacing: 0.04em;
  line-height: 1.44;
}

.about-modal[hidden] {
  display: none;
}

.about-modal__row {
  display: flex;
  align-items: flex-start;
  gap: var(--col); /* one grid column's width between the text and the close button */
}

.about-modal__text {
  display: flex;
  flex-direction: column;
  gap: 1em;
  font-size: 11pt;
  opacity: 0.8;
}

.about-modal__close {
  flex: none;
  font: inherit;
  font-size: 11pt;
  letter-spacing: inherit;
  line-height: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  opacity: 0.8;
  transition: opacity 0.3s ease;
}

.about-modal__close:hover {
  opacity: 1;
}

.menu-item--inquiries:hover {
  opacity: 1;
}

.inquiries-label {
  /* reset <button> chrome so it reads exactly like the other plain-text labels */
  font: inherit;
  letter-spacing: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

/* Positioned in JS (see inquiry-modal.js) to align with "AVAILABLE FOR
   PROJECTS:"'s own top-left corner, the same way .about-modal anchors to
   the year label -- expands rightward from there, covering 3 of the right
   menu's own 4 equal-width columns (available/chips/services), leaving
   the INQUIRIES trigger itself clear so it still reads as the close
   affordance. */
.inquiry-modal {
  position: fixed;
  z-index: 10;
  width: calc(var(--col) * 3 + var(--gutter) * 2);
  background: #000;
  color: #fff;
  padding: 0;
  font-family: "Space Grotesk", sans-serif;
  font-weight: 300;
  letter-spacing: 0.04em;
  line-height: 1.44;
}

.inquiry-modal[hidden] {
  display: none;
}

.inquiry-modal__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--col);
}

.inquiry-modal__title {
  font-size: 10pt;
  line-height: 1.32;
  opacity: 0.8;
}

.inquiry-modal__form {
  display: flex;
  flex-direction: column;
  gap: 1em;
  font-size: 11pt;
  margin-top: 4pt;
}

.inquiry-modal__field-row {
  display: flex;
  gap: 1em;
}

.inquiry-modal__field-row .inquiry-modal__field {
  flex: 1;
  min-width: 0;
}

.inquiry-modal__field {
  display: flex;
  flex-direction: column;
  gap: 2pt;
}

.inquiry-modal__label {
  opacity: 0.6;
  font-size: 8pt;
}

.inquiry-modal__input {
  font: inherit;
  font-size: 11pt;
  letter-spacing: inherit;
  color: inherit;
  background: none;
  border: none;
  border-bottom: 1px solid currentColor;
  border-radius: 0;
  opacity: 0.9;
  padding: 4pt 0;
  outline: none;
}

.inquiry-modal__input option {
  color: #000;
}

.inquiry-modal__close {
  flex: none;
  font: inherit;
  font-size: 11pt;
  letter-spacing: inherit;
  line-height: inherit;
  color: inherit;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  opacity: 0.8;
  transition: opacity 0.3s ease;
}

.inquiry-modal__submit {
  align-self: flex-end;
  font: inherit;
  font-size: 11pt;
  letter-spacing: inherit;
  color: inherit;
  background: none;
  border: 1px solid currentColor;
  padding: 6pt 14pt;
  cursor: pointer;
  opacity: 0.9;
  transition: opacity 0.3s ease;
}

.inquiry-modal__submit:hover {
  opacity: 1;
}

.inquiry-modal__close:hover {
  opacity: 1;
}

@media (max-width: 1024px) {
  .hero {
    grid-template-rows: auto 1fr auto;
    padding-top: var(--margin);
  }

  .sphere-wrap {
    grid-column: 1 / 5;
    grid-row: 2;
    /* relative offset -- purely visual, doesn't touch row 2's own size,
       so row 3 (the bottom menu) doesn't get pushed down along with it */
    position: relative;
    top: 60pt;
  }

  /* Stacked below the globe -- cols 9/13 don't exist on the 4-col grid.
     2x2 instead of 1x4: [available][chips] on top, [services][inquiries]
     below, each label pinned to its own cell's top-left. */
  .menu--right {
    grid-column: 1 / 5;
    grid-row: 3;
    grid-template-columns: repeat(2, 1fr);
    align-items: start;
    padding-bottom: 20pt;
  }

  /* Desktop locks this to exactly 2-per-row -- on mobile, fit as many
     equal-width chips as the row has room for before wrapping instead. */
  .chip-list {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
  }

  .profile-pic {
    grid-column: 1 / 5;
    grid-row: 2;
    /* .sphere-wrap's own width (110%), inset 20pt on each side -- since
       it's a 1:1 square, that's an even 20pt gap on all four edges --
       then scaled down by 25% of that result. */
    width: calc((110% - 40pt) * 0.75);
    /* matches .sphere-wrap's offset so it stays centered on the globe */
    position: relative;
    top: 60pt;
  }

  /* full width of the mobile menu row (all 4 columns) instead of 3 --
     covers end to end, including the ABOUT label itself this time,
     since there's no room to leave it peeking out on a narrow screen */
  .about-modal {
    width: calc(var(--col) * 4 + var(--gutter) * 3);
  }

  .inquiry-modal {
    width: calc(var(--col) * 4 + var(--gutter) * 3);
  }
}

.sphere-wrap canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}

/* Off-screen, not display:none -- it still needs to actually be playing */
.logo-video {
  position: fixed;
  width: 1px;
  height: 1px;
  top: -9999px;
  left: -9999px;
  opacity: 0;
  pointer-events: none;
}
