/* The word cloud /tags is made of.
 *
 * Every word is an <a> the server rendered with `--w` on it: 0 for the least-used tag
 * on the page, 1 for the most (`catalog.weigh`). Both the size and the colour are
 * calc()ed from that one number, so the cloud is sized, ranked and readable before any
 * script runs -- which matters more than it used to, because there is no list of tags
 * under it any more. The words are the page.
 *
 * `static/js/tag-cloud.js` then measures them, packs them round each other and adds
 * `.is-packed`, which is the only thing that switches them to absolute positioning.
 * Without the script they stay in the flow layout below: a centred cloud of words,
 * tidier than the packed one and every bit as usable.
 */

.gen-tag-cloud {
    /* The ends of the ramp. The script scales what sits between them, never below
     * --tag-min, so the smallest tag stays legible at every width. */
    --tag-min: 16px;
    --tag-max: 72px;
    /* Rewritten by the script's fit search: one multiplier over the ramp, so a hundred
     * words come out the size the window can hold rather than a size fixed here that is
     * too big on a phone and too small on a desktop. */
    --tag-fit: 1;
    position: relative;
    /* Full-bleed: this element is outside `.container` (which the theme caps at
     * 1710px), so its own padding is what keeps words off the window edge. */
    padding: clamp(16px, 3vw, 48px);
    /* The last guard. A word wider than the window would otherwise scroll the whole
     * document sideways; the fit loop is what stops it happening, this is what stops
     * it mattering. */
    overflow: hidden;
}

/* One accessory, and the only decoration on the page: a wash of the brand colour
 * behind the middle of the cloud, where the packing puts the biggest words. It gives
 * the area some depth on the theme's flat near-black without drawing a box round it. */
.gen-tag-cloud::before {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(58% 58% at 50% 50%, rgba(233, 30, 99, .12), transparent 72%);
    pointer-events: none;
}

.gen-tag-cloud-stage {
    position: relative;
    z-index: 1;
    /* The fallback layout, in use until the script packs the words. */
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 4px 22px;
    transition: opacity .45s ease;
}

/* Hidden only while the script is measuring -- a cloud caught half-packed looks
 * broken. The hiding is deliberately the script's own doing rather than a default here
 * that something has to undo: whatever goes wrong afterwards, up to the file never
 * loading, the words are still on the page and still readable in the layout above. */
.gen-tag-cloud.is-measuring .gen-tag-cloud-stage {
    opacity: 0;
}

.gen-tag-cloud-item {
    /* How far down the ramp this word is, as an opacity: the tail sits back, the
     * popular tags come forward. Kept as its own variable because the hover dimming
     * has to scale it rather than replace it. */
    --tag-alpha: calc(.62 + .38 * var(--w, .5));
    display: inline-block;
    /* Not shrinkable: in the flow fallback these are flex items, and a squeezed word
     * would wrap mid-tag. */
    flex: 0 0 auto;
    font-family: var(--title-fonts);
    font-weight: 500;
    line-height: 1.2;
    white-space: nowrap;
    text-decoration: none;
    font-size: calc(var(--tag-min) + (var(--tag-max) - var(--tag-min)) * var(--w, .5) * var(--tag-fit));
    /* Popularity reads twice: the biggest words are also the pinkest, and the tail
     * recedes towards plain white. The flat colour above is what a browser without
     * color-mix() falls back to. */
    color: var(--white-color);
    color: color-mix(in srgb, var(--primary-color) calc(var(--w, .5) * 100%), var(--white-color));
    opacity: var(--tag-alpha);
    transition: color .2s ease, opacity .2s ease;
}

/* The word's own box carries its position and its drift; this inner span carries the
 * hover growth. Two elements, because both of those are transforms and one element
 * can only have one. */
.gen-tag-cloud-item > span {
    display: inline-block;
    transition: transform .25s ease, text-shadow .25s ease;
}

/*================================================
Packed
================================================*/
.gen-tag-cloud.is-packed .gen-tag-cloud-stage {
    display: block;
    /* Pointer parallax. Two numbers, written by the script at most once a frame; the
     * transition is what smooths them into a drift rather than a jump. */
    transform: translate3d(calc(var(--px, 0) * 1px), calc(var(--py, 0) * 1px), 0);
    transition: opacity .45s ease, transform .4s ease-out;
}

.gen-tag-cloud.is-packed .gen-tag-cloud-item {
    position: absolute;
    /* left/top are written per word by the packer; transform is left alone for the
     * drift, so placement and motion never overwrite each other. */
    top: 0;
    left: 0;
    animation: gen-tag-drift var(--dur, 9s) ease-in-out var(--delay, 0s) infinite alternate;
    will-change: transform;
}

/* A few pixels each way, on its own slow loop per word -- enough to make the cloud
 * breathe, small enough that the packer's gap absorbs it and no two words ever touch. */
@keyframes gen-tag-drift {
    from {
        transform: translate3d(0, 0, 0);
    }

    to {
        transform: translate3d(var(--dx, 3px), var(--dy, -4px), 0);
    }
}

/*================================================
Hover and focus
================================================*/
/* Only where there is a pointer to hover with: on a touch screen the dimming would
 * fire on the tap that follows the link and flash the whole cloud. */
@media (hover: hover) {
    /* Everything else steps back, so the word under the pointer is the only one being
     * read. */
    .gen-tag-cloud:hover .gen-tag-cloud-item {
        opacity: calc(var(--tag-alpha) * .3);
    }

    .gen-tag-cloud .gen-tag-cloud-item:hover {
        color: var(--primary-color);
        opacity: 1;
        /* A moving target is hard to click, so the word stops while it is under the
         * pointer. */
        animation-play-state: paused;
    }

    .gen-tag-cloud-item:hover > span {
        transform: scale(1.14);
        text-shadow: 0 0 22px rgba(233, 30, 99, .55);
    }
}

/* The keyboard gets the same treatment, outside the hover query -- a tab through the
 * cloud has to be followable on a touch screen too. */
.gen-tag-cloud .gen-tag-cloud-item:focus-visible {
    color: var(--primary-color);
    opacity: 1;
}

.gen-tag-cloud-item:focus-visible > span {
    transform: scale(1.14);
    text-shadow: 0 0 22px rgba(233, 30, 99, .55);
}

.gen-tag-cloud-item:focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: 6px;
    border-radius: 2px;
}

/*================================================
Narrower windows
================================================*/
/* The ramp comes down with the window; the script's fit factor then does the rest, so
 * a phone gets all one hundred tags in smaller type rather than the first sixty in
 * large type. Hiding the tail was fine when the index below listed it -- there is no
 * index now, and a hidden tag would be one this page cannot reach. */
@media (max-width: 1199px) {
    .gen-tag-cloud {
        --tag-min: 15px;
        --tag-max: 58px;
    }
}

@media (max-width: 767px) {
    .gen-tag-cloud {
        --tag-min: 14px;
        --tag-max: 44px;
    }
}

@media (max-width: 479px) {
    .gen-tag-cloud {
        --tag-min: 13px;
        --tag-max: 34px;
    }
}

/*================================================
Reduced motion
================================================*/
/* Everything that moves stops; the cloud is still packed, sized and coloured. */
@media (prefers-reduced-motion: reduce) {
    .gen-tag-cloud.is-packed .gen-tag-cloud-item {
        animation: none;
    }

    .gen-tag-cloud.is-packed .gen-tag-cloud-stage {
        transform: none;
        transition: none;
    }

    .gen-tag-cloud-stage,
    .gen-tag-cloud-item,
    .gen-tag-cloud-item > span {
        transition: none;
    }
}
