/* The moving background behind the panels, ported from Sabo OS (frontend/src/home/backdrop.css).
   One fixed layer under everything; colours from theme.css (--fx1..3, --particle), strength from --fx-strength.
   Only transform and opacity animate. Reduced motion holds it all still (theme.css). */
.backdrop {
  position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; background: var(--bg);
  --fx-a: 60%;
  --fx-c1: color-mix(in srgb, var(--fx1) var(--fx-a), transparent);
  --fx-c2: color-mix(in srgb, var(--fx2) var(--fx-a), transparent);
  --fx-c3: color-mix(in srgb, var(--fx3) var(--fx-a), transparent);
  --vignette: rgb(20 25 40 / .14);
}
[data-mode="dark"] .backdrop { --fx-a: 46%; --vignette: rgb(0 0 0 / .5); }
.backdrop > *, .fx-scene > * { position: absolute; display: block; pointer-events: none; }
/* the scene is drawn zoomed in: a third larger than the screen, from the centre */
.fx-scene { inset: 0; scale: var(--fx-zoom, 1.35); }

/* colour washes (Seasons): three soft rounds drifting on long, unrelated clocks.
   Sized to the screen's longer side (vmax) so on a tall phone they reach the lower panel too, and a little
   stronger than Sabo OS: the panels cover the whole screen here, so the washes are what Transparency and Blur show. */
.fx-wash { border-radius: 50%; will-change: transform; opacity: calc(var(--fx-strength, 1) / 1.2); }
.fx-wash.a { width: 80vmax; height: 80vmax; top: -30vmax; left: -30vmax; background: radial-gradient(closest-side circle, var(--fx-c1), transparent 72%); animation: fx-drift-a 47s ease-in-out infinite alternate; }
.fx-wash.b { width: 84vmax; height: 84vmax; bottom: -32vmax; right: -30vmax; background: radial-gradient(closest-side circle, var(--fx-c2), transparent 72%); animation: fx-drift-b 59s ease-in-out infinite alternate; }
.fx-wash.c { width: 60vmax; height: 60vmax; top: 35%; left: 20%; background: radial-gradient(closest-side circle, var(--fx-c3), transparent 70%); animation: fx-drift-c 71s ease-in-out infinite alternate; }
@keyframes fx-drift-a { from { transform: translate(0, 0) scale(1); } to { transform: translate(9vw, 7vh) scale(1.15); } }
@keyframes fx-drift-b { from { transform: translate(0, 0) scale(1.1); } to { transform: translate(-8vw, -6vh) scale(.95); } }
@keyframes fx-drift-c { from { transform: translate(0, 0); } to { transform: translate(-12vw, 9vh) scale(1.2); } }

/* fine static film grain over the washes */
.fx-grain { inset: 0; opacity: calc(.12 * var(--fx-strength, 1)); mix-blend-mode: overlay; background-image: url("fx/grain.png"); }

/* Seasons: falling things sway and tumble on separate clocks */
.fx-gust { inset: 0; }
.fx-particle { position: absolute; top: -4vh; will-change: transform, opacity; animation: fx-fall linear infinite; }
.fx-particle .inner { display: block; animation: fx-sway var(--sway-dur, 3.4s) ease-in-out infinite alternate, fx-tumble var(--tumble-dur, 6s) linear infinite; }
@keyframes fx-fall { 0% { transform: translateY(-6vh); opacity: 0; } 8%, 88% { opacity: min(1, calc(var(--p-op, .8) * var(--fx-strength, 1))); } 100% { transform: translateY(108vh); opacity: 0; } }
@keyframes fx-sway { from { translate: calc(var(--sway, 20px) * -1) 0; } to { translate: var(--sway, 20px) 0; } }
@keyframes fx-tumble { from { rotate: 0deg; } to { rotate: 360deg; } }
.fx-particle.petal .inner { width: 9px; height: 7px; background: var(--particle); border-radius: 80% 10% 80% 10%; opacity: .7; }
.fx-particle.leaf .inner { width: 11px; height: 8px; background: var(--particle); border-radius: 0 70% 0 70%; opacity: .6; }
.fx-particle.snow .inner { width: 4px; height: 4px; background: var(--particle); border-radius: 50%; box-shadow: 0 0 5px 1px rgb(200 220 240 / .4); opacity: .7; animation: fx-sway var(--sway-dur, 3.4s) ease-in-out infinite alternate; }
.fx-particle.firefly { animation-name: fx-hover; animation-timing-function: ease-in-out; top: auto; }
.fx-particle.firefly .inner { width: 5px; height: 5px; background: var(--particle); border-radius: 50%; box-shadow: 0 0 11px 4px color-mix(in srgb, var(--particle) 45%, transparent); animation: fx-flicker var(--sway-dur, 2.8s) ease-in-out infinite; }
.fx-particle.rain { animation-timing-function: linear; }
.fx-particle.rain .inner { width: 1.5px; height: 20px; background: linear-gradient(to bottom, transparent, var(--particle)); border-radius: 1px; rotate: 14deg; opacity: .55; animation: none; }
@keyframes fx-hover { 0%, 100% { transform: translate(0, 0); } 20% { transform: translate(3vw, -5vh); } 45% { transform: translate(-2.5vw, -9vh); } 70% { transform: translate(3.5vw, -4vh); } }
@keyframes fx-flicker { 0%, 100% { opacity: .1; transform: scale(.8); } 50% { opacity: 1; transform: scale(1.15); } }

