/* ==========================================================================
   geforcy.com — depth landing

   A value-only palette: no accent hue, depth is carried entirely by luminance
   and atmosphere.

   The smoke is two pre-baked, seamlessly tiling textures. Each fog layer runs
   two copies of its texture at different scales, offsets and speeds — the
   interference between them reads as billowing, where a single panning image
   would read as a sliding photograph.

   Blending: the textures are opaque greyscale on black, composited with
   `screen`, so black contributes nothing and the greys add as backlit smoke.
   That needs the blend declared in two places. On the sublayers, so the two
   copies combine additively with *each other*; and on the layer itself, so the
   combined group adds over the figure and the stage. A stacking context traps
   blending inside it, so a single declaration would do only one of those jobs.
   ========================================================================== */

:root {
    --void:     #050507;  /* base black, a hair of blue in it */
    --steel:    #8f959f;  /* armour mid-tone — the corner mark */

    --ease:     cubic-bezier(0.22, 0.61, 0.36, 1);
    --scrim:    0.46;     /* flat black over the whole scene — raise to darken */
}

*,
*::before,
*::after {
    box-sizing: border-box;
}

html,
body {
    height: 100%;
}

body {
    margin: 0;
    background: var(--void);
    color: var(--steel);
    font-family: 'Antonio', system-ui, sans-serif;
    -webkit-font-smoothing: antialiased;
    overflow: hidden;
}

/* --------------------------------------------------------------------------
   Effect frame

   Two Canvas UI passes composite over the stage: Blaze below, Clouds above.
   Both are transparent overlays that never take the cursor, so the scene keeps
   its own stacking and the page survives them failing to start at all.

   The source canvases are capture hosts for the html-in-canvas path and hold
   nothing on their own. They are laid out only when effects.js confirms that
   path is live and adds `is-native`; until then they must not occupy the frame,
   or an empty canvas would paint over the scene.
   -------------------------------------------------------------------------- */

.fx {
    position: relative;
    height: 100svh;
    min-height: 100%;
}

.fx-source {
    display: none;
}

