/* ── @property for animatable gradient angle ── */
@property --gradient-angle {
    syntax: '<angle>';
    initial-value: 0deg;
    inherits: false;
}
@property --gradient-angle-2 {
    syntax: '<angle>';
    initial-value: 180deg;
    inherits: false;
}

/* ── Theme vars (Forerunner) ── */
:root {
    --bg-primary: #0a0e14;
    --bg-secondary: #141920;
    --cyan-bright: #00e5ff;
    --cyan-medium: #00b8d4;
    --cyan-glow: rgba(0, 229, 255, 0.4);
    --orange-bright: #ff9500;
    --orange-medium: #e67e00;
    --orange-glow: rgba(255, 149, 0, 0.3);
    --green-bright: #00e676;
    --green-glow: rgba(0, 230, 118, 0.4);
    --text-primary: #e8f0f7;
    --text-secondary: #8fa3b8;
    --font-primary: 'Exo 2', sans-serif;
    --font-mono: 'JetBrains Mono', monospace;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
    font-family: var(--font-primary);
    background: var(--bg-primary);
    color: var(--text-primary);
    min-height: 100dvh;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
    overflow: hidden;
    touch-action: none;
}

body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 0;
    opacity: 0.05;
    background-image: url('data:image/svg+xml;utf8,<svg width="100" height="87" xmlns="http://www.w3.org/2000/svg"><polygon points="50,10 90,30 90,70 50,90 10,70 10,30" fill="none" stroke="%2300b8d4" stroke-width="1"/></svg>');
    background-size: 100px 87px;
    pointer-events: none;
}

/* ── Camera feed — phone mode only ── */
#camera-video {
    display: none;
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100dvh;
    height: 100vh;
    object-fit: cover;
    z-index: 0;
    background: #000;
}
#sample-canvas { display: none; }

/* XR8 owns the camera feed and renders it onto the #ar-three canvas —
   our original <video> becomes a silent hidden element kept around only
   to satisfy existing references. */
body[data-mode="phone"] #camera-video { display: none; }
body[data-mode="phone"]::before { display: none; }

/* ── Forerunner icon ── */
.forerunner-icon {
    width: min(60vmin, 420px);
    height: min(60vmin, 420px);
    position: relative;
    z-index: 2;
    --gradient-angle: 0deg;
    --gradient-angle-2: 180deg;
    --spin-angle: 0deg;
    --counter-angle: 0deg;
}

/* Outer ring, inner hex, and core hidden — only the orbiting satellites remain. */
.forerunner-icon::before,
.forerunner-icon::after,
.core {
    display: none;
}

/* ── Satellite targets around the ring ── */
/* Targets live in a viewport-fixed container so left/top are viewport pixels. */
.targets {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 4;
}
.target {
    position: absolute;
    width: 32px; height: 32px;
    margin: -16px 0 0 -16px; /* center via margin rather than transform */
    border-radius: 50%;
    background: hsl(var(--hue, 180), 95%, 55%);
    box-shadow:
        0 0 10px hsl(var(--hue, 180), 95%, 55%),
        0 0 20px hsla(var(--hue, 180), 95%, 55%, 0.35);
    border: none;
    transition: transform 0.25s ease;
    z-index: 4;
}
.target::before {
    content: '';
    position: absolute;
    inset: -10px;
    border-radius: 50%;
    border: 2px solid var(--status-color, transparent);
    box-shadow: 0 0 14px var(--status-color, transparent);
    opacity: var(--status-opacity, 0);
    transition: border-color 0.2s ease, box-shadow 0.25s ease, opacity 0.25s ease;
}
/* On desktop, hide the status ring — it pollutes the camera-captured blob
   with an off-hue halo. The phone still renders its own status ring. */
body[data-mode="desktop"] .target::before { display: none; }
body[data-mode="desktop"] .target[data-status="locked"] {
    transform: scale(1.15);
}
body[data-mode="phone"] .target[data-status="locked"] {
    /* No scale pop on phone — keeps the user's aim stable as targets lock. */
    border-color: var(--green-bright);
}
/* Phone mode: slightly smaller hollow reticles, higher opacity, for aim assist */
body[data-mode="phone"] .target {
    width: 40px; height: 40px;
    margin: -20px 0 0 -20px;
    background: transparent;
    border: 2px solid rgba(255, 255, 255, 0.85);
    box-shadow: 0 0 6px rgba(0, 0, 0, 0.6) inset, 0 0 4px rgba(0, 0, 0, 0.6);
    transition: border-color 0.2s ease; /* override parent transition — no transform */
}
body[data-mode="phone"] .target::after {
    content: '';
    position: absolute;
    top: 50%; left: 50%;
    width: 6px; height: 6px;
    margin: -3px 0 0 -3px;
    background: hsl(var(--hue, 180), 95%, 55%);
    border-radius: 50%;
    box-shadow: 0 0 6px hsl(var(--hue, 180), 95%, 55%);
}

