/* ============================================================
   MAIN
   Site styles. Organised as:
     1. Base / typography
     2. Utilities
     3. Masthead      (logo + social links)
     4. Hero          (copy, globe stage, email)
     5. Outro         (final logo screen)
     6. Animation setup / fallbacks
   ============================================================ */


/* ------------------------------------------------------------
   1. BASE
   ------------------------------------------------------------ */

/* NOTE: do NOT put `overflow-x: hidden` on html or body.
   It turns them into a scroll container, which stops ScrollTrigger's
   pinning from holding the hero in place — the section scrolls away
   and leaves a blank screen. The bubble man is clipped by `.hero`
   instead (see section 4), which achieves the same thing safely. */

body {
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: var(--weight-regular);
}

a {
  color: var(--color-link);
  transition: color var(--duration-fast) var(--ease-out);
}

a:hover {
  color: var(--color-link-hover);
}


/* ------------------------------------------------------------
   2. UTILITIES
   ------------------------------------------------------------ */

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

.skip-link {
  position: absolute;
  top: var(--space-sm);
  left: var(--space-sm);
  z-index: var(--z-modal);
  padding: var(--space-xs) var(--space-sm);
  background: var(--color-text);
  color: var(--color-bg);
  border-radius: 4px;
  transform: translateY(-200%);
  transition: transform var(--duration-fast) var(--ease-out);
}

.skip-link:focus {
  transform: translateY(0);
}


/* ------------------------------------------------------------
   3. MASTHEAD
   Logo top left, social icons top right.
   Lives inside the hero so it pins and releases along with it —
   that's what leaves the final screen showing only the mark.
   ------------------------------------------------------------ */

.masthead {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-overlay);

  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);

  padding: var(--space-md) var(--gutter);
}

.masthead__logo img {
  width: clamp(72px, 8vw, 104px);
  height: auto;
}

/* --- Right-hand group: nav link(s) + social icons --- */

.masthead__actions {
  display: flex;
  align-items: center;
  gap: clamp(0.75rem, 2.5vw, 1.5rem);
}

.masthead__link {
  padding: 0.4rem 0.2rem;            /* a comfortable tap target */
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  color: var(--color-link);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease-out);
}
.masthead__link:hover,
.masthead__link:focus-visible {
  color: var(--color-link-hover);
}

/* --- Social icons --- */

.masthead__social {
  display: flex;
  align-items: center;
  gap: clamp(0.5rem, 2vw, 1.25rem);
}

.masthead__social a {
  /* 44px minimum tap target, per accessibility guidance */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  transition: transform var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out);
}

.masthead__social a:hover {
  transform: translateY(-2px);
}

.masthead__social svg {
  width: clamp(19px, 2vw, 22px);
  height: auto;

  /* Icons are drawn as outlines and inherit the current text colour,
     so they follow the palette automatically. */
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Parts of an icon that read better solid than outlined */
.masthead__social .icon-dot,
.masthead__social .icon-fill {
  fill: currentColor;
  stroke: none;
}


/* ------------------------------------------------------------
   4. HERO
   Full-screen centred column: copy -> globe -> email.
   ------------------------------------------------------------ */

.hero {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;

  min-height: 100svh;
  padding: calc(var(--space-2xl) + var(--space-lg)) var(--gutter) var(--space-xl);

  /* Clips the bubble man while he's off-stage, so the page never
     gains a horizontal scrollbar. Deliberately here rather than on
     html/body, which would break ScrollTrigger's pinning. */
  overflow: hidden;

  /* New stacking context so the pinned hero can't be overlapped
     by the outro during the hand-off. */
  isolation: isolate;
}

.hero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(var(--space-md), 3vh, var(--space-lg));
  width: 100%;
}

.hero__title {
  max-width: var(--measure);
  font-size: var(--text-2xl);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  text-wrap: balance;
}

.hero__text {
  max-width: var(--measure);
  margin-top: var(--space-xs);
  font-size: var(--text-lg);
  line-height: var(--leading-body);
  text-wrap: pretty;
}

/* --- Email signup (Buttondown) -----------------------------
   Replaces the old mailto link. Pill input + solid button, using
   the palette. The button inverts to yellow on hover.
   ------------------------------------------------------------ */

.signup {
  width: 100%;
  max-width: 30rem;         /* keeps it contained on wide screens */
  margin-inline: auto;
}

.signup__row {
  display: flex;
  flex-direction: column;   /* input, with the button stacked below it */
  gap: var(--space-xs);
}

.signup__input {
  flex: 1 1 auto;
  min-width: 0;             /* allows the input to shrink inside flex */
  padding: 0.75rem 1.1rem;
  font: inherit;
  font-size: var(--text-base);
  color: var(--color-text);
  background: transparent;
  border: 1.5px solid var(--color-text);
  border-radius: 999px;    /* pill, echoing the round social buttons */
  transition: border-color var(--duration-fast) var(--ease-out),
              box-shadow var(--duration-fast) var(--ease-out);
}

