#!/usr/bin/env python3
# Book 24 (Eicher Motors) — THE THUNDER ON TWO WHEELS. Science->business: one big cylinder fires
# ONCE per cycle -> a slow deep THUMP -> pressure waves your ears hear -> a long-stroke single's
# famous beat -> the company that sells not a machine but a FEELING and a badge people love.
import os
ART="/root/omahaindia.com/comics/site/book/eicher/art"
OUT="/root/omahaindia.com/comics/site/book/eicher/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"
REX=("Rex, a warm friendly Western MOTORCYCLE MECHANIC, NOT Indian, sturdy build, bushy grey mustache, warm grin, "
"a well-kept leather apron over a plaid shirt, oil-smudged hands, a spanner tucked in the apron pocket")

# name -> (w,h,seed,prompt,ref). ref set -> Seedream character; None -> flux scene. Ritual panels copied by mkbook.
NEW={
 "00_cover":(1024,1180,2400,f"{STYLE}. BOOK COVER, {CHARLOTTE} standing with {REX} beside a gleaming classic single-cylinder motorcycle in a warm workshop doorway, soft golden rings of sound rippling out from its exhaust across an open evening road, wonder, cinematic","charlotte"),
 "_ref_rex":(1024,1024,2401,f"{STYLE}. clean friendly character portrait, {REX}, plain soft warm background, warm grin, head and shoulders",None),
 # Ch1
 "ei_c1_coin":(1024,720,2410,f"{STYLE}. {CHARLOTTE} at home, Grandpa Ben's glowing brass compass twitching between a small shiny engine piston on the table and a distant motorcycle rolling past the window, EXPRESSION puzzled and intrigued, cozy lamp light","charlotte"),
 "ei_c1_diary":(1024,720,2411,f"{STYLE}. an open magical diary glowing warm gold on a desk, faint golden rings of sound rising from the page and curling into a tiny engine piston, no people",None),
 "ei_c1_swirl":(1024,720,2412,f"{STYLE}. {CHARLOTTE} pulled into a swirling spiral of golden light and rippling rings of sound from a glowing diary, EXPRESSION thrilled, magical","charlotte"),
 # Ch2 — arrive at the workshop, meet Rex
 "ei_c2_arrive":(1024,720,2413,f"{STYLE}. {CHARLOTTE} landing wide-eyed in the doorway of a cosy motorcycle workshop, gleaming machines and hanging tools everywhere, warm afternoon light through the shutter, EXPRESSION awe and delight","charlotte"),
 "ei_c2_rex":(1024,720,2414,f"{STYLE}. {REX} warmly welcoming {CHARLOTTE} into the workshop, wiping his oily hands on his leather apron, EXPRESSION Charlotte shy happy smile; keep the SAME mechanic from the reference, same leather apron over plaid shirt, same bushy grey mustache","rex"),
 "ei_c2_thump":(1024,720,2415,f"{STYLE}. a beautiful classic single-cylinder motorcycle idling in a workshop, slow soft golden rings of sound pulsing outward from its exhaust one at a time, dust motes trembling in the air, no faces close up",None),
 # Ch3 — one big cylinder, one bang per cycle
 "ei_c3_cylinder":(1024,720,2416,f"{STYLE}. {REX} kneeling to show {CHARLOTTE} a single big engine cylinder in his hands, a tiny warm spark glowing inside it, EXPRESSION Charlotte fascinated leaning in; SAME mechanic, leather apron, plaid shirt, grey mustache","rex"),
 "ei_c3_bang":(1024,720,2417,f"{STYLE}. a magical cutaway of one big engine cylinder, a small puff of fuel and air lighting up in a warm orange flash and shoving the piston down hard, playful science, no people",None),
 "ei_c3_once":(1024,720,2418,f"{STYLE}. a friendly diagram-like scene of a single piston travelling up and down inside a tall cylinder, one bright flash of fire happening only once while the piston makes its long round trip, warm tones, no people",None),
 # Ch4 — slow, deep, spaced-out thumps
 "ei_c4_slow":(1024,720,2419,f"{STYLE}. {CHARLOTTE} watching in wonder as a big single-cylinder engine ticks over slowly, one lazy golden pulse of sound leaving the exhaust, then a long pause, then another, EXPRESSION amazed and thoughtful, warm workshop light","charlotte"),
 "ei_c4_many":(1024,720,2420,f"{STYLE}. a playful side-by-side, a row of four tiny quick cylinders firing in a rapid blur of small fast sparks beside one single big cylinder giving one huge slow flash, warm storybook science, no people",None),
 "ei_c4_feel":(1024,720,2421,f"{STYLE}. an extreme close up of an exhaust pipe with one deep round pulse of golden air rolling out of it, ripples spreading through the dust and gently shaking a leaf on the ground, cosy and dramatic, no people",None),
 # Ch5 — sound is a pressure wave your ears hear
 "ei_c5_wave":(1024,720,2422,f"{STYLE}. magical rings of squeezed and stretched air travelling through the workshop like ripples on a pond, each ring a gentle band of warm light, showing sound as a pressure wave, no people",None),
 "ei_c5_ear":(1024,720,2423,f"{STYLE}. {REX} showing {CHARLOTTE} how the rolling rings of sound reach her ear and make a tiny drum inside it wobble, a friendly glowing cutaway of an ear beside them, EXPRESSION Charlotte delighted 'aha'; SAME mechanic, leather apron, plaid shirt, grey mustache","rex"),
 # Ch6 — a long-stroke single makes the famous beat
 "ei_c6_stroke":(1024,720,2424,f"{STYLE}. {CHARLOTTE} watching a tall long-stroke piston take a long unhurried journey down its cylinder, glowing motion trails marking the slow sweep, EXPRESSION awed and excited, bright workshop","charlotte"),
 "ei_c6_beat":(1024,720,2425,f"{STYLE}. a lone classic motorcycle rolling down an open evening road, big evenly-spaced golden rings of sound rolling out behind it across the fields like a slow heartbeat, magical, no faces close up",None),
 # Ch7 — the feeling and the badge people love
 "ei_c7_feeling":(1024,720,2426,f"{STYLE}. a joyful gathering of riders and their classic motorcycles at a roadside tea stall at dusk, machines gleaming, everyone laughing together, warm togetherness, no faces close up",None),
 "ei_c7_badge":(1024,720,2427,f"{STYLE}. a warm close up of a hand lovingly polishing a shiny blank metal badge on a motorcycle fuel tank, golden reflections, deep affection, no letters no words, no faces close up",None),
 # Ch8 — reveal Eicher Motors / Royal Enfield
 "ei_c8_reveal":(1024,720,2428,f"{STYLE}. {CHARLOTTE} and {REX} standing proudly before a bright modern motorcycle factory with a long line of gleaming classic machines rolling out under a blue sky, EXPRESSION proud recognition; SAME mechanic, leather apron, plaid shirt, grey mustache","charlotte"),
 "ei_c8_old":(1024,720,2429,f"{STYLE}. warm sepia mid-century scene, an old workshop building the very first classic thumping motorcycles, hopeful workers and old machines, inspiring, no text",None),
 "ei_c8_scale":(1024,720,2430,f"{STYLE}. a mighty modern motorcycle factory at golden hour, rows and rows of finished classic machines and a long open road leading away to the horizon, proud and grand, no people",None),
 # Ch10 (ch9 uses ritual castle/invert/verdict; ch10 uses ritual wake/diary)
 "ei_c10_look":(1024,720,2431,f"{STYLE}. {CHARLOTTE} outside at dusk, eyes closed and smiling as a distant motorcycle thumps past, soft golden rings of sound rolling over her and gently stirring her ponytails, EXPRESSION delighted wonder","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 Sound You Can Feel","pages 1-8",[
   P("ei_c1_coin","Back home, Grandpa Ben's compass twitched again — pointing between a little metal piston and a motorcycle thumping past the window.","A lump of metal… and a sound? What's the link?",None,"twitch"),
   P("ei_c1_diary","The old diary fell open. A new line shimmered in Grandpa Ben's hand.",None,'"Find the machine whose heartbeat people fall in love with."'),
   WOW("A motorcycle is just metal and rubber — so why do people stand still and smile when a certain one rumbles past? Some machines make a sound you don't only hear; you FEEL it in your chest. What makes that sound, and why does anyone love it? That's the puzzle of this whole adventure."),
   P("ei_c1_swirl","Golden light swirled up and pulled Charlotte in.","Here we GO!",None,"WHOOSH","#c8621f"),
   NOTE("Charlotte's Secret Notebook","Two BIG questions:<br>➊ How does an engine make that slow deep THUMP?<br>➋ Grandpa Ben says people love a <i>sound</i>. How can a company sell a feeling? 🏍️"),
 ]),
 ("c2","CHAPTER 2","The Workshop at the End of the Road","pages 9-16",[
   P("ei_c2_arrive","One blink, and she stood in the doorway of a warm little workshop — tools on hooks, machines gleaming, the air smelling of oil.","It's a hospital for motorcycles!"),
   P("ei_c2_rex","A cheerful man with a bushy grey mustache waved a spanner. \"I'm Rex, a motorcycle mechanic. In here we tune a very famous heartbeat.\"","A heart… BEAT?!"),
   P("ei_c2_thump","In the corner an old machine ticked over. THUMP… pause… THUMP. The dust in the air trembled with every one.","I can feel it in my TUMMY!","Stick with me, kiddo."),
   NOTE("Charlotte's Secret Notebook","Rex is a motorcycle mechanic, and he grins like he has a secret — just like the other guides did. He says the engine has a heartbeat. Okay Rex… show me! ✨"),
 ]),
 ("c3","CHAPTER 3","One Big Bang at a Time","pages 17-26",[
   P("ei_c3_cylinder","Rex held up a big metal tube. \"This is a cylinder. Inside, a tiny puff of fuel and air lights up — BANG — and shoves the piston down.\"","A little explosion pushes it!"),
   P("ei_c3_bang","\"That shove is what turns the wheel. No bang, no go. It's how every engine on earth gets its push.\"","Fire in… push out!"),
   P("ei_c3_once","\"But here's the trick,\" said Rex. \"This engine has just ONE big cylinder, and it only bangs ONCE every round trip.\"","Only once? Not all the time?",None,"boom","#c8621f"),
   WOW("Here's the first secret! 💥 An engine works by lighting a small puff of fuel and air inside a <b>cylinder</b>. The bang <b>shoves a piston</b>, and the piston turns the wheel. This machine has only <b>ONE big cylinder</b>, and it fires just <b>once per cycle</b> — not over and over in a hurry."),
   ("diagram",),
   NOTE("Charlotte's Secret Notebook","Fuel + air + spark = BANG. The bang pushes the piston, the piston turns the wheel. One BIG cylinder = one bang every round trip. 💥"),
 ]),
 ("c4","CHAPTER 4","Slow, Deep and Spaced Out","pages 27-36",[
   P("ei_c4_slow","\"Listen,\" said Rex. THUMP… long pause… THUMP. \"One bang. A rest. Another bang. Slow as a walking heart.\"","It's not in a rush at all!"),
   P("ei_c4_many","\"Squeeze in four little cylinders and you get lots of tiny quick bangs — a busy buzzing whine. But ONE big one?\"","One big thump beats four little buzzes!",None,"brrrr","#37414d"),
   P("ei_c4_feel","\"One big one gives a big, slow, fat pulse of air. Big and slow, you don't just hear — you FEEL.\"","That's why it wobbles my tummy!"),
   WOW("Many small cylinders fire quickly, so their bangs blur into a fast, high buzz. <b>One big cylinder</b> fires slowly, so each bang stays separate: a <b>deep, spaced-out THUMP</b>. Big and slow makes a sound you can feel in your chest."),
   NOTE("Charlotte's Secret Notebook · SOLVED (part 1)","Lots of little cylinders = fast tiny bangs = a buzzy whine. ONE big cylinder = slow fat bangs = a deep THUMP… pause… THUMP you can FEEL. 🫀"),
 ]),
 ("c5","CHAPTER 5","Rings in the Air","pages 37-44",[
   P("ei_c5_wave","\"So what IS a sound?\" asked Rex. \"Each bang squashes the air, and the squash rolls away like a ripple on a pond.\"","Sound is a RIPPLE in the air!",None,"ripple","#c8621f"),
   P("ei_c5_ear","\"That ripple reaches your ear and wobbles a tiny drum inside it. Wobble, wobble — and your brain calls it a THUMP.\"","So my ears catch the ripples?"),
   WOW("Sound is a <b>pressure wave</b>: each bang squeezes the air, and that squeeze travels outward like a ripple. When it reaches you, it wobbles a tiny drum inside your ear — and that wobble is what you <b>hear</b>. Big slow squeezes make deep sounds; fast little ones make high sounds."),
   NOTE("Charlotte's Secret Notebook","Sound = a squeeze of air travelling like a ripple. It wobbles the little drum in my ear, and my brain turns it into a sound. Slow big ripples = deep. Fast tiny ripples = high. 🔊"),
 ]),
 ("c6","CHAPTER 6","The Long Stroke","pages 45-52",[
   P("ei_c6_stroke","Rex opened the tall engine. The piston took a long, unhurried journey — all the way down, all the way up.","It goes SO far each time!"),
   P("ei_c6_beat","\"A long stroke means a long, lazy swing,\" said Rex. \"Fewer bangs, bigger ripples — and out comes that famous beat.\"","THAT'S the sound everyone knows!"),
   WOW("A <b>long-stroke single</b> has one big piston that travels a long way, slowly. Fewer bangs, each one bigger — so the air ripples arrive deep and evenly spaced, like a slow heartbeat. Bang → shove → squeeze of air → ripple → your ear. That's the whole chain of the famous beat!"),
   NOTE("Charlotte's Secret Notebook · SOLVED!","Long stroke = the piston swings a long way, slowly = fewer, bigger bangs = deep evenly-spaced ripples = the famous THUMP. The whole chain: BANG → shove → air squeeze → ripple → my ear! 🎵"),
 ]),
 ("c7","CHAPTER 7","Selling a Feeling","pages 53-60",[
   P("ei_c7_feeling","Rex swept his arm across the tea stall at dusk — riders laughing, machines gleaming, all of them thumping home together.","They didn't come for the METAL, did they?"),
   P("ei_c7_badge","\"Anyone can sell metal and rubber,\" said Rex, polishing the tank badge. \"These folks bought a feeling — and a badge they're proud of.\"","You can SELL a feeling?"),
   WOW("Here's the business secret. 💛 The company doesn't only sell a <b>machine</b> — it sells a <b>feeling</b>: the thump in your chest, the open road, the friends who ride beside you. And it sells a <b>badge</b> people are proud to wear. That is why they'll wait, and pay, for this one and not another."),
   NOTE("Charlotte's Secret Notebook","People don't buy just metal and rubber. They buy the FEELING — the thump, the road, the friends — and a badge they love. That's what a great brand really is. 🏍️💛"),
 ]),
 ("c8","CHAPTER 8 · THE BIG DISCOVERY","The Company Behind the Thunder","pages 61-70",[
   P("ei_c8_reveal","Rex led Charlotte to a viewpoint over a bright factory, where gleaming classic machines rolled out in a long line.","Who MAKES all of these?"),
   P("ei_c8_old","\"This machine is old,\" said Rex. \"Royal Enfield has been thumping along for longer than almost any motorcycle on earth.\"","It's been around FOREVER!"),
   P("ei_c8_scale","\"It nearly faded away. Then it was looked after properly — and grew into the maker of the world's most loved thump.\"","Rescued… and then it ROARED!"),
   WOW("A \"company\" can be the keeper of a feeling. <b>Eicher Motors</b> looks after <b>Royal Enfield</b> — one of the oldest motorcycle names still running. It once nearly faded; cared for well, it grew into a machine millions love, sold across the whole world."),
   TL([("Long ago","The classic thumping single is born — one big cylinder, one famous beat."),
       ("Hard years","The old machines wobble; quality slips and the love starts to fade."),
       ("The rescue","Eicher Motors takes care of it properly — better machines, same beloved thump."),
       ("Today","Riders around the world queue up for that badge and that beat. 🏍️")]),
   NOTE("Charlotte's Secret Notebook","Eicher Motors makes Royal Enfield — one of the oldest motorcycle names alive. It nearly faded, was cared for properly, and became the world's most loved thump. 🏆"),
 ]),
 ("c9","CHAPTER 9","The Castle Built of Love","pages 71-80",[
   P("ei_c9_castle","Two kind old men appeared with a glow. Warren pointed at a castle standing on a long open road. \"What keeps rivals out, Charlotte?\"","Walls! But what kind?"),
   MOAT([("🎵","Wall One: A Sound No One Copies","That deep long-stroke thump belongs to this machine. Rivals can build a motorcycle — they cannot borrow a heartbeat people already love.",False),
         ("🏅","Wall Two: The Badge","Decades of stories, friends and open roads live in that badge. You can't print loyalty; it has to be earned, one rider at a time.",False),
         ("🛠️","Wall Three: Decades of Loyalty","Riders, clubs, mechanics and grandparents who rode one first. A new company would need thirty years to grow that — and can't buy it.",False),
         ("⚠️","The Crack in the Wall","Love is not a wall of stone. If the machines stop being honest — shabby quality, a broken promise — the feeling fades, and a brand with no love is only metal.",True)]),
   P("ei_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 beloved brand strong, imagine how it could FADE — selling a shabby machine, chasing a fashion and losing the thump, or letting the badge go cheap — then carefully avoid every one of them, and keep the promise honest."),
   P("ei_c9_verdict","Warren nodded. \"A sound no one can copy and decades of love — a mighty castle, so long as the machine stays honest.\"","I SEE the whole picture!"),
   NOTE("Charlotte's Secret Notebook · The Big Secret","Royal Enfield is hard to copy: a SOUND nobody else has, a BADGE people are proud of, and DECADES of loyalty. But it must stay HONEST — if the machine disappoints, the love fades. Charlie's trick: picture how you'd lose (shabby work, broken promises), then never do it. Invert! 🏰"),
 ]),
 ("c10","CHAPTER 10","The Heartbeat on the Road","pages 81-88",[
   P("ei_c10_wake","Charlotte woke in her own bed. The workshop was gone… but the little piston on her desk shimmered softly.","Was it real? It FELT real…"),
   P("ei_c10_diary","On the diary's page, a new line glowed — as if Grandpa Ben wrote it just for her.",None,'"Now you know why a sound can be loved. Well done."'),
   P("ei_c10_look","She ran outside at dusk. Far away, one machine thumped past — and the ripples rolled right through her, warm and slow.","One big bang, rolling all the way to ME!"),
   WOW("Next time a motorcycle rumbles past, you'll know the secret: one big cylinder, one slow bang, one deep ripple of squeezed air rolling out to your ear. And you'll know why people love it — because a machine can carry a <b>feeling</b>. That's <b>the thunder on two wheels.</b> 🏍️🎵"),
   NOTE("Charlotte's Secret Notebook · What I Learned","➊ Fuel + air + spark = a BANG that shoves a piston.<br>➋ One BIG cylinder fires only once per cycle — slow, deep, spaced-out thumps.<br>➌ Sound is a pressure wave: squeezed air ripples out and wobbles a drum in my ear.<br>➍ A long-stroke single makes the famous beat everyone knows.<br>➎ Eicher's Royal Enfield sells a FEELING and a badge — a sound nobody copies. But Charlie's trick: picture how you'd lose (a dishonest machine), then stay honest. 💛"),
 ]),
]

