#!/usr/bin/env python3
# Book 41 (JSW Steel) — THE NEW FORGE. Science->business: steel is IRON ORE + a little CARBON
# + ferocious HEAT -> a metal stronger than the rock it came from. But the real secret is EFFICIENCY:
# reuse the furnace's own hot gas, melt old scrap again, squeeze every rupee of waste out -> the
# LOW-COST maker survives when steel prices crash -> the lean new forge that grew into a giant.
import os
ART="/root/omahaindia.com/comics/site/book/jsw/art"
OUT="/root/omahaindia.com/comics/site/book/jsw/index.html"
STYLE=("children's storybook manga illustration, warm painted animation style, soft cinematic light, "
"clean linework, NO text NO letters NO words")
CHARLOTTE=("Charlotte, the SAME young Indian CHILD girl from the reference, round young face, warm tan skin, "
"freckles, glossy black hair in two low twin ponytails, big curious dark eyes, bright TEAL t-shirt, NO facial hair")
GRAHAM="Ben, a kind scholarly elderly American gentleman, round wire spectacles, tweed suit and bow tie"
BUFFETT="Warren, a folksy cheerful elderly American man, thinning white hair, big friendly glasses, grey suit"
MUNGER="Charlie, a stocky wise elderly American man, very thick black-framed glasses, dark suit"
BRAM=("Bram, a warm friendly Western STEEL FURNACE OPERATOR, NOT Indian, broad shoulders, bright determined smile, "
"a silver heat suit, a protective visor pushed up on his forehead, a long steel rod in one hand")

