/* ============================================================
   M.O. SYSTEM — Landing base styles
   ============================================================ */

/* ============================================================
   WAFER FLIGHT OVERLAY — fly into node 0x01 → Wafer
   ============================================================ */
.wf {
  position: fixed;
  inset: 0;
  z-index: 900;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
.wf--on { opacity: 1; visibility: visible; }
.wf--dissolve .wf__mount { opacity: 0; transition: opacity 700ms var(--e-out); }
.wf--dissolve .wf__veil { opacity: 0; transition: opacity 700ms var(--e-out); }
.wf--dissolve .wf__tag { opacity: 0; transition: opacity 400ms var(--e-out); }
.wf__veil {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at 50% 50%, rgba(0,240,200,0.06) 0%, transparent 58%),
    var(--void);
  opacity: 0;
  transition: opacity 460ms var(--e-out);
}
.wf--on .wf__veil { opacity: 1; }
.wf__mount { position: absolute; inset: 0; opacity: 1; transition: opacity 160ms var(--e-out); }

/* HANDOFF HOLD — the rig is mounted and already drawing, but the reader still
   has to see the card it is about to lift out of. While .wf--hold is set the
   veil and the mount stay unpainted, so the live universe (and the tile
   carrying the model) remains on screen. Releasing the hold crossfades the
   mount in over that same tile and only then fades the universe under it. */
.wf--on.wf--hold .wf__veil { opacity: 0; }
/* Snap out on the way in (transition: none) so the empty mount never sits over
   the card for a frame, then let the base rule fade it back in on release. */
.wf--on.wf--hold .wf__mount { opacity: 0; transition: none; }
.wf__mount canvas { display: block; width: 100%; height: 100%; }
.wf__tag {
  position: absolute;
  bottom: var(--s-7);
  left: var(--s-8);
  display: inline-flex;
  align-items: center;
  gap: var(--s-4);
  font-family: var(--ff-mono);
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ghost);
  opacity: 0;
}
.wf--on .wf__tag { animation: wfTag 900ms var(--e-out) 200ms both; }
@keyframes wfTag {
  0% { opacity: 0; transform: translateY(8px); }
  30% { opacity: 1; transform: translateY(0); }
  100% { opacity: 1; transform: translateY(0); }
}
.wf__tagDot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--signal); box-shadow: 0 0 8px var(--signal);
  animation: pulse 1.4s var(--e-in-out) infinite;
}
.wf__tagSep { width: 16px; height: 1px; background: var(--hairline); }
.wf__tagName { color: var(--signal); }

/* fly-out when navigating to a project page */
body.landing-exit .lp { animation: lpFlyOut 380ms var(--e-in-out) forwards; }
body.landing-exit .lp-shell { opacity: 0; transition: opacity 280ms var(--e-out); }
body.landing-exit .universeBg {
  filter: blur(6px) brightness(1.4);
  transform: scale(1.08);
  transition: filter 420ms var(--e-out), transform 420ms var(--e-out);
}
/* Leaving for All Projects. Unlike `landing-exit` this deliberately does
   NOT blur or scale the universe: the field is sorting its tiles into a
   column and that collapse is the transition, so it has to stay sharp and
   visible while the page content fades off the top of it. */
body.lp-toIndex .lp {
  opacity: 0;
  pointer-events: none;
  transition: opacity 420ms var(--e-out);
}
body.lp-toIndex .lp-shell {
  opacity: 0;
  pointer-events: none;
  transition: opacity 300ms var(--e-out);
}
body.lp-toIndex .universeBg { filter: none; }

@keyframes lpFlyOut {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(1.06); }
}

/* ============================================================
   UNIVERSE BG — single, persistent, fixed behind everything.
   The whole site lives on top of the same 3D field.
   ============================================================ */
.universeBg {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: auto;
  transition: filter var(--d-slow) var(--e-out),
              opacity var(--d-slow) var(--e-out);
}
.universeBg .universe { position: absolute; inset: 0; }

.lp {
  position: relative;
  z-index: 1;
  padding-top: 0;
  /* empty areas of .lp pass through to the fixed universe background.
     Interactive children (sections, links, rows) opt back in to pointer events. */
  pointer-events: none;
}
.lp > * { pointer-events: auto; }


