/* ─────────────────────────────────────────────────────────────
   $SOON — WE PREVAIL
   Acid green on void. Everything glows or it doesn't exist.
   ───────────────────────────────────────────────────────────── */

@font-face {
  font-family: 'Chakra Petch';
  src: url('../assets/fonts/chakra-petch-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Chakra Petch';
  src: url('../assets/fonts/chakra-petch-600-italic.woff2') format('woff2');
  font-weight: 600;
  font-style: italic;
  font-display: swap;
}

:root {
  --void: #04060B;
  --void-lift: #080d14;
  --acid: #8FE320;
  --acid-hot: #C6FF3D;
  --acid-deep: #4b7d10;
  --ink: #d8eec2;
  --ink-dim: #8aa76c;

  --glow-sm: 0 0 6px rgba(143, 227, 32, .55);
  --glow-md: 0 0 18px rgba(143, 227, 32, .45), 0 0 44px rgba(143, 227, 32, .18);
  --glow-lg: 0 0 28px rgba(198, 255, 61, .55), 0 0 80px rgba(143, 227, 32, .28);

  --ui: 'Chakra Petch', ui-sans-serif, system-ui, sans-serif;
  --mono: ui-monospace, 'Cascadia Mono', 'Consolas', 'SFMono-Regular', monospace;

  --gutter: clamp(1.25rem, 4vw, 3rem);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
}

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

html,
body {
  height: 100%;
}

body {
  margin: 0;
  background: var(--void);
  color: var(--ink);
  font-family: var(--ui);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Must outrank id-based display rules below, or `hidden` elements still
   occupy grid tracks and stay in the accessibility tree. */
[hidden] {
  display: none !important;
}

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

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

#stage {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 0;
  opacity: 0;
  transition: opacity 1.2s var(--ease-out);
}

body[data-state='gate'] #stage,
body[data-state='transiting'] #stage,
body[data-state='terminal'] #stage {
  opacity: 1;
}

/* Static hero, used only when WebGL is out or motion is reduced. */
#fallback {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: none;
  background:
    radial-gradient(120% 80% at 50% 45%, rgba(143, 227, 32, .10), transparent 60%),
    url('../assets/img/hero-fallback.jpg') center / cover no-repeat,
    var(--void);
}

body[data-fallback='1'] #fallback {
  display: block;
}

/* The hero art is far brighter than the WebGL scene it replaces, and the
   terminal text sits straight on top of it. Sink the middle so the copy keeps
   its contrast, while the edges of the artwork stay fully visible. */
#fallback::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse 72% 58% at 50% 42%,
    rgba(4, 6, 11, .82),
    rgba(4, 6, 11, .58) 55%,
    rgba(4, 6, 11, .28) 100%
  );
}

body[data-fallback='1'] #stage {
  display: none;
}

/* ─────────────  UI shell  ───────────── */

#ui {
  position: fixed;
  inset: 0;
  z-index: 2;
  display: grid;
  grid-template: 1fr / 1fr;
  place-items: center;
  padding: var(--gutter);
  pointer-events: none;
}

/* Both states share one cell so they cross-fade in place rather than
   stacking into separate rows. */
#gate,
#terminal {
  grid-area: 1 / 1;
}

#ui a,
#ui button {
  pointer-events: auto;
}

/* ─────────────  Wordmark  ───────────── */

.wordmark {
  margin: 0;
  line-height: 0;
}

.wordmark img {
  display: block;
  width: min(52vw, 27rem);
  height: auto;
  filter: drop-shadow(0 0 22px rgba(143, 227, 32, .45)) drop-shadow(0 0 60px rgba(143, 227, 32, .2));
}

.wordmark--sm img {
  width: min(46vw, 20rem);
}

.tagline {
  margin: .5rem 0 0;
  font-style: italic;
  font-size: clamp(.7rem, 1.6vw, .95rem);
  letter-spacing: .62em;
  text-indent: .62em;
  color: var(--acid);
  text-shadow: var(--glow-sm);
}

.tagline--sm {
  font-size: clamp(.6rem, 1.3vw, .75rem);
  opacity: .85;
}

/* ─────────────  STATE A — the gate  ───────────── */