# name -> (w,h,seed,prompt,ref). ref set -> Seedream character; None -> flux scene. Ritual panels copied by mkbook.
NEW={
 "00_cover":(1024,1180,4100,f"{STYLE}. BOOK COVER, {CHARLOTTE} standing with {BRAM} on the floor of a vast steel mill, holding up a gleaming grey steel bar that catches the light, a glowing orange river of molten metal and showering golden sparks behind them, wonder, cinematic","charlotte"),
 "_ref_bram":(1024,1024,4101,f"{STYLE}. clean friendly character portrait, {BRAM}, plain soft warm background, determined smile, head and shoulders",None),
 # Ch1
 "js_c1_coin":(1024,720,4110,f"{STYLE}. {CHARLOTTE} at home, Grandpa Ben's glowing brass compass twitching between a rough rusty reddish lump of iron ore and a gleaming steel spoon on the table, EXPRESSION puzzled and intrigued, cozy lamp light","charlotte"),
 "js_c1_diary":(1024,720,4111,f"{STYLE}. an open magical diary glowing warm gold on a desk, a faint ribbon of orange fire-light rising from the page and curling around a tiny grey steel bar, no people",None),
 "js_c1_swirl":(1024,720,4112,f"{STYLE}. {CHARLOTTE} pulled into a swirling spiral of golden sparks and glowing orange embers from a glowing diary, EXPRESSION thrilled, magical","charlotte"),
 # Ch2 — arrive at the mill, meet Bram
 "js_c2_arrive":(1024,720,4113,f"{STYLE}. {CHARLOTTE} landing wide-eyed on the floor of an enormous steel mill, towering furnaces and a glowing orange river of molten metal, EXPRESSION awe and delight, dramatic firelight","charlotte"),
 "js_c2_bram":(1024,720,4114,f"{STYLE}. {BRAM} warmly welcoming {CHARLOTTE} at the steel mill, tipping his pushed-up visor, EXPRESSION Charlotte shy happy smile; keep the SAME furnace operator from the reference, same silver heat suit and pushed-up visor and long steel rod","bram"),
 "js_c2_pit":(1024,720,4115,f"{STYLE}. a vast steel mill floor seen from above, giant ladles pouring glowing molten steel, showers of orange sparks, tiny workers along the gantries for scale, powerful and awe-inspiring, no faces close up",None),
 # Ch3 — the recipe: iron ore + a little carbon
 "js_c3_ore":(1024,720,4116,f"{STYLE}. {BRAM} kneeling to show {CHARLOTTE} a rough reddish-brown lump of iron ore, a tiny silvery fleck of metal glinting inside the rock, EXPRESSION Charlotte fascinated leaning in; SAME operator, silver heat suit, pushed-up visor","bram"),
 "js_c3_carbon":(1024,720,4117,f"{STYLE}. a friendly close up of shiny black chunks of carbon laid beside the rough reddish iron ore, two simple ingredients set out like a recipe, warm storybook style, no people",None),
 "js_c3_mix":(1024,720,4118,f"{STYLE}. a magical close up of red iron ore and black carbon swirling together into a glowing crucible, bright sparks rising, playful science of a recipe coming together, no people",None),
 # Ch4 — the ferocious heat that melts it (cross-book: the fire that never sleeps)
 "js_c4_furnace":(1024,720,4119,f"{STYLE}. {CHARLOTTE} watching in wonder as a colossal furnace roars with white-hot flame, the ore and carbon glowing inside, EXPRESSION amazed and a little awed, intense heat glow","charlotte"),
 "js_c4_molten":(1024,720,4120,f"{STYLE}. a beautiful close up of a glowing orange-white river of molten steel pouring from a giant ladle, brilliant sparks, dark rim bright center, no people",None),
 "js_c4_bar":(1024,720,4121,f"{STYLE}. {BRAM} showing {CHARLOTTE} a fresh red-hot steel bar being rolled and cooled into a strong grey beam, EXPRESSION Charlotte delighted 'aha'; SAME operator, silver heat suit, pushed-up visor","bram"),
 # Ch5 — the secret begins: waste not, reuse the furnace's own hot gas
 "js_c5_gas":(1024,720,4122,f"{STYLE}. {BRAM} pointing up at hot waste gas swirling out of the furnace top, shimmering wisps of heat that would normally float away and be lost, EXPRESSION Charlotte curious and thoughtful; SAME operator, silver heat suit, pushed-up visor","bram"),
 "js_c5_reuse":(1024,720,4123,f"{STYLE}. a magical diagram-like scene of glowing hot gas caught in shiny pipes and looped back down to heat the furnace again instead of escaping, arrows showing the loop, warm glow, no people",None),
 # Ch6 — a second life: recycle old scrap steel
 "js_c6_scrap":(1024,720,4124,f"{STYLE}. {CHARLOTTE} standing before a huge mountain of old scrap metal — bent bars, old cans, worn machine parts — waiting to be reborn, EXPRESSION surprised and curious, bright daylight","charlotte"),
 "js_c6_remelt":(1024,720,4125,f"{STYLE}. old scrap metal melting in a bright electric furnace and pouring out as gleaming new steel, a joyful second life for the metal, showering sparks, no people",None),
 # Ch7 — the leanest one wins: low cost survives the fall
 "js_c7_cheap":(1024,720,4126,f"{STYLE}. {BRAM} beside a glowing balance scale weighing a small stack of coins against a big shiny steel beam, showing steel made for the least cost, EXPRESSION cheerful and clever; SAME operator, silver heat suit, pushed-up visor","bram"),
 "js_c7_survive":(1024,720,4127,f"{STYLE}. a storybook scene of steel prices falling like a stormy grey sea, one lean sturdy little steel mill still standing bright and safe on a rock while costlier ones sink, hopeful, no faces close up",None),
 # Ch8 — reveal JSW Steel
 "js_c8_reveal":(1024,720,4128,f"{STYLE}. {CHARLOTTE} and {BRAM} standing proudly before a vast modern steel plant glowing against a blue sky, EXPRESSION proud recognition; SAME operator, silver heat suit, pushed-up visor","charlotte"),
 "js_c8_founding":(1024,720,4129,f"{STYLE}. warm sepia 1980s scene, a small steel works by the western sea slowly growing into a great modern plant, hopeful workers and rising chimneys, inspiring, no text",None),
 "js_c8_scale":(1024,720,4130,f"{STYLE}. a mighty modern steel plant at golden hour, endless bright rolls of steel and long trains carrying beams away, one of India's biggest steelmakers, proud and grand, no people",None),
 # Ch10 (ch9 uses ritual castle/invert/verdict; ch10 uses ritual wake/diary)
 "js_c10_look":(1024,720,4131,f"{STYLE}. {CHARLOTTE} outside in the city looking with wonder at all the steel around her — a bridge, a railway, tall buildings, a bright new car — quietly strong everywhere, EXPRESSION delighted wonder, warm daylight","charlotte"),
}

