/* ---------------------------------------------------------------------------
   The Olympus Gate: the closed temple doors shown on first arrival.

   This file is deliberately NOT part of the Vite CSS bundle. The gate markup
   lives in index.html so it paints on the very first frame, before the app
   bundle is fetched. Bundled CSS arrives too late for that, so these styles
   ship as a plain render-blocking stylesheet instead.

   The gate stays hidden until public/gate-init.js adds `gate-armed` to <html>.
   That script also skips the gate when it has played recently, so moving
   around the site never flashes a door. With JavaScript disabled the class is
   never added and the gate simply never appears.
--------------------------------------------------------------------------- */

:root {
  --gate-wood-fallback: #241608;
  --gate-ember-deep: #ff7a1e;
  --gate-ember-mid: #ff9d3d;
  --gate-ember-hot: #ffe6bd;
  --gate-light-core: #fff4dc;
  --gate-open-ms: 900ms;
  --gate-fade-ms: 260ms;
}

#olympus-gate {
  display: none;
}

/* Deliberately larger than the viewport. The whole gate shakes during the
   open, and if it sat flush to the edges the shake would drag its corners
   inward and flash slivers of the page down the sides. The overscan is sized
   well past the largest shake offset so the seal never breaks. It stays
   centred, so the seam still lands dead centre of the screen. */
.gate-armed #olympus-gate {
  display: block;
  position: fixed;
  inset: -3vmax;
  z-index: 9999;
  overflow: hidden;
  background: transparent;
  contain: strict;
}

/* The blackout behind the doors. It exists only so nothing shows through
   before the doors move, and it clears as soon as they begin to part: the gap
   between the doors has to reveal the actual site, not an empty void. */
.gate-armed #olympus-gate::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background: #0d0a12;
}

/* --- The light behind the doors ------------------------------------------
   The seam column sits UNDERNEATH both door panels. Nothing animates it open:
   as the doors travel apart they simply expose more of a light that was always
   burning there, which is both cheaper and more physically honest than trying
   to grow a beam in step with the doors.
------------------------------------------------------------------------- */

.gate-seam {
  position: absolute;
  top: -6%;
  bottom: -6%;
  left: 50%;
  z-index: 1;
  width: 34vmin;
  transform: translateX(-50%);
  background: radial-gradient(
    ellipse 13% 54% at 50% 50%,
    rgba(255, 236, 196, 0.62),
    rgba(255, 168, 64, 0.26) 32%,
    rgba(255, 120, 30, 0) 70%
  );
  animation: gate-seam-breathe 2.6s ease-in-out infinite;
}

.gate-seam::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 2px;
  transform: translateX(-50%);
  background: linear-gradient(
    to bottom,
    transparent,
    var(--gate-light-core) 13%,
    var(--gate-light-core) 87%,
    transparent
  );
}

/* --- The doors ------------------------------------------------------------
   Both panels are halves of ONE image. Each door box is 50% wide with
   overflow hidden; the wood inside is 200% of that box (a full viewport wide)
   and pinned to the outer edge. The seam therefore falls exactly where the
   source image splits, so no amount of resizing can ever open a hairline gap.
------------------------------------------------------------------------- */

.gate-door {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 2;
  width: 50%;
  overflow: hidden;
  will-change: transform;
}

.gate-door--l {
  left: 0;
  transform-origin: 100% 50%;
}

.gate-door--r {
  right: 0;
  transform-origin: 0 50%;
}

/* Art direction by viewport shape, not device width. A single portrait image
   stretched over a landscape screen crops to a narrow band and stops reading
   as doors at all, so landscape gets its own wide framing. Only one of the two
   is ever fetched; gate-init.js preloads whichever this query will pick. */
.gate-wood {
  position: absolute;
  top: 0;
  height: 100%;
  width: 200%;
  background-color: var(--gate-wood-fallback);
  background-image: url("/assets/gate/doors-tall.webp");
  background-size: cover;
  background-position: center;
}

@media (min-aspect-ratio: 1/1) {
  .gate-wood {
    background-image: url("/assets/gate/doors-wide.webp");
  }
}

.gate-door--l .gate-wood {
  left: 0;
}

.gate-door--r .gate-wood {
  right: 0;
}

/* Warm rim along the inner edge, so the wood nearest the seam catches light. */
.gate-edge {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 10%;
  pointer-events: none;
}

.gate-door--l .gate-edge {
  right: 0;
  background: linear-gradient(to left, rgba(255, 172, 74, 0.3), transparent);
}

.gate-door--r .gate-edge {
  left: 0;
  background: linear-gradient(to right, rgba(255, 172, 74, 0.3), transparent);
}