/* Wordmark up top, ENTER down low — the portal owns the middle of the frame. */
#gate {
  display: grid;
  grid-template-rows: auto 1fr;
  align-self: stretch;
  justify-self: stretch;
  justify-items: center;
  padding: 4vh 0 0;
  text-align: center;
  transition: opacity .5s var(--ease-out), transform .9s var(--ease-out), filter .9s var(--ease-out);
}

.gate-inner {
  display: grid;
  place-items: center;
  align-self: start;
}

body[data-state='transiting'] #gate,
body[data-state='terminal'] #gate {
  opacity: 0;
  transform: scale(1.35);
  filter: blur(6px);
  pointer-events: none;
}

/* The 3D wordmark takes over once it has loaded. The markup stays for screen
   readers and for the no-WebGL fallback, so hide it visually rather than
   removing it from the accessibility tree. */
body[data-wordmark3d='1'] .gate-inner,
body[data-wordmark3d='1'] .terminal-head {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* There is no ENTER button any more — the whole gate is the target, so the
   hint is the only affordance and has to read as one. */
.hint {
  position: fixed;
  left: 0;
  right: 0;
  bottom: calc(var(--gutter) * 2 + env(safe-area-inset-bottom, 0px));
  margin: 0;
  text-align: center;
  font-size: .78rem;
  letter-spacing: .44em;
  text-indent: .44em;
  text-transform: uppercase;
  color: var(--acid);
  text-shadow: var(--glow-sm);
  animation: breathe 3.4s ease-in-out infinite;
}

body[data-state='transiting'] .hint,
body[data-state='terminal'] .hint {
  opacity: 0;
  transition: opacity .3s;
}

@keyframes breathe {

  0%,
  100% {
    opacity: .42;
  }

  50% {
    opacity: .75;
  }
}

/* ─────────────  STATE B — the terminal  ───────────── */

#terminal {
  display: grid;
  justify-items: center;
  /* Positioned so ::before below anchors to this panel and not to #ui — an
     unpositioned parent silently made that backing cover the whole viewport. */
  position: relative;
  /* Sits low in the frame: the 3D wordmark owns the top, the portal owns the
     middle, and this keeps clear of both.
     `safe` matters: bottom-aligned overflow pushes the FIRST items out of the
     scroll box, which hid the ticker and contract until the X feed gave its
     space back. `safe` falls back to top alignment rather than losing them. */
  align-self: safe end;
  align-content: safe end;
  gap: clamp(1.1rem, 2.6vh, 1.9rem);
  width: min(100%, 46rem);
  max-height: 68%;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Sits right down at the bottom so the portal reads in full above it. */
  padding: clamp(1rem, 4vh, 2.5rem) 0 clamp(.75rem, 2.5vh, 1.75rem);
  text-align: center;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .8s var(--ease-out) .1s, transform .8s var(--ease-out) .1s;
  scrollbar-width: thin;
  scrollbar-color: rgba(143, 227, 32, .22) transparent;
}

body[data-state='terminal'] #terminal {
  opacity: 1;
  transform: none;
}

/* No scrim of any kind — not over the frame, and not over the panel either.
   A panel-sized rectangle behind the text left its own box edge visible as a
   hard seam straight across the middle of the scene. Contrast is earned per
   block instead; see .ticker, .ca-label, .ca-button and .buy. */

.terminal-head {
  display: grid;
  justify-items: center;
}

/* Ticker */

.ticker {
  display: flex;
  gap: clamp(1.2rem, 5vw, 3rem);
  margin: 0;
  padding: .7rem clamp(1rem, 4vw, 2rem);
  border-top: 1px solid rgba(143, 227, 32, .18);
  border-bottom: 1px solid rgba(143, 227, 32, .18);
  /* Filled band between the two rules it already had, faded out at both ends
     so it has no hard vertical edges against the scene. */
  background: linear-gradient(90deg,
    rgba(4, 6, 11, 0),
    rgba(4, 6, 11, .84) 10%,
    rgba(4, 6, 11, .84) 90%,
    rgba(4, 6, 11, 0));
}

.ticker-cell {
  display: grid;
  gap: .25rem;
}