def P(img,cap=None,b1=None,b2=None,sfx=None,sfxc="#e8425a",tall=False): return ("panel",img,cap,b1,b2,sfx,sfxc,tall)
def SUB(d,t): return ("sub",d,t)
def WOW(t): return ("wow",t)
def NOTE(t,b): return ("note",t,b)
def TL(r): return ("timeline",r)
def MOAT(c): return ("moat",c)

CHAPTERS=[
 ("c1","CHAPTER 1","The Rusty Rock and the Shining Bar","pages 1-8",[
   P("js_c1_coin","Back home, Grandpa Ben's compass twitched again — pointing between a crumbly rusty rock and a gleaming steel spoon.","A crumbly rock… and shining steel? What's the link?",None,"twitch"),
   P("js_c1_diary","The old diary fell open. A new line shimmered in Grandpa Ben's hand.",None,'"Find the maker who beats the whole world by wasting the least."'),
   WOW("A rusty rock and a mighty steel beam don't seem to belong together — yet skyscrapers, railways and cars are all built from the strong grey metal we pull out of dull rock. What secret turns weak rusty iron into powerful steel — and why does it matter WHO makes it? That's the puzzle of this whole adventure."),
   P("js_c1_swirl","Golden sparks swirled up and pulled Charlotte in.","Here we GO!",None,"WHOOSH","#c8621f"),
   NOTE("Charlotte's Secret Notebook","Two BIG questions:<br>➊ How does a plain rusty rock become strong shining steel?<br>➋ Grandpa Ben says the winner is the one who <i>wastes the least</i>. What does wasting less have to do with making metal? 🔩"),
 ]),
 ("c2","CHAPTER 2","The Forge at the Edge of the World","pages 9-16",[
   P("js_c2_arrive","One blink, and she stood on the floor of the biggest workshop she'd ever seen — furnaces taller than houses and a glowing orange river of metal.","A whole RIVER made of fire?!"),
   P("js_c2_bram","A broad-shouldered man in a silver suit waved. \"I'm Bram, a furnace operator. Down here we turn plain rock into the strongest bones of the world.\"","Rock into… BONES?!"),
   P("js_c2_pit","Far below, giant ladles tipped and poured glowing steel, sparks raining down like fireworks.","So much fire — and it all began as a dull rock?","Stick with me, kiddo."),
   NOTE("Charlotte's Secret Notebook","Bram is a furnace operator, and he grins like he has a secret — just like the other guides did. He calls steel the 'bones of the world'. Okay Bram… show me how! ✨"),
 ]),
 ("c3","CHAPTER 3","The Recipe: Rock, Carbon, Fire","pages 17-26",[
   P("js_c3_ore","Bram held up a rusty rock. \"Steel starts here — iron ore. It's just a plain reddish rock, but it's full of iron hiding inside.\"","There's METAL hiding in that rock!"),
   P("js_c3_carbon","\"Now add a pinch of carbon — the black stuff, cousin to coal,\" he said. \"Iron alone is soft and rusty. Iron plus a little carbon becomes strong steel.\"","A secret pinch of black dust!"),
   P("js_c3_mix","\"Rock, carbon, and ferocious heat,\" said Bram. \"That's the entire recipe. Three simple things.\"","Just THREE things make steel?!",None,"sizzle","#c8621f"),
   WOW("Here's the first secret! 💡 Steel is a <b>recipe</b>: take <b>iron ore</b> (a rusty rock), add a small amount of <b>carbon</b>, and cook it with <b>ferocious heat</b>. Iron by itself is soft and rusts; iron with a little carbon becomes the strong steel that holds up bridges and buildings."),
   ("diagram",),
   NOTE("Charlotte's Secret Notebook","Steel = iron ore + a little carbon + fierce heat. Iron alone is weak and rusty; the pinch of carbon makes it strong. Three simple ingredients! 🪨🔥"),
 ]),
 ("c4","CHAPTER 4","The Fire That Never Sleeps","pages 27-36",[
   P("js_c4_furnace","Bram opened a glowing door. Inside, a furnace roared white-hot, hotter than Charlotte could imagine.","It's like a tiny SUN in a box!"),
   P("js_c4_molten","The rock softened, then melted, then poured out as a glowing orange river of liquid steel.","The rock turned into fire-soup!"),
   P("js_c4_bar","\"We shape it while it's soft,\" said Bram, rolling a red-hot bar until it cooled to strong grey steel.","So THAT'S how you make a beam!"),
   WOW("It takes <b>tremendous heat</b> to melt rock into metal. Long ago, in another adventure, Charlotte met <b>Tata Steel</b> — India's very first great steelmaker, built in 1907, where the fire has never gone out. People call it <b>the fire that never sleeps</b>. Making steel means keeping that huge, hungry fire roaring day and night."),
   NOTE("Charlotte's Secret Notebook","A furnace melts the ore with ferocious heat into a glowing river of steel, then it's rolled into bars and beams. India's oldest steel fire (Tata Steel, 1907) has never gone out — the fire that never sleeps. 🌋"),
 ]),
 ("c5","CHAPTER 5","Waste Not: Catch the Heat","pages 37-44",[
   P("js_c5_gas","Bram pointed up. \"See that hot gas rushing out the top? Most makers just let all that heat fly away and be lost.\"","All that heat… escaping for free?",None,"whoooo","#c8621f"),
   P("js_c5_reuse","\"But not the clever ones,\" he grinned. \"We catch it in pipes and send it right back to heat the furnace again. Waste not!\"","Use the SAME heat twice — brilliant!"),
   WOW("Now the <b>real</b> secret begins. A furnace makes loads of hot gas. Let it escape and that heat — and money — is <b>wasted</b>. The smartest makers <b>catch the furnace's own hot gas and reuse it</b> to keep the fire going. Same heat, used again and again. Less waste means cheaper steel."),
   NOTE("Charlotte's Secret Notebook · SOLVED (part 1)","Making steel wastes a LOT of heat. Clever makers trap the furnace's hot gas and loop it back to reheat the furnace — so nothing is thrown away. Less waste = cheaper steel. ♻️🔥"),
 ]),
 ("c6","CHAPTER 6","A Second Life","pages 45-52",[
   P("js_c6_scrap","Bram led her to a mountain of junk — old cans, bent bars, broken machines. \"Steel never truly dies,\" he said.","A whole MOUNTAIN of old metal!"),
   P("js_c6_remelt","\"We melt the old scrap and pour it out brand new,\" he said. \"Free iron — already dug, already made. No new rock needed.\"","The old spoon becomes a new beam!"),
   WOW("Here's the second thrifty trick: <b>recycle scrap steel.</b> Old, rusty, broken steel can be <b>melted and made new again</b> — over and over. That saves digging fresh ore, which saves heat, effort and money. Reusing the old is far cheaper than always starting from rock."),
   NOTE("Charlotte's Secret Notebook","Old steel can be melted and reborn as new steel, forever. Recycling scrap saves ore, heat and money — another way to waste less and make steel cheaply. 🔁"),
 ]),
 ("c7","CHAPTER 7","The Leanest One Wins","pages 53-60",[
   P("js_c7_cheap","Bram weighed coins against a shiny beam. \"Put it together — reused heat, recycled scrap — and you can make steel cheaper than almost anyone.\"","Cheapest to make means… what?"),
   P("js_c7_survive","\"Steel prices swing like a wild sea,\" he said. \"Up, down, up, down. When they CRASH, the cheapest maker is the one still standing.\"","So being lean is how you SURVIVE!"),
   WOW("Steel prices rise and fall with the whole world — some years everyone pays high, some years the price <b>crashes</b>. When it crashes, a costly maker loses money on every beam and can drown. But the <b>low-cost maker</b> — the one who wastes the least — still earns a little, and <b>survives the lean years.</b> Being lean isn't just nice; it's how you stay alive."),
   NOTE("Charlotte's Secret Notebook · SOLVED!","The maker who wastes the least makes the cheapest steel. Steel prices swing wildly; when they crash, only the LOW-COST maker survives while costlier ones sink. Waste less → survive. 🌊⚓"),
 ]),
 ("c8","CHAPTER 8 · THE BIG DISCOVERY","The New Forge","pages 61-70",[
   P("js_c8_reveal","Bram led Charlotte to a viewpoint over a gleaming modern plant, humming with clever, thrifty machines.","Who makes steel THIS cleverly?"),
   P("js_c8_founding","\"This one started small by the sea, long after the old fire of Tata Steel,\" said Bram. \"A new forge, born lean and hungry.\"","A young one, learning to waste less!"),
   P("js_c8_scale","\"And it grew, and grew — always the leanest — until it became one of the biggest steelmakers in India.\"","From a little seaside works to a GIANT!"),
   WOW("Meet <b>JSW Steel</b> — <i>The New Forge.</i> It began much later than the century-old fire of Tata Steel, growing up by the western sea. By squeezing out waste — reusing heat, recycling scrap, staying <b>low-cost</b> — it grew into <b>one of India's largest steelmakers.</b> A new forge, standing tall beside the old fire that never sleeps."),
   TL([("1980s","A small steel works starts up by the western sea."),
       ("Decade by decade","It grows leaner and bigger, squeezing waste out at every step."),
       ("Today","It stands among India's largest — and lowest-cost — steelmakers."),
       ("Tomorrow","It recycles ever more scrap and cleans up its fire for a greener age. 🔩")]),
   NOTE("Charlotte's Secret Notebook","JSW Steel — 'the new forge' — began small by the sea, much later than Tata Steel's 1907 fire. By wasting the least (reused heat, recycled scrap, low cost) it grew into one of India's biggest steelmakers. 🏆"),
 ]),
 ("c9","CHAPTER 9","The Castle Built of Steel","pages 71-80",[
   P("js_c9_castle","Two kind old men appeared with a glow. Warren pointed at a castle standing on shining fields of steel. \"What keeps rivals out, Charlotte?\"","Walls! But what kind?"),
   MOAT([("💰","Wall One: The Lowest Cost","Because it wastes the least — reused heat, recycled scrap — it makes steel cheaper than most rivals. When prices crash, it still earns while others bleed.",False),
         ("🏭","Wall Two: Giant Scale","Huge plants, ports, mines and hard-won know-how, built up over decades. A rival can't copy all of that overnight.",False),
         ("🌉","Wall Three: Woven Into Everything","Buildings, railways, cars and bridges all need steel. A whole growing country leans on the strong grey metal it makes.",False),
         ("⚠️","The Crack in the Wall","Steel prices swing wildly with the whole world, and a big plant carries big debts. In lean years even good makers suffer — only the very leanest survive.",True)]),
   P("js_c9_munger","Charlie tapped his head. \"Don't just ask how to WIN. Ask how you'd LOSE — then don't do that. Invert!\"","Think backwards?"),
   WOW("<b>Charlie's Inversion:</b> to keep a steel giant strong, imagine how it could FAIL — letting costs creep up, borrowing too much, or being caught fat and lazy when prices crash — then carefully avoid each one, and stay the leanest maker of all."),
   P("js_c9_verdict","Warren nodded. \"It makes steel cheaper than most and at giant scale — a mighty castle, so long as it stays the LEANEST through the wild price swings.\"","I SEE the whole picture!"),
   NOTE("Charlotte's Secret Notebook · The Big Secret","JSW's castle: it's the LOW-COST maker, it's GIANT, and everything is built from its steel. But steel prices swing wildly and giants carry big debts — only the leanest survive the lean years. Charlie's trick: picture how you'd lose (costs creep, too much debt), then never do it. Invert! 🏰"),
 ]),
 ("c10","CHAPTER 10","Steel Everywhere","pages 81-88",[
   P("js_c10_wake","Charlotte woke in her own bed. The roaring mill was gone… but the little steel bar on her desk gleamed softly.","Was it real? It FELT real…"),
   P("js_c10_diary","On the diary's page, a new line glowed — as if Grandpa Ben wrote it just for her.",None,'"Now you see the strength hidden in a plain grey bar — and why the leanest maker wins. Well done."'),
   P("js_c10_look","She ran outside. The bridge, the railway, the tall buildings, a passing car — all of it, quietly, was steel pulled from rock and forged with care.","Steel is holding up the whole WORLD — I just never noticed!"),
   WOW("Next time you cross a bridge or ride a train, you'll know the secret: it's <b>steel</b> — iron ore and a pinch of carbon, melted in a ferocious fire and rolled strong. And the maker who <b>wastes the least</b> makes it cheapest and survives the lean years. That's <b>the new forge.</b> 🔩✨"),
   NOTE("Charlotte's Secret Notebook · What I Learned","➊ Steel = iron ore + a little carbon + ferocious heat.<br>➋ The real secret is EFFICIENCY: reuse the furnace's hot gas, recycle old scrap, waste nothing.<br>➌ Wasting less makes steel cheapest — and the low-cost maker survives when prices crash.<br>➍ JSW Steel (the new forge) grew lean into one of India's biggest steelmakers.<br>➎ But steel prices swing wildly, so Charlie's trick — picture how you'd lose (costs creep, too much debt), then stay the leanest. 💛"),
 ]),
]

