/*
 * The page that says Panzerfisch is coming and nothing else.
 *
 * It is the 404 of the GUI taken one step further: there, the fish of the logo
 * hangs in the water where the nought would be and the drawing says what has
 * happened instead of a paragraph. Here a school of them crosses the page, the
 * bubbles rise past the text, and the only words are the name, the sentence
 * under it and one teaser at a time.
 *
 * Two things this page deliberately does not do. It loads no font: the wordmark
 * is the logo's own lettering as a path, and everything else is whatever the
 * machine already has, so the page needs nothing from any other host to look
 * finished. And it has no build step - three files and a favicon, served as
 * they are written, because a placeholder that needs a toolchain to change a
 * sentence is a placeholder nobody changes.
 */

:root {
    /* The palette. Teal is the logo's own #089695; the brighter one is what
       that color has to become to be read as light coming through water. */
    --pf-teal: #089695;
    --pf-teal-bright: #14c2be;
    --pf-deep: #010a0d;
    --pf-water: #04252b;
    --pf-surface: #0a4a50;
    --pf-foam: #d6f2f2;
    --pf-foam-dim: rgba(214, 242, 242, 0.62);

    color-scheme: dark;
}

* {
    box-sizing: border-box;
}

html,
body {
    min-height: 100%;
}

body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    /* Two gradients: the light coming in at the surface, and the water getting
       darker towards the bottom. Fixed, so scrolling on a short window moves
       the text through the water rather than moving the water. */
    background:
        radial-gradient(ellipse 130% 70% at 50% -12%, var(--pf-surface) 0%, transparent 62%),
        linear-gradient(180deg, var(--pf-water) 0%, var(--pf-deep) 78%);
    background-color: var(--pf-deep);
    background-attachment: fixed;
    color: var(--pf-foam);
    font-family: "Open Sans", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
    font-size: 16px;
    line-height: 1.55;
    /* Horizontally only: a fish leaves at both edges and a bubble drifts out
       at an angle, and neither is worth a scrollbar - but clipping the other
       way would take the bottom of the page off a short window, where there
       would be nothing left to reach it with. */
    overflow-x: hidden;
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
}

/* The symbol the school is drawn from. It is not a picture on the page. */
.sprite {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

/*
 * The water and the school.
 *
 * Both are fixed and take no click: the bubbles rise past the headline as well
 * as behind the fish, because the page is in the water and does not contain a
 * tank. The school is above the water layer and below the text - a fish may
 * swim behind the name, never over it.
 */
.water,
.school {
    position: fixed;
    inset: 0;
    pointer-events: none;
    /* Clipped at the window, which is where these two end anyway: a fish is
       off the page before and after its crossing and a bubble starts below
       the bottom edge. What it buys is the certainty that nothing drifting
       about out there can put a scrollbar on the page. */
    overflow: hidden;
}

.water {
    z-index: 0;
}

.school {
    z-index: 1;
}

.bubble {
    position: absolute;
    bottom: -12vh;
    left: var(--bubble-left);
    width: var(--bubble-size);
    height: var(--bubble-size);
    border-radius: 50%;
    border: 1px solid rgba(20, 194, 190, 0.55);
    background: rgba(20, 194, 190, 0.12);
    opacity: 0;
    /* The delay is negative: the animation starts part-way through, so the
       page opens on water that is already moving instead of filling up. */
    animation: pf-bubble-rise var(--bubble-duration) linear var(--bubble-delay) infinite;
}

@keyframes pf-bubble-rise {
    0% {
        transform: translate3d(0, 0, 0) scale(0.5);
        opacity: 0;
    }
    12% {
        opacity: 0.5;
    }
    86% {
        opacity: 0.3;
    }
    100% {
        transform: translate3d(var(--bubble-drift), -118vh, 0) scale(1);
        opacity: 0;
    }
}

/*
 * One fish.
 *
 * Three elements for one animal, and they are three because the motions would
 * otherwise fight over a single transform: the outer one crosses the page, the
 * middle one bobs and leans, and the innermost is only ever mirrored - a fish
 * swimming left is the same drawing the other way round.
 */
.fish {
    position: absolute;
    top: var(--fish-depth);
    left: 0;
    width: var(--fish-size);
    opacity: var(--fish-fade);
    color: var(--pf-teal);
    will-change: transform;
}

.fish-bob {
    animation: pf-fish-bob var(--fish-bob-duration) ease-in-out infinite;
}

.fish svg {
    display: block;
    width: 100%;
    height: auto;
    fill: currentColor;
    filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.45));
}

/* Which way it faces. The drawing looks right; a fish going the other way is
   mirrored and nothing else. */
.fish.is-leftward svg {
    transform: scaleX(-1);
}

/* Crossing. The offsets are percentages of the fish and not of the window, so
   a big one is fully gone before it turns up again on the other side. */
@keyframes pf-swim-right {
    from {
        transform: translateX(-130%);
    }
    to {
        transform: translateX(calc(100vw + 30%));
    }
}

@keyframes pf-swim-left {
    from {
        transform: translateX(calc(100vw + 30%));
    }
    to {
        transform: translateX(-130%);
    }
}

@keyframes pf-fish-bob {
    0%,
    100% {
        transform: translateY(var(--fish-bob-amount)) rotate(calc(var(--fish-lean) * -1));
    }
    50% {
        transform: translateY(calc(var(--fish-bob-amount) * -1)) rotate(var(--fish-lean));
    }
}

/*
 * What there is to read.
 */
.page {
    position: relative;
    z-index: 2;
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    /* 'safe' and not plain centring: on a window shorter than the content,
       centred flex content overflows at both ends and the top of it sits above
       the scroll origin, where nothing can reach it. */
    justify-content: center;
    justify-content: safe center;
    gap: clamp(18px, 3vw, 30px);
    padding: clamp(40px, 8vh, 96px) clamp(18px, 5vw, 48px) clamp(72px, 12vh, 120px);
    text-align: center;
}

