/* $BELIEVE — Gate: hold-to-believe ritual + preloader shell.
   DOM is built entirely by js/gate.js; this file only styles it. Colors read
   from CSS custom properties gate.js copies off CFG.colors (--believe-*) so
   there's one source of truth for the palette; the literal hex fallbacks
   here just mirror config.js in case that bridge somehow hasn't run yet. */

#gate {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--believe-bg, #050603);
  color: var(--believe-grey, #6a6f5e);
  pointer-events: auto;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  /* iOS: suppress the long-press callout/magnifier a 1.2s hold would
     otherwise summon over the word/ring. */
  -webkit-touch-callout: none;
  touch-action: none;
  opacity: 1;
  transition: opacity var(--gate-dissolve-ms, 430ms) ease;
}

#gate.is-dissolving {
  opacity: 0;
}

#gate[hidden] {
  display: none;
}

/* Full-bleed white-green pulse for the pass moment. It's first in source
   order (so it doesn't sit in the tab/focus path before the real content),
   which means it needs an explicit z-index to still paint above both view
   sections -- they're positioned too, and later DOM order would otherwise
   win the stacking order. */
.gate__flash {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: radial-gradient(circle at center, #fff, var(--believe-bright, #d4f24b) 55%, transparent 75%);
  opacity: 0;
  pointer-events: none;
}

#gate.is-flashing .gate__flash {
  animation: gateFlash var(--gate-flash-ms, 220ms) ease-out;
}

@keyframes gateFlash {
  0% { opacity: 0; }
  35% { opacity: 1; }
  100% { opacity: 0; }
}

/* Exile static burst: the doubt-static visual language (grey pixel noise
   faintly tinted house-green, chunky/steps -- fx.js's 160x90 canvas look,
   rebuilt as stacked repeating gradients) washing over the swap to the
   verdict card. js/gate.js toggles is-exiling for EXILE_STATIC_MS. */
.gate__static {
  position: absolute;
  inset: 0;
  z-index: 2;
  opacity: 0;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(0deg, rgba(138, 144, 124, 0.55) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, rgba(184, 214, 46, 0.18) 0 2px, transparent 2px 5px),
    repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.16) 0 1px, transparent 1px 2px);
  background-size: 100% 3px, 7px 100%, 100% 2px;
}

#gate.is-exiling .gate__static {
  animation: exileStatic 340ms steps(5, jump-none);
}

@keyframes exileStatic {
  0% { opacity: 0.9; background-position: 0 0, 0 0, 0 0; }
  25% { opacity: 0.5; background-position: 0 -7px, 3px 0, 0 5px; }
  50% { opacity: 0.8; background-position: 0 9px, -5px 0, 0 -3px; }
  75% { opacity: 0.35; background-position: 0 -3px, 2px 0, 0 7px; }
  100% { opacity: 0; background-position: 0 4px, -2px 0, 0 -6px; }
}

.gate__view {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
  padding: 2rem;
  text-align: center;
}

.gate__view[hidden] {
  display: none;
}

.gate__ring {
  width: min(38vmin, 260px);
  aspect-ratio: 1;
  color: inherit;
  /* Placeholder for the Task 9 audio bass-swell: the glow simply intensifies
     with hold progress via --progress (set per rAF tick in gate.js). No
     audio here yet. */
  filter: drop-shadow(0 0 calc(2px + 10px * var(--progress, 0)) currentColor);
}

.gate__ring svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* ---- opening transmission (intro) ----
   js/gate.js adds .is-drawing to the ring wrap the moment the enso svg
   arrives (resting state only -- never during a hold) and .gate--intro to
   #gate at init; both are removed on the first press or once the staging
   completes. The draw animates stroke-dasharray from nothing to the sigil's
   own resting arc (0.861111 on / 0.138889 off, pathLength-normalized), so
   the brush appears to travel the full ~310 degrees and stop at the gap --
   an enso is never closed until a hold closes it. */

.gate__ring.is-drawing #enso-ring {
  animation: ensoDraw 1200ms cubic-bezier(0.5, 0.05, 0.3, 1) both;
}

@keyframes ensoDraw {
  from { stroke-dasharray: 0 1; }
  to { stroke-dasharray: 0.861111 0.138889; }
}

/* The two static ink-texture strokes soak in late, once the main pass is
   most of the way around. No `to` keyframe on purpose: each stroke animates
   to its own attribute opacity (0.5 / 0.65). */
.gate__ring.is-drawing .enso-texture {
  animation: ensoInk 1200ms ease-in both;
}

@keyframes ensoInk {
  from { opacity: 0; }
  55% { opacity: 0; }
}

/* Pre-paint hold: gate.js adds .gate--intro at init and .gate--intro-run
   only on the first painted frame (double rAF). Between the two, the text
   tier hides statically -- no animation is running yet, so without this
   the resting-opacity content would flash for that first frame. Scoped
   with :not(...-run) so that once the show starts, each element's
   underlying computed opacity is its true resting value again (the rise
   keyframes below deliberately have no `to` -- they animate back to that
   underlying value). */
#gate.gate--intro:not(.gate--intro-run) .gate__word,
#gate.gate--intro:not(.gate--intro-run) .gate__tagline,
#gate.gate--intro:not(.gate--intro-run) .gate__instruction,
#gate.gate--intro:not(.gate--intro-run) .gate__nonbeliever {
  opacity: 0;
}

/* Staged rises: tagline, then the HOLD instruction, then the quiet exit.
   No `to` keyframes -- each element rises to its own resting opacity
   (tagline 1, instruction 0.85, nonbeliever 0.85) rather than a hardcoded
   copy of those values. The BELIEVE word itself needs no rise: its
   glyph-scramble (js/scramble.js) is the reveal. */
