/* ══ AWAKENED · RADIANT (100%) ═══════════════════════════════════════════════
   ONE copy of the fully-awakened treatment, for every surface that draws a hero.

   It existed four times over — the builder's slot, the tier-list card, the lineup card
   (printed inline into wp_footer) and the lineup page — and the four had drifted apart.
   Only this one, the builder's, carried the real recipe; the others were reduced
   stand-ins with their own keyframes and, in the tier list, stars faked with
   pseudo-elements. That is why the same hero animated differently depending on where
   you met him.

   The rules below are the builder's, byte for byte. Every selector simply gained a
   host-neutral twin, so a surface can opt in without inheriting the builder's markup:

     .inv-awk-radiant   the CARD  — carries --awk-star
     .inv-awk-fx        the PORTRAIT box — clips and isolates; carries the rim
     .inv-awk-stars     the star field — REAL spans (invictus_awakened_stars_html)

   The drifting smoke that used to live here was removed on 2026-09-07, once the rendered
   clip existed: two effects doing the same job, and the clip does it better. What is left
   is the rim, the stars, and the clip itself.

   Everything measured about this effect, and the several things that do NOT work, is in
   the comments below. Read them before tuning anything. */

/* ── Awakened hero card ───────────────────────────────────────────────────────
   At 100% awakening the game does not fill the card with light: it burns a short
   fringe of flame BEHIND the card, licking out past the left and right edges and
   pooling along the foot, while the card's own art and frame stay clean. Earlier
   passes here built the opposite — a bloom sitting inside the bottom of the card —
   and no amount of tuning made that read as fire, because the flame is legible only
   where it meets the dark page behind the card.

   Three layers, all `z-index:-1` so they paint behind the card and only their
   overhang shows. Each is a set of tongues repeated along the edge: the side tiles
   repeat DOWN the edge and are swept upward, the way flame climbs; the foot tile
   sweeps right to left. They are separate ELEMENTS rather than layers of one, because
   an element carries a single transform and a single animation timeline — one layer
   can only ever flare as a whole, which is what made the first attempts read as a
   lamp being dimmed rather than as fire.

   Reach is deliberately short sideways (7-13px) and longer at the foot (13-25px):
   the three commander slots sit in a grid 8px apart, so a wide halo would burn into
   the neighbouring card, while the row has real margin underneath.

   `pointer-events:none` throughout so the card stays clickable; motion is disabled
   under prefers-reduced-motion, and the static fringe remains. */
.inv-cmd-slot.awakened .inv-cmd-slot-portrait-wrap,
.inv-cmd-slot-filled.awakened .inv-cmd-slot-portrait-wrap,
.inv-awk-fx{position:relative}

/* A warm rim on the portrait, breathing on its own slow cycle. */
.inv-cmd-slot.radiant .inv-cmd-slot-portrait-wrap,
.inv-awk-radiant .inv-awk-fx{
	border-radius:.5rem;isolation:isolate;
	box-shadow:0 0 0 1px #f8d67a99,0 0 14px 2px #f59e0b66,inset 0 0 18px #f59e0b22;
	position:relative}
@keyframes inv-awk-rim{0%,100%{opacity:0}50%{opacity:1}}



/* NO FLAME OVER THE ART. Tried twice and measured both: with normal blending a
   translucent layer just greys the portrait into a milky haze, and with `screen` it
   bleaches it — Lu Bu's black hair came out blond. The awakened art is bright gold
   end to end, so it offers nothing dark for fire to sit in. The fire stays at the
   card's edge, where it meets the dark page and reads as fire. */

/* The rendered aura clip, injected by awakening-picker.js when the asset exists.
   `screen` drops its black background, so it needs no alpha channel and reads as light
   on the card rather than as a film over it. While it plays the CSS smoke stands down —
   two smokes at once is worse than either. The star glints stay: they are DOM elements,
   so they keep their edge at any card size and are scattered differently per card, which
   a sparkle baked into the clip cannot be. */
