/* ==========================================================================
   INMUSING — style.css
   Five portals (Arrival / The Becoming / The Threshold / Entering / final).
   Each portal is composed from separate pieces (body text, a small label,
   and a clickable eye/cover icon) rather than one flattened scan, so they
   can be laid out independently. Advancing happens by clicking a portal's
   own icon directly. No accent color — everything is ink on the paper
   background, and portal-to-portal transitions are a plain crossfade (see
   .portal below and showPortal in script.js).
   ========================================================================== */

:root {
  --paper: #C2A482;
  --ink: #1A1A1A;
  /* Shared by body's own background (below) and the acquire popover (see
     .acquire-popover) — kept as one variable so the two can never drift
     out of sync. During the sequential portal crossfade (see crossfadeTo in
     script.js), the outgoing portal fades fully transparent before the next
     one starts fading in, briefly exposing body's own background — using
     this color there means that flash reads as the same surface as the
     popover, not a mismatched tan flicker. */
  --modal-bg: #f6e3c4;
}

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
}

body {
  /* Instant, load-independent fallback: paints immediately, before the
     background image (see body::before below) has even started
     downloading, so first paint is never bare-white — and is also what's
     briefly exposed during the sequential portal crossfade (see
     --modal-bg above) if that layer somehow isn't painted yet either. */
  background-color: var(--modal-bg);
  color: var(--ink);
  min-height: 100vh;
  /* 100dvh (dynamic viewport height) tracks the actual visible area as
     mobile browser chrome (address bar, etc.) shows/hides. Layered after
     the 100vh fallback so older browsers still get a value. */
  min-height: 100dvh;
  overflow-x: hidden;
  cursor: auto;
}

/* The background image lives on its own fixed layer rather than as body's
   own background-image. body's box is sized off 100vh/100dvh above, which
   mobile browsers can get wrong right as their address bar shows/hides
   (the box is measured a frame before/after the chrome actually resizes)
   — leaving background-size: cover computed against a stale height and
   gaps at the top/bottom edges. position: fixed + inset: 0 sidesteps that
   entirely: it's pinned straight to the visual viewport every frame, the
   same reliable technique .portal already uses to cover the screen
   exactly. Combined with viewport-fit=cover on the <meta viewport> tag (see
   index.html), this is also what lets the image paint underneath the
   notch/home-indicator safe areas on an iPhone instead of Safari filling
   those strips with its own default background color. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background-image: url("assets/bg/IM_BACKGROUND.jpg");
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* Phones load a much smaller, more compressed copy of the same background
   (960px wide, ~75KB vs. the full 2512px/555KB original) — plenty of
   resolution once it's stretched to cover a phone-width viewport, and a
   meaningful chunk off the page's total weight on mobile data. */
@media (max-width: 700px) {
  body::before {
    background-image: url("assets/bg/IM_BACKGROUND-mobile.jpg");
  }
}

/* ---- portals ------------------------------------------------------- */

.portal {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 6vh 24px;
  /* auto, not hidden: on a short viewport a portal's own content (e.g.
     Portal 2's longer text) can genuinely be taller than the screen and
     needs to scroll on its own merits — this isn't just about the acquire
     popover's extra bottom margin below. */
  overflow-y: auto;

  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  /* The entire portal-to-portal transition: a plain fade. Toggling
     .is-active (see script.js's showPortal) fades the portal leaving out
     and the one arriving in at the same time, since both are driven by
     this one rule. */
  transition: opacity 700ms ease, visibility 700ms;
}

