0xffd6563c…95fesent to0x32a5ece9…9d43·#25,427,625·view on Etherscan
i<=sst;i++){ const u=i/sst;
d += angTo(d,UP)*0.16;
sp=[sp[0]+Math.cos(d)*GRID, sp[1]+Math.sin(d)*GRID];
const taper=Math.min(1,u*4)*(1-Math.pow(u,1.7)), hw=sW*taper, px=-Math.sin(d), py=Math.cos(d);
for(let w=-hw; w<=hw; w+=GRID){ if(rs()<0.12) continue;
const fx=sp[0]+px*w, fy=sp[1]+py*w, localOpen=clamp(open*(1.25-u),0,1), isOpen=rs()<localOpen*0.85;
const col = isOpen ? shade(mix(cFlower, cFlowerLt, rs()*0.7), rs) : shade(mix(cBud, cFlowerDk, localOpen), rs);
const sym = isOpen ? (rs()<0.5?'+':'X') : (rs()<0.6?'.':'O');
C.push(fx, fy, flZ, sym, col, 'flowers');
}
}
}
}
}
// ===================== SETTING: a patch — Solo / Pair / Cluster / Field =====================
const sr=rng('setting',st.seed), roll=sr();
const nC = roll<0.18?0 : roll<0.50?1 : roll<0.82?2 : 3+Math.floor(rng('fn',st.seed)()*2);
const comps=[];
for(let i=0;i<nC;i++){ const rc=rng('comp',st.seed,i);
const side=(i%2===0?1:-1), f=i/Math.max(1,nC);
const scale=lerp(0.72,0.46,f)*(0.9+0.2*rc()), off=side*(80+i*46+rc()*46), raise=(34+i*24+rc()*30);
comps.push({cx:baseX+off, by:baseY-raise, S:scale, fade:lerp(0.5,0.74,f),
m, v, sen, browny, density:clamp(density+(rc()*2-1)*0.12,0.05,1), rewards, zb:-10, pseed:'c'+i, depth:f});
}
comps.sort((a,b)=>b.depth-a.depth).forEach(paintBush); // far (faint) → near
paintBush({cx:baseX, by:baseY, S:1.0, fade:0, m, v, sen, browny, density, rewards, zb:0, pseed:'hero'});
},
traits(st,env){
const sr=rng('setting',st.seed), roll=sr();
const setting = roll<0.18?'Solo' : roll<0.50?'Pair' : roll<0.82?'Cluster' : 'Field';
return [
['Setting', setting],
['Stems', clamp(Math.round(1+env.density*14),1,15)],
['Stature', Math.round((110+230*env.m)*(0.7+0.3*env.v))+' px'],
];
}
}));
})();
;
/* species/lotus.js */
/* lotus — species index 10. GENERATED from prototypes/sacred-lotus.html by extract.js.
Do not edit here — edit the prototype and re-run `npm run extract`. The prototype's
mount() call is redirected to SeedlingRenderer.registerSpecies via the local shim. */
(function () {
const mount = (spec) => SeedlingRenderer.registerSpecies(10, spec);
/* Sacred Lotus — Nelumbo nucifera.
An aquatic plant: large peltate pads with a WAVY / undulate margin and radial veins,
held on SHORT petioles low over the water (heavy leaves don't reach far up; the newest
leaf is a small cupped emerging blade), and a solitary red-pink BLOOM raised above the
foliage on its own taller peduncle — broad layered petals around a golden receptacle
with a fringe of stamens. Lifecycle on the peduncle: nodding BUD → open BLOOM → green
POD → dry "showerhead" seed pod. Lotuses often grow in a stand: a seed-driven COLONY
renders 1 → many plants (solo to a pond bunch). Summer bloom, winter pad-dieback. */
const SP = window.SEEDLING_SPECIES.find(x=>x.key==='lotus');
mount(Object.assign(window.SEEDLING_toSpec(SP), {
title:'Sacred Lotus', sub:'nelumbo v4',
senFrom:0.82,
note:'Aquatic · big wavy pads low on the water, side-view cup blooms (pointed petals + golden stamen crown) on stems of varied height → showerhead seed pod · flower varieties Pink / White / Rose · coherent WIND bends the stalks downwind · 1–5 flowers per root, spread both sides · solo to a full pond bunch scattered across the water.',
PAL:{ soil:'#6a7a5a',
stalk:'#6E9A6B', stalkDk:'#4C7754', speck:'#365E3C',
pad:'#6E9A5E', padDk:'#3E6B43', padLt:'#9CC07E', padVein:'#33572F', padEdge:'#274A29', padYellow:'#C7A24E',
petal:'#E0796E', petalLt:'#F7D6CE', petalDk:'#C53A30', petalVein:'#962017',
center:'#E8B23A', centerDk:'#B07A1E', stamen:'#EFC85C',
pod:'#7E9A55', podRim:'#5A7A3C', podDry:'#8A714A', podDryDk:'#5A4730', pit:'#3E2E1A' },
stageName(st,env){ if(st.diedAt>0)return'Dry pod'; const m=env.m;
if(m<0.15)return'Sprout'; if(m<0.50)return'Floating pads';
if(m<0.72)return'Budding'; return'Bloom'; },
draw(st, C, env){
const {m,v,sen,season,browny,density,rewards,reproOK,dormancy,dead,baseX,baseY}=env, P=this.PAL;
const winter=dormancy||0;
const bgC = BACKDROPS[backdropFor(st.seed)];
const waterY = baseY - 8; // plants root at the bottom (like the other species), grow UP from here
const seasonBloom = season==='Summer'?1 : season==='Spring'?0.7 : season==='Autumn'?0.45 : 0.30; // monsoon bias, floored
// per-seed POND environment, shared by every plant in the colony
const windDir = rng('windD',st.seed)()<0.5?-1:1;
const windStr = Math.pow(rng('windS',st.seed)(),2.6); // 0..1, skewed LOW → mostly calm, rarely a storm
function varietyPal(key){ const x=rng('variety',st.seed,key)(); // per-PLANT flower colour (so a pond can mix pink + white)
if(x<0.42) return {name:'Pink', lt:'#F7D6CE', pt:'#E0796E', dk:'#C53A30', vn:'#962017'};
if(x<0.80) return {name:'White', lt:'#FFFFFF', pt:'#F6F1EA', dk:'#EBA48C', vn:'#C56A50'}; // white petals, soft rose-flushed tips + rim (reads white & pops on cream)
return {name:'Rose', lt:'#F3C4D4', pt:'#D85A7A', dk:'#A82A52', vn:'#7C1B3C'};
}
// ===================== one lotus plant (repeated to form a colony) =====================
function paintLotus(I){
const {cx, by, S, fade, m, v, sen, density, rewards, reproOK, browny, dead, pseed} = I;
const FC = c => fade>0 ? mix(c, bgC, fade) : c;
const PV = varietyPal(pseed); // this plant's petal palette (Pink / White / Rose)
const collapse = clamp(1-0.45*sen-0.42*winter, 0.18, 1); // pads sink & yellow as they die back
const standC = clamp(1-0.12*sen-0.42*winter, 0.55, 1); // woody flower/pod stalks stay STANDING
const bloomDrive = reproOK ? clamp(rewards*(0.55+0.45*seasonBloom)*(1-sen),0,1) : 0;
const podDrive = reproOK ? clamp(sen*1.2 + (season==='Autumn'?0.35:0) + smooth(0.82,1.0,m)*0.5, 0,1) : 0;
// -- petiole / peduncle: erect at the base, GENTLY arcing toward `lean` near the top, with a
// faint sway — so stems read natural (not ramrod-straight parallel poles). --
function rise(x0,y0,h,lean,z,col,wideF,rs){
const steps=Math.max(2,Math.ceil(h/GRID)); let pos=[x0,y0], dir=UP;
const sway=(rs()*2-1), swF=0.7+rs()*0.8;
for(let s=1;s<=steps;s++){ const u=s/steps;
const target=UP + lean*smooth(0,1,u); // straight low → curving over near the flower
dir += angTo(dir, target)*0.08 + Math.sin(u*Math.PI*swF)*0.006*sway;
pos=[pos[0]+Math.cos(dir)*GRID, pos[1]+Math.sin(dir)*GRID];
const px=-Math.sin(dir), py=Math.cos(dir);
C.push(pos[0],pos[1],z,'|',shade(FC(col),rs),'stem');
if(wideF) C.push(pos[0]+px*GRID,pos[1]+py*GRID,z,'|',shade(FC(mix(col,P.stalkDk,0.35)),rs),'stem');
if(s%4===0) C.push(pos[0]+px*(rs()<0.5?GRID:-GRID), pos[1]+py*(rs()<0.5?GRID:-GRID), z,'.', shade(FC(P.speck),rs),'stem'); }
return pos;
}
// -- WAVY peltate pad: undulate margin (not a clean circle) + radial veins + sinus notch --
function drawPad(px,py,R,z,yellowF,ragged,cupped,rs){
const sq = cupped? 0.60 : 0.84;
const seam = rng('pr',Math.round(px),Math.round(py),pseed)()*TAU;
const Rf = a => R*(1 + 0.14*Math.sin(7*a+seam) + 0.06*Math.sin(3*a-seam*1.3)); // undulate edge
const notch = seam + DOWN; // sinus toward the petiole
const tone = c => shade(FC(mix(c,P.padYellow,yellowF)),rs);
for(let gx=-R*1.25;gx<=R*1.25;gx+=GRID)for(let gy=-R*sq*1.25;gy<=R*sq*1.25;gy+=GRID){
const a=Math.atan2(gy/sq,gx), rr=Math.hypot(gx,gy/sq), re=Rf(a); if(rr>re)continue;
const rad=rr/re, cr=rng('pf',Math.round(px),Math.round(gx),Math.round(gy));
if(Math.abs(angTo(a,notch))<0.22 && rad>0.5) continue; // sinus cut
if(cr()>0.78) continue; // airy stipple (ground shows through)
if(rad>0.6 && ragged>0 && cr()<ragged) continue; // collapsing margin
const sheen=Math.sin(a*3+seam)*0.5*(0.6-rad);
const col=mix(mix(P.pad, sheen<0?P.padDk:P.padLt, Math.abs(sheen)), P.padDk, smooth(0.7,1.0,rad)*0.3 + (cupped&&gy>0?0.22:0));
C.push(px+gx,py+gy, z, dirSym(gx,gy), tone(col),'leaves');
}
const nVein=cupped?9:13;
for(let k=0;k<nVein;k++){ const a=seam + k/nVein*TAU; if(Math.abs(angTo(a,notch))<0.22) continue;
const ca=Math.cos(a), sa=Math.sin(a)*sq, re=Rf(a);
for(let t=R*0.16;t<=re*0.92;t+=GRID){ const gx=ca*t, gy=sa*t;
if(ragged>0 && t>re*0.6 && rng('vg',Math.round(px),Math.round(gx),k)()<ragged) continue;
C.push(px+gx, py+gy, z+0.6, dirSym(ca,sa), tone(P.padVein),'leaves'); } }
for(let a=0;a<TAU;a+=0.045){ if(Math.abs(angTo(a,notch))<0.20) continue;
const re=Rf(a), ca=Math.cos(a), sa=Math.sin(a)*sq;
if(ragged>0 && rng('eg',Math.round(px),Math.round(a*30))()<ragged*0.8) continue;
for(const rf of [0.99,0.92]) C.push(px+ca*re*rf, py+sa*re*rf, z+0.7, dirSym(ca,sa), tone(P.padEdge),'leaves'); }
if(cupped) for(let a=Math.PI*1.04;a<=Math.PI*1.96;a+=0.05){ const re=Rf(a), ca=Math.cos(a), sa=Math.sin(a)*sq; // curled upper rim
C.push(px+ca*re*0.85, py+sa*re*0.85, z+0.8, dirSym(ca,sa), tone(mix(P.padEdge,'#000000',0.18)),'leaves'); }
C.push(px,py,z+0.9,'O',tone(P.padEdge),'leaves'); // peltate hub
C.push(px,py,z+1.0,'.',tone(P.padEdge),'leaves');
}
// -- one DISTINCT pointed lotus petal: almond shape, dark outline (so petals separate),
// central vein, pale base → red tip. `ang` is an absolute direction; the petal grows
// from a base point. Outline sits a touch higher z so it cuts over the petal behind. --
function petal(bx,by,ang,len,halfW,z,ez,pal,rs){
const nx=Math.cos(ang), ny=Math.sin(ang), px=-ny, py=nx, tn=Math.max(3,Math.ceil(len/GRID));
for(let t=1;t<=tn;t++){ const u=t/tn;
const taper=1-smooth(0.5,1.0,u); // taper over the top half
const hw=halfW*Math.min(1, 2.2*Math.sin(Math.PI*u*0.5))*taper*taper; // broad lower body → pointed lanceolate tip (lotus)
if(hw<GRID*0.32 && u>0.5) continue;
const ox=bx+nx*GRID*t, oy=by+ny*GRID*t;
for(let w=-hw;w<=hw;w+=GRID){ const aw=Math.abs(w);
const edge = aw>hw-GRID*1.5 || (u>0.78 && hw<halfW*0.5); // outline: sides + the pointed tip
const vein = aw<GRID*0.7 && u<0.85; // central vein
const grad = u<0.28?pal.base : u<0.62?mix(pal.base,pal.body,smooth(0.28,0.62,u)) : mix(pal.body,pal.tip,smooth(0.62,1,u));
const col = edge?pal.edge : vein?mix(pal.body,pal.edge,0.4) : grad;
// outlines go to a HIGH shared z (ez) so EVERY petal boundary renders over the fills → petals read as separate
C.push(ox+px*w, oy+py*w, edge?ez:z, '+', shade(FC(col),rs),'flowers'); }
}
}
// a SIDE-VIEW lotus: an egg/goblet of pointed petals opening upward from the stem tip,
// top petals longest (tall egg), reflexed lower petals, paler inner cup, gold stamen centre.
function drawBloom(fx,fy,R,open,rs){
const PB ={base:PV.lt, body:mix(PV.pt,PV.lt,0.45), tip:PV.dk, edge:PV.vn}; // outer: pale body, saturated tip, dark rim (per variety)
const PBi={base:'#ffffff', body:mix(PV.lt,'#ffffff',0.35), tip:PV.pt, edge:PV.dk}; // inner cup, paler
// SIDE VIEW: the flower is a CUP / egg opening UPWARD. Petals fan UP & OUT from a NARROW base just
// above the stem (which hides behind them) — NONE point straight down (that would be a top view).
// A shorter, PALER FRONT whorl sits in front of the centre so we read the cup's DEPTH; the golden
// stamens nestle LOW in the middle of the cup. Broad pointed petals overlap into a full flower.
const fcx=fx, fcy=fy - R*0.14, ringR=R*0.15;
const sp=lerp(1.04,1.38,open);
const bN=7; for(let k=0;k<bN;k++){ const t=(k/(bN-1))*2-1, ang=UP+t*sp; // BACK whorl: tall outer-face cup-wall petals (behind)
petal(fcx+Math.cos(ang)*ringR, fcy+Math.sin(ang)*ringR, ang, R*(1.10-0.16*Math.abs(t)), R*0.245, 66+Math.round(Math.abs(t)*2), 82, PB, rs); }
const fN=6; for(let k=0;k<fN;k++){ const t=((k+0.5)/fN)*2-1, ang=UP+t*lerp(0.60,1.04,open); // FRONT whorl: shorter, PALER inner-face petals, offset between, in FRONT → cup depth
petal(fcx+Math.cos(ang)*ringR*0.66, fcy+Math.sin(ang)*ringR*0.66, ang, R*(0.76-0.12*Math.abs(t)), R*0.215, 74+Math.round((1-Math.abs(t))*2), 81, PBi, rs); }
// ---- the golden POLLEN crown in the MIDDLE: a ring of stamens (filament + anther) radiating
// from a small central receptacle, surrounded by the petals (the lotus signature). ----
const ccx=fcx, ccy=fcy - R*0.30, baseR=R*0.075; // pollen crown LOW in the middle of the cup
const nStam=Math.round(46*(0.6+0.4*open)); // dense → a fuzzy golden cluster, not a sparse star
for(let k=0;k<nStam;k++){ const sr=rng('st',Math.round(fx),Math.round(fy),k);
const a=(k/nStam)*TAU + (sr()*2-1)*0.22;
const up=(-Math.sin(a)+1)/2; // 1 at top → 0 at bottom
const r0=baseR*(0.4+1.0*sr()); // start radius jitter → fuzzy core, no clean spokes
const len=R*(0.085+0.085*sr())*(0.55+0.55*up)*(0.7+0.5*open); // upper stamens longer → a crown, not a flat disc
const f0x=ccx+Math.cos(a)*r0, f0y=ccy+Math.sin(a)*r0, steps=Math.max(1,Math.ceil(len/GRID));
for(let t=1;t<=steps;t++) C.push(f0x+Math.cos(a)*GRID*t, f0y+Math.sin(a)*GRID*t, 84, '.', shade(FC(mix(P.stamen,P.center,sr()*0.5)),sr),'flowers');
C.push(f0x+Math.cos(a)*len, f0y+Math.sin(a)*len, 85,'.',shade(FC(P.centerDk),sr),'flowers'); // anther tip (pollen)
}
// central receptacle dome (the seed-pod head), on top of the stamens
const cw=R*0.13*(0.7+0.3*open), ch=cw*0.85;
for(let gx=-cw;gx<=cw;gx+=GRID)for(let gy=-ch;gy<=ch;gy+=GRID){ if((gx*gx)/(cw*cw)+(gy*gy)/(ch*ch)>1)continue;
const top=clamp(-gy/ch*0.5+0.5,0,1); C.push(ccx+gx,ccy+gy,87,'+',shade(FC(mix(P.centerDk,P.center,top)),rs),'flowers'); }
for(let k=0;k<5;k++){ const a=k/5*TAU; C.push(ccx+Math.cos(a)*cw*0.42, ccy+Math.sin(a)*ch*0.42, 88,'O',shade(FC(mix(P.centerDk,'#5e4e1c',0.45)),rs),'flowers'); }
}
// young = a tight GREEN egg (sepal cover); as it ripens the coloured petals BREAK out of the tip
// (colour spreads down from the top) and tips poke out — then it opens into the bloom.
// `ripe` 0→1 = green→coloured/about-to-open · `grow` 0→1 = young round egg → elongated bud.
function drawBud(fx,fy,R,lean,ripe,grow,rs){
const a=UP+lean, nx=Math.cos(a), ny=Math.sin(a), px=-ny, py=nx;
const len=R*(1.3+0.9*grow), maxW=R*0.58, tn=Math.max(3,Math.ceil(len/GRID)); let p=[fx,fy];
const grA=mix(P.padDk,'#46663c',0.25), grB=mix(P.pad,P.padLt,0.35), grE=P.padEdge; // green sepal cover
for(let t=1;t<=tn;t++){ p=[p[0]+nx*GRID, p[1]+ny*GRID]; const u=t/tn;
const hw=maxW*Math.pow(Math.sin(Math.PI*clamp(u*0.62+0.12,0,1)),0.60)*(1-0.85*smooth(0.55,1,u)); // egg: round fat base → pointed tip
if(hw<GRID*0.3 && u>0.55) continue;
for(let w=-hw;w<=hw;w+=GRID){ const aw=Math.abs(w), seam=aw<GRID*0.7, e=aw>hw-GRID*1.1;
const pink=clamp(ripe*1.5 - (1-u)*0.95, 0, 1); // colour breaks at the TIP first, spreads down
const gcol = e?grE : seam?mix(grA,grE,0.5) : (w<0?grA:grB);
const pcol = e?PV.dk : seam?PV.vn : (w<0?PV.pt:PV.lt);
C.push(p[0]+px*w, p[1]+py*w, 70, '.', shade(FC(mix(gcol,pcol,pink)),rs),'flowers'); } }
if(ripe>0.45){ const tipN=2+Math.round(ripe*3), tbx=fx+nx*len*0.82, tby=fy+ny*len*0.82; // coloured petal tips poking from the top
for(let k=0;k<tipN;k++){ const tt=(tipN>1?(k/(tipN-1)-0.5):0)*lerp(0.25,0.9,ripe), ta=a+tt, tlen=R*(0.18+0.5*ripe);
let pp=[tbx,tby]; const ptn=Math.max(2,Math.ceil(tlen/GRID));
for(let s=1;s<=ptn;s++){ pp=[pp[0]+Math.cos(ta)*GRID, pp[1]+Math.sin(ta)*GRID]; const pu=s/ptn;
C.push(pp[0],pp[1],71, pu>0.7?'.':dirSym(Math.cos(ta),Math.sin(ta)), shade(FC(mix(PV.pt,PV.dk,pu*0.4)),rs),'flowers'); } } }
}
function drawPod(fx,fy,R,dryF,lean,rs){ // the iconic flat-topped "showerhead" receptacle
const topW=R*1.08, topH=R*0.40, bodyH=R*0.66, lx=Math.sin(lean)*bodyH*0.5;
const cTop=mix(P.pod,P.podDry,dryF), cSide=mix(P.podRim,P.podDryDk,dryF), cFace=mix(mix(P.pod,P.podDry,dryF),'#000000',0.10);
const rows=Math.ceil(bodyH/GRID);
for(let r=0;r<=rows;r++){ const u=r/rows, wdt=lerp(topW*0.92, topW*0.34, Math.pow(u,0.8)), yy=fy+topH*0.55+u*bodyH, xx=fx+lx*u;
for(let gx=-wdt;gx<=wdt;gx+=GRID){ const e=Math.abs(gx)>wdt-GRID*1.1;
C.push(xx+gx,yy,80, e?dirSym(0,1):'\\', shade(FC(e?mix(cSide,'#000000',0.15):cSide),rs),'flowers'); } }
for(let gx=-topW;gx<=topW;gx+=GRID)for(let gy=-topH;gy<=topH;gy+=GRID){ if((gx*gx)/(topW*topW)+(gy*gy)/(topH*topH)>1)continue;
const e=(gx*gx)/(topW*topW)+(gy*gy)/(topH*topH)>0.8;
C.push(fx+gx,fy+gy,83,'-',shade(FC(e?cSide:cFace),rs),'flowers'); }
function pit(ox,oy){ C.push(ox,oy,85,'O',shade(FC(mix(P.podRim,'#000000',0.1+0.2*dryF)),rs),'flowers');
C.push(ox,oy,86,'.',shade(FC(P.pit),rs),'flowers'); }
const nOut=Math.round(7+dryF*3);
for(let k=0;k<nOut;k++){ const a=k/nOut*TAU + rs()*0.2; pit(fx+Math.cos(a)*topW*0.62, fy+Math.sin(a)*topH*0.62); }
for(let k=0,nIn=Math.round(3+dryF);k<nIn;k++){ const a=k/nIn*TAU + 0.5; pit(fx+Math.cos(a)*topW*0.30, fy+Math.sin(a)*topH*0.30); }
pit(fx,fy);
}
// small rhizome crown the petioles emerge from (NO long roots)
for(let k=0;k<6;k++){ const rs=rng('crown',pseed,k), a=Math.PI + (k/5-0.5)*1.7;
C.push(cx+Math.cos(a)*GRID*(1+k%3)*S, by+2+Math.abs(Math.sin(a))*GRID*0.5, 18, dirSym(Math.cos(a),Math.sin(a)), shade(FC(P.stalkDk),rs),'stem'); }
// ---- LEAVES: SHORT petioles (heavy pads stay low), wavy pads, 1–3 per plant ----
const nF=clamp(1+density*1.6, 1, 2.4), n=Math.ceil(nF); // 1–2 big distinct leaves (reference shows two)
const leafHmax=(55+110*m)*S; // SHORT relative to the heavy leaf (no towering petioles)
let tallest=by, padTop=by;
for(let i=0;i<n;i++){ const rs=rng('pad',st.seed,pseed,i);
const emerge=i===n-1?clamp(nF-(n-1),0.2,1):1, newest=i===n-1 && n>1;
const t=n>1? i/(n-1):0.5;
const bx=cx+(t-0.5)*120*S+(rs()*2-1)*8*S; // leaves sit apart with a clear gap
const hFrac=newest ? 0.64 : (0.88+0.12*rs()); // STAGGER heights → two pads separate, don't form a clover
const leafH=leafHmax*hFrac*emerge*collapse + 10;
const lean=(t-0.5)*0.55 + (rs()*2-1)*0.05 + windDir*windStr*0.14; // pads ride low on the water → only a slight wind lean
const top=rise(bx, by+2, leafH, lean, 22, mix(P.stalk,P.stalkDk,0.25*(1-hFrac)), false, rs);
tallest=Math.min(tallest, top[1]);
const yellowF=clamp(browny*0.55+sen*0.9+winter*0.7, 0, 1)*(0.6+0.4*(1-i/n));
const ragged=clamp(sen*0.7+winter*0.5-0.15,0,0.6);
const z=30+Math.round((1-hFrac)*9);
const R=(58+72*m)*emerge*(0.84+0.24*rs())*S*(newest?0.7:1); // BIG pads — lotus leaves are bigger than its flowers
drawPad(top[0], top[1], R, z, yellowF, ragged, newest, rs);
padTop=Math.min(padTop, top[1]-R*0.80); // visual top of the foliage (for flower-height reference)
}
// ---- FLOWER stalks: each flower EMERGES as a short budded stem just above the pads, then its
// peduncle RISES and the bud OPENS into a bloom (→ pod). Staggered birth maturities mean
// flowers appear one at a time and GROW IN — no full-length stem popping out of nowhere.
// A well-cared mature root carries up to 3, each at its own phase. ----
// Up to 5 flowers per plant — count = per-seed floriferousness × earned rewards (lots of per-plant
// variation). Positions are SPREAD SYMMETRICALLY across the plant's width (never center+left only,
// so no side is starved); central blooms emerge first, outer L/R pairs together at higher maturity.
const florif = 0.7 + 0.6*rng('florif',st.seed,pseed)();
const nFlow = reproOK ? clamp(Math.round((1 + rewards*3.4)*florif), 1, 5) : 0;
const fpos = nFlow>1 ? Array.from({length:nFlow},(_,i)=>(i/(nFlow-1)-0.5)*2) : [0]; // symmetric −1..1
for(let f=0; f<nFlow; f++){
const fside = fpos[f], sgn = fside<0?-1:1;
const birthF = 0.50 + Math.abs(fside)*0.20; // centre 0.50 → outer 0.70; L/R pairs emerge together
if(!reproOK || m < birthF) continue;
const rs=rng('fl',st.seed,pseed,f); // structural draws in FIXED order (frame-stable)
const posJit=(rs()*2-1), hr=rs()*(Math.abs(fside)<0.34?1:0.6), leanJit=(rs()*2-1), Rjit=rs();
const fgrow = clamp((m - birthF)/0.18, 0, 1); // 0 = just emerged (short bud) → 1 = full height
const fx0 = cx + fside*122*S + posJit*16*S; // blooms fan across the plant's full span
const targetH = ((by-padTop) + lerp(18+28*m, 205+225*m, Math.pow(hr,1.9))*S) * (0.85+0.18*v)*standC;
const shortH = (by-padTop) + (14+12*m)*S; // just-emerged: a bud peeking above the pads
const ph = lerp(shortH, Math.max(shortH,targetH), smooth(0,1,fgrow)); // the peduncle RISES as the flower grows
const lean = leanJit*0.10 + fside*0.05 + windDir*windStr*(0.22+0.45*hr)*fgrow;
const top = rise(fx0, by+2, ph, lean, 52+f, P.stalk, true, rng('flStem',st.seed,pseed,f)); // stem on its own stream
const R = (40+42*m)*S*(0.88+0.14*Rjit); // blooms SMALLER than the pads (lotus proportions)
const hRs = rng('flHead',st.seed,pseed,f); // head on its own stream → stable regardless of stem height
const localPod = clamp(podDrive + f*0.06 + (dead?1:0), 0, 1);
const opening = smooth(0.50,0.92,fgrow) * clamp(bloomDrive*1.7, 0, 1); // opens near full height, gated by season/rewards
if(dead || localPod>0.5) drawPod(top[0], top[1], R*0.6, clamp(sen*1.1+(season==='Autumn'?0.3:0)+(dead?0.4:0),0,1), 0.04*sgn, hRs);
else if(opening>0.40) drawBloom(top[0], top[1], R, opening, hRs);
else { const ripe=clamp(Math.max(