.fx.is-native .fx-source[data-clouds-source] {
    display: block;
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

/* The captured element. Must carry its own full-size box — inside a canvas
   layout subtree there is no parent width to resolve 100% against, so a scene
   sized only by its content would collapse to a strip. */
.fx-content {
    position: relative;
    width: 100%;
    height: 100%;
}

/* Sizing reference for Blaze. Empty and transparent — it exists so the effect
   has a content box to measure, not to render anything. */
.fx-bed {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.fx-output {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

.fx-clouds { z-index: 6; }
.fx-blaze  { z-index: 7; }

/* --------------------------------------------------------------------------
   Stage
   -------------------------------------------------------------------------- */

.depth-landing {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100svh;
    min-height: 100%;
    overflow: hidden;
    background: var(--void);
    isolation: isolate; /* keep the blending inside the stage */
}

.layer {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

/* Shared geometry for the four drifting smoke sublayers. Each is twice the
   stage width and tiles its texture at exactly one stage width, so a translate
   of one tile lands on an identical frame and the loop is invisible. */
.far-fog::before,
.far-fog::after,
.near-fog::before,
.near-fog::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 200%;
    height: 100%;
    background-repeat: repeat;
    mix-blend-mode: screen;
    will-change: transform;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
}

/* --- Layer 1: far smoke ---------------------------------------------------
   Behind the figure. Slow, large, soft — it reads as depth rather than
   movement, and pools into a backlit band around the midsection. */

.far-fog {
    z-index: 1;
    mix-blend-mode: screen;
    background:
        radial-gradient(88% 56% at 50% 52%, rgba(44, 53, 70, 0.55), transparent 72%),
        radial-gradient(56% 34% at 24% 62%, rgba(32, 39, 52, 0.40), transparent 74%);
}

.far-fog::before {
    background-image: url('smoke-far.webp');
    background-size: 50% 100%;
    opacity: 0.85;
    animation-name: drift-50;
    animation-duration: 150s;
}

.far-fog::after {
    background-image: url('smoke-far.webp');
    background-size: 72% 124%;
    background-position: 37% 18%;
    opacity: 0.6;
    animation-name: drift-72-rev;
    animation-duration: 210s;
}

/* --- Layer 3: near smoke --------------------------------------------------
   In front of the figure, so it genuinely veils the legs. Roughly four times
   the speed of the far layer — that difference is what sells the depth. */

.near-fog {
    z-index: 3;
    mix-blend-mode: screen;
}

.near-fog::before {
    background-image: url('smoke-near.webp');
    background-size: 58% 100%;
    opacity: 0.78;
    animation-name: drift-58;
    animation-duration: 46s;
}

.near-fog::after {
    background-image: url('smoke-near.webp');
    background-size: 64% 132%;
    background-position: 61% 8%;
    opacity: 0.52;
    animation-name: drift-64-rev;
    animation-duration: 34s;
}

/* Vignette and floor — static, above the smoke, closing the frame and
   grounding the composition into black. Kept off .near-fog because a `screen`
   layer cannot darken. */
.depth-landing::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 4;
    pointer-events: none;
    /* Flat scrim, under this layer's own gradients but over the smoke and the
       figure. The type sits at z-index 5 and stays clear of it. */
    background-color: rgba(0, 0, 0, var(--scrim));
    background-image:
        radial-gradient(84% 70% at 50% 46%, transparent 44%, rgba(5, 5, 7, 0.70) 100%),
        linear-gradient(180deg, rgba(5, 5, 7, 0.80) 0%, transparent 24%),
        /* grounds the composition into black, and gives the corner mark
           something dark enough to read against */
        linear-gradient(180deg, transparent 64%, rgba(5, 5, 7, 0.52) 85%, rgba(5, 5, 7, 0.88) 100%);
}

/* --- Layer 2: the subject -------------------------------------------------
   A real cutout on transparency, so it needs no blending and no edge mask —
   the smoke passes behind and in front of it on its own. */

.subject {
    z-index: 2;
    display: block;
    inset: 0;
    margin: auto;
    height: 100%;
    width: auto;
    max-width: none;
    filter: drop-shadow(0 0 60px rgba(5, 5, 7, 0.9));
    animation: emerge 2.6s var(--ease) both;
}

/* --------------------------------------------------------------------------
   Layer 4: content

   The scene carries the page, so the wordmark steps back to a corner label —
   small, widely tracked, low in the value range. It sits in the darkest part
   of the frame, which is the only reason it can stay this quiet and still read.
   -------------------------------------------------------------------------- */

.content {
    position: relative;
    z-index: 5;
    align-self: flex-end;
    width: 100%;
    padding: 0 clamp(1.5rem, 4vw, 3.25rem) clamp(1.5rem, 4vh, 2.75rem);
    text-align: left;
}

.content h1 {
    margin: 0;
    font-family: 'Antonio', system-ui, sans-serif;
    font-weight: 300;
    font-size: clamp(0.9rem, 1.1vw, 1.15rem);
    line-height: 1;
    letter-spacing: 0.3em;
    color: var(--steel);
    text-shadow: 0 0 20px rgba(5, 5, 7, 0.95), 0 1px 3px rgba(5, 5, 7, 0.9);
    animation: settle 1.6s var(--ease) 1.5s both;
}

/* --------------------------------------------------------------------------
   Motion

   Each drift translates by exactly one background tile, so the last frame is
   identical to the first. The percentages match the background-size above.
   -------------------------------------------------------------------------- */

@keyframes drift-50 {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-50%, 0, 0); }
}

@keyframes drift-72-rev {
    from { transform: translate3d(-72%, 0, 0); }
    to   { transform: translate3d(0, 0, 0); }
}

@keyframes drift-58 {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-58%, 0, 0); }
}

@keyframes drift-64-rev {
    from { transform: translate3d(-64%, 0, 0); }
    to   { transform: translate3d(0, 0, 0); }
}

@keyframes emerge {
    from {
        opacity: 0;
        transform: translateY(2.5%) scale(1.045);
        filter: blur(12px);
    }
    to {
        opacity: 1;
        transform: none;
        filter: blur(0);
    }
}

@keyframes settle {
    from { opacity: 0; transform: translateY(0.5rem); }
    to   { opacity: 1; transform: none; }
}

/* When the viewport is proportionally narrower than the image, constrain the
   figure by width instead of height, so a tall screen doesn't crop the swords
   and shoulders off. Anchored to the bottom rather than centred — he should be
   sitting on the floor, not floating in the middle of the frame. */
@media (max-aspect-ratio: 896 / 1195) {
    .subject {
        width: 100%;
        height: auto;
        inset: auto 0 0;
        margin: 0 auto;
    }
}

/* Phone-shaped viewports: scale him up so he fills the frame. Auto side
   margins still centre him once he is wider than the screen. Gated at 3/5
   because that is the aspect where 122% is exactly viewport-height tall —
   anything wider than this cap would push the helmet off the top. */
@media (max-aspect-ratio: 3 / 5) {
    .subject {
        width: 122%;
    }
}

@media (prefers-reduced-motion: reduce) {
    .far-fog::before,
    .far-fog::after,
    .near-fog::before,
    .near-fog::after,
    .subject,
    .content h1 {
        animation: none;
    }
}
