/* =============================================================
   CHAPTER FX  ·  reusable "living world" system
   Shared by every BuildVerse chapter (Food You, IBM, Journey,
   Suzhal, Signal Yard, Fusion Works). Pair with chapter-fx.js.
   Philosophy: less animation, more life. Calm, cinematic,
   handcrafted. Transform/opacity only. Honours reduced-motion.
   ============================================================= */
:root{
  --fx-accent:#e8520a;
  --fx-warm:255,196,120;      /* warm light rgb */
  --fx-water:150,190,220;     /* stream rgb     */
  --wind:0;                   /* shared breeze, set by JS (-1..1) */
}

/* ---------------------------------------------------------------
   PARALLAX  — scene drifts a few px toward the cursor (overscan
   via scale so edges never expose). JS feeds --sx/--sy.
   --------------------------------------------------------------- */
.fx-scene{
  transform:translate(-50%,-50%) translate(var(--sx,0px),var(--sy,0px)) scale(1.035);
}

/* ---------------------------------------------------------------
   SHARED WIND — one breeze moves everything together.
   Elements opt in with .fx-wind and a per-element amount --wa
   (degrees) + pivot --wo. No transition: JS updates --wind
   smoothly each frame, so motion is already eased.
   --------------------------------------------------------------- */
.fx-wind{ transform-origin:var(--wo, top center);
  transform:rotate(calc(var(--wind) * var(--wa,1) * 1deg)); }

/* ---------------------------------------------------------------
   WARM LIGHT — breathing, never flickering. ~2–3% over 6–8s.
   --------------------------------------------------------------- */
.fx-lamp{position:absolute; pointer-events:none;}
.fx-lamp i{display:block; width:var(--s,90px); height:var(--s,90px); transform:translate(-50%,-30%);
  border-radius:50%; mix-blend-mode:screen;
  background:radial-gradient(closest-side, rgba(var(--fx-warm),.5), transparent 70%);
  animation:fx-breathe var(--sp,7s) ease-in-out infinite;}
@keyframes fx-breathe{0%,100%{opacity:.84; transform:translate(-50%,-30%) scale(1)}
  50%{opacity:.9; transform:translate(-50%,-30%) scale(1.02)}}

/* ---------------------------------------------------------------
   STEAM — improved: 5 motes, staggered non-repeating timing,
   opacity variation, drifts with the shared wind.
   --------------------------------------------------------------- */
.fx-steam{position:absolute; width:var(--w,34px); height:var(--h,90px);
  transform:translateX(calc(-50% + var(--wind) * 7px)); pointer-events:none;}
.fx-steam b{position:absolute; left:50%; bottom:0; width:9px; height:9px; border-radius:50%;
  background:radial-gradient(closest-side, rgba(255,248,238,.6), transparent); filter:blur(2px);
  animation:fx-steam 6.5s ease-in infinite;}
.fx-steam b:nth-child(1){animation-duration:6.2s; animation-delay:0s;   left:50%;}
.fx-steam b:nth-child(2){animation-duration:7.3s; animation-delay:1.7s; left:61%;}
.fx-steam b:nth-child(3){animation-duration:5.6s; animation-delay:3.1s; left:41%;}
.fx-steam b:nth-child(4){animation-duration:8.1s; animation-delay:4.4s; left:55%;}
.fx-steam b:nth-child(5){animation-duration:6.9s; animation-delay:2.6s; left:46%;}
@keyframes fx-steam{
  0%{opacity:0; transform:translate(-50%,6px) scale(.55)}
  22%{opacity:.5}
  55%{opacity:.32; transform:translate(-64%,-34px) scale(1.3)}
  100%{opacity:0; transform:translate(-78%,-74px) scale(2.1)}}

/* cooking heat shimmer (activity cue near a stove) */
.fx-heat{position:absolute; transform:translate(-50%,-50%); pointer-events:none;
  width:var(--w,70px); height:var(--h,60px); border-radius:40%;
  background:radial-gradient(closest-side, rgba(255,170,90,.14), transparent 72%);
  filter:blur(3px); mix-blend-mode:screen; animation:fx-heat 3.6s ease-in-out infinite;}
