/* Unveiled — Copyright (c) 2026 Andre Prior // PushingPandas // a.prior@pushingpandas.io
   All rights reserved. No distribution, modification, or sale without written consent. */

/* GeistPixel family — five LED-matrix variants self-hosted under
   /assets/fonts/. Each variant is a different cell shape:
   Circle (dots), Grid, Line, Square, Triangle. Each declared as an
   independent font-family so CSS consumers can pick by name. The
   wordmark uses Circle by default per the mock. font-display: swap
   so the page is readable while the woff2 streams. */
@font-face {
  font-family: "GeistPixel Circle";
  src: url("/assets/fonts/GeistPixel-Circle.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "GeistPixel Grid";
  src: url("/assets/fonts/GeistPixel-Grid.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "GeistPixel Line";
  src: url("/assets/fonts/GeistPixel-Line.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "GeistPixel Square";
  src: url("/assets/fonts/GeistPixel-Square.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "GeistPixel Triangle";
  src: url("/assets/fonts/GeistPixel-Triangle.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
/* Geist Mono — 9 static weights self-hosted under /assets/fonts/.
   Each weight is its own woff2 (no variable font); browser picks
   the right file from the family stack via font-weight. */
@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-Thin.woff2") format("woff2");
  font-weight: 100;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-ExtraLight.woff2") format("woff2");
  font-weight: 200;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-Light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-Medium.woff2") format("woff2");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-SemiBold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-ExtraBold.woff2") format("woff2");
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-Black.woff2") format("woff2");
  font-weight: 900;
  font-style: normal;
  font-display: swap;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  background: #000;
  color: #f6f8fc;
  /* Geist Mono is the canonical body face for the marketing teaser.
     Only the wordmark overrides this (GeistPixel Circle). Everything
     else — season, byline, signup label, footer, status messages —
     inherits this stack. */
  font-family: "Geist Mono", ui-monospace, "JetBrains Mono", Consolas,
    monospace;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body {
  /* Let actual content drive page height — the original 180vh was
     padding the footer halfway down a void. Parallax (sky.js) still
     reacts to whatever scroll the content induces; with the planet
     + signup pill + footer we land ~110vh, just enough to see the
     star layers shift. */
  min-height: 100vh;
}

/* Star canvas sits ABOVE the planet (z-index 10) with screen blend
   mode — black canvas pixels become transparent against whatever is
   behind, so only the bright star dots are visible. Net effect: the
   field hovers over the entire page including the planet rectangle,
   killing the "pasted-on box" feel from the previous render.
   pointer-events: none so it doesn't intercept clicks/hovers. */
#sky {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  display: block;
  z-index: 10;
  pointer-events: none;
  mix-blend-mode: screen;
}

/* No z-index here: main needs to share the page's stacking context
   so the planet img's own mix-blend-mode resolves against the
   #000 body background instead of an isolated layer. Otherwise the
   planet's near-black PNG pixels would render as a slightly-darker
   rectangle. */
main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 9vh 6vw 8vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

.hero {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.wordmark {
  /* GeistPixel Circle is the canonical wordmark face. Fallbacks
     stay monospaced so a font-loading hiccup still reads as
     dot-matrix-adjacent, never like a serif title. */
  font-family: "GeistPixel Circle", ui-monospace, "JetBrains Mono",
    Consolas, monospace;
  font-weight: 500;
  font-size: clamp(58px, 9vw, 124px);
  letter-spacing: 4px;
  margin: 0;
  line-height: 1;
  color: #f6f8fc;
  /* Subtle backlit glow so the dots feel emissive against the black
     background, like an LED matrix at low power. */
  text-shadow: 0 0 18px rgba(255, 255, 255, 0.18),
    0 0 48px rgba(180, 200, 255, 0.08);
  cursor: default;
  /* hover triggers a one-shot CRT glitch — chromatic split + jitter
     + brief skew, ~480ms then back to rest. steps() (vs cubic-bezier)
     keeps the motion staccato so it reads as digital corruption,
     not a smooth zoom. */
}

/* Three glitch variants. glitch.js picks one at random on each
   trigger (auto every 4-10 s, plus hover) so the wordmark never
   reads the same way twice in a row. All return to a clean rest
   pose so they can stack visually with the static text-shadow.
   - A: chromatic split + skew + filter swings (the original)
   - B: short vertical jitter, minimal colour
   - C: long, slow chromatic-only pulse, no motion */
@keyframes wordmark-glitch-a {
  0% {
    transform: translate(0, 0) skew(0deg);
    text-shadow: 0 0 18px rgba(255, 255, 255, 0.18),
      0 0 48px rgba(180, 200, 255, 0.08);
    filter: none;
  }
  8% {
    transform: translate(-3px, 1px) skewX(-4deg);
    text-shadow: 3px 0 0 rgba(255, 60, 90, 0.85),
      -3px 0 0 rgba(60, 200, 255, 0.85);
    filter: contrast(1.4);
  }
  16% {
    transform: translate(3px, -2px);
    text-shadow: -2px 0 0 rgba(255, 60, 90, 0.85),
      2px 0 0 rgba(60, 200, 255, 0.85);
    filter: hue-rotate(40deg);
  }
  26% {
    transform: translate(-2px, 2px) skewX(2deg);
    text-shadow: 2px 0 0 rgba(255, 220, 80, 0.7),
      -2px 0 0 rgba(120, 90, 255, 0.7);
    filter: invert(0.1);
  }
  38% {
    transform: translate(2px, 0);
    text-shadow: 4px 0 0 rgba(255, 60, 90, 0.55),
      -4px 0 0 rgba(60, 200, 255, 0.55);
    filter: brightness(1.3);
  }
  52% {
    transform: translate(-1px, -1px) skewX(-1deg);
    text-shadow: -3px 0 0 rgba(255, 60, 90, 0.7),
      3px 0 0 rgba(60, 200, 255, 0.7);
    filter: hue-rotate(-30deg);
  }
  68% {
    transform: translate(2px, 1px);
    text-shadow: 1px 0 0 rgba(255, 255, 255, 0.9),
      -1px 0 0 rgba(140, 200, 255, 0.9);
    filter: contrast(1.2);
  }
  82% {
    transform: translate(-1px, 0);
    text-shadow: -1px 0 0 rgba(255, 60, 90, 0.4),
      1px 0 0 rgba(60, 200, 255, 0.4),
      0 0 18px rgba(255, 255, 255, 0.18);
    filter: none;
  }
  100% {
    transform: translate(0, 0) skew(0deg);
    text-shadow: 0 0 18px rgba(255, 255, 255, 0.18),
      0 0 48px rgba(180, 200, 255, 0.08);
    filter: none;
  }
}

/* Variant B — short, sharp vertical jitter. ~260 ms / 8 steps. No
   skew, minimal chromatic kick, just a brief shudder. */
@keyframes wordmark-glitch-b {
  0% {
    transform: translate(0, 0);
    text-shadow: 0 0 18px rgba(255, 255, 255, 0.18),
      0 0 48px rgba(180, 200, 255, 0.08);
  }
  20% {
    transform: translate(0, -2px);
  }
  40% {
    transform: translate(0, 3px);
    text-shadow: 1px 0 0 rgba(255, 60, 90, 0.65),
      -1px 0 0 rgba(60, 200, 255, 0.65);
  }
  60% {
    transform: translate(0, -1px);
  }
  80% {
    transform: translate(0, 1px);
  }
  100% {
    transform: translate(0, 0);
    text-shadow: 0 0 18px rgba(255, 255, 255, 0.18),
      0 0 48px rgba(180, 200, 255, 0.08);
  }
}

/* Variant C — slow chromatic pulse, no motion at all. ~700 ms,
   ease-out so it ramps in and falls back gently. Reads as a quiet
   signal-loss flicker rather than a rough glitch. */
@keyframes wordmark-glitch-c {
  0% {
    text-shadow: 0 0 18px rgba(255, 255, 255, 0.18),
      0 0 48px rgba(180, 200, 255, 0.08);
  }
  35% {
    text-shadow: 4px 0 0 rgba(255, 60, 90, 0.45),
      -4px 0 0 rgba(60, 200, 255, 0.45),
      0 0 18px rgba(255, 255, 255, 0.18);
  }
  60% {
    text-shadow: 2px 0 0 rgba(255, 220, 80, 0.4),
      -2px 0 0 rgba(120, 90, 255, 0.4),
      0 0 18px rgba(255, 255, 255, 0.18);
  }
  100% {
    text-shadow: 0 0 18px rgba(255, 255, 255, 0.18),
      0 0 48px rgba(180, 200, 255, 0.08);
  }
}

.wordmark.glitching-a {
  animation: wordmark-glitch-a 0.48s steps(12) 1;
}
.wordmark.glitching-b {
  animation: wordmark-glitch-b 0.26s steps(8) 1;
}
.wordmark.glitching-c {
  animation: wordmark-glitch-c 0.7s ease-out 1;
}

@media (prefers-reduced-motion: reduce) {
  .wordmark.glitching-a,
  .wordmark.glitching-b,
  .wordmark.glitching-c {
    animation: none;
  }
}

.season {
  font-family: "Geist Mono", ui-monospace, "JetBrains Mono", Consolas,
    monospace;
  font-size: clamp(13px, 1.7vw, 18px);
  letter-spacing: 4px;
  color: rgba(246, 248, 252, 0.85);
  margin: 0;
  font-weight: 400;
}

.planet {
  /* Top margin tightened (6vh → 2vh) per Andre's tweak — planet
     sits closer to the subtitle. Bottom is 0 because the byline
     uses its own negative margin-top to overlap the PNG's empty
     bottom padding. */
  margin: 2vh 0 0;
  width: min(82vw, 980px);
  display: flex;
  justify-content: center;
}

.planet img {
  width: 100%;
  height: auto;
  display: block;
  /* `screen` blend kills the rectangle: every near-black pixel in
     the PNG resolves to the body's #000 sky and disappears, while
     white ring highlights stay crisp. Combined with the canvas
     overlay above, stars render OVER the planet too — there's no
     visible image boundary anymore. drop-shadow is intentionally
     omitted: any halo would re-introduce a rectangular footprint. */
  mix-blend-mode: screen;
}

.byline {
  font-family: "Geist Mono", ui-monospace, "JetBrains Mono", Consolas,
    monospace;
  font-size: clamp(13px, 1.6vw, 16px);
  letter-spacing: 1px;
  color: rgba(246, 248, 252, 0.78);
  /* Pull the line up into the planet image's empty bottom padding
     (PNG has ~25-30% dead space below the visible planet), then
     bump it back down two line-heights per Andre's tweak. The
     `lh` unit equals one line-height of the element, so the calc
     is self-consistent if the font-size changes. */
  margin: calc(-10vh + 2lh) auto 0;
  max-width: 720px;
  line-height: 1.55;
}

footer {
  /* Same stacking-context rule as main — no z-index, no positioning.
     The canvas overlays at z-index 10 with screen blend; footer text
     stays in the page flow and remains readable because text colour
     is light grey on #000 page bg, untouched by the overlay. */
  max-width: 1100px;
  margin: 0 auto;
  padding: 6vh 6vw 4vh;
  font-size: 11px;
  letter-spacing: 1px;
  color: rgba(246, 248, 252, 0.4);
  display: flex;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
}

footer a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: rgba(246, 248, 252, 0.2);
}

footer a:hover {
  color: rgba(246, 248, 252, 0.85);
  text-decoration-color: rgba(246, 248, 252, 0.5);
}

footer .dot {
  opacity: 0.5;
}

/* ───────── Newsletter signup ───────── */

/* @property registers the angle as a real animatable angle type so
   the conic-gradient interpolates smoothly. Without it browsers
   treat custom-prop values as opaque strings and skip the
   transition between keyframes — the glow would jump in 1deg
   integer ticks instead of gliding around the pill. */
@property --glow-angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

.signup {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  width: min(340px, 92vw);
  margin: 6vh auto 0;
}

.signup__label {
  font-family: "Geist Mono", ui-monospace, "JetBrains Mono", Consolas,
    monospace;
  font-size: 11px;
  letter-spacing: 4px;
  text-transform: uppercase;
  color: rgba(246, 248, 252, 0.55);
}

/* The visible pill. The cyan ring is NOT a real CSS border — it's a
   conic-gradient painted into ::before, masked down to a 1px outer
   ring via the standard "two masks + composite-exclude" trick. The
   gradient is 95% transparent except for a bright wedge near
   --glow-angle; rotating that custom property animates the wedge
   around the pill, reading as a single point of light running the
   line. */
.signup__field {
  position: relative;
  display: flex;
  align-items: stretch;
  width: 100%;
  border-radius: 999px;
  background: #0a0d12;
  isolation: isolate;
}

/* Outer halo — same wedge, blurred, sits just outside the pill.
   Sells the glow as emissive instead of a flat outline. */
.signup__field::after {
  content: "";
  position: absolute;
  inset: -8px;
  border-radius: inherit;
  background: conic-gradient(
    from var(--glow-angle),
    rgba(255, 255, 255, 0.85) 0deg,
    rgba(255, 255, 255, 0.35) 14deg,
    transparent 50deg,
    transparent 360deg
  );
  filter: blur(14px);
  opacity: 0.55;
  pointer-events: none;
  z-index: 0;
  animation: signup-orbit 4.2s linear infinite;
}

/* The 1px ring itself. The double-mask is the canonical
   "gradient border" trick: two opaque masks (full-area and
   content-box), composite EXCLUDE keeps only the padding region. */
.signup__field::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: conic-gradient(
    from var(--glow-angle),
    rgba(255, 255, 255, 1) 0deg,
    rgba(255, 255, 255, 0.55) 10deg,
    transparent 38deg,
    transparent 360deg
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
  z-index: 1;
  animation: signup-orbit 4.2s linear infinite;
}

@keyframes signup-orbit {
  to {
    --glow-angle: 360deg;
  }
}

/* Speed the orbit up while the field has focus — small reward for
   engaging with it, also signals "you're in". */
.signup__field:focus-within::before,
.signup__field:focus-within::after {
  animation-duration: 2.2s;
}

.signup__input,
.signup__submit {
  position: relative;
  z-index: 2;
  background: transparent;
  border: none;
  outline: none;
  color: #f6f8fc;
  font-family: "Geist Mono", ui-monospace, "JetBrains Mono", Consolas,
    monospace;
}

.signup__input {
  flex: 1;
  min-width: 0;
  font-size: 14px;
  letter-spacing: 2px;
  padding: 16px 4px 16px 22px;
  border-radius: 999px 0 0 999px;
}

.signup__input::placeholder {
  color: rgba(246, 248, 252, 0.28);
  letter-spacing: 1.5px;
}

.signup__input::selection {
  background: rgba(255, 255, 255, 0.25);
}

.signup__submit {
  font-size: 18px;
  color: rgba(255, 255, 255, 0.85);
  padding: 0 22px;
  cursor: pointer;
  border-radius: 0 999px 999px 0;
  transition: color 160ms ease, transform 160ms ease;
}

.signup__submit:hover {
  color: rgb(255, 255, 255);
  transform: translateX(2px);
}

.signup__submit:active {
  transform: translateX(0);
}

.signup__submit:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  transform: none;
}

.signup__status {
  font-family: "Geist Mono", ui-monospace, "JetBrains Mono", Consolas,
    monospace;
  font-size: 11px;
  letter-spacing: 2px;
  color: rgba(246, 248, 252, 0.55);
  min-height: 1.4em;
  margin: 0;
}

.signup__status.is-error {
  color: rgba(255, 110, 130, 0.9);
}

.signup__status.is-success {
  color: rgba(255, 255, 255, 0.9);
}

/* Reduced-motion honoured the same way as the wordmark glitch:
   freeze the gradient at angle 0 — the field still reads as a
   glowing pill, just static. */
@media (prefers-reduced-motion: reduce) {
  .signup__field::before,
  .signup__field::after {
    animation: none;
  }
}

@media (max-width: 600px) {
  .wordmark {
    letter-spacing: 2px;
  }
  main {
    padding: 6vh 5vw 6vh;
  }
  .signup__input {
    font-size: 13px;
    padding: 14px 4px 14px 18px;
  }
  .signup__submit {
    padding: 0 18px;
  }
}
