/* Home page world: a gradient from the cosmos down to the core, one band per layer,
   with neon line art floating at different depths (moved by world.js). */

/* -------------------------
   Background
-------------------------- */

body {
    background: #0b0520;
}

.world {
    position: relative;
    isolation: isolate;
    /* Art drifting past the edges must not grow the page */
    overflow: clip;
}

/* Each layer paints its own band; every band starts with the colour the previous one ended on,
   so the page reads as one continuous gradient. Backgrounds scroll natively and never stutter. */
#universe { background: linear-gradient(#0b0520, #170a38); }
#clouds { background: linear-gradient(#170a38 0%, #22297a 28%, #3a64bd 55%, #5b9be3 80%, #4f8bd6 100%); }
#mountains { background: linear-gradient(#4f8bd6 0%, #2d7f9e 20%, #0f6a5a 42%, #0b6447 70%, #0d5236 100%); }
#earth { background: linear-gradient(#0d5236 0%, #173f22 45%, #382c16 100%); }
#underground { background: linear-gradient(#382c16 0%, #4c1e15 50%, #4a1610 100%); }
#core {
    background:
        radial-gradient(120% 45% at 50% 100%, rgba(255, 120, 50, 0.35), rgba(255, 120, 50, 0) 70%),
        linear-gradient(#4a1610, #3a1209);
}

/* Star field layer, above the bands and below the art */
.world-bg {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
}