@keyframes fx-heat{0%,100%{opacity:.5; transform:translate(-50%,-50%) scale(1)}
  50%{opacity:.8; transform:translate(-50%,-52%) scale(1.04)}}

/* ---------------------------------------------------------------
   WATER SYSTEM — a garden stream. Two flow layers at
   non-multiple speeds + a slow warm sheen = never-repeating
   shimmer that rides on top of the baked water (soft-light).
   --------------------------------------------------------------- */
.fx-water{position:absolute; pointer-events:none; overflow:hidden;
  border-radius:46% 46% 40% 40% / 12%; mix-blend-mode:soft-light; opacity:.85;}
.fx-water__flow{position:absolute; inset:-24%;
  background-image:
    repeating-linear-gradient(112deg, rgba(255,255,255,.06) 0 5px, transparent 5px 15px),
    repeating-linear-gradient(66deg, rgba(var(--fx-water),.12) 0 8px, transparent 8px 22px);
  animation:fx-flow 9s linear infinite;}
.fx-water__flow.b{animation-duration:13.5s; animation-direction:reverse; opacity:.7;
  background-image:repeating-linear-gradient(120deg, rgba(255,236,205,.07) 0 9px, transparent 9px 25px);}
.fx-water__sheen{position:absolute; inset:0; mix-blend-mode:screen;
  background:radial-gradient(58% 46% at 42% 28%, rgba(255,220,170,.16), transparent 70%);
  animation:fx-sheen 11s ease-in-out infinite;}
@keyframes fx-flow{to{transform:translate(-15%,13%)}}
@keyframes fx-sheen{0%,100%{opacity:.45; transform:translate(0,0)}
  50%{opacity:.85; transform:translate(4%,-3%)}}

/* ripple rings around supports / rocks */
.fx-ripple{position:absolute; transform:translate(-50%,-50%); pointer-events:none;
  width:var(--w,90px); height:var(--h,30px);}
.fx-ripple span{position:absolute; left:50%; top:50%; width:24px; height:9px;
  transform:translate(-50%,-50%) scale(.4); border-radius:50%;
  border:1.2px solid rgba(var(--fx-water),.5); mix-blend-mode:screen;
  animation:fx-ripple var(--sp,6s) ease-out infinite;}
.fx-ripple span:nth-child(2){animation-delay:calc(var(--sp,6s) / 3)}
.fx-ripple span:nth-child(3){animation-delay:calc(var(--sp,6s) / 3 * 2)}
@keyframes fx-ripple{0%{opacity:0; transform:translate(-50%,-50%) scale(.4)}
  16%{opacity:.5} 100%{opacity:0; transform:translate(-50%,-50%) scale(3)}}