/* ============================================================
   TITLE SCREEN — minimalist framed viewport
   The corner reticles mark a discoverable interactive area; pointer
   shields outside the frame absorb mouse events so the universe is
   only reachable INSIDE the frame. The frame geometry (insets,
   shield sizes, corner-column alignment) lives in title.css.
   ============================================================ */
.lp-title {
  position: relative;
  width: 100%;
  pointer-events: none;
}
.lp-title > * { pointer-events: auto; }

/* shield strips — perimeter blockers, transparent visually */
.title__shield {
  position: absolute;
  pointer-events: auto;
  z-index: 2;
}
.title__shield--top   { top: 0; left: 0; right: 0; }
.title__shield--bot   { bottom: 0; left: 0; right: 0; }
.title__shield--left  { left: 0; }
.title__shield--right { right: 0; }

/* ===== FRAME — corner reticles ===== */
.title__frame {
  position: absolute;
  pointer-events: none;
}
.title__corner {
  position: absolute;
  width: 18px; height: 18px;
  border: 1px solid var(--signal);
  opacity: 0.7;
}
.title__corner--tl { top: 0; left: 0;     border-right: 0; border-bottom: 0; }
.title__corner--tr { top: 0; right: 0;    border-left:  0; border-bottom: 0; }
.title__corner--bl { bottom: 0; left: 0;  border-right: 0; border-top:    0; }
.title__corner--br { bottom: 0; right: 0; border-left:  0; border-top:    0; }

/* ===== ASCII M.O. — bottom-left, original styling restored ===== */
.title__wordmark {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-4);
  /* This box is far wider than the glyph grid inside it, and the title stage
     is otherwise pointer-transparent so the visitor can keep flying the
     universe. Catching the pointer here put an invisible dead patch across
     the bottom of the hero where a drag did nothing. The ASCII now reads the
     pointer from a window listener instead, so it lights up without ever
     taking an event off the field behind it. */
  pointer-events: none;
  max-width: 62vw;
}
/* ASCII base styling — matches the original hero ASCII exactly */
.title__wordmark .asciiHero {
  font-family: var(--ff-mono);
  /* Generative ASCII grid, not a ramp rung: the cell size is solved to
     fill the wordmark box, so it is deliberately off-scale. */
  font-size: clamp(6px, 0.68vw, 24px);
  line-height: 1;
  letter-spacing: 0;
  color: var(--ash);
  white-space: pre;
  margin: 0;
  user-select: none;
  text-shadow: 0 0 18px rgba(4, 6, 13, 0.9);
  mix-blend-mode: screen;
  opacity: 0.78;
  /* The box must be the art. `width: 100%` made it as wide as the wordmark
     column while `white-space: pre` kept the glyph grid at its own natural
     width, so the element's rect and the columns it draws disagreed — and the
     component maps the pointer through that rect. Hugging the content makes
     rect and grid the same thing, which is what puts the cursor bubble under
     the actual cursor. */
  width: max-content;
  /* The exit affordance lives on the art itself — see title.css. */
  pointer-events: auto;
}
.title__wordmark .asciiHero i {
  color: var(--signal);
  font-style: normal;
  text-shadow: 0 0 14px var(--signal-glow);
}
/* junction nodes — the hot cores of the letterform lattice */
.title__wordmark .asciiHero b {
  color: #eafffa;
  font-weight: 600;
  text-shadow: 0 0 10px var(--signal), 0 0 24px var(--signal-glow);
}
.title__wordmarkSub {
  display: inline-flex;
  align-items: center;
  gap: var(--s-4);
  font-family: var(--ff-mono);
  font-size: var(--fs-micro);
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ghost);
  padding-top: var(--s-4);
  border-top: 1px solid var(--hairline);
}
.title__wordmarkSubBullet {
  color: var(--signal);
  margin-right: var(--s-3);
}
.title__wordmarkSubSep { color: var(--shadow); }