.ticker dt {
  font-size: .58rem;
  letter-spacing: .3em;
  text-indent: .3em;
  color: var(--ink-dim);
  /* Dark halo rather than a box — dim small caps over the vortex need the
     contrast, and this leaves no edge behind. */
  text-shadow: 0 0 9px rgba(4, 6, 11, .95), 0 0 20px rgba(4, 6, 11, .85);
}

.ticker dd {
  margin: 0;
  font-family: var(--mono);
  font-size: clamp(.85rem, 2.2vw, 1.05rem);
  color: var(--acid-hot);
  text-shadow: var(--glow-sm);
  font-variant-numeric: tabular-nums;
}

.ticker dd[data-dir='up'] {
  color: var(--acid-hot);
}

.ticker dd[data-dir='down'] {
  color: #ff5f5f;
  text-shadow: 0 0 8px rgba(255, 95, 95, .5);
}

/* Contract address */

.ca {
  display: grid;
  gap: .5rem;
  justify-items: center;
  width: 100%;
}

.ca-label {
  font-size: .58rem;
  letter-spacing: .34em;
  text-indent: .34em;
  color: var(--ink-dim);
  text-shadow: 0 0 9px rgba(4, 6, 11, .95), 0 0 20px rgba(4, 6, 11, .85);
}

.ca-button {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: .9rem;
  max-width: 100%;
  padding: .7em 1.1em;
  /* Opaque enough to carry a 42-character address over the vortex. */
  background: rgba(4, 6, 11, .76);
  border: 1px solid rgba(143, 227, 32, .28);
  color: var(--ink);
  font-family: var(--mono);
  font-size: clamp(.62rem, 1.9vw, .88rem);
  letter-spacing: .02em;
  cursor: pointer;
  transition: border-color .25s, background .25s, box-shadow .25s, color .25s;
}

.ca-button:hover,
.ca-button:focus-visible {
  border-color: var(--acid);
  background: rgba(14, 30, 6, .82);
  box-shadow: var(--glow-sm);
  color: #eaffc0;
  outline: none;
}

.ca-short {
  display: none;
}

/* The COPY label is gone everywhere — the address is what people need to read,
   and the box itself is the control. It stays in the markup because the copy
   handler writes its state there, and #ca-live announces it to screen readers.
   Sighted feedback is the glow below. */
.ca-state {
  display: none;
}

/* Copied: the control inverts to a solid acid slab for a beat, kicks slightly
   larger, and throws a wide ring outward. Deliberately loud — with no label
   left to change, this is the only thing telling you it worked. */
.ca-button[data-copied='1'] {
  border-color: var(--acid-hot);
  background: rgba(26, 58, 6, .92);
  color: #f2ffd6;
  text-shadow: var(--glow-md);
  animation: ca-copied 1.15s var(--ease-out);
}

@keyframes ca-copied {
  0% {
    transform: scale(1);
    background: rgba(198, 255, 61, .96);
    color: #06110a;
    text-shadow: none;
    box-shadow:
      0 0 0 0 rgba(198, 255, 61, .95),
      0 0 26px rgba(198, 255, 61, .9),
      inset 0 0 0 rgba(198, 255, 61, 0);
  }

  14% {
    transform: scale(1.055);
    background: rgba(178, 245, 50, .92);
    color: #06110a;
    text-shadow: none;
  }

  55% {
    transform: scale(1.012);
    box-shadow:
      0 0 0 30px rgba(198, 255, 61, 0),
      0 0 78px rgba(143, 227, 32, 1),
      inset 0 0 42px rgba(198, 255, 61, .55);
  }

  100% {
    transform: scale(1);
    box-shadow:
      0 0 0 0 rgba(198, 255, 61, 0),
      0 0 20px rgba(143, 227, 32, .45),
      inset 0 0 0 rgba(198, 255, 61, 0);
  }
}

/* Copy refused by the browser — say so in red rather than pretending. */
.ca-button[data-copied='0'] {
  border-color: #ff5f5f;
  color: #ffd9d9;
  animation: ca-failed .85s var(--ease-out);
}

@keyframes ca-failed {
  0% {
    box-shadow: 0 0 0 0 rgba(255, 95, 95, .55);
  }

  45% {
    box-shadow: 0 0 0 12px rgba(255, 95, 95, 0), 0 0 30px rgba(255, 95, 95, .7);
  }

  100% {
    box-shadow: 0 0 0 0 rgba(255, 95, 95, 0), 0 0 12px rgba(255, 95, 95, .25);
  }
}