/* Archive: warm out-of-focus lamps behind smoked glass, rising dust, a vignette and a hairline frame at the edge */
.backdrop[data-fx="archive"] .fx-scene::before { content: ""; position: absolute; inset: 0; background: radial-gradient(90% 70% at 50% 45%, color-mix(in srgb, var(--fx-c1) 7%, transparent), transparent 70%); }
.fx-bokeh { border-radius: 50%; translate: -50% -50%; will-change: transform, opacity; opacity: calc(var(--b-op, .5) * var(--fx-strength, 1));
  background: radial-gradient(circle closest-side, var(--b-c) 0, color-mix(in srgb, var(--b-c) 55%, transparent) 45%, transparent 100%);
  animation: fx-bokeh-drift 90s ease-in-out infinite alternate, fx-bokeh-breathe 10s ease-in-out infinite alternate; }
.fx-bokeh.c1 { --b-c: var(--fx-c1); } .fx-bokeh.c2 { --b-c: var(--fx-c2); } .fx-bokeh.c3 { --b-c: var(--fx-c3); }
@keyframes fx-bokeh-drift { from { transform: translate3d(0, 0, 0) scale(1); } to { transform: translate3d(4vw, -3vh, 0) scale(1.12); } }
@keyframes fx-bokeh-breathe { from { opacity: calc(var(--b-op, .5) * .55 * var(--fx-strength, 1)); } to { opacity: calc(var(--b-op, .5) * var(--fx-strength, 1)); } }
.fx-mote { border-radius: 50%; background: var(--particle); box-shadow: 0 0 4px 1px color-mix(in srgb, var(--particle) 60%, transparent); will-change: transform, opacity;
  opacity: calc(var(--m-op, .5) * var(--fx-strength, 1)); animation: fx-mote-rise 40s linear infinite, fx-mote-twinkle 4s ease-in-out infinite alternate; }
@keyframes fx-mote-rise { 0% { transform: translate3d(0, 0, 0) scale(0); } 12% { transform: translate3d(calc(var(--m-dx, 2vw) * .12), -4.5vh, 0) scale(1); } 88% { transform: translate3d(calc(var(--m-dx, 2vw) * .88), -33vh, 0) scale(1); } 100% { transform: translate3d(var(--m-dx, 2vw), -38vh, 0) scale(0); } }
@keyframes fx-mote-twinkle { from { opacity: calc(var(--m-op, .5) * .2 * var(--fx-strength, 1)); } to { opacity: calc(var(--m-op, .5) * var(--fx-strength, 1)); } }
.fx-vignette { inset: 0; background: radial-gradient(120% 95% at 50% 42%, transparent 38%, var(--vignette) 100%); opacity: min(1, var(--fx-strength, 1)); }
.fx-frame { inset: 6px; --fr: color-mix(in srgb, var(--accent) 34%, transparent); }
.fx-frame > b { position: absolute; width: 56px; height: clamp(80px, 22vh, 180px); border: 0 solid var(--fr); }
.fx-frame > .tl { left: 0; top: 0; border-width: 1px 0 0 1px; border-top-left-radius: 14px; }
.fx-frame > .tr { right: 0; top: 0; border-width: 1px 1px 0 0; border-top-right-radius: 14px; }
.fx-frame > .bl { left: 0; bottom: 0; border-width: 0 0 1px 1px; border-bottom-left-radius: 14px; }
.fx-frame > .br { right: 0; bottom: 0; border-width: 0 1px 1px 0; border-bottom-right-radius: 14px; }

