/**
 * THE HOLDING PAGE — five animations, no words, no chrome.
 *
 * Owner, 23 Sep 2026: *"creative animation like whole page nothing else there no header no
 * footer no button nothing … it also using my project preference based bootstrap."*
 *
 * ## EVERY COLOUR ON THIS PAGE IS THE PRODUCT'S, AND NONE OF IT IS INVENTED HERE
 *
 * There is no palette in this file. There are exactly three sources, and all three are
 * already on `<html>` by the time the first rule below is parsed:
 *
 *   `--primary-rgb`      the person's accent, as a bare `r, g, b` triplet, injected inline by
 *                        `App\Core\Support\Appearance::htmlAttributes()`. Every stroke, glow
 *                        and field below is `rgba(var(--primary-rgb), …)`. Change the accent
 *                        in preferences and the whole page changes with it, with no second
 *                        control and nothing to keep in step.
 *   `--bs-body-bg`       Bootstrap 5.3's own, which is a DIFFERENT COLOUR under
 *   `--bs-body-color`    `data-bs-theme="dark"` because Bootstrap redefines it there. That
 *                        one fact is the whole of this page's dark mode: it is not handled,
 *                        it is inherited.
 *
 * The previous version of this file took its colours from six per-site fields — a background
 * colour, a background image, an accent. All six are gone, permanently, by the instruction
 * above. The page now looks like the product rather than like whatever was typed into it.
 *
 * ## REDUCED MOTION IS THE BASE STATE, NOT AN OVERRIDE
 *
 * The platform honours a person's `reduce_motion` preference (`html[data-reduce-motion]`,
 * written by `Appearance`) and the operating system's own `prefers-reduced-motion`. A page
 * that is ENTIRELY a full-screen animation is the exact case that setting exists for, so
 * this is not a polite dimming — nothing moves at all.
 *
 * It is done in two lines, and the technique is worth the sentence it takes to explain.
 * Every animated rule below names its keyframes through `var(--ws-play, <name>)`. A `var()`
 * falls back to its second argument only while the property is UNSET — so by default each
 * element animates normally, and setting `--ws-play: none` once on `:root` turns
 * `animation-name` into `none` for every one of them at once.
 *
 * The consequence is the part that matters: with the animation off, an element shows its
 * ORDINARY declared styles. So each animation's resting composition is written as its plain
 * CSS — rings parked at four radii, moons parked around their orbits, waves parked mid-swell
 * — and the keyframes move away from it and back. The still page is a designed still page,
 * not an animation stopped on its first frame, and there is not one duplicated block.
 *
 * ## HIGHER CONTRAST
 *
 * `html[data-higher-contrast]` raises `--ws-ink`, which every alpha below is multiplied by.
 * One number, one place, and the animation keeps its shape instead of being replaced by a
 * high-contrast version of itself that nobody has looked at.
 *
 * `100vmin` rather than `vw`/`vh` throughout: these shapes must stay circular and stay whole
 * on a phone held either way up, and a `vw` radius on a 390px-wide screen is a shape that
 * leaves the viewport on one axis.
 */

/* ---------------------------------------------------------------- the two switches */

@media (prefers-reduced-motion: reduce) {
    :root {
        --ws-play: none;
    }
}

html[data-reduce-motion] {
    --ws-play: none;
}

html[data-higher-contrast] .ws-stage {
    --ws-ink: 1.7;
}

/* ---------------------------------------------------------------- the document */

/*
 * A holding page is one screen and is never scrolled: there is nothing below the fold
 * because there is nothing at all. `overflow: hidden` on both elements stops a shape that
 * deliberately overhangs the viewport — the waves are wider than the screen by design —
 * from growing scrollbars for a page with no content to scroll to.
 *
 * `body` colours are stated rather than left to Bootstrap's own `body` rule so that the
 * page cannot flash white on a dark machine between the two stylesheets.
 */
html,
body.ws-body {
    height: 100%;
    overflow: hidden;
}

body.ws-body {
    margin: 0;
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
}