.signup__input::placeholder {
  color: var(--color-text);
  opacity: 0.45;
}

.signup__input:focus-visible {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 3px rgba(237, 196, 106, 0.45);   /* yellow focus ring */
}

.signup__button {
  width: 100%;              /* full width, matching the input above it */
  padding: 0.75rem 1.5rem;
  font: inherit;
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  color: var(--color-off-white);
  background: var(--color-text);
  border: 1.5px solid var(--color-text);
  border-radius: 999px;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out);
}

.signup__button:hover {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-text);
}

.signup__button:active {
  transform: translateY(1px);
}

.signup__button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(237, 196, 106, 0.55);
}


/* --- The orbit stage ---------------------------------------
   Square box with the globe at its centre. The bubble man is
   positioned by JS relative to this centre, so the stage size
   is the single source of truth for the orbit's scale.
   ------------------------------------------------------------ */

.stage {
  position: relative;
  /* Deliberately capped by viewport HEIGHT as well as width, so the
     globe never crowds out the copy on short or landscape screens. */
  width: min(52vh, 78vw, 460px);
  aspect-ratio: 1;

  /* The bubble man travels well outside these bounds on his way in */
  overflow: visible;
}

.stage__globe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 2;

  /* The artwork is cleanly transparent outside the planet, so no mask
     is needed — the bubble man is occluded by the disc itself, which
     is exactly the effect we want. */

  /* Rotation is driven by JS; hint it to the compositor */
  will-change: transform;
}

.stage__bubbleman {
  position: absolute;
  /* Anchored at the stage centre — JS moves him out from here, so
     every transform is a simple offset from the globe's middle. */
  top: 50%;
  left: 50%;
  width: 32%;
  height: auto;

  /* z-index is swapped by JS as he passes in front of / behind
     the globe. Starts behind. */
  z-index: 1;
  will-change: transform, opacity;
}


/* --- Scroll affordance --- */

.scroll-hint {
  position: absolute;
  bottom: var(--space-lg);
  left: 50%;
  translate: -50% 0;
  display: flex;
  justify-content: center;
  width: 1px;
  height: 44px;
  overflow: hidden;
  opacity: 0.45;
}

.scroll-hint__line {
  width: 1px;
  height: 100%;
  background: currentColor;
  transform-origin: top;
  animation: scroll-hint-slide 2.4s var(--ease-in-out) infinite;
}

@keyframes scroll-hint-slide {
  0%        { transform: scaleY(0); transform-origin: top; }
  45%       { transform: scaleY(1); transform-origin: top; }
  55%       { transform: scaleY(1); transform-origin: bottom; }
  100%      { transform: scaleY(0); transform-origin: bottom; }
}


/* ------------------------------------------------------------
   5. OUTRO
   The final screen: nothing but the mark.
   ------------------------------------------------------------ */

.outro {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100svh;
  padding: var(--space-xl) var(--gutter);
}

.outro__logo {
  width: clamp(140px, 22vw, 260px);
  height: auto;
}


/* ------------------------------------------------------------
   6. ANIMATION SETUP

   Elements marked [data-animate] start invisible so GSAP can fade
   them in. Hiding content behind JavaScript is risky, so there are
   several independent ways it gets revealed again — the content must
   never be able to get stuck invisible:

     .no-js       JavaScript didn't run at all
     .intro-done  the entrance finished normally, OR the failsafe
                  timer fired because it didn't (js/animations.js)
     .motion-off  motion is genuinely off: GSAP missing or the
                  visitor prefers reduced motion
     inline       GSAP sets opacity directly as it animates

   `.intro-done` and `.motion-off` are deliberately separate. Both
   reveal the copy, but only `.motion-off` means "there will be no
   animation at all" — which is what the orbit rules below key off.
   Merging them would hide the bubble man the instant the entrance
   finished, which is precisely when he should start flying.

   Note the `.js` prefix on the hiding rule: without JavaScript
   nothing is ever hidden in the first place.
   ------------------------------------------------------------ */

.js [data-animate] {
  opacity: 0;
}

.no-js [data-animate],
.intro-done [data-animate],
.motion-off [data-animate] {
  opacity: 1;
}

/* The bubble man only makes sense as part of the orbit. With no JS
   to fly him, hide him rather than parking him on top of the globe. */
.no-js .stage__bubbleman,
.motion-off .stage__bubbleman {
  display: none;
}

/* Likewise the scroll hint: it promises motion that won't happen. */
.no-js .scroll-hint,
.motion-off .scroll-hint {
  display: none;
}


/* ------------------------------------------------------------
   REDUCED MOTION
   Respect the OS setting. The orbit is decorative; the page must
   still read as a complete, static composition without it.
   ------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .stage__bubbleman,
  .scroll-hint {
    display: none;
  }
}