.portal.is-active {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

.portal-inner {
  width: 100%;
  max-width: 480px;
  margin: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;
}

/* ---- portal pieces ------------------------------------------------- */

.portal-text {
  width: 100%;
  max-width: 340px;
  height: auto;
  display: block;
}

.portal-symbol {
  width: 100%;
  max-width: 170px;
  height: auto;
  display: block;
}

/* Wraps Portal 4's symbol so it's clickable (leads to Portal 5) without
   needing its own sizing rule — it just holds .portal-symbol's box as-is. */
.portal-symbol-btn {
  display: block;
}

.portal-cover {
  width: 100%;
  max-width: 132px;
  height: auto;
  display: block;
}

.portal-cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

/* Anchor point for the acquire popover (below) — positioned relative so the
   popover can sit precisely below just the eye, not the "Acquire Here"
   label above it. This eye only opens the popover (confirming is the
   separate eye inside it — see .popover-confirm below and script.js), so
   it stays fully visible throughout rather than disappearing or dimming
   while its own popover is open. */
.acquire-icon-wrap {
  position: relative;
}

.cta-label {
  width: auto;
  height: 22px;
}

.cta-icon img {
  width: 96px;
  height: auto;
  display: block;
}

.portal1-icon img {
  width: 100%;
  max-width: 300px;
  height: auto;
  display: block;
  opacity: 0.7;
  transition: opacity 260ms ease;
}

.portal1-icon:hover img,
.portal1-icon:focus-visible img {
  opacity: 1;
}

/* Any portal's clickable graphic — plain image button, no chrome. Normal
   mouse cursor everywhere; the browser's own pointer/hand cursor is enough
   to signal these are clickable. */
.icon-button {
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  /* Skips the ~300ms wait some mobile browsers impose on a tap while they
     wait to see if it's the first half of a double-tap-to-zoom gesture —
     these buttons are the whole interaction on every portal, so there's
     nothing double-tap-zoom would ever need to win against here. */
  touch-action: manipulation;
}

/* ---- acquire popover ----------------------------------------------------
   No full-page modal/backdrop — just a small popover appearing directly
   below Portal 3's own eye (see .acquire-icon-wrap above), which opens it.
   Confirming (see .popover-confirm/.popover-eye below and script.js) is a
   second, separate eye inside the popover itself. The background is a
   radial gradient rather than a flat fill, so its edges dissolve into the
   paper instead of ending in a hard rectangle. */

.acquire-popover {
  position: absolute;
  left: 50%;
  top: 100%;
  /* Pulled in tighter than .portal-cta's own 14px label-to-eye gap — this
     sits inside a soft gradient fade (unlike the label, which is a plain
     image with a hard edge), so a smaller margin here is what actually
     ends up reading as the same visual distance once the fade is added on
     top of it. Top padding (below) is kept noticeably smaller than the
     sides/bottom for the same reason: so the visible content doesn't sit
     far down inside that fade zone. */
  margin-top: 4px;
  /* Breathing room below, so on short/mobile viewports it isn't flush
     against the very bottom of the page. Purely cosmetic — position:
     absolute means this popover never affects .acquire-cta's own layout
     height, so it can't shift/recenter the page when it opens. */
  margin-bottom: 32px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  text-align: center;
  padding: 14px 52px 32px;
  max-width: min(90vw, 360px);
  /* rgb(246, 227, 196) is --modal-bg (#f6e3c4) spelled out in rgba() —
     gradients can't fade a var()'s alpha directly, so keep this in sync
     with --modal-bg by hand if that value ever changes. Core stops at 0.95
     (not fully solid) so the popover itself reads as translucent even at
     rest, before its own entrance fade (below) is a factor. */
  background-image: radial-gradient(ellipse farthest-side at 50% 50%,
    rgba(246, 227, 196, 0.95) 0%,
    rgba(246, 227, 196, 0.95) 45%,
    rgba(246, 227, 196, 0.82) 65%,
    rgba(246, 227, 196, 0.5) 80%,
    rgba(246, 227, 196, 0.18) 92%,
    rgba(246, 227, 196, 0) 100%);
  z-index: 10;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  /* No vertical slide (there used to be a translateY here) — script.js's
     openModal scrolls this into view as it opens, and any motion of its
     own on top of that read as a small secondary shift once the two
     didn't finish in perfect lockstep. A plain fade has nothing left to
     desync from the scroll. */
  transform: translate(-50%, 0);
  /* Slow, deliberate entrance — noticeably slower than the portal
     crossfade itself. */
  transition: opacity 900ms cubic-bezier(.22, .61, .36, 1),
    visibility 900ms;
}

.acquire-popover.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

.popover-line {
  margin: 0;
  font-family: "JetBrains Mono", "SF Mono", Menlo, monospace;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  line-height: 1.6;
  color: var(--ink);
}

/* Purely decorative — same eye, same size as the one under "Acquire Here"
   (see .cta-icon img above), just echoed here under the popover's own
   text. .acquire-popover's own flex/gap (above) is what stacks and spaces
   it below .popover-line. */
.popover-eye {
  width: 96px;
  height: auto;
  display: block;
}