/* A raking sheen that only sweeps while the door is actually moving. */
.gate-sheen {
  position: absolute;
  inset: -20% -60%;
  opacity: 0;
  pointer-events: none;
  background: linear-gradient(
    104deg,
    transparent 34%,
    rgba(255, 216, 146, 0.26) 48%,
    transparent 63%
  );
}

/* Bloom washes over the doors at the moment they part. */
.gate-bloom {
  position: absolute;
  inset: 0;
  z-index: 3;
  opacity: 0;
  pointer-events: none;
  background: radial-gradient(
    ellipse 42% 62% at 50% 50%,
    rgba(255, 232, 190, 0.92),
    rgba(255, 156, 56, 0.34) 42%,
    transparent 72%
  );
}

/* --- Idle: the charge loop -----------------------------------------------
   These run for as long as the gate is closed. On a warm load that is about
   120ms and nobody consciously sees it; on a slow connection the loop simply
   continues, which is what lets the closed gate double as the loading state.
------------------------------------------------------------------------- */

@keyframes gate-seam-breathe {
  0%,
  100% {
    opacity: 0.62;
  }
  50% {
    opacity: 0.92;
  }
}

/* --- Opening --------------------------------------------------------------
   One timeline, three beats, all keyed in percentages so the whole sequence
   retimes from --gate-open-ms alone:

     0-22%   tremor. The doors hold still and the container shakes.
     22-27%  the choke. Doors press hard shut, the light snuffs out. The burst
             only lands because the frame before it was starved.
     27-73%  the burst. Light detonates, doors blow outward past the edges.
     73-100% the gate fades off the page that was already standing behind it.
------------------------------------------------------------------------- */

/* Two animations, two properties: the quake drives transform, the fade drives
   opacity, so neither clobbers the other. */
.gate-armed #olympus-gate.is-open {
  animation: gate-fade var(--gate-open-ms) linear forwards,
    gate-quake var(--gate-open-ms) linear forwards;
}

/* Camera shake, in three movements.
   0-24%  tremor, amplitude climbing from a whisper to a rattle
   24-27% the choke, everything goes dead still for a beat
   27-45% the impact, hardest hit of the sequence, then decaying
   45-72% settling back to nothing
   Rotation is tiny on purpose. Past about half a degree a full-screen shake
   stops reading as force and starts reading as a broken page. */
@keyframes gate-quake {
  0% { transform: translate3d(0, 0, 0) rotate(0deg); }
  2% { transform: translate3d(-1px, 1px, 0) rotate(0.03deg); }
  4% { transform: translate3d(1px, -1px, 0) rotate(-0.03deg); }
  6% { transform: translate3d(-2px, 1px, 0) rotate(0.05deg); }
  8% { transform: translate3d(2px, -2px, 0) rotate(-0.05deg); }
  10% { transform: translate3d(-3px, 2px, 0) rotate(0.07deg); }
  12% { transform: translate3d(3px, -2px, 0) rotate(-0.07deg); }
  14% { transform: translate3d(-4px, 3px, 0) rotate(0.09deg); }
  16% { transform: translate3d(4px, -3px, 0) rotate(-0.09deg); }
  18% { transform: translate3d(-5px, 3px, 0) rotate(0.11deg); }
  20% { transform: translate3d(6px, -4px, 0) rotate(-0.13deg); }
  22% { transform: translate3d(-7px, 4px, 0) rotate(0.15deg); }
  24% { transform: translate3d(4px, -2px, 0) rotate(-0.07deg); }
  26% { transform: translate3d(0, 0, 0) rotate(0deg) scale(1); }
  29% { transform: translate3d(-15px, 7px, 0) rotate(0.34deg) scale(1.014); }
  32% { transform: translate3d(13px, -8px, 0) rotate(-0.3deg) scale(1.01); }
  35% { transform: translate3d(-11px, 6px, 0) rotate(0.24deg) scale(1.006); }
  38% { transform: translate3d(9px, -5px, 0) rotate(-0.19deg); }
  42% { transform: translate3d(-7px, 4px, 0) rotate(0.14deg); }
  46% { transform: translate3d(5px, -3px, 0) rotate(-0.1deg); }
  50% { transform: translate3d(-4px, 2px, 0) rotate(0.07deg); }
  55% { transform: translate3d(3px, -2px, 0) rotate(-0.05deg); }
  60% { transform: translate3d(-2px, 1px, 0) rotate(0.03deg); }
  66% { transform: translate3d(1px, 0, 0) rotate(-0.01deg); }
  72%,
  100% { transform: translate3d(0, 0, 0) rotate(0deg); }
}

/* Clears just after the doors start moving, so the widening gap shows the
   page rather than black. Invisible until then: the shut doors cover it. */
.gate-armed #olympus-gate.is-open::before {
  animation: gate-void-clear var(--gate-open-ms) forwards;
}