.ws-stage {
    --ws-ink: 1;

    position: fixed;
    inset: 0;
    display: grid;
    place-items: center;
    overflow: hidden;
    background:
        radial-gradient(ellipse 120% 90% at 50% 45%, rgba(var(--primary-rgb), calc(0.08 * var(--ws-ink))), transparent 70%),
        var(--bs-body-bg);
}

/*
 * A vignette, so the shapes sit IN the page rather than on top of it.
 *
 * `--bs-body-bg` again and not black: on a light theme a black vignette is a dirty edge,
 * while the body's own colour reads as depth in both themes without a second opinion.
 */
.ws-stage::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: radial-gradient(ellipse 85% 75% at 50% 50%, transparent 55%, var(--bs-body-bg));
    opacity: 0.7;
}

/* ================================================================ orbit */

/*
 * Three tilted rings, one body on each, around a lit centre.
 *
 * The tilt is a real rotation on a perspective rather than a squashed circle, so the near
 * edge of each ring is genuinely nearer. Each moon therefore carries the INVERSE rotation:
 * a dot tilted with the ring it rides is an ellipse pointing the wrong way.
 *
 * AT REST each ring is turned to a different angle — `--i` again — so the three moons are
 * spread around the system instead of lined up on one side of it.
 */

.ws-stage[data-ws="orbit"] {
    perspective: 100vmin;
}

/* ================================================================ waves */

/*
 * Three enormous, almost-round shapes turning slowly beneath the fold.
 *
 * A disc whose radius is uneven — 42% one way, 47% the other — reads as a swell rather than
 * as a wheel, and three of them at three speeds, one of them reversed, never bring the same
 * crest back twice. This is the whole technique: no path data, no canvas, no library.
 *
 * AT REST each is turned to its own angle, so the still page is a layered seascape rather
 * than three identical lids.
 */

.ws-waves {
    position: absolute;
    inset: 0;
}

.ws-waves__layer {
    position: absolute;
    left: 50%;
    width: 260vmax;
    height: 260vmax;
    margin-left: -130vmax;
    top: calc(57% + (var(--i) * 5%));
    border-radius: 42% 47% 45% 43%;
    background: rgba(var(--primary-rgb), calc((0.09 + (var(--i) * 0.055)) * var(--ws-ink)));
    transform: rotate(calc(var(--i) * 41deg));
    animation: var(--ws-play, ws-waves-roll) calc(34s - (var(--i) * 8s)) linear infinite;
}

.ws-waves__layer[style*="--i:1"] {
    animation-direction: reverse;
}

@keyframes ws-waves-roll {
    to {
        transform: rotate(calc((var(--i) * 41deg) + 360deg));
    }
}

/* ================================================================ aurora */

/*
 * Four soft fields drifting across one another.
 *
 * THE COLOURS ARE THE ACCENT, TURNED. `hue-rotate` on a field painted in `--primary-rgb`
 * gives three companions to the accent rather than three colours chosen here — so a red
 * platform gets a warm aurora and a blue tenant a cold one, and neither was designed for.
 *
 * The blur is what makes it an aurora rather than four circles, and it is the one expensive
 * declaration on this page; it is on four elements, composited once, and does not repaint.
 *
 * AT REST the four are laid out in a composition that is meant to be looked at — the
 * `--i` offsets place them, and the drift moves from there.
 */

.ws-aurora {
    position: absolute;
    inset: -12%;
}

.ws-aurora__band {
    position: absolute;
    width: calc(58vmin + (var(--i) * 12vmin));
    height: calc(52vmin + (var(--i) * 9vmin));
    border-radius: 50%;
    background: radial-gradient(circle at 50% 50%,
        rgba(var(--primary-rgb), calc(0.62 * var(--ws-ink))),
        rgba(var(--primary-rgb), 0) 70%);

    /*
     * THE TURN IS SMALL, AND IT IS SMALL ON PURPOSE.
     *
     * The first version stepped 46° a band, which took a red accent all the way round to a
     * flat green by the fourth — a colour nobody on this platform had chosen, on a page
     * whose whole brief was to look like the product. `(--i − 1.5) × 26°` puts the four
     * bands at −39°, −13°, +13° and +39° — either side of the accent rather than away from
     * it, which is the difference between a family of colours and four different ones.
     */
    filter: blur(7vmin) hue-rotate(calc((var(--i) - 1.5) * 26deg));
    animation: var(--ws-play, ws-aurora-drift) calc(24s + (var(--i) * 6s)) ease-in-out infinite alternate;
    animation-delay: calc(var(--i) * -5s);
}