.inv-cmd-slot.radiant .inv-cmd-slot-portrait-wrap .inv-awk-vfx,
.inv-awk-radiant .inv-awk-fx .inv-awk-vfx{
	position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
	mix-blend-mode:screen;pointer-events:none;z-index:3;opacity:.6;
	-webkit-mask-image:linear-gradient(to top,#000 0%,#000 37%,transparent 82%);
	mask-image:linear-gradient(to top,#000 0%,#000 37%,transparent 82%);}
@media (prefers-reduced-motion:reduce){
	.inv-cmd-slot.radiant .inv-cmd-slot-portrait-wrap .inv-awk-vfx{display:none}}

/* GOLDEN SMOKE, drifting up the card's edges. Not flame — on artwork this bright a
   fire drawn over it either greys into haze or bleaches, both measured. Smoke works for
   the opposite reason: it is LOW contrast and it MOVES.

   `screen` at small alpha is what keeps it luminous rather than milky — it only ever
   brightens, and at this weight it lifts the gold without touching the armour's darks.
   The same blend at high alpha is what bleached an earlier attempt; the effect has to
   come from motion, not from weight.

   TWO DIFFERENT REACHES. The edge columns climb the whole card. The centre plume is on
   the second layer, which is masked out by just past the middle, so smoke over the art
   thins away instead of drifting across the face. Blur stays moderate: enough to be
   smoke, not so much that it turns into a fog bank.

   Both layers live inside the portrait wrap, which clips them to the card and, carrying
   `isolation:isolate`, keeps the blending off everything behind it. Tile heights are
   100/(k+1) so each column loops with no seam, and the two layers use different heights
   and periods so the smoke never pulses as one body. */
.inv-cmd-slot.radiant .inv-cmd-slot-filled,
/* WHERE THE BLENDING IS CONTAINED.
   `.inv-awk-fx` is two different things. On the PHP-rendered card it IS the portrait box,
   so isolating it is right: `screen` blends the clip with the art inside it. The script
   path (invAwkApply) instead lays an EMPTY `.inv-awk-fx.is-overlay` over the art — and
   isolating THAT gives screen nothing to blend with, so the clip's black ground composites
   onto the card as a veil and darkens it. That is why the same hero looked correct on his
   own page and dimmed on the grids.
   Contain the blending on the picture box instead: the art lives inside it, so screen
   lightens as intended and still cannot reach the page behind the card. */
.inv-awk-radiant{position:relative;isolation:isolate}

/* Star glints around the card's edge — REAL ELEMENTS, built by awakening-picker.js.

   They used to be background layers on two pseudo-elements, which is why they read like
   a hazard light: every point in a layer shares that layer's opacity, so ten stars lit
   and died in perfect unison, and CSS has no way to vary it per point. Each star is now
   its own span with its own position, size, period, delay and peak brightness, drawn at
   random along the border — so no two are ever at the same phase.

   The shape is still one inline SVG in a custom property; the span only positions it.
   Fading is ease-in-out over a long period, so a star swells and dies slowly instead of
   snapping on. */
.inv-cmd-slot.radiant,
.inv-awk-radiant{--awk-star:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 0c.6 6.6 4.8 10.8 12 12-7.2 1.2-11.4 5.4-12 12-.6-6.6-4.8-10.8-12-12C7.2 10.8 11.4 6.6 12 0z' fill='%23fffdf5'/%3E%3C/svg%3E")}
.inv-awk-stars{position:absolute;inset:-2px;pointer-events:none;z-index:5;overflow:visible}
.inv-awk-star{
	position:absolute;opacity:0;
	background:var(--awk-star) center/contain no-repeat;
	filter:drop-shadow(0 0 3px #f59e0b99);
	animation-name:inv-awk-star-twinkle;
	animation-timing-function:ease-in-out;
	animation-iteration-count:infinite}
/* A swell and a fade, dark for a good part of the cycle so the border is never
   uniformly lit. --awk-peak is set per star. */
@keyframes inv-awk-star-twinkle{
	0%,100%{opacity:0}
	22%    {opacity:calc(var(--awk-peak,1) * .45)}
	50%    {opacity:var(--awk-peak,1)}
	78%    {opacity:calc(var(--awk-peak,1) * .35)}}
/* Some of them catch properly: ONE fast, hard spike with a brighter halo, then a slower
   fall — which is what a star does and a lamp does not. The halo is on `filter`, not on
   size, so nothing moves. The curve used to rise again at 58%, which gave those stars a
   heartbeat; a catch of light happens once. */
.inv-awk-star--flare{animation-name:inv-awk-star-flare}
/* Stars pulse on opacity alone. The flare used to animate `filter: drop-shadow`, which
   repaints every frame exactly like the rims did, and a hero can carry five stars. The
   static drop-shadow stays on the element; only the fade moves. */
@keyframes inv-awk-star-flare{
	0%,100%{opacity:0}
	16%    {opacity:calc(var(--awk-peak,1) * .35)}
	30%    {opacity:1}
	56%    {opacity:calc(var(--awk-peak,1) * .45)}
	80%    {opacity:calc(var(--awk-peak,1) * .12)}}

/* Reduced motion: the rim stops breathing and the stars stop twinkling, both holding a
   steady value. The list used to name the smoke pseudo-elements too, and with them the
   portrait wrap itself — which dimmed the whole picture to 70%. They are gone. */
@media (prefers-reduced-motion:reduce){
	.inv-cmd-slot.radiant .inv-cmd-slot-portrait-wrap,
	.inv-awk-radiant .inv-awk-fx{animation:none}
	.inv-awk-star{animation:none;opacity:.7}}

/* ── When the card IS the picture ─────────────────────────────────────────────
   The contract wants two boxes: the card (foot band) and the picture (rim, smoke).
   On most surfaces they are one and the same element — a stage tile, a loadout
   portrait, a tier-list thumbnail, a lineup-page anchor — and putting the card half
   on the enclosing box instead lands the foot band over the hero's NAME and throws
   the rim outside the tile.

   So that element takes .inv-awk-radiant, and the picture half becomes an overlay
   inside it: it draws nothing of its own, it only carries the rim and the two smoke
   layers, and it is clipped to exactly the picture. */
.inv-awk-fx.is-overlay{position:absolute;inset:0;z-index:1;pointer-events:none;overflow:hidden;
	border-radius:inherit;
	/* SCREEN BELONGS ON THIS BOX, not on the clip inside it. `z-index` on a positioned
	   element makes a stacking context, so a blend set on the video only ever blends
	   against what is inside this overlay — which is nothing. The clip's black ground then
	   composited onto the card as a veil and dimmed it, which is why the same hero looked
	   right on his own page (where .inv-awk-fx IS the portrait box and the art is inside)
	   and dark everywhere the script lays this empty overlay over the art. Blending the
	   overlay as a group puts the art back underneath it. */
	mix-blend-mode:screen;
	/* INSET rim. The card form's glow reaches ~16px OUTSIDE the box, which is right when
	   the box is the whole card and wrong here: on a stage tile it washed over the
	   neighbouring hero, and on a loadout portrait it could not simply be clipped away
	   because that box is deliberately overflow:visible so skill panels can escape it.
	   Same breathing rim, turned inwards, so the effect fills its picture and stops. */
	box-shadow:inset 0 0 0 1px #f8d67a99,inset 0 0 18px #f59e0b33;}
@keyframes inv-awk-rim-inset{0%,100%{opacity:0}50%{opacity:1}}
@media (prefers-reduced-motion: reduce){
	.inv-awk-fx.is-overlay{animation:none}
}

/* The square field sits 2px PROUD of its box, which is right when the stars scatter along
   four straight edges — a sparkle just off the corner reads as light catching the frame.
   On a round host the positions are angles measured from the centre, so those 2px push the
   whole ring outside the disc. Match the box exactly there. */
.inv-awk-stars.is-round{inset:0}

/* ── ROUND portrait: the game's own small-card treatment ──────────────────────
   On the little circular hero cards the game does not scatter sparkles. It rings the
   portrait in gold and breathes a warm bloom outward from that ring — brightest on the
   edge, falling off into the panel behind it. At 46px a scatter of points had nowhere to
   sit anyway; they ended up beside the hero rather than on him.

   So the round form drops the smoke (two blurred layers inside a 46px disc is mush) and
   the star field, and becomes the ring. The glow reaches ~8px, which the gaps between
   portraits absorb — the reason the card form's rim had to point INWARD does not apply
   at this size. `overflow:hidden` clips children, never the element's own shadow, so the
   bloom still leaves the box. */
.inv-awk-fx.is-overlay.is-round{
	border-radius:50%;overflow:visible;
	box-shadow:inset 0 0 0 1.5px #f8d67acc,inset 0 0 9px #f59e0b59,
		0 0 8px 1px #f59e0b73,0 0 18px 4px #f59e0b33}
@keyframes inv-awk-halo{0%,100%{opacity:0}50%{opacity:1}}
@media (prefers-reduced-motion: reduce){
	.inv-awk-fx.is-overlay.is-round{animation:none}
}


/* ── Pulse without repainting ────────────────────────────────────────────────
   These rims used to animate `box-shadow` directly, and that is the whole reason a
   lineup page with several awakened heroes crawled: a shadow cannot be composited,
   so every frame of every pulse forced a full repaint of the card, forever, on each
   awakened portrait at once.

   Same look, different mechanism. The dim shadow stays STATIC on the element, and the
   bright one lives on an ::after overlay whose opacity is what animates. Opacity is a
   compositor property, so the pulse now runs on the GPU and costs no repaint. The two
   states cross-fade rather than morph, which at this blur radius is not a visible
   difference.

   `will-change` is deliberately absent: it would promote a layer per portrait and trade
   the repaint for memory on exactly the pages that have most of them. */
.inv-cmd-slot.radiant .inv-cmd-slot-portrait-wrap::after,
.inv-awk-radiant .inv-awk-fx::after{
	content:'';position:absolute;inset:0;pointer-events:none;
	border-radius:inherit;
	box-shadow:0 0 0 1px #ffe9b0cc,0 0 22px 4px #f59e0b88,inset 0 0 22px #f59e0b33;
	animation:inv-awk-rim 2.6s ease-in-out infinite;
}
.inv-awk-fx.is-overlay.is-round::after{
	box-shadow:inset 0 0 0 1.5px #fff0c0,inset 0 0 14px #f59e0b8c,
		0 0 14px 3px #f59e0bb3,0 0 30px 9px #f59e0b54;
	animation:inv-awk-halo 2.4s ease-in-out infinite;
}
@media (prefers-reduced-motion:reduce){
	.inv-cmd-slot.radiant .inv-cmd-slot-portrait-wrap::after,
	.inv-awk-radiant .inv-awk-fx::after,
	.inv-awk-fx.is-overlay.is-round::after{animation:none;opacity:.55}
}

/* The inset rim on the clipped overlay gets its own ::before layer rather than sharing
   ::after with the outer rim. It cannot animate on the element itself: this box CONTAINS
   the portrait, so fading its opacity would blink the picture rather than the glow. */
.inv-awk-fx.is-overlay::before{
	content:'';position:absolute;inset:0;pointer-events:none;z-index:2;
	border-radius:inherit;
	box-shadow:inset 0 0 0 1px #ffe9b0cc,inset 0 0 26px #f59e0b4d;
	animation:inv-awk-rim-inset 2.6s ease-in-out infinite;
}
@media (prefers-reduced-motion:reduce){
	.inv-awk-fx.is-overlay::before{animation:none;opacity:.55}
}

/* ── Card grids: glow, but hold still ────────────────────────────────────────
   A lineup card used to embed the awakened VFX as an autoplay looping <video>, one
   per awakened hero. On a profile page that is fourteen lineups times three heroes --
   up to forty-two videos decoding at once, which is what actually made the machine
   work, far more than any CSS could.

   Cards now render `.inv-awk-fx--still`: the same gold rim, no video, no pulse. The
   full effect stays on the single hero and single lineup pages, where there is one of
   it and it is the point. In a grid the rim alone is already the tell. */
.inv-awk-fx--still{
	position:absolute;inset:0;z-index:1;pointer-events:none;
	border-radius:inherit;
	box-shadow:0 0 0 1px #f8d67a99,0 0 14px 2px #f59e0b66,inset 0 0 18px #f59e0b22;
}
.inv-awk-radiant:has(.inv-awk-fx--still)::after,
.inv-awk-fx--still::after,
.inv-awk-fx--still::before{content:none}