/* Phone mode: larger icon so reticles reach nearer the edges; no fade. */
body[data-mode="phone"] .forerunner-icon {
    width: min(80vmin, 520px);
    height: min(80vmin, 520px);
}

/* ── Desktop info panel ── */
.panel {
    position: fixed;
    bottom: 24px;
    right: 24px;
    background: rgba(20, 35, 50, 0.7);
    backdrop-filter: blur(8px);
    border: 1px solid rgba(0, 180, 212, 0.3);
    padding: 16px;
    z-index: 5;
    max-width: 260px;
    clip-path: polygon(8px 0, calc(100% - 8px) 0, 100% 8px, 100% calc(100% - 8px), calc(100% - 8px) 100%, 8px 100%, 0 calc(100% - 8px), 0 8px);
    display: none;
}
body[data-mode="desktop"] #desktop-panel { display: block; }

.panel-title {
    color: var(--cyan-bright);
    font-weight: 600;
    letter-spacing: 2px;
    text-transform: uppercase;
    font-size: 0.9rem;
    margin-bottom: 4px;
}
.panel-sub {
    color: var(--text-secondary);
    font-size: 0.75rem;
    margin-bottom: 12px;
    line-height: 1.3;
}
#qr-canvas {
    display: block;
    width: 100%;
    height: auto;
    background: #fff;
    image-rendering: pixelated;
}
.panel-code {
    color: var(--cyan-bright);
    font-family: var(--font-mono);
    font-weight: 600;
    letter-spacing: 3px;
    text-align: center;
    font-size: 1rem;
    margin-top: 8px;
}

/* Hide desktop panel once phone connects */
body[data-state="locked"] #desktop-panel,
body[data-state="syncing"] #desktop-panel {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.6s ease;
}

/* ── Phone panel: reticle + hint ── */
.phone-panel {
    display: none;
    position: fixed;
    inset: 0;
    background: none;
    border: none;
    padding: 0;
    clip-path: none;
    pointer-events: none;
    max-width: none;
    z-index: 3;
    /* Critical: do NOT inherit the panel's backdrop blur, or the whole camera
       feed behind this full-viewport element gets blurred. */
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}
body[data-mode="phone"] #phone-panel { display: block; }

.reticle { display: none; } /* center reticle retired; satellites are the reticles now */
.phone-hint {
    position: absolute;
    top: calc(50% + 50px);
    left: 50%;
    transform: translateX(-50%);
    font-size: 0.8rem;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.85);
    text-shadow: 0 2px 8px rgba(0, 0, 0, 0.8);
}

/* ── HUD ── */
.hud {
    position: fixed;
    top: 24px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    z-index: 6;
    pointer-events: none;
}
.hud-status {
    font-size: 0.75rem;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--text-secondary);
    font-family: var(--font-mono);
    padding: 6px 12px;
    background: rgba(10, 20, 35, 0.6);
    backdrop-filter: blur(6px);
    border: 1px solid rgba(0, 180, 212, 0.25);
}
.hud-lock {
    font-size: 1rem;
    letter-spacing: 4px;
    color: var(--green-bright);
    text-shadow: 0 0 12px var(--green-glow);
    opacity: 0;
    transform: translateY(-8px);
    transition: opacity 0.4s ease, transform 0.4s ease;
}
body[data-state="locked"] .hud-lock {
    opacity: 1;
    transform: translateY(0);
}

/* ── Locked-state amplification: desktop only; phone stays visually calm ── */
body[data-mode="desktop"][data-state="locked"] .target {
    animation: lockedSatellitePulse 0.85s ease-in-out infinite alternate;
}
@keyframes lockedSatellitePulse {
    from {
        box-shadow:
            0 0 14px hsl(var(--hue, 180), 95%, 55%),
            0 0 28px hsla(var(--hue, 180), 95%, 55%, 0.55);
    }
    to {
        box-shadow:
            0 0 22px hsl(var(--hue, 180), 95%, 55%),
            0 0 44px hsla(var(--hue, 180), 95%, 55%, 0.85);
    }
}