@keyframes gate-void-clear {
  0%,
  27% {
    opacity: 1;
  }
  44%,
  100% {
    opacity: 0;
  }
}

.is-open .gate-door--l {
  animation: gate-door-left var(--gate-open-ms) forwards;
}

.is-open .gate-door--r {
  animation: gate-door-right var(--gate-open-ms) forwards;
}

.is-open .gate-seam {
  animation: gate-seam-fire var(--gate-open-ms) forwards;
}

.is-open .gate-bloom {
  animation: gate-bloom-flash var(--gate-open-ms) forwards;
}

.is-open .gate-sheen {
  animation: gate-sheen-sweep var(--gate-open-ms) forwards;
}

/* The doors hold still through the tremor: the container is doing the shaking,
   so anything here would double up and read as mush. They press hard shut at
   the choke, which is the wind-up, then blow outward. */
@keyframes gate-door-left {
  0%,
  22% {
    transform: translateX(0) scale(1);
    animation-timing-function: cubic-bezier(0.5, 0, 0.9, 0.4);
  }
  27% {
    transform: translateX(1.7%) scale(1);
    animation-timing-function: cubic-bezier(0.62, 0, 0.28, 1);
  }
  73%,
  100% {
    transform: translateX(-102%) scale(1.07);
  }
}

@keyframes gate-door-right {
  0%,
  22% {
    transform: translateX(0) scale(1);
    animation-timing-function: cubic-bezier(0.5, 0, 0.9, 0.4);
  }
  27% {
    transform: translateX(-1.7%) scale(1);
    animation-timing-function: cubic-bezier(0.62, 0, 0.28, 1);
  }
  73%,
  100% {
    transform: translateX(102%) scale(1.07);
  }
}

/* The light is what tells you something is coming. It surges harder with each
   tremor, snuffs almost dead at the choke, then detonates. The near-blackout at
   26% is doing most of the work here: the burst only feels violent because the
   frame before it was starved. */
@keyframes gate-seam-fire {
  0% {
    opacity: 0.5;
    transform: translateX(-50%) scaleX(1);
  }
  6% {
    opacity: 0.78;
    transform: translateX(-50%) scaleX(1.12);
  }
  10% {
    opacity: 0.48;
    transform: translateX(-50%) scaleX(0.94);
  }
  14% {
    opacity: 0.92;
    transform: translateX(-50%) scaleX(1.3);
  }
  18% {
    opacity: 0.55;
    transform: translateX(-50%) scaleX(1.02);
  }
  22% {
    opacity: 1;
    transform: translateX(-50%) scaleX(1.5);
  }
  26% {
    opacity: 0.1;
    transform: translateX(-50%) scaleX(0.34);
  }
  30% {
    opacity: 1;
    transform: translateX(-50%) scaleX(2.7);
  }
  55% {
    opacity: 0.72;
    transform: translateX(-50%) scaleX(3.05);
  }
  100% {
    opacity: 0;
    transform: translateX(-50%) scaleX(3.6);
  }
}

/* The hard core of the seam has to die faster than its halo. Once the doors
   clear, a 2px bright line sitting over the page stops reading as light and
   starts reading as a rendering fault. */
.is-open .gate-seam::after {
  animation: gate-seam-core-out var(--gate-open-ms) forwards;
}

@keyframes gate-seam-core-out {
  0%,
  30% {
    opacity: 1;
  }
  48% {
    opacity: 0.4;
  }
  64%,
  100% {
    opacity: 0;
  }
}

/* Fires on the impact frame, not before: the flash IS the hit. */
@keyframes gate-bloom-flash {
  0%,
  26% {
    opacity: 0;
  }
  30% {
    opacity: 0.92;
  }
  46% {
    opacity: 0.38;
  }
  100% {
    opacity: 0;
  }
}

@keyframes gate-sheen-sweep {
  0%,
  26% {
    opacity: 0;
    transform: translateX(0);
  }
  34% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translateX(38%);
  }
}

@keyframes gate-fade {
  0%,
  78% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

/* --- Reduced motion -------------------------------------------------------
   No doors move at all. The gate is still shown, briefly, then dissolves.
------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .gate-seam,
  .gate-door--l,
  .gate-door--r {
    animation: none !important;
  }

  .gate-armed #olympus-gate.is-open {
    animation: gate-fade-only var(--gate-fade-ms) ease-out forwards;
  }

  .is-open .gate-door--l,
  .is-open .gate-door--r,
  .is-open .gate-seam,
  .is-open .gate-bloom,
  .is-open .gate-sheen {
    animation: none !important;
  }

  /* Nothing parts, so the blackout stays put and crossfades with the gate. */
  .gate-armed #olympus-gate.is-open::before,
  .is-open .gate-seam::after {
    animation: none !important;
  }
}

@keyframes gate-fade-only {
  to {
    opacity: 0;
  }
}