/* Papyrus: a calm warm page. Two soft pools of lamplight drift very slowly; the edges deepen a touch, like an old page. */
.backdrop[data-fx="papyrus"] { --edge-c: color-mix(in srgb, var(--muted) 16%, transparent); }
[data-mode="dark"] .backdrop[data-fx="papyrus"] { --edge-c: rgb(0 0 0 / .45); }
.backdrop[data-fx="papyrus"] .fx-grain { opacity: calc(.05 * var(--fx-strength, 1)); }
/* a fine, even paper grain over the page: felt more than seen */
.fx-paper { inset: 0; opacity: calc(.07 * var(--fx-strength, 1)); background: var(--text);
  -webkit-mask: var(--paper) 0 0 / 300px 300px repeat; mask: var(--paper) 0 0 / 300px 300px repeat;
  --paper: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='300' height='300'%3E%3Cfilter id='f'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' seed='4' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1.4 0 0 0 -.45'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23f)'/%3E%3C/svg%3E"); }
[data-mode="dark"] .fx-paper { opacity: calc(.05 * var(--fx-strength, 1)); }
.fx-lamp { border-radius: 50%; will-change: transform; translate: -50% -50%; }
.fx-lamp.a { width: 110vmax; height: 110vmax; left: 32%; top: 28%; opacity: calc(.32 * var(--fx-strength, 1));
  background: radial-gradient(closest-side circle, var(--fx-c3), transparent 80%); animation: fx-lamp-a 120s ease-in-out infinite alternate; }
.fx-lamp.b { width: 80vmax; height: 80vmax; left: 78%; top: 85%; opacity: calc(.2 * var(--fx-strength, 1));
  background: radial-gradient(closest-side circle, var(--fx-c1), transparent 78%); animation: fx-lamp-b 140s ease-in-out infinite alternate; }
@keyframes fx-lamp-a { from { transform: translate(0, 0); } to { transform: translate(14vw, 10vh); } }
@keyframes fx-lamp-b { from { transform: translate(0, 0); } to { transform: translate(-12vw, -9vh); } }
.fx-edge { inset: 0; background: radial-gradient(140% 110% at 50% 45%, transparent 55%, var(--edge-c) 100%); opacity: min(1, var(--fx-strength, 1)); }

/* Starfield (Space, from Sabo OS): square 2px stars twinkling in steps on a slow drift, a faint 4px grid, two soft glows */
.fx-glow { inset: 0; opacity: var(--fx-strength, 1); background-image: radial-gradient(60% 50% at 75% 20%, color-mix(in srgb, var(--fx1) 14%, transparent), transparent), radial-gradient(50% 45% at 15% 85%, color-mix(in srgb, var(--fx2) 10%, transparent), transparent); }
.fx-grid { inset: 0; opacity: calc(.06 * var(--fx-strength, 1)); background-image: linear-gradient(var(--text) 1px, transparent 1px), linear-gradient(90deg, var(--text) 1px, transparent 1px); background-size: 4px 4px; }
.backdrop[data-fx="starfield"] .fx-grain { opacity: calc(.06 * var(--fx-strength, 1)); }
.fx-star { width: 2px; height: 2px; background: var(--particle); will-change: transform, opacity; animation: fx-twinkle 3s steps(4, end) infinite, fx-star-drift 120s linear infinite; }
.fx-star.big { width: 4px; height: 4px; animation-name: fx-twinkle, fx-star-drift-slow; }
.fx-star.dim { width: 1px; height: 1px; }
@keyframes fx-twinkle { 0%, 100% { opacity: calc(.15 * var(--fx-strength, 1)); } 50% { opacity: min(1, calc(.9 * var(--fx-strength, 1))); } }
@keyframes fx-star-drift { to { transform: translate3d(-6vw, 3vh, 0); } }
@keyframes fx-star-drift-slow { to { transform: translate3d(-3vw, 1.5vh, 0); } }

/* Reduced motion: the scene holds still (the washes and lamps stay, nothing falls) */
[data-motion="reduced"] .backdrop *, [data-motion="reduced"] .backdrop *::before { animation: none !important; }
[data-motion="reduced"] .fx-particle { display: none; }
