// 赛博宝宝前端（2026-10-21 从 index.html 的内联 <script type="text/babel"> 原样搬出）：App 的院子覆盖层（scene === 'yard'）。
// index.html 按顺序加载 app/*.jsx，前面文件定义的全局（函数、常量、组件）后面的文件直接用。
// App 拆成几段函数，按原来的顺序每次渲染都调用；上一段的局部变量放进 bag 递给下一段（hook 调用顺序不变）。
function renderAppYard(bag) {
  const { blinkOpen, currentMood, dirtyLevel, flowerGiving, flowerNote, gardenPlantOf, gardenPot, handleGarden, handleLightsOn, handlePet,
    isAdventuring, particleData, plantStageImg, pokeBounce, pupiSprite, scene, setFlowerGiving, setFlowerNote, setGardenPot, speechBubble, state,
    weatherState, yardHorseRef } = bag;
  return (
        scene === 'yard' && !state.isComa && (() => {
          const yardNight = weatherState.includes('night');
          const yardRainy = weatherState === 'rainy' || weatherState === 'night_rain';
          const yardSunny = weatherState === 'sunny';
          const yardCloudy = weatherState === 'cloudy';
          // ⛔ 雨夜（night_rain）不挂月亮——真实里云压着就是看不见（小玉要的就是真实）
          const yardMoon = weatherState === 'night' ? moonPhase(new Date()) : null;
          // 七夕前后两天。?qixi=1 是预览开关（跟 ?weather= 一个用法），不到日子也能先看看
          const yardQixi = isQixi() || new URLSearchParams(window.location.search).get('qixi') === '1';
          // ⛔ 银河 2026-08-11 撤了：她定「天空就只有烟花」。月亮留着（那不是七夕装饰，是常驻的）
          const yardGalaxy = false;
          // 烟花只放七夕当晚（?qixi=1 预览时也放，不然看不着）
          const yardFireworks = weatherState === 'night'
            && (isQixiNight() || new URLSearchParams(window.location.search).get('qixi') === '1');
          const yardFoliage = yardFoliageSet();
          const yardIds = (state.inventory || []).filter(i => i.item_type === 'furniture' && YARD_FURNITURE_INFO[i.item_id]).map(i => i.item_id);
          return (
            <div className={`yard-overlay${yardNight ? ' yard-night' : ''}`} style={yardWindVars(weatherState, _weatherCache.windKmh)}>
              <div className={`yard-sky yard-sky-${weatherState}`} />
              {yardSunny && <div className="yard-sun" />}
              {yardMoon && (
                <div className="yard-moon" title={`今晚：${yardMoon.name}，照亮 ${Math.round(yardMoon.illum * 100)}%`}>
                  <svg viewBox="-60 -60 120 120" width="100%" height="100%">
                    {/* 暗面：地球反照，新月那几天天上就只剩这一圈很淡的影子 */}
                    <circle cx="0" cy="0" r="50" fill="rgba(214,226,255,0.10)" />
                    {yardMoon.illum > 0.005 && (
                      <path d={moonLitPath(yardMoon.frac, 50)} fill="#FBF3D5"
                        style={{ filter: `drop-shadow(0 0 ${Math.round(6 + 16 * yardMoon.illum)}px rgba(255,247,214,${(0.25 + 0.45 * yardMoon.illum).toFixed(2)}))` }} />
                    )}
                  </svg>
                </div>
              )}
              {yardGalaxy && (
                <svg className="qixi-galaxy" viewBox="0 0 100 60" preserveAspectRatio="none">
                  <defs>
                    <linearGradient id="qixi-mw" x1="0" y1="0" x2="1" y2="1">
                      <stop offset="0%" stopColor="rgba(198,212,255,0)" />
                      <stop offset="28%" stopColor="rgba(214,224,255,0.34)" />
                      <stop offset="60%" stopColor="rgba(228,233,255,0.40)" />
                      <stop offset="100%" stopColor="rgba(198,212,255,0)" />
                    </linearGradient>
                    <filter id="qixi-soft" x="-30%" y="-60%" width="160%" height="220%">
                      <feGaussianBlur stdDeviation="3.4" />
                    </filter>
                  </defs>
                  {/* 银河：一条斜着糊过去的光带，不能有边 */}
                  <path d="M-12,7 L112,45" stroke="url(#qixi-mw)" strokeWidth="21" fill="none" filter="url(#qixi-soft)" />
                  {qixiStars(96).map((s, i) => (
                    <circle key={i} cx={s.x} cy={s.y} r={s.r * 0.42} fill="rgba(236,242,255,1)" opacity={s.o * 0.9} />
                  ))}
                  {/* ⛔ 鹊桥 2026-08-11 她拍板撤掉了（「算了不要鹊桥了。改成烟花好了」），别再加回来 */}
                </svg>
              )}
              {yardFireworks && (
                <div className="qixi-fireworks">
                  {FIREWORKS.map(f => (
                    <div key={f.seed} className="fw-burst"
                      style={{ left: `${f.left}%`, top: `${f.top}%`, '--c': f.color, '--dur': `${f.dur}s` }}>
                      <div className="fw-shell" style={{ animationDelay: `${f.delay}s` }} />
                      <div className="fw-flash" style={{ animationDelay: `${f.delay}s` }} />
                      {fireworkSparks(f.n, f.r, f.seed).map(s => (
                        <div key={s.i} className="fw-spark" style={{
                          '--tx': `${s.tx.toFixed(1)}px`, '--ty': `${s.ty.toFixed(1)}px`,
                          width: `${s.size.toFixed(1)}px`, height: `${s.size.toFixed(1)}px`,
                          margin: `${(-s.size / 2).toFixed(1)}px 0 0 ${(-s.size / 2).toFixed(1)}px`,
                          animationDelay: `${(f.delay - Number(s.lag)).toFixed(3)}s`,
                        }} />
                      ))}
                    </div>
                  ))}
                </div>
              )}
              {yardQixi && qixiGlows().map(g => (
                <div key={g.key} className={`qixi-ring${g.hue ? ' h' + g.hue : ''}`} style={{
                  left: `${g.left}%`, top: `${g.top}%`, width: `${g.size}px`, height: `${g.size}px`,
                  marginLeft: `${(-g.size / 2).toFixed(1)}px`, marginTop: `${(-g.size / 2).toFixed(1)}px`,
                  animationDuration: `${g.dur}s`, animationDelay: `${g.delay}s`,
                  '--dim': g.dim.toFixed(2),
                }} />
              ))}
              {yardSunny && <>
                <img src={`${ASSETS}/yard_items/cloud_1.png`} className="yard-cloud-img drift-slow" style={{ top: '7%', left: '3%', width: '14%', animationDuration: '96s' }} />
                <img src={`${ASSETS}/yard_items/cloud_4.png`} className="yard-cloud-img drift-slow" style={{ top: '11%', left: '52%', width: '11%', animationDuration: '124s', animationDelay: '-40s' }} />
              </>}
              {yardCloudy && <>
                <img src={`${ASSETS}/yard_items/cloud_6.png`} className="yard-cloud-img drift" style={{ top: '2%', width: '55%', animationDuration: '50s', left: '-60%', opacity: 0.9 }} />
                <img src={`${ASSETS}/yard_items/cloud_3.png`} className="yard-cloud-img drift" style={{ top: '18%', width: '40%', animationDuration: '62s', animationDelay: '-20s', left: '-45%', opacity: 0.8 }} />
                <img src={`${ASSETS}/yard_items/cloud_7.png`} className="yard-cloud-img drift" style={{ top: '8%', width: '48%', animationDuration: '55s', animationDelay: '-35s', left: '-52%', opacity: 0.85 }} />
                <img src={`${ASSETS}/yard_items/cloud_1.png`} className="yard-cloud-img drift" style={{ top: '30%', width: '35%', animationDuration: '70s', animationDelay: '-10s', left: '-40%', opacity: 0.7 }} />
                <img src={`${ASSETS}/yard_items/cloud_4.png`} className="yard-cloud-img drift" style={{ top: '42%', width: '45%', animationDuration: '58s', animationDelay: '-28s', left: '-50%', opacity: 0.65 }} />
              </>}
              <img src={`${ASSETS}/${yardFoliage.base}`} className={`yard-bg${yardNight ? ' yard-dim' : ''}`} alt="院子" style={{ zIndex: 2 }} />
              {/* 院子的风：叶丛图层，跟底图同层（z 2），家具/花盆/宝宝都在它上面 */}
              {yardFoliage.items.length > 0 && (
                <div className="yard-foliage-frame"><div className="yard-foliage-box">
                  {yardFoliage.items.map((f, i) => (
                    <div key={f.img} className="yard-leaf" style={yardLeafStyle(f, i)}>
                      <img src={`${ASSETS}/${yardFoliage.dir}${f.img}`} className={yardNight ? 'yard-dim' : undefined} alt="" draggable={false} />
                    </div>
                  ))}
                </div></div>
              )}
              {yardRainy && <div className="yard-rain-tint" />}
              {/* 家具按当年调坐标的 0.68 框摆（见 style-yard.css .yard-furn-layer） */}
              <div className="yard-furn-layer">
              {yardIds.map(id => {
                const info = YARD_FURNITURE_INFO[id];
                if (!info || !info.parts) return null;
                const potN = id === 'yard_pot' ? Math.max(1, Math.min(3, state.yardPotCount || 1)) : null;
                const parts = potN ? info.parts.slice(0, potN * 2) : info.parts;
                const shadowsList = potN ? (info.shadows || []).slice(0, potN) : (info.shadows || []);
                const lightsLit = id === 'yard_lights' && state.lightsOnAt && (Date.now() - state.lightsOnAt < 12 * 3600000);
                return (
                  <React.Fragment key={id}>
                    {shadowsList.map((s, i) => (
                      <div key={`s${i}`} className="yard-furn-shadow" style={{ ...s, zIndex: 3 }} />
                    ))}
                    {parts.map((p, i) => {
                      // 植物层：空盆不画，种了就按浇水进度换图
                      if (p.plant) {
                        const img = plantStageImg(p.plant);
                        if (!img) return null;
                        return (
                          <img key={i} src={`${ASSETS}/yard_items/${img}`}
                            className={`yard-furn yard-plant${img === 'plant_stage4.png' ? ' bloom' : ''}${yardNight ? ' yard-dim' : ''}`}
                            style={{ ...p.pos, zIndex: (p.pos.zIndex || 1) + 2, pointerEvents: 'auto', cursor: 'pointer', ...yardPlantStyle(p.plant) }}
                            onClick={() => setGardenPot(p.plant)}
                            alt="植物" />
                        );
                      }
                      const isPot = !!p.potNo;
                      // 摇摇马：点一下轻轻摇三四下（2026-10-05 她：「点一下摇摇马，就微微动一下」）
                      if (id === 'rocking_horse') {
                        return (
                          <img key={i} ref={yardHorseRef} src={`${ASSETS}/yard_items/${p.img}`}
                            className={`yard-furn yard-horse${yardNight ? ' yard-dim' : ''}`}
                            style={{ ...p.pos, zIndex: (p.pos.zIndex || 1) + 2 }}
                            onClick={() => yardRockHorse(yardHorseRef.current, 'tap')}
                            draggable={false} alt={info.name} />
                        );
                      }
                      return (
                        <img key={i} src={`${ASSETS}/yard_items/${p.img}`}
                          className={`yard-furn${lightsLit ? ' lights-lit' : yardNight ? ' yard-dim' : ''}`}
                          style={{ ...p.pos, zIndex: (p.pos.zIndex || 1) + 2, transform: p.flip ? 'scaleX(-1)' : undefined,
                            ...(id === 'yard_lights' || isPot ? { pointerEvents: 'auto', cursor: 'pointer' } : {}) }}
                          onClick={id === 'yard_lights' ? handleLightsOn : (isPot ? () => setGardenPot(p.potNo) : undefined)}
                          alt={info.name} />
                      );
                    })}
                  </React.Fragment>
                );
              })}
              </div>
              {yardNight && <div className="yard-night-tint" />}
              {yardNight && <div className="yard-door-glow" />}
              {/* 她出门探险的时候院子里就没人，别画一个不在家的小璋 */}
              {!yardRainy && !isAdventuring && <div className="character-area yard-character">
                {speechBubble && <div className="speech-bubble">{speechBubble}</div>}
                <div className="pet-zone" onClick={handlePet} />
                <div className={`pupi-sprite${currentMood === 'sad' && !blinkOpen ? ' pupi-tremble' : ''}${pokeBounce ? ' pupi-poke-bounce' : ''}`} style={{
                  backgroundImage: `url(${ASSETS}/character/pupi/${pupiSprite})`,
                  backgroundPosition: `${blinkOpen ? '0%' : '100%'} ${TALL_SPRITES.has(pupiSprite) ? '15%' : 'center'}`,
                }} />
                {dirtyLevel > 0 && (
                  <div className="dirty-overlay">
                    <div className="smudge" style={{ left: '35%', top: '45%', width: 28, height: 22, transform: 'rotate(-12deg)' }} />
                    <div className="smudge" style={{ left: '55%', top: '42%', width: 24, height: 20, transform: 'rotate(8deg)' }} />
                    {dirtyLevel >= 2 && <>
                      <div className="smudge smudge-dark" style={{ left: '45%', top: '55%', width: 26, height: 20, transform: 'rotate(-5deg)' }} />
                      <div className="smudge smudge-dark" style={{ left: '30%', top: '35%', width: 24, height: 18, transform: 'rotate(15deg)' }} />
                      <div className="smudge smudge-dark" style={{ left: '60%', top: '50%', width: 28, height: 22, transform: 'rotate(-20deg)' }} />
                      <svg className="stink-line" style={{ left: '32%', top: '8%' }} width="10" height="28" viewBox="0 0 10 28"><path d="M5,28 C2,24 8,20 5,16 C2,12 8,8 5,4 C3,1 5,0 5,0" fill="none" stroke="rgba(120,100,60,0.5)" strokeWidth="1.5" strokeLinecap="round"/></svg>
                      <svg className="stink-line" style={{ left: '60%', top: '8%', animationDelay: '0.8s' }} width="10" height="28" viewBox="0 0 10 28"><path d="M5,28 C2,24 8,20 5,16 C2,12 8,8 5,4 C3,1 5,0 5,0" fill="none" stroke="rgba(120,100,60,0.5)" strokeWidth="1.5" strokeLinecap="round"/></svg>
                    </>}
                    {dirtyLevel >= 3 && <>
                      <div className="smudge smudge-dark" style={{ left: '40%', top: '48%', width: 36, height: 28 }} />
                      <svg className="stink-line" style={{ left: '20%', top: '3%' }} width="10" height="32" viewBox="0 0 10 32"><path d="M5,32 C2,28 8,24 5,20 C2,16 8,12 5,8 C3,5 5,4 5,4" fill="none" stroke="rgba(120,100,60,0.5)" strokeWidth="1.5" strokeLinecap="round"/></svg>
                      <svg className="stink-line" style={{ left: '42%', top: '3%', animationDelay: '0.5s' }} width="10" height="32" viewBox="0 0 10 32"><path d="M5,32 C2,28 8,24 5,20 C2,16 8,12 5,8 C3,5 5,4 5,4" fill="none" stroke="rgba(120,100,60,0.5)" strokeWidth="1.5" strokeLinecap="round"/></svg>
                      <svg className="stink-line" style={{ left: '64%', top: '3%', animationDelay: '1s' }} width="10" height="32" viewBox="0 0 10 32"><path d="M5,32 C2,28 8,24 5,20 C2,16 8,12 5,8 C3,5 5,4 5,4" fill="none" stroke="rgba(120,100,60,0.5)" strokeWidth="1.5" strokeLinecap="round"/></svg>
                      <div className="fly-buzz" style={{ right: '10%', top: '15%' }}>🪰</div>
                      <div className="fly-buzz" style={{ left: '10%', top: '30%', animationDelay: '0.7s' }}>🪰</div>
                    </>}
                  </div>
                )}
              </div>}
              {yardRainy && !isAdventuring && <div className="yard-empty-note" style={{ zIndex: 27 }}>{state.name || '宝宝'}躲屋里去了，不肯淋雨</div>}
              {isAdventuring && <div className="yard-empty-note" style={{ zIndex: 27 }}>{state.name || '宝宝'}出去玩了，院子先交给你</div>}
              {/* 点花盆弹出来的小卡片：空盆给选种什么，种了给浇水，开花了给摘 */}
              {gardenPot !== null && (() => {
                const g = state.garden || {};
                const p = gardenPlantOf(gardenPot);
                return (
                  <div className="garden-card" onClick={e => e.stopPropagation()}>
                    <div className="garden-card-title">{gardenPot}号花盆</div>
                    {!p && (<>
                      <div className="garden-card-line">
                        空着的。种点什么？
                        {g.canPlantFlower ? `（手上有 ${g.seeds || 0} 颗种子）` : ''}
                      </div>
                      <div className="garden-card-btns">
                        <button onClick={async () => { await handleGarden('plant', { pot: gardenPot, type: 'veggie' }); setGardenPot(null); }}>种菜<span>浇3次结一样</span></button>
                        <button disabled={!g.canPlantFlower || !g.seeds}
                          onClick={async () => { await handleGarden('plant', { pot: gardenPot, type: 'flower' }); setGardenPot(null); }}>
                          种花<span>{!g.canPlantFlower ? '？' : (!g.seeds ? '没有种子' : '浇7次开花')}</span>
                        </button>
                      </div>
                    </>)}
                    {p && !p.canPick && (<>
                      <div className="garden-card-line">
                        {p.type === 'flower' ? '种的是花，' : '种的是菜，'}
                        浇了 {p.waters}/{p.needWaters} 次{p.harvests ? `，已经结过 ${p.harvests} 回` : ''}
                      </div>
                      <div className="garden-card-btns">
                        <button disabled={!p.canWater}
                          onClick={async () => { await handleGarden('water', { pot: gardenPot }); }}>
                          浇水<span>{p.canWater ? '土干了' : (p.rained ? '今天下雨，雨替你浇了' : `还要等${p.waterInHours}小时`)}</span>
                        </button>
                      </div>
                    </>)}
                    {p && p.canPick && (<>
                      <div className="garden-card-line">{p.type === 'flower' ? '花开了。' : '菜熟了。'}</div>
                      <div className="garden-card-btns">
                        <button onClick={async () => { await handleGarden('pick', { pot: gardenPot }); setGardenPot(null); }}>
                          {p.type === 'flower' ? <>摘下来<span>看看是什么颜色</span></> : <>采摘<span>摘完盆就空了，能换种别的</span></>}
                        </button>
                      </div>
                    </>)}
                    <button className="garden-card-close" onClick={() => setGardenPot(null)}>关上</button>
                  </div>
                );
              })()}
              {/* 手里有摘下来的花：给个送花入口 */}
              {gardenPot === null && Number(state.garden?.flowersOnHand || 0) > 0 && (
                <div className="garden-flower-badge" onClick={() => setFlowerGiving(true)}>
                  <img src={`${ASSETS}/yard_items/flower_icon_pink.png`} alt="花" />
                  手里有 {state.garden.flowersOnHand} 朵花，送给谁？
                </div>
              )}
              {flowerGiving && (
                <div className="garden-card" onClick={e => e.stopPropagation()}>
                  <div className="garden-card-title">送花</div>
                  <div className="garden-card-line">送出去就留在对方的记录里，不能收回。</div>
                  <textarea
                    className="garden-flower-note"
                    placeholder="想跟花一起说的话（可不写）"
                    value={flowerNote}
                    maxLength={500}
                    rows={3}
                    onChange={e => setFlowerNote(e.target.value)}
                  />
                  <div className="garden-card-btns">
                    {[
                      ['mama', state.youAre === 'mama' ? state.yourName : state.partnerName],
                      ['daddy', state.youAre === 'daddy' ? state.yourName : state.partnerName],
                      ['baby', state.name || '宝宝'],
                    ]
                      .filter(([k]) => k !== (state.youAre || 'mama'))
                      .map(([k, label]) => (
                        <button key={k} onClick={async () => { const r = await handleGarden('give_flower', { to: k, note: flowerNote.trim() }); if (r?.ok) setFlowerNote(''); setFlowerGiving(false); }}>{label}</button>
                      ))}
                  </div>
                  <button className="garden-card-close" onClick={() => setFlowerGiving(false)}>算了</button>
                </div>
              )}
              <div className="particles yard-particles">
                {particleData.map((pd, i) => (
                  <div key={i} className={`particle ${pd.cls}`} style={{
                    left: pd.left, top: pd.top,
                    width: pd.size, height: pd.size, background: pd.bg,
                    animationDelay: pd.delay, animationDuration: pd.duration,
                  }} />
                ))}
              </div>
            </div>
          );
        })()
  );
}