/* ============================================================
   TITLE · VARIANT 1 (CONSOLE) — center field-guide + Continue cue
   The guide is the OBVIOUS coachmark; it dismisses on first real
   universe interaction (mo:universeInteract → .is-touched).
   ============================================================ */

/* ---- identity stamp · top-left (replaces the dense top rail) ---- */
.title__idTop {
  position: absolute;
  z-index: 3;
  display: flex; flex-direction: column; gap: var(--s-3);
}
.title__idName {
  font-family: var(--ff-mono); font-size: var(--fs-meta);
  letter-spacing: var(--tr-caps); text-transform: uppercase; color: var(--bone);
  display: inline-flex; align-items: center;
}
.title__idBullet { color: var(--signal); margin-right: var(--s-4); font-size: var(--fs-nano); }
.title__idRole {
  font-family: var(--ff-mono); font-size: var(--fs-micro);
  letter-spacing: var(--tr-caps); text-transform: uppercase; color: var(--ghost);
  padding-left: calc(10px + var(--s-4));
}

/* ---- FIELD GUIDE — center coachmark ----
   The OUTER .fieldHint owns positioning (translate-centering) and the
   dismiss (opacity/transform/filter). The INNER wrapper owns the
   cinematic rise+blur entrance. They are deliberately separated: the
   cinRise animation has `forwards` fill, so if it ran on the SAME
   element it would permanently clobber the centering transform AND
   lock opacity:1 (defeating the dismiss). Keeping the animation on the
   inner element leaves the outer's transform/opacity free. */
.fieldHint {
  position: absolute;
  left: 50%; top: 46%;
  transform: translate(-50%, -50%);
  z-index: 3;
  pointer-events: none;
  text-align: center;
  transition: opacity 0.6s var(--e-out), transform 0.7s var(--e-out), filter 0.6s var(--e-out);
}
.fieldHint__inner {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-6);
}
.fieldHint.is-dismissed {
  opacity: 0;
  transform: translate(-50%, calc(-50% - 10px)) scale(0.96);
  filter: blur(3px);
  pointer-events: none;
}
.fieldHint__ring { position: relative; width: 64px; height: 64px; }
.fieldHint__ring::before,
.fieldHint__ring::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 64px; height: 64px; border-radius: 50%;
  border: 1px solid var(--signal);
}
.fieldHint__ring::after { animation: titleRingPulse 2.4s var(--e-out) infinite; }
.fieldHint__ring::before { opacity: 0.25; }
.fieldHint__ring .dot {
  position: absolute; inset: 0; margin: auto;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--signal); box-shadow: 0 0 14px var(--signal);
}
.fieldHint__ring .cross { position: absolute; inset: 0; }
.fieldHint__ring .cross::before,
.fieldHint__ring .cross::after { content: ""; position: absolute; background: var(--signal-glow); }
.fieldHint__ring .cross::before { left: 50%; top: -14px; width: 1px; height: 14px; transform: translateX(-50%); }
.fieldHint__ring .cross::after  { left: 50%; bottom: -14px; width: 1px; height: 14px; transform: translateX(-50%); }
@keyframes titleRingPulse {
  0%   { transform: scale(1);   opacity: 0.9; }
  70%  { transform: scale(2.4); opacity: 0; }
  100% { transform: scale(2.4); opacity: 0; }
}
.fieldHint__lede {
  font-family: var(--ff-mono); font-size: var(--fs-micro); letter-spacing: var(--tr-caps);
  text-transform: uppercase; color: var(--ash);
  display: inline-flex; align-items: center; gap: var(--s-4);
}
.fieldHint__cues { display: flex; align-items: center; gap: var(--s-5); flex-wrap: nowrap; justify-content: center; }
.fieldHint__cue {
  display: inline-flex; align-items: center; gap: var(--s-4);
  font-family: var(--ff-mono); font-size: var(--fs-body);
  letter-spacing: var(--tr-mono); color: var(--ash); white-space: nowrap;
}
.fieldHint__cue b { color: var(--bone); font-weight: 600; }
.fieldHint__sep { width: 1px; height: 22px; background: var(--hairline); }