/* a single leaf drifting downstream (spawned by JS) */
.fx-leaf-drift{position:absolute; width:12px; height:8px; pointer-events:none; z-index:8;
  background:radial-gradient(closest-side at 30% 40%, #7fae52, #4f7d34 75%, transparent);
  border-radius:60% 10% 55% 12%; filter:drop-shadow(0 2px 2px rgba(30,50,15,.35)); opacity:0;}

/* ---------------------------------------------------------------
   FOLIAGE / CLOTH — wind-driven, bottom or top anchored.
   --------------------------------------------------------------- */
.fx-sway{position:absolute; pointer-events:none;}      /* combine with .fx-wind */
.fx-sway i{display:block; width:var(--s,26px); height:var(--s,26px); transform:translate(-50%,-100%);
  border-radius:60% 60% 55% 55%; filter:blur(2.5px); opacity:.2;
  background:radial-gradient(closest-side, rgba(120,160,70,.9), rgba(90,130,50,.2) 70%, transparent);}

.fx-cloth{position:absolute; pointer-events:none; transform-origin:top center;}
.fx-cloth i{display:block; width:var(--w,20px); height:var(--h,30px); transform:translateX(-50%);
  border-radius:2px 2px 5px 5px; opacity:.5; filter:blur(.4px);
  background:linear-gradient(180deg, rgba(250,246,238,.85), rgba(225,216,200,.6));
  box-shadow:0 4px 8px -5px rgba(60,45,25,.5);}

/* ---------------------------------------------------------------
   BIRD — idle most of the time; JS triggers hop / peck / tilt.
   --------------------------------------------------------------- */
.fx-bird{position:absolute; width:13px; height:11px; pointer-events:none; z-index:9;
  transform-origin:60% 90%;}
.fx-bird b{position:absolute; left:0; bottom:0; width:11px; height:8px; border-radius:60% 60% 50% 50%;
  background:rgba(58,42,26,.72);}                        /* body */
.fx-bird b::after{content:""; position:absolute; right:-3px; top:-4px; width:6px; height:6px;
  border-radius:50%; background:rgba(58,42,26,.78);}     /* head */
.fx-bird b::before{content:""; position:absolute; right:-6px; top:-1px; width:3px; height:1.5px;
  border-radius:1px; background:var(--fx-accent);}       /* beak */
.fx-bird.hop{animation:fx-hop .6s ease}
.fx-bird.peck{animation:fx-peck .7s ease}
.fx-bird.tilt{animation:fx-tilt 1.4s ease}
@keyframes fx-hop{0%,100%{transform:translateY(0)}35%{transform:translateY(-5px)}70%{transform:translateY(0)}}
@keyframes fx-peck{0%,100%{transform:rotate(0)}30%{transform:rotate(16deg)}45%{transform:rotate(2deg)}60%{transform:rotate(14deg)}}
@keyframes fx-tilt{0%,100%{transform:rotate(0)}40%,70%{transform:rotate(-11deg)}}

/* ---------------------------------------------------------------
   SCOOTER HEADLIGHT — the parked Food You scooter's lamp blinks
   on/off (~2–3s) for a small sign of life. No movement.
   --------------------------------------------------------------- */
.fx-headlight{position:absolute; transform:translate(-50%,-50%); pointer-events:none; z-index:7;
  width:var(--s,14px); height:var(--s,14px); border-radius:50%;
  background:radial-gradient(closest-side, #fffdf2 0%, rgba(255,236,180,.95) 34%, rgba(255,206,120,.5) 62%, transparent 80%);
  box-shadow:0 0 10px 3px rgba(255,214,140,.85), 0 0 20px 8px rgba(255,190,90,.4);
  animation:fx-headlight 2.8s steps(1,end) infinite;}
@keyframes fx-headlight{0%,40%{opacity:1} 41%,100%{opacity:0}}

/* ---------------------------------------------------------------
   ENVIRONMENTAL SHADOW — slow breathing vignette.
   --------------------------------------------------------------- */
.fx-shadow{position:absolute; inset:0; pointer-events:none;
  box-shadow:inset 0 0 220px 40px rgba(40,22,6,.42); animation:fx-shadowb 16s ease-in-out infinite;}
@keyframes fx-shadowb{0%,100%{opacity:.85} 50%{opacity:1}}

/* floating dust canvas (drawn by chapter-fx.js) */
canvas.fx-dust{position:fixed; inset:0; z-index:5; pointer-events:none;}

/* ---------------------------------------------------------------
   WATERFALL SPARKLE — twinkles at the base of a waterfall.
   --------------------------------------------------------------- */
.fx-sparkle{position:absolute; pointer-events:none; z-index:6;
  width:var(--w,50px); height:var(--h,30px); transform:translate(-50%,-50%);}
.fx-sparkle b{position:absolute; width:3px; height:3px; border-radius:50%;
  background:#fff; box-shadow:0 0 6px rgba(255,240,200,.9);
  animation:fx-sparkle 2.4s ease-in-out infinite;}
.fx-sparkle b:nth-child(1){left:12%; top:35%; animation-delay:0s}
.fx-sparkle b:nth-child(2){left:32%; top:60%; animation-delay:.5s}
.fx-sparkle b:nth-child(3){left:52%; top:22%; animation-delay:1s}
.fx-sparkle b:nth-child(4){left:74%; top:55%; animation-delay:1.5s}
.fx-sparkle b:nth-child(5){left:88%; top:38%; animation-delay:2s}
@keyframes fx-sparkle{0%,100%{opacity:0; transform:scale(.5)} 50%{opacity:1; transform:scale(1.2)}}

/* ---------------------------------------------------------------
   SOLAR PANEL SHEEN — a slow moving sun reflection.
   --------------------------------------------------------------- */
.fx-panel{position:absolute; pointer-events:none; z-index:5;
  width:var(--w,60px); height:var(--h,30px); transform:translate(-50%,-50%);
  border-radius:2px; overflow:hidden; mix-blend-mode:screen;}
.fx-panel::before{content:""; position:absolute; top:0; bottom:0; left:-40%; width:40%;
  background:linear-gradient(100deg, transparent, rgba(255,244,200,.55) 45%, transparent 60%);
  animation:fx-panel 12s ease-in-out infinite;}
@keyframes fx-panel{0%,100%{transform:translateX(0)} 50%{transform:translateX(280%)}}

/* ---------------------------------------------------------------
   SKY CLOUDS — soft drifting patches across the horizon band.
   --------------------------------------------------------------- */
.fx-cloud{position:absolute; pointer-events:none; z-index:4;
  width:var(--w,120px); height:var(--h,26px); transform:translateX(-100vw);
  background:radial-gradient(70% 130% at 30% 60%, rgba(255,248,232,.55), transparent 70%),
    radial-gradient(60% 130% at 65% 55%, rgba(255,248,232,.45), transparent 72%);
  filter:blur(4px); mix-blend-mode:screen;
  animation:fx-cloud var(--sp,140s) linear infinite;}
@keyframes fx-cloud{from{transform:translateX(-30vw)} to{transform:translateX(130vw)}}

/* background sky-bird flock (occasional flight) — spawned by JS */
.fx-flock{position:absolute; pointer-events:none; z-index:4; width:80px; height:14px; opacity:.55;}
.fx-flock b{position:absolute; top:50%; left:0; width:6px; height:2px; border-radius:2px;
  background:rgba(50,35,20,.7);}
.fx-flock b::before,.fx-flock b::after{content:""; position:absolute; top:0; width:5px; height:2px;
  background:rgba(50,35,20,.7); border-radius:1px;
  animation:fx-flap 1.1s ease-in-out infinite;}
.fx-flock b::before{left:-4px; transform-origin:right center; transform:rotate(20deg)}
.fx-flock b::after{right:-4px; transform-origin:left center; transform:rotate(-20deg)}
.fx-flock b:nth-child(2){left:22px; top:20%; animation-delay:.4s}
.fx-flock b:nth-child(3){left:44px; top:70%; animation-delay:.2s}
.fx-flock b:nth-child(4){left:64px; top:30%; animation-delay:.6s}

/* ---------------------------------------------------------------
   MONITOR SCREEN — a tiny animated screen over a baked monitor.
   The JS swaps its inner "scene" (cursor / windows / graph)
   every few seconds so each screen updates independently.
   --------------------------------------------------------------- */
.fx-monitor{position:absolute; transform:translate(-50%,-50%); pointer-events:none; z-index:6;
  width:var(--w,42px); height:var(--h,28px); border-radius:2px; overflow:hidden;
  background:linear-gradient(160deg,#1a2a3a,#0e1a25); mix-blend-mode:screen; opacity:.9;}
.fx-monitor::before{content:""; position:absolute; inset:0; opacity:.4;
  background-image:repeating-linear-gradient(rgba(255,255,255,.15) 0 1px, transparent 1px 3px);}
.fx-monitor .cursor{position:absolute; width:3px; height:5px; background:#fff;
  transition:left .8s ease, top .8s ease;}
.fx-monitor .win{position:absolute; border-radius:1px;}
.fx-monitor .bar{position:absolute; left:12%; right:12%; bottom:24%; height:6%; border-radius:1px;
  background:rgba(255,255,255,.14);}
.fx-monitor .bar b{display:block; height:100%; background:#5ad1ff; border-radius:1px;
  transition:width 1.6s ease;}
.fx-monitor .chart{position:absolute; left:14%; right:14%; top:18%; height:52%;}
.fx-monitor .chart b{position:absolute; bottom:0; width:12%; background:#8fd7ff; opacity:.8;
  transition:height .8s ease;}

/* =============================================================
   INTERACTION — one carved wooden board holds the chapter's
   actions, with a warm outer glow that signals "clickable".
   Reusable: ChapterFX.board(scene, items, { x, y, w, head, onSelect }).
   ============================================================= */
.fx-board{position:absolute; transform:translate(-50%,-50%); z-index:22; width:var(--bw,230px);
  padding:12px 12px 14px; border-radius:12px;
  background:
    repeating-linear-gradient(93deg, rgba(0,0,0,.05) 0 2px, transparent 2px 8px),
    linear-gradient(180deg,#7c4c26,#5a3316);
  border:2px solid #3f250f;
  box-shadow:0 26px 46px -20px rgba(15,8,2,.85),
    inset 0 2px 0 rgba(255,214,164,.4), inset 0 -4px 8px rgba(0,0,0,.5),
    inset 0 0 0 1px rgba(0,0,0,.3);}
/* pulsing warm outer glow — the affordance */
.fx-board::after{content:""; position:absolute; inset:-9px; border-radius:18px; z-index:-1; pointer-events:none;
  background:radial-gradient(closest-side, rgba(255,176,86,.55), transparent 78%);
  filter:blur(7px); animation:fx-boardglow 3.4s ease-in-out infinite;}
@keyframes fx-boardglow{0%,100%{opacity:.4}50%{opacity:.85}}
.fx-board__head{text-align:center; font-family:'Cormorant Garamond',serif; font-weight:700;
  font-size:12px; letter-spacing:.3em; text-transform:uppercase; color:#f2dcb8;
  padding-bottom:9px; margin-bottom:5px; border-bottom:1px solid rgba(0,0,0,.4);
  text-shadow:0 1px 0 rgba(0,0,0,.55), 0 -1px 0 rgba(255,214,164,.18);
  box-shadow:0 1px 0 rgba(255,214,164,.14);}
.fx-board__row{display:flex; align-items:center; gap:11px; width:100%; text-align:left;
  padding:9px 11px; border-radius:8px; cursor:pointer; color:#f4e6cb; font:inherit;
  background:none; border:1px solid transparent; -webkit-tap-highlight-color:transparent;
  transition:background .25s ease, transform .25s ease, box-shadow .25s ease, border-color .25s ease;}
.fx-board__row:hover,.fx-board__row:focus-visible{
  background:linear-gradient(180deg, rgba(255,192,112,.24), rgba(255,158,74,.12));
  border-color:rgba(255,200,140,.4); transform:translateX(3px);
  box-shadow:inset 0 0 0 1px rgba(255,224,184,.18), 0 0 16px rgba(255,168,78,.4);}
.fx-board__ic{flex:none; width:26px; font-size:20px; text-align:center; line-height:1;
  filter:drop-shadow(0 1px 2px rgba(0,0,0,.55));}
.fx-board__tx{display:flex; flex-direction:column; gap:2px; line-height:1;}
.fx-board__nm{font-family:'Cormorant Garamond',serif; font-weight:600; font-size:17px; color:#fdf0d5;}
.fx-board__rl{display:flex; align-items:center; gap:5px; font-size:9px; font-weight:600;
  letter-spacing:.2em; text-transform:uppercase; color:#ffb277;}
.fx-board__rl::before{content:"→"; font-size:11px; letter-spacing:0;}

/* ---------------------------------------------------------------
   ACCESSIBILITY — honour reduced-motion everywhere.
   --------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  .fx-lamp i,.fx-steam b,.fx-heat,.fx-water__flow,.fx-water__sheen,.fx-ripple span,
  .fx-shadow,.fx-bird,.fx-headlight,.fx-board::after{animation:none!important}
  .fx-scene{transform:translate(-50%,-50%) scale(1.02)!important}
  .fx-wind{transform:none!important}
}