/*
 * WHERE EACH BAND SITS — by position and not by arithmetic on `--i`.
 *
 * One formula spread the four along a diagonal and left the top right of the screen empty,
 * which a formula is very good at doing. Four placements are four lines, they are the
 * composition, and they are meant to be adjusted by eye.
 */
.ws-aurora__band:nth-child(1) {
    top: -6%;
    left: -4%;
}

.ws-aurora__band:nth-child(2) {
    top: 36%;
    left: 44%;
}

.ws-aurora__band:nth-child(3) {
    top: 2%;
    left: 56%;
}

.ws-aurora__band:nth-child(4) {
    top: 50%;
    left: 4%;
}

@keyframes ws-aurora-drift {
    from {
        transform: translate3d(0, 0, 0) scale(1);
    }

    to {
        transform: translate3d(14vmin, -9vmin, 0) scale(1.25);
    }
}

/* ===========================================================================
   THE WORDS (23 Sep 2026)

   Two fixed sentences, centred over the animation. The owner asked for them
   on top of it rather than beside it, so this is one composition and not two
   things sharing a screen.

   The hard part is not the placing, it is staying READABLE over five different
   animations in two themes. `aurora` puts saturated colour behind the middle
   of the screen; `grid` puts a bright glow at the vanishing point. So the type
   takes the theme's own body colour — which inverts with `data-bs-theme` — and
   sits on a soft radial scrim that lifts it off whatever is moving underneath,
   without drawing a visible box around it.
   =========================================================================== */
.ws-say {
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.6vmin;
    text-align: center;
    padding: 6vmin;
    pointer-events: none;

    /* The scrim. Transparent at the edges so the animation still reaches the
       corners, and strongest exactly where the words are. */
    background: radial-gradient(
        ellipse 60% 42% at 50% 50%,
        rgba(var(--bs-body-bg-rgb, 255, 255, 255), .72) 0%,
        rgba(var(--bs-body-bg-rgb, 255, 255, 255), .45) 45%,
        rgba(var(--bs-body-bg-rgb, 255, 255, 255), 0) 72%
    );
}

.ws-say__lead {
    margin: 0;
    font-weight: 600;

    /* Scales with the viewport rather than stepping at breakpoints: this page
       is one line of type and it should look deliberate on a phone and on a
       television. Clamped so it never gets silly at either end. */
    font-size: clamp(2rem, 7.5vmin, 5.5rem);
    line-height: 1.05;
    letter-spacing: -.02em;
    color: var(--bs-body-color);
}

.ws-say__sub {
    margin: 0;
    font-size: clamp(.95rem, 2.1vmin, 1.5rem);
    line-height: 1.5;
    max-width: 34ch;

    /* Quieter than the heading, but not so quiet it fails over a bright frame
       of the animation — hence a mix with the body colour rather than a fixed
       grey, so it follows the theme too. */
    color: color-mix(in srgb, var(--bs-body-color) 72%, transparent);
}

/* Somebody who asked for higher contrast gets the full body colour and a
   firmer scrim — the one place on this page where the preference has real
   work to do, because the words sit over moving colour. */
html[data-higher-contrast] .ws-say {
    background: radial-gradient(
        ellipse 66% 48% at 50% 50%,
        rgba(var(--bs-body-bg-rgb, 255, 255, 255), .9) 0%,
        rgba(var(--bs-body-bg-rgb, 255, 255, 255), .68) 48%,
        rgba(var(--bs-body-bg-rgb, 255, 255, 255), 0) 76%
    );
}

html[data-higher-contrast] .ws-say__sub { color: var(--bs-body-color); }
