Story images: sources for landing-page cover and live screenshots
This commit is contained in:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user