/* ---- control cluster · bottom-right (Continue + PROCEED) ---- */
.title__ctl {
  position: absolute;
  z-index: 3;
  display: flex; flex-direction: column; align-items: flex-end; gap: var(--s-4);
  pointer-events: auto;
}
.title__proceedRow { display: inline-flex; align-items: center; }
.title__proceedRow .key { transform: scale(1.25); transform-origin: bottom right; }
.title__proceedCap {
  font-family: var(--ff-mono); font-size: var(--fs-nano); letter-spacing: var(--tr-caps);
  text-transform: uppercase; color: var(--ghost);
}
.scrollcue {
  display: inline-flex; align-items: center; gap: var(--s-4);
  font-family: var(--ff-mono); font-size: var(--fs-meta);
  letter-spacing: var(--tr-caps); text-transform: uppercase; color: var(--signal);
}
.scrollcue__txt { text-align: right; line-height: var(--lh-compact); }
.scrollcue__chev {
  display: inline-flex; flex-direction: column; align-items: center; gap: 1px;
  animation: titleBob 1.8s var(--e-in-out) infinite;
}
.scrollcue__chev span {
  width: 8px; height: 8px;
  border-right: 1.5px solid var(--signal);
  border-bottom: 1.5px solid var(--signal);
  transform: rotate(45deg); opacity: 0.9;
}
.scrollcue__chev span:nth-child(2) { opacity: 0.45; margin-top: -4px; }
@keyframes titleBob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(4px); }
}
@media (prefers-reduced-motion: reduce) {
  .scrollcue__chev, .fieldHint__ring::after { animation: none; }
}

/* When the field has been touched, quiet the Continue cue chevron too — the
   visitor has clearly figured out the controls. */
.lp-title.is-touched .scrollcue { color: var(--signal-dim); }

@media (max-width: 860px) {
  .fieldHint__cues { flex-direction: column; gap: var(--s-4); }
  .fieldHint__sep { display: none; }
}

/* ============================================================
   SECTIONS — shared
   Fibonacci-aligned 33-column system: rails at 1, 2, 4, 7, 12, 20 / 33.
   Content sits inside [21.21% .. 100%] for asymmetric editorial layout.
   ============================================================ */
.lp-section {
  position: relative;
  padding: var(--s-10) var(--s-7) var(--s-9);
  max-width: var(--w-canvas);
  margin: 0 auto;
  z-index: 1;
}

/* ============================================================
   UNIVERSE — WebGL infinite project gallery
   ============================================================ */
.universe {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at 50% 40%, rgba(0,240,200,0.04) 0%, transparent 55%),
    radial-gradient(ellipse at 80% 80%, rgba(255,91,59,0.025) 0%, transparent 50%),
    var(--void);
}
.universe__mount {
  position: absolute;
  inset: 0;
  touch-action: pan-y pinch-zoom;
}
body.mo-explore .universe__mount { touch-action: none; }
.universe__mount canvas { display: block; }

/* ============================================================
   Universe hover inspector — "lock-on" targeting frame
   A reticle follows the card's true projected (rotated) shape, with
   an upright Open project label. Fixed-size HTML segments follow
   the four corners through __updateHUD using transforms only;
   tracking does not rewrite SVG geometry or layout coordinates.
   ============================================================ */
.uhud {
  position: absolute;
  inset: 0;                         /* covers the universe mount */
  z-index: 6;
  pointer-events: none;
  overflow: visible;
  contain: layout style;
}
/* Only transforms move. The layer dimensions never follow the card. */
.uhud__lock {position:absolute;inset:0;animation:hudLockIn 240ms var(--e-out) both}
@keyframes hudLockIn {from {opacity:0} to {opacity:1}}
.uhud__segment {position:absolute;left:0;top:0;width:28px;height:1px;background:var(--signal);transform-origin:0 0;will-change:transform}
.uhud__outline {opacity:.22}
.uhud__bk {height:1.5px;box-shadow:0 0 5px var(--signal-glow)}

