/**
 * TechBBQ — animated hero background (hero orb, footer orb).
 *
 * THE BACKGROUND IS ONE DESIGNED IMAGE THAT MOVES. It is not a composition assembled here.
 *
 * Eight versions tried to build the Figma hero out of CSS: radial gradients, then a 56% layer blur,
 * then an #ff2f2f drop shadow, then the three circles exported as transparent PNGs and laid over
 * each other. Every version got the hues right and the picture wrong, because Figma composites a
 * layer blur, a progressive blur, a shadow and a 55% fill through its own pipeline, and CSS
 * `filter` has a different falloff and a different compositing order. Approximating a render is the
 * wrong job, and each attempt cost Auri a round of review to say so.
 *
 * On 2026-09-12 he settled it: *"maybe instead of that we can just create backgrounds the way I
 * imagine, basically, and that's it. We will upload just either PNGs or JPEGs."* He exported fifteen
 * of them. So the composition now lives in Figma, where it belongs, and this file only moves it.
 *
 * That also means CHANGING THE LOOK IS A ONE-LINE EDIT. Swap the file behind
 * `--techbbq-hero-image` below, or set that variable on one hero to give a single page its own
 * background. No gradient stops to retune, nothing here to re-derive from a screenshot.
 *
 * ENCODED LOSSLESS AT 900px, AND BOTH HALVES OF THAT MATTER.
 *
 * The first attempt shipped 2000px at WebP q80, 14 KB, and looked visibly cheap. A smooth gradient
 * is the worst case for a lossy codec: with no detail to spend bits on, the encoder collapses the
 * ramp into flat 8-pixel blocks. The hero then draws the image at roughly 3900 device pixels on a
 * retina 1440 screen, so every block is magnified into a visible square. Measured, the middle row
 * went from 744 luminance transitions in the source to 231. Raising quality does not fix it:
 * blockiness was still 0.26 at q97 against 0.048 in the source, for 80 KB.
 *
 * Lossless has no blocks by construction. The size comes from resolution instead, and this artwork
 * is pure blur, so it has no detail above about 600px: measured error against the source, drawn at
 * 3900px, is flat from 600px (0.30) to 1100px (0.27). 900px is chosen with headroom, not need.
 *
 * PNG-8 with Floyd-Steinberg was tried at 155 KB and rejected: it posterises the ramp into visible
 * contour rings, which is worse than the blocking it was meant to cure.
 *
 * A visitor downloads ONE of these, 156 KB, replacing hero-orb-gradient.png at 1.25 MB. The other
 * fourteen sit in the repo unreferenced so that changing the site's hero stays a one-line edit.
 *
 * WEBP, DELIBERATELY, against the theme's ~2018 baseline. Only Safari 13 and older cannot decode it,
 * and the theme already shipped WebP for the orb exports.
 */

.techbbq-hero__orb,
.techbbq-footer__orb,
.techbbq-startup-program-hero__orb--generated,
.techbbq-page-hero__orb--generated {
	/* Painted behind the image so the frame is never empty while the WebP is still loading, and so
	   a hero that is taller than the image's crop has something to sit on. Matches the artwork's own
	   dark corner. */
	--techbbq-hero-bg-color-1: #0d0d0d;

	/* THE BACKGROUND ITSELF is `--techbbq-hero-image` in tokens.css, because the page heroes, the
	   404 and the About reel paint the same file and this sheet is not loaded on the 404. Nothing
	   here needs to know which image it is. */

	/* RECOLOURING WITHOUT A NEW EXPORT.
	 *
	 * The background is a finished image, so its colours are baked in. This filter is the one honest
	 * way to move them from CSS, and it is a real one: `hue-rotate` rotates the whole palette while
	 * leaving the blur, the orb edges and the falloff exactly as designed, and it runs as a GPU
	 * shader on a layer that is already composited, so it costs nothing measurable.
	 *
	 * Site-wide, set it here. One page only, set it on that page's orb element.
	 *
	 * ALWAYS COLLAPSE TO ONE HUE FIRST. Auri's rule, 2026-09-12: *"it cannot be two very distinct
	 * colours."* A bare `hue-rotate` breaks it. hero-6's two lobes start at different hues, crimson
	 * and orange, so rotating the wheel pulls them APART: measured, the hue spread across the frame
	 * goes from 16 degrees as shipped to 25 degrees under hue-rotate(170deg), and the teal version
	 * comes out with one green lobe and one cyan one. `grayscale(1) sepia(1)` first flattens the
	 * whole frame onto a single hue, 1.7 degrees of spread, and everything after it stays there.
	 *
	 *   --techbbq-hero-filter: grayscale(1) sepia(1) hue-rotate(122deg) saturate(3) brightness(1.6);
	 *
	 * Fitted against Auri's own Figma colour variants, hue then saturation then brightness:
	 *
	 *   teal     hue-rotate(122deg) saturate(3)   brightness(1.6)
	 *   cyan     hue-rotate(134deg) saturate(3)   brightness(1.7)
	 *   blue     hue-rotate(150deg) saturate(3)   brightness(1.5)
	 *   green    hue-rotate(84deg)  saturate(2.8) brightness(1.5)
	 *
	 * all after `grayscale(1) sepia(1)`. THE WARM ONES ARE NOT LISTED because the chain cannot reach
	 * them: greyscaling throws the saturation away and `saturate()` caps out before it comes back,
	 * so orange, crimson and purple come out washed. They need the real export.
	 *
	 * AND NONE OF THIS IS A MATCH. It is an approximation of a design that already exists. Auri has
	 * seven colour variants in Figma; when they are exported they drop in as files, exact by
	 * construction, the same way the fifteen did. Re-export at 2400x1180, encode 900px lossless, put
	 * it in assets/images/hero-bg/, name it in TechBBQ_Assets::add_hero_image_token().
	 *
	 * `none` is the default and is free: browsers skip the filter step entirely, so it changes
	 * nothing until somebody gives it a value.
	 *
	 * DECLARED IN tokens.css, NOT HERE, and that is load-bearing. A value set on this block would
	 * beat anything set on :root, because the orb element is more specific than the root element, so
	 * a site-wide change made in tokens.css would silently do nothing. Caught by testing the knob
	 * rather than trusting it. Declaring it once, at the root, leaves both levels working: :root for
	 * the whole site, the orb element for a single page. */

	--techbbq-hero-speed-ground: 26s;
	--techbbq-hero-interactive-size: 22vw;
	--techbbq-hero-blob-interactive-color: #F22400;
	--techbbq-hero-interactive-opacity: 0.8;

	background-color: var(--techbbq-hero-bg-color-1);
	background-image: none;
	contain: strict;
	isolation: isolate;
}