body[data-mode="desktop"][data-charge="on"] .target {
    animation: lockedChargePulse 0.55s ease-in-out infinite alternate;
}
body[data-mode="desktop"][data-charge="on"] .target::before {
    display: block;
    border-color: var(--green-bright, #00e676);
    box-shadow: 0 0 14px var(--green-bright, #00e676);
    animation: chargeRingClose 0.9s ease-in infinite;
    opacity: 0;
}
@keyframes lockedChargePulse {
    from {
        transform: scale(1.12);
        box-shadow:
            0 0 18px hsl(var(--hue, 180), 95%, 55%),
            0 0 36px hsla(var(--hue, 180), 95%, 55%, 0.7);
    }
    to {
        transform: scale(1.32);
        box-shadow:
            0 0 30px hsl(var(--hue, 180), 95%, 55%),
            0 0 60px hsla(var(--hue, 180), 95%, 55%, 1);
    }
}
@keyframes chargeRingClose {
    0%   { inset: -36px; opacity: 0; border-width: 1px; }
    40%  { opacity: 1; }
    100% { inset: -2px; opacity: 0; border-width: 3px; }
}

body[data-mode="desktop"][data-charge="snap"] .target {
    animation: finalLockSnap 0.3s cubic-bezier(0.2, 0.9, 0.3, 1) forwards;
}
body[data-mode="desktop"][data-charge="snap"] .target::before {
    display: block;
    border-color: var(--green-bright, #00e676);
    animation: finalLockBurst 0.3s ease-out forwards;
}
@keyframes finalLockSnap {
    0% {
        transform: scale(1.30);
        background: hsl(var(--hue, 180), 95%, 55%);
        box-shadow:
            0 0 30px hsl(var(--hue, 180), 95%, 55%),
            0 0 60px hsla(var(--hue, 180), 95%, 55%, 1);
    }
    40% {
        transform: scale(1.65);
        background: #fff;
        box-shadow:
            0 0 60px #fff,
            0 0 100px var(--green-bright, #00e676);
    }
    100% {
        transform: scale(1.15);
        background: hsl(var(--hue, 180), 95%, 55%);
        box-shadow:
            0 0 22px hsl(var(--hue, 180), 95%, 55%),
            0 0 44px hsla(var(--hue, 180), 95%, 55%, 0.85);
    }
}
@keyframes finalLockBurst {
    0%   { inset: -2px; opacity: 1; border-width: 3px; }
    100% { inset: -28px; opacity: 0; border-width: 1px; }
}

/* ── Core Reaction ── */
body[data-mode="desktop"][data-charge="snap"] .core {
    display: block;
    position: absolute;
    top: 50%; left: 50%;
    width: 60px; height: 60px;
    margin: -30px 0 0 -30px;
    background: #fff;
    border-radius: 50%;
    filter: blur(2px);
    box-shadow:
        0 0 40px #fff,
        0 0 80px var(--green-bright, #00e676);
    animation: coreSnapBurst 0.3s ease-out forwards;
    z-index: 5;
}
@keyframes coreSnapBurst {
    0%   { transform: scale(0); opacity: 0; }
    20%  { transform: scale(1.2); opacity: 1; }
    100% { transform: scale(2.2); opacity: 0; }
}

/* ── Choreography FX — cyan so nothing competes with the magenta satellites ── */
.choreo-ring {
    position: absolute;
    width: 20px; height: 20px;
    margin: -10px 0 0 -10px;
    border-radius: 50%;
    border: 2px solid var(--cyan-bright);
    box-shadow:
        0 0 18px var(--cyan-glow),
        0 0 40px rgba(0, 229, 255, 0.25),
        inset 0 0 18px var(--cyan-glow);
    pointer-events: none;
    animation: choreoRingExpand 1.4s cubic-bezier(0.22, 1, 0.36, 1) forwards;
    will-change: transform, opacity;
}
@keyframes choreoRingExpand {
    0%   { transform: scale(0.3); opacity: 0; border-width: 3px; }
    15%  { opacity: 0.95; }
    100% { transform: scale(40); opacity: 0; border-width: 1px; }
}
.choreo-flash {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: radial-gradient(circle at center,
        rgba(0, 229, 255, 0.55) 0%,
        rgba(0, 229, 255, 0.15) 35%,
        transparent 70%);
    animation: choreoFlash 0.9s ease-out forwards;
    will-change: opacity;
    mix-blend-mode: screen;
}
@keyframes choreoFlash {
    0%   { opacity: 0; }
    20%  { opacity: 1; }
    100% { opacity: 0; }
}

/* Keep the satellites glowing a bit harder during the dance — no hue change. */
body[data-choreo="running"] .target {
    box-shadow:
        0 0 14px hsl(var(--hue, 300), 95%, 55%),
        0 0 32px hsla(var(--hue, 300), 95%, 55%, 0.55);
}

/* ── Data packet layer ── */
.packet-layer {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 7;
    overflow: hidden;
}
.packet {
    position: absolute;
    width: 10px; height: 10px;
    border-radius: 50%;
    background: hsl(var(--hue, 180), 95%, 60%);
    box-shadow:
        0 0 12px hsl(var(--hue, 180), 95%, 60%),
        0 0 22px hsla(var(--hue, 180), 95%, 60%, 0.6),
        0 0 4px #fff inset;
    left: 50%; top: 50%;
    margin: -5px 0 0 -5px;
    --dx: 0px;
    --dy: 0px;
    --rot: 0turn;
    animation: packetFly var(--dur, 1200ms) cubic-bezier(0.2, 0.8, 0.3, 1) forwards;
    will-change: transform, opacity;
}
@keyframes packetFly {
    0%   { transform: translate(0, 0) rotate(0deg) scale(0.6); opacity: 0; }
    12%  { opacity: 1; }
    100% {
        transform: translate(var(--dx), var(--dy)) rotate(calc(var(--rot) * 360deg)) scale(1.1);
        opacity: 0;
    }
}
/* Packet trail */
.packet::after {
    content: '';
    position: absolute;
    top: 50%; left: 50%;
    width: 50px; height: 2px;
    transform: translate(-100%, -50%);
    background: linear-gradient(90deg, transparent, hsla(var(--hue, 180), 95%, 60%, 0.8));
    border-radius: 2px;
    filter: blur(0.5px);
    opacity: 0.9;
}

/* ── Debug: live dots showing every blob the detector currently sees ── */
.blob-debug {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 2;
    display: none;
}
body[data-mode="phone"] .blob-debug { display: block; }
.blob-dot {
    position: absolute;
    width: 14px; height: 14px;
    margin: -7px 0 0 -7px;
    border-radius: 50%;
    background: rgba(255, 255, 0, 0.85);
    box-shadow: 0 0 6px rgba(255, 255, 0, 0.9), 0 0 0 2px rgba(0, 0, 0, 0.6);
    transition: left 80ms linear, top 80ms linear;
}

/* ── XR8 main canvas (phone only). 8th Wall renders the camera feed and
   Three.js scene onto this canvas via its pipeline modules. Sits at the
   bottom of the stacking order so DOM reticles / HUD stay visually on top. ── */
#ar-three {
    display: none;
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100dvh;
    height: 100vh;
    pointer-events: auto;  /* XR8 + our raycaster need taps */
    background: #000;
    z-index: 1;
}
body[data-mode="phone"] #ar-three {
    display: block;
}
/* Buttons need touch events; we manage them via document-level listeners
   (raycast against Three meshes), so the canvas itself stays pointer-events
   none. Individual tap handling is wired up in ar-layer.js. */

/* ── AR edge flash: fires during Retrieve Data finale to sell "data absorbed
   into the phone". Cyan inward vignette. ── */
#ar-edge-flash {
    display: none;
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 8;
    background: radial-gradient(circle at center,
        transparent 30%,
        rgba(0, 229, 255, 0.25) 75%,
        rgba(0, 229, 255, 0.55) 100%);
    opacity: 0;
    mix-blend-mode: screen;
}
body[data-mode="phone"] #ar-edge-flash { display: block; }
body[data-mode="phone"] #ar-edge-flash.firing {
    animation: arEdgeFlash 1.6s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes arEdgeFlash {
    0%   { opacity: 0; }
    55%  { opacity: 1; }
    100% { opacity: 0; }
}

/* ── AR status notice: bottom-left, shown only when SLAM fallback engages. ── */
#ar-status {
    display: none;
    position: fixed;
    bottom: 12px;
    left: 12px;
    font-family: var(--font-mono);
    font-size: 0.65rem;
    letter-spacing: 1px;
    color: var(--text-secondary);
    opacity: 0.5;
    padding: 3px 6px;
    background: rgba(10, 20, 35, 0.5);
    border-left: 2px solid var(--orange-medium);
    pointer-events: none;
    z-index: 9;
    text-transform: lowercase;
}
body[data-mode="phone"] #ar-status.shown { display: block; }

/* ── Desktop complementary effects triggered by phone button presses.
   Stacked above the packet layer so echoes are fully visible. ── */
#desktop-fx-layer {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 8;
    display: none;
}
body[data-mode="desktop"] #desktop-fx-layer { display: block; }