HOOK=("js_c1_swirl","EVERY ORDINARY THING HIDES A SECRET.",
 "Charlotte learned how a rusty rock becomes mighty steel — and why the leanest maker wins. But the compass is twitching again — this time toward the shiny new cars gliding down the road. Who builds the machines that carry a whole country, and how do they win?",
 "NEXT ADVENTURE · The Machines That Carry Us →")

DIAGRAM='''<div class="panel wide" style="background:#fbf3ec;">
<svg viewBox="0 0 820 380"><rect width="820" height="380" fill="#fbf3ec"/>
<text x="410" y="38" text-anchor="middle" font-family="Bangers" font-size="24" fill="#c8621f">STEEL = IRON + CARBON + FIRE</text>
<g><rect x="60" y="112" width="150" height="90" rx="12" fill="#b5533a" stroke="#171717" stroke-width="4"/><text x="135" y="102" text-anchor="middle" font-family="Baloo 2" font-size="14" fill="#b5533a">IRON ORE</text><text x="135" y="165" text-anchor="middle" font-family="Bangers" font-size="24" fill="#f6b53f">a rusty rock</text><text x="135" y="235" text-anchor="middle" font-family="Nunito" font-size="13" fill="#555">iron hidden inside</text></g>
<g stroke="#37414d" stroke-width="5" fill="#37414d"><line x1="220" y1="150" x2="330" y2="150" stroke-dasharray="10 8"/><path d="M326 141 L344 150 L326 159Z"/></g>
<text x="275" y="135" text-anchor="middle" font-family="Baloo 2" font-size="12" fill="#37414d">+ a pinch of carbon</text>
<g><rect x="350" y="112" width="150" height="90" rx="12" fill="#c8621f" stroke="#171717" stroke-width="4"/><text x="425" y="102" text-anchor="middle" font-family="Baloo 2" font-size="14" fill="#c8621f">THE FURNACE</text><text x="425" y="165" text-anchor="middle" font-family="Bangers" font-size="24" fill="#fff">ferocious heat</text><text x="425" y="235" text-anchor="middle" font-family="Nunito" font-size="13" fill="#555">melts &amp; mixes it</text></g>
<g stroke="#5b6b7a" stroke-width="5" fill="#5b6b7a"><line x1="510" y1="150" x2="620" y2="150" stroke-dasharray="10 8"/><path d="M616 141 L634 150 L616 159Z"/></g>
<g><rect x="640" y="112" width="150" height="90" rx="12" fill="#5b6b7a" stroke="#171717" stroke-width="4"/><text x="715" y="102" text-anchor="middle" font-family="Baloo 2" font-size="16" fill="#37414d">STEEL</text><text x="715" y="165" text-anchor="middle" font-family="Bangers" font-size="22" fill="#fff">strong metal</text><text x="715" y="235" text-anchor="middle" font-family="Nunito" font-size="13" fill="#555">bones of the world</text></g>
<rect x="70" y="300" width="680" height="52" rx="9" fill="#fff" stroke="#171717" stroke-width="3"/>
<text x="410" y="322" text-anchor="middle" font-family="Nunito" font-size="14" fill="#123">Reuse the furnace's own hot gas &#43; melt old scrap again &#8594; far less waste &#8594; the LOWEST cost.</text>
<text x="410" y="343" text-anchor="middle" font-family="Nunito" font-size="13" fill="#555">And the leanest maker, wasting the least, survives when steel prices crash.</text>
</svg><div class="figcap">Iron ore and a pinch of carbon are melted in a ferocious furnace to make steel — and the leanest maker, wasting the least, wins.</div></div>'''