/* Upright Open project label pinned to the top-left corner. */
.uhud__tab {
  position: absolute;
  left: 0; top: 0;
  will-change: transform;
}
.uhud__tabLabel {
  transform: translate(-1px, calc(-100% - 7px));
  display: flex; align-items: center; gap: 6px;
  padding: 3px 8px;
  background: var(--void);
  border: 1px solid var(--signal-glow);
  box-shadow: 0 0 16px -6px var(--signal-glow);
  font-family: var(--ff-mono);
  font-size: var(--fs-nano);
  letter-spacing: var(--tr-caps-xl);
  color: var(--signal);
  white-space: nowrap;
  animation: hudTabIn 260ms var(--e-out) 60ms both;
}
@keyframes hudTabIn {
  from { opacity: 0; transform: translate(-1px, calc(-100% - 1px)); }
  to   { opacity: 1; transform: translate(-1px, calc(-100% - 7px)); }
}
.uhud__tabDot {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--signal); box-shadow: 0 0 8px var(--signal);
  animation: hudPulse 1.6s var(--e-in-out) infinite;
}
@keyframes hudPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }

@media (prefers-reduced-motion: reduce) {
  .uhud__lock, .uhud__tabLabel, .uhud__tabDot { animation: none !important; opacity: 1 !important; }
  .uhud__lock { transform: none !important; }
  .uhud__tabLabel { transform: translate(-1px, calc(-100% - 7px)); }
}

/* ============================================================
   EXPLORE MODE · mobile universe window
   The overlay is pointer-transparent (touches fall through to the
   universe mount); only its buttons take events. Page scroll is locked
   by the component while it's open.
   ============================================================ */
.xpl {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  display: flex;
  flex-direction: column;
}
.xpl__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: calc(env(safe-area-inset-top, 0px) + 16px) 16px 0;
}
.xpl__tag {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--ff-mono); font-size: var(--fs-micro);
  letter-spacing: var(--tr-caps); text-transform: uppercase;
  color: var(--ash);
}
.xpl__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--signal); box-shadow: 0 0 10px var(--signal-glow);
  animation: xplPulse 2s ease-in-out infinite;
}
@keyframes xplPulse { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .xpl__dot { animation: none; } }
.xpl__close {
  pointer-events: auto;
  font-family: var(--ff-mono); font-size: var(--fs-micro); letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--bone);
  background: rgba(7, 10, 18, 0.72);
  border: 1px solid var(--hairline);
  padding: 12px 18px;
  min-height: 44px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  cursor: pointer;
}
.xpl__close:active { background: var(--signal-ghost); color: var(--signal); }
.xpl__hint {
  margin-top: auto;
  align-self: center;
  font-family: var(--ff-mono); font-size: var(--fs-nano); letter-spacing: var(--tr-caps);
  text-transform: uppercase; color: var(--ghost);
  padding: 8px 14px;
  border: 1px solid var(--hairline);
  background: rgba(7, 10, 18, 0.55);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  margin-bottom: calc(env(safe-area-inset-bottom, 0px) + 20px);
  text-align: center;
}
/* The shell is invisible in explore mode but `.shell > *` (shell.css) re-enables
   pointer events on its children, so the volume button went on hit-testing under
   the overlay's EXIT — and .lp is a z-index:1 stacking context, which traps the
   overlay's z-index:60 BELOW the z-index:50 shell. Tapping EXIT toggled the
   sound and left explore mode open. Chrome that has been faded out must not
   catch the pointer. */
body.mo-explore .lp-shell > *,
body.mo-explore .lp-shell > * > * { pointer-events: none; }

/* explore mode strips the title chrome — the field owns the screen */
body.mo-explore .title__shield,
body.mo-explore .fieldHint,
body.mo-explore .title__wordmark,
body.mo-explore .title__ctl,
body.mo-explore .title__idTop,
body.mo-explore .lp-shell {
  opacity: 0;
  pointer-events: none;
  transition: opacity 300ms var(--e-out);
}

/* ============================================================
   MOBILE LAYOUT
   ============================================================ */
@media (max-width: 700px) {
  /* control cluster: two keycaps fit, the Continue chevron stack doesn't */
  .title__proceedRow .scrollcue { display: none; }
  .title__proceedRow .key { transform: scale(1.05); }
}