/* Star field over the cosmos and the top of the sky */
.stars {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 260vh;
    background-image:
        radial-gradient(1.5px 1.5px at 22px 38px, #fff, transparent),
        radial-gradient(1px 1px at 140px 92px, #e9dcff, transparent),
        radial-gradient(1.2px 1.2px at 210px 170px, #fff, transparent),
        radial-gradient(1px 1px at 70px 210px, #cfe9ff, transparent),
        radial-gradient(1.6px 1.6px at 180px 20px, #fff, transparent),
        radial-gradient(1px 1px at 100px 140px, #fff, transparent);
    background-size: 240px 240px;
    -webkit-mask-image: linear-gradient(#000 0%, #000 45%, transparent 100%);
    mask-image: linear-gradient(#000 0%, #000 45%, transparent 100%);
    opacity: 0.75;
}

.stars-b {
    background-size: 330px 330px;
    background-position: 60px 90px;
    opacity: 0.5;
    animation: twinkle 5s ease-in-out infinite alternate;
}

@keyframes twinkle {
    from { opacity: 0.15; }
    to { opacity: 0.6; }
}

/* -------------------------
   Layers
-------------------------- */

.zone {
    position: relative;
    min-height: 130vh;
    padding: 16vh 0 14vh;
}

#universe {
    min-height: 100vh;
    padding-top: 20vh;
}

/* The side quest reads as a narrow column on the left; the planet, moon and station fill the right */
.quest {
    max-width: none;
}

/* One type scale for every section: big title, mid subheading, 19px body */
.zone h1,
.zone .zone-title,
.zone .hero h1 {
    font-size: clamp(40px, 5vw, 64px);
    line-height: 1.05;
    letter-spacing: -0.02em;
    margin: 0 0 18px;
}

.zone h3,
.zone .detail-card .detail-title {
    margin: 0 0 22px;
    font-size: clamp(22px, 2.2vw, 28px);
    line-height: 1.2;
}

.zone .detail-card .detail-title {
    margin: 0;
}

.zone p:not(.legal-links),
.zone .detail-card .detail-body {
    font-size: 19px;
    line-height: 1.55;
}

.quest .quest-call {
    margin-top: 20px;
    color: var(--text);
}

.quest .hint {
    display: inline-block;
    margin-top: 34px;
    text-decoration: none;
    transition: opacity 0.2s ease;
}

.quest .hint:hover,
.quest .hint:focus-visible {
    opacity: 1;
}

#clouds {
    min-height: 115vh;
    padding-top: 24vh;
}

/* The wizard sits on the sun (80% across, like the sun in neon.js); the centred card ends at 50vw + 460px and it is min(30vw, 420px) wide */
@media (min-width: 1100px) {
    #clouds .hero-mascot {
        left: calc(30vw - 460px + min(15vw, 210px));
    }
}

#core {
    min-height: 120vh;
    padding-bottom: 30vh;
}

.zone-inner {
    position: relative;
    z-index: 10;
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 32px;
}

/* List and detail panel slide side by side */
.panels {
    position: relative;
    z-index: 10;
    overflow: hidden;
}

.panels .htrack {
    position: relative;
    inset: auto;
    width: 200%;
    height: auto;
    grid-template-columns: 1fr 1fr;
    align-items: start;
}

.chapter.is-detail .panels .htrack {
    transform: translate3d(-50%, 0, 0);
}

.scene-inner {
    max-width: 1100px;
    margin: 0 auto;
    padding: 0 32px;
}

/* Cards are narrower than their column (920px in 1036px): centre them so every layer's block sits mid-screen */
.scene-inner > .card,
.zone-inner > .card {
    margin-inline: auto;
}

/* -------------------------
   Neon line art
-------------------------- */

/* No z-index here on purpose: each drawing sorts against the content on its own */
.art {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

/* Horizon strips are not clipped: their long faded edges overlap into the neighbouring world, so
   borders melt instead of showing a line. Keep their drift (k) small so they don't slide far. */
.strips {
    position: absolute;
    inset: 0;
}

.neon {
    position: absolute;
    z-index: 2;
    width: clamp(calc(var(--w) * 0.45px), calc(var(--w) * 0.0694vw), calc(var(--w) * 1px));
    translate: -50% -50%;
    will-change: transform;
}

/* Illustrated scenery: behind the neon accents and the content */
.scene {
    position: absolute;
    z-index: 1;
    pointer-events: none;
    will-change: transform;
}

.scene-art {
    display: block;
    width: 100%;
    height: auto;
    opacity: var(--neon-o);
    user-select: none;
}

/* Full-width strips bleed past the edges so drifting never shows a seam */
.scene-bottom,
.scene-top {
    left: -2%;
    width: 104%;
}

/* Strips melt into the next world instead of ending in a line: a long, eased fade over their outer
   ~45%, which also leaves room for the parallax drift before the section border clips them */
.scene-bottom {
    bottom: -1px;
    -webkit-mask-image: linear-gradient(#000 55%, rgba(0, 0, 0, 0.75) 68%, rgba(0, 0, 0, 0.35) 84%, transparent 100%);
    mask-image: linear-gradient(#000 55%, rgba(0, 0, 0, 0.75) 68%, rgba(0, 0, 0, 0.35) 84%, transparent 100%);
}

.scene-top {
    top: -1px;
    -webkit-mask-image: linear-gradient(transparent 0%, rgba(0, 0, 0, 0.35) 16%, rgba(0, 0, 0, 0.75) 32%, #000 45%);
    mask-image: linear-gradient(transparent 0%, rgba(0, 0, 0, 0.35) 16%, rgba(0, 0, 0, 0.75) 32%, #000 45%);
}

.scene-object {
    width: clamp(calc(var(--w) * 0.45px), calc(var(--w) * 0.0694vw), calc(var(--w) * 1px));
    translate: -50% -50%;
}

/* Foreground: big, fast, and passing over the content near the edges */
.neon.is-front {
    z-index: 20;
    --stroke: 3px;
}

.neon svg {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
    rotate: var(--rot, 0deg);
    fill: none;
    stroke: var(--c);
    stroke-width: var(--stroke, 2px);
    stroke-linecap: round;
    stroke-linejoin: round;
    filter: drop-shadow(0 0 2px var(--c)) drop-shadow(0 0 9px var(--c));
}

.neon svg * {
    vector-effect: non-scaling-stroke;
}

/* Illustrated object in place of the neon outline (fades out the same way when it glitches) */
.neon-art {
    display: block;
    width: 100%;
    height: auto;
    rotate: var(--rot, 0deg);
    opacity: var(--neon-o);
    filter: drop-shadow(0 10px 16px rgba(0, 0, 0, 0.3));
    user-select: none;
}

/* Slow idle float, three rhythms so nothing moves in lockstep */
.bob-0 { animation: bob 9s ease-in-out infinite; }
.bob-1 { animation: bob 12s ease-in-out -4s infinite; }
.bob-2 { animation: bob 15s ease-in-out -8s infinite; }

@keyframes bob {
    0%, 100% { transform: translate3d(0, 0, 0); }
    50% { transform: translate3d(0, -10px, 0); }
}

/* -------------------------
   Reality glitch: now and then a drawing flickers out
   and its realistic version shows through for a second
-------------------------- */

@property --neon-o {
    syntax: "<number>";
    inherits: true;
    initial-value: 1;
}

.neon svg {
    opacity: var(--neon-o);
}

.neon-real {
    position: absolute;
    left: -10%;
    top: -10%;
    width: 120%;
    height: 120%;
    opacity: 0;
    pointer-events: none;
    user-select: none;
}

/* Scenery glitch: the neon tracing sits exactly on the picture */
.scene .neon-real {
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
}

.scene .neon-real svg {
    display: block;
    width: 100%;
    height: 100%;
    overflow: visible;
    fill: none;
    stroke: var(--c);
    stroke-linecap: round;
    stroke-linejoin: round;
    filter: drop-shadow(0 0 3px var(--c)) drop-shadow(0 0 14px var(--c));
}

.scene .neon-real path {
    vector-effect: non-scaling-stroke;
}

.scene .neon-real .edge {
    stroke-width: 2.6px;
}

/* A magenta ghost of the skyline, slightly offset, like a CRT out of register */
.scene .neon-real .echo {
    stroke: #ff3ea5;
    stroke-width: 1.4px;
    opacity: 0.55;
    transform: translate(5px, 4px);
}

/* Retro scanlines filling the shape, like a wireframe terrain */
.scene .neon-real .scan path {
    stroke-width: 1px;
    opacity: 0.38;
}

/* Neon outline shown during an illustration's glitch: fills the glitch layer, keeps its own glow */
span.neon-real svg {
    width: 100%;
    height: 100%;
    opacity: 1;
}

.neon.is-glitch,
.scene.is-glitch {
    animation: neon-fail 1.2s linear forwards;
}

.is-glitch .neon-real {
    animation: reality 1.2s linear forwards;
}

/* The neon tube stutters, dies, and comes back */
@keyframes neon-fail {
    0% { --neon-o: 1; }
    4% { --neon-o: 0.2; }
    7% { --neon-o: 1; }
    10% { --neon-o: 0; }
    86% { --neon-o: 0; }
    90% { --neon-o: 0.5; }
    93% { --neon-o: 0; }
    97%, 100% { --neon-o: 1; }
}

/* Torn, colour-split frames on the way in and out, a clean realistic image in between */
@keyframes reality {
    0%, 9% {
        opacity: 0;
        clip-path: inset(0);
        transform: none;
        filter: none;
    }
    10% {
        opacity: 1;
        clip-path: inset(18% 0 52% 0);
        transform: translate3d(-8px, 0, 0);
        filter: drop-shadow(-5px 0 0 rgba(255, 0, 90, 0.8)) drop-shadow(5px 0 0 rgba(0, 240, 255, 0.8));
    }
    13% {
        clip-path: inset(58% 0 8% 0);
        transform: translate3d(7px, 0, 0);
    }
    16% {
        clip-path: inset(4% 0 30% 0);
        transform: translate3d(-3px, 0, 0);
        filter: drop-shadow(-2px 0 0 rgba(255, 0, 90, 0.6)) drop-shadow(2px 0 0 rgba(0, 240, 255, 0.6));
    }
    19%, 80% {
        opacity: 1;
        clip-path: inset(0);
        transform: none;
        filter: none;
    }
    83% {
        clip-path: inset(40% 0 28% 0);
        transform: translate3d(9px, 0, 0);
        filter: drop-shadow(-5px 0 0 rgba(255, 0, 90, 0.8)) drop-shadow(5px 0 0 rgba(0, 240, 255, 0.8));
    }
    87% {
        opacity: 1;
        clip-path: inset(8% 0 70% 0);
        transform: translate3d(-6px, 0, 0);
    }
    90%, 100% {
        opacity: 0;
        clip-path: inset(0);
        transform: none;
        filter: none;
    }
}

/* -------------------------
   System error on arrival: the whole view jolts while everything glitches into neon
-------------------------- */

body.is-system-error .world {
    animation: system-jolt 1.5s linear;
}

body.is-system-error .kicker {
    color: #ff3ea5;
    text-shadow: -2px 0 rgba(94, 246, 255, 0.8), 2px 0 rgba(255, 62, 165, 0.8);
}

/* A few sideways jolts with a colour split, never more than three a second */
@keyframes system-jolt {
    0%, 100% { transform: none; filter: none; }
    6% { transform: translate3d(-7px, 0, 0); filter: saturate(1.6) hue-rotate(-14deg) contrast(1.15); }
    12% { transform: translate3d(5px, 0, 0); filter: saturate(1.3) hue-rotate(10deg); }
    18% { transform: none; filter: none; }
    52% { transform: translate3d(3px, 0, 0) skewX(-1deg); filter: saturate(1.4) hue-rotate(-8deg); }
    58% { transform: none; filter: none; }
}

/* -------------------------
   Headline word reveal
-------------------------- */

[data-reveal] .w {
    display: inline-block;
    opacity: 0.18;
    transition: opacity 0.12s linear;
}

/* -------------------------
   Small screens + reduced motion
-------------------------- */

@media (max-width: 760px) {
    .zone {
        min-height: 110vh;
        padding: 13vh 0 12vh;
    }

    .zone-inner,
    .scene-inner {
        padding: 0 16px;
    }

    /* Foreground drawings pass behind the cards here: there is no free edge for them on a phone */
    .neon.is-front {
        z-index: 2;
    }

    /* The sun sits lower, behind the wizard, so its glow doesn't wash out the intro text */
    .s-clouds-1 {
        margin-top: max(35vh, 340px);
    }
}

@media (prefers-reduced-motion: reduce) {
    .bob-0,
    .bob-1,
    .bob-2,
    .stars-b {
        animation: none;
    }

    [data-reveal] .w {
        opacity: 1;
    }

    .neon.is-glitch,
    .scene.is-glitch,
    .is-glitch .neon-real,
    body.is-system-error .world {
        animation: none;
    }
}