/* Retrieve Data echo: converging packet burst + brief cyan flash. */
.desktop-retrieve-flash {
    position: fixed;
    inset: 0;
    pointer-events: none;
    background: radial-gradient(circle at center,
        rgba(0, 229, 255, 0.35) 0%,
        rgba(0, 229, 255, 0.1) 30%,
        transparent 55%);
    animation: desktopRetrieveFlash 1.3s cubic-bezier(0.22, 1, 0.36, 1) forwards;
    mix-blend-mode: screen;
    will-change: opacity;
}
@keyframes desktopRetrieveFlash {
    0%   { opacity: 0; }
    25%  { opacity: 1; }
    100% { opacity: 0; }
}
.desktop-retrieve-converger {
    position: fixed;
    width: 8px; height: 8px;
    margin: -4px 0 0 -4px;
    border-radius: 50%;
    background: hsl(180, 95%, 60%);
    box-shadow:
        0 0 10px hsl(180, 95%, 60%),
        0 0 22px rgba(0, 229, 255, 0.6);
    animation: desktopRetrieveConverge var(--dur, 1100ms) cubic-bezier(0.2, 0.8, 0.3, 1) forwards;
    will-change: transform, opacity;
}
@keyframes desktopRetrieveConverge {
    0%   { transform: translate(0, 0) scale(0.6); opacity: 0; }
    15%  { opacity: 1; }
    100% { transform: translate(var(--dx), var(--dy)) scale(0.15); opacity: 0; }
}