/* Page-wide pulse on copy */
body.copied::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background: radial-gradient(circle at 50% 62%, rgba(143, 227, 32, .3), transparent 72%);
  animation: pulse .85s var(--ease-out) forwards;
}

@keyframes pulse {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

/* Buy */

.buy {
  position: relative;
  display: inline-block;
  padding: 1em 3.4em;
  /* Dark base under the acid tint, so the label holds up over the portal. */
  background:
    linear-gradient(180deg, rgba(143, 227, 32, .18), rgba(143, 227, 32, .07)),
    rgba(4, 6, 11, .78);
  border: 1px solid var(--acid);
  color: var(--acid-hot);
  font-size: clamp(.9rem, 2.2vw, 1.1rem);
  font-style: italic;
  font-weight: 600;
  letter-spacing: .3em;
  text-indent: .3em;
  text-decoration: none;
  text-shadow: var(--glow-sm);
  box-shadow: var(--glow-md), inset 0 0 30px rgba(143, 227, 32, .1);
  transition: transform .25s var(--ease-out), box-shadow .25s, background .25s;
}

.buy:hover,
.buy:focus-visible {
  transform: translateY(-2px);
  background: linear-gradient(180deg, rgba(143, 227, 32, .26), rgba(143, 227, 32, .1));
  box-shadow: var(--glow-lg), inset 0 0 40px rgba(143, 227, 32, .16);
  outline: none;
}

/* Secondary links */

.links {
  display: flex;
  gap: 1.4rem;
}

.links a {
  display: grid;
  place-items: center;
  width: 2.6rem;
  height: 2.6rem;
  border: 1px solid rgba(143, 227, 32, .22);
  color: var(--acid);
  transition: color .25s, border-color .25s, box-shadow .25s, transform .25s var(--ease-out);
}

.links svg {
  width: 1.1rem;
  height: 1.1rem;
  fill: currentColor;
}

.links a:hover,
.links a:focus-visible {
  color: var(--acid-hot);
  border-color: var(--acid);
  box-shadow: var(--glow-sm);
  transform: translateY(-2px);
  outline: none;
}

/* ─────────────  Loading  ───────────── */

body[data-state='loading'] #gate {
  opacity: 0;
}

body[data-state='loading'] .hint {
  opacity: 0;
}

/* ─────────────  Mobile  ───────────── */

@media (max-width: 34rem) {
  /* The whole address on phones, on one row, in a box no taller than before.
     A truncated contract is useless to read and untrustworthy to look at, so
     the COPY label gives up its space to fit the real thing — the box itself
     is still the copy control. Type is sized in vw so all 42 characters stay
     on a single line down to the narrowest phones. */
  .ca {
    width: 100%;
  }

  .ca-button {
    gap: 0;
    width: 100%;
    padding: .7em .55em;
    font-size: min(3.1vw, .78rem);
  }

  .ca-full {
    display: block;
    width: 100%;
    white-space: nowrap;
  }

  .ca-short {
    display: none;
  }

  .buy {
    padding: 1em 2.4em;
    width: 100%;
    text-align: center;
  }

  .links a {
    width: 3rem;
    height: 3rem;
  }

}

/* Short viewports — protect the primary actions. */
@media (max-height: 44rem) {
  /* Tighten up so the terminal fits without scrolling at all. */
  #terminal {
    gap: .85rem;
    padding: .5rem 0;
  }

  .wordmark--sm img {
    width: min(38vw, 15rem);
  }

  .ticker {
    padding: .45rem 1rem;
  }

  .buy {
    padding: .8em 2.8em;
  }
}

/* Thin, unobtrusive scrollbars in WebKit too. */
#terminal::-webkit-scrollbar {
  width: 6px;
}

#terminal::-webkit-scrollbar-track {
  background: transparent;
}

#terminal::-webkit-scrollbar-thumb {
  background: rgba(143, 227, 32, .22);
  border-radius: 3px;
}

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

@media (prefers-reduced-motion: reduce) {

  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }

  #gate {
    transform: none !important;
    filter: none !important;
  }
}