#gate.gate--intro-run .gate__tagline {
  animation: gateRise 700ms ease-out 850ms both;
}

#gate.gate--intro-run .gate__instruction {
  animation: gateRise 700ms ease-out 1150ms both;
}

#gate.gate--intro-run .gate__nonbeliever {
  animation: gateRise 700ms ease-out 1400ms both;
}

@keyframes gateRise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
}

#gate[data-state='holding'] .gate__ring,
#gate[data-state='passed'] .gate__ring {
  color: var(--believe-green, #b8d62e);
}

#gate[data-state='passed'] .gate__ring {
  color: var(--believe-bright, #d4f24b);
}

/* Task 12 egg 5 (triple shatter): every 3rd consecutive shatter (gate.js's
   own shatterStreak counter, reset on an actual pass) scrambles the enso
   ring's stroke-dasharray via this class for TRIPLE_SHATTER_MESSAGE_MS.
   !important is required to win over setRingProgress()'s own per-tick
   inline style; gate.js re-asserts the real value via setRingProgress()
   the instant this class is removed, so no scramble ever lingers. */
.gate__ring.is-broken-enso #enso-ring {
  stroke-dasharray: 0.05 0.02 0.14 0.01 0.08 0.03 0.19 0.11 0.04 0.02 !important;
}

.gate__word {
  font: 700 clamp(2.5rem, 9vmin, 5.5rem) / 1 'Space Mono', 'Courier New', monospace;
  letter-spacing: 0.08em;
  margin: 0;
  color: var(--believe-green, #b8d62e);
  /* The word ignites as the hold charges -- driven by the same --progress
     custom property gate.js writes per tick for the ring's glow. Exactly
     zero at rest (0px blur), so this costs nothing until a hold begins.
     Progress feedback, not motion -- deliberately outside the
     reduced-motion kill list, like the ring's own glow. */
  text-shadow: 0 0 calc(30px * var(--progress, 0)) color-mix(in srgb, var(--believe-bright, #d4f24b) 65%, transparent);
}

.gate__tagline {
  margin: 0;
  font-size: clamp(0.7rem, 1.6vmin, 0.95rem);
  letter-spacing: 0.22em;
  color: var(--believe-grey, #6a6f5e);
  max-width: 32ch;
  /* Remedy 7 (critic: "red/blue text-shadow shimmer... the only non-green
     hues on the site"): there is no shadow -- the fringes are LCD SUBPIXEL
     antialiasing. Every other line in the gate's text tier carries
     opacity < 1, which puts it on a composited layer with grayscale AA;
     the tagline alone sat at opacity 1 on the root layer, so its small
     letter-spaced glyphs got RGB-fringed subpixel rendering (measured:
     584 red/blue-imbalanced pixels in the tagline rect, 0 in HOLD and the
     nonbeliever link). Promoting it to its own layer forces grayscale AA
     -- zero visual change otherwise, and the intro's gateRise transform
     animation composes fine on top (its keyframes end at the underlying
     computed transform, i.e. exactly this). */
  transform: translateZ(0);
}

.gate__instruction {
  margin: 0;
  font-size: clamp(0.85rem, 2vmin, 1.1rem);
  letter-spacing: 0.35em;
  font-weight: 700;
  opacity: 0.85;
  min-height: 1.2em;
}

#gate[data-state='holding'] .gate__instruction {
  opacity: 0.35;
}

.gate__instruction.is-flicker {
  animation: gateFlicker 220ms steps(2, jump-none) 4;
}

@keyframes gateFlicker {
  0%, 100% { opacity: 0.85; }
  50% { opacity: 0.15; }
}

.gate__nonbeliever {
  appearance: none;
  background: none;
  border: none;
  color: var(--believe-grey, #6a6f5e);
  font: inherit;
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  text-decoration: underline;
  text-underline-offset: 3px;
  /* 0.85 is the AA floor for this grey on the void -- it's an interactive
     control, so it doesn't get the deeper dimming the labels can take. */
  opacity: 0.85;
  cursor: pointer;
  padding: 0.5rem;
  margin-top: 0.75rem;
}

.gate__nonbeliever:hover,
.gate__nonbeliever:focus-visible {
  opacity: 1;
  color: var(--believe-green, #b8d62e);
}

#gate.is-shaking .gate__view--hold {
  animation: gateShake var(--gate-shake-ms, 420ms) ease-in-out;
}

@keyframes gateShake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-8px) rotate(-0.5deg); }
  30% { transform: translateX(7px) rotate(0.5deg); }
  45% { transform: translateX(-6px); }
  60% { transform: translateX(5px); }
  75% { transform: translateX(-3px); }
  90% { transform: translateX(2px); }
}

@media (prefers-reduced-motion: reduce) {
  #gate,
  .gate__flash {
    transition: none !important;
  }
  #gate.is-shaking .gate__view--hold,
  .gate__instruction.is-flicker,
  #gate.is-flashing .gate__flash,
  #gate.is-exiling .gate__static,
  .gate__ring.is-drawing #enso-ring,
  .gate__ring.is-drawing .enso-texture,
  #gate.gate--intro-run .gate__tagline,
  #gate.gate--intro-run .gate__instruction,
  #gate.gate--intro-run .gate__nonbeliever {
    animation: none !important;
  }
}

@media (max-width: 480px) {
  .gate__tagline {
    max-width: 26ch;
  }
}

/* Coarse pointers: the nonbeliever link's text is 0.7rem -- pad it up to a
   real thumb target (~44px) instead of sizing up the deliberately-quiet
   type. */
@media (pointer: coarse) {
  .gate__nonbeliever {
    padding: 0.9rem 1.2rem;
    margin-top: 0.4rem;
  }
}
