/* Tangent Gallery — landing mark
   The logo is a CSS mask; every light effect is painted THROUGH the engraving. */

:root {
  --ink:        #050507;   /* ground */
  --ink-lift:   #0a0c11;   /* centre haze */
  --pewter:     #e9edf4;   /* mark at rest, unlit */
  --lit:        #fffaf0;   /* warm specular */
  --port-rest:  #8f96a3;
  --port-ring:  rgba(226, 232, 240, 0.4);

  --mark-size:  min(88vw, 62vh, 560px);
  --port:       54px;
  --port-gap:   22px;

  --ease-out:   cubic-bezier(0.16, 1, 0.3, 1);
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--ink);
}

body {
  overflow: hidden;
  color: var(--port-rest);
  font: 400 1rem/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
}

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

/* ── Sky ──────────────────────────────────────────────────────────────── */

#sky {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
}

/* Centre haze + corner vignette. The ground is never flat black. */
.stage::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(52vmin 52vmin at 50% 46%, var(--ink-lift), transparent 70%),
    radial-gradient(120vmax 90vmax at 50% 50%, transparent 38%, rgba(0, 0, 0, 0.82) 100%);
}

/* ── Stage ────────────────────────────────────────────────────────────── */

.stage {
  position: relative;
  z-index: 2;
  min-height: 100%;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(3.5rem, 9vmin, 5.5rem);
  padding: max(1.5rem, env(safe-area-inset-top)) 1.25rem max(1.5rem, env(safe-area-inset-bottom));
}

/* ── The mark ─────────────────────────────────────────────────────────── */

.mark {
  position: relative;
  width: var(--mark-size);
  height: var(--mark-size);
  flex: none;

  /* Pointer position, in % of the mark's own box. Driven by main.js. */
  --mx: 38%;
  --my: 24%;

  transition:
    opacity 1400ms var(--ease-out),
    transform 1600ms var(--ease-out),
    filter 1200ms var(--ease-out);
}

/* Entrance states apply only once we know scripting is live. */
.anim .mark {
  opacity: 0;
  transform: scale(0.955);
  filter: blur(7px);
}

.mark-ready .mark {
  opacity: 1;
  transform: none;
  filter: none;
}

.mark__body,
.mark__bloom-in {
  position: absolute;
  inset: 0;
  -webkit-mask-image: url("assets/logo.svg");
          mask-image: url("assets/logo.svg");
  -webkit-mask-size: contain;
          mask-size: contain;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
}

/* Emissive halo. The blur lives on the parent so it is not clipped by the mask. */
.mark__bloom {
  position: absolute;
  inset: 0;
  filter: blur(22px);
  opacity: 0.62;
  transition: opacity 1800ms var(--ease-out) 320ms;
  will-change: opacity;
}

.anim .mark__bloom { opacity: 0; }

.mark-ready .mark__bloom { opacity: 0.62; }

.mark__bloom-in {
  background: radial-gradient(
    42% 42% at var(--mx) var(--my),
    #fffaf0 0%,
    rgba(255, 250, 240, 0.52) 44%,
    rgba(255, 250, 240, 0) 78%
  );
}

.mark__base,
.mark__shade,
.mark__spec {
  position: absolute;
  inset: 0;
}

.mark__base { background: var(--pewter); }

/* Falloff away from the light. Multiply gives the highlight somewhere to
   travel without ever making the mark read as grey. */
.mark__shade {
  background: radial-gradient(
    80% 80% at var(--mx) var(--my),
    #ffffff 0%,
    #ffffff 50%,
    #fafbfd 80%,
    #f4f6fa 100%
  );
  mix-blend-mode: multiply;
}

/* Specular highlight — the light the visitor is holding. */
.mark__spec {
  background: radial-gradient(
    27% 27% at var(--mx) var(--my),
    rgba(255, 250, 238, 1),
    rgba(255, 250, 238, 0.74) 36%,
    rgba(255, 250, 238, 0) 76%
  );
  mix-blend-mode: screen;
}

/* ── Ports ────────────────────────────────────────────────────────────── */

.ports {
  display: flex;
  gap: var(--port-gap);
  transition:
    opacity 900ms var(--ease-out) 620ms,
    transform 1000ms var(--ease-out) 620ms;
}

.anim .ports {
  opacity: 0;
  transform: translateY(10px);
}

.ports-ready .ports {
  opacity: 1;
  transform: none;
}

.port {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--port);
  height: var(--port);
  border-radius: 50%;
  color: var(--port-rest);
  text-decoration: none;
  transition: color 420ms var(--ease-out);
}

/* The ring echoes the bezel's hairline circles. */
.port::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid var(--port-ring);
  border-radius: 50%;
  transition:
    border-color 420ms var(--ease-out),
    transform 520ms var(--ease-out),
    box-shadow 520ms var(--ease-out);
}

.port__glyph {
  width: 22px;
  height: 22px;
  overflow: visible;
}

@media (min-width: 700px) {
  :root { --mark-size: min(62vw, 62vh, 560px); }
}

/* Tall frames get a squarer mark in a longer viewport, so the desktop gap reads
   as a hem. Open it — but proximity is what binds the ports to the mark, so the
   air belongs in the top and bottom margins, not inside the lockup. */
@media (max-aspect-ratio: 3 / 4) {
  .stage { gap: clamp(4rem, 9vh, 6rem); }
}

@media (hover: hover) {
  .port:hover { color: var(--lit); }
  .port:hover::before {
    border-color: rgba(255, 246, 232, 0.62);
    transform: scale(1.07);
    box-shadow: 0 2px 22px -6px rgba(255, 246, 232, 0.45);
  }
}

.port:active::before { transform: scale(0.97); }

.port:focus-visible {
  outline: 2px solid var(--lit);
  outline-offset: 5px;
  color: var(--lit);
}

/* ── Reduced motion ───────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  /* The entrance still runs, it just arrives instead of travelling. Opacity is
     deliberately NOT forced here: the mark must stay hidden until its mask
     loads, or it paints as a white rectangle. */
  .mark,
  .mark__bloom,
  .ports {
    transition-duration: 1ms;
    transition-delay: 0ms;
  }

  /* Rest position for the light, in case scripting never sets it. */
  .mark { --mx: 38%; --my: 24%; }

  .port::before { transition: none; }
}