try:
    from book_extra import EXTRA, NEW2
    NEW.update(NEW2)
except Exception:
    EXTRA={}

def render_panel(pn,pageno=None):
    _,img,cap,b1,b2,sfx,sfxc,tall=pn
    pg=f'<div class="pageno">{pageno}</div>' if pageno else ''
    p=[f'<div class="panel wide">{pg}<img src="art/{img}.jpg" alt="" loading="lazy">']
    if cap: p.append(f'<div class="cap tl tilt">{cap}</div>')
    if b1: p.append(f'<div class="bubble dl" style="left:12px;bottom:12px;">{b1}</div>')
    if b2: p.append(f'<div class="bubble dr" style="right:12px;top:12px;">{b2}</div>')
    if sfx: p.append(f'<div class="sfx" style="left:40%;top:14px;font-size:32px;color:{sfxc};">{sfx}</div>')
    p.append('</div>'); return "".join(p)

def render_item(it,pageno=None):
    t=it[0]
    if t=="panel": return render_panel(it,pageno)
    if t=="diagram": return DIAGRAM
    if t=="sub": return f'<div class="sub"><span class="dot">{it[1]}</span><b>{it[2]}</b></div>'
    if t=="wow": return f'<div class="wow">{it[1]}</div>'
    if t=="note": return f'<div class="note"><div class="nt">{it[1]}</div><p>{it[2]}</p></div>'
    if t=="timeline":
        rows="".join(f'<div class="tl"><div class="yr">{y}</div><p>{x}</p></div>' for y,x in it[1])
        return f'<div class="timeline">{rows}</div>'
    if t=="moat":
        cards="".join(f'<div class="mc{" bad" if bad else ""}"><h4>{ic} {ti}</h4><p>{tx}</p></div>' for ic,ti,tx,bad in it[1])
        return f'<div class="moat">{cards}</div>'
    return ""

