/* Animated backgrounds that move only transform and opacity, so the browser composites them without repainting. They stop under prefers-reduced-motion and when the pause checkbox is ticked. */
@keyframes bp-slide{to{transform:translateX(-50%)}}
@keyframes bp-drift{50%{transform:translate(18%,12%) scale(1.12)}to{transform:translate(-10%,22%) scale(.92)}}
@keyframes bp-sway{from{transform:translateX(-8%) rotate(-8deg) scaleY(.9)}to{transform:translateX(8%) rotate(4deg) scaleY(1.15)}}
@keyframes bp-sway-b{from{transform:translate(6%,4%) rotate(6deg);opacity:.35}to{transform:translate(-6%,-4%) rotate(-4deg)}}
@keyframes bp-rise{to{transform:translateY(calc(var(--bp-size) * -1))}}
@keyframes bp-twinkle{from{opacity:.45}to{opacity:1}}
/* Moving gradient: a layer four tiles wide holds the gradient twice and slides left by one repeat, so the loop is seamless */
.bp-flow{--bp-bg:#6366f1;--bp-c1:#6366f1;--bp-c2:#ec4899;--bp-c3:#f59e0b;--bp-speed:14s;--bp-opacity:1;position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg)}
.bp-flow::before{content:"";position:absolute;inset:0 auto 0 0;width:400%;z-index:-1;pointer-events:none;opacity:var(--bp-opacity);background:linear-gradient(90deg,var(--bp-c1),var(--bp-c2),var(--bp-c3),var(--bp-c1)) 0 0/50% 100% repeat-x;will-change:transform;animation:bp-slide var(--bp-speed) linear infinite}
@media (prefers-reduced-motion:reduce){.bp-flow::before{animation:none}.bp-flow::after{animation:none}.bp-flow > i{animation:none}}
/* Floating blobs: three soft radial circles (no filter) drifting on transform only */
.bp-blobs{--bp-bg:#f8fafc;--bp-c1:#a5b4fc;--bp-c2:#f9a8d4;--bp-c3:#99f6e4;--bp-size:70%;--bp-speed:16s;--bp-opacity:.9;position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg)}
.bp-blobs > i{position:absolute;z-index:-1;width:var(--bp-size);aspect-ratio:1;border-radius:50%;pointer-events:none;opacity:var(--bp-opacity);will-change:transform;animation:bp-drift var(--bp-speed) ease-in-out infinite alternate}
.bp-blobs > i:nth-of-type(1){top:-30%;left:-15%;background:radial-gradient(closest-side,var(--bp-c1),transparent)}
.bp-blobs > i:nth-of-type(2){top:0;right:-25%;background:radial-gradient(closest-side,var(--bp-c2),transparent);animation-duration:calc(var(--bp-speed) * 1.3);animation-direction:alternate-reverse}
.bp-blobs > i:nth-of-type(3){bottom:-45%;left:25%;background:radial-gradient(closest-side,var(--bp-c3),transparent);animation-duration:calc(var(--bp-speed) * .8)}
@media (prefers-reduced-motion:reduce){.bp-blobs::before{animation:none}.bp-blobs::after{animation:none}.bp-blobs > i{animation:none}}
/* Aurora: two layers of soft bands that sway and fade, on transform and opacity */
.bp-aurora{--bp-bg:linear-gradient(#020617,#0b1d33);--bp-c1:#34d399;--bp-c2:#22d3ee;--bp-c3:#a78bfa;--bp-speed:12s;--bp-opacity:.75;position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg)}
.bp-aurora::before{content:"";position:absolute;inset:-25% -15%;z-index:-1;pointer-events:none;opacity:var(--bp-opacity);will-change:transform;background:radial-gradient(45% 20% at 30% 45%,var(--bp-c1),transparent 70%),radial-gradient(40% 16% at 66% 36%,var(--bp-c2),transparent 70%);animation:bp-sway var(--bp-speed) ease-in-out infinite alternate}
.bp-aurora::after{content:"";position:absolute;inset:-25% -15%;z-index:-1;pointer-events:none;opacity:var(--bp-opacity);will-change:transform,opacity;background:radial-gradient(35% 18% at 55% 62%,var(--bp-c3),transparent 70%);animation:bp-sway-b calc(var(--bp-speed) * 1.4) ease-in-out infinite alternate}
@media (prefers-reduced-motion:reduce){.bp-aurora::before{animation:none}.bp-aurora::after{animation:none}.bp-aurora > i{animation:none}}
/* Starfield: two star layers rising by exactly one tile per loop; the near layer also twinkles */
.bp-stars{--bp-bg:linear-gradient(#030712,#1e1b4b);--bp-star:#fff;--bp-size:200px;--bp-speed:40s;--bp-opacity:1;position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg)}
.bp-stars::before{content:"";position:absolute;inset:0 0 auto;height:calc(100% + var(--bp-size));z-index:-1;pointer-events:none;opacity:var(--bp-opacity);will-change:transform;background-image:radial-gradient(circle at 43% 21%,var(--bp-star) 1px,transparent 1.6px),radial-gradient(circle at 85% 8%,var(--bp-star) 0.8px,transparent 1.4px),radial-gradient(circle at 70% 14%,var(--bp-star) 1px,transparent 1.6px),radial-gradient(circle at 76% 9%,var(--bp-star) 1.2px,transparent 1.8px),radial-gradient(circle at 29% 6%,var(--bp-star) 0.8px,transparent 1.4px),radial-gradient(circle at 57% 55%,var(--bp-star) 0.8px,transparent 1.4px),radial-gradient(circle at 32% 13%,var(--bp-star) 1.2px,transparent 1.8px),radial-gradient(circle at 56% 9%,var(--bp-star) 1.2px,transparent 1.8px),radial-gradient(circle at 17% 30%,var(--bp-star) 1.2px,transparent 1.8px),radial-gradient(circle at 82% 76%,var(--bp-star) 0.8px,transparent 1.4px),radial-gradient(circle at 75% 76%,var(--bp-star) 1px,transparent 1.6px),radial-gradient(circle at 8% 30%,var(--bp-star) 0.8px,transparent 1.4px),radial-gradient(circle at 73% 19%,var(--bp-star) 1px,transparent 1.6px),radial-gradient(circle at 55% 20%,var(--bp-star) 1.2px,transparent 1.8px);background-size:var(--bp-size) var(--bp-size);animation:bp-rise var(--bp-speed) linear infinite}
.bp-stars::after{content:"";position:absolute;inset:0 0 auto;height:calc(100% + var(--bp-size));z-index:-1;pointer-events:none;will-change:transform,opacity;background-image:radial-gradient(circle at 59% 73%,var(--bp-star) 2px,transparent 2.6px),radial-gradient(circle at 59% 67%,var(--bp-star) 1.6px,transparent 2.2px),radial-gradient(circle at 25% 67%,var(--bp-star) 2px,transparent 2.6px),radial-gradient(circle at 82% 80%,var(--bp-star) 1.6px,transparent 2.2px),radial-gradient(circle at 14% 59%,var(--bp-star) 2px,transparent 2.6px),radial-gradient(circle at 20% 13%,var(--bp-star) 1.6px,transparent 2.2px);background-size:var(--bp-size) var(--bp-size);background-position:calc(var(--bp-size) / 3) 0;animation:bp-rise calc(var(--bp-speed) / 2) linear infinite,bp-twinkle 3s ease-in-out infinite alternate}
@media (prefers-reduced-motion:reduce){.bp-stars::before{animation:none}.bp-stars::after{animation:none}.bp-stars > i{animation:none}}
/* Pause switch: a checkbox anywhere inside .bp-motion freezes every animation in it (:has()). */
.bp-motion{display:flex;flex-direction:column;gap:32px}
.bp-motion:has(.bp-pause:checked) :is(.bp-flow,.bp-aurora,.bp-stars)::before,.bp-motion:has(.bp-pause:checked) :is(.bp-aurora,.bp-stars)::after,.bp-motion:has(.bp-pause:checked) .bp-blobs > i{animation-play-state:paused}
.bp-pause-label{display:inline-flex;align-items:center;gap:8px;align-self:flex-start;font-size:.875rem;line-height:20px;font-weight:600;color:#334155;cursor:pointer}
.bp-pause{width:16px;height:16px;margin:0;accent-color:#4f46e5}
.bp-pause:focus-visible{outline:2px solid #4f46e5;outline-offset:3px}
/* Demo tile and name chip */
.bp-tile{min-height:var(--bp-tile-h,180px);border-radius:16px;border:1px solid #dee2e6}
/* The name chip is an opaque scrim, so its text keeps 4.5:1 whatever pattern is underneath. */
.bp-tag{position:absolute;left:16px;bottom:16px;padding:4px 8px;border-radius:6px;background:#fff;color:#0f172a;font-size:.875rem;font-weight:600;line-height:1.5;box-shadow:0 2px 4px rgb(0 0 0 / .075)}
.bp-tile{--bp-tile-h:220px}
/* Animated backgrounds that move only transform and opacity, so the browser composites them without repainting. They stop under prefers-reduced-motion and when the pause checkbox is ticked. */
@keyframes bp-slide{to{transform:translateX(-50%)}}
@keyframes bp-drift{50%{transform:translate(18%,12%) scale(1.12)}to{transform:translate(-10%,22%) scale(.92)}}
@keyframes bp-sway{from{transform:translateX(-8%) rotate(-8deg) scaleY(.9)}to{transform:translateX(8%) rotate(4deg) scaleY(1.15)}}
@keyframes bp-sway-b{from{transform:translate(6%,4%) rotate(6deg);opacity:.35}to{transform:translate(-6%,-4%) rotate(-4deg)}}
@keyframes bp-rise{to{transform:translateY(calc(var(--bp-size) * -1))}}
@keyframes bp-twinkle{from{opacity:.45}to{opacity:1}}
/* Moving gradient: a layer four tiles wide holds the gradient twice and slides left by one repeat, so the loop is seamless */
.bp-flow{--bp-bg:#6366f1;--bp-c1:#6366f1;--bp-c2:#ec4899;--bp-c3:#f59e0b;--bp-speed:14s;--bp-opacity:1;position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg)}
.bp-flow::before{content:"";position:absolute;inset:0 auto 0 0;width:400%;z-index:-1;pointer-events:none;opacity:var(--bp-opacity);background:linear-gradient(90deg,var(--bp-c1),var(--bp-c2),var(--bp-c3),var(--bp-c1)) 0 0/50% 100% repeat-x;will-change:transform;animation:bp-slide var(--bp-speed) linear infinite}
@media (prefers-reduced-motion:reduce){.bp-flow::before{animation:none}.bp-flow::after{animation:none}.bp-flow > i{animation:none}}
/* Floating blobs: three soft radial circles (no filter) drifting on transform only */
.bp-blobs{--bp-bg:#f8fafc;--bp-c1:#a5b4fc;--bp-c2:#f9a8d4;--bp-c3:#99f6e4;--bp-size:70%;--bp-speed:16s;--bp-opacity:.9;position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg)}
.bp-blobs > i{position:absolute;z-index:-1;width:var(--bp-size);aspect-ratio:1;border-radius:50%;pointer-events:none;opacity:var(--bp-opacity);will-change:transform;animation:bp-drift var(--bp-speed) ease-in-out infinite alternate}
.bp-blobs > i:nth-of-type(1){top:-30%;left:-15%;background:radial-gradient(closest-side,var(--bp-c1),transparent)}
.bp-blobs > i:nth-of-type(2){top:0;right:-25%;background:radial-gradient(closest-side,var(--bp-c2),transparent);animation-duration:calc(var(--bp-speed) * 1.3);animation-direction:alternate-reverse}
.bp-blobs > i:nth-of-type(3){bottom:-45%;left:25%;background:radial-gradient(closest-side,var(--bp-c3),transparent);animation-duration:calc(var(--bp-speed) * .8)}
@media (prefers-reduced-motion:reduce){.bp-blobs::before{animation:none}.bp-blobs::after{animation:none}.bp-blobs > i{animation:none}}
/* Aurora: two layers of soft bands that sway and fade, on transform and opacity */
.bp-aurora{--bp-bg:linear-gradient(#020617,#0b1d33);--bp-c1:#34d399;--bp-c2:#22d3ee;--bp-c3:#a78bfa;--bp-speed:12s;--bp-opacity:.75;position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg)}
.bp-aurora::before{content:"";position:absolute;inset:-25% -15%;z-index:-1;pointer-events:none;opacity:var(--bp-opacity);will-change:transform;background:radial-gradient(45% 20% at 30% 45%,var(--bp-c1),transparent 70%),radial-gradient(40% 16% at 66% 36%,var(--bp-c2),transparent 70%);animation:bp-sway var(--bp-speed) ease-in-out infinite alternate}
.bp-aurora::after{content:"";position:absolute;inset:-25% -15%;z-index:-1;pointer-events:none;opacity:var(--bp-opacity);will-change:transform,opacity;background:radial-gradient(35% 18% at 55% 62%,var(--bp-c3),transparent 70%);animation:bp-sway-b calc(var(--bp-speed) * 1.4) ease-in-out infinite alternate}
@media (prefers-reduced-motion:reduce){.bp-aurora::before{animation:none}.bp-aurora::after{animation:none}.bp-aurora > i{animation:none}}
/* Starfield: two star layers rising by exactly one tile per loop; the near layer also twinkles */
.bp-stars{--bp-bg:linear-gradient(#030712,#1e1b4b);--bp-star:#fff;--bp-size:200px;--bp-speed:40s;--bp-opacity:1;position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg)}
.bp-stars::before{content:"";position:absolute;inset:0 0 auto;height:calc(100% + var(--bp-size));z-index:-1;pointer-events:none;opacity:var(--bp-opacity);will-change:transform;background-image:radial-gradient(circle at 43% 21%,var(--bp-star) 1px,transparent 1.6px),radial-gradient(circle at 85% 8%,var(--bp-star) 0.8px,transparent 1.4px),radial-gradient(circle at 70% 14%,var(--bp-star) 1px,transparent 1.6px),radial-gradient(circle at 76% 9%,var(--bp-star) 1.2px,transparent 1.8px),radial-gradient(circle at 29% 6%,var(--bp-star) 0.8px,transparent 1.4px),radial-gradient(circle at 57% 55%,var(--bp-star) 0.8px,transparent 1.4px),radial-gradient(circle at 32% 13%,var(--bp-star) 1.2px,transparent 1.8px),radial-gradient(circle at 56% 9%,var(--bp-star) 1.2px,transparent 1.8px),radial-gradient(circle at 17% 30%,var(--bp-star) 1.2px,transparent 1.8px),radial-gradient(circle at 82% 76%,var(--bp-star) 0.8px,transparent 1.4px),radial-gradient(circle at 75% 76%,var(--bp-star) 1px,transparent 1.6px),radial-gradient(circle at 8% 30%,var(--bp-star) 0.8px,transparent 1.4px),radial-gradient(circle at 73% 19%,var(--bp-star) 1px,transparent 1.6px),radial-gradient(circle at 55% 20%,var(--bp-star) 1.2px,transparent 1.8px);background-size:var(--bp-size) var(--bp-size);animation:bp-rise var(--bp-speed) linear infinite}
.bp-stars::after{content:"";position:absolute;inset:0 0 auto;height:calc(100% + var(--bp-size));z-index:-1;pointer-events:none;will-change:transform,opacity;background-image:radial-gradient(circle at 59% 73%,var(--bp-star) 2px,transparent 2.6px),radial-gradient(circle at 59% 67%,var(--bp-star) 1.6px,transparent 2.2px),radial-gradient(circle at 25% 67%,var(--bp-star) 2px,transparent 2.6px),radial-gradient(circle at 82% 80%,var(--bp-star) 1.6px,transparent 2.2px),radial-gradient(circle at 14% 59%,var(--bp-star) 2px,transparent 2.6px),radial-gradient(circle at 20% 13%,var(--bp-star) 1.6px,transparent 2.2px);background-size:var(--bp-size) var(--bp-size);background-position:calc(var(--bp-size) / 3) 0;animation:bp-rise calc(var(--bp-speed) / 2) linear infinite,bp-twinkle 3s ease-in-out infinite alternate}
@media (prefers-reduced-motion:reduce){.bp-stars::before{animation:none}.bp-stars::after{animation:none}.bp-stars > i{animation:none}}
/* Pause switch: a checkbox anywhere inside .bp-motion freezes every animation in it (:has()). */
.bp-motion{display:flex;flex-direction:column;gap:32px}
.bp-motion:has(.bp-pause:checked) :is(.bp-flow,.bp-aurora,.bp-stars)::before,.bp-motion:has(.bp-pause:checked) :is(.bp-aurora,.bp-stars)::after,.bp-motion:has(.bp-pause:checked) .bp-blobs > i{animation-play-state:paused}
.bp-motion .form-check{--bs-form-check-bg:#fff;align-self:flex-start;min-height:0;margin:0;line-height:20px;font-size:.875rem;font-weight:600;color:#334155}
.bp-motion .form-check-input{--bs-border-color:#64748b}
.bp-motion .form-check-input:checked{background-color:#4f46e5;border-color:#4f46e5}
.bp-motion .form-check-input:focus-visible{outline:2px solid #4f46e5;outline-offset:3px;box-shadow:none}
/* Demo tile height; corners, border and the name chip use Bootstrap utilities */
.bp-tile{min-height:var(--bp-tile-h,180px)}
.bp-tile{--bp-tile-h:220px}
/* Animated backgrounds that move only transform and opacity, so the browser composites them without repainting. They stop under prefers-reduced-motion and when the pause checkbox is ticked. Each background is a utility; pass colours and speed as arbitrary properties, e.g. [--bp-speed:20s]. */
@keyframes bp-slide{to{transform:translateX(-50%)}}
@keyframes bp-drift{50%{transform:translate(18%,12%) scale(1.12)}to{transform:translate(-10%,22%) scale(.92)}}
@keyframes bp-sway{from{transform:translateX(-8%) rotate(-8deg) scaleY(.9)}to{transform:translateX(8%) rotate(4deg) scaleY(1.15)}}
@keyframes bp-sway-b{from{transform:translate(6%,4%) rotate(6deg);opacity:.35}to{transform:translate(-6%,-4%) rotate(-4deg)}}
@keyframes bp-rise{to{transform:translateY(calc(var(--bp-size) * -1))}}
@keyframes bp-twinkle{from{opacity:.45}to{opacity:1}}
/* Moving gradient: a layer four tiles wide holds the gradient twice and slides left by one repeat, so the loop is seamless */
@utility bp-flow{
position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg,#6366f1);
&::before{content:"";position:absolute;inset:0 auto 0 0;width:400%;z-index:-1;pointer-events:none;opacity:var(--bp-opacity,1);background:linear-gradient(90deg,var(--bp-c1,#6366f1),var(--bp-c2,#ec4899),var(--bp-c3,#f59e0b),var(--bp-c1,#6366f1)) 0 0/50% 100% repeat-x;will-change:transform;animation:bp-slide var(--bp-speed,14s) linear infinite}
@media (prefers-reduced-motion:reduce){&::before{animation:none}&::after{animation:none}& > i{animation:none}}
}
/* Floating blobs: three soft radial circles (no filter) drifting on transform only */
@utility bp-blobs{
position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg,#f8fafc);
& > i{position:absolute;z-index:-1;width:var(--bp-size,70%);aspect-ratio:1;border-radius:50%;pointer-events:none;opacity:var(--bp-opacity,.9);will-change:transform;animation:bp-drift var(--bp-speed,16s) ease-in-out infinite alternate}
& > i:nth-of-type(1){top:-30%;left:-15%;background:radial-gradient(closest-side,var(--bp-c1,#a5b4fc),transparent)}
& > i:nth-of-type(2){top:0;right:-25%;background:radial-gradient(closest-side,var(--bp-c2,#f9a8d4),transparent);animation-duration:calc(var(--bp-speed,16s) * 1.3);animation-direction:alternate-reverse}
& > i:nth-of-type(3){bottom:-45%;left:25%;background:radial-gradient(closest-side,var(--bp-c3,#99f6e4),transparent);animation-duration:calc(var(--bp-speed,16s) * .8)}
@media (prefers-reduced-motion:reduce){&::before{animation:none}&::after{animation:none}& > i{animation:none}}
}
/* Aurora: two layers of soft bands that sway and fade, on transform and opacity */
@utility bp-aurora{
position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg,linear-gradient(#020617,#0b1d33));
&::before{content:"";position:absolute;inset:-25% -15%;z-index:-1;pointer-events:none;opacity:var(--bp-opacity,.75);will-change:transform;background:radial-gradient(45% 20% at 30% 45%,var(--bp-c1,#34d399),transparent 70%),radial-gradient(40% 16% at 66% 36%,var(--bp-c2,#22d3ee),transparent 70%);animation:bp-sway var(--bp-speed,12s) ease-in-out infinite alternate}
&::after{content:"";position:absolute;inset:-25% -15%;z-index:-1;pointer-events:none;opacity:var(--bp-opacity,.75);will-change:transform,opacity;background:radial-gradient(35% 18% at 55% 62%,var(--bp-c3,#a78bfa),transparent 70%);animation:bp-sway-b calc(var(--bp-speed,12s) * 1.4) ease-in-out infinite alternate}
@media (prefers-reduced-motion:reduce){&::before{animation:none}&::after{animation:none}& > i{animation:none}}
}
/* Starfield: two star layers rising by exactly one tile per loop; the near layer also twinkles */
@utility bp-stars{
position:relative;isolation:isolate;overflow:hidden;background:var(--bp-bg,linear-gradient(#030712,#1e1b4b));
&::before{content:"";position:absolute;inset:0 0 auto;height:calc(100% + var(--bp-size,200px));z-index:-1;pointer-events:none;opacity:var(--bp-opacity,1);will-change:transform;background-image:radial-gradient(circle at 43% 21%,var(--bp-star,#fff) 1px,transparent 1.6px),radial-gradient(circle at 85% 8%,var(--bp-star,#fff) 0.8px,transparent 1.4px),radial-gradient(circle at 70% 14%,var(--bp-star,#fff) 1px,transparent 1.6px),radial-gradient(circle at 76% 9%,var(--bp-star,#fff) 1.2px,transparent 1.8px),radial-gradient(circle at 29% 6%,var(--bp-star,#fff) 0.8px,transparent 1.4px),radial-gradient(circle at 57% 55%,var(--bp-star,#fff) 0.8px,transparent 1.4px),radial-gradient(circle at 32% 13%,var(--bp-star,#fff) 1.2px,transparent 1.8px),radial-gradient(circle at 56% 9%,var(--bp-star,#fff) 1.2px,transparent 1.8px),radial-gradient(circle at 17% 30%,var(--bp-star,#fff) 1.2px,transparent 1.8px),radial-gradient(circle at 82% 76%,var(--bp-star,#fff) 0.8px,transparent 1.4px),radial-gradient(circle at 75% 76%,var(--bp-star,#fff) 1px,transparent 1.6px),radial-gradient(circle at 8% 30%,var(--bp-star,#fff) 0.8px,transparent 1.4px),radial-gradient(circle at 73% 19%,var(--bp-star,#fff) 1px,transparent 1.6px),radial-gradient(circle at 55% 20%,var(--bp-star,#fff) 1.2px,transparent 1.8px);background-size:var(--bp-size,200px) var(--bp-size,200px);animation:bp-rise var(--bp-speed,40s) linear infinite}
&::after{content:"";position:absolute;inset:0 0 auto;height:calc(100% + var(--bp-size,200px));z-index:-1;pointer-events:none;will-change:transform,opacity;background-image:radial-gradient(circle at 59% 73%,var(--bp-star,#fff) 2px,transparent 2.6px),radial-gradient(circle at 59% 67%,var(--bp-star,#fff) 1.6px,transparent 2.2px),radial-gradient(circle at 25% 67%,var(--bp-star,#fff) 2px,transparent 2.6px),radial-gradient(circle at 82% 80%,var(--bp-star,#fff) 1.6px,transparent 2.2px),radial-gradient(circle at 14% 59%,var(--bp-star,#fff) 2px,transparent 2.6px),radial-gradient(circle at 20% 13%,var(--bp-star,#fff) 1.6px,transparent 2.2px);background-size:var(--bp-size,200px) var(--bp-size,200px);background-position:calc(var(--bp-size,200px) / 3) 0;animation:bp-rise calc(var(--bp-speed,40s) / 2) linear infinite,bp-twinkle 3s ease-in-out infinite alternate}
@media (prefers-reduced-motion:reduce){&::before{animation:none}&::after{animation:none}& > i{animation:none}}
}
/* Pause checkbox: freezes every animation inside .bp-motion (:has()). */
.bp-motion:has(.bp-pause:checked) :is(.bp-flow,.bp-aurora,.bp-stars)::before,.bp-motion:has(.bp-pause:checked) :is(.bp-aurora,.bp-stars)::after,.bp-motion:has(.bp-pause:checked) .bp-blobs > i{animation-play-state:paused}
About this set
Four animated backgrounds that move only transform and opacity, so the browser can hand them to the compositor instead of repainting the gradients on every frame. Moving gradient is a layer four tiles wide that holds the gradient twice and slides left by exactly one repeat, so the loop has no jump. Floating blobs are three soft radial circles, drawn without a blur filter, drifting and scaling on different timings. Aurora is two layers of soft green, cyan and violet bands that sway, stretch and fade. Starfield has two star layers that rise by exactly one background tile per loop, and the nearer layer also twinkles through opacity. Use the moving gradient for banners and launch pages, the blobs behind sign-up and pricing sections, and the aurora or starfield behind dark heroes and event pages. Every animation stops under prefers-reduced-motion and shows a still frame. A Pause background motion checkbox above the tiles freezes all of them at once through :has(), which also covers the WCAG need to let people stop motion that lasts more than five seconds. Speeds and colours are custom properties.
What’s included
Moving gradient, floating blobs, aurora and starfield
Transform and opacity only, no animated gradients or filters
Seamless loops: layers move by exactly one repeat
Stops under prefers-reduced-motion and with a pause checkbox
Plain CSS, Bootstrap 5 and Tailwind CSS 4 versions of the same design
Accessibility
All animation is removed under prefers-reduced-motion, and a labelled Pause background motion checkbox freezes every background in the group, so motion can be stopped as WCAG 2.2.2 requires. The checkbox shows a 2px outline on keyboard focus. The backgrounds are decorative, and name labels are opaque chips above 4.5:1.
Customise it
Set --bp-speed for the loop length, --bp-c1 to --bp-c3 for colours, --bp-bg for the ground, --bp-size for blob or star tile size and --bp-opacity for strength. In Tailwind pass them as arbitrary properties on bp-flow, bp-blobs, bp-aurora or bp-stars.