Story images: sources for landing-page cover and live screenshots

This commit is contained in:
pat
2026-09-25 17:58:24 +00:00
parent 1685918c11
commit a69711227e
22 changed files with 223 additions and 195 deletions
+55 -72
View File
@@ -45,6 +45,8 @@ def slot(src, label, size, cls='', style=''):
return (f'<div class="slot {cls}" data-src="../assets/{src}" style="{style}"><div class="ph">{ICONS["camera"]}'
f'<b>{label}</b><small>{size}</small></div></div>')
COVER_3D = '../assets-gen/cover-3d.png' # rendered from the landing page (render_cover.py)
COVER_FLAT = '../assets-gen/cover-flat.png'
pages = [] # dicts: name, body, css, sel, w, h(optional), dpr
def page(name, body, css='', w=680, h=None, extra_css='', px_h=None):
pages.append(dict(name=name, body=body, css=css, w=w, h=h, extra_css=extra_css, px_h=px_h))
@@ -74,7 +76,7 @@ add_header('01')
page('01b-welcome-cover-icons', f'''
<div class="canvas" id="shot">
<div class="wel">
<div class="bookwrap"><div class="book">{slot("cover", "Book cover goes here", "Final cover · owner supplies", "cover")}</div></div>
<figure class="bookwrap"><img src="{COVER_3D}" alt=""><figcaption>Cover mockup</figcaption></figure>
<div class="wtxt">
<h2>Welcome</h2>
<p class="t">{TITLE}</p>
@@ -88,11 +90,10 @@ page('01b-welcome-cover-icons', f'''
<div>{icon("updated", "ico big")}<p>Library updated often</p></div>
</div>
</div>''', css='''
.wel{display:flex;gap:34px;align-items:center;padding:6px 6px 26px}
.bookwrap{flex:none;padding:6px 14px 14px 4px}
.book{width:184px;height:258px;border-radius:3px 8px 8px 3px;position:relative;box-shadow:10px 12px 0 -3px #efe9db,10px 12px 0 -2px #d6cdba,0 26px 40px -18px rgba(18,63,52,.5)}
.book .slot{position:absolute;inset:0;border-radius:3px 8px 8px 3px}
.book .slot .ph b{font-size:15px}
.wel{display:flex;gap:30px;align-items:center;padding:0 6px 22px}
.bookwrap{flex:none;margin:0;width:196px;text-align:center}
.bookwrap img{width:196px;display:block}
.bookwrap figcaption{font-family:var(--mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);margin-top:-14px}
.wtxt h2{font-family:var(--display);font-weight:900;font-size:50px;letter-spacing:-.03em;line-height:1;color:var(--pine-deep)}
.wtxt .t{font-family:var(--display);font-weight:600;font-size:22px;letter-spacing:-.02em;line-height:1.2;margin:14px 0 12px}
.wtxt .s{font-size:16px;line-height:1.5;color:var(--muted);max-width:25rem}
@@ -183,67 +184,40 @@ page('04b-workflow-recipe-diagram', f'''<div class="canvas" id="shot">
.co h4{font-weight:700;font-size:16px;line-height:1.25}
.co p{font-size:14px;line-height:1.42;color:var(--muted);margin-top:2px}
''')
CHAPTERS = ['Real Estate', 'Financial Advisors', 'Fractional <span style="white-space:nowrap">C-Suite</span>', 'Higher Education', 'Communities', 'Technology']
page('04c-chapter-tiles-DRAFT', '<div class="canvas" id="shot"><p class="spec">Chapters</p><div class="tg">' + ''.join(
f'<div class="card t"><span class="n">{i:02d}</span><h3>{c}</h3>{icon("book")}</div>' for i, c in enumerate(CHAPTERS, 1)) + '</div></div>', css='''
# Published on the landing page: marketing-site/components/Contents.jsx ("The founding library")
CHAPTERS = [('Real Estate', 'Market briefs & listings, without leaking client data'),
('Financial Advisors', 'Portfolio summaries that stay compliant & reviewed'),
('Fractional <span style="white-space:nowrap">C-Suite</span>', 'Board updates & plans with real escalation paths'),
('Higher Education', 'Course & research workflows with academic integrity'),
('Communities', 'Moderation & programming that respects members'),
('Technology', 'Agent patterns, evals & the responsible defaults')]
page('04c-chapter-tiles', '<div class="canvas" id="shot"><p class="spec">The founding library</p><div class="tg">' + ''.join(
f'<div class="card t"><span class="n">{i:02d}</span><h3>{c}</h3><p>{d}</p></div>' for i, (c, d) in enumerate(CHAPTERS, 1)) + '</div></div>', css='''
.tg{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-top:14px}
.t{padding:18px 18px 20px;position:relative;min-height:118px;display:flex;flex-direction:column}
.t .n{font-family:var(--mono);font-size:13px;color:var(--muted);letter-spacing:.08em}
.t h3{font-family:var(--display);font-weight:600;font-size:22px;letter-spacing:-.02em;line-height:1.12;margin-top:auto;padding-top:22px}
.t .ico{position:absolute;right:16px;top:16px;width:22px;height:22px;opacity:.8}
.t{padding:16px 16px 18px;display:flex;flex-direction:column}
.t .n{font-family:var(--mono);font-size:13px;color:var(--pine);letter-spacing:.08em}
.t h3{font-family:var(--display);font-weight:600;font-size:21px;letter-spacing:-.02em;line-height:1.12;margin:10px 0 6px}
.t p{font-size:14px;line-height:1.4;color:var(--muted)}
''')
# ---------------------------------------------------------------- 05 try one workflow
add_header('05')
CHECK = ['Use the included sample_listings.csv (no live client file).',
'Produce the market-1pager brief.',
'Walk the human checklist.',
'Mark complete when you have a brief you would send after review.']
page('05b-sample-workflow-try-it', f'''<div class="canvas" id="shot">
page('05b-sample-workflow-try-it', '''<div class="canvas" id="shot">
<div class="win">
<div class="bar"><i></i><i></i><i></i><span class="url">aibook.fun</span></div>
<div class="scr">
<article class="recipe">
<div class="recipe-top"><span class="recipe-id"><b>RE-01</b> · v1.2 · ~15 min · ≈ $0.03</span><span class="recipe-vert">Real Estate</span></div>
<div class="recipe-body">
<h3 class="recipe-title">Draft a property-market brief with an AI agent without leaking client data.</h3>
<div class="xrow"><h4 class="k">Safeguards</h4><div class="v">
<div class="guard">{ICONS["shield"]}<span>Redaction runs on your machine. Raw client data never reaches the model.</span></div>
<div class="guard">{ICONS["shield"]}<span>A human checklist is required before the brief is shared.</span></div>
</div></div>
<div class="xrow try"><h4 class="k">Try-it</h4><div class="v">
<p class="mins">About 15 minutes</p>
<ul class="cl">{''.join(f'<li><span class="box"></span>{c}</li>' for c in CHECK)}</ul>
<div class="acts"><span class="chip">◷ cost to try ≈ $0.03</span><span class="btn btn-ghost">Mark complete</span></div>
</div></div>
</div>
</article>
</div>
<div class="bar"><i></i><i></i><i></i><span class="url">aibook.fun/read/recipe/RE-01</span></div>
<img src="../assets-gen/re01-top.png" alt="">
<div class="gap"><span>Problem · Actors · Setup · Safeguards · Method · Evidence</span></div>
<img src="../assets-gen/re01-tryit.png" alt="">
</div>
<p class="foot-note">Sample workflow RE-01, shown in the AiBook.fun reader.</p>
</div>''', extra_css='<link rel="stylesheet" href="../reader/globals.css">', css='''
body{background:#fff}
.win{background:#fff;border:1px solid var(--line-2);border-radius:14px;overflow:hidden;box-shadow:var(--sh-lg)}
<p class="foot-note">Screenshots of sample workflow RE-01 at aibook.fun/read/recipe/RE-01</p>
</div>''', css='''
.win{background:var(--paper);border:1px solid var(--line-2);border-radius:14px;overflow:hidden;box-shadow:var(--sh-lg)}
.bar{display:flex;align-items:center;gap:7px;padding:10px 14px;background:#f1eee7;border-bottom:1px solid var(--line)}
.bar i{width:11px;height:11px;border-radius:999px;background:#d8d1c4;display:block}
.bar .url{margin-left:14px;flex:1;background:#fff;border:1px solid var(--line);border-radius:999px;padding:4px 14px;font-family:var(--mono);font-size:13px;color:var(--muted)}
.scr{padding:20px;background:var(--paper)}
.recipe{box-shadow:var(--sh-md);border-radius:16px}
.recipe-top{padding:14px 18px}
.recipe-id{font-size:13px}
.recipe-vert{font-size:12px}
.recipe-body{padding:22px 20px 22px}
.recipe-title{font-size:23px;margin-bottom:16px}
.xrow{display:grid;grid-template-columns:96px 1fr;gap:14px;padding:14px 0;border-top:1px dashed var(--line-2)}
.xrow .k{font-family:var(--mono);font-size:12.5px;text-transform:uppercase;letter-spacing:.1em;color:var(--pine);padding-top:3px;font-weight:500}
.guard{font-size:15px;padding:5px 0;color:var(--muted);gap:9px;line-height:1.45}
.guard svg{width:18px;height:18px;color:var(--pine);flex:none;margin-top:1px}
.mins{font-weight:700;font-size:16px;margin-bottom:8px}
.cl li{display:flex;gap:11px;align-items:flex-start;font-size:15px;line-height:1.45;padding:5px 0}
.cl .box{width:18px;height:18px;border:1.6px solid var(--pine);border-radius:5px;flex:none;margin-top:2px;background:#fff}
.acts{display:flex;align-items:center;gap:12px;margin-top:12px;flex-wrap:wrap}
.chip{font-size:13px}
.btn{font-size:15px;padding:10px 18px}
.win img{display:block;width:614px}
.gap{padding:10px 0;text-align:center;background:linear-gradient(transparent calc(50% - 1px),var(--line-2) calc(50% - 1px) calc(50% + 1px),transparent calc(50% + 1px))}
.gap span{font-family:var(--mono);font-size:12.5px;color:var(--muted);background:var(--paper);padding:2px 10px;letter-spacing:.02em}
.foot-note{text-align:center}
''')
page('05c-button-try-the-workflow', f'''<div class="canvas" id="shot">
@@ -265,7 +239,7 @@ def three_ways(paperback=True):
rows = ''.join(f'<li class="{"feat" if f else ""}"><b>{p}</b><span>{t}' + (f'<small>{s}</small>' if s else '') + '</span></li>'
for p, t, s, f in cloud_items)
if paperback:
rows += f'<li><b class="np">{ICONS["book"]}</b><span>Signed paperback and eBook<small>Price on the reward</small></span></li>'
rows += f'<li><b class="np"><img src="{COVER_3D}" alt=""></b><span>Signed paperback and eBook<small>Price on the reward</small></span></li>'
cloud_deliv = ('<li>Library access: within about 14 days of a successful campaign</li><li>eBook: January 2027</li>'
+ ('<li>Signed paperback: February 2027</li>' if paperback else ''))
inc = f'<ul class="inc"><li>{icon("seat")}Your seat</li><li>{icon("book")}The eBook</li><li>{icon("library")}12 months of AiBook.fun</li></ul>'
@@ -287,7 +261,7 @@ def three_ways(paperback=True):
<div class="pr"><p class="price">Price on the reward</p></div>
</div>
<div class="col card cloud">
<p class="nm">Cloud</p><h3>Do it yourself, online</h3>
<p class="nm">Cloud</p><div class="ch"><h3>Do it yourself, online</h3><img src="{COVER_FLAT}" alt=""></div>
<div class="sec span2"><p class="lb">Choose one</p><ul class="prices">{rows}</ul></div>
<div class="sec"><p class="lb">When</p><ul class="dl">{cloud_deliv}</ul></div>
<div class="pr"></div>
@@ -312,7 +286,9 @@ TW_CSS = '''
.prices li{display:grid;grid-template-columns:58px 1fr;gap:8px;align-items:baseline;padding:7px 8px;border-radius:10px;font-size:14px;line-height:1.35}
.prices li+li{margin-top:4px}
.prices b{font-family:var(--display);font-weight:900;font-size:24px;letter-spacing:-.03em;color:var(--ink)}
.prices b.np{align-self:start;color:var(--muted);width:22px;height:22px;margin:1px 0 0 4px}
.prices b.np{align-self:start;width:30px;margin:0 0 0 6px}.prices b.np img{width:30px;display:block}
.ch{display:flex;align-items:flex-start;justify-content:space-between;gap:10px}
.ch img{width:46px;border-radius:2px 4px 4px 2px;box-shadow:0 6px 12px -6px rgba(18,63,52,.5);margin:4px 0 8px}
.prices small{display:block;font-size:13px;color:var(--muted)}
.prices li.feat{background:var(--pine);color:#eaf1ec}
.prices li.feat b{color:#fff}
@@ -405,7 +381,7 @@ page('09b-aibook-fun-device-mockup', f'''<div class="canvas" id="shot">
<span class="co" data-c="updated" style="right:0;bottom:0">Last updated</span>
<svg class="lines"></svg>
</div>
<p class="foot-note">Mockup of the AiBook.fun reader, showing the sample Real Estate chapter.</p>
<p class="foot-note">Screenshots of the AiBook.fun reader (sample Real Estate chapter) in device frames.</p>
</div>
<script>
window.afterLayout = async () => {{
@@ -527,20 +503,24 @@ def lane(title, sub, ic, steps):
st = ''
for j, (i2, t, d) in enumerate(steps):
if j: st += f'<span class="ar">{ICONS["arrow"]}</span>'
st += f'<div class="stp">{icon(i2, "ico ci")}<b>{t}</b>' + (f'<p>{d}</p>' if d else '') + '</div>'
return f'<div class="card lane"><div class="lh">{icon(ic, "ico lhi")}<div><h3>{title}</h3><p>{sub}</p></div></div><div class="steps n{len(steps)}">{st}</div></div>'
vis = f'<img class="cv" src="{i2[4:]}" alt="">' if i2.startswith('img:') else icon(i2, "ico ci")
st += f'<div class="stp">{vis}<b>{t}</b>' + (f'<p>{d}</p>' if d else '') + '</div>'
head = f'<img class="lhc" src="{ic[4:]}" alt="">' if ic.startswith('img:') else icon(ic, "ico lhi")
return f'<div class="card lane"><div class="lh">{head}<div><h3>{title}</h3><p>{sub}</p></div></div><div class="steps n{len(steps)}">{st}</div></div>'
page('12b-shipping-and-delivery-lanes', '<div class="canvas" id="shot">' +
lane('Digital rewards', 'Arrive by email', 'mail', [
('mail', 'Email', 'To the address on your Kickstarter account'),
('library', 'Library access', 'Within about 14 days of a successful campaign'),
('book', 'eBook', 'January 2027')]) +
lane('Signed paperbacks', 'Ship in February 2027', 'box', [
('img:' + COVER_FLAT, 'eBook', 'January 2027')]) +
lane('Signed paperbacks', 'Ship in February 2027', 'img:' + COVER_3D, [
('survey', 'Survey', 'Kickstarter’s Pledge Manager asks for your address and charges shipping and any taxes'),
('printer', 'Printing', ''),
('pen', 'Signing', 'January 2027'),
('truck', 'Shipping', 'February 2027')]) +
'</div>', css='''
.lane{padding:16px 18px 18px}
.cv{display:block;height:46px;margin:0 auto 8px;border-radius:1px 3px 3px 1px;box-shadow:0 4px 8px -4px rgba(18,63,52,.5)}
.lhc{height:52px;display:block;margin:-6px 2px -4px 0}
.lane+.lane{margin-top:14px}
.lh{display:flex;align-items:center;gap:12px;padding-bottom:12px;border-bottom:1px dashed var(--line-2);margin-bottom:14px}
.ico.lhi{width:40px;height:40px;padding:9px;border-radius:12px;background:var(--pine);color:#fff}
@@ -585,20 +565,23 @@ TEAM = [('@THY', 'Name goes here', 'Author and Retreat host'),
team_html = ''.join(
f'<div class="card tm">{slot("team-" + (h[1:].lower() if h else ("osinachi-patrick" if n.startswith("Osinachi") else "academy-lead")), "Photo", "800 × 800", "tp")}'
f'<div><p class="tn">{n}</p>' + (f'<p class="th">{h}</p>' if h else '') + f'<p class="tr">{r}</p></div></div>' for h, n, r in TEAM)
# Landing page (Authors.jsx) lists three co-authors as confirmed, by chapter; names are not published yet.
AUTHORS = [('Name goes here', 'Co-author, Real Estate & Finance'), ('Name goes here', 'Co-author, <span style="white-space:nowrap">Fractional C-Suite</span>'),
('Name goes here', 'Co-author, Technology & RAAI')]
AUTHOR_HTML = ''.join(f'<div class="card au">{slot(f"author-{i}", "Author photo goes here", "800 × 800", "ap")}<p class="an">{n}</p><p class="ar">{r}</p></div>'
for i, (n, r) in enumerate(AUTHORS, 1))
page('14b-authors-and-team', f'''<div class="canvas" id="shot">
<p class="spec">The authors</p>
<div class="ag">
<div class="card au">{slot("author-1", "Author photo goes here", "800 × 800", "ap")}<p class="an">Author name</p><p class="ar">Role in the book</p></div>
<div class="card au">{slot("author-2", "Author photo goes here", "800 × 800", "ap")}<p class="an">Author name</p><p class="ar">Role in the book</p></div>
</div>
<div class="ag">{AUTHOR_HTML}</div>
<p class="spec" style="margin-top:22px">The team</p>
<div class="tg">{team_html}</div>
</div>''', css='''
.ag{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:12px}
.ag{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-top:12px}
.au{padding:14px 14px 18px}
.ap{aspect-ratio:1/1;border-radius:10px}
.an{font-family:var(--display);font-weight:600;font-size:22px;letter-spacing:-.02em;margin-top:12px;color:#8d8579}
.ar{font-size:15px;color:var(--muted);margin-top:2px}
.ap .ph b{font-size:13px}
.an{font-family:var(--display);font-weight:600;font-size:19px;letter-spacing:-.02em;margin-top:12px;color:#8d8579}
.ar{font-size:14px;line-height:1.35;color:var(--muted);margin-top:2px}
.tg{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-top:12px}
.tm{padding:12px;display:flex;gap:11px;align-items:flex-start}
.tp{width:60px;height:60px;border-radius:999px;flex:none}