/* ══════════════════════════════════════════════════════════════════════════
   PORTAL FX — integration layer for the WebGL 3D (portal-3d.js)

   The 3D only becomes visible when portal-3d.js actually mounts a context; it
   then adds `fx-on` to <html>. Everything that lifts a ground to reveal the
   canvas is scoped under html.fx-on, so with reduced-motion / no-WebGL the
   portal keeps its exact opaque composed backdrop (the CSS fallback).

   Stacking (matches the homepage's rail model):
     body void  →  #fx-ambient (fixed, z0)  →  page content (z1)
   Hero graph slots sit at z0 inside a positioned hero; the hero's real content
   is lifted to z1 so the graph stays behind the copy.
   ══════════════════════════════════════════════════════════════════════════ */

/* the fixed ambient canvas (portal-3d.js also sets these inline; declared here too) */
#fx-ambient { position: fixed; inset: 0; z-index: 0; pointer-events: none; }

/* 3D brand mark: the SVG fallback fills the box until portal-3d.js drops a canvas over it */
.logo3d { position: relative; }
.logo3d canvas { position: absolute; inset: 0; filter: drop-shadow(0 0 4px rgba(207,168,241,.55)); }
.logo3d .horns-fallback { display: block; width: 100%; height: 100%; }

/* lift page shells above the ambient canvas */
html.fx-on .shell,
html.fx-on .auth-wrap,
html.fx-on .wiz { position: relative; z-index: 1; }

/* reveal the ambient canvas through the working grounds: drop the opaque void
   base from the composed backdrop, keep only the glow/lattice as an overlay.
   Panels stay their own translucent rgba(6,6,5,…) so the mist reads as depth. */
html.fx-on .main {
  background:
    radial-gradient(58% 44% at 82% 16%, rgba(163,74,236,.10), transparent 70%),
    radial-gradient(96% 60% at 50% 118%, rgba(207,168,241,.05), transparent 72%),
    repeating-linear-gradient(90deg, rgba(242,239,232,.02) 0 1px, transparent 1px 92px);
}
/* auth + wizard grounds: same treatment (their base bg lives on .auth-wrap/.wiz-body) */
html.fx-on .auth-wrap,
html.fx-on .wiz-body,
html.fx-on .main,
html.fx-on .wiz-main { background-color: transparent; }
/* keep the sidebar/rails legible: mostly-opaque so nav text never fights the mist */
html.fx-on .sidebar { background: rgba(6,6,5,.86); }
html.fx-on .wiz-rail { background: rgba(6,6,5,.82); }
html.fx-on .topbar,
html.fx-on .wiz-foot { background: rgba(6,6,5,.78); }

/* ── hero graph slots ─────────────────────────────────────────────────────
   Markup: a positioned hero contains <div class="fx-graph" data-fx-graph …>.
   The slot fills the hero and sits behind; the hero's content must be z1. */
.fx-graph, .fx-horns {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  overflow: hidden;
}
/* screen-blend brightens the additive glow against the dark grounds so the graph
   reads crisply without a full bloom pass */
.fx-graph canvas, .fx-horns canvas { display: block; width: 100%; height: 100%; mix-blend-mode: screen; filter: brightness(2.05) saturate(1.12); }
#fx-ambient { mix-blend-mode: screen; filter: brightness(1.45); }

/* a hero that hosts a graph slot needs a positioning context + content above */
.fx-host { position: relative; }
.fx-host > *:not(.fx-graph):not(.fx-horns) { position: relative; z-index: 1; }

/* auth: offset the protocol graph to the side of the centred card so the glowing
   core reads clearly in the open void beside the sign-in box (not hidden behind it).
   Mobile has no side room, so there it sits above the card instead. */
.auth-wrap .fx-graph { transform: translateX(28%) scale(1.06); }
@media (max-width: 1000px){ .auth-wrap .fx-graph { transform: translateX(22%) scale(1.05); } }
@media (max-width: 720px){ .auth-wrap .fx-graph { transform: translateY(-22%) scale(1.18); } }
html.fx-on .auth-card {
  background: rgba(9,9,8,.72);
  backdrop-filter: blur(9px) saturate(1.08);
  -webkit-backdrop-filter: blur(9px) saturate(1.08);
}

/* the dashboard quick-audit hero already draws its own scan-rule; push the core
   to the right behind the horns and fade the left so the copy keeps full contrast */
.quick-audit .fx-graph {
  transform: translateX(24%) scale(1.18);
  -webkit-mask-image: linear-gradient(90deg, transparent 6%, #000 58%);
          mask-image: linear-gradient(90deg, transparent 6%, #000 58%);
  opacity: .95;
}
/* empty-state horns slot sizing */
.empty .fx-horns { position: absolute; inset: 0; }

@media (max-width: 720px) {
  /* on phones, keep the graph even quieter behind dense content */
  .fx-graph { opacity: .6; }
}