HOOK=("ei_c1_swirl","EVERY ORDINARY THING HIDES A SECRET.",
 "Charlotte found a heartbeat inside an engine. But the compass is twitching again — toward the little painted tin of colour that turns a dull grey wall bright, and keeps it bright through years of rain. What is paint really made of, and how does a colour stick?",
 "NEXT ADVENTURE · The Colour That Sticks →")

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">ONE BIG BANG BECOMES A THUMP</text>
<g><rect x="70" y="112" width="140" height="90" rx="12" fill="#fff" stroke="#171717" stroke-width="4"/><text x="140" y="102" text-anchor="middle" font-family="Baloo 2" font-size="14" fill="#c8621f">ONE CYLINDER</text><text x="140" y="165" text-anchor="middle" font-family="Bangers" font-size="24" fill="#c8621f">BANG</text><text x="140" y="235" text-anchor="middle" font-family="Nunito" font-size="13" fill="#555">fuel + air + spark</text></g>
<g stroke="#e8425a" stroke-width="5" fill="#e8425a"><line x1="220" y1="150" x2="310" y2="150" stroke-dasharray="10 8"/><path d="M306 141 L324 150 L306 159Z"/></g>
<text x="265" y="135" text-anchor="middle" font-family="Baloo 2" font-size="12" fill="#e8425a">shoves</text>
<g><rect x="330" y="112" width="140" height="90" rx="12" fill="#37414d" stroke="#171717" stroke-width="4"/><text x="400" y="102" text-anchor="middle" font-family="Baloo 2" font-size="14" fill="#37414d">LONG PISTON</text><text x="400" y="165" text-anchor="middle" font-family="Bangers" font-size="22" fill="#f6b53f">slow swing</text><text x="400" y="235" text-anchor="middle" font-family="Nunito" font-size="13" fill="#555">a long, unhurried stroke</text></g>
<g stroke="#2aa15a" stroke-width="5" fill="#2aa15a"><line x1="480" y1="150" x2="600" y2="150" stroke-dasharray="10 8"/><path d="M596 141 L614 150 L596 159Z"/></g>
<text x="540" y="135" text-anchor="middle" font-family="Baloo 2" font-size="12" fill="#2aa15a">squeezes the air</text>
<text x="540" y="182" text-anchor="middle" font-family="Nunito" font-size="12" fill="#555">one fat ripple at a time</text>
<g><rect x="620" y="112" width="150" height="90" rx="12" fill="#fff" stroke="#171717" stroke-width="4"/><text x="695" y="102" text-anchor="middle" font-family="Baloo 2" font-size="16" fill="#2aa15a">YOUR EAR</text><text x="695" y="165" text-anchor="middle" font-family="Bangers" font-size="24" fill="#c8621f">THUMP</text><text x="695" y="235" text-anchor="middle" font-family="Nunito" font-size="13" fill="#555">a tiny drum wobbles</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">One big cylinder fires ONCE per cycle &#8594; a slow fat shove &#8594; a big squeeze of air &#8594; a deep ripple &#8594; the famous THUMP.</text>
<text x="410" y="343" text-anchor="middle" font-family="Nunito" font-size="13" fill="#555">Many small cylinders fire fast and blur into a buzz. One big slow one you can feel in your chest.</text>
</svg><div class="figcap">A single big cylinder fires once per cycle; the slow bang squeezes the air into a deep ripple your ear hears as a thump.</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_rex"); 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/eicher/_head.html").read()
    tail=open("/root/omahaindia.com/comics/site/book/eicher/_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()