/* A generated orb must not also try to paint its own PNG. The image is set inline on the program
 * hero and from the stylesheet on the page hero, so both are cleared above and here. */
.techbbq-startup-program-hero__orb--generated,
.techbbq-page-hero__orb--generated {
	background-image: none;
}

/* The artwork sits on a pseudo-element so it can be TRANSFORMED. A background-image on the orb
 * itself could only be animated through `background-position`, which repaints the whole layer every
 * frame; a transform on a child is composited on the GPU and costs essentially nothing.
 *
 * It breathes rather than travels. A slow scale with a little drift, inset past the edges and never
 * scaled below 1.06, so no corner can ever pull away from the frame. Twenty-six seconds is long
 * enough that you notice the frame has changed without ever catching it moving, which is what the
 * Figma mock reads like when you sit with it.
 */
.techbbq-hero__orb::before,
.techbbq-footer__orb::before,
.techbbq-startup-program-hero__orb--generated::before,
.techbbq-page-hero__orb--generated::before {
	content: "";
	position: absolute;
	z-index: 0;
	inset: -8%;
	background-image: var(--techbbq-hero-image);
	background-repeat: no-repeat;
	background-position: center center;
	background-size: cover;
	transform-origin: 62% 38%;
	filter: var(--techbbq-hero-filter);
	animation: techbbq-hero-ground-breathe var(--techbbq-hero-speed-ground) ease-in-out infinite;
	will-change: transform;
}

@keyframes techbbq-hero-ground-breathe {
	0% {
		transform: scale(1.06) translate3d(0, 0, 0);
	}

	50% {
		transform: scale(1.16) translate3d(-2.5%, 1.8%, 0);
	}

	100% {
		transform: scale(1.06) translate3d(0, 0, 0);
	}
}

.techbbq-hero__orb.is-paused::before,
.techbbq-footer__orb.is-paused::before {
	animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
	.techbbq-hero__orb::before,
	.techbbq-footer__orb::before,
	.techbbq-startup-program-hero__orb--generated::before,
	.techbbq-page-hero__orb--generated::before {
		animation: none;
		transform: scale(1.06);
	}
}

/* THE THREE BLOBS ARE GONE, but their markup is not. It is emitted from four places
 * (class-techbbq-gradient-bg.php and the hero, page-hero and startup-program-hero widgets), and
 * removing it means four PHP edits for no visible gain. They are hidden here instead, in one rule,
 * where the reason is written down. Deleting the markup later is safe; this rule then matches
 * nothing. */
.techbbq-hero__orb-gradients,
.techbbq-hero__orb-blob {
	display: none;
}

/* The cursor-following blob is the one piece of the old composition worth keeping: it is not part
 * of the artwork, it is a response to the visitor, and it reads clearly over a blurred ground. */
.techbbq-hero__orb-blob--interactive {
	position: absolute;
	z-index: 1;
	background: var(--techbbq-hero-blob-interactive-color);
	width: var(--techbbq-hero-interactive-size);
	height: var(--techbbq-hero-interactive-size);
	top: calc(var(--techbbq-hero-interactive-size) / -2);
	left: calc(var(--techbbq-hero-interactive-size) / -2);
	border-radius: 50%;
	filter: blur(6vw);
	opacity: var(--techbbq-hero-interactive-opacity);
	display: none;
}

@media (min-width: 768px) and (hover: hover) and (pointer: fine) {
	.techbbq-hero__orb-gradients {
		display: block;
		position: relative;
		z-index: 1;
		width: 100%;
		height: 100%;
	}

	.techbbq-hero__orb-blob--interactive {
		display: block;
	}
}

.techbbq-hero__orb.is-paused .techbbq-hero__orb-blob--interactive {
	display: none;
}

@media (prefers-reduced-motion: reduce) {
	.techbbq-hero__orb-blob--interactive {
		display: none;
	}
}