def referenced_images():
    imgs=set()
    for cls,_,_,_,items in CHAPTERS:
        for it in items:
            if it[0]=="panel": imgs.add(it[1])
        for it in EXTRA.get(cls,[]):
            if it[0]=="panel": imgs.add(it[1])
    imgs.add(HOOK[0]); imgs.add("00_cover"); imgs.add("_ref_bram"); return imgs

def build():
    body=[]; pageno=0
    for cls,num,title,pglabel,items in CHAPTERS:
        items=list(items); extra=EXTRA.get(cls,[])
        if extra:
            idx=[i for i,it in enumerate(items) if it[0]=="panel"]
            at=(idx[-1]+1) if idx else len(items)
            for ex in reversed(extra): items.insert(at,ex)
        start=pageno+1
        inner=[f'<div class="chapbar {cls}"><div class="n">{num}</div><h2>{title}</h2></div>']
        for it in items:
            if it[0]=="panel": pageno+=1; inner.append(render_item(it,pageno))
            else: inner.append(render_item(it))
        lbl=f"pages {start}-{pageno}" if pageno>=start else pglabel
        body.append(f'<div class="sheet" data-pg="{lbl}">{"".join(inner)}</div>')
    hook=f'''<div class="sheet" data-pg="the end… for now"><div class="hook"><img src="art/{HOOK[0]}.jpg" alt="">
<div class="hc"><h3>{HOOK[1]}</h3><p>{HOOK[2]}</p><div class="b2">{HOOK[3]}</div></div></div></div>'''
    head=open("/root/omahaindia.com/comics/site/book/jsw/_head.html").read()
    tail=open("/root/omahaindia.com/comics/site/book/jsw/_tail.html").read()
    open(OUT,"w").write(head+"".join(body)+hook+tail)
    print("built",OUT,"| chapters:",len(CHAPTERS),"| images:",len(referenced_images()))

if __name__=="__main__":
    build()