.brand {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: clamp(6px, 1.4vw, 14px);
    margin: 0;
}

/* The logo, whole: the fish over the lettering, in one drawing. Its height is
   what decides how much of a short window is taken by the name, so it is
   written against the height as well as against the width - 'vmin' rather than
   'vw', and a cap in pixels above both. */
.logo {
    width: min(420px, 64vmin);
    height: auto;
    display: block;
    fill: var(--pf-teal-bright);
    /* The glow is the light of the water on the mark, and it is the one place
       on this page where a color is written twice - a drop-shadow takes no
       variable through a filter list in every browser that matters yet. */
    filter: drop-shadow(0 0 30px rgba(20, 194, 190, 0.32));
}

.verb {
    font-size: clamp(18px, 3.4vw, 32px);
    font-weight: 600;
    letter-spacing: 0.42em;
    /* Letter-spacing puts the gap after the last letter too, which pushes a
       centred line left by half of it. */
    text-indent: 0.42em;
    text-transform: uppercase;
    color: var(--pf-foam);
}

.lede {
    max-width: 62ch;
    margin: 0;
    font-size: clamp(15px, 1.9vw, 19px);
    color: var(--pf-foam-dim);
}

/*
 * The teasers.
 *
 * All of them are in the page and stacked on one another; the one carrying the
 * mark is the one that can be seen. Stacked rather than swapped, because a
 * teaser that is replaced in the document makes the page jump by the
 * difference in height, and these are two, three and four lines long.
 *
 * The height is the tallest of them, held by the grid: every teaser sits in
 * the same cell, so the box is as high as it has to be and never changes.
 */
.teasers {
    display: grid;
    width: min(680px, 100%);
    margin-top: clamp(10px, 2vh, 24px);
    padding-top: clamp(18px, 3vh, 28px);
    border-top: 1px solid rgba(20, 194, 190, 0.28);
}

.teaser {
    grid-area: 1 / 1;
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 900ms ease, transform 900ms ease;
    /* Not read out while it is invisible, and not reachable by a find on the
       page either - eight teasers at once would be eight sentences nobody
       can see. */
    visibility: hidden;
}

.teaser.is-current {
    opacity: 1;
    transform: none;
    visibility: visible;
}

.teaser h2 {
    margin: 0 0 6px;
    font-size: clamp(13px, 1.5vw, 15px);
    font-weight: 700;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--pf-teal-bright);
}

.teaser p {
    margin: 0;
    font-size: clamp(16px, 2.2vw, 21px);
    color: var(--pf-foam);
}

/*
 * The two pictures.
 *
 * They are the only bright thing on the page, which is the point: a screenshot
 * of the real editors is the one claim here that is evidence rather than
 * wording. Framed rather than floated on the water - a light rectangle with no
 * edge looks like a hole in the page - and given the room they need before they
 * are loaded, through the width and height on the element itself.
 */
.shots {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: clamp(28px, 5vh, 52px);
    width: min(900px, 100%);
    margin-top: clamp(28px, 6vh, 64px);
}

.shot {
    width: 100%;
    margin: 0;
}

.shot img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 10px;
    border: 1px solid rgba(20, 194, 190, 0.35);
    /* Two shadows: the dark one seats it in the water, the teal one is the
       light of the page falling on its edge. */
    box-shadow:
        0 18px 40px rgba(0, 0, 0, 0.45),
        0 0 32px rgba(20, 194, 190, 0.12);
}

.shot figcaption {
    margin-top: clamp(8px, 1.4vh, 14px);
    font-size: clamp(13px, 1.6vw, 15px);
    color: var(--pf-foam-dim);
}

/*
 * The channel.
 *
 * A link and not a player - see the note in the markup. It sits under the two
 * pictures because that is what it is more of: the same two editors, in motion
 * and with somebody explaining them.
 */
.channel {
    margin: clamp(6px, 1.5vh, 16px) 0 0;
}

.channel-link {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 10px 20px 10px 12px;
    border: 1px solid rgba(20, 194, 190, 0.4);
    border-radius: 999px;
    color: var(--pf-foam);
    font-size: clamp(14px, 1.8vw, 17px);
    text-decoration: none;
    transition: border-color 200ms ease, background-color 200ms ease;
}

.channel-link:hover,
.channel-link:focus-visible {
    border-color: var(--pf-teal-bright);
    background: rgba(20, 194, 190, 0.1);
}

.channel-play {
    display: inline-flex;
    width: 26px;
    height: 26px;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--pf-teal);
    color: var(--pf-deep);
}

.channel-play svg {
    width: 14px;
    height: 14px;
    /* The triangle is drawn as a closed path and filled with the color of the
       water, so the disc behind it is what is teal - the same way round as
       the mark at the top of the page. */
    fill: currentColor;
    /* Optically centred: a triangle's weight sits left of its bounding box. */
    margin-left: 2px;
}

/*
 * Somebody who has asked for less movement gets the same picture and none of
 * the motion: the water stands still, the fish hang where they were put, and
 * the teasers are a list instead of a rotation - because a sentence that
 * replaces itself every few seconds is exactly what that request is about.
 */
@media (prefers-reduced-motion: reduce) {
    .bubble,
    .fish,
    .fish-bob {
        animation: none !important;
    }

    .bubble {
        opacity: 0.35;
    }

    .teasers {
        grid-auto-flow: row;
        gap: clamp(14px, 2.4vh, 22px);
        text-align: left;
    }

    .teaser {
        grid-area: auto;
        opacity: 1;
        transform: none;
        transition: none;
        visibility: visible;
    }
}