/* Portal echo: satellite dim + slow ring collapse. Applied via body data attr. */
body[data-mode="desktop"][data-portal-echo="on"] .target {
    filter: brightness(0.45);
    transition: filter 0.8s ease;
}
body[data-mode="desktop"][data-portal-echo="on"] .forerunner-icon {
    animation: desktopPortalShake 0.6s cubic-bezier(0.36, 0, 0.66, 1);
}
@keyframes desktopPortalShake {
    0%   { transform: translate(0, 0); }
    20%  { transform: translate(-4px, 2px); }
    40%  { transform: translate(5px, -3px); }
    60%  { transform: translate(-3px, -2px); }
    80%  { transform: translate(2px, 3px); }
    100% { transform: translate(0, 0); }
}
.desktop-portal-ringshock {
    position: fixed;
    left: 50%;
    top: 50%;
    width: 20px; height: 20px;
    margin: -10px 0 0 -10px;
    border-radius: 50%;
    border: 3px solid var(--cyan-bright);
    box-shadow: 0 0 40px var(--cyan-bright), inset 0 0 20px var(--cyan-bright);
    animation: desktopPortalRingshock 1.4s cubic-bezier(0.22, 1, 0.36, 1) forwards;
    will-change: transform, opacity;
    pointer-events: none;
}
@keyframes desktopPortalRingshock {
    0%   { transform: scale(0.4); opacity: 0; }
    15%  { opacity: 1; }
    100% { transform: scale(60); opacity: 0; border-width: 1px; }
}

/* ── AR overlay: SVG polygon outline of the tracked desktop screen ── */
#ar-overlay {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    z-index: 3;
    display: none;
    overflow: visible;
}
body[data-mode="phone"] #ar-overlay {
    display: block;
}
#ar-screen-frame {
    fill: rgba(0, 229, 255, 0.06);
    stroke: var(--cyan-bright);
    stroke-width: 2;
    stroke-dasharray: 8 6;
    filter: drop-shadow(0 0 10px var(--cyan-glow));
    opacity: 0;
    transition: opacity 0.25s ease;
}
body[data-mode="phone"][data-ar="tracked"] #ar-screen-frame {
    opacity: 0.9;
}
body[data-mode="phone"][data-state="locked"] #ar-screen-frame {
    stroke: var(--green-bright);
    fill: rgba(0, 230, 118, 0.08);
    filter: drop-shadow(0 0 14px var(--green-glow));
    stroke-dasharray: none;
}

/* ── Reduced motion ── */
@media (prefers-reduced-motion: reduce) {
    .core { animation: none; }
    .packet { animation-duration: 400ms; }
